fix(presi): wire up db:push for presi schema via @mana/api

The presi module's schema was defined inline in routes.ts but had no
working db:push mechanism — the old references to @presi/server and
@presi/backend no longer exist after consolidation. Extracts schema
into its own file, adds a dedicated drizzle config, and updates the
setup script so tables are actually created.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-12 14:32:44 +02:00
parent 474ba93d70
commit a9c51517eb
7 changed files with 104 additions and 75 deletions

View file

@ -0,0 +1,11 @@
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/modules/presi/schema.ts',
out: './drizzle/presi',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL || 'postgresql://mana:devpassword@localhost:5432/mana_platform',
},
schemaFilter: ['presi'],
});

View file

@ -9,7 +9,8 @@
"start": "bun run dist/index.js", "start": "bun run dist/index.js",
"type-check": "tsc --noEmit", "type-check": "tsc --noEmit",
"db:generate": "drizzle-kit generate", "db:generate": "drizzle-kit generate",
"db:push": "drizzle-kit push" "db:push": "drizzle-kit push",
"db:push:presi": "drizzle-kit push --config=drizzle.presi.config.ts"
}, },
"dependencies": { "dependencies": {
"@ai-sdk/openai-compatible": "^2.0.41", "@ai-sdk/openai-compatible": "^2.0.41",

View file

@ -14,80 +14,20 @@ import type { AuthVariables } from '@mana/shared-hono';
import { drizzle } from 'drizzle-orm/postgres-js'; import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres'; import postgres from 'postgres';
import { import {
pgSchema, decks,
uuid, slides,
text, themes,
boolean, sharedDecks,
timestamp, decksRelations,
integer, slidesRelations,
jsonb, sharedDecksRelations,
index, } from './schema.js';
} from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
// ─── DB Schema (read-only for share lookups) ──────────────── // ─── DB Connection ─────────────────────────────────────────
const DATABASE_URL = const DATABASE_URL =
process.env.DATABASE_URL ?? 'postgresql://mana:devpassword@localhost:5432/mana_platform'; process.env.DATABASE_URL ?? 'postgresql://mana:devpassword@localhost:5432/mana_platform';
const presiSchema = pgSchema('presi');
const decks = presiSchema.table('decks', {
id: uuid('id').primaryKey().defaultRandom(),
userId: text('user_id').notNull(),
title: text('title').notNull(),
description: text('description'),
themeId: uuid('theme_id'),
isPublic: boolean('is_public').default(false).notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
const slides = presiSchema.table(
'slides',
{
id: uuid('id').primaryKey().defaultRandom(),
deckId: uuid('deck_id').notNull(),
order: integer('order').default(0).notNull(),
content: jsonb('content'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
},
(table) => [index('slides_deck_order_api_idx').on(table.deckId, table.order)]
);
const themes = presiSchema.table('themes', {
id: uuid('id').primaryKey().defaultRandom(),
name: text('name').notNull(),
colors: jsonb('colors'),
fonts: jsonb('fonts'),
isDefault: boolean('is_default').default(false),
});
const sharedDecks = presiSchema.table(
'shared_decks',
{
id: uuid('id').primaryKey().defaultRandom(),
deckId: uuid('deck_id').notNull(),
shareCode: text('share_code').notNull().unique(),
expiresAt: timestamp('expires_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
},
(table) => [index('shared_decks_deck_id_api_idx').on(table.deckId)]
);
const decksRelations = relations(decks, ({ many }) => ({
slides: many(slides),
sharedDecks: many(sharedDecks),
}));
const slidesRelations = relations(slides, ({ one }) => ({
deck: one(decks, { fields: [slides.deckId], references: [decks.id] }),
}));
const sharedDecksRelations = relations(sharedDecks, ({ one }) => ({
deck: one(decks, { fields: [sharedDecks.deckId], references: [decks.id] }),
}));
const connection = postgres(DATABASE_URL, { max: 5, idle_timeout: 20 }); const connection = postgres(DATABASE_URL, { max: 5, idle_timeout: 20 });
const db = drizzle(connection, { const db = drizzle(connection, {
schema: { schema: {

View file

@ -0,0 +1,76 @@
/**
* Presi module DB schema
*
* Lives in mana_platform under its own pgSchema('presi').
* Shared between routes.ts (runtime) and drizzle-kit (migrations/push).
*/
import {
pgSchema,
uuid,
text,
boolean,
timestamp,
integer,
jsonb,
index,
} from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
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(),
description: text('description'),
themeId: uuid('theme_id'),
isPublic: boolean('is_public').default(false).notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const slides = presiSchema.table(
'slides',
{
id: uuid('id').primaryKey().defaultRandom(),
deckId: uuid('deck_id').notNull(),
order: integer('order').default(0).notNull(),
content: jsonb('content'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
},
(table) => [index('slides_deck_order_api_idx').on(table.deckId, table.order)]
);
export const themes = presiSchema.table('themes', {
id: uuid('id').primaryKey().defaultRandom(),
name: text('name').notNull(),
colors: jsonb('colors'),
fonts: jsonb('fonts'),
isDefault: boolean('is_default').default(false),
});
export const sharedDecks = presiSchema.table(
'shared_decks',
{
id: uuid('id').primaryKey().defaultRandom(),
deckId: uuid('deck_id').notNull(),
shareCode: text('share_code').notNull().unique(),
expiresAt: timestamp('expires_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
},
(table) => [index('shared_decks_deck_id_api_idx').on(table.deckId)]
);
export const decksRelations = relations(decks, ({ many }) => ({
slides: many(slides),
sharedDecks: many(sharedDecks),
}));
export const slidesRelations = relations(slides, ({ one }) => ({
deck: one(decks, { fields: [slides.deckId], references: [decks.id] }),
}));
export const sharedDecksRelations = relations(sharedDecks, ({ one }) => ({
deck: one(decks, { fields: [sharedDecks.deckId], references: [decks.id] }),
}));

View file

@ -10,5 +10,5 @@
"rootDir": "src", "rootDir": "src",
"types": ["bun-types"] "types": ["bun-types"]
}, },
"include": ["src/**/*.ts", "scripts/**/*.ts"] "include": ["src/**/*.ts", "scripts/**/*.ts", "drizzle.*.config.ts"]
} }

View file

@ -4,8 +4,8 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "pnpm --filter '@presi/*' run dev", "dev": "pnpm --filter '@presi/*' run dev",
"db:push": "pnpm --filter @presi/backend db:push", "db:push": "pnpm --filter @mana/api db:push:presi",
"db:studio": "pnpm --filter @presi/backend db:studio" "db:studio": "drizzle-kit studio --config=../../apps/api/drizzle.presi.config.ts"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5.7.2" "typescript": "^5.7.2"

View file

@ -54,9 +54,10 @@ create_schema_if_not_exists() {
push_schema() { push_schema() {
local filter=$1 local filter=$1
local name=$2 local name=$2
local script=${3:-db:push}
echo -e "${YELLOW}Pushing schema for ${name}...${NC}" echo -e "${YELLOW}Pushing schema for ${name}...${NC}"
local output local output
output=$(pnpm --filter "$filter" db:push --force 2>&1) output=$(pnpm --filter "$filter" "$script" --force 2>&1)
local exit_code=$? local exit_code=$?
if echo "$output" | grep -q "No projects matched the filters\|None of the selected packages has"; then if echo "$output" | grep -q "No projects matched the filters\|None of the selected packages has"; then
echo -e " ${YELLOW}⊘ Skipped (no db:push script for ${filter})${NC}" echo -e " ${YELLOW}⊘ Skipped (no db:push script for ${filter})${NC}"
@ -117,7 +118,7 @@ setup_service() {
push_schema "@traces/server" "traces" push_schema "@traces/server" "traces"
;; ;;
presi) presi)
push_schema "@presi/server" "presi" push_schema "@mana/api" "presi" "db:push:presi"
;; ;;
uload) uload)
push_schema "@mana/uload-database" "uload" push_schema "@mana/uload-database" "uload"