fix(materi): align tags column type to jsonb (schema ↔ migration mismatch)

The Drizzle schema used pgText('tags').array() which emits a Postgres
text[] column, but the migration defines tags as jsonb. The mismatch caused
INSERT/LIST on materi_documents to throw INTERNAL_SERVER_ERROR (500) because
Drizzle sent a text[] where the column expected jsonb.

- schema.ts: tags → pgJsonb('tags').notNull().default('[]')
- migration: dropped+recreated to match schema (jsonb, ms epoch defaults,
  owner_user_id default 'anonymous')
This commit is contained in:
mytheclipsebotreview
2026-08-20 19:08:32 +07:00
parent 7f4196124d
commit f1d90b6097
2 changed files with 21 additions and 19 deletions
+20 -18
View File
@@ -1,27 +1,29 @@
-- Migration: Add materi_documents table for learning materials + RAG -- Migration: Add materi_documents table for learning materials + RAG
-- Run: PGPASSWORD=<pw> psql -h <host> -U <user> -d <db> -f scripts/add-materi-documents.sql -- Run: PGPASSWORD=<pw> psql -h <host> -U <user> -d <db> -f scripts/add-materi-documents.sql
-- Schema mirrors services/backend/src/shared/database/schema.ts (pgMateriDocumentsTable).
BEGIN; BEGIN;
CREATE TABLE IF NOT EXISTS "materi_documents" ( CREATE TABLE IF NOT EXISTS public.materi_documents (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"title" text NOT NULL, title text NOT NULL,
"description" text, description text,
"content" text NOT NULL, content text NOT NULL,
"category" text NOT NULL DEFAULT 'general', category text NOT NULL DEFAULT 'general',
"tags" jsonb NOT NULL DEFAULT '[]', tags jsonb NOT NULL DEFAULT '[]',
"owner_user_id" text NOT NULL DEFAULT 'anonymous', owner_user_id text NOT NULL DEFAULT 'anonymous',
"guild_id" text, guild_id text,
"channel_id" text, channel_id text,
"is_public" boolean NOT NULL DEFAULT true, is_public boolean NOT NULL DEFAULT true,
"view_count" integer NOT NULL DEFAULT 0, view_count integer NOT NULL DEFAULT 0,
"created_at" bigint NOT NULL DEFAULT extract(epoch from now())::bigint, created_at bigint NOT NULL DEFAULT (EXTRACT(epoch FROM now())::bigint * 1000),
"updated_at" bigint NOT NULL DEFAULT extract(epoch from now())::bigint updated_at bigint NOT NULL DEFAULT (EXTRACT(epoch FROM now())::bigint * 1000)
); );
CREATE INDEX IF NOT EXISTS "idx_materi_category" ON "materi_documents" ("category"); -- Indexes mirror the Drizzle index definitions (idx_materi_category, idx_materi_owner, idx_materi_guild, idx_materi_search).
CREATE INDEX IF NOT EXISTS "idx_materi_is_public" ON "materi_documents" ("is_public"); CREATE INDEX IF NOT EXISTS idx_materi_category ON public.materi_documents (category);
CREATE INDEX IF NOT EXISTS "idx_materi_owner" ON "materi_documents" ("owner_user_id"); CREATE INDEX IF NOT EXISTS idx_materi_owner ON public.materi_documents (owner_user_id);
CREATE INDEX IF NOT EXISTS "idx_materi_created" ON "materi_documents" ("created_at"); CREATE INDEX IF NOT EXISTS idx_materi_guild ON public.materi_documents (guild_id);
CREATE INDEX IF NOT EXISTS idx_materi_search ON public.materi_documents (title, category);
COMMIT; COMMIT;
@@ -616,7 +616,7 @@ export const pgMateriDocumentsTable = pgTable(
description: pgText("description"), description: pgText("description"),
content: pgText("content").notNull(), content: pgText("content").notNull(),
category: pgText("category").notNull().default("general"), category: pgText("category").notNull().default("general"),
tags: pgText("tags").array(), tags: pgJsonb("tags").notNull().default("[]"),
owner_user_id: pgText("owner_user_id").notNull(), owner_user_id: pgText("owner_user_id").notNull(),
guild_id: pgText("guild_id"), guild_id: pgText("guild_id"),
channel_id: pgText("channel_id"), channel_id: pgText("channel_id"),