mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 18:41:08 +02:00
✨ feat(cors): add cross-app communication bundle
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 <noreply@anthropic.com>
This commit is contained in:
parent
4c44764838
commit
3504172e60
6 changed files with 283 additions and 12 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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<string[]>('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<string>('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(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue