CREATE TYPE "public"."audit_actor_type" AS ENUM('system', 'cli_user');--> statement-breakpoint CREATE TYPE "public"."build_artifact_kind" AS ENUM('dist', 'manifest', 'report', 'log');--> statement-breakpoint CREATE TYPE "public"."build_job_status" AS ENUM('queued', 'snapshotting', 'building', 'validating', 'succeeded', 'failed', 'cancelled', 'stale');--> statement-breakpoint CREATE TABLE "audit_logs" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid, "actor_type" "audit_actor_type" NOT NULL, "actor_id" text, "actor_label" text NOT NULL, "command_id" text, "action" text NOT NULL, "object_type" text NOT NULL, "object_id" text NOT NULL, "before_value" jsonb, "after_value" jsonb, "occurred_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE "build_artifacts" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "build_job_id" uuid NOT NULL, "kind" "build_artifact_kind" NOT NULL, "storage_path" text NOT NULL, "sha256" text NOT NULL, "byte_size" bigint NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE "build_input_snapshots" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "build_job_id" uuid NOT NULL, "site_id" uuid NOT NULL, "content_snapshot_hash" text NOT NULL, "route_snapshot_hash" text NOT NULL, "asset_manifest_hash" text NOT NULL, "site_config_revision_id" uuid, "theme_key" text NOT NULL, "theme_version" text NOT NULL, "theme_source_hash" text NOT NULL, "dependency_lock_hash" text NOT NULL, "source_commit" text NOT NULL, "node_version" text NOT NULL, "pnpm_version" text NOT NULL, "astro_version" text NOT NULL, "locale" text NOT NULL, "timezone" text NOT NULL, "build_timestamp" timestamp with time zone NOT NULL, "manifest_path" text NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "build_input_snapshots_build_job_id_unique" UNIQUE("build_job_id") ); --> statement-breakpoint CREATE TABLE "build_jobs" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "status" "build_job_status" NOT NULL, "idempotency_key" text NOT NULL, "attempt" integer DEFAULT 0 NOT NULL, "worker_id" text, "started_at" timestamp with time zone, "heartbeat_at" timestamp with time zone, "finished_at" timestamp with time zone, "error_code" text, "error_summary" text, "created_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "build_jobs_idempotency_key_unique" UNIQUE("idempotency_key") ); --> statement-breakpoint CREATE TABLE "validation_issues" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "validation_run_id" uuid NOT NULL, "severity" text NOT NULL, "rule" text NOT NULL, "url" text, "evidence" jsonb NOT NULL, "resolution_status" text NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE "validation_runs" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "build_job_id" uuid, "rule_version" text NOT NULL, "status" text NOT NULL, "started_at" timestamp with time zone, "finished_at" timestamp with time zone, "created_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "build_artifacts" ADD CONSTRAINT "build_artifacts_build_job_id_build_jobs_id_fk" FOREIGN KEY ("build_job_id") REFERENCES "public"."build_jobs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "build_input_snapshots" ADD CONSTRAINT "build_input_snapshots_build_job_id_build_jobs_id_fk" FOREIGN KEY ("build_job_id") REFERENCES "public"."build_jobs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "build_input_snapshots" ADD CONSTRAINT "build_input_snapshots_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "build_input_snapshots" ADD CONSTRAINT "build_input_snapshots_site_config_revision_id_site_config_revisions_id_fk" FOREIGN KEY ("site_config_revision_id") REFERENCES "public"."site_config_revisions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "build_jobs" ADD CONSTRAINT "build_jobs_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "validation_issues" ADD CONSTRAINT "validation_issues_validation_run_id_validation_runs_id_fk" FOREIGN KEY ("validation_run_id") REFERENCES "public"."validation_runs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "validation_runs" ADD CONSTRAINT "validation_runs_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "validation_runs" ADD CONSTRAINT "validation_runs_build_job_id_build_jobs_id_fk" FOREIGN KEY ("build_job_id") REFERENCES "public"."build_jobs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint CREATE UNIQUE INDEX "build_artifacts_uq" ON "build_artifacts" USING btree ("build_job_id","kind","storage_path");--> statement-breakpoint -- ↓ 手写追加(drizzle-kit 不生成触发器):输入快照不可变 + 审计日志追加式(禁改禁删)。 -- sf_forbid_update 为 0001 建立的共享函数,此处复用(其报错文案 "immutable table %: UPDATE forbidden" -- 同样覆盖 DELETE——触发器挂在 BEFORE UPDATE OR DELETE 上,两条路径走同一函数)。 CREATE TRIGGER build_input_snapshots_immutable BEFORE UPDATE OR DELETE ON build_input_snapshots FOR EACH ROW EXECUTE FUNCTION sf_forbid_update();--> statement-breakpoint CREATE TRIGGER audit_logs_immutable BEFORE UPDATE OR DELETE ON audit_logs FOR EACH ROW EXECUTE FUNCTION sf_forbid_update();