fix empty game database migrations
This commit is contained in:
@@ -15,6 +15,8 @@
|
|||||||
"typecheck": "tsc -b",
|
"typecheck": "tsc -b",
|
||||||
"prisma:generate:game": "prisma generate --schema prisma/game.prisma",
|
"prisma:generate:game": "prisma generate --schema prisma/game.prisma",
|
||||||
"prisma:generate:gateway": "prisma generate --schema prisma/gateway.prisma",
|
"prisma:generate:gateway": "prisma generate --schema prisma/gateway.prisma",
|
||||||
|
"prisma:migrate:deploy:game": "prisma migrate deploy --schema prisma/game.prisma",
|
||||||
|
"prisma:migrate:status:game": "prisma migrate status --schema prisma/game.prisma",
|
||||||
"prisma:db:push:game": "prisma db push --schema prisma/game.prisma",
|
"prisma:db:push:game": "prisma db push --schema prisma/game.prisma",
|
||||||
"prisma:db:push:gateway": "prisma db push --schema prisma/gateway.prisma"
|
"prisma:db:push:gateway": "prisma db push --schema prisma/gateway.prisma"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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