refactor(db): consolidate ~20+ databases into 2 (mana_platform + mana_sync)

Mirrors the frontend unification (single IndexedDB) on the backend.
All services now use pgSchema() for isolation within one shared database,
enabling cross-schema JOINs, simplified ops, and zero DB setup for new apps.

- Migrate 7 services from pgTable() to pgSchema(): mana-user (usr),
  mana-media (media), todo, traces, presi, uload, cards
- Update all DATABASE_URLs in .env.development, docker-compose, configs
- Rewrite init-db scripts for 2 databases + 12 schemas
- Rewrite setup-databases.sh for consolidated architecture
- Update shared-drizzle-config default to mana_platform
- Update CLAUDE.md with new database architecture docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-02 14:31:28 +02:00
parent b1a5c95f1d
commit 3ea28b9065
44 changed files with 311 additions and 346 deletions

View file

@ -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
# ============================================

View file

@ -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)

View file

@ -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(),

View file

@ -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(),

View file

@ -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(',');

View file

@ -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(),

View file

@ -22,8 +22,7 @@ let client: ReturnType<typeof postgres> | null = null;
export function getDb(): ReturnType<typeof drizzle<typeof schema>> {
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,

View file

@ -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(),

View file

@ -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}

View file

@ -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}

View file

@ -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;

View file

@ -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

View file

@ -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,
});

View file

@ -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(),

View file

@ -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(),

View file

@ -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(),

View file

@ -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(),

View file

@ -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(),

View file

@ -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(),

View file

@ -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';

View file

@ -0,0 +1,3 @@
import { pgSchema } from 'drizzle-orm/pg-core';
export const cardsSchema = pgSchema('cards');

View file

@ -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(),

View file

@ -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(),

View file

@ -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',

View file

@ -36,7 +36,7 @@ export function createDb<TSchema extends Record<string, unknown>>(
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,

View file

@ -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

View file

@ -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'],
});

View file

@ -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'),

View file

@ -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'],
});

View file

@ -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'

View file

@ -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'],
});

View file

@ -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'),

View file

@ -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'],
});

View file

@ -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(),

View file

@ -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'],
});

View file

@ -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'),

View file

@ -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'],
});

View file

@ -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(',') },

View file

@ -1,3 +1,4 @@
export * from './schema';
export * from './tag-groups';
export * from './tags';
export * from './tag-links';

View file

@ -0,0 +1,3 @@
import { pgSchema } from 'drizzle-orm/pg-core';
export const usrSchema = pgSchema('usr');

View file

@ -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({

View file

@ -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(),

View file

@ -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(),

View file

@ -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(),