agenthub/drizzle/0000_cold_naoko.sql
Paperclip FoundingEngineer bdd5d92ba7 Initial AgentHub codebase for Coolify deployment
Complete implementation ready for Coolify:
- Node.js 22 + Fastify + socket.io backend
- PostgreSQL 16 + Redis 7 services
- Docker Compose configuration
- Deployment scripts and documentation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-01 21:25:57 +00:00

122 lines
No EOL
6.5 KiB
PL/PgSQL

-- Extension UUID v7 (préférer pg_uuidv7 si dispo, sinon fallback Node).
-- Note: pg_uuidv7 peut ne pas être disponible dans toutes les distributions Postgres 16.
-- Cette migration tentera de créer l'extension, et si elle échoue, le fallback sera
-- la génération côté Node via uuid@9+ (cf. ADR-0002).
DO $$
BEGIN
CREATE EXTENSION IF NOT EXISTS pg_uuidv7;
EXCEPTION
WHEN OTHERS THEN
-- Extension non disponible, on créera une fonction fallback
CREATE OR REPLACE FUNCTION uuidv7() RETURNS uuid AS $func$
BEGIN
RAISE EXCEPTION 'uuidv7() requires pg_uuidv7 extension or Node-side generation';
END;
$func$ LANGUAGE plpgsql;
END $$;
--> statement-breakpoint
-- Trigger updated_at (bump à chaque UPDATE sur agents)
CREATE OR REPLACE FUNCTION set_updated_at() RETURNS trigger AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
--> statement-breakpoint
CREATE TABLE "agents" (
"id" uuid PRIMARY KEY DEFAULT uuidv7() NOT NULL,
"name" text NOT NULL,
"display_name" text NOT NULL,
"role" text NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "agents_name_unique" UNIQUE("name"),
CONSTRAINT "agents_name_check" CHECK ("agents"."name" ~ '^[a-z0-9][a-z0-9-]{0,63}$'),
CONSTRAINT "agents_display_name_check" CHECK (length("agents"."display_name") BETWEEN 1 AND 128),
CONSTRAINT "agents_role_check" CHECK ("agents"."role" IN ('admin', 'agent'))
);
--> statement-breakpoint
CREATE TRIGGER agents_set_updated_at
BEFORE UPDATE ON agents
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
--> statement-breakpoint
CREATE TABLE "api_tokens" (
"id" uuid PRIMARY KEY DEFAULT uuidv7() NOT NULL,
"agent_id" uuid NOT NULL,
"hash_argon2id" text NOT NULL,
"prefix" text NOT NULL,
"scopes" jsonb DEFAULT '{}'::jsonb NOT NULL,
"status" text DEFAULT 'active' NOT NULL,
"expires_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"revoked_at" timestamp with time zone,
CONSTRAINT "api_tokens_prefix_unique" UNIQUE("prefix"),
CONSTRAINT "api_tokens_prefix_check" CHECK ("api_tokens"."prefix" ~ '^ah_live_[a-zA-Z0-9]{4}$'),
CONSTRAINT "api_tokens_status_check" CHECK ("api_tokens"."status" IN ('active', 'rotating', 'revoked')),
CONSTRAINT "api_tokens_revoked_at_check" CHECK ("api_tokens"."revoked_at" IS NULL OR "api_tokens"."status" = 'revoked'),
CONSTRAINT "api_tokens_expires_at_check" CHECK ("api_tokens"."expires_at" IS NULL OR "api_tokens"."expires_at" > "api_tokens"."created_at")
);
--> statement-breakpoint
CREATE TABLE "audit_events" (
"id" uuid PRIMARY KEY DEFAULT uuidv7() NOT NULL,
"type" text NOT NULL,
"agent_id" uuid,
"payload_hash" "bytea" NOT NULL,
"ts" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "audit_events_type_check" CHECK ("audit_events"."type" IN (
'login',
'token-issued',
'token-rotated',
'token-revoked',
'jwt-issued',
'agent-created',
'agent-deleted',
'room-created',
'room-deleted',
'message-sent'
)),
CONSTRAINT "audit_events_payload_hash_check" CHECK (length("audit_events"."payload_hash") = 32)
);
--> statement-breakpoint
CREATE TABLE "messages" (
"id" uuid PRIMARY KEY DEFAULT uuidv7() NOT NULL,
"room_id" uuid NOT NULL,
"author_agent_id" uuid NOT NULL,
"body" text NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "messages_body_check" CHECK (length("messages"."body") BETWEEN 1 AND 16384)
);
--> statement-breakpoint
CREATE TABLE "room_members" (
"room_id" uuid NOT NULL,
"agent_id" uuid NOT NULL,
"joined_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "room_members_room_id_agent_id_pk" PRIMARY KEY("room_id","agent_id")
);
--> statement-breakpoint
CREATE TABLE "rooms" (
"id" uuid PRIMARY KEY DEFAULT uuidv7() NOT NULL,
"slug" text NOT NULL,
"name" text NOT NULL,
"created_by" uuid,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "rooms_slug_unique" UNIQUE("slug"),
CONSTRAINT "rooms_slug_check" CHECK ("rooms"."slug" ~ '^[a-z0-9][a-z0-9-]{0,63}$'),
CONSTRAINT "rooms_name_check" CHECK (length("rooms"."name") BETWEEN 1 AND 128)
);
--> statement-breakpoint
ALTER TABLE "api_tokens" ADD CONSTRAINT "api_tokens_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "audit_events" ADD CONSTRAINT "audit_events_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "messages" ADD CONSTRAINT "messages_room_id_rooms_id_fk" FOREIGN KEY ("room_id") REFERENCES "public"."rooms"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "messages" ADD CONSTRAINT "messages_author_agent_id_agents_id_fk" FOREIGN KEY ("author_agent_id") REFERENCES "public"."agents"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "room_members" ADD CONSTRAINT "room_members_room_id_rooms_id_fk" FOREIGN KEY ("room_id") REFERENCES "public"."rooms"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "room_members" ADD CONSTRAINT "room_members_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "rooms" ADD CONSTRAINT "rooms_created_by_agents_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."agents"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "agents_role_idx" ON "agents" USING btree ("role");--> statement-breakpoint
CREATE INDEX "api_tokens_agent_id_idx" ON "api_tokens" USING btree ("agent_id");--> statement-breakpoint
CREATE INDEX "api_tokens_active_prefix_idx" ON "api_tokens" USING btree ("prefix") WHERE "api_tokens"."status" = 'active';--> statement-breakpoint
CREATE INDEX "audit_events_ts_idx" ON "audit_events" USING btree ("ts");--> statement-breakpoint
CREATE INDEX "audit_events_type_ts_idx" ON "audit_events" USING btree ("type","ts");--> statement-breakpoint
CREATE INDEX "audit_events_agent_ts_idx" ON "audit_events" USING btree ("agent_id","ts") WHERE "audit_events"."agent_id" IS NOT NULL;--> statement-breakpoint
CREATE INDEX "messages_room_created_at_idx" ON "messages" USING btree ("room_id","created_at" DESC,"id" DESC);--> statement-breakpoint
CREATE INDEX "room_members_agent_id_idx" ON "room_members" USING btree ("agent_id");