fix empty game database migrations
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
-- Baseline tables that predate the incremental Prisma migration history.
|
||||
--
|
||||
-- IF NOT EXISTS is intentional: existing installations were originally
|
||||
-- provisioned with `prisma db push` and already own these objects. This
|
||||
-- migration must be safe both for those databases and for a genuinely empty
|
||||
-- database running the full migration chain.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
CREATE TYPE "LogScope" AS ENUM ('SYSTEM', 'NATION', 'GENERAL', 'USER');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
CREATE TYPE "LogCategory" AS ENUM (
|
||||
'HISTORY',
|
||||
'SUMMARY',
|
||||
'ACTION',
|
||||
'BATTLE_BRIEF',
|
||||
'BATTLE_DETAIL',
|
||||
'USER'
|
||||
);
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "world_state" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"scenario_code" TEXT NOT NULL,
|
||||
"current_year" INTEGER NOT NULL,
|
||||
"current_month" INTEGER NOT NULL,
|
||||
"tick_seconds" INTEGER NOT NULL,
|
||||
"config" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"meta" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "world_state_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "nation" (
|
||||
"id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"color" TEXT NOT NULL,
|
||||
"capital_city_id" INTEGER,
|
||||
"gold" INTEGER NOT NULL DEFAULT 0,
|
||||
"rice" INTEGER NOT NULL DEFAULT 0,
|
||||
"tech" DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
"level" INTEGER NOT NULL DEFAULT 0,
|
||||
"type_code" TEXT NOT NULL DEFAULT 'che_중립',
|
||||
"meta" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
|
||||
CONSTRAINT "nation_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "city" (
|
||||
"id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"level" INTEGER NOT NULL,
|
||||
"nation_id" INTEGER NOT NULL DEFAULT 0,
|
||||
"supply_state" INTEGER NOT NULL DEFAULT 1,
|
||||
"front_state" INTEGER NOT NULL DEFAULT 0,
|
||||
"pop" INTEGER NOT NULL,
|
||||
"pop_max" INTEGER NOT NULL,
|
||||
"agri" INTEGER NOT NULL,
|
||||
"agri_max" INTEGER NOT NULL,
|
||||
"comm" INTEGER NOT NULL,
|
||||
"comm_max" INTEGER NOT NULL,
|
||||
"secu" INTEGER NOT NULL,
|
||||
"secu_max" INTEGER NOT NULL,
|
||||
"trust" INTEGER NOT NULL DEFAULT 0,
|
||||
"trade" INTEGER NOT NULL DEFAULT 100,
|
||||
"def" INTEGER NOT NULL,
|
||||
"def_max" INTEGER NOT NULL,
|
||||
"wall" INTEGER NOT NULL,
|
||||
"wall_max" INTEGER NOT NULL,
|
||||
"region" INTEGER NOT NULL,
|
||||
"conflict" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"meta" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
|
||||
CONSTRAINT "city_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "general" (
|
||||
"id" INTEGER NOT NULL,
|
||||
"user_id" TEXT,
|
||||
"name" TEXT NOT NULL,
|
||||
"nation_id" INTEGER NOT NULL DEFAULT 0,
|
||||
"city_id" INTEGER NOT NULL DEFAULT 0,
|
||||
"troop_id" INTEGER NOT NULL DEFAULT 0,
|
||||
"npc_state" INTEGER NOT NULL DEFAULT 0,
|
||||
"affinity" INTEGER,
|
||||
"born_year" INTEGER NOT NULL DEFAULT 180,
|
||||
"dead_year" INTEGER NOT NULL DEFAULT 300,
|
||||
"picture" TEXT,
|
||||
"image_server" INTEGER NOT NULL DEFAULT 0,
|
||||
"leadership" INTEGER NOT NULL DEFAULT 50,
|
||||
"strength" INTEGER NOT NULL DEFAULT 50,
|
||||
"intel" INTEGER NOT NULL DEFAULT 50,
|
||||
"injury" INTEGER NOT NULL DEFAULT 0,
|
||||
"experience" INTEGER NOT NULL DEFAULT 0,
|
||||
"dedication" INTEGER NOT NULL DEFAULT 0,
|
||||
"officer_level" INTEGER NOT NULL DEFAULT 0,
|
||||
"gold" INTEGER NOT NULL DEFAULT 1000,
|
||||
"rice" INTEGER NOT NULL DEFAULT 1000,
|
||||
"crew" INTEGER NOT NULL DEFAULT 0,
|
||||
"crew_type_id" INTEGER NOT NULL DEFAULT 0,
|
||||
"train" INTEGER NOT NULL DEFAULT 0,
|
||||
"atmos" INTEGER NOT NULL DEFAULT 0,
|
||||
"weapon_code" TEXT NOT NULL DEFAULT 'None',
|
||||
"book_code" TEXT NOT NULL DEFAULT 'None',
|
||||
"horse_code" TEXT NOT NULL DEFAULT 'None',
|
||||
"item_code" TEXT NOT NULL DEFAULT 'None',
|
||||
"turn_time" TIMESTAMP(3) NOT NULL,
|
||||
"recent_war_time" TIMESTAMP(3),
|
||||
"age" INTEGER NOT NULL DEFAULT 20,
|
||||
"start_age" INTEGER NOT NULL DEFAULT 20,
|
||||
"personal_code" TEXT NOT NULL DEFAULT 'None',
|
||||
"special_code" TEXT NOT NULL DEFAULT 'None',
|
||||
"special2_code" TEXT NOT NULL DEFAULT 'None',
|
||||
"last_turn" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"meta" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"penalty" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "general_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "message" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"mailbox" INTEGER NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"src" INTEGER NOT NULL,
|
||||
"dest" INTEGER NOT NULL,
|
||||
"time" TIMESTAMP(3) NOT NULL,
|
||||
"valid_until" TIMESTAMP(3) NOT NULL,
|
||||
"message" JSONB NOT NULL,
|
||||
|
||||
CONSTRAINT "message_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "troop" (
|
||||
"troop_leader" INTEGER NOT NULL,
|
||||
"nation" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "troop_pkey" PRIMARY KEY ("troop_leader")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "general_turn" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"general_id" INTEGER NOT NULL,
|
||||
"turn_idx" INTEGER NOT NULL,
|
||||
"action_code" TEXT NOT NULL,
|
||||
"arg" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "general_turn_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "general_turn_general_id_turn_idx_key"
|
||||
ON "general_turn"("general_id", "turn_idx");
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "nation_turn" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"nation_id" INTEGER NOT NULL,
|
||||
"officer_level" INTEGER NOT NULL,
|
||||
"turn_idx" INTEGER NOT NULL,
|
||||
"action_code" TEXT NOT NULL,
|
||||
"arg" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "nation_turn_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "nation_turn_nation_id_officer_level_turn_idx_key"
|
||||
ON "nation_turn"("nation_id", "officer_level", "turn_idx");
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "diplomacy" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"src_nation_id" INTEGER NOT NULL,
|
||||
"dest_nation_id" INTEGER NOT NULL,
|
||||
"state_code" INTEGER NOT NULL,
|
||||
"term" INTEGER NOT NULL DEFAULT 0,
|
||||
"is_dead" BOOLEAN NOT NULL DEFAULT false,
|
||||
"is_showing" BOOLEAN NOT NULL DEFAULT true,
|
||||
"meta" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "diplomacy_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "diplomacy_src_nation_id_dest_nation_id_key"
|
||||
ON "diplomacy"("src_nation_id", "dest_nation_id");
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "event" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"target_code" TEXT NOT NULL,
|
||||
"priority" INTEGER NOT NULL DEFAULT 0,
|
||||
"condition" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"action" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"meta" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "event_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "log_entry" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"scope" "LogScope" NOT NULL,
|
||||
"category" "LogCategory" NOT NULL,
|
||||
"sub_type" TEXT,
|
||||
"year" INTEGER NOT NULL,
|
||||
"month" INTEGER NOT NULL,
|
||||
"text" TEXT NOT NULL,
|
||||
"general_id" INTEGER,
|
||||
"nation_id" INTEGER,
|
||||
"user_id" INTEGER,
|
||||
"meta" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "log_entry_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "log_entry_scope_category_id_idx"
|
||||
ON "log_entry"("scope", "category", "id");
|
||||
CREATE INDEX IF NOT EXISTS "log_entry_general_id_category_id_idx"
|
||||
ON "log_entry"("general_id", "category", "id");
|
||||
CREATE INDEX IF NOT EXISTS "log_entry_nation_id_category_id_idx"
|
||||
ON "log_entry"("nation_id", "category", "id");
|
||||
CREATE INDEX IF NOT EXISTS "log_entry_user_id_category_id_idx"
|
||||
ON "log_entry"("user_id", "category", "id");
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "error_log" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"category" TEXT NOT NULL,
|
||||
"source" TEXT,
|
||||
"message" TEXT NOT NULL,
|
||||
"trace" TEXT,
|
||||
"context" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "error_log_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "error_log_category_id_idx"
|
||||
ON "error_log"("category", "id");
|
||||
@@ -0,0 +1,5 @@
|
||||
-- The Prisma model and auction runtime persist bid metadata, but the original
|
||||
-- auction migration predates that field. Existing db-push installations may
|
||||
-- already have it, so keep this reconciliation migration idempotent.
|
||||
ALTER TABLE "auction_bid"
|
||||
ADD COLUMN IF NOT EXISTS "meta" JSONB NOT NULL DEFAULT '{}'::jsonb;
|
||||
@@ -0,0 +1,38 @@
|
||||
# Game database migrations
|
||||
|
||||
`game.prisma`의 운영·검증 DB는 이 디렉터리의 migration chain으로
|
||||
준비한다. 새 환경을 `prisma db push`만으로 만들지 않는다.
|
||||
|
||||
## 적용
|
||||
|
||||
Git에서 제외된 환경 파일 또는 secret 주입으로 `DATABASE_URL`을 설정한 뒤
|
||||
저장소 루트에서 실행한다.
|
||||
|
||||
```bash
|
||||
pnpm --filter @sammo-ts/infra prisma:migrate:deploy:game
|
||||
pnpm --filter @sammo-ts/infra prisma:migrate:status:game
|
||||
```
|
||||
|
||||
`20250101000000_init_game_schema`는 증분 migration 이력이 생기기 전에
|
||||
`db push`로 제공되던 핵심 game table의 baseline이다. 기존 설치에는
|
||||
이미 같은 object가 있으므로 type, table과 index 생성은 idempotent하게
|
||||
작성했다. 새 migration은 이 baseline 또는 기존 migration 파일을
|
||||
수정하지 말고 뒤에 추가한다.
|
||||
|
||||
## 빈 DB 검증
|
||||
|
||||
다른 개발 DB나 volume을 재사용하지 말고 전용 임시 PostgreSQL database를
|
||||
만든다. 위 deploy 명령을 한 번 실행해 전체 migration이 성공하는지,
|
||||
두 번째 실행이 `No pending migrations to apply`인지 확인한다. 최소한
|
||||
다음도 확인한다.
|
||||
|
||||
- `_prisma_migrations`의 모든 행이 완료 상태
|
||||
- `world_state`, `nation`, `city`, `general`, `message`, `troop`,
|
||||
`general_turn`, `nation_turn`, `diplomacy`, `event`, `log_entry`,
|
||||
`error_log` 존재
|
||||
- `nation.chief_general_id` 존재
|
||||
- `city.trade` nullable, `city.trust` REAL
|
||||
- `auction_bid.meta` JSONB, NOT NULL
|
||||
|
||||
검증 뒤에는 이름을 확인한 임시 database와 role만 제거한다. 공유 개발
|
||||
database나 Compose volume을 삭제하지 않는다.
|
||||
Reference in New Issue
Block a user