diff --git a/.env.development b/.env.development new file mode 100644 index 000000000..d07da0a5b --- /dev/null +++ b/.env.development @@ -0,0 +1,110 @@ +# ============================================ +# Mana Core Monorepo - Development Environment +# ============================================ +# This is the central source of truth for all dev environment variables. +# Run `pnpm setup:env` to generate app-specific .env files. +# +# DO NOT commit real API keys or production values here. +# This file contains development/local values only. +# ============================================ + +# ============================================ +# SHARED - Used across multiple apps +# ============================================ + +# Mana Core Auth Service +MANA_CORE_AUTH_URL=http://localhost:3001 + +# JWT Keys (shared across apps for token verification) +JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDGRsOXROB4lprw\n9oXaOIt+cwHe3UxBOoiWiUXcpFuXwb+kBWn/LyjeCIOXtefOwE0S10JEodK+6foe\naqGHanq86qAmmkb4a8sjj5LAxXkHL35sJo8HaYcx5NkJQLxQSRHpTfdfxsKsKwxa\n4R4uqrvToqdo6tl/VMsGDPS8L7KzaiKaSdGugvlVtXWgV1soeXSUPyPwpyAXQg7h\nY4CkTSkJAplrs77RLdj8u6jbHKR3F7QkwiU1JocjhM1GP/suKiqXRu8omLFnu45C\ns09SNSRsOpNY5csrKA4PZ2LCks9VHH7HafFvB+BbRw4+Ssr6myOysAztqi3bZMRW\nLTakWpBbAgMBAAECggEAF5zi0IzaghHxhtkyYfrSRgSynX9+WYBRNu2ch8/SZqAj\neghOXMkZgAPEjtiSMDGqRsr4ReMoYtB2Qea8sOX8kwC1gj4Po1Mhtez0cwexclUf\nebLH3X/y9/1YiZJk5YImOMIuaoC/ELDvFOhIEhJcMbKREbIc+oiMcH6HgN0vViVh\nJptgHTnqnGHNARkEpf+xnxqJJxEgrEMz50b4fApKpoZsWXNnZ3Atc/i2ziGew5z4\npnGJxs9TWSukBZaQvl9iluBBvqmPkCOId+L7CmB44bNURpqQOm8gxEgLcdn06y5j\nIKee3Z4H6OTseFvSIYYqBqCyyyZWHICBZXUCDQKUbQKBgQDnFe+O+pQc5looLFiF\nxuYsfDtJqvoMgQ0BaVAo6wVpPe6w+1NA6ZxghcM0+8zyc70jZvdMXINhdsfWD5Gi\nJ/NEDI8EXJJKMfnFQ7F1Ad5NyTnnn/TsLda4GIGQznPRS6uxUP4ljFtxmU9G8Diz\nUQ47XsLjwzzbTedMTSYoQ46kdwKBgQDbp0dIq047o4A72/BBttKdZbgQmjFmqCXF\n8YRUquIDXh/CJ4OQwOIaOvk2398Rg53c3MsV+XCJaMmWYqnJ4BdITLsqeGKsczoS\nI0DMehDr++aOoX/f29r1c+7J/fV5jtAEUcwIEOR1vyAM+WdiWnnTvdpMPVUDsgaT\ntuH0E8WgPQKBgQCCINci87Z+Q7VXVAmRY7zwJhEY3eArNGzHc6+BKz+D0S1dmll6\nf1LhA9I2PuldSpGiovP1m08cjk/gGipPXyHdGxlaQmravyPA0urWUfQGZ59k8K1y\nZim4x4wGqEuN+4e2tT44lL5VzRhYgSPcznMuOaGTsrjNYiQy0mr/V3O25wKBgHvV\nryaVDaIp553XvXgO7ma2djNF+xv5KHKUWxqwzINBiX4YcOAnHlHTdbUuOcDSByoB\ngK1+16dgYGZccYTSxc2JFOw4usimndKj9WBSYT/p4G4BNuqqNKO1HKbceoxxq20E\nAJd7jpGjkxo9cb/Nammp22yoF0niEDsvG+xTSVOxAoGBAMfxHYCMdPc625upCbqG\nkPSJJGYREKGad80OtXilYXLvBPzV65q32k2YZGjaicPKRAzj72KO4nfIu9SY6bfO\nBvXCtIcvllZQuxyd3Cd8MirujJodKwThLTMd4bAYYMXGz1/W6R6pzunZs5KEpgEr\nczy9Gk9WNp0t8vfzyZZ9aago\n-----END PRIVATE KEY-----" +JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxkbDl0TgeJaa8PaF2jiL\nfnMB3t1MQTqIlolF3KRbl8G/pAVp/y8o3giDl7XnzsBNEtdCRKHSvun6Hmqhh2p6\nvOqgJppG+GvLI4+SwMV5By9+bCaPB2mHMeTZCUC8UEkR6U33X8bCrCsMWuEeLqq7\n06KnaOrZf1TLBgz0vC+ys2oimknRroL5VbV1oFdbKHl0lD8j8KcgF0IO4WOApE0p\nCQKZa7O+0S3Y/Luo2xykdxe0JMIlNSaHI4TNRj/7Lioql0bvKJixZ7uOQrNPUjUk\nbDqTWOXLKygOD2diwpLPVRx+x2nxbwfgW0c+Ssr6myOysAztqi3bZMRWLTakWpBb\nwIDAQAB\n-----END PUBLIC KEY-----" + +# Database (shared Postgres for local Docker) +POSTGRES_USER=manacore +POSTGRES_PASSWORD=devpassword + +# Redis +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD=devpassword + +# ============================================ +# MANA-CORE-AUTH SERVICE +# ============================================ + +MANA_CORE_AUTH_PORT=3001 +MANA_CORE_AUTH_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/manacore +JWT_ACCESS_TOKEN_EXPIRY=15m +JWT_REFRESH_TOKEN_EXPIRY=7d +JWT_ISSUER=manacore +JWT_AUDIENCE=manacore +CORS_ORIGINS=http://localhost:3000,http://localhost:3002,http://localhost:5173,http://localhost:8081 +CREDITS_SIGNUP_BONUS=150 +CREDITS_DAILY_FREE=5 +RATE_LIMIT_TTL=60 +RATE_LIMIT_MAX=100 + +# Stripe (test keys - get your own from Stripe dashboard) +STRIPE_SECRET_KEY=sk_test_YOUR_KEY +STRIPE_PUBLISHABLE_KEY=pk_test_YOUR_KEY +STRIPE_WEBHOOK_SECRET=whsec_YOUR_SECRET + +# ============================================ +# CHAT PROJECT +# ============================================ + +# Chat Backend +CHAT_BACKEND_PORT=3002 +CHAT_DATABASE_URL=postgresql://chat:chatpassword@localhost:5432/chat + +# Azure OpenAI (required for chat - get your own keys) +AZURE_OPENAI_ENDPOINT=https://your-azure-openai-endpoint.openai.azure.com +AZURE_OPENAI_API_KEY=YOUR_API_KEY +AZURE_OPENAI_API_VERSION=2024-12-01-preview + +# Chat Supabase (if using Supabase for chat data) +CHAT_SUPABASE_URL=https://your-chat-project.supabase.co +CHAT_SUPABASE_ANON_KEY=your-supabase-anon-key + +# ============================================ +# MAERCHENZAUBER PROJECT +# ============================================ + +MAERCHENZAUBER_BACKEND_PORT=3003 +MAERCHENZAUBER_APP_ID=8d2f5ddb-e251-4b3b-8802-84022a7ac77f + +# Supabase +MAERCHENZAUBER_SUPABASE_URL=https://your-storyteller-project.supabase.co +MAERCHENZAUBER_SUPABASE_ANON_KEY=your-supabase-anon-key +MAERCHENZAUBER_JWT_SECRET=your-jwt-secret + +# Azure OpenAI for story generation +MAERCHENZAUBER_AZURE_OPENAI_KEY=YOUR_KEY +MAERCHENZAUBER_AZURE_OPENAI_ENDPOINT=https://your-endpoint.openai.azure.com/openai/deployments/gpt-4o/chat/completions?api-version=2024-08-01-preview + +# Replicate for image generation +MAERCHENZAUBER_REPLICATE_API_KEY=YOUR_KEY + +# ============================================ +# MEMORO PROJECT +# ============================================ + +MEMORO_SUPABASE_URL=https://your-memoro-project.supabase.co +MEMORO_SUPABASE_ANON_KEY=your-supabase-anon-key +MEMORO_MIDDLEWARE_API_URL=https://mana-core-middleware-111768794939.europe-west3.run.app +MEMORO_APPID=your-app-id + +# ============================================ +# MANACORE PROJECT +# ============================================ + +MANACORE_SUPABASE_URL=https://your-manacore-project.supabase.co +MANACORE_SUPABASE_ANON_KEY=your-supabase-anon-key + +# ============================================ +# MANADECK PROJECT +# ============================================ + +MANADECK_BACKEND_PORT=3004 +MANADECK_SUPABASE_URL=https://your-manadeck-project.supabase.co +MANADECK_SUPABASE_ANON_KEY=your-supabase-anon-key diff --git a/.gitignore b/.gitignore index 241721c80..1aafd3fd1 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ ios/ .env.production.local .env*.local +# BUT commit the central development env file +!.env.development + # IDE .idea/ .vscode/ diff --git a/CLAUDE.md b/CLAUDE.md index 9b43ac79e..53099efeb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -169,7 +169,34 @@ pnpm add --filter @manacore/shared-utils ## Environment Variables -Each project/app has its own `.env` file. Common patterns: +### Centralized Development Environment + +All development environment variables are managed from a single file: `.env.development` + +```bash +# First-time setup: generates all app-specific .env files +pnpm setup:env + +# This also runs automatically after `pnpm install` +``` + +The script reads `.env.development` and generates platform-specific `.env` files for each app with the correct prefixes: +- **Expo mobile**: `EXPO_PUBLIC_*` prefix +- **SvelteKit web**: `PUBLIC_*` prefix +- **NestJS backend**: No prefix + +### Key Files +- `.env.development` - Central source of truth (committed to git) +- `scripts/generate-env.mjs` - Generation script +- `apps/**/apps/**/.env` - Generated files (gitignored) + +### Adding New Variables + +1. Add the variable to `.env.development` +2. Update `scripts/generate-env.mjs` to map it to the appropriate apps +3. Run `pnpm setup:env` to regenerate + +### Platform Prefix Patterns **Mobile (Expo):** ``` @@ -193,6 +220,8 @@ PORT=... ## Project-Specific Documentation +- **[docs/ENVIRONMENT_VARIABLES.md](docs/ENVIRONMENT_VARIABLES.md)** - Complete environment setup guide + Each project has its own `CLAUDE.md` with detailed information: - `apps/maerchenzauber/CLAUDE.md` - Story generation specifics, AI services - `apps/manacore/CLAUDE.md` - Multi-app ecosystem, auth details diff --git a/docs/ENVIRONMENT_VARIABLES.md b/docs/ENVIRONMENT_VARIABLES.md new file mode 100644 index 000000000..47803088c --- /dev/null +++ b/docs/ENVIRONMENT_VARIABLES.md @@ -0,0 +1,256 @@ +# Environment Variables Guide + +This document explains the centralized environment variable system for the Mana Core monorepo. + +## Quick Start + +```bash +# After cloning the repo, install dependencies (auto-generates .env files) +pnpm install + +# Or manually generate .env files +pnpm setup:env +``` + +That's it! All app-specific `.env` files are generated from `.env.development`. + +## How It Works + +``` +.env.development # Central config (committed) + │ + ▼ +scripts/generate-env.mjs # Transforms variables + │ + ▼ +apps/**/apps/**/.env # Generated files (gitignored) +``` + +The generator reads `.env.development` and creates app-specific `.env` files with the correct prefixes for each platform: + +| Platform | Prefix | Example | +|----------|--------|---------| +| Expo (mobile) | `EXPO_PUBLIC_` | `EXPO_PUBLIC_SUPABASE_URL` | +| SvelteKit (web) | `PUBLIC_` | `PUBLIC_SUPABASE_URL` | +| NestJS (backend) | None | `SUPABASE_URL` | + +## File Locations + +### Source File +- **`.env.development`** - Single source of truth, committed to git + +### Generated Files (gitignored) +- `services/mana-core-auth/.env` +- `apps/chat/apps/backend/.env` +- `apps/chat/apps/mobile/.env` +- `apps/chat/apps/web/.env` +- `apps/maerchenzauber/apps/backend/.env` +- `apps/maerchenzauber/apps/mobile/.env` +- `apps/maerchenzauber/apps/web/.env` +- `apps/manacore/apps/mobile/.env` +- `apps/manacore/apps/web/.env` +- `apps/memoro/apps/mobile/.env` +- `apps/memoro/apps/web/.env` +- `apps/manadeck/apps/backend/.env` +- `apps/manadeck/apps/web/.env` + +## Variable Reference + +### Shared Variables + +| Variable | Description | Used By | +|----------|-------------|---------| +| `MANA_CORE_AUTH_URL` | Auth service URL | All apps | +| `JWT_PRIVATE_KEY` | JWT signing key | mana-core-auth | +| `JWT_PUBLIC_KEY` | JWT verification key | All backends | +| `POSTGRES_USER` | Database user | Docker, backends | +| `POSTGRES_PASSWORD` | Database password | Docker, backends | +| `REDIS_HOST` | Redis host | mana-core-auth | +| `REDIS_PORT` | Redis port | mana-core-auth | +| `REDIS_PASSWORD` | Redis password | mana-core-auth | + +### Mana Core Auth Service + +| Variable | Description | Default | +|----------|-------------|---------| +| `MANA_CORE_AUTH_PORT` | Service port | `3001` | +| `MANA_CORE_AUTH_DATABASE_URL` | PostgreSQL connection string | - | +| `JWT_ACCESS_TOKEN_EXPIRY` | Access token TTL | `15m` | +| `JWT_REFRESH_TOKEN_EXPIRY` | Refresh token TTL | `7d` | +| `JWT_ISSUER` | JWT issuer claim | `manacore` | +| `JWT_AUDIENCE` | JWT audience claim | `manacore` | +| `STRIPE_SECRET_KEY` | Stripe secret key | - | +| `STRIPE_PUBLISHABLE_KEY` | Stripe publishable key | - | +| `STRIPE_WEBHOOK_SECRET` | Stripe webhook secret | - | +| `CORS_ORIGINS` | Allowed CORS origins | - | +| `CREDITS_SIGNUP_BONUS` | Credits on signup | `150` | +| `CREDITS_DAILY_FREE` | Daily free credits | `5` | +| `RATE_LIMIT_TTL` | Rate limit window (seconds) | `60` | +| `RATE_LIMIT_MAX` | Max requests per window | `100` | + +### Chat Project + +| Variable | Description | Default | +|----------|-------------|---------| +| `CHAT_BACKEND_PORT` | Backend service port | `3002` | +| `CHAT_DATABASE_URL` | PostgreSQL connection string | - | +| `AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint URL | - | +| `AZURE_OPENAI_API_KEY` | Azure OpenAI API key | - | +| `AZURE_OPENAI_API_VERSION` | API version | `2024-12-01-preview` | +| `CHAT_SUPABASE_URL` | Supabase project URL | - | +| `CHAT_SUPABASE_ANON_KEY` | Supabase anonymous key | - | + +### Maerchenzauber Project + +| Variable | Description | Default | +|----------|-------------|---------| +| `MAERCHENZAUBER_BACKEND_PORT` | Backend service port | `3003` | +| `MAERCHENZAUBER_APP_ID` | Mana Core app ID | - | +| `MAERCHENZAUBER_SUPABASE_URL` | Supabase project URL | - | +| `MAERCHENZAUBER_SUPABASE_ANON_KEY` | Supabase anonymous key | - | +| `MAERCHENZAUBER_JWT_SECRET` | JWT secret for Supabase | - | +| `MAERCHENZAUBER_AZURE_OPENAI_KEY` | Azure OpenAI key | - | +| `MAERCHENZAUBER_AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint | - | +| `MAERCHENZAUBER_REPLICATE_API_KEY` | Replicate API key (images) | - | + +### Memoro Project + +| Variable | Description | +|----------|-------------| +| `MEMORO_SUPABASE_URL` | Supabase project URL | +| `MEMORO_SUPABASE_ANON_KEY` | Supabase anonymous key | +| `MEMORO_MIDDLEWARE_API_URL` | Middleware API URL | +| `MEMORO_APPID` | Mana Core app ID | + +### Manacore Project + +| Variable | Description | +|----------|-------------| +| `MANACORE_SUPABASE_URL` | Supabase project URL | +| `MANACORE_SUPABASE_ANON_KEY` | Supabase anonymous key | + +### Manadeck Project + +| Variable | Description | Default | +|----------|-------------|---------| +| `MANADECK_BACKEND_PORT` | Backend service port | `3004` | +| `MANADECK_SUPABASE_URL` | Supabase project URL | - | +| `MANADECK_SUPABASE_ANON_KEY` | Supabase anonymous key | - | + +## Adding New Variables + +### Step 1: Add to `.env.development` + +```bash +# In .env.development +MY_NEW_PROJECT_API_KEY=your-api-key +MY_NEW_PROJECT_URL=https://api.example.com +``` + +### Step 2: Update the Generator Script + +Edit `scripts/generate-env.mjs` and add your app config: + +```javascript +// In APP_CONFIGS array +{ + path: 'apps/my-project/apps/mobile/.env', + vars: { + // For Expo, add EXPO_PUBLIC_ prefix + EXPO_PUBLIC_API_KEY: (env) => env.MY_NEW_PROJECT_API_KEY, + EXPO_PUBLIC_API_URL: (env) => env.MY_NEW_PROJECT_URL, + }, +}, +{ + path: 'apps/my-project/apps/web/.env', + vars: { + // For SvelteKit, add PUBLIC_ prefix + PUBLIC_API_KEY: (env) => env.MY_NEW_PROJECT_API_KEY, + PUBLIC_API_URL: (env) => env.MY_NEW_PROJECT_URL, + }, +}, +{ + path: 'apps/my-project/apps/backend/.env', + vars: { + // For NestJS, no prefix needed + API_KEY: (env) => env.MY_NEW_PROJECT_API_KEY, + API_URL: (env) => env.MY_NEW_PROJECT_URL, + }, +}, +``` + +### Step 3: Regenerate + +```bash +pnpm setup:env +``` + +## Local Overrides + +If you need to override variables locally without affecting others: + +1. The generated `.env` files are gitignored +2. You can manually edit them after generation +3. Or create `.env.local` files (also gitignored) that some frameworks auto-load + +**Note:** Running `pnpm setup:env` will overwrite your changes, so use `.env.local` for persistent overrides. + +## Docker Integration + +The root `.env.development` is also used by Docker Compose: + +```bash +# Start all services with shared env +pnpm docker:up:all +``` + +Docker services read from: +- Root `.env.development` for shared values +- Service-specific `.env` files for service-specific values + +## Troubleshooting + +### "Variable is undefined" Error + +1. Check if the variable exists in `.env.development` +2. Run `pnpm setup:env` to regenerate +3. Restart your dev server (env changes require restart) + +### Generated File Has Wrong Value + +1. Check the mapping in `scripts/generate-env.mjs` +2. Ensure the source variable name matches exactly +3. Run `pnpm setup:env` again + +### New App Not Getting Generated + +1. Add app config to `APP_CONFIGS` in `scripts/generate-env.mjs` +2. Ensure the target directory exists +3. Run `pnpm setup:env` + +### Expo Not Picking Up Changes + +Expo caches environment variables. Clear the cache: + +```bash +cd apps//apps/mobile +npx expo start -c +``` + +## Security Notes + +- `.env.development` contains **development-only** values +- Never put production secrets in this file +- The JWT keys in `.env.development` are for local development only +- Use separate secrets management for production (1Password, AWS Secrets Manager, etc.) + +## Migration from Old System + +If you have existing `.env` files with real values: + +1. Copy important values to `.env.development` +2. Delete the old `.env` files +3. Run `pnpm setup:env` +4. Verify apps still work + +The old `.env.example` files can remain as documentation. diff --git a/package.json b/package.json index c4d4a2494..52525d807 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,9 @@ "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,svelte,astro}\"", "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,svelte,astro}\"", + "setup:env": "node scripts/generate-env.mjs", + "postinstall": "node scripts/generate-env.mjs || true", + "maerchenzauber:dev": "turbo run dev --filter=maerchenzauber...", "manacore:dev": "turbo run dev --filter=manacore...", "manadeck:dev": "turbo run dev --filter=manadeck...", diff --git a/scripts/generate-env.mjs b/scripts/generate-env.mjs new file mode 100644 index 000000000..ed877251d --- /dev/null +++ b/scripts/generate-env.mjs @@ -0,0 +1,264 @@ +#!/usr/bin/env node + +/** + * Environment Variable Generator + * + * Reads from .env.development and generates app-specific .env files + * with the appropriate prefixes for each platform. + * + * Usage: pnpm setup:env + */ + +import { readFileSync, writeFileSync, existsSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const ROOT_DIR = join(__dirname, '..'); +const ENV_FILE = join(ROOT_DIR, '.env.development'); + +// Parse a .env file into an object +function parseEnvFile(content) { + const env = {}; + const lines = content.split('\n'); + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + + const match = trimmed.match(/^([^=]+)=(.*)$/); + if (match) { + let [, key, value] = match; + // Handle quoted values + if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { + value = value.slice(1, -1); + } + env[key.trim()] = value; + } + } + return env; +} + +// Generate env file content +function generateEnvContent(vars) { + const lines = ['# Auto-generated by scripts/generate-env.mjs', '# Source: .env.development', '']; + for (const [key, value] of Object.entries(vars)) { + // Quote values that contain special characters or newlines + const needsQuotes = value.includes('\n') || value.includes(' ') || value.includes('#'); + const formattedValue = needsQuotes ? `"${value}"` : value; + lines.push(`${key}=${formattedValue}`); + } + return lines.join('\n') + '\n'; +} + +// App configurations - maps source variables to target variables +const APP_CONFIGS = [ + // Mana Core Auth Service + { + path: 'services/mana-core-auth/.env', + vars: { + NODE_ENV: () => 'development', + PORT: (env) => env.MANA_CORE_AUTH_PORT || '3001', + DATABASE_URL: (env) => env.MANA_CORE_AUTH_DATABASE_URL, + REDIS_HOST: (env) => env.REDIS_HOST, + REDIS_PORT: (env) => env.REDIS_PORT, + REDIS_PASSWORD: (env) => env.REDIS_PASSWORD || '', + JWT_PRIVATE_KEY: (env) => env.JWT_PRIVATE_KEY, + JWT_PUBLIC_KEY: (env) => env.JWT_PUBLIC_KEY, + JWT_ACCESS_TOKEN_EXPIRY: (env) => env.JWT_ACCESS_TOKEN_EXPIRY, + JWT_REFRESH_TOKEN_EXPIRY: (env) => env.JWT_REFRESH_TOKEN_EXPIRY, + JWT_ISSUER: (env) => env.JWT_ISSUER, + JWT_AUDIENCE: (env) => env.JWT_AUDIENCE, + STRIPE_SECRET_KEY: (env) => env.STRIPE_SECRET_KEY, + STRIPE_PUBLISHABLE_KEY: (env) => env.STRIPE_PUBLISHABLE_KEY, + STRIPE_WEBHOOK_SECRET: (env) => env.STRIPE_WEBHOOK_SECRET, + CORS_ORIGINS: (env) => env.CORS_ORIGINS, + CREDITS_SIGNUP_BONUS: (env) => env.CREDITS_SIGNUP_BONUS, + CREDITS_DAILY_FREE: (env) => env.CREDITS_DAILY_FREE, + RATE_LIMIT_TTL: (env) => env.RATE_LIMIT_TTL, + RATE_LIMIT_MAX: (env) => env.RATE_LIMIT_MAX, + }, + }, + + // Chat Backend + { + path: 'apps/chat/apps/backend/.env', + vars: { + NODE_ENV: () => 'development', + PORT: (env) => env.CHAT_BACKEND_PORT || '3002', + AZURE_OPENAI_ENDPOINT: (env) => env.AZURE_OPENAI_ENDPOINT, + AZURE_OPENAI_API_KEY: (env) => env.AZURE_OPENAI_API_KEY, + AZURE_OPENAI_API_VERSION: (env) => env.AZURE_OPENAI_API_VERSION, + MANA_CORE_AUTH_URL: (env) => env.MANA_CORE_AUTH_URL, + DATABASE_URL: (env) => env.CHAT_DATABASE_URL, + }, + }, + + // Chat Mobile (Expo) + { + path: 'apps/chat/apps/mobile/.env', + vars: { + EXPO_PUBLIC_MANA_CORE_AUTH_URL: (env) => env.MANA_CORE_AUTH_URL, + EXPO_PUBLIC_SUPABASE_URL: (env) => env.CHAT_SUPABASE_URL, + EXPO_PUBLIC_SUPABASE_ANON_KEY: (env) => env.CHAT_SUPABASE_ANON_KEY, + EXPO_PUBLIC_BACKEND_URL: (env) => `http://localhost:${env.CHAT_BACKEND_PORT || '3002'}`, + }, + }, + + // Chat Web (SvelteKit) + { + path: 'apps/chat/apps/web/.env', + vars: { + PUBLIC_MANA_CORE_AUTH_URL: (env) => env.MANA_CORE_AUTH_URL, + PUBLIC_SUPABASE_URL: (env) => env.CHAT_SUPABASE_URL, + PUBLIC_SUPABASE_ANON_KEY: (env) => env.CHAT_SUPABASE_ANON_KEY, + PUBLIC_BACKEND_URL: (env) => `http://localhost:${env.CHAT_BACKEND_PORT || '3002'}`, + }, + }, + + // Maerchenzauber Backend + { + path: 'apps/maerchenzauber/apps/backend/.env', + vars: { + NODE_ENV: () => 'development', + PORT: (env) => env.MAERCHENZAUBER_BACKEND_PORT || '3003', + MANA_SERVICE_URL: (env) => env.MANA_CORE_AUTH_URL, + APP_ID: (env) => env.MAERCHENZAUBER_APP_ID, + MAERCHENZAUBER_SUPABASE_URL: (env) => env.MAERCHENZAUBER_SUPABASE_URL, + MAERCHENZAUBER_SUPABASE_ANON_KEY: (env) => env.MAERCHENZAUBER_SUPABASE_ANON_KEY, + MAERCHENZAUBER_JWT_SECRET: (env) => env.MAERCHENZAUBER_JWT_SECRET, + MAERCHENZAUBER_AZURE_OPENAI_KEY: (env) => env.MAERCHENZAUBER_AZURE_OPENAI_KEY, + MAERCHENZAUBER_AZURE_OPENAI_ENDPOINT: (env) => env.MAERCHENZAUBER_AZURE_OPENAI_ENDPOINT, + MAERCHENZAUBER_REPLICATE_API_KEY: (env) => env.MAERCHENZAUBER_REPLICATE_API_KEY, + CORS_ORIGINS: (env) => env.CORS_ORIGINS, + }, + }, + + // Maerchenzauber Mobile + { + path: 'apps/maerchenzauber/apps/mobile/.env', + vars: { + EXPO_PUBLIC_STORYTELLER_BACKEND_URL: (env) => `http://localhost:${env.MAERCHENZAUBER_BACKEND_PORT || '3003'}`, + EXPO_ROUTER_APP_ROOT: () => 'app', + }, + }, + + // Maerchenzauber Web + { + path: 'apps/maerchenzauber/apps/web/.env', + vars: { + PUBLIC_SUPABASE_URL: (env) => env.MAERCHENZAUBER_SUPABASE_URL, + PUBLIC_SUPABASE_ANON_KEY: (env) => env.MAERCHENZAUBER_SUPABASE_ANON_KEY, + PUBLIC_BACKEND_URL: (env) => `http://localhost:${env.MAERCHENZAUBER_BACKEND_PORT || '3003'}`, + }, + }, + + // Manacore Mobile + { + path: 'apps/manacore/apps/mobile/.env', + vars: { + EXPO_PUBLIC_SUPABASE_URL: (env) => env.MANACORE_SUPABASE_URL, + EXPO_PUBLIC_SUPABASE_ANON_KEY: (env) => env.MANACORE_SUPABASE_ANON_KEY, + }, + }, + + // Manacore Web + { + path: 'apps/manacore/apps/web/.env', + vars: { + PUBLIC_SUPABASE_URL: (env) => env.MANACORE_SUPABASE_URL, + PUBLIC_SUPABASE_ANON_KEY: (env) => env.MANACORE_SUPABASE_ANON_KEY, + MIDDLEWARE_URL: (env) => env.MANA_CORE_AUTH_URL, + }, + }, + + // Memoro Mobile + { + path: 'apps/memoro/apps/mobile/.env', + vars: { + EXPO_PUBLIC_SUPABASE_URL: (env) => env.MEMORO_SUPABASE_URL, + EXPO_PUBLIC_SUPABASE_ANON_KEY: (env) => env.MEMORO_SUPABASE_ANON_KEY, + EXPO_PUBLIC_MIDDLEWARE_API_URL: (env) => env.MEMORO_MIDDLEWARE_API_URL, + EXPO_PUBLIC_APPID: (env) => env.MEMORO_APPID, + }, + }, + + // Memoro Web + { + path: 'apps/memoro/apps/web/.env', + vars: { + PUBLIC_SUPABASE_URL: (env) => env.MEMORO_SUPABASE_URL, + PUBLIC_SUPABASE_ANON_KEY: (env) => env.MEMORO_SUPABASE_ANON_KEY, + }, + }, + + // Manadeck Backend + { + path: 'apps/manadeck/apps/backend/.env', + vars: { + NODE_ENV: () => 'development', + PORT: (env) => env.MANADECK_BACKEND_PORT || '3004', + SUPABASE_URL: (env) => env.MANADECK_SUPABASE_URL, + SUPABASE_ANON_KEY: (env) => env.MANADECK_SUPABASE_ANON_KEY, + }, + }, + + // Manadeck Web + { + path: 'apps/manadeck/apps/web/.env', + vars: { + PUBLIC_SUPABASE_URL: (env) => env.MANADECK_SUPABASE_URL, + PUBLIC_SUPABASE_ANON_KEY: (env) => env.MANADECK_SUPABASE_ANON_KEY, + }, + }, +]; + +function main() { + console.log('Generating environment files from .env.development...\n'); + + // Check if source file exists + if (!existsSync(ENV_FILE)) { + console.error(`Error: ${ENV_FILE} not found.`); + console.error('Please create .env.development from .env.development.example'); + process.exit(1); + } + + // Parse source env file + const sourceContent = readFileSync(ENV_FILE, 'utf-8'); + const sourceEnv = parseEnvFile(sourceContent); + + let generated = 0; + let skipped = 0; + + for (const config of APP_CONFIGS) { + const targetPath = join(ROOT_DIR, config.path); + const targetDir = dirname(targetPath); + + // Check if target directory exists + if (!existsSync(targetDir)) { + console.log(` Skipping ${config.path} (directory not found)`); + skipped++; + continue; + } + + // Generate variables + const targetVars = {}; + for (const [key, getter] of Object.entries(config.vars)) { + const value = getter(sourceEnv); + if (value !== undefined && value !== null) { + targetVars[key] = value; + } + } + + // Write file + const content = generateEnvContent(targetVars); + writeFileSync(targetPath, content); + console.log(` Generated ${config.path}`); + generated++; + } + + console.log(`\nDone! Generated ${generated} files, skipped ${skipped}.`); + console.log('\nNote: Generated .env files are gitignored. Only .env.development is committed.'); +} + +main(); diff --git a/turbo.json b/turbo.json index 2d1d4f88f..2f441dc8e 100644 --- a/turbo.json +++ b/turbo.json @@ -1,9 +1,17 @@ { "$schema": "https://turbo.build/schema.json", + "globalEnv": [ + "NODE_ENV", + "MANA_CORE_AUTH_URL", + "JWT_PUBLIC_KEY", + "AZURE_OPENAI_ENDPOINT", + "AZURE_OPENAI_API_KEY" + ], "tasks": { "dev": { "cache": false, - "persistent": true + "persistent": true, + "dotEnv": [".env", ".env.development", ".env.local"] }, "build": { "dependsOn": ["^build"],