feat(server): add brand kit management and project binding

Add a brand kit domain with memory and postgres stores, a BrandKitService
for CRUD and resolution, and REST endpoints to list, upsert, and delete
brand kits. Projects can bind a brand kit whose resolved context and
reference images feed into the agent's long-term memory and design prompts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 20:58:55 +08:00
parent 3653474355
commit 789f51b29e
38 changed files with 2165 additions and 135 deletions
@@ -43,6 +43,19 @@ CREATE TABLE IF NOT EXISTS auth_verification_codes (
CREATE INDEX IF NOT EXISTS auth_codes_target_idx ON auth_verification_codes(region, channel, target, purpose, created_at DESC);
CREATE TABLE IF NOT EXISTS brand_kits (
id TEXT PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
document JSONB NOT NULL CHECK (jsonb_typeof(document) = 'object'),
is_default BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS brand_kits_user_updated_idx ON brand_kits(user_id, updated_at DESC);
CREATE UNIQUE INDEX IF NOT EXISTS brand_kits_user_default_idx ON brand_kits(user_id) WHERE is_default;
CREATE TABLE IF NOT EXISTS projects (
id TEXT PRIMARY KEY,
user_id UUID NOT NULL DEFAULT '00000000-0000-0000-0000-000000000001'::uuid,
@@ -54,6 +67,7 @@ CREATE TABLE IF NOT EXISTS projects (
version TEXT NOT NULL DEFAULT '',
snapshot_id TEXT NOT NULL DEFAULT '',
last_thread_id TEXT NOT NULL DEFAULT '',
brand_kit_id TEXT REFERENCES brand_kits(id) ON DELETE SET NULL,
viewport_x DOUBLE PRECISION NOT NULL DEFAULT 0,
viewport_y DOUBLE PRECISION NOT NULL DEFAULT 0,
viewport_k DOUBLE PRECISION NOT NULL DEFAULT 1,
@@ -70,7 +84,22 @@ ALTER TABLE projects ADD COLUMN IF NOT EXISTS canvas TEXT NOT NULL DEFAULT '';
ALTER TABLE projects ADD COLUMN IF NOT EXISTS version TEXT NOT NULL DEFAULT '';
ALTER TABLE projects ADD COLUMN IF NOT EXISTS snapshot_id TEXT NOT NULL DEFAULT '';
ALTER TABLE projects ADD COLUMN IF NOT EXISTS last_thread_id TEXT NOT NULL DEFAULT '';
ALTER TABLE projects ADD COLUMN IF NOT EXISTS brand_kit_id TEXT;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'projects_brand_kit_id_fkey'
AND conrelid = 'projects'::regclass
) THEN
ALTER TABLE projects
ADD CONSTRAINT projects_brand_kit_id_fkey
FOREIGN KEY (brand_kit_id) REFERENCES brand_kits(id) ON DELETE SET NULL;
END IF;
END $$;
CREATE INDEX IF NOT EXISTS projects_user_updated_idx ON projects(user_id, updated_at DESC);
CREATE INDEX IF NOT EXISTS projects_brand_kit_idx ON projects(user_id, brand_kit_id) WHERE brand_kit_id IS NOT NULL;
CREATE TABLE IF NOT EXISTS project_share_policies (
project_id TEXT PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,