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
@@ -1,18 +1,18 @@
-- name: ListProjects :many
SELECT id, user_id, title, brief, status, thumbnail, updated_at
SELECT id, user_id, title, brief, status, thumbnail, brand_kit_id, updated_at
FROM projects
WHERE user_id = $1
ORDER BY updated_at DESC;
-- name: ListProjectsPage :many
SELECT id, user_id, title, brief, status, thumbnail, updated_at
SELECT id, user_id, title, brief, status, thumbnail, brand_kit_id, updated_at
FROM projects
WHERE user_id = $1
ORDER BY updated_at DESC
LIMIT $2 OFFSET $3;
-- name: GetProject :one
SELECT id, user_id, title, brief, status, thumbnail, canvas, version, snapshot_id, last_thread_id, viewport_x, viewport_y, viewport_k, updated_at
SELECT id, user_id, title, brief, status, thumbnail, canvas, version, snapshot_id, last_thread_id, brand_kit_id, viewport_x, viewport_y, viewport_k, updated_at
FROM projects
WHERE id = $1 AND user_id = $2;
@@ -27,8 +27,8 @@ DELETE FROM projects
WHERE id = $1 AND user_id = $2;
-- name: UpsertProject :exec
INSERT INTO projects (id, user_id, title, brief, status, thumbnail, canvas, version, snapshot_id, last_thread_id, viewport_x, viewport_y, viewport_k, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
INSERT INTO projects (id, user_id, title, brief, status, thumbnail, canvas, version, snapshot_id, last_thread_id, brand_kit_id, viewport_x, viewport_y, viewport_k, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
ON CONFLICT (id) DO UPDATE SET
user_id = EXCLUDED.user_id,
title = EXCLUDED.title,
@@ -39,11 +39,50 @@ ON CONFLICT (id) DO UPDATE SET
version = EXCLUDED.version,
snapshot_id = EXCLUDED.snapshot_id,
last_thread_id = EXCLUDED.last_thread_id,
brand_kit_id = EXCLUDED.brand_kit_id,
viewport_x = EXCLUDED.viewport_x,
viewport_y = EXCLUDED.viewport_y,
viewport_k = EXCLUDED.viewport_k,
updated_at = EXCLUDED.updated_at;
-- name: ListBrandKits :many
SELECT id, user_id, name, document, is_default, created_at, updated_at
FROM brand_kits
WHERE user_id = $1
ORDER BY updated_at DESC, id ASC;
-- name: GetBrandKit :one
SELECT id, user_id, name, document, is_default, created_at, updated_at
FROM brand_kits
WHERE id = $1 AND user_id = $2;
-- name: GetDefaultBrandKit :one
SELECT id, user_id, name, document, is_default, created_at, updated_at
FROM brand_kits
WHERE user_id = $1 AND is_default = TRUE
LIMIT 1;
-- name: ClearDefaultBrandKits :exec
UPDATE brand_kits
SET is_default = FALSE
WHERE user_id = $1 AND is_default = TRUE AND id <> $2;
-- name: UpsertBrandKit :one
INSERT INTO brand_kits (id, user_id, name, document, is_default, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
document = EXCLUDED.document,
is_default = EXCLUDED.is_default,
updated_at = EXCLUDED.updated_at
WHERE brand_kits.user_id = EXCLUDED.user_id
RETURNING id, user_id, name, document, is_default, created_at, updated_at;
-- name: DeleteBrandKit :one
DELETE FROM brand_kits
WHERE id = $1 AND user_id = $2
RETURNING id;
-- name: CreateCanvasSnapshot :exec
INSERT INTO canvas_snapshots (id, project_id, user_id, version, canvas, viewport_json, nodes_json, connections_json, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)