mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:01:09 +02:00
fix(manacore-web,todo-web): use runtime URLs for backend API services
- manacore-web: Update todo, calendar, contacts service files to use runtime URLs from window instead of import.meta.env (baked at build time) - manacore-web: Add backend URL env vars to docker-compose.staging.yml - manacore-web: Fix fallback ports (todo: 3017→3018, calendar: 3014→3016) - todo-web: Update API client to use runtime URL from window This enables Docker containers to use runtime-injected URLs instead of build-time environment variables. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
7caeea4abd
commit
4398fbc29b
6 changed files with 123 additions and 24 deletions
|
|
@ -8,14 +8,29 @@ import type { Handle } from '@sveltejs/kit';
|
||||||
* but Docker containers need runtime configuration.
|
* but Docker containers need runtime configuration.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Auth URL
|
||||||
const PUBLIC_MANA_CORE_AUTH_URL_CLIENT =
|
const PUBLIC_MANA_CORE_AUTH_URL_CLIENT =
|
||||||
process.env.PUBLIC_MANA_CORE_AUTH_URL_CLIENT || process.env.PUBLIC_MANA_CORE_AUTH_URL || '';
|
process.env.PUBLIC_MANA_CORE_AUTH_URL_CLIENT || process.env.PUBLIC_MANA_CORE_AUTH_URL || '';
|
||||||
|
|
||||||
|
// Backend URLs for dashboard widgets
|
||||||
|
const PUBLIC_TODO_API_URL_CLIENT =
|
||||||
|
process.env.PUBLIC_TODO_API_URL_CLIENT || process.env.PUBLIC_TODO_API_URL || '';
|
||||||
|
const PUBLIC_CALENDAR_API_URL_CLIENT =
|
||||||
|
process.env.PUBLIC_CALENDAR_API_URL_CLIENT || process.env.PUBLIC_CALENDAR_API_URL || '';
|
||||||
|
const PUBLIC_CLOCK_API_URL_CLIENT =
|
||||||
|
process.env.PUBLIC_CLOCK_API_URL_CLIENT || process.env.PUBLIC_CLOCK_API_URL || '';
|
||||||
|
const PUBLIC_CONTACTS_API_URL_CLIENT =
|
||||||
|
process.env.PUBLIC_CONTACTS_API_URL_CLIENT || process.env.PUBLIC_CONTACTS_API_URL || '';
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
return resolve(event, {
|
return resolve(event, {
|
||||||
transformPageChunk: ({ html }) => {
|
transformPageChunk: ({ html }) => {
|
||||||
const envScript = `<script>
|
const envScript = `<script>
|
||||||
window.__PUBLIC_MANA_CORE_AUTH_URL__ = "${PUBLIC_MANA_CORE_AUTH_URL_CLIENT}";
|
window.__PUBLIC_MANA_CORE_AUTH_URL__ = "${PUBLIC_MANA_CORE_AUTH_URL_CLIENT}";
|
||||||
|
window.__PUBLIC_TODO_API_URL__ = "${PUBLIC_TODO_API_URL_CLIENT}";
|
||||||
|
window.__PUBLIC_CALENDAR_API_URL__ = "${PUBLIC_CALENDAR_API_URL_CLIENT}";
|
||||||
|
window.__PUBLIC_CLOCK_API_URL__ = "${PUBLIC_CLOCK_API_URL_CLIENT}";
|
||||||
|
window.__PUBLIC_CONTACTS_API_URL__ = "${PUBLIC_CONTACTS_API_URL_CLIENT}";
|
||||||
</script>`;
|
</script>`;
|
||||||
return html.replace('<head>', `<head>${envScript}`);
|
return html.replace('<head>', `<head>${envScript}`);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,32 @@
|
||||||
* Fetches events from the Calendar backend for dashboard widgets.
|
* Fetches events from the Calendar backend for dashboard widgets.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { browser } from '$app/environment';
|
||||||
import { createApiClient, type ApiResult } from '../base-client';
|
import { createApiClient, type ApiResult } from '../base-client';
|
||||||
|
|
||||||
// Backend URL - falls back to localhost for development
|
// Get Calendar API URL dynamically at runtime
|
||||||
const CALENDAR_API_URL = import.meta.env.PUBLIC_CALENDAR_API_URL || 'http://localhost:3014/api/v1';
|
function getCalendarApiUrl(): string {
|
||||||
|
if (browser && typeof window !== 'undefined') {
|
||||||
|
// Client-side: use injected window variable (set by hooks.server.ts)
|
||||||
|
const injectedUrl = (window as unknown as { __PUBLIC_CALENDAR_API_URL__?: string })
|
||||||
|
.__PUBLIC_CALENDAR_API_URL__;
|
||||||
|
if (injectedUrl) {
|
||||||
|
return `${injectedUrl}/api/v1`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback for local development
|
||||||
|
return 'http://localhost:3016/api/v1';
|
||||||
|
}
|
||||||
|
|
||||||
const client = createApiClient(CALENDAR_API_URL);
|
// Lazy-initialized client to ensure we get the correct URL at runtime
|
||||||
|
let _client: ReturnType<typeof createApiClient> | null = null;
|
||||||
|
|
||||||
|
function getClient() {
|
||||||
|
if (!_client) {
|
||||||
|
_client = createApiClient(getCalendarApiUrl());
|
||||||
|
}
|
||||||
|
return _client;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calendar entity from Calendar backend
|
* Calendar entity from Calendar backend
|
||||||
|
|
@ -59,7 +79,7 @@ export const calendarService = {
|
||||||
const startDate = new Date().toISOString().split('T')[0];
|
const startDate = new Date().toISOString().split('T')[0];
|
||||||
const endDate = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
const endDate = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
||||||
|
|
||||||
const result = await client.get<{ events: CalendarEvent[] }>(
|
const result = await getClient().get<{ events: CalendarEvent[] }>(
|
||||||
`/events?startDate=${startDate}&endDate=${endDate}`
|
`/events?startDate=${startDate}&endDate=${endDate}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -75,7 +95,7 @@ export const calendarService = {
|
||||||
*/
|
*/
|
||||||
async getTodayEvents(): Promise<ApiResult<CalendarEvent[]>> {
|
async getTodayEvents(): Promise<ApiResult<CalendarEvent[]>> {
|
||||||
const today = new Date().toISOString().split('T')[0];
|
const today = new Date().toISOString().split('T')[0];
|
||||||
const result = await client.get<{ events: CalendarEvent[] }>(
|
const result = await getClient().get<{ events: CalendarEvent[] }>(
|
||||||
`/events?startDate=${today}&endDate=${today}`
|
`/events?startDate=${today}&endDate=${today}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -90,7 +110,7 @@ export const calendarService = {
|
||||||
* Get all calendars
|
* Get all calendars
|
||||||
*/
|
*/
|
||||||
async getCalendars(): Promise<ApiResult<Calendar[]>> {
|
async getCalendars(): Promise<ApiResult<Calendar[]>> {
|
||||||
const result = await client.get<{ calendars: Calendar[] }>('/calendars');
|
const result = await getClient().get<{ calendars: Calendar[] }>('/calendars');
|
||||||
|
|
||||||
if (result.error || !result.data) {
|
if (result.error || !result.data) {
|
||||||
return { data: null, error: result.error };
|
return { data: null, error: result.error };
|
||||||
|
|
@ -109,7 +129,7 @@ export const calendarService = {
|
||||||
const startDate = new Date().toISOString().split('T')[0];
|
const startDate = new Date().toISOString().split('T')[0];
|
||||||
const endDate = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
const endDate = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
||||||
|
|
||||||
const result = await client.get<{ events: CalendarEvent[] }>(
|
const result = await getClient().get<{ events: CalendarEvent[] }>(
|
||||||
`/events?calendarIds=${calendarId}&startDate=${startDate}&endDate=${endDate}`
|
`/events?calendarIds=${calendarId}&startDate=${startDate}&endDate=${endDate}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,32 @@
|
||||||
* Fetches contacts from the Contacts backend for dashboard widgets.
|
* Fetches contacts from the Contacts backend for dashboard widgets.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { browser } from '$app/environment';
|
||||||
import { createApiClient, type ApiResult } from '../base-client';
|
import { createApiClient, type ApiResult } from '../base-client';
|
||||||
|
|
||||||
// Backend URL - falls back to localhost for development
|
// Get Contacts API URL dynamically at runtime
|
||||||
const CONTACTS_API_URL = import.meta.env.PUBLIC_CONTACTS_API_URL || 'http://localhost:3015/api/v1';
|
function getContactsApiUrl(): string {
|
||||||
|
if (browser && typeof window !== 'undefined') {
|
||||||
|
// Client-side: use injected window variable (set by hooks.server.ts)
|
||||||
|
const injectedUrl = (window as unknown as { __PUBLIC_CONTACTS_API_URL__?: string })
|
||||||
|
.__PUBLIC_CONTACTS_API_URL__;
|
||||||
|
if (injectedUrl) {
|
||||||
|
return `${injectedUrl}/api/v1`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback for local development
|
||||||
|
return 'http://localhost:3015/api/v1';
|
||||||
|
}
|
||||||
|
|
||||||
const client = createApiClient(CONTACTS_API_URL);
|
// Lazy-initialized client to ensure we get the correct URL at runtime
|
||||||
|
let _client: ReturnType<typeof createApiClient> | null = null;
|
||||||
|
|
||||||
|
function getClient() {
|
||||||
|
if (!_client) {
|
||||||
|
_client = createApiClient(getContactsApiUrl());
|
||||||
|
}
|
||||||
|
return _client;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contact entity from Contacts backend
|
* Contact entity from Contacts backend
|
||||||
|
|
@ -55,7 +75,7 @@ export const contactsService = {
|
||||||
* Get favorite contacts
|
* Get favorite contacts
|
||||||
*/
|
*/
|
||||||
async getFavoriteContacts(limit: number = 5): Promise<ApiResult<Contact[]>> {
|
async getFavoriteContacts(limit: number = 5): Promise<ApiResult<Contact[]>> {
|
||||||
const result = await client.get<Contact[]>(`/contacts?isFavorite=true&limit=${limit}`);
|
const result = await getClient().get<Contact[]>(`/contacts?isFavorite=true&limit=${limit}`);
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -63,7 +83,7 @@ export const contactsService = {
|
||||||
* Get recent contacts (by updatedAt)
|
* Get recent contacts (by updatedAt)
|
||||||
*/
|
*/
|
||||||
async getRecentContacts(limit: number = 5): Promise<ApiResult<Contact[]>> {
|
async getRecentContacts(limit: number = 5): Promise<ApiResult<Contact[]>> {
|
||||||
const result = await client.get<Contact[]>(`/contacts?limit=${limit}`);
|
const result = await getClient().get<Contact[]>(`/contacts?limit=${limit}`);
|
||||||
|
|
||||||
if (result.error || !result.data) {
|
if (result.error || !result.data) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -82,7 +102,7 @@ export const contactsService = {
|
||||||
* Get contacts with upcoming birthdays
|
* Get contacts with upcoming birthdays
|
||||||
*/
|
*/
|
||||||
async getUpcomingBirthdays(days: number = 30): Promise<ApiResult<Contact[]>> {
|
async getUpcomingBirthdays(days: number = 30): Promise<ApiResult<Contact[]>> {
|
||||||
const result = await client.get<Contact[]>('/contacts');
|
const result = await getClient().get<Contact[]>('/contacts');
|
||||||
|
|
||||||
if (result.error || !result.data) {
|
if (result.error || !result.data) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -113,7 +133,7 @@ export const contactsService = {
|
||||||
* Get contact count
|
* Get contact count
|
||||||
*/
|
*/
|
||||||
async getContactCount(): Promise<ApiResult<{ total: number; favorites: number }>> {
|
async getContactCount(): Promise<ApiResult<{ total: number; favorites: number }>> {
|
||||||
const result = await client.get<Contact[]>('/contacts');
|
const result = await getClient().get<Contact[]>('/contacts');
|
||||||
|
|
||||||
if (result.error || !result.data) {
|
if (result.error || !result.data) {
|
||||||
return { data: null, error: result.error };
|
return { data: null, error: result.error };
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,32 @@
|
||||||
* Fetches tasks from the Todo backend for dashboard widgets.
|
* Fetches tasks from the Todo backend for dashboard widgets.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { browser } from '$app/environment';
|
||||||
import { createApiClient, type ApiResult } from '../base-client';
|
import { createApiClient, type ApiResult } from '../base-client';
|
||||||
|
|
||||||
// Backend URL - falls back to localhost for development
|
// Get Todo API URL dynamically at runtime
|
||||||
const TODO_API_URL = import.meta.env.PUBLIC_TODO_API_URL || 'http://localhost:3017/api/v1';
|
function getTodoApiUrl(): string {
|
||||||
|
if (browser && typeof window !== 'undefined') {
|
||||||
|
// Client-side: use injected window variable (set by hooks.server.ts)
|
||||||
|
const injectedUrl = (window as unknown as { __PUBLIC_TODO_API_URL__?: string })
|
||||||
|
.__PUBLIC_TODO_API_URL__;
|
||||||
|
if (injectedUrl) {
|
||||||
|
return `${injectedUrl}/api/v1`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback for local development
|
||||||
|
return 'http://localhost:3018/api/v1';
|
||||||
|
}
|
||||||
|
|
||||||
const client = createApiClient(TODO_API_URL);
|
// Lazy-initialized client to ensure we get the correct URL at runtime
|
||||||
|
let _client: ReturnType<typeof createApiClient> | null = null;
|
||||||
|
|
||||||
|
function getClient() {
|
||||||
|
if (!_client) {
|
||||||
|
_client = createApiClient(getTodoApiUrl());
|
||||||
|
}
|
||||||
|
return _client;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task entity from Todo backend
|
* Task entity from Todo backend
|
||||||
|
|
@ -49,7 +69,7 @@ export const todoService = {
|
||||||
* Get today's tasks
|
* Get today's tasks
|
||||||
*/
|
*/
|
||||||
async getTodayTasks(): Promise<ApiResult<Task[]>> {
|
async getTodayTasks(): Promise<ApiResult<Task[]>> {
|
||||||
const result = await client.get<{ tasks: Task[] }>('/tasks/today');
|
const result = await getClient().get<{ tasks: Task[] }>('/tasks/today');
|
||||||
|
|
||||||
if (result.error || !result.data) {
|
if (result.error || !result.data) {
|
||||||
return { data: null, error: result.error };
|
return { data: null, error: result.error };
|
||||||
|
|
@ -62,7 +82,7 @@ export const todoService = {
|
||||||
* Get upcoming tasks for the next N days
|
* Get upcoming tasks for the next N days
|
||||||
*/
|
*/
|
||||||
async getUpcomingTasks(days: number = 7): Promise<ApiResult<Task[]>> {
|
async getUpcomingTasks(days: number = 7): Promise<ApiResult<Task[]>> {
|
||||||
const result = await client.get<{ tasks: Task[] }>(`/tasks/upcoming?days=${days}`);
|
const result = await getClient().get<{ tasks: Task[] }>(`/tasks/upcoming?days=${days}`);
|
||||||
|
|
||||||
if (result.error || !result.data) {
|
if (result.error || !result.data) {
|
||||||
return { data: null, error: result.error };
|
return { data: null, error: result.error };
|
||||||
|
|
@ -75,7 +95,7 @@ export const todoService = {
|
||||||
* Get inbox tasks (unassigned to project)
|
* Get inbox tasks (unassigned to project)
|
||||||
*/
|
*/
|
||||||
async getInboxTasks(): Promise<ApiResult<Task[]>> {
|
async getInboxTasks(): Promise<ApiResult<Task[]>> {
|
||||||
const result = await client.get<{ tasks: Task[] }>('/tasks/inbox');
|
const result = await getClient().get<{ tasks: Task[] }>('/tasks/inbox');
|
||||||
|
|
||||||
if (result.error || !result.data) {
|
if (result.error || !result.data) {
|
||||||
return { data: null, error: result.error };
|
return { data: null, error: result.error };
|
||||||
|
|
@ -88,7 +108,7 @@ export const todoService = {
|
||||||
* Get all projects
|
* Get all projects
|
||||||
*/
|
*/
|
||||||
async getProjects(): Promise<ApiResult<Project[]>> {
|
async getProjects(): Promise<ApiResult<Project[]>> {
|
||||||
const result = await client.get<{ projects: Project[] }>('/projects');
|
const result = await getClient().get<{ projects: Project[] }>('/projects');
|
||||||
|
|
||||||
if (result.error || !result.data) {
|
if (result.error || !result.data) {
|
||||||
return { data: null, error: result.error };
|
return { data: null, error: result.error };
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,28 @@ interface ApiError {
|
||||||
statusCode: number;
|
statusCode: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the backend URL, preferring runtime-injected value in browser
|
||||||
|
* This allows Docker to inject PUBLIC_BACKEND_URL_CLIENT at runtime
|
||||||
|
* instead of using the build-time PUBLIC_BACKEND_URL
|
||||||
|
*/
|
||||||
|
function getBackendUrl(): string {
|
||||||
|
if (browser && typeof window !== 'undefined') {
|
||||||
|
const runtimeUrl = (window as Window & { __PUBLIC_BACKEND_URL__?: string })
|
||||||
|
.__PUBLIC_BACKEND_URL__;
|
||||||
|
if (runtimeUrl) {
|
||||||
|
return runtimeUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PUBLIC_BACKEND_URL || 'http://localhost:3018';
|
||||||
|
}
|
||||||
|
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
private baseUrl: string;
|
|
||||||
private accessToken: string | null = null;
|
private accessToken: string | null = null;
|
||||||
|
|
||||||
constructor() {
|
// Use getter to evaluate URL at request time (browser may hydrate after construction)
|
||||||
this.baseUrl = PUBLIC_BACKEND_URL || 'http://localhost:3018';
|
private get baseUrl(): string {
|
||||||
|
return getBackendUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
setAccessToken(token: string | null) {
|
setAccessToken(token: string | null) {
|
||||||
|
|
|
||||||
|
|
@ -173,8 +173,16 @@ services:
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: staging
|
NODE_ENV: staging
|
||||||
PORT: 5173
|
PORT: 5173
|
||||||
|
# Auth URLs
|
||||||
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||||
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: http://46.224.108.214:3001
|
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: http://46.224.108.214:3001
|
||||||
|
# Backend URLs for dashboard widgets
|
||||||
|
PUBLIC_TODO_API_URL: http://todo-backend:3018
|
||||||
|
PUBLIC_TODO_API_URL_CLIENT: http://46.224.108.214:3018
|
||||||
|
PUBLIC_CALENDAR_API_URL: http://calendar-backend:3016
|
||||||
|
PUBLIC_CALENDAR_API_URL_CLIENT: http://46.224.108.214:3016
|
||||||
|
PUBLIC_CLOCK_API_URL: http://clock-backend:3017
|
||||||
|
PUBLIC_CLOCK_API_URL_CLIENT: http://46.224.108.214:3017
|
||||||
ports:
|
ports:
|
||||||
- "5173:5173"
|
- "5173:5173"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue