mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 00:01:10 +02:00
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>
404 lines
12 KiB
Markdown
404 lines
12 KiB
Markdown
# CORS Configuration Guide
|
|
|
|
## Problem
|
|
|
|
Every deployed app on staging was encountering CORS errors:
|
|
|
|
```
|
|
Access to fetch at 'https://chat-api.staging.manacore.ai/api/v1/...' from origin
|
|
'https://chat.staging.manacore.ai' has been blocked by CORS policy:
|
|
No 'Access-Control-Allow-Origin' header is present on the requested resource.
|
|
```
|
|
|
|
### Root Causes
|
|
|
|
1. **Missing `CORS_ORIGINS` environment variable** in docker-compose.staging.yml
|
|
2. **Inconsistent CORS configuration** across backends (different patterns in each main.ts)
|
|
3. **No centralized CORS management** leading to missing configurations during deployment
|
|
|
|
## Solution
|
|
|
|
Created `@manacore/shared-nestjs-cors` package providing standardized CORS configuration for all backends.
|
|
|
|
### Package Structure
|
|
|
|
```
|
|
packages/shared-nestjs-cors/
|
|
├── src/
|
|
│ ├── cors-config.ts # CORS configuration utilities
|
|
│ └── index.ts # Public exports
|
|
├── package.json
|
|
├── tsconfig.json
|
|
└── README.md
|
|
```
|
|
|
|
### Key Features
|
|
|
|
✅ **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
|
|
|
|
## Usage
|
|
|
|
### 1. Add Package Dependency
|
|
|
|
```json
|
|
// apps/{app}/apps/backend/package.json
|
|
{
|
|
"dependencies": {
|
|
"@manacore/shared-nestjs-cors": "workspace:*"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 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';
|
|
import { createCorsConfig } from '@manacore/shared-nestjs-cors';
|
|
import { AppModule } from './app.module';
|
|
|
|
async function bootstrap() {
|
|
const app = await NestFactory.create(AppModule);
|
|
|
|
// Enable CORS with centralized configuration
|
|
app.enableCors(
|
|
createCorsConfig({
|
|
corsOriginsEnv: process.env.CORS_ORIGINS,
|
|
})
|
|
);
|
|
|
|
await app.listen(3000);
|
|
}
|
|
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
|
|
# docker-compose.staging.yml
|
|
chat-backend:
|
|
environment:
|
|
# CORS - Allow chat web app and main web app to access backend
|
|
CORS_ORIGINS: https://chat.staging.manacore.ai,https://staging.manacore.ai,http://localhost:3000,http://localhost:5173
|
|
```
|
|
|
|
## Default Origins
|
|
|
|
### Development Origins
|
|
|
|
The utility automatically includes these development origins:
|
|
|
|
```typescript
|
|
// Common development ports (always available)
|
|
[
|
|
'http://localhost:3000', // Chat web (production build)
|
|
'http://localhost:3001', // Auth service
|
|
'http://localhost:3002', // Chat backend
|
|
'http://localhost:5173', // Main web (Vite)
|
|
'http://localhost:5174-5190', // Additional Vite instances
|
|
'http://localhost:8081', // Expo mobile
|
|
'exp://localhost:8081', // Expo mobile (exp:// protocol)
|
|
]
|
|
```
|
|
|
|
### 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:
|
|
|
|
### ✅ Backend Configuration
|
|
|
|
1. Add `@manacore/shared-nestjs-cors` dependency to `package.json`
|
|
2. Update `main.ts` to use `createCorsConfig()`
|
|
3. Add `CORS_ORIGINS` to service environment in `docker-compose.staging.yml`
|
|
|
|
### ✅ Docker Compose
|
|
|
|
```yaml
|
|
{app}-backend:
|
|
environment:
|
|
CORS_ORIGINS: https://{app}.staging.manacore.ai,https://{app}-api.staging.manacore.ai,https://staging.manacore.ai,http://localhost:5XXX,http://localhost:5173
|
|
```
|
|
|
|
**Pattern:**
|
|
- App's web frontend: `https://{app}.staging.manacore.ai`
|
|
- App's API endpoint: `https://{app}-api.staging.manacore.ai`
|
|
- Main web app: `https://staging.manacore.ai`
|
|
- Local development: `http://localhost:{web-port},http://localhost:5173`
|
|
|
|
### ✅ Verification Steps
|
|
|
|
1. **Build and deploy** the backend with new CORS config
|
|
2. **Open browser DevTools** (Network tab)
|
|
3. **Navigate to** `https://{app}.staging.manacore.ai`
|
|
4. **Check API requests** - should show `Access-Control-Allow-Origin` header
|
|
5. **Verify no CORS errors** in console
|
|
|
|
## Troubleshooting
|
|
|
|
### CORS error still appears after deployment
|
|
|
|
**Symptoms:**
|
|
- CORS errors persist after adding `CORS_ORIGINS`
|
|
- Container logs show correct config
|
|
|
|
**Solutions:**
|
|
|
|
1. **Restart backend container**
|
|
```bash
|
|
ssh -i ~/.ssh/hetzner_deploy_key deploy@46.224.108.214
|
|
cd ~/manacore-staging
|
|
docker compose restart {app}-backend
|
|
```
|
|
|
|
2. **Verify environment variable**
|
|
```bash
|
|
docker exec {app}-backend-staging printenv | grep CORS
|
|
```
|
|
|
|
3. **Check backend logs**
|
|
```bash
|
|
docker logs {app}-backend-staging | grep -i cors
|
|
```
|
|
|
|
4. **Rebuild if package was added**
|
|
```bash
|
|
# Rebuild Docker image with new dependency
|
|
docker compose build {app}-backend
|
|
docker compose up -d {app}-backend
|
|
```
|
|
|
|
### Mobile app can't connect
|
|
|
|
**Solution:** Use callback-based CORS for mobile apps:
|
|
|
|
```typescript
|
|
import { createCorsConfigWithCallback } from '@manacore/shared-nestjs-cors';
|
|
|
|
app.enableCors(
|
|
createCorsConfigWithCallback({
|
|
corsOriginsEnv: process.env.CORS_ORIGINS,
|
|
})
|
|
);
|
|
```
|
|
|
|
This allows requests with no `Origin` header (common for mobile apps).
|
|
|
|
### Development origins not working
|
|
|
|
**Verification:**
|
|
|
|
```bash
|
|
# Test CORS in development
|
|
curl -H "Origin: http://localhost:5173" \
|
|
-H "Access-Control-Request-Method: POST" \
|
|
-X OPTIONS \
|
|
http://localhost:3002/api/v1/health
|
|
```
|
|
|
|
Should return:
|
|
```
|
|
Access-Control-Allow-Origin: http://localhost:5173
|
|
Access-Control-Allow-Credentials: true
|
|
```
|
|
|
|
## Migration from Manual CORS
|
|
|
|
### Before (Manual Configuration)
|
|
|
|
```typescript
|
|
// ❌ Different in every backend
|
|
const corsOrigins = process.env.CORS_ORIGINS?.split(',').map(o => o.trim()) || [
|
|
'http://localhost:3000',
|
|
'http://localhost:5173',
|
|
// Different defaults per app
|
|
];
|
|
|
|
app.enableCors({
|
|
origin: corsOrigins,
|
|
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
|
credentials: true,
|
|
});
|
|
```
|
|
|
|
### After (Centralized Configuration)
|
|
|
|
```typescript
|
|
// ✅ Consistent everywhere
|
|
import { createCorsConfig } from '@manacore/shared-nestjs-cors';
|
|
|
|
app.enableCors(
|
|
createCorsConfig({
|
|
corsOriginsEnv: process.env.CORS_ORIGINS,
|
|
})
|
|
);
|
|
```
|
|
|
|
## Updated Applications
|
|
|
|
The following backends have been migrated to use `@manacore/shared-nestjs-cors`:
|
|
|
|
- ✅ chat-backend (apps/chat/apps/backend)
|
|
- ✅ picture-backend (apps/picture/apps/backend)
|
|
- ✅ zitare-backend (apps/zitare/apps/backend)
|
|
- ✅ contacts-backend (apps/contacts/apps/backend)
|
|
- ✅ calendar-backend (apps/calendar/apps/backend)
|
|
- ✅ clock-backend (apps/clock/apps/backend)
|
|
- ✅ todo-backend (apps/todo/apps/backend)
|
|
|
|
## Best Practices
|
|
|
|
1. **Always use `@manacore/shared-nestjs-cors`** for new backends
|
|
2. **Always add `CORS_ORIGINS`** to docker-compose when deploying
|
|
3. **Include both app domain and API domain** in CORS_ORIGINS
|
|
4. **Include `staging.manacore.ai`** for cross-app navigation
|
|
5. **Keep localhost ports** for local development testing
|
|
|
|
## Related Documentation
|
|
|
|
- [Staging Setup Guide](./STAGING_SETUP.md) - Complete staging deployment guide
|
|
- [Package README](../packages/shared-nestjs-cors/README.md) - Detailed API documentation
|
|
- [Deployment Architecture](./DEPLOYMENT_ARCHITECTURE.md) - Infrastructure overview
|
|
|
|
---
|
|
|
|
**Last Updated:** 2025-12-17
|
|
**Author:** Claude Code (Automated CORS Solution)
|