mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 18:41:08 +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.
|
||||
*/
|
||||
|
||||
// Auth URL
|
||||
const PUBLIC_MANA_CORE_AUTH_URL_CLIENT =
|
||||
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 }) => {
|
||||
return resolve(event, {
|
||||
transformPageChunk: ({ html }) => {
|
||||
const envScript = `<script>
|
||||
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>`;
|
||||
return html.replace('<head>', `<head>${envScript}`);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,12 +4,32 @@
|
|||
* Fetches events from the Calendar backend for dashboard widgets.
|
||||
*/
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
import { createApiClient, type ApiResult } from '../base-client';
|
||||
|
||||
// Backend URL - falls back to localhost for development
|
||||
const CALENDAR_API_URL = import.meta.env.PUBLIC_CALENDAR_API_URL || 'http://localhost:3014/api/v1';
|
||||
// Get Calendar API URL dynamically at runtime
|
||||
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
|
||||
|
|
@ -59,7 +79,7 @@ export const calendarService = {
|
|||
const startDate = new Date().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}`
|
||||
);
|
||||
|
||||
|
|
@ -75,7 +95,7 @@ export const calendarService = {
|
|||
*/
|
||||
async getTodayEvents(): Promise<ApiResult<CalendarEvent[]>> {
|
||||
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}`
|
||||
);
|
||||
|
||||
|
|
@ -90,7 +110,7 @@ export const calendarService = {
|
|||
* Get all calendars
|
||||
*/
|
||||
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) {
|
||||
return { data: null, error: result.error };
|
||||
|
|
@ -109,7 +129,7 @@ export const calendarService = {
|
|||
const startDate = new Date().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}`
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,32 @@
|
|||
* Fetches contacts from the Contacts backend for dashboard widgets.
|
||||
*/
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
import { createApiClient, type ApiResult } from '../base-client';
|
||||
|
||||
// Backend URL - falls back to localhost for development
|
||||
const CONTACTS_API_URL = import.meta.env.PUBLIC_CONTACTS_API_URL || 'http://localhost:3015/api/v1';
|
||||
// Get Contacts API URL dynamically at runtime
|
||||
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
|
||||
|
|
@ -55,7 +75,7 @@ export const contactsService = {
|
|||
* Get favorite contacts
|
||||
*/
|
||||
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;
|
||||
},
|
||||
|
||||
|
|
@ -63,7 +83,7 @@ export const contactsService = {
|
|||
* Get recent contacts (by updatedAt)
|
||||
*/
|
||||
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) {
|
||||
return result;
|
||||
|
|
@ -82,7 +102,7 @@ export const contactsService = {
|
|||
* Get contacts with upcoming birthdays
|
||||
*/
|
||||
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) {
|
||||
return result;
|
||||
|
|
@ -113,7 +133,7 @@ export const contactsService = {
|
|||
* Get contact count
|
||||
*/
|
||||
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) {
|
||||
return { data: null, error: result.error };
|
||||
|
|
|
|||
|
|
@ -4,12 +4,32 @@
|
|||
* Fetches tasks from the Todo backend for dashboard widgets.
|
||||
*/
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
import { createApiClient, type ApiResult } from '../base-client';
|
||||
|
||||
// Backend URL - falls back to localhost for development
|
||||
const TODO_API_URL = import.meta.env.PUBLIC_TODO_API_URL || 'http://localhost:3017/api/v1';
|
||||
// Get Todo API URL dynamically at runtime
|
||||
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
|
||||
|
|
@ -49,7 +69,7 @@ export const todoService = {
|
|||
* Get today's tasks
|
||||
*/
|
||||
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) {
|
||||
return { data: null, error: result.error };
|
||||
|
|
@ -62,7 +82,7 @@ export const todoService = {
|
|||
* Get upcoming tasks for the next N days
|
||||
*/
|
||||
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) {
|
||||
return { data: null, error: result.error };
|
||||
|
|
@ -75,7 +95,7 @@ export const todoService = {
|
|||
* Get inbox tasks (unassigned to project)
|
||||
*/
|
||||
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) {
|
||||
return { data: null, error: result.error };
|
||||
|
|
@ -88,7 +108,7 @@ export const todoService = {
|
|||
* Get all projects
|
||||
*/
|
||||
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) {
|
||||
return { data: null, error: result.error };
|
||||
|
|
|
|||
|
|
@ -12,12 +12,28 @@ interface ApiError {
|
|||
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 {
|
||||
private baseUrl: string;
|
||||
private accessToken: string | null = null;
|
||||
|
||||
constructor() {
|
||||
this.baseUrl = PUBLIC_BACKEND_URL || 'http://localhost:3018';
|
||||
// Use getter to evaluate URL at request time (browser may hydrate after construction)
|
||||
private get baseUrl(): string {
|
||||
return getBackendUrl();
|
||||
}
|
||||
|
||||
setAccessToken(token: string | null) {
|
||||
|
|
|
|||
|
|
@ -173,8 +173,16 @@ services:
|
|||
environment:
|
||||
NODE_ENV: staging
|
||||
PORT: 5173
|
||||
# Auth URLs
|
||||
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth: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:
|
||||
- "5173:5173"
|
||||
healthcheck:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue