From 3504172e6072c32acd3552d06bd73a4f55baa332 Mon Sep 17 00:00:00 2001 From: Wuesteon Date: Wed, 17 Dec 2025 17:57:06 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(cors):=20add=20cross-app=20com?= =?UTF-8?q?munication=20bundle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add includeAllManaApps option to enable all ManaCore apps to communicate with each other without manually listing each app's domains. **Changes:** - Added MANACORE_STAGING_ORIGINS, MANACORE_PRODUCTION_ORIGINS, and MANACORE_ALL_APP_ORIGINS constants - Added includeAllManaApps flag to CorsConfigOptions interface - Updated createCorsConfig() and createCorsConfigWithCallback() to support the new flag - Updated mana-core-auth to use includeAllManaApps: true (auth needs to be accessible by all apps) - Updated documentation with usage examples and decision matrix **Benefits:** - One-line configuration enables cross-app communication - Automatically stays in sync as new apps are added - No need to manually update CORS_ORIGINS for each app - Works in both staging and production environments 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/CORS_CONFIGURATION_GUIDE.md | 137 ++++++++++++++++++ .../shared-nestjs-cors/src/cors-config.ts | 125 +++++++++++++++- packages/shared-nestjs-cors/src/index.ts | 8 +- pnpm-lock.yaml | 3 + services/mana-core-auth/package.json | 1 + services/mana-core-auth/src/main.ts | 21 +-- 6 files changed, 283 insertions(+), 12 deletions(-) diff --git a/docs/CORS_CONFIGURATION_GUIDE.md b/docs/CORS_CONFIGURATION_GUIDE.md index f40539183..e386b8892 100644 --- a/docs/CORS_CONFIGURATION_GUIDE.md +++ b/docs/CORS_CONFIGURATION_GUIDE.md @@ -36,6 +36,7 @@ packages/shared-nestjs-cors/ ✅ **Automatic development origins** - Works in dev without configuration ✅ **Staging/production via env var** - `CORS_ORIGINS` for deployed environments +✅ **Cross-app communication bundle** - Enable all ManaCore apps with one flag ✅ **Mobile app support** - Includes `exp://` and custom protocols ✅ **Prevents duplicates** - Deduplicates origin lists ✅ **Consistent security** - Same methods, headers, credentials across all apps @@ -55,6 +56,10 @@ packages/shared-nestjs-cors/ ### 2. Update main.ts +#### Option A: Basic Setup (Single App) + +For apps that only need to be accessed by their own frontend: + ```typescript // apps/{app}/apps/backend/src/main.ts import { NestFactory } from '@nestjs/core'; @@ -76,6 +81,38 @@ async function bootstrap() { bootstrap(); ``` +#### Option B: Cross-App Communication (Recommended for Shared Services) + +For backends that need to communicate with multiple ManaCore apps: + +```typescript +// apps/{app}/apps/backend/src/main.ts +import { NestFactory } from '@nestjs/core'; +import { createCorsConfig } from '@manacore/shared-nestjs-cors'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + + // Enable CORS with ALL ManaCore apps (staging + production) + app.enableCors( + createCorsConfig({ + corsOriginsEnv: process.env.CORS_ORIGINS, + includeAllManaApps: true, // 🎯 Enables cross-app communication + }) + ); + + await app.listen(3000); +} +bootstrap(); +``` + +**When to use `includeAllManaApps: true`:** +- ✅ Shared services (auth, notifications, file storage) +- ✅ APIs that need to be called from multiple apps +- ✅ Apps with cross-app navigation or embedded widgets +- ❌ Simple single-purpose apps with dedicated frontend (saves bandwidth) + ### 3. Configure Staging Environment ```yaml @@ -88,6 +125,8 @@ chat-backend: ## Default Origins +### Development Origins + The utility automatically includes these development origins: ```typescript @@ -103,6 +142,104 @@ The utility automatically includes these development origins: ] ``` +### ManaCore App Bundles + +When using `includeAllManaApps: true`, the following origin bundles are automatically included: + +#### Staging Bundle (`MANACORE_STAGING_ORIGINS`) + +```typescript +[ + 'https://staging.manacore.ai', // Main web + 'https://auth.staging.manacore.ai', // Auth service + 'https://chat.staging.manacore.ai', // Chat app + 'https://chat-api.staging.manacore.ai', + 'https://picture.staging.manacore.ai', // Picture app + 'https://picture-api.staging.manacore.ai', + 'https://zitare.staging.manacore.ai', // Zitare app + 'https://zitare-api.staging.manacore.ai', + 'https://contacts.staging.manacore.ai', // Contacts app + 'https://contacts-api.staging.manacore.ai', + 'https://calendar.staging.manacore.ai', // Calendar app + 'https://calendar-api.staging.manacore.ai', + 'https://clock.staging.manacore.ai', // Clock app + 'https://clock-api.staging.manacore.ai', + 'https://todo.staging.manacore.ai', // Todo app + 'https://todo-api.staging.manacore.ai', +] +``` + +#### Production Bundle (`MANACORE_PRODUCTION_ORIGINS`) + +```typescript +[ + 'https://manacore.ai', // Main web + 'https://auth.manacore.ai', // Auth service + 'https://chat.manacore.ai', // Chat app + 'https://chat-api.manacore.ai', + 'https://picture.manacore.ai', // Picture app + 'https://picture-api.manacore.ai', + // ... all other production apps +] +``` + +#### Combined Bundle (`MANACORE_ALL_APP_ORIGINS`) + +When `includeAllManaApps: true`, both staging and production bundles are included automatically. + +**Benefits:** +- 🎯 One-line configuration enables all cross-app communication +- 🔄 Automatically stays in sync as new apps are added +- 🚀 No need to manually update CORS_ORIGINS for each app +- ✅ Works in both staging and production environments + +## Cross-App Communication + +### Problem: Apps Need to Call Each Other + +In a microservices architecture, apps often need to communicate: +- **Chat app** fetches user profiles from **Contacts API** +- **Calendar app** sends notifications via **Notifications service** +- **Picture app** uses **Auth service** for authentication + +Without proper CORS setup, each backend would need to manually list every other app: + +```yaml +# ❌ OLD WAY: Manually list every app (tedious and error-prone) +chat-backend: + environment: + CORS_ORIGINS: https://chat.staging.manacore.ai,https://contacts.staging.manacore.ai,https://calendar.staging.manacore.ai,https://picture.staging.manacore.ai,https://zitare.staging.manacore.ai,... +``` + +### Solution: Use `includeAllManaApps: true` + +Enable cross-app communication with one flag: + +```typescript +// ✅ NEW WAY: One flag enables all ManaCore apps +app.enableCors( + createCorsConfig({ + corsOriginsEnv: process.env.CORS_ORIGINS, + includeAllManaApps: true, // 🎯 Automatically includes all apps + }) +); +``` + +This automatically allows requests from: +- All staging apps (`*.staging.manacore.ai`) +- All production apps (`*.manacore.ai`) +- All development ports (`localhost:*`) + +### When to Use Cross-App Bundle + +| Use Case | `includeAllManaApps` | +|----------|----------------------| +| **Shared services** (Auth, Notifications, Storage) | ✅ `true` | +| **Public APIs** called by multiple apps | ✅ `true` | +| **Apps with embedded widgets** from other apps | ✅ `true` | +| **Simple apps** with dedicated frontend only | ❌ `false` | +| **Third-party integrations** (webhooks) | ❌ `false` (use `additionalOrigins`) | + ## Deployment Checklist When deploying a new app to staging, ensure: diff --git a/packages/shared-nestjs-cors/src/cors-config.ts b/packages/shared-nestjs-cors/src/cors-config.ts index 5537f807e..517936296 100644 --- a/packages/shared-nestjs-cors/src/cors-config.ts +++ b/packages/shared-nestjs-cors/src/cors-config.ts @@ -17,6 +17,12 @@ export interface CorsConfigOptions { * Additional origins to always allow (e.g., for mobile apps). */ additionalOrigins?: string[]; + + /** + * Include all ManaCore app origins for cross-app communication. + * When true, automatically includes all staging and production URLs. + */ + includeAllManaApps?: boolean; } /** @@ -49,6 +55,91 @@ const DEFAULT_DEV_ORIGINS = [ 'exp://localhost:8081', // Expo mobile (exp:// protocol) ]; +/** + * All ManaCore staging app origins. + * Use this bundle to allow cross-app communication in staging environment. + */ +export const MANACORE_STAGING_ORIGINS = [ + // Main apps + 'https://staging.manacore.ai', // Main web + 'https://auth.staging.manacore.ai', // Auth service + + // Chat app + 'https://chat.staging.manacore.ai', + 'https://chat-api.staging.manacore.ai', + + // Picture app + 'https://picture.staging.manacore.ai', + 'https://picture-api.staging.manacore.ai', + + // Zitare app + 'https://zitare.staging.manacore.ai', + 'https://zitare-api.staging.manacore.ai', + + // Contacts app + 'https://contacts.staging.manacore.ai', + 'https://contacts-api.staging.manacore.ai', + + // Calendar app + 'https://calendar.staging.manacore.ai', + 'https://calendar-api.staging.manacore.ai', + + // Clock app + 'https://clock.staging.manacore.ai', + 'https://clock-api.staging.manacore.ai', + + // Todo app + 'https://todo.staging.manacore.ai', + 'https://todo-api.staging.manacore.ai', +]; + +/** + * All ManaCore production app origins. + * Use this bundle to allow cross-app communication in production environment. + */ +export const MANACORE_PRODUCTION_ORIGINS = [ + // Main apps + 'https://manacore.ai', // Main web + 'https://auth.manacore.ai', // Auth service + + // Chat app + 'https://chat.manacore.ai', + 'https://chat-api.manacore.ai', + + // Picture app + 'https://picture.manacore.ai', + 'https://picture-api.manacore.ai', + + // Zitare app + 'https://zitare.manacore.ai', + 'https://zitare-api.manacore.ai', + + // Contacts app + 'https://contacts.manacore.ai', + 'https://contacts-api.manacore.ai', + + // Calendar app + 'https://calendar.manacore.ai', + 'https://calendar-api.manacore.ai', + + // Clock app + 'https://clock.manacore.ai', + 'https://clock-api.manacore.ai', + + // Todo app + 'https://todo.manacore.ai', + 'https://todo-api.manacore.ai', +]; + +/** + * Combined bundle of all ManaCore app origins (staging + production). + * Use this for maximum cross-app compatibility across all environments. + */ +export const MANACORE_ALL_APP_ORIGINS = [ + ...MANACORE_STAGING_ORIGINS, + ...MANACORE_PRODUCTION_ORIGINS, +]; + /** * Creates a standardized CORS configuration for NestJS apps. * @@ -68,6 +159,14 @@ const DEFAULT_DEV_ORIGINS = [ * })); * ``` * + * ### With cross-app communication bundle (enables all ManaCore apps) + * ```typescript + * app.enableCors(createCorsConfig({ + * corsOriginsEnv: process.env.CORS_ORIGINS, + * includeAllManaApps: true // Includes all staging + production app URLs + * })); + * ``` + * * ### With custom development origins * ```typescript * app.enableCors(createCorsConfig({ @@ -93,6 +192,7 @@ const DEFAULT_DEV_ORIGINS = [ * * ## Staging/Production Setup * + * ### Simple setup (no cross-app communication needed) * In docker-compose.staging.yml: * ```yaml * chat-backend: @@ -100,6 +200,15 @@ const DEFAULT_DEV_ORIGINS = [ * CORS_ORIGINS: https://chat.staging.manacore.ai,https://chat-api.staging.manacore.ai * ``` * + * ### Cross-app setup (allow all ManaCore apps to communicate) + * ```typescript + * // main.ts + * app.enableCors(createCorsConfig({ + * corsOriginsEnv: process.env.CORS_ORIGINS, + * includeAllManaApps: true // No need to list each app individually + * })); + * ``` + * * @param options - Configuration options * @returns NestJS CORS configuration object */ @@ -108,6 +217,7 @@ export function createCorsConfig(options: CorsConfigOptions = {}): CorsOptions { corsOriginsEnv, developmentOrigins = DEFAULT_DEV_ORIGINS, additionalOrigins = [], + includeAllManaApps = false, } = options; // Parse CORS_ORIGINS from environment @@ -119,7 +229,12 @@ export function createCorsConfig(options: CorsConfigOptions = {}): CorsOptions { : []; // Combine all origins - const allOrigins = [...envOrigins, ...developmentOrigins, ...additionalOrigins]; + const allOrigins = [ + ...envOrigins, + ...developmentOrigins, + ...additionalOrigins, + ...(includeAllManaApps ? MANACORE_ALL_APP_ORIGINS : []), + ]; // Remove duplicates const uniqueOrigins = Array.from(new Set(allOrigins)); @@ -145,6 +260,7 @@ export function createCorsConfigWithCallback(options: CorsConfigOptions = {}): C corsOriginsEnv, developmentOrigins = DEFAULT_DEV_ORIGINS, additionalOrigins = [], + includeAllManaApps = false, } = options; const envOrigins = corsOriginsEnv @@ -154,7 +270,12 @@ export function createCorsConfigWithCallback(options: CorsConfigOptions = {}): C .filter(Boolean) : []; - const allOrigins = [...envOrigins, ...developmentOrigins, ...additionalOrigins]; + const allOrigins = [ + ...envOrigins, + ...developmentOrigins, + ...additionalOrigins, + ...(includeAllManaApps ? MANACORE_ALL_APP_ORIGINS : []), + ]; const uniqueOrigins = Array.from(new Set(allOrigins)); return { diff --git a/packages/shared-nestjs-cors/src/index.ts b/packages/shared-nestjs-cors/src/index.ts index 2f1ac479d..153cad233 100644 --- a/packages/shared-nestjs-cors/src/index.ts +++ b/packages/shared-nestjs-cors/src/index.ts @@ -1,2 +1,8 @@ -export { createCorsConfig, createCorsConfigWithCallback } from './cors-config'; +export { + createCorsConfig, + createCorsConfigWithCallback, + MANACORE_STAGING_ORIGINS, + MANACORE_PRODUCTION_ORIGINS, + MANACORE_ALL_APP_ORIGINS, +} from './cors-config'; export type { CorsConfigOptions } from './cors-config'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa0cb1c55..cb979ad01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4507,6 +4507,9 @@ importers: '@google/generative-ai': specifier: ^0.24.1 version: 0.24.1 + '@manacore/shared-nestjs-cors': + specifier: workspace:* + version: link:../../packages/shared-nestjs-cors '@nestjs/common': specifier: ^10.4.15 version: 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) diff --git a/services/mana-core-auth/package.json b/services/mana-core-auth/package.json index c8e4843a0..655be43ec 100644 --- a/services/mana-core-auth/package.json +++ b/services/mana-core-auth/package.json @@ -23,6 +23,7 @@ "dependencies": { "@getbrevo/brevo": "^3.0.1", "@google/generative-ai": "^0.24.1", + "@manacore/shared-nestjs-cors": "workspace:*", "@nestjs/common": "^10.4.15", "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.4.15", diff --git a/services/mana-core-auth/src/main.ts b/services/mana-core-auth/src/main.ts index 8245fd05e..177faf67f 100644 --- a/services/mana-core-auth/src/main.ts +++ b/services/mana-core-auth/src/main.ts @@ -3,6 +3,7 @@ import { ValidationPipe } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import helmet from 'helmet'; import cookieParser from 'cookie-parser'; +import { createCorsConfig } from '@manacore/shared-nestjs-cors'; import { AppModule } from './app.module'; async function bootstrap() { @@ -19,15 +20,17 @@ async function bootstrap() { ); app.use(cookieParser()); - // CORS configuration - const corsOrigins = configService.get('cors.origin') || []; - console.log('📋 CORS Origins configured:', corsOrigins); - app.enableCors({ - origin: corsOrigins, - credentials: true, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'X-App-Id'], - }); + // CORS configuration with cross-app communication + // Auth service needs to be accessible by ALL ManaCore apps + const corsOriginsEnv = configService.get('cors.origin'); + console.log('📋 CORS Origins from env:', corsOriginsEnv); + app.enableCors( + createCorsConfig({ + corsOriginsEnv, + includeAllManaApps: true, // 🎯 Enable all ManaCore apps to authenticate + additionalOrigins: [], // Keep X-App-Id support for custom headers + }) + ); // Global validation pipe app.useGlobalPipes(