diff --git a/.env.development b/.env.development index 05b936c48..9a82fabc1 100644 --- a/.env.development +++ b/.env.development @@ -58,7 +58,7 @@ S3_SECRET_KEY=minioadmin # ============================================ MANA_CORE_AUTH_PORT=3001 -MANA_CORE_AUTH_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/manacore +MANA_CORE_AUTH_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform JWT_ACCESS_TOKEN_EXPIRY=15m JWT_REFRESH_TOKEN_EXPIRY=7d JWT_ISSUER=manacore @@ -155,7 +155,7 @@ UMAMI_WEBSITE_ID_MUKKE_LANDING=b2c9ab34-3c53-4463-9dde-1ecf098886a5 # Chat Backend CHAT_BACKEND_PORT=3002 -CHAT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/chat +CHAT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform DEV_BYPASS_AUTH=true DEV_USER_ID=00000000-0000-0000-0000-000000000000 @@ -187,7 +187,7 @@ MAERCHENZAUBER_REPLICATE_API_KEY=YOUR_KEY # ============================================ CARDS_BACKEND_PORT=3009 -CARDS_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/cards +CARDS_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform CARDS_APP_ID=cea4bfc6-a4de-4e17-91e2-54275940156e # ============================================ @@ -196,7 +196,7 @@ CARDS_APP_ID=cea4bfc6-a4de-4e17-91e2-54275940156e PICTURE_BACKEND_PORT=3006 PICTURE_BACKEND_URL=http://localhost:3006 -PICTURE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/picture +PICTURE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # Replicate API Token for AI image generation PICTURE_REPLICATE_API_TOKEN=r8_QlvkstNhIc6NBX1ktpQ6ibvzOE2d2UQ1Emamd @@ -214,7 +214,7 @@ PICTURE_MANA_CORE_SERVICE_KEY= # ============================================ NUTRIPHI_BACKEND_PORT=3023 -NUTRIPHI_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/nutriphi +NUTRIPHI_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform NUTRIPHI_APP_ID=nutriphi # Google Gemini API for food image analysis @@ -228,14 +228,14 @@ NUTRIPHI_S3_PUBLIC_URL=http://localhost:9000/nutriphi-storage # ============================================ ZITARE_BACKEND_PORT=3007 -ZITARE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/zitare +ZITARE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # ZITARE TELEGRAM BOT # ============================================ ZITARE_BOT_PORT=3303 -ZITARE_BOT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/zitare_bot +ZITARE_BOT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform ZITARE_BOT_TELEGRAM_TOKEN=8489424174:AAHHG_mlLVeu6xAWY6U2ZGXO0D8JKWnqBvg # ============================================ @@ -243,7 +243,7 @@ ZITARE_BOT_TELEGRAM_TOKEN=8489424174:AAHHG_mlLVeu6xAWY6U2ZGXO0D8JKWnqBvg # ============================================ TODO_BOT_PORT=3304 -TODO_BOT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/todo_bot +TODO_BOT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform TODO_BOT_TELEGRAM_TOKEN=8363906368:AAHzNC1DPSb0TUb2a3UGWWH1_rrAQFdBv2w TODO_BOT_API_URL=http://localhost:3018 @@ -252,14 +252,14 @@ TODO_BOT_API_URL=http://localhost:3018 # ============================================ PRESI_BACKEND_PORT=3008 -PRESI_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/presi +PRESI_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # VOXEL-LAVA PROJECT # ============================================ VOXEL_LAVA_BACKEND_PORT=3010 -VOXEL_LAVA_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/voxel_lava +VOXEL_LAVA_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform VOXEL_LAVA_API_URL=http://localhost:3010 # ============================================ @@ -267,7 +267,7 @@ VOXEL_LAVA_API_URL=http://localhost:3010 # ============================================ CONTACTS_BACKEND_PORT=3015 -CONTACTS_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/contacts +CONTACTS_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # S3 Storage for contact photos CONTACTS_S3_BUCKET=contacts-photos @@ -286,7 +286,7 @@ CONTACTS_GOOGLE_REDIRECT_URI=http://localhost:5184/import?tab=google CALENDAR_BACKEND_PORT=3014 CALENDAR_BACKEND_URL=http://localhost:3014 -CALENDAR_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/calendar +CALENDAR_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # Speech-to-Text Service (mana-stt) # Production: https://stt-api.mana.how @@ -298,7 +298,7 @@ STT_URL=https://stt-api.mana.how # ============================================ CONTEXT_BACKEND_PORT=3020 -CONTEXT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/context +CONTEXT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # AI API Keys (server-side only) CONTEXT_AZURE_OPENAI_API_KEY=YOUR_KEY @@ -310,7 +310,7 @@ CONTEXT_GOOGLE_API_KEY=YOUR_KEY # ============================================ STORAGE_BACKEND_PORT=3016 -STORAGE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/storage +STORAGE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform STORAGE_S3_PUBLIC_URL=http://localhost:9000/storage-storage STORAGE_MAX_FILE_SIZE=104857600 STORAGE_MAX_FILES_PER_UPLOAD=10 @@ -320,7 +320,7 @@ STORAGE_MAX_FILES_PER_UPLOAD=10 # ============================================ CLOCK_BACKEND_PORT=3017 -CLOCK_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/clock +CLOCK_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # TODO PROJECT @@ -328,14 +328,14 @@ CLOCK_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/clock TODO_BACKEND_PORT=3018 TODO_BACKEND_URL=http://localhost:3018 -TODO_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/todo +TODO_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # MOODLIT PROJECT # ============================================ MOODLIT_BACKEND_PORT=3012 -MOODLIT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/moods +MOODLIT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # MANA-GAMES PROJECT @@ -364,14 +364,14 @@ MANA_GAMES_GITHUB_REPO=mana-games # ============================================ FINANCE_BACKEND_PORT=3019 -FINANCE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/finance +FINANCE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # INVENTORY PROJECT # ============================================ INVENTORY_BACKEND_PORT=3020 -INVENTORY_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/inventory +INVENTORY_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform INVENTORY_S3_PUBLIC_URL=http://localhost:9000/inventory-storage # ============================================ @@ -379,14 +379,14 @@ INVENTORY_S3_PUBLIC_URL=http://localhost:9000/inventory-storage # ============================================ TECHBASE_BACKEND_PORT=3021 -TECHBASE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/techbase +TECHBASE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # PLANTA PROJECT # ============================================ PLANTA_BACKEND_PORT=3022 -PLANTA_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/planta +PLANTA_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform PLANTA_S3_PUBLIC_URL=http://localhost:9000/planta-storage # Google Gemini API for plant vision analysis @@ -397,27 +397,27 @@ PLANTA_GEMINI_API_KEY=AIzaSyC_-hPWpVttTlqJdU4jbXR5H0OAnRi2LgI # ============================================ TRACES_BACKEND_PORT=3026 -TRACES_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/traces +TRACES_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # SKILLTREE PROJECT # ============================================ SKILLTREE_BACKEND_PORT=3024 -SKILLTREE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/skilltree +SKILLTREE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # MUKKE PROJECT # ============================================ MUKKE_BACKEND_PORT=3010 -MUKKE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mukke +MUKKE_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform # ============================================ # CITYCORNERS PROJECT # ============================================ CITYCORNERS_BACKEND_PORT=3025 -CITYCORNERS_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/citycorners +CITYCORNERS_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mana_platform CITYCORNERS_WEB_PORT=5196 # ============================================ diff --git a/CLAUDE.md b/CLAUDE.md index ade663399..6775ec5c6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -666,12 +666,51 @@ import { createAuthService } from '@manacore/shared-auth'; import { formatDate, truncate } from '@manacore/shared-utils'; ``` -## Database (Supabase) +## Database Architecture (PostgreSQL) -- All projects use Supabase for PostgreSQL database, auth, and storage -- Row Level Security (RLS) policies enforce access control via JWT claims -- Each project has its own Supabase project/schema -- Types typically generated via `supabase gen types` +All backend data lives in **2 PostgreSQL databases**: + +| Database | Purpose | Tech | +|----------|---------|------| +| **mana_platform** | All services + app server-side tables | Drizzle ORM, pgSchema isolation | +| **mana_sync** | Sync engine (write-heavy, append-only) | Go + pgx | + +### Schema Mapping (mana_platform) + +Each service owns a PostgreSQL schema within the shared database: + +| Schema | Service | Tables | +|--------|---------|--------| +| `auth` | mana-auth | users, sessions, orgs, passkeys | +| `credits` | mana-credits | balances, transactions, packages | +| `gifts` | mana-credits | gift codes, redemptions | +| `subscriptions` | mana-subscriptions | plans, invoices | +| `feedback` | mana-analytics | user feedback, votes | +| `usr` | mana-user | settings, tags, tag groups | +| `media` | mana-media | CAS, thumbnails, references | +| `todo` | todo server | tasks, projects, reminders | +| `traces` | traces server | locations, cities, POIs, guides | +| `presi` | presi server | decks, slides, themes, shares | +| `uload` | uload server | users, links, clicks | +| `cards` | cards package | decks, cards, study progress | + +### Using pgSchema + +All tables must use `pgSchema('name').table()` — never plain `pgTable()`: + +```typescript +import { pgSchema } from 'drizzle-orm/pg-core'; + +const mySchema = pgSchema('myapp'); +export const items = mySchema.table('items', { ... }); +``` + +### Adding a New Schema + +1. Add schema to `docker/init-db/01-create-databases.sql` +2. Add schema to `scripts/setup-databases.sh` (PLATFORM_SCHEMAS array) +3. Create `drizzle.config.ts` with `schemaFilter: ['myschema']` +4. All DATABASE_URLs point to `mana_platform` (one DB for everything) ## Object Storage (MinIO) diff --git a/apps/presi/apps/server/src/db/index.ts b/apps/presi/apps/server/src/db/index.ts index b7b61eb58..0fa413ab0 100644 --- a/apps/presi/apps/server/src/db/index.ts +++ b/apps/presi/apps/server/src/db/index.ts @@ -6,7 +6,7 @@ import { drizzle } from 'drizzle-orm/postgres-js'; import postgres from 'postgres'; import { - pgTable, + pgSchema, uuid, text, boolean, @@ -18,7 +18,7 @@ import { import { relations } from 'drizzle-orm'; const DATABASE_URL = - process.env.DATABASE_URL ?? 'postgresql://manacore:devpassword@localhost:5432/presi'; + process.env.DATABASE_URL ?? 'postgresql://manacore:devpassword@localhost:5432/mana_platform'; const connection = postgres(DATABASE_URL, { max: 5, @@ -27,7 +27,9 @@ const connection = postgres(DATABASE_URL, { // ─── Schema (read-only for share lookups) ──────────────── -export const decks = pgTable('decks', { +export const presiSchema = pgSchema('presi'); + +export const decks = presiSchema.table('decks', { id: uuid('id').primaryKey().defaultRandom(), userId: text('user_id').notNull(), title: text('title').notNull(), @@ -38,7 +40,7 @@ export const decks = pgTable('decks', { updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(), }); -export const slides = pgTable( +export const slides = presiSchema.table( 'slides', { id: uuid('id').primaryKey().defaultRandom(), @@ -50,7 +52,7 @@ export const slides = pgTable( (table) => [index('slides_deck_order_idx').on(table.deckId, table.order)] ); -export const themes = pgTable('themes', { +export const themes = presiSchema.table('themes', { id: uuid('id').primaryKey().defaultRandom(), name: text('name').notNull(), colors: jsonb('colors'), @@ -58,7 +60,7 @@ export const themes = pgTable('themes', { isDefault: boolean('is_default').default(false), }); -export const sharedDecks = pgTable( +export const sharedDecks = presiSchema.table( 'shared_decks', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/apps/todo/apps/server/src/db/index.ts b/apps/todo/apps/server/src/db/index.ts index c606ef58b..e9349c1ff 100644 --- a/apps/todo/apps/server/src/db/index.ts +++ b/apps/todo/apps/server/src/db/index.ts @@ -8,7 +8,7 @@ import { drizzle } from 'drizzle-orm/postgres-js'; import postgres from 'postgres'; import { - pgTable, + pgSchema, uuid, text, timestamp, @@ -20,16 +20,20 @@ import { } from 'drizzle-orm/pg-core'; const DATABASE_URL = - process.env.DATABASE_URL ?? 'postgresql://manacore:devpassword@localhost:5432/todo'; + process.env.DATABASE_URL ?? 'postgresql://manacore:devpassword@localhost:5432/mana_platform'; const connection = postgres(DATABASE_URL, { max: 5, idle_timeout: 20, }); +// ─── Schema ──────────────── + +export const todoSchema = pgSchema('todo'); + // ─── Minimal Schema (only what server needs) ──────────────── -export const tasks = pgTable('tasks', { +export const tasks = todoSchema.table('tasks', { id: uuid('id').primaryKey().defaultRandom(), userId: text('user_id').notNull(), projectId: uuid('project_id'), @@ -55,12 +59,12 @@ export const tasks = pgTable('tasks', { updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(), }); -export const projects = pgTable('projects', { +export const projects = todoSchema.table('projects', { id: uuid('id').primaryKey().defaultRandom(), userId: text('user_id').notNull(), }); -export const reminders = pgTable( +export const reminders = todoSchema.table( 'reminders', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/apps/traces/apps/server/src/index.ts b/apps/traces/apps/server/src/index.ts index ab40a12a0..f4671ac13 100644 --- a/apps/traces/apps/server/src/index.ts +++ b/apps/traces/apps/server/src/index.ts @@ -17,7 +17,7 @@ import { GuideService } from './services/guide'; const PORT = parseInt(process.env.PORT || '3026', 10); const DB_URL = - process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/traces'; + process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_platform'; const LLM_URL = process.env.MANA_LLM_URL || 'http://localhost:3025'; const SEARCH_URL = process.env.MANA_SEARCH_URL || 'http://localhost:3021'; const CORS_ORIGINS = (process.env.CORS_ORIGINS || 'http://localhost:5173').split(','); diff --git a/apps/traces/apps/server/src/schema.ts b/apps/traces/apps/server/src/schema.ts index 3533bf130..7e2879368 100644 --- a/apps/traces/apps/server/src/schema.ts +++ b/apps/traces/apps/server/src/schema.ts @@ -1,5 +1,5 @@ import { - pgTable, + pgSchema, uuid, text, doublePrecision, @@ -10,6 +10,8 @@ import { uniqueIndex, } from 'drizzle-orm/pg-core'; +export const tracesSchema = pgSchema('traces'); + // ============================================ // Enums // ============================================ @@ -49,7 +51,7 @@ export const guideStatusEnum = pgEnum('guide_status', ['generating', 'ready', 'e // Tables // ============================================ -export const locations = pgTable( +export const locations = tracesSchema.table( 'locations', { id: uuid('id').defaultRandom().primaryKey(), @@ -80,7 +82,7 @@ export const locations = pgTable( ] ); -export const cities = pgTable( +export const cities = tracesSchema.table( 'cities', { id: uuid('id').defaultRandom().primaryKey(), @@ -94,7 +96,7 @@ export const cities = pgTable( (table) => [uniqueIndex('cities_name_country_code_idx').on(table.name, table.countryCode)] ); -export const cityVisits = pgTable( +export const cityVisits = tracesSchema.table( 'city_visits', { id: uuid('id').defaultRandom().primaryKey(), @@ -115,7 +117,7 @@ export const cityVisits = pgTable( ] ); -export const places = pgTable( +export const places = tracesSchema.table( 'places', { id: uuid('id').defaultRandom().primaryKey(), @@ -139,7 +141,7 @@ export const places = pgTable( ] ); -export const pois = pgTable( +export const pois = tracesSchema.table( 'pois', { id: uuid('id').defaultRandom().primaryKey(), @@ -165,7 +167,7 @@ export const pois = pgTable( ] ); -export const guides = pgTable( +export const guides = tracesSchema.table( 'guides', { id: uuid('id').defaultRandom().primaryKey(), @@ -190,7 +192,7 @@ export const guides = pgTable( ] ); -export const guidePois = pgTable( +export const guidePois = tracesSchema.table( 'guide_pois', { id: uuid('id').defaultRandom().primaryKey(), diff --git a/apps/uload/packages/uload-database/src/index.ts b/apps/uload/packages/uload-database/src/index.ts index 0afdc9490..28c22705e 100644 --- a/apps/uload/packages/uload-database/src/index.ts +++ b/apps/uload/packages/uload-database/src/index.ts @@ -22,8 +22,7 @@ let client: ReturnType | null = null; export function getDb(): ReturnType> { if (!db) { const connectionString = - process.env.DATABASE_URL || - 'postgresql://uload:uload_dev_password_123@localhost:5432/uload_dev'; + process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_platform'; client = postgres(connectionString, { max: 10, diff --git a/apps/uload/packages/uload-database/src/schema.ts b/apps/uload/packages/uload-database/src/schema.ts index 6513ef255..2a454befd 100644 --- a/apps/uload/packages/uload-database/src/schema.ts +++ b/apps/uload/packages/uload-database/src/schema.ts @@ -1,5 +1,5 @@ import { - pgTable, + pgSchema, uuid, text, boolean, @@ -10,10 +10,12 @@ import { } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; +export const uloadSchema = pgSchema('uload'); + // ============================================ // Users Table // ============================================ -export const users = pgTable( +export const users = uloadSchema.table( 'users', { id: uuid('id').primaryKey().defaultRandom(), @@ -48,7 +50,7 @@ export const users = pgTable( // ============================================ // Accounts Table // ============================================ -export const accounts = pgTable( +export const accounts = uloadSchema.table( 'accounts', { id: uuid('id').primaryKey().defaultRandom(), @@ -70,7 +72,7 @@ export const accounts = pgTable( // ============================================ // Workspaces Table // ============================================ -export const workspaces = pgTable( +export const workspaces = uloadSchema.table( 'workspaces', { id: uuid('id').primaryKey().defaultRandom(), @@ -92,7 +94,7 @@ export const workspaces = pgTable( // ============================================ // Links Table // ============================================ -export const links = pgTable( +export const links = uloadSchema.table( 'links', { id: uuid('id').primaryKey().defaultRandom(), @@ -129,7 +131,7 @@ export const links = pgTable( // ============================================ // Clicks Table // ============================================ -export const clicks = pgTable( +export const clicks = uloadSchema.table( 'clicks', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 7114c91e8..0b042cca5 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -10,7 +10,7 @@ services: container_name: manacore-postgres restart: unless-stopped environment: - POSTGRES_DB: manacore + POSTGRES_DB: mana_platform POSTGRES_USER: ${POSTGRES_USER:-manacore} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-devpassword} volumes: @@ -113,7 +113,7 @@ services: environment: NODE_ENV: development PORT: 3001 - DATABASE_URL: postgresql://${POSTGRES_USER:-manacore}:${POSTGRES_PASSWORD:-devpassword}@postgres:5432/mana_auth + DATABASE_URL: postgresql://${POSTGRES_USER:-manacore}:${POSTGRES_PASSWORD:-devpassword}@postgres:5432/mana_platform BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:-dev-secret-change-me} BETTER_AUTH_URL: http://localhost:3001 CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000,http://localhost:5173,http://localhost:8081} @@ -136,7 +136,7 @@ services: environment: NODE_ENV: development PORT: 3002 - DATABASE_URL: postgresql://${POSTGRES_USER:-manacore}:${POSTGRES_PASSWORD:-devpassword}@postgres:5432/chat + DATABASE_URL: postgresql://${POSTGRES_USER:-manacore}:${POSTGRES_PASSWORD:-devpassword}@postgres:5432/mana_platform DB_HOST: postgres DB_PORT: 5432 DB_USER: ${POSTGRES_USER:-manacore} diff --git a/docker-compose.macmini.yml b/docker-compose.macmini.yml index 628148e54..2f4551684 100644 --- a/docker-compose.macmini.yml +++ b/docker-compose.macmini.yml @@ -29,7 +29,7 @@ services: restart: always mem_limit: 1024m environment: - POSTGRES_DB: mana + POSTGRES_DB: mana_platform POSTGRES_USER: postgres POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mana123} volumes: @@ -263,13 +263,13 @@ services: TZ: Europe/Berlin NODE_ENV: production PORT: 3001 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_auth + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform BASE_URL: https://auth.mana.how COOKIE_DOMAIN: .mana.how MANA_CORE_SERVICE_KEY: ${MANA_CORE_SERVICE_KEY} MANA_CREDITS_URL: http://mana-credits:3061 MANA_SUBSCRIPTIONS_URL: http://mana-subscriptions:3063 - SYNC_DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana + SYNC_DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:-${JWT_SECRET:-your-jwt-secret-change-me}} SMTP_HOST: smtp-relay.brevo.com SMTP_PORT: 587 @@ -305,7 +305,7 @@ services: environment: TZ: Europe/Berlin PORT: 3002 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_credits + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform MANA_CORE_AUTH_URL: http://mana-auth:3001 MANA_CORE_SERVICE_KEY: ${MANA_CORE_SERVICE_KEY} STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-} @@ -339,7 +339,7 @@ services: environment: TZ: Europe/Berlin PORT: 3062 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_user + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform MANA_CORE_AUTH_URL: http://mana-auth:3001 MANA_CORE_SERVICE_KEY: ${MANA_CORE_SERVICE_KEY} CORS_ORIGINS: https://mana.how,https://calc.mana.how,https://calendar.mana.how,https://chat.mana.how,https://clock.mana.how,https://contacts.mana.how,https://context.mana.how,https://cards.mana.how,https://mukke.mana.how,https://nutriphi.mana.how,https://photos.mana.how,https://picture.mana.how,https://planta.mana.how,https://presi.mana.how,https://questions.mana.how,https://storage.mana.how,https://todo.mana.how,https://zitare.mana.how @@ -365,7 +365,7 @@ services: environment: TZ: Europe/Berlin PORT: 3063 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_subscriptions + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform MANA_CORE_AUTH_URL: http://mana-auth:3001 MANA_CORE_SERVICE_KEY: ${MANA_CORE_SERVICE_KEY} STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-} @@ -394,7 +394,7 @@ services: environment: TZ: Europe/Berlin PORT: 3064 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_analytics + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform MANA_CORE_AUTH_URL: http://mana-auth:3001 MANA_LLM_URL: http://mana-llm:3025 CORS_ORIGINS: https://mana.how @@ -427,7 +427,7 @@ services: environment: TZ: Europe/Berlin PORT: 3016 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana?sslmode=disable + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform?sslmode=disable REDIS_HOST: redis REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD:-redis123} @@ -510,7 +510,7 @@ services: condition: service_healthy environment: PORT: 3010 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana?sslmode=disable + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_sync?sslmode=disable JWKS_URL: http://mana-auth:3001/api/v1/auth/jwks CORS_ORIGINS: "https://mana.how,https://*.mana.how" ports: @@ -535,7 +535,7 @@ services: condition: service_healthy environment: PORT: 3013 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana?sslmode=disable + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform?sslmode=disable SERVICE_KEY: ${NOTIFY_SERVICE_KEY:-dev-service-key} MANA_CORE_AUTH_URL: http://mana-auth:3001 SMTP_HOST: ${SMTP_HOST:-smtp-relay.brevo.com} @@ -571,7 +571,7 @@ services: environment: TZ: Europe/Berlin PORT: 3014 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana?sslmode=disable + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform?sslmode=disable REDIS_HOST: redis REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD:-redis123} @@ -603,7 +603,7 @@ services: environment: NODE_ENV: production PORT: 3011 - DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_media + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform REDIS_HOST: redis REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD:-redis123} diff --git a/docker/init-db/01-create-databases.sql b/docker/init-db/01-create-databases.sql index b4cb25923..3bc2e1c1e 100644 --- a/docker/init-db/01-create-databases.sql +++ b/docker/init-db/01-create-databases.sql @@ -1,43 +1,15 @@ --- Create databases for all services --- This script runs on first container initialization +-- Create additional databases for services +-- Note: mana_platform is already created as POSTGRES_DB by Docker --- Core databases -CREATE DATABASE IF NOT EXISTS glitchtip; -CREATE DATABASE IF NOT EXISTS chat; -CREATE DATABASE IF NOT EXISTS zitare; -CREATE DATABASE IF NOT EXISTS contacts; -CREATE DATABASE IF NOT EXISTS calendar; -CREATE DATABASE IF NOT EXISTS clock; -CREATE DATABASE IF NOT EXISTS todo; -CREATE DATABASE IF NOT EXISTS cards; -CREATE DATABASE IF NOT EXISTS storage; -CREATE DATABASE IF NOT EXISTS mail; -CREATE DATABASE IF NOT EXISTS moodlit; -CREATE DATABASE IF NOT EXISTS finance; -CREATE DATABASE IF NOT EXISTS inventory; -CREATE DATABASE IF NOT EXISTS techbase; -CREATE DATABASE IF NOT EXISTS voxel_lava; -CREATE DATABASE IF NOT EXISTS figgos; -CREATE DATABASE IF NOT EXISTS context; -CREATE DATABASE IF NOT EXISTS citycorners; +-- Sync database: separate for I/O isolation (write-heavy, append-only) +CREATE DATABASE mana_sync; --- Grant all privileges to the default user -GRANT ALL PRIVILEGES ON DATABASE chat TO manacore; -GRANT ALL PRIVILEGES ON DATABASE zitare TO manacore; -GRANT ALL PRIVILEGES ON DATABASE contacts TO manacore; -GRANT ALL PRIVILEGES ON DATABASE calendar TO manacore; -GRANT ALL PRIVILEGES ON DATABASE clock TO manacore; -GRANT ALL PRIVILEGES ON DATABASE todo TO manacore; -GRANT ALL PRIVILEGES ON DATABASE cards TO manacore; -GRANT ALL PRIVILEGES ON DATABASE storage TO manacore; -GRANT ALL PRIVILEGES ON DATABASE mail TO manacore; -GRANT ALL PRIVILEGES ON DATABASE moodlit TO manacore; -GRANT ALL PRIVILEGES ON DATABASE finance TO manacore; -GRANT ALL PRIVILEGES ON DATABASE inventory TO manacore; -GRANT ALL PRIVILEGES ON DATABASE techbase TO manacore; -GRANT ALL PRIVILEGES ON DATABASE voxel_lava TO manacore; -GRANT ALL PRIVILEGES ON DATABASE figgos TO manacore; -GRANT ALL PRIVILEGES ON DATABASE context TO manacore; -GRANT ALL PRIVILEGES ON DATABASE citycorners TO manacore; +-- Infrastructure databases (external tools) +CREATE DATABASE glitchtip; +CREATE DATABASE umami; + +-- Grant privileges +GRANT ALL PRIVILEGES ON DATABASE mana_platform TO manacore; +GRANT ALL PRIVILEGES ON DATABASE mana_sync TO manacore; GRANT ALL PRIVILEGES ON DATABASE glitchtip TO manacore; -GRANT ALL PRIVILEGES ON DATABASE manacore TO manacore; +GRANT ALL PRIVILEGES ON DATABASE umami TO manacore; diff --git a/docker/init-db/02-create-schemas.sh b/docker/init-db/02-create-schemas.sh new file mode 100755 index 000000000..32afcac32 --- /dev/null +++ b/docker/init-db/02-create-schemas.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Create schemas within mana_platform database +# Docker entrypoint runs .sh files after .sql files + +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname mana_platform <<-EOSQL + -- Core service schemas + CREATE SCHEMA IF NOT EXISTS auth; + CREATE SCHEMA IF NOT EXISTS credits; + CREATE SCHEMA IF NOT EXISTS gifts; + CREATE SCHEMA IF NOT EXISTS subscriptions; + CREATE SCHEMA IF NOT EXISTS feedback; + CREATE SCHEMA IF NOT EXISTS usr; + CREATE SCHEMA IF NOT EXISTS media; + + -- App server-side schemas + CREATE SCHEMA IF NOT EXISTS todo; + CREATE SCHEMA IF NOT EXISTS traces; + CREATE SCHEMA IF NOT EXISTS presi; + CREATE SCHEMA IF NOT EXISTS uload; + CREATE SCHEMA IF NOT EXISTS cards; + + -- Grant schema usage + GRANT ALL ON SCHEMA auth, credits, gifts, subscriptions, feedback, usr, media, + todo, traces, presi, uload, cards TO manacore; +EOSQL diff --git a/packages/cards-database/drizzle.config.ts b/packages/cards-database/drizzle.config.ts index c47f004dc..31cd36dd4 100644 --- a/packages/cards-database/drizzle.config.ts +++ b/packages/cards-database/drizzle.config.ts @@ -1,12 +1,14 @@ import { defineConfig } from 'drizzle-kit'; export default defineConfig({ - schema: './src/schema/index.ts', + schema: './src/schema/*.ts', out: './drizzle', dialect: 'postgresql', dbCredentials: { - url: process.env.DATABASE_URL!, + url: + process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_platform', }, + schemaFilter: ['cards'], verbose: true, strict: true, }); diff --git a/packages/cards-database/src/schema/aiGenerations.ts b/packages/cards-database/src/schema/aiGenerations.ts index ec6f1f823..695e13659 100644 --- a/packages/cards-database/src/schema/aiGenerations.ts +++ b/packages/cards-database/src/schema/aiGenerations.ts @@ -1,6 +1,7 @@ -import { pgTable, uuid, text, varchar, timestamp, jsonb, index, pgEnum } from 'drizzle-orm/pg-core'; +import { uuid, text, varchar, timestamp, jsonb, index, pgEnum } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; -import { decks } from './decks.js'; +import { cardsSchema } from './schema'; +import { decks } from './decks'; // AI generation status enum export const aiGenerationStatusEnum = pgEnum('ai_generation_status', [ @@ -21,7 +22,7 @@ export interface AIGenerationMetadata { [key: string]: unknown; } -export const aiGenerations = pgTable( +export const aiGenerations = cardsSchema.table( 'ai_generations', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/packages/cards-database/src/schema/cardProgress.ts b/packages/cards-database/src/schema/cardProgress.ts index d036dc219..91afb4e3b 100644 --- a/packages/cards-database/src/schema/cardProgress.ts +++ b/packages/cards-database/src/schema/cardProgress.ts @@ -1,5 +1,4 @@ import { - pgTable, uuid, text, integer, @@ -10,7 +9,8 @@ import { unique, } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; -import { cards } from './cards.js'; +import { cardsSchema } from './schema'; +import { cards } from './cards'; // Progress status enum (SM-2 algorithm states) export const progressStatusEnum = pgEnum('progress_status', [ @@ -20,7 +20,7 @@ export const progressStatusEnum = pgEnum('progress_status', [ 'relearning', ]); -export const cardProgress = pgTable( +export const cardProgress = cardsSchema.table( 'card_progress', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/packages/cards-database/src/schema/cards.ts b/packages/cards-database/src/schema/cards.ts index d3c493b6e..b0fc8ff9e 100644 --- a/packages/cards-database/src/schema/cards.ts +++ b/packages/cards-database/src/schema/cards.ts @@ -1,5 +1,4 @@ import { - pgTable, uuid, varchar, text, @@ -11,8 +10,9 @@ import { pgEnum, } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; -import { decks } from './decks.js'; -import { cardProgress } from './cardProgress.js'; +import { cardsSchema } from './schema'; +import { decks } from './decks'; +import { cardProgress } from './cardProgress'; // Card type enum export const cardTypeEnum = pgEnum('card_type', ['text', 'flashcard', 'quiz', 'mixed']); @@ -46,7 +46,7 @@ export interface MixedContent { export type CardContent = TextContent | FlashcardContent | QuizContent | MixedContent; -export const cards = pgTable( +export const cards = cardsSchema.table( 'cards', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/packages/cards-database/src/schema/dailyProgress.ts b/packages/cards-database/src/schema/dailyProgress.ts index 20bca5683..e7dafa331 100644 --- a/packages/cards-database/src/schema/dailyProgress.ts +++ b/packages/cards-database/src/schema/dailyProgress.ts @@ -1,16 +1,7 @@ -import { - pgTable, - uuid, - text, - date, - integer, - decimal, - timestamp, - index, - unique, -} from 'drizzle-orm/pg-core'; +import { uuid, text, date, integer, decimal, timestamp, index, unique } from 'drizzle-orm/pg-core'; +import { cardsSchema } from './schema'; -export const dailyProgress = pgTable( +export const dailyProgress = cardsSchema.table( 'daily_progress', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/packages/cards-database/src/schema/deckTemplates.ts b/packages/cards-database/src/schema/deckTemplates.ts index 36ad39d0f..9d1e20959 100644 --- a/packages/cards-database/src/schema/deckTemplates.ts +++ b/packages/cards-database/src/schema/deckTemplates.ts @@ -1,5 +1,4 @@ import { - pgTable, uuid, varchar, text, @@ -9,6 +8,7 @@ import { jsonb, index, } from 'drizzle-orm/pg-core'; +import { cardsSchema } from './schema'; // Template data structure export interface DeckTemplateData { @@ -21,7 +21,7 @@ export interface DeckTemplateData { tags?: string[]; } -export const deckTemplates = pgTable( +export const deckTemplates = cardsSchema.table( 'deck_templates', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/packages/cards-database/src/schema/decks.ts b/packages/cards-database/src/schema/decks.ts index 7644cf09d..cf897c2de 100644 --- a/packages/cards-database/src/schema/decks.ts +++ b/packages/cards-database/src/schema/decks.ts @@ -1,19 +1,11 @@ -import { - pgTable, - uuid, - text, - varchar, - boolean, - timestamp, - jsonb, - index, -} from 'drizzle-orm/pg-core'; +import { uuid, text, varchar, boolean, timestamp, jsonb, index } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; -import { cards } from './cards.js'; -import { studySessions } from './studySessions.js'; -import { aiGenerations } from './aiGenerations.js'; +import { cardsSchema } from './schema'; +import { cards } from './cards'; +import { studySessions } from './studySessions'; +import { aiGenerations } from './aiGenerations'; -export const decks = pgTable( +export const decks = cardsSchema.table( 'decks', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/packages/cards-database/src/schema/index.ts b/packages/cards-database/src/schema/index.ts index 31fe539ea..3dfe25c57 100644 --- a/packages/cards-database/src/schema/index.ts +++ b/packages/cards-database/src/schema/index.ts @@ -1,16 +1,19 @@ +// Export schema definition +export * from './schema'; + // Export all schemas -export * from './decks.js'; -export * from './cards.js'; -export * from './studySessions.js'; -export * from './cardProgress.js'; -export * from './deckTemplates.js'; -export * from './aiGenerations.js'; -export * from './userStats.js'; -export * from './dailyProgress.js'; +export * from './decks'; +export * from './cards'; +export * from './studySessions'; +export * from './cardProgress'; +export * from './deckTemplates'; +export * from './aiGenerations'; +export * from './userStats'; +export * from './dailyProgress'; // Re-export relations for use with Drizzle query builder -export { decksRelations } from './decks.js'; -export { cardsRelations } from './cards.js'; -export { studySessionsRelations } from './studySessions.js'; -export { cardProgressRelations } from './cardProgress.js'; -export { aiGenerationsRelations } from './aiGenerations.js'; +export { decksRelations } from './decks'; +export { cardsRelations } from './cards'; +export { studySessionsRelations } from './studySessions'; +export { cardProgressRelations } from './cardProgress'; +export { aiGenerationsRelations } from './aiGenerations'; diff --git a/packages/cards-database/src/schema/schema.ts b/packages/cards-database/src/schema/schema.ts new file mode 100644 index 000000000..380fef39f --- /dev/null +++ b/packages/cards-database/src/schema/schema.ts @@ -0,0 +1,3 @@ +import { pgSchema } from 'drizzle-orm/pg-core'; + +export const cardsSchema = pgSchema('cards'); diff --git a/packages/cards-database/src/schema/studySessions.ts b/packages/cards-database/src/schema/studySessions.ts index abb0f4c0c..f80f9e4c4 100644 --- a/packages/cards-database/src/schema/studySessions.ts +++ b/packages/cards-database/src/schema/studySessions.ts @@ -1,11 +1,12 @@ -import { pgTable, uuid, text, integer, timestamp, index, pgEnum } from 'drizzle-orm/pg-core'; +import { uuid, text, integer, timestamp, index, pgEnum } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; -import { decks } from './decks.js'; +import { cardsSchema } from './schema'; +import { decks } from './decks'; // Study mode enum export const studyModeEnum = pgEnum('study_mode', ['all', 'new', 'review', 'favorites', 'random']); -export const studySessions = pgTable( +export const studySessions = cardsSchema.table( 'study_sessions', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/packages/cards-database/src/schema/userStats.ts b/packages/cards-database/src/schema/userStats.ts index bcb448fb8..f6cff5a6d 100644 --- a/packages/cards-database/src/schema/userStats.ts +++ b/packages/cards-database/src/schema/userStats.ts @@ -1,6 +1,7 @@ -import { pgTable, text, integer, decimal, date, timestamp, index } from 'drizzle-orm/pg-core'; +import { text, integer, decimal, date, timestamp, index } from 'drizzle-orm/pg-core'; +import { cardsSchema } from './schema'; -export const userStats = pgTable( +export const userStats = cardsSchema.table( 'user_stats', { userId: text('user_id').primaryKey(), diff --git a/packages/shared-drizzle-config/src/index.ts b/packages/shared-drizzle-config/src/index.ts index db94f9cdf..90d752fe4 100644 --- a/packages/shared-drizzle-config/src/index.ts +++ b/packages/shared-drizzle-config/src/index.ts @@ -3,9 +3,9 @@ import { defineConfig, type Config } from 'drizzle-kit'; export interface DrizzleConfigOptions { /** * Database name for fallback URL when DATABASE_URL is not set - * Example: 'calendar' -> postgresql://manacore:devpassword@localhost:5432/calendar + * @default 'mana_platform' */ - dbName: string; + dbName?: string; /** * Path to schema file(s) @@ -82,7 +82,7 @@ const DEFAULT_PG_PASSWORD = 'devpassword'; */ export function createDrizzleConfig(options: DrizzleConfigOptions): Config { const { - dbName, + dbName = 'mana_platform', schemaPath = './src/db/schema/index.ts', outDir = './src/db/migrations', envVar = 'DATABASE_URL', diff --git a/packages/shared-hono/src/db.ts b/packages/shared-hono/src/db.ts index 12ce5b7d5..1eba30fd1 100644 --- a/packages/shared-hono/src/db.ts +++ b/packages/shared-hono/src/db.ts @@ -36,7 +36,7 @@ export function createDb>( const url = opts?.url ?? process.env.DATABASE_URL ?? - 'postgresql://manacore:devpassword@localhost:5432/mana'; + 'postgresql://manacore:devpassword@localhost:5432/mana_platform'; const connection = postgres(url, { max: opts?.maxConnections ?? 5, diff --git a/scripts/setup-databases.sh b/scripts/setup-databases.sh index fcb251d92..09e775d8c 100755 --- a/scripts/setup-databases.sh +++ b/scripts/setup-databases.sh @@ -4,8 +4,10 @@ # Usage: ./scripts/setup-databases.sh [service] # Examples: # ./scripts/setup-databases.sh # Setup all -# ./scripts/setup-databases.sh chat # Setup only chat -# ./scripts/setup-databases.sh auth # Setup only auth +# ./scripts/setup-databases.sh auth # Setup only auth schema +# +# Architecture: 2 databases (mana_platform + mana_sync) +# All service schemas live in mana_platform as separate PostgreSQL schemas. set -e @@ -21,7 +23,7 @@ YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' # No Color -echo -e "${GREEN}🗄️ Database Setup Script${NC}" +echo -e "${GREEN}Database Setup Script${NC}" echo "======================================" # Function to create database if it doesn't exist @@ -40,12 +42,19 @@ create_db_if_not_exists() { fi } +# Function to create schema within a database +create_schema_if_not_exists() { + local db_name=$1 + local schema_name=$2 + PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $db_name -c \ + "CREATE SCHEMA IF NOT EXISTS $schema_name;" > /dev/null 2>&1 +} + # Function to push schema for a service push_schema() { local filter=$1 local name=$2 echo -e "${YELLOW}Pushing schema for ${name}...${NC}" - # Use --force to auto-approve in development (skips interactive prompts) if pnpm --filter "$filter" db:push --force 2>/dev/null; then echo -e " ${GREEN}✓ Schema pushed${NC}" else @@ -53,41 +62,20 @@ push_schema() { fi } -# All databases that should exist -ALL_DATABASES=( - "manacore" - "mana_auth" - "chat" - "zitare" - "contacts" - "calendar" - "clock" +# All schemas in mana_platform +PLATFORM_SCHEMAS=( + "auth" + "credits" + "gifts" + "subscriptions" + "feedback" + "usr" + "media" "todo" - "cards" - "storage" - "presi" - "moodlit" - "inventory" - "planta" - "nutriphi" - "photos" - "projectdoc" - "zitare_bot" - "todo_bot" - "nutriphi_bot" - "questions" - "skilltree" - "mukke" "traces" - "context" - "citycorners" + "presi" "uload" - # Hono service databases (extracted from former mana-core-auth) - "mana_credits" - "mana_user" - "mana_subscriptions" - "mana_analytics" - "mana_sync" + "cards" ) # Check if specific service requested @@ -98,112 +86,41 @@ setup_service() { case $service in auth|mana-auth) - create_db_if_not_exists "mana_auth" push_schema "@mana/auth" "mana-auth" ;; - chat) - create_db_if_not_exists "chat" - push_schema "@chat/server" "chat" + credits|mana-credits) + push_schema "@mana/credits" "mana-credits" ;; - zitare) - create_db_if_not_exists "zitare" - # Schema managed by mana-sync (backend removed) + user|mana-user) + push_schema "@mana/user" "mana-user" ;; - contacts) - create_db_if_not_exists "contacts" - # Schema managed by mana-sync (local-first) + subscriptions|mana-subscriptions) + push_schema "@mana/subscriptions" "mana-subscriptions" ;; - calendar) - create_db_if_not_exists "calendar" - # Schema managed by mana-sync (local-first) + analytics|mana-analytics) + push_schema "@mana/analytics" "mana-analytics" ;; - clock) - create_db_if_not_exists "clock" - # Schema managed by mana-sync (backend removed) + media|mana-media) + push_schema "@mana/media" "mana-media" ;; todo) - create_db_if_not_exists "todo" push_schema "@todo/server" "todo" ;; - cards) - create_db_if_not_exists "cards" - # Schema managed by mana-sync (local-first) - ;; - moodlit) - create_db_if_not_exists "moodlit" - push_schema "@moodlit/server" "moodlit" - ;; - picture) - create_db_if_not_exists "picture" - # Schema managed by mana-sync (local-first) - ;; - photos) - create_db_if_not_exists "photos" - # Schema managed by mana-sync (backend removed) - ;; - planta) - create_db_if_not_exists "planta" - push_schema "@planta/server" "planta" - ;; - nutriphi) - create_db_if_not_exists "nutriphi" - # Schema managed by mana-sync (local-first) - ;; - presi) - create_db_if_not_exists "presi" - # Schema managed by mana-sync (NestJS backend removed, Hono server for shares) - ;; - storage) - create_db_if_not_exists "storage" - # Schema managed by mana-sync (local-first) - ;; - projectdoc) - create_db_if_not_exists "projectdoc" - push_schema "@manacore/telegram-project-doc-bot" "projectdoc" - ;; - zitare_bot|zitare-bot) - create_db_if_not_exists "zitare_bot" - push_schema "@manacore/telegram-zitare-bot" "zitare-bot" - ;; - todo_bot|todo-bot) - create_db_if_not_exists "todo_bot" - push_schema "@manacore/telegram-todo-bot" "todo-bot" - ;; - nutriphi_bot|nutriphi-bot) - create_db_if_not_exists "nutriphi_bot" - push_schema "@manacore/telegram-nutriphi-bot" "nutriphi-bot" - ;; - questions) - create_db_if_not_exists "questions" - # Schema managed by mana-sync (local-first) - ;; - skilltree) - create_db_if_not_exists "skilltree" - # Schema managed by mana-sync (backend removed) - ;; - mukke) - create_db_if_not_exists "mukke" - push_schema "@mukke/server" "mukke" - ;; traces) - create_db_if_not_exists "traces" push_schema "@traces/server" "traces" ;; - context) - create_db_if_not_exists "context" - push_schema "@context/server" "context" - ;; - citycorners) - create_db_if_not_exists "citycorners" - # Schema managed by mana-sync (backend removed) + presi) + push_schema "@presi/server" "presi" ;; uload) - create_db_if_not_exists "uload" - # Schema managed by mana-sync (local-first app) + push_schema "@manacore/uload-database" "uload" + ;; + cards) + push_schema "@manacore/cards-database" "cards" ;; *) echo -e "${RED}Unknown service: $service${NC}" - echo "Available services: auth, chat, zitare, contacts, calendar, clock, todo, cards, moodlit, picture, photos, planta, nutriphi, presi, storage, projectdoc, zitare_bot, todo_bot, nutriphi_bot, questions, skilltree, mukke, traces, context, citycorners, uload" + echo "Available services: auth, credits, user, subscriptions, analytics, media, todo, traces, presi, uload, cards" exit 1 ;; esac @@ -219,15 +136,21 @@ fi # Setup all databases echo -e "\n${GREEN}Step 1: Creating databases${NC}" echo "--------------------------------------" -for db in "${ALL_DATABASES[@]}"; do - create_db_if_not_exists "$db" +create_db_if_not_exists "mana_platform" +create_db_if_not_exists "mana_sync" + +echo -e "\n${GREEN}Step 2: Creating schemas in mana_platform${NC}" +echo "--------------------------------------" +for schema in "${PLATFORM_SCHEMAS[@]}"; do + echo -e " ${YELLOW}Creating schema: ${schema}${NC}" + create_schema_if_not_exists "mana_platform" "$schema" + echo -e " ${GREEN}✓ ${schema}${NC}" done -echo -e "\n${GREEN}Step 2: Pushing schemas${NC}" +echo -e "\n${GREEN}Step 3: Pushing schemas${NC}" echo "--------------------------------------" -# Push schemas for all known services -for service in auth chat zitare contacts calendar clock todo cards picture photos moodlit planta nutriphi presi storage questions skilltree mukke traces context citycorners; do +for service in auth credits user subscriptions analytics media todo traces presi uload cards; do setup_service "$service" 2>/dev/null || true done diff --git a/services/mana-analytics/drizzle.config.ts b/services/mana-analytics/drizzle.config.ts index 5bbf66d2c..5f6b4c86f 100644 --- a/services/mana-analytics/drizzle.config.ts +++ b/services/mana-analytics/drizzle.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ dialect: 'postgresql', dbCredentials: { url: - process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_analytics', + process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_platform', }, schemaFilter: ['feedback'], }); diff --git a/services/mana-analytics/src/config.ts b/services/mana-analytics/src/config.ts index f9d5bef53..c21b64180 100644 --- a/services/mana-analytics/src/config.ts +++ b/services/mana-analytics/src/config.ts @@ -13,7 +13,7 @@ export function loadConfig(): Config { port: parseInt(env('PORT', '3064'), 10), databaseUrl: env( 'DATABASE_URL', - 'postgresql://manacore:devpassword@localhost:5432/mana_analytics' + 'postgresql://manacore:devpassword@localhost:5432/mana_platform' ), manaAuthUrl: env('MANA_CORE_AUTH_URL', 'http://localhost:3001'), manaLlmUrl: env('MANA_LLM_URL', 'http://localhost:3025'), diff --git a/services/mana-auth/drizzle.config.ts b/services/mana-auth/drizzle.config.ts index 28afcb6e5..040dd94d3 100644 --- a/services/mana-auth/drizzle.config.ts +++ b/services/mana-auth/drizzle.config.ts @@ -5,7 +5,8 @@ export default defineConfig({ out: './drizzle', dialect: 'postgresql', dbCredentials: { - url: process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_auth', + url: + process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_platform', }, schemaFilter: ['auth'], }); diff --git a/services/mana-auth/src/config.ts b/services/mana-auth/src/config.ts index 525871980..1be80cc72 100644 --- a/services/mana-auth/src/config.ts +++ b/services/mana-auth/src/config.ts @@ -22,7 +22,10 @@ export function loadConfig(): Config { const env = (key: string, fallback?: string) => process.env[key] || fallback || ''; return { port: parseInt(env('PORT', '3001'), 10), - databaseUrl: env('DATABASE_URL', 'postgresql://manacore:devpassword@localhost:5432/mana_auth'), + databaseUrl: env( + 'DATABASE_URL', + 'postgresql://manacore:devpassword@localhost:5432/mana_platform' + ), syncDatabaseUrl: env( 'SYNC_DATABASE_URL', 'postgresql://manacore:devpassword@localhost:5432/mana_sync' diff --git a/services/mana-credits/drizzle.config.ts b/services/mana-credits/drizzle.config.ts index ebfe36f4f..7d5e7b4ca 100644 --- a/services/mana-credits/drizzle.config.ts +++ b/services/mana-credits/drizzle.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ dialect: 'postgresql', dbCredentials: { url: - process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_credits', + process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_platform', }, schemaFilter: ['credits', 'gifts'], }); diff --git a/services/mana-credits/src/config.ts b/services/mana-credits/src/config.ts index 70920cdbe..7cb9c927e 100644 --- a/services/mana-credits/src/config.ts +++ b/services/mana-credits/src/config.ts @@ -28,7 +28,7 @@ export function loadConfig(): Config { port: parseInt(process.env.PORT || '3061', 10), databaseUrl: requiredEnv( 'DATABASE_URL', - 'postgresql://manacore:devpassword@localhost:5432/mana_credits' + 'postgresql://manacore:devpassword@localhost:5432/mana_platform' ), manaAuthUrl: requiredEnv('MANA_CORE_AUTH_URL', 'http://localhost:3001'), serviceKey: requiredEnv('MANA_CORE_SERVICE_KEY', 'dev-service-key'), diff --git a/services/mana-media/apps/api/drizzle.config.ts b/services/mana-media/apps/api/drizzle.config.ts index f5e53dd86..a3882a5f8 100644 --- a/services/mana-media/apps/api/drizzle.config.ts +++ b/services/mana-media/apps/api/drizzle.config.ts @@ -1,6 +1,7 @@ import { createDrizzleConfig } from '@manacore/shared-drizzle-config'; export default createDrizzleConfig({ - dbName: 'mana_media', + dbName: 'mana_platform', schemaPath: './src/db/schema/index.ts', + schemaFilter: ['media'], }); diff --git a/services/mana-media/apps/api/src/db/schema/media.schema.ts b/services/mana-media/apps/api/src/db/schema/media.schema.ts index 24f5a32e6..5368eb24d 100644 --- a/services/mana-media/apps/api/src/db/schema/media.schema.ts +++ b/services/mana-media/apps/api/src/db/schema/media.schema.ts @@ -1,5 +1,5 @@ import { - pgTable, + pgSchema, uuid, text, timestamp, @@ -11,11 +11,13 @@ import { } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; +export const mediaSchema = pgSchema('media'); + /** * Core media table - stores unique files by content hash (SHA-256) * This is the Content-Addressable Storage (CAS) approach */ -export const media = pgTable( +export const media = mediaSchema.table( 'media', { id: uuid('id').primaryKey().defaultRandom(), @@ -70,7 +72,7 @@ export const media = pgTable( * Media references - tracks which user/app owns a reference to a media item * Multiple users can reference the same media (deduplication) */ -export const mediaReferences = pgTable( +export const mediaReferences = mediaSchema.table( 'media_references', { id: uuid('id').primaryKey().defaultRandom(), @@ -101,7 +103,7 @@ export const mediaReferences = pgTable( * Lazy-generated thumbnails cache * Stores on-the-fly generated thumbnails with specific parameters */ -export const mediaThumbnails = pgTable( +export const mediaThumbnails = mediaSchema.table( 'media_thumbnails', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/services/mana-subscriptions/drizzle.config.ts b/services/mana-subscriptions/drizzle.config.ts index 4776667b7..0fa501f6b 100644 --- a/services/mana-subscriptions/drizzle.config.ts +++ b/services/mana-subscriptions/drizzle.config.ts @@ -6,8 +6,7 @@ export default defineConfig({ dialect: 'postgresql', dbCredentials: { url: - process.env.DATABASE_URL || - 'postgresql://manacore:devpassword@localhost:5432/mana_subscriptions', + process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_platform', }, schemaFilter: ['subscriptions'], }); diff --git a/services/mana-subscriptions/src/config.ts b/services/mana-subscriptions/src/config.ts index 384d9b3cf..780875831 100644 --- a/services/mana-subscriptions/src/config.ts +++ b/services/mana-subscriptions/src/config.ts @@ -14,7 +14,7 @@ export function loadConfig(): Config { port: parseInt(env('PORT', '3063'), 10), databaseUrl: env( 'DATABASE_URL', - 'postgresql://manacore:devpassword@localhost:5432/mana_subscriptions' + 'postgresql://manacore:devpassword@localhost:5432/mana_platform' ), manaAuthUrl: env('MANA_CORE_AUTH_URL', 'http://localhost:3001'), serviceKey: env('MANA_CORE_SERVICE_KEY', 'dev-service-key'), diff --git a/services/mana-user/drizzle.config.ts b/services/mana-user/drizzle.config.ts index 86eb46eb0..2af869d09 100644 --- a/services/mana-user/drizzle.config.ts +++ b/services/mana-user/drizzle.config.ts @@ -5,6 +5,8 @@ export default defineConfig({ out: './drizzle', dialect: 'postgresql', dbCredentials: { - url: process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_user', + url: + process.env.DATABASE_URL || 'postgresql://manacore:devpassword@localhost:5432/mana_platform', }, + schemaFilter: ['usr'], }); diff --git a/services/mana-user/src/config.ts b/services/mana-user/src/config.ts index 0f72c425a..154c3ef8e 100644 --- a/services/mana-user/src/config.ts +++ b/services/mana-user/src/config.ts @@ -10,7 +10,10 @@ export function loadConfig(): Config { const env = (key: string, fallback?: string) => process.env[key] || fallback || ''; return { port: parseInt(env('PORT', '3062'), 10), - databaseUrl: env('DATABASE_URL', 'postgresql://manacore:devpassword@localhost:5432/mana_user'), + databaseUrl: env( + 'DATABASE_URL', + 'postgresql://manacore:devpassword@localhost:5432/mana_platform' + ), manaAuthUrl: env('MANA_CORE_AUTH_URL', 'http://localhost:3001'), serviceKey: env('MANA_CORE_SERVICE_KEY', 'dev-service-key'), cors: { origins: env('CORS_ORIGINS', 'http://localhost:5173').split(',') }, diff --git a/services/mana-user/src/db/schema/index.ts b/services/mana-user/src/db/schema/index.ts index a095950b3..36de347a1 100644 --- a/services/mana-user/src/db/schema/index.ts +++ b/services/mana-user/src/db/schema/index.ts @@ -1,3 +1,4 @@ +export * from './schema'; export * from './tag-groups'; export * from './tags'; export * from './tag-links'; diff --git a/services/mana-user/src/db/schema/schema.ts b/services/mana-user/src/db/schema/schema.ts new file mode 100644 index 000000000..6d57d7fe4 --- /dev/null +++ b/services/mana-user/src/db/schema/schema.ts @@ -0,0 +1,3 @@ +import { pgSchema } from 'drizzle-orm/pg-core'; + +export const usrSchema = pgSchema('usr'); diff --git a/services/mana-user/src/db/schema/settings.ts b/services/mana-user/src/db/schema/settings.ts index cc6fbe745..2fff813ec 100644 --- a/services/mana-user/src/db/schema/settings.ts +++ b/services/mana-user/src/db/schema/settings.ts @@ -1,6 +1,7 @@ -import { pgTable, text, jsonb, timestamp } from 'drizzle-orm/pg-core'; +import { text, jsonb, timestamp } from 'drizzle-orm/pg-core'; +import { usrSchema } from './schema'; -export const userSettings = pgTable('user_settings', { +export const userSettings = usrSchema.table('user_settings', { userId: text('user_id').primaryKey(), globalSettings: jsonb('global_settings') .default({ diff --git a/services/mana-user/src/db/schema/tag-groups.ts b/services/mana-user/src/db/schema/tag-groups.ts index e07b8bd20..f484c0b85 100644 --- a/services/mana-user/src/db/schema/tag-groups.ts +++ b/services/mana-user/src/db/schema/tag-groups.ts @@ -1,15 +1,7 @@ -import { - pgTable, - varchar, - text, - uuid, - timestamp, - index, - unique, - integer, -} from 'drizzle-orm/pg-core'; +import { varchar, text, uuid, timestamp, index, unique, integer } from 'drizzle-orm/pg-core'; +import { usrSchema } from './schema'; -export const tagGroups = pgTable( +export const tagGroups = usrSchema.table( 'tag_groups', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/services/mana-user/src/db/schema/tag-links.ts b/services/mana-user/src/db/schema/tag-links.ts index 12d93a447..e861ccbd4 100644 --- a/services/mana-user/src/db/schema/tag-links.ts +++ b/services/mana-user/src/db/schema/tag-links.ts @@ -1,7 +1,8 @@ -import { pgTable, varchar, text, uuid, timestamp, index, unique } from 'drizzle-orm/pg-core'; +import { varchar, text, uuid, timestamp, index, unique } from 'drizzle-orm/pg-core'; +import { usrSchema } from './schema'; import { tags } from './tags'; -export const tagLinks = pgTable( +export const tagLinks = usrSchema.table( 'tag_links', { id: uuid('id').primaryKey().defaultRandom(), diff --git a/services/mana-user/src/db/schema/tags.ts b/services/mana-user/src/db/schema/tags.ts index b1dfa9c8d..e65752f5e 100644 --- a/services/mana-user/src/db/schema/tags.ts +++ b/services/mana-user/src/db/schema/tags.ts @@ -1,16 +1,8 @@ -import { - pgTable, - varchar, - text, - uuid, - timestamp, - index, - unique, - integer, -} from 'drizzle-orm/pg-core'; +import { varchar, text, uuid, timestamp, index, unique, integer } from 'drizzle-orm/pg-core'; +import { usrSchema } from './schema'; import { tagGroups } from './tag-groups'; -export const tags = pgTable( +export const tags = usrSchema.table( 'tags', { id: uuid('id').primaryKey().defaultRandom(),