db(cards): baseline migration + drizzle-tracking bootstrap script
Some checks are pending
CI / validate (push) Waiting to run
Some checks are pending
CI / validate (push) Waiting to run
Schließt die Ops-Lücke „kein versioniertes Schema-Tracking" aus FEATURE_IDEAS.md. * apps/api/src/db/migrations/0000_baseline.sql — Drizzle-generierte Baseline-Migration, 355 Zeilen, 25 Tabellen + 5 Enums (cards- und marketplace-Schema). Eingefrostet auf den Live-Stand 2026-05-12. * apps/api/scripts/bootstrap-drizzle-tracking.ts — neues Script, markiert die Baseline in einer bestehenden DB als „bereits angewandt", ohne SQL erneut auszuführen. Verwendet sha256 wie drizzle-orm/migrator (Hash 312d67ba1aeb…), idempotent. * package.json: drizzle:migrate + drizzle:bootstrap-tracking npm-scripts. * docs/playbooks/DRIZZLE_MIGRATIONS_BOOTSTRAP.md — Hand-Over für Prod (Bootstrap einmalig, dann normaler Workflow: schema → generate → commit → migrate, kein push --force mehr). Lokal verifiziert: 17/104 Tests grün, bootstrap idempotent, drizzle-kit migrate erkennt die Baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5a29dd9a8c
commit
4bb1390180
7 changed files with 3523 additions and 5 deletions
|
|
@ -14,8 +14,10 @@
|
|||
"lint": "echo 'lint configured later (eslint flat-config)'",
|
||||
"clean": "rm -rf dist .turbo coverage",
|
||||
"drizzle:generate": "drizzle-kit generate",
|
||||
"drizzle:migrate": "drizzle-kit migrate",
|
||||
"drizzle:push": "drizzle-kit push --force",
|
||||
"drizzle:studio": "drizzle-kit studio"
|
||||
"drizzle:studio": "drizzle-kit studio",
|
||||
"drizzle:bootstrap-tracking": "bun run scripts/bootstrap-drizzle-tracking.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cards/domain": "workspace:*",
|
||||
|
|
|
|||
90
apps/api/scripts/bootstrap-drizzle-tracking.ts
Normal file
90
apps/api/scripts/bootstrap-drizzle-tracking.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Bootstrap-Script für drizzle.__drizzle_migrations.
|
||||
*
|
||||
* Eine Cards-DB, die bisher über `drizzle-kit push` (Schema-Sync ohne
|
||||
* Migrations-Tracking) gepflegt wurde, hat das Drizzle-Tracking-Schema
|
||||
* nicht. Dieses Script holt das nach:
|
||||
*
|
||||
* 1. Erstellt `drizzle.__drizzle_migrations` (idempotent).
|
||||
* 2. Liest `src/db/migrations/meta/_journal.json`.
|
||||
* 3. Markiert jede dort gelistete Migration als „bereits angewandt",
|
||||
* mit dem gleichen Hash, den `drizzle-orm/migrator` selbst
|
||||
* berechnen würde — sha256(file_content) als hex.
|
||||
*
|
||||
* Nutzung:
|
||||
* DATABASE_URL=postgresql://… pnpm bootstrap:drizzle
|
||||
*
|
||||
* Idempotent: ein zweiter Run macht nichts neu, sondern überspringt
|
||||
* Migrations, deren Hash schon eingetragen ist.
|
||||
*
|
||||
* Nach dem Bootstrap wird `drizzle-kit migrate` jede Migration aus
|
||||
* dem Journal als bekannt erkennen und keine SQL erneut ausführen.
|
||||
* Künftige Schema-Änderungen ⇒ `drizzle-kit generate` ⇒ commit ⇒
|
||||
* `drizzle-kit migrate` führt nur die neuen Migrations aus.
|
||||
*/
|
||||
|
||||
import { createHash } from 'node:crypto';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import postgres from 'postgres';
|
||||
|
||||
const url = process.env.DATABASE_URL;
|
||||
if (!url) {
|
||||
console.error('DATABASE_URL not set');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const migrationsDir = resolve(here, '..', 'src', 'db', 'migrations');
|
||||
const journalPath = resolve(migrationsDir, 'meta', '_journal.json');
|
||||
|
||||
interface JournalEntry {
|
||||
idx: number;
|
||||
version: string;
|
||||
when: number;
|
||||
tag: string;
|
||||
breakpoints: boolean;
|
||||
}
|
||||
interface Journal {
|
||||
version: string;
|
||||
dialect: string;
|
||||
entries: JournalEntry[];
|
||||
}
|
||||
|
||||
const journal: Journal = JSON.parse(readFileSync(journalPath, 'utf-8'));
|
||||
|
||||
const sql = postgres(url, { max: 1 });
|
||||
|
||||
try {
|
||||
await sql`CREATE SCHEMA IF NOT EXISTS drizzle`;
|
||||
await sql`CREATE TABLE IF NOT EXISTS drizzle.__drizzle_migrations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hash text NOT NULL,
|
||||
created_at bigint
|
||||
)`;
|
||||
|
||||
const existing = await sql<{ hash: string }[]>`SELECT hash FROM drizzle.__drizzle_migrations`;
|
||||
const existingHashes = new Set(existing.map((r) => r.hash));
|
||||
|
||||
for (const entry of journal.entries) {
|
||||
const migrationPath = resolve(migrationsDir, `${entry.tag}.sql`);
|
||||
const fileContent = readFileSync(migrationPath, 'utf-8');
|
||||
const hash = createHash('sha256').update(fileContent).digest('hex');
|
||||
|
||||
if (existingHashes.has(hash)) {
|
||||
console.log(`SKIP ${entry.tag} (hash ${hash.slice(0, 12)}… already tracked)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await sql`INSERT INTO drizzle.__drizzle_migrations ("hash", "created_at")
|
||||
VALUES (${hash}, ${entry.when})`;
|
||||
console.log(`MARKED ${entry.tag} (hash ${hash.slice(0, 12)}…)`);
|
||||
}
|
||||
|
||||
const final = await sql<{ count: number }[]>`SELECT COUNT(*)::int AS count
|
||||
FROM drizzle.__drizzle_migrations`;
|
||||
console.log(`\nTracking-Tabelle hat jetzt ${final[0]!.count} Eintrag/Einträge.`);
|
||||
} finally {
|
||||
await sql.end({ timeout: 5 });
|
||||
}
|
||||
356
apps/api/src/db/migrations/0000_baseline.sql
Normal file
356
apps/api/src/db/migrations/0000_baseline.sql
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
CREATE SCHEMA "cards";
|
||||
--> statement-breakpoint
|
||||
CREATE SCHEMA "marketplace";
|
||||
--> statement-breakpoint
|
||||
CREATE TYPE "marketplace"."ai_mod_verdict" AS ENUM('pass', 'flag', 'block');--> statement-breakpoint
|
||||
CREATE TYPE "marketplace"."card_type" AS ENUM('basic', 'basic-reverse', 'cloze', 'type-in', 'image-occlusion', 'audio', 'multiple-choice');--> statement-breakpoint
|
||||
CREATE TYPE "marketplace"."pr_status" AS ENUM('open', 'merged', 'closed', 'rejected');--> statement-breakpoint
|
||||
CREATE TYPE "marketplace"."report_category" AS ENUM('spam', 'copyright', 'nsfw', 'misinformation', 'hate', 'other');--> statement-breakpoint
|
||||
CREATE TYPE "marketplace"."report_status" AS ENUM('open', 'dismissed', 'actioned');--> statement-breakpoint
|
||||
CREATE TABLE "cards"."card_tags" (
|
||||
"card_id" text NOT NULL,
|
||||
"tag_id" text NOT NULL,
|
||||
CONSTRAINT "card_tags_card_id_tag_id_pk" PRIMARY KEY("card_id","tag_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "cards"."cards" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"deck_id" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"type" text NOT NULL,
|
||||
"fields" jsonb NOT NULL,
|
||||
"media_refs" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
"content_hash" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "cards"."decks" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"description" text,
|
||||
"color" text,
|
||||
"category" text,
|
||||
"visibility" text DEFAULT 'private' NOT NULL,
|
||||
"fsrs_settings" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"content_hash" text,
|
||||
"forked_from_marketplace_deck_id" text,
|
||||
"forked_from_marketplace_version_id" text,
|
||||
"archived_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "cards"."import_jobs" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"source" text NOT NULL,
|
||||
"state" text DEFAULT 'queued' NOT NULL,
|
||||
"meta" jsonb,
|
||||
"error" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"finished_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."ai_moderation_log" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"version_id" uuid NOT NULL,
|
||||
"verdict" "marketplace"."ai_mod_verdict" NOT NULL,
|
||||
"categories" text[],
|
||||
"model" text,
|
||||
"rationale" text,
|
||||
"human_reviewed" boolean DEFAULT false NOT NULL,
|
||||
"human_overrode" boolean DEFAULT false NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."author_follows" (
|
||||
"follower_user_id" text NOT NULL,
|
||||
"author_user_id" text NOT NULL,
|
||||
"since" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."author_payouts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"author_user_id" text NOT NULL,
|
||||
"source_purchase_id" uuid NOT NULL,
|
||||
"credits_granted" integer NOT NULL,
|
||||
"credits_grant_id" text,
|
||||
"granted_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."authors" (
|
||||
"user_id" text PRIMARY KEY NOT NULL,
|
||||
"slug" text NOT NULL,
|
||||
"display_name" text NOT NULL,
|
||||
"bio" text,
|
||||
"avatar_url" text,
|
||||
"joined_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"pseudonym" boolean DEFAULT false NOT NULL,
|
||||
"verified_mana" boolean DEFAULT false NOT NULL,
|
||||
"verified_community" boolean DEFAULT false NOT NULL,
|
||||
"banned_at" timestamp with time zone,
|
||||
"banned_reason" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."card_discussions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"card_content_hash" text NOT NULL,
|
||||
"deck_id" uuid NOT NULL,
|
||||
"author_user_id" text NOT NULL,
|
||||
"parent_id" uuid,
|
||||
"body" text NOT NULL,
|
||||
"hidden" boolean DEFAULT false NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."deck_forks" (
|
||||
"user_id" text NOT NULL,
|
||||
"source_deck_id" uuid NOT NULL,
|
||||
"source_version_id" uuid NOT NULL,
|
||||
"forked_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."deck_pull_requests" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"deck_id" uuid NOT NULL,
|
||||
"author_user_id" text NOT NULL,
|
||||
"status" "marketplace"."pr_status" DEFAULT 'open' NOT NULL,
|
||||
"title" text NOT NULL,
|
||||
"body" text,
|
||||
"diff" jsonb NOT NULL,
|
||||
"merged_into_version_id" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"resolved_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."deck_purchases" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"buyer_user_id" text NOT NULL,
|
||||
"deck_id" uuid NOT NULL,
|
||||
"version_id" uuid NOT NULL,
|
||||
"price_credits" integer NOT NULL,
|
||||
"author_share" integer NOT NULL,
|
||||
"mana_share" integer NOT NULL,
|
||||
"credits_transaction" text,
|
||||
"purchased_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"refunded_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."deck_reports" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"deck_id" uuid NOT NULL,
|
||||
"version_id" uuid,
|
||||
"card_content_hash" text,
|
||||
"reporter_user_id" text NOT NULL,
|
||||
"category" "marketplace"."report_category" NOT NULL,
|
||||
"body" text,
|
||||
"status" "marketplace"."report_status" DEFAULT 'open' NOT NULL,
|
||||
"resolved_by" text,
|
||||
"resolved_at" timestamp with time zone,
|
||||
"resolution_notes" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."deck_stars" (
|
||||
"user_id" text NOT NULL,
|
||||
"deck_id" uuid NOT NULL,
|
||||
"starred_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."deck_subscriptions" (
|
||||
"user_id" text NOT NULL,
|
||||
"deck_id" uuid NOT NULL,
|
||||
"current_version_id" uuid,
|
||||
"subscribed_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"notify_updates" boolean DEFAULT true NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."deck_tags" (
|
||||
"deck_id" uuid NOT NULL,
|
||||
"tag_id" uuid NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "cards"."media_files" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"object_key" text NOT NULL,
|
||||
"mime_type" text NOT NULL,
|
||||
"original_filename" text,
|
||||
"size_bytes" integer NOT NULL,
|
||||
"kind" text NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "cards"."media_refs" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"card_id" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"mana_media_object_id" text NOT NULL,
|
||||
"kind" text NOT NULL,
|
||||
"ord" integer DEFAULT 0 NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."deck_cards" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"version_id" uuid NOT NULL,
|
||||
"type" "marketplace"."card_type" NOT NULL,
|
||||
"fields" jsonb NOT NULL,
|
||||
"ord" integer NOT NULL,
|
||||
"content_hash" text NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."deck_versions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"deck_id" uuid NOT NULL,
|
||||
"semver" text NOT NULL,
|
||||
"changelog" text,
|
||||
"content_hash" text NOT NULL,
|
||||
"card_count" integer NOT NULL,
|
||||
"published_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"deprecated_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."decks" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"slug" text NOT NULL,
|
||||
"title" text NOT NULL,
|
||||
"description" text,
|
||||
"language" text,
|
||||
"category" text,
|
||||
"license" text DEFAULT 'Cardecky-Personal-Use-1.0' NOT NULL,
|
||||
"price_credits" integer DEFAULT 0 NOT NULL,
|
||||
"owner_user_id" text NOT NULL,
|
||||
"latest_version_id" uuid,
|
||||
"is_featured" boolean DEFAULT false NOT NULL,
|
||||
"is_takedown" boolean DEFAULT false NOT NULL,
|
||||
"takedown_at" timestamp with time zone,
|
||||
"takedown_reason" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "decks_price_requires_license" CHECK (price_credits = 0 OR license = 'Cardecky-Pro-Only-1.0')
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "cards"."reviews" (
|
||||
"card_id" text NOT NULL,
|
||||
"sub_index" integer DEFAULT 0 NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"due" timestamp with time zone NOT NULL,
|
||||
"stability" real NOT NULL,
|
||||
"difficulty" real NOT NULL,
|
||||
"elapsed_days" real DEFAULT 0 NOT NULL,
|
||||
"scheduled_days" real DEFAULT 0 NOT NULL,
|
||||
"learning_steps" integer DEFAULT 0 NOT NULL,
|
||||
"reps" integer DEFAULT 0 NOT NULL,
|
||||
"lapses" integer DEFAULT 0 NOT NULL,
|
||||
"state" text DEFAULT 'new' NOT NULL,
|
||||
"last_review" timestamp with time zone,
|
||||
CONSTRAINT "reviews_card_id_sub_index_pk" PRIMARY KEY("card_id","sub_index")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "cards"."study_sessions" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"deck_id" text NOT NULL,
|
||||
"started_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"finished_at" timestamp with time zone,
|
||||
"cards_reviewed" integer DEFAULT 0 NOT NULL,
|
||||
"cards_correct" integer DEFAULT 0 NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "marketplace"."tag_definitions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"slug" text NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"parent_id" uuid,
|
||||
"description" text,
|
||||
"curated" boolean DEFAULT false NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "cards"."tags" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"deck_id" text NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "cards"."card_tags" ADD CONSTRAINT "card_tags_card_id_cards_id_fk" FOREIGN KEY ("card_id") REFERENCES "cards"."cards"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "cards"."cards" ADD CONSTRAINT "cards_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "cards"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."ai_moderation_log" ADD CONSTRAINT "ai_moderation_log_version_id_deck_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "marketplace"."deck_versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."author_follows" ADD CONSTRAINT "author_follows_author_user_id_authors_user_id_fk" FOREIGN KEY ("author_user_id") REFERENCES "marketplace"."authors"("user_id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."author_payouts" ADD CONSTRAINT "author_payouts_author_user_id_authors_user_id_fk" FOREIGN KEY ("author_user_id") REFERENCES "marketplace"."authors"("user_id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."author_payouts" ADD CONSTRAINT "author_payouts_source_purchase_id_deck_purchases_id_fk" FOREIGN KEY ("source_purchase_id") REFERENCES "marketplace"."deck_purchases"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."card_discussions" ADD CONSTRAINT "card_discussions_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "marketplace"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_forks" ADD CONSTRAINT "deck_forks_source_deck_id_decks_id_fk" FOREIGN KEY ("source_deck_id") REFERENCES "marketplace"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_forks" ADD CONSTRAINT "deck_forks_source_version_id_deck_versions_id_fk" FOREIGN KEY ("source_version_id") REFERENCES "marketplace"."deck_versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_pull_requests" ADD CONSTRAINT "deck_pull_requests_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "marketplace"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_pull_requests" ADD CONSTRAINT "deck_pull_requests_merged_into_version_id_deck_versions_id_fk" FOREIGN KEY ("merged_into_version_id") REFERENCES "marketplace"."deck_versions"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_purchases" ADD CONSTRAINT "deck_purchases_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "marketplace"."decks"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_purchases" ADD CONSTRAINT "deck_purchases_version_id_deck_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "marketplace"."deck_versions"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_reports" ADD CONSTRAINT "deck_reports_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "marketplace"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_reports" ADD CONSTRAINT "deck_reports_version_id_deck_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "marketplace"."deck_versions"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_stars" ADD CONSTRAINT "deck_stars_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "marketplace"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_subscriptions" ADD CONSTRAINT "deck_subscriptions_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "marketplace"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_subscriptions" ADD CONSTRAINT "deck_subscriptions_current_version_id_deck_versions_id_fk" FOREIGN KEY ("current_version_id") REFERENCES "marketplace"."deck_versions"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_tags" ADD CONSTRAINT "deck_tags_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "marketplace"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_tags" ADD CONSTRAINT "deck_tags_tag_id_tag_definitions_id_fk" FOREIGN KEY ("tag_id") REFERENCES "marketplace"."tag_definitions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "cards"."media_refs" ADD CONSTRAINT "media_refs_card_id_cards_id_fk" FOREIGN KEY ("card_id") REFERENCES "cards"."cards"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_cards" ADD CONSTRAINT "deck_cards_version_id_deck_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "marketplace"."deck_versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."deck_versions" ADD CONSTRAINT "deck_versions_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "marketplace"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "marketplace"."decks" ADD CONSTRAINT "decks_owner_user_id_authors_user_id_fk" FOREIGN KEY ("owner_user_id") REFERENCES "marketplace"."authors"("user_id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "cards"."reviews" ADD CONSTRAINT "reviews_card_id_cards_id_fk" FOREIGN KEY ("card_id") REFERENCES "cards"."cards"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "cards"."study_sessions" ADD CONSTRAINT "study_sessions_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "cards"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "cards"."tags" ADD CONSTRAINT "tags_deck_id_decks_id_fk" FOREIGN KEY ("deck_id") REFERENCES "cards"."decks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "cards_deck_idx" ON "cards"."cards" USING btree ("deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "cards_user_idx" ON "cards"."cards" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "decks_user_idx" ON "cards"."decks" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "imports_user_idx" ON "cards"."import_jobs" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "imports_state_idx" ON "cards"."import_jobs" USING btree ("state");--> statement-breakpoint
|
||||
CREATE INDEX "ai_moderation_log_version_idx" ON "marketplace"."ai_moderation_log" USING btree ("version_id");--> statement-breakpoint
|
||||
CREATE INDEX "ai_moderation_log_verdict_idx" ON "marketplace"."ai_moderation_log" USING btree ("verdict");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "author_follows_pk" ON "marketplace"."author_follows" USING btree ("follower_user_id","author_user_id");--> statement-breakpoint
|
||||
CREATE INDEX "author_follows_author_idx" ON "marketplace"."author_follows" USING btree ("author_user_id");--> statement-breakpoint
|
||||
CREATE INDEX "author_follows_follower_idx" ON "marketplace"."author_follows" USING btree ("follower_user_id");--> statement-breakpoint
|
||||
CREATE INDEX "author_payouts_author_idx" ON "marketplace"."author_payouts" USING btree ("author_user_id");--> statement-breakpoint
|
||||
CREATE INDEX "author_payouts_purchase_idx" ON "marketplace"."author_payouts" USING btree ("source_purchase_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "authors_slug_idx" ON "marketplace"."authors" USING btree ("slug");--> statement-breakpoint
|
||||
CREATE INDEX "authors_verified_idx" ON "marketplace"."authors" USING btree ("verified_mana","verified_community");--> statement-breakpoint
|
||||
CREATE INDEX "card_discussions_hash_idx" ON "marketplace"."card_discussions" USING btree ("card_content_hash");--> statement-breakpoint
|
||||
CREATE INDEX "card_discussions_deck_idx" ON "marketplace"."card_discussions" USING btree ("deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "card_discussions_parent_idx" ON "marketplace"."card_discussions" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "deck_forks_pk" ON "marketplace"."deck_forks" USING btree ("user_id","source_deck_id","source_version_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_forks_source_idx" ON "marketplace"."deck_forks" USING btree ("source_deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_pull_requests_deck_idx" ON "marketplace"."deck_pull_requests" USING btree ("deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_pull_requests_status_idx" ON "marketplace"."deck_pull_requests" USING btree ("deck_id","status");--> statement-breakpoint
|
||||
CREATE INDEX "deck_pull_requests_author_idx" ON "marketplace"."deck_pull_requests" USING btree ("author_user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "deck_purchases_buyer_deck_idx" ON "marketplace"."deck_purchases" USING btree ("buyer_user_id","deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_purchases_buyer_idx" ON "marketplace"."deck_purchases" USING btree ("buyer_user_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_purchases_deck_idx" ON "marketplace"."deck_purchases" USING btree ("deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_reports_deck_idx" ON "marketplace"."deck_reports" USING btree ("deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_reports_status_idx" ON "marketplace"."deck_reports" USING btree ("status");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "deck_stars_pk" ON "marketplace"."deck_stars" USING btree ("user_id","deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_stars_deck_idx" ON "marketplace"."deck_stars" USING btree ("deck_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "deck_subscriptions_pk" ON "marketplace"."deck_subscriptions" USING btree ("user_id","deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_subscriptions_deck_idx" ON "marketplace"."deck_subscriptions" USING btree ("deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_subscriptions_user_idx" ON "marketplace"."deck_subscriptions" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "deck_tags_pk" ON "marketplace"."deck_tags" USING btree ("deck_id","tag_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_tags_tag_idx" ON "marketplace"."deck_tags" USING btree ("tag_id");--> statement-breakpoint
|
||||
CREATE INDEX "media_files_user_idx" ON "cards"."media_files" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "media_card_idx" ON "cards"."media_refs" USING btree ("card_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "deck_cards_version_ord_idx" ON "marketplace"."deck_cards" USING btree ("version_id","ord");--> statement-breakpoint
|
||||
CREATE INDEX "deck_cards_hash_idx" ON "marketplace"."deck_cards" USING btree ("content_hash");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "deck_versions_deck_semver_idx" ON "marketplace"."deck_versions" USING btree ("deck_id","semver");--> statement-breakpoint
|
||||
CREATE INDEX "deck_versions_deck_idx" ON "marketplace"."deck_versions" USING btree ("deck_id");--> statement-breakpoint
|
||||
CREATE INDEX "deck_versions_hash_idx" ON "marketplace"."deck_versions" USING btree ("content_hash");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "decks_slug_idx" ON "marketplace"."decks" USING btree ("slug");--> statement-breakpoint
|
||||
CREATE INDEX "decks_owner_idx" ON "marketplace"."decks" USING btree ("owner_user_id");--> statement-breakpoint
|
||||
CREATE INDEX "decks_featured_idx" ON "marketplace"."decks" USING btree ("is_featured");--> statement-breakpoint
|
||||
CREATE INDEX "reviews_user_due_idx" ON "cards"."reviews" USING btree ("user_id","due");--> statement-breakpoint
|
||||
CREATE INDEX "sessions_user_started_idx" ON "cards"."study_sessions" USING btree ("user_id","started_at");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "tag_definitions_slug_idx" ON "marketplace"."tag_definitions" USING btree ("slug");--> statement-breakpoint
|
||||
CREATE INDEX "tag_definitions_parent_idx" ON "marketplace"."tag_definitions" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE INDEX "tags_deck_idx" ON "cards"."tags" USING btree ("deck_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "tags_deck_name_uniq" ON "cards"."tags" USING btree ("deck_id","name");
|
||||
2896
apps/api/src/db/migrations/meta/0000_snapshot.json
Normal file
2896
apps/api/src/db/migrations/meta/0000_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
13
apps/api/src/db/migrations/meta/_journal.json
Normal file
13
apps/api/src/db/migrations/meta/_journal.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1778604624860,
|
||||
"tag": "0000_baseline",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue