diff --git a/backend/src/migrations/1783000000000-AddDriveFileExtractDropProjectAttachments.ts b/backend/src/migrations/1783000000000-AddDriveFileExtractDropProjectAttachments.ts index 6853e7b..7e54240 100644 --- a/backend/src/migrations/1783000000000-AddDriveFileExtractDropProjectAttachments.ts +++ b/backend/src/migrations/1783000000000-AddDriveFileExtractDropProjectAttachments.ts @@ -5,35 +5,35 @@ import { MigrationInterface, QueryRunner } from 'typeorm'; // → 연동 폴더 문서를 NCP 에서 받아 텍스트 추출·캐싱(AI 업무 추천 주입) // - 프로젝트 직접 업로드(project_attachments) 폐기 → 테이블 제거(완전 대체) // 운영(migrationsRun)에서만 실행되며, dev 는 synchronize 로 자동 반영된다. +// +// 멱등·견고하게 작성: 과거 synchronize 로 만들어진 운영 DB(제약 이름 상이)나 +// 부분 적용 상태에서도 실패하지 않도록 IF [NOT] EXISTS / CASCADE 를 사용한다. export class AddDriveFileExtractDropProjectAttachments1783000000000 implements MigrationInterface { name = 'AddDriveFileExtractDropProjectAttachments1783000000000'; public async up(queryRunner: QueryRunner): Promise { - // 1) drive_files AI 추출 캐시 컬럼 + // 1) drive_files AI 추출 캐시 컬럼 (이미 있으면 건너뜀) await queryRunner.query( - `ALTER TABLE "drive_files" ADD "extracted_text" text`, + `ALTER TABLE "drive_files" ADD COLUMN IF NOT EXISTS "extracted_text" text`, ); await queryRunner.query( - `ALTER TABLE "drive_files" ADD "extract_status" character varying NOT NULL DEFAULT 'pending'`, + `ALTER TABLE "drive_files" ADD COLUMN IF NOT EXISTS "extract_status" character varying NOT NULL DEFAULT 'pending'`, ); // 2) project_attachments 폐기 (드라이브 연동으로 대체) + // 제약 이름에 의존하지 않도록 CASCADE 로 테이블·FK 를 한 번에 제거. await queryRunner.query( - `ALTER TABLE "project_attachments" DROP CONSTRAINT "FK_project_attachments_uploader"`, + `DROP TABLE IF EXISTS "project_attachments" CASCADE`, ); - await queryRunner.query( - `ALTER TABLE "project_attachments" DROP CONSTRAINT "FK_project_attachments_project"`, - ); - await queryRunner.query(`DROP TABLE "project_attachments"`); } public async down(queryRunner: QueryRunner): Promise { // 1) project_attachments 복원 (AddProjectAttachments + ExtractedText 와 동일 스키마) await queryRunner.query( - `CREATE TABLE "project_attachments" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "stored_name" character varying NOT NULL, "size" integer NOT NULL, "mime" character varying NOT NULL, "kind" character varying NOT NULL, "extracted_text" text, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "project_id" uuid, "uploader_id" uuid, CONSTRAINT "PK_project_attachments" PRIMARY KEY ("id"))`, + `CREATE TABLE IF NOT EXISTS "project_attachments" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "stored_name" character varying NOT NULL, "size" integer NOT NULL, "mime" character varying NOT NULL, "kind" character varying NOT NULL, "extracted_text" text, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "project_id" uuid, "uploader_id" uuid, CONSTRAINT "PK_project_attachments" PRIMARY KEY ("id"))`, ); await queryRunner.query( - `CREATE INDEX "IDX_project_attachments_project" ON "project_attachments" ("project_id")`, + `CREATE INDEX IF NOT EXISTS "IDX_project_attachments_project" ON "project_attachments" ("project_id")`, ); await queryRunner.query( `ALTER TABLE "project_attachments" ADD CONSTRAINT "FK_project_attachments_project" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, @@ -44,10 +44,10 @@ export class AddDriveFileExtractDropProjectAttachments1783000000000 implements M // 2) drive_files 추출 캐시 컬럼 제거 await queryRunner.query( - `ALTER TABLE "drive_files" DROP COLUMN "extract_status"`, + `ALTER TABLE "drive_files" DROP COLUMN IF EXISTS "extract_status"`, ); await queryRunner.query( - `ALTER TABLE "drive_files" DROP COLUMN "extracted_text"`, + `ALTER TABLE "drive_files" DROP COLUMN IF EXISTS "extracted_text"`, ); } }