diff --git a/apps/calendar/apps/web/src/hooks.server.ts b/apps/calendar/apps/web/src/hooks.server.ts
index 5215d05b8..b63bf26fe 100644
--- a/apps/calendar/apps/web/src/hooks.server.ts
+++ b/apps/calendar/apps/web/src/hooks.server.ts
@@ -13,6 +13,10 @@ const PUBLIC_BACKEND_URL_CLIENT =
process.env.PUBLIC_BACKEND_URL_CLIENT || process.env.PUBLIC_BACKEND_URL || '';
const PUBLIC_STT_URL = process.env.PUBLIC_STT_URL || 'https://stt-api.mana.how';
+// Cross-app integration URLs (for todo and contacts APIs)
+const PUBLIC_TODO_BACKEND_URL = process.env.PUBLIC_TODO_BACKEND_URL || 'http://localhost:3018';
+const PUBLIC_CONTACTS_API_URL = process.env.PUBLIC_CONTACTS_API_URL || 'http://localhost:3015';
+
export const handle: Handle = async ({ event, resolve }) => {
return resolve(event, {
transformPageChunk: ({ html }) => {
@@ -22,6 +26,8 @@ export const handle: Handle = async ({ event, resolve }) => {
window.__PUBLIC_MANA_CORE_AUTH_URL__ = "${PUBLIC_MANA_CORE_AUTH_URL_CLIENT}";
window.__PUBLIC_BACKEND_URL__ = "${PUBLIC_BACKEND_URL_CLIENT}";
window.__PUBLIC_STT_URL__ = "${PUBLIC_STT_URL}";
+window.__PUBLIC_TODO_BACKEND_URL__ = "${PUBLIC_TODO_BACKEND_URL}";
+window.__PUBLIC_CONTACTS_API_URL__ = "${PUBLIC_CONTACTS_API_URL}";
`;
return html.replace('
', `${envScript}`);
},
diff --git a/apps/calendar/apps/web/src/lib/api/birthdays.ts b/apps/calendar/apps/web/src/lib/api/birthdays.ts
index 9734a0cd8..224712a90 100644
--- a/apps/calendar/apps/web/src/lib/api/birthdays.ts
+++ b/apps/calendar/apps/web/src/lib/api/birthdays.ts
@@ -3,19 +3,44 @@
* Allows Calendar app to fetch contact birthdays for display
*/
+import { browser } from '$app/environment';
import { env } from '$env/dynamic/public';
import { createApiClient } from '@manacore/shared-api-client';
import { authStore } from '$lib/stores/auth.svelte';
-const CONTACTS_API_BASE = env.PUBLIC_CONTACTS_API_URL || 'http://localhost:3015';
+// Get contacts API base URL from injected window variable (browser) or env (SSR)
+function getContactsApiBase(): string {
+ if (browser && typeof window !== 'undefined') {
+ const injectedUrl = (window as unknown as { __PUBLIC_CONTACTS_API_URL__?: string })
+ .__PUBLIC_CONTACTS_API_URL__;
+ if (injectedUrl) return injectedUrl;
+ }
+ return env.PUBLIC_CONTACTS_API_URL || 'http://localhost:3015';
+}
-const contactsClient = createApiClient({
- baseUrl: CONTACTS_API_BASE,
- apiPrefix: '/api/v1',
- getAuthToken: () => authStore.getValidToken(),
- timeout: 30000,
- debug: import.meta.env.DEV,
-});
+let _contactsClient: ReturnType | null = null;
+
+function getContactsClient() {
+ if (!_contactsClient) {
+ _contactsClient = createApiClient({
+ baseUrl: getContactsApiBase(),
+ apiPrefix: '/api/v1',
+ getAuthToken: () => authStore.getValidToken(),
+ timeout: 30000,
+ debug: import.meta.env.DEV,
+ });
+ }
+ return _contactsClient;
+}
+
+// For backwards compatibility
+const contactsClient = {
+ get: (endpoint: string) => getContactsClient().get(endpoint),
+ post: (endpoint: string, body?: unknown) => getContactsClient().post(endpoint, body),
+ put: (endpoint: string, body?: unknown) => getContactsClient().put(endpoint, body),
+ patch: (endpoint: string, body?: unknown) => getContactsClient().patch(endpoint, body),
+ delete: (endpoint: string) => getContactsClient().delete(endpoint),
+};
// ============================================
// Types for Birthday Integration
diff --git a/apps/calendar/apps/web/src/lib/api/todos.ts b/apps/calendar/apps/web/src/lib/api/todos.ts
index a7c3328b0..4dea3385b 100644
--- a/apps/calendar/apps/web/src/lib/api/todos.ts
+++ b/apps/calendar/apps/web/src/lib/api/todos.ts
@@ -3,19 +3,44 @@
* Allows Calendar app to fetch/manage todos from the Todo service
*/
+import { browser } from '$app/environment';
import { env } from '$env/dynamic/public';
import { createApiClient, buildQueryString, type ApiResult } from '@manacore/shared-api-client';
import { authStore } from '$lib/stores/auth.svelte';
-const TODO_API_BASE = env.PUBLIC_TODO_BACKEND_URL || 'http://localhost:3018';
+// Get todo API base URL from injected window variable (browser) or env (SSR)
+function getTodoApiBase(): string {
+ if (browser && typeof window !== 'undefined') {
+ const injectedUrl = (window as unknown as { __PUBLIC_TODO_BACKEND_URL__?: string })
+ .__PUBLIC_TODO_BACKEND_URL__;
+ if (injectedUrl) return injectedUrl;
+ }
+ return env.PUBLIC_TODO_BACKEND_URL || 'http://localhost:3018';
+}
-const todoClient = createApiClient({
- baseUrl: TODO_API_BASE,
- apiPrefix: '/api/v1',
- getAuthToken: () => authStore.getValidToken(),
- timeout: 30000,
- debug: import.meta.env.DEV,
-});
+let _todoClient: ReturnType | null = null;
+
+function getTodoClient() {
+ if (!_todoClient) {
+ _todoClient = createApiClient({
+ baseUrl: getTodoApiBase(),
+ apiPrefix: '/api/v1',
+ getAuthToken: () => authStore.getValidToken(),
+ timeout: 30000,
+ debug: import.meta.env.DEV,
+ });
+ }
+ return _todoClient;
+}
+
+// For backwards compatibility
+const todoClient = {
+ get: (endpoint: string) => getTodoClient().get(endpoint),
+ post: (endpoint: string, body?: unknown) => getTodoClient().post(endpoint, body),
+ put: (endpoint: string, body?: unknown) => getTodoClient().put(endpoint, body),
+ patch: (endpoint: string, body?: unknown) => getTodoClient().patch(endpoint, body),
+ delete: (endpoint: string) => getTodoClient().delete(endpoint),
+};
// ============================================
// Types (mirrored from @todo/shared for cross-app use)