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

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