diff --git a/CHANGELOG.md b/CHANGELOG.md index 57c690d..8b95440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ 本文件的格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), 并且本项目遵循 [语义化版本 (Semantic Versioning)](https://semver.org/lang/zh-CN/spec/v2.0.0.html)。 +## [1.2.3] - 2026-01-15 + +### 新增 +- **自动化数据库迁移**: 引入了自动化数据库初始化与迁移机制。 + - 添加了 `src/db/migrate.ts` 脚本,使用 Drizzle Migrator 自动应用挂起的迁移。 + - 更新了 `Dockerfile`,使容器启动时自动执行数据库迁移。 + - 在 `package.json` 中新增了 `db:migrate:deploy` 脚本。 + +### 修复 +- **初始化错误**: 修复了在全新环境下启动时因缺少数据库表导致的 `relation "users" does not exist` 错误。 +- **迁移历史**: 清理并重新生成了初始迁移文件,确保所有表在全新部署时能正确创建。 + ## [1.2.2] - 2026-01-14 ### 变更 diff --git a/Dockerfile b/Dockerfile index 3d0a432..9ab3d6a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,4 +23,4 @@ WORKDIR /app/apps/server EXPOSE 3000 -CMD ["bun", "run", "start"] +CMD ["sh", "-c", "bun run db:migrate:deploy && bun run start"] diff --git a/README.md b/README.md index 502d979..a63663e 100644 --- a/README.md +++ b/README.md @@ -71,15 +71,15 @@ FEISHU_APP_ID="cli_xxx" FEISHU_APP_SECRET="xxx" ADMIN_EMAILS="user1@example.com,user2@example.com" # 管理员列表 -# 数据库推送 -cd apps/server && bun run db:push +# 数据库推送/迁移 +cd apps/server && bun run db:migrate:deploy # 启动开发环境 bun run dev ``` ### 3. Docker 部署 -项目支持使用 Docker Compose 快速部署: +项目支持使用 Docker Compose 快速部署,且**数据库会自动进行初始化与迁移**: ```bash # 复制并填写环境变量 diff --git a/apps/server/drizzle/0000_famous_lionheart.sql b/apps/server/drizzle/0000_stiff_electro.sql similarity index 57% rename from apps/server/drizzle/0000_famous_lionheart.sql rename to apps/server/drizzle/0000_stiff_electro.sql index 68b54f4..14bfa11 100644 --- a/apps/server/drizzle/0000_famous_lionheart.sql +++ b/apps/server/drizzle/0000_stiff_electro.sql @@ -9,7 +9,8 @@ CREATE TABLE "alert_logs" ( --> statement-breakpoint CREATE TABLE "alert_tasks" ( "id" text PRIMARY KEY NOT NULL, - "topic_slug" text NOT NULL, + "topic_slug" text, + "sender_id" text, "status" text DEFAULT 'pending' NOT NULL, "recipient_count" integer DEFAULT 0, "success_count" integer DEFAULT 0, @@ -19,6 +20,12 @@ CREATE TABLE "alert_tasks" ( "updated_at" timestamp DEFAULT now() NOT NULL ); --> statement-breakpoint +CREATE TABLE "known_group_chats" ( + "chat_id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "last_active_at" timestamp DEFAULT now() +); +--> statement-breakpoint CREATE TABLE "subscriptions" ( "user_id" text NOT NULL, "topic_id" text NOT NULL, @@ -26,6 +33,15 @@ CREATE TABLE "subscriptions" ( CONSTRAINT "subscriptions_user_id_topic_id_pk" PRIMARY KEY("user_id","topic_id") ); --> statement-breakpoint +CREATE TABLE "topic_group_chats" ( + "id" text PRIMARY KEY NOT NULL, + "topic_id" text NOT NULL, + "chat_id" text NOT NULL, + "name" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "created_by" text +); +--> statement-breakpoint CREATE TABLE "topics" ( "id" text PRIMARY KEY NOT NULL, "slug" text NOT NULL, @@ -33,6 +49,7 @@ CREATE TABLE "topics" ( "description" text, "status" text DEFAULT 'approved' NOT NULL, "created_by" text, + "approved_by" text, "created_at" timestamp DEFAULT now() NOT NULL, CONSTRAINT "topics_slug_unique" UNIQUE("slug") ); @@ -43,10 +60,16 @@ CREATE TABLE "users" ( "feishu_user_id" text NOT NULL, "email" text, "is_admin" boolean DEFAULT false, - CONSTRAINT "users_email_unique" UNIQUE("email") + "personal_token" text NOT NULL, + CONSTRAINT "users_email_unique" UNIQUE("email"), + CONSTRAINT "users_personal_token_unique" UNIQUE("personal_token") ); --> statement-breakpoint ALTER TABLE "alert_logs" ADD CONSTRAINT "alert_logs_task_id_alert_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."alert_tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "alert_tasks" ADD CONSTRAINT "alert_tasks_sender_id_users_id_fk" FOREIGN KEY ("sender_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "subscriptions" ADD CONSTRAINT "subscriptions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "subscriptions" ADD CONSTRAINT "subscriptions_topic_id_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."topics"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "topics" ADD CONSTRAINT "topics_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file +ALTER TABLE "topic_group_chats" ADD CONSTRAINT "topic_group_chats_topic_id_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."topics"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "topic_group_chats" ADD CONSTRAINT "topic_group_chats_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "topics" ADD CONSTRAINT "topics_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "topics" ADD CONSTRAINT "topics_approved_by_users_id_fk" FOREIGN KEY ("approved_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/apps/server/drizzle/0001_bumpy_orphan.sql b/apps/server/drizzle/0001_bumpy_orphan.sql deleted file mode 100644 index 039c725..0000000 --- a/apps/server/drizzle/0001_bumpy_orphan.sql +++ /dev/null @@ -1,18 +0,0 @@ -CREATE TABLE "topic_group_webhooks" ( - "id" text PRIMARY KEY NOT NULL, - "topic_id" text NOT NULL, - "name" text NOT NULL, - "url" text NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - "created_by" text -); ---> statement-breakpoint -ALTER TABLE "alert_tasks" ALTER COLUMN "topic_slug" DROP NOT NULL;--> statement-breakpoint -ALTER TABLE "alert_tasks" ADD COLUMN "sender_id" text;--> statement-breakpoint -ALTER TABLE "topics" ADD COLUMN "approved_by" text;--> statement-breakpoint -ALTER TABLE "users" ADD COLUMN "personal_token" text NOT NULL;--> statement-breakpoint -ALTER TABLE "topic_group_webhooks" ADD CONSTRAINT "topic_group_webhooks_topic_id_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."topics"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "topic_group_webhooks" ADD CONSTRAINT "topic_group_webhooks_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "alert_tasks" ADD CONSTRAINT "alert_tasks_sender_id_users_id_fk" FOREIGN KEY ("sender_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "topics" ADD CONSTRAINT "topics_approved_by_users_id_fk" FOREIGN KEY ("approved_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "users" ADD CONSTRAINT "users_personal_token_unique" UNIQUE("personal_token"); \ No newline at end of file diff --git a/apps/server/package.json b/apps/server/package.json index f9966c7..30fea03 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -6,6 +6,7 @@ "start": "bun run src/index.ts", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", + "db:migrate:deploy": "bun run src/db/migrate.ts", "db:push": "drizzle-kit push", "db:studio": "drizzle-kit studio" }, @@ -24,4 +25,4 @@ "drizzle-kit": "^0.31.8", "pino-pretty": "^13.1.3" } -} +} \ No newline at end of file diff --git a/apps/server/src/db/migrate.ts b/apps/server/src/db/migrate.ts new file mode 100644 index 0000000..4d2851d --- /dev/null +++ b/apps/server/src/db/migrate.ts @@ -0,0 +1,26 @@ +import { drizzle } from "drizzle-orm/postgres-js"; +import { migrate } from "drizzle-orm/postgres-js/migrator"; +import postgres from "postgres"; +import * as schema from "./schema"; + +const connectionString = + process.env.DATABASE_URL || + "postgres://postgres:password@localhost:5432/alert_message_center"; + +async function main() { + console.log("⏳ Running migrations..."); + const sql = postgres(connectionString, { max: 1 }); + const db = drizzle(sql, { schema }); + + try { + await migrate(db, { migrationsFolder: "./drizzle" }); + console.log("✅ Migrations completed!"); + } catch (error) { + console.error("❌ Migration failed:", error); + process.exit(1); + } finally { + await sql.end(); + } +} + +main(); diff --git a/docs/copilot-context.md b/docs/copilot-context.md index 25b0882..88a5eb0 100644 --- a/docs/copilot-context.md +++ b/docs/copilot-context.md @@ -1,4 +1,4 @@ -# Project Context for GitHub Copilot (v1.2.2) +# Project Context for GitHub Copilot (v1.2.3) This document provides technical context, architectural decisions, and code conventions for the **Alert Message Center** project. It is intended to help AI assistants understand the codebase. @@ -216,6 +216,7 @@ The database schema is defined in `apps/server/src/db/schema.ts`. - GitHub Actions automates building a multi-stage Docker image and pushing it to GitHub Container Registry (GHCR). - Image path: `ghcr.io/${USER}/alert-message-center`. - Deployment Architecture: A single container runs the Bun server, which serves API requests and static frontend assets (via `hono/bun`'s `serveStatic`). + - **Database Initialization**: The Docker entrypoint automatically runs `bun run db:migrate:deploy` before starting the server to ensure the schema is up-to-date in new environments. ## 8. Core Documents diff --git a/todo.md b/todo.md index 7d251e1..70a9122 100644 --- a/todo.md +++ b/todo.md @@ -27,4 +27,5 @@ - [x] **Long Connection**: WebSocket support for intranet deployments. - [x] **Structured Logging**: Integrated `pino` for better observability. - [x] **Linting**: Tightened Biome rules and resolved all a11y/correctness issues. +- [x] **Automated Migrations**: Automatically initialize database schema on startup (especially in Docker).