Initial commit: img-infinite-canvas AI design workbench MVP

Moteva-style AI design workbench replica. Home prompt creates a project;
the project page provides an infinite canvas with node drag, zoom/pan,
a design chat panel, history replay, image asset upload, and project
save/regenerate.

- Backend: go-zero, DDD layering, sqlc/pgx, optional PostgreSQL; memory
  or Redis cache; asynq job queue; MinIO/S3/R2/OSS object storage;
  sky-valley/pi agent runtime adapter.
- Frontend: Next.js App Router + Vite artifact build, TypeScript, i18n,
  shadcn/ui components, auth (OTP/Turnstile/Google/WeChat).
- Deploy: Docker Compose and k3s manifests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-07 23:15:37 +08:00
commit 44406b72db
349 changed files with 73265 additions and 0 deletions
@@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
package db
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}
@@ -0,0 +1,121 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
package db
import (
"github.com/jackc/pgx/v5/pgtype"
)
type AgentMessage struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
Role string `json:"role"`
MessageType string `json:"message_type"`
Title string `json:"title"`
Content string `json:"content"`
ThreadID string `json:"thread_id"`
ActionID string `json:"action_id"`
StepID string `json:"step_id"`
ToolCallID string `json:"tool_call_id"`
Name string `json:"name"`
ToolHint string `json:"tool_hint"`
Status string `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type CanvasConnection struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
FromNodeID string `json:"from_node_id"`
ToNodeID string `json:"to_node_id"`
}
type CanvasNode struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
NodeType string `json:"node_type"`
Title string `json:"title"`
X float64 `json:"x"`
Y float64 `json:"y"`
Width float64 `json:"width"`
Height float64 `json:"height"`
Content string `json:"content"`
Tone string `json:"tone"`
Status string `json:"status"`
SortOrder int32 `json:"sort_order"`
ParentID string `json:"parent_id"`
LayerRole string `json:"layer_role"`
FontSize float64 `json:"font_size"`
FontFamily string `json:"font_family"`
FontWeight string `json:"font_weight"`
Color string `json:"color"`
TextAlign string `json:"text_align"`
LineHeight float64 `json:"line_height"`
LetterSpacing float64 `json:"letter_spacing"`
TextDecoration string `json:"text_decoration"`
TextTransform string `json:"text_transform"`
Opacity float64 `json:"opacity"`
FillColor string `json:"fill_color"`
StrokeColor string `json:"stroke_color"`
StrokeWidth float64 `json:"stroke_width"`
StrokeStyle string `json:"stroke_style"`
FlipX bool `json:"flip_x"`
FlipY bool `json:"flip_y"`
Rotation float64 `json:"rotation"`
}
type CanvasSnapshot struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
Version string `json:"version"`
Canvas string `json:"canvas"`
ViewportJson []byte `json:"viewport_json"`
NodesJson []byte `json:"nodes_json"`
ConnectionsJson []byte `json:"connections_json"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type OutboxEvent struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
AggregateType string `json:"aggregate_type"`
AggregateID string `json:"aggregate_id"`
EventType string `json:"event_type"`
ThreadID string `json:"thread_id"`
PayloadJson []byte `json:"payload_json"`
Status string `json:"status"`
PublishedAt pgtype.Timestamptz `json:"published_at"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type Project struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Title string `json:"title"`
Brief string `json:"brief"`
Status string `json:"status"`
Thumbnail string `json:"thumbnail"`
Canvas string `json:"canvas"`
Version string `json:"version"`
SnapshotID string `json:"snapshot_id"`
LastThreadID string `json:"last_thread_id"`
ViewportX float64 `json:"viewport_x"`
ViewportY float64 `json:"viewport_y"`
ViewportK float64 `json:"viewport_k"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type User struct {
ID pgtype.UUID `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
Status string `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
@@ -0,0 +1,34 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
type Querier interface {
CreateCanvasSnapshot(ctx context.Context, arg CreateCanvasSnapshotParams) error
CreateConnection(ctx context.Context, arg CreateConnectionParams) error
CreateMessage(ctx context.Context, arg CreateMessageParams) error
DeleteConnectionsByProject(ctx context.Context, arg DeleteConnectionsByProjectParams) error
DeleteMessagesByProject(ctx context.Context, arg DeleteMessagesByProjectParams) error
DeleteNodesByProject(ctx context.Context, arg DeleteNodesByProjectParams) error
DeleteProject(ctx context.Context, arg DeleteProjectParams) error
GetCanvasSnapshot(ctx context.Context, arg GetCanvasSnapshotParams) (CanvasSnapshot, error)
GetProject(ctx context.Context, arg GetProjectParams) (Project, error)
ListConnectionsByProject(ctx context.Context, arg ListConnectionsByProjectParams) ([]CanvasConnection, error)
ListMessagesByProject(ctx context.Context, arg ListMessagesByProjectParams) ([]AgentMessage, error)
ListNodesByProject(ctx context.Context, arg ListNodesByProjectParams) ([]CanvasNode, error)
ListProjects(ctx context.Context, userID pgtype.UUID) ([]ListProjectsRow, error)
ListProjectsPage(ctx context.Context, arg ListProjectsPageParams) ([]ListProjectsPageRow, error)
LockProject(ctx context.Context, arg LockProjectParams) (LockProjectRow, error)
UpdateProjectCanvasSnapshot(ctx context.Context, arg UpdateProjectCanvasSnapshotParams) error
UpsertCanvasNode(ctx context.Context, arg UpsertCanvasNodeParams) error
UpsertProject(ctx context.Context, arg UpsertProjectParams) error
}
var _ Querier = (*Queries)(nil)
@@ -0,0 +1,744 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: query.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const createCanvasSnapshot = `-- 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)
ON CONFLICT (id) DO UPDATE SET
project_id = EXCLUDED.project_id,
user_id = EXCLUDED.user_id,
version = EXCLUDED.version,
canvas = EXCLUDED.canvas,
viewport_json = EXCLUDED.viewport_json,
nodes_json = EXCLUDED.nodes_json,
connections_json = EXCLUDED.connections_json,
created_at = EXCLUDED.created_at
`
type CreateCanvasSnapshotParams struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
Version string `json:"version"`
Canvas string `json:"canvas"`
ViewportJson []byte `json:"viewport_json"`
NodesJson []byte `json:"nodes_json"`
ConnectionsJson []byte `json:"connections_json"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
func (q *Queries) CreateCanvasSnapshot(ctx context.Context, arg CreateCanvasSnapshotParams) error {
_, err := q.db.Exec(ctx, createCanvasSnapshot,
arg.ID,
arg.ProjectID,
arg.UserID,
arg.Version,
arg.Canvas,
arg.ViewportJson,
arg.NodesJson,
arg.ConnectionsJson,
arg.CreatedAt,
)
return err
}
const createConnection = `-- name: CreateConnection :exec
INSERT INTO canvas_connections (id, project_id, user_id, from_node_id, to_node_id)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (id) DO UPDATE SET
project_id = EXCLUDED.project_id,
user_id = EXCLUDED.user_id,
from_node_id = EXCLUDED.from_node_id,
to_node_id = EXCLUDED.to_node_id
`
type CreateConnectionParams struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
FromNodeID string `json:"from_node_id"`
ToNodeID string `json:"to_node_id"`
}
func (q *Queries) CreateConnection(ctx context.Context, arg CreateConnectionParams) error {
_, err := q.db.Exec(ctx, createConnection,
arg.ID,
arg.ProjectID,
arg.UserID,
arg.FromNodeID,
arg.ToNodeID,
)
return err
}
const createMessage = `-- name: CreateMessage :exec
INSERT INTO agent_messages (id, project_id, user_id, role, message_type, title, content, thread_id, action_id, step_id, tool_call_id, name, tool_hint, status, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
ON CONFLICT (id) DO UPDATE SET
project_id = EXCLUDED.project_id,
user_id = EXCLUDED.user_id,
role = EXCLUDED.role,
message_type = EXCLUDED.message_type,
title = EXCLUDED.title,
content = EXCLUDED.content,
thread_id = EXCLUDED.thread_id,
action_id = EXCLUDED.action_id,
step_id = EXCLUDED.step_id,
tool_call_id = EXCLUDED.tool_call_id,
name = EXCLUDED.name,
tool_hint = EXCLUDED.tool_hint,
status = EXCLUDED.status,
created_at = EXCLUDED.created_at
`
type CreateMessageParams struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
Role string `json:"role"`
MessageType string `json:"message_type"`
Title string `json:"title"`
Content string `json:"content"`
ThreadID string `json:"thread_id"`
ActionID string `json:"action_id"`
StepID string `json:"step_id"`
ToolCallID string `json:"tool_call_id"`
Name string `json:"name"`
ToolHint string `json:"tool_hint"`
Status string `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
func (q *Queries) CreateMessage(ctx context.Context, arg CreateMessageParams) error {
_, err := q.db.Exec(ctx, createMessage,
arg.ID,
arg.ProjectID,
arg.UserID,
arg.Role,
arg.MessageType,
arg.Title,
arg.Content,
arg.ThreadID,
arg.ActionID,
arg.StepID,
arg.ToolCallID,
arg.Name,
arg.ToolHint,
arg.Status,
arg.CreatedAt,
)
return err
}
const deleteConnectionsByProject = `-- name: DeleteConnectionsByProject :exec
DELETE FROM canvas_connections
WHERE project_id = $1 AND user_id = $2
`
type DeleteConnectionsByProjectParams struct {
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
}
func (q *Queries) DeleteConnectionsByProject(ctx context.Context, arg DeleteConnectionsByProjectParams) error {
_, err := q.db.Exec(ctx, deleteConnectionsByProject, arg.ProjectID, arg.UserID)
return err
}
const deleteMessagesByProject = `-- name: DeleteMessagesByProject :exec
DELETE FROM agent_messages
WHERE project_id = $1 AND user_id = $2
`
type DeleteMessagesByProjectParams struct {
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
}
func (q *Queries) DeleteMessagesByProject(ctx context.Context, arg DeleteMessagesByProjectParams) error {
_, err := q.db.Exec(ctx, deleteMessagesByProject, arg.ProjectID, arg.UserID)
return err
}
const deleteNodesByProject = `-- name: DeleteNodesByProject :exec
DELETE FROM canvas_nodes
WHERE project_id = $1 AND user_id = $2
`
type DeleteNodesByProjectParams struct {
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
}
func (q *Queries) DeleteNodesByProject(ctx context.Context, arg DeleteNodesByProjectParams) error {
_, err := q.db.Exec(ctx, deleteNodesByProject, arg.ProjectID, arg.UserID)
return err
}
const deleteProject = `-- name: DeleteProject :exec
DELETE FROM projects
WHERE id = $1 AND user_id = $2
`
type DeleteProjectParams struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
}
func (q *Queries) DeleteProject(ctx context.Context, arg DeleteProjectParams) error {
_, err := q.db.Exec(ctx, deleteProject, arg.ID, arg.UserID)
return err
}
const getCanvasSnapshot = `-- name: GetCanvasSnapshot :one
SELECT id, project_id, user_id, version, canvas, viewport_json, nodes_json, connections_json, created_at
FROM canvas_snapshots
WHERE id = $1 AND project_id = $2 AND user_id = $3
`
type GetCanvasSnapshotParams struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
}
func (q *Queries) GetCanvasSnapshot(ctx context.Context, arg GetCanvasSnapshotParams) (CanvasSnapshot, error) {
row := q.db.QueryRow(ctx, getCanvasSnapshot, arg.ID, arg.ProjectID, arg.UserID)
var i CanvasSnapshot
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.UserID,
&i.Version,
&i.Canvas,
&i.ViewportJson,
&i.NodesJson,
&i.ConnectionsJson,
&i.CreatedAt,
)
return i, err
}
const getProject = `-- 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
FROM projects
WHERE id = $1 AND user_id = $2
`
type GetProjectParams struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
}
func (q *Queries) GetProject(ctx context.Context, arg GetProjectParams) (Project, error) {
row := q.db.QueryRow(ctx, getProject, arg.ID, arg.UserID)
var i Project
err := row.Scan(
&i.ID,
&i.UserID,
&i.Title,
&i.Brief,
&i.Status,
&i.Thumbnail,
&i.Canvas,
&i.Version,
&i.SnapshotID,
&i.LastThreadID,
&i.ViewportX,
&i.ViewportY,
&i.ViewportK,
&i.UpdatedAt,
)
return i, err
}
const listConnectionsByProject = `-- name: ListConnectionsByProject :many
SELECT id, project_id, user_id, from_node_id, to_node_id
FROM canvas_connections
WHERE project_id = $1 AND user_id = $2
ORDER BY id
`
type ListConnectionsByProjectParams struct {
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
}
func (q *Queries) ListConnectionsByProject(ctx context.Context, arg ListConnectionsByProjectParams) ([]CanvasConnection, error) {
rows, err := q.db.Query(ctx, listConnectionsByProject, arg.ProjectID, arg.UserID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CanvasConnection
for rows.Next() {
var i CanvasConnection
if err := rows.Scan(
&i.ID,
&i.ProjectID,
&i.UserID,
&i.FromNodeID,
&i.ToNodeID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listMessagesByProject = `-- name: ListMessagesByProject :many
SELECT id, project_id, user_id, role, message_type, title, content, thread_id, action_id, step_id, tool_call_id, name, tool_hint, status, created_at
FROM agent_messages
WHERE project_id = $1 AND user_id = $2
ORDER BY created_at ASC
`
type ListMessagesByProjectParams struct {
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
}
func (q *Queries) ListMessagesByProject(ctx context.Context, arg ListMessagesByProjectParams) ([]AgentMessage, error) {
rows, err := q.db.Query(ctx, listMessagesByProject, arg.ProjectID, arg.UserID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AgentMessage
for rows.Next() {
var i AgentMessage
if err := rows.Scan(
&i.ID,
&i.ProjectID,
&i.UserID,
&i.Role,
&i.MessageType,
&i.Title,
&i.Content,
&i.ThreadID,
&i.ActionID,
&i.StepID,
&i.ToolCallID,
&i.Name,
&i.ToolHint,
&i.Status,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listNodesByProject = `-- name: ListNodesByProject :many
SELECT id, project_id, user_id, node_type, title, x, y, width, height, content, tone, status, sort_order, parent_id, layer_role, font_size, font_family, font_weight, color, text_align, line_height, letter_spacing, text_decoration, text_transform, opacity, fill_color, stroke_color, stroke_width, stroke_style, flip_x, flip_y, rotation
FROM canvas_nodes
WHERE project_id = $1 AND user_id = $2
ORDER BY sort_order ASC, id ASC
`
type ListNodesByProjectParams struct {
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
}
func (q *Queries) ListNodesByProject(ctx context.Context, arg ListNodesByProjectParams) ([]CanvasNode, error) {
rows, err := q.db.Query(ctx, listNodesByProject, arg.ProjectID, arg.UserID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CanvasNode
for rows.Next() {
var i CanvasNode
if err := rows.Scan(
&i.ID,
&i.ProjectID,
&i.UserID,
&i.NodeType,
&i.Title,
&i.X,
&i.Y,
&i.Width,
&i.Height,
&i.Content,
&i.Tone,
&i.Status,
&i.SortOrder,
&i.ParentID,
&i.LayerRole,
&i.FontSize,
&i.FontFamily,
&i.FontWeight,
&i.Color,
&i.TextAlign,
&i.LineHeight,
&i.LetterSpacing,
&i.TextDecoration,
&i.TextTransform,
&i.Opacity,
&i.FillColor,
&i.StrokeColor,
&i.StrokeWidth,
&i.StrokeStyle,
&i.FlipX,
&i.FlipY,
&i.Rotation,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listProjects = `-- name: ListProjects :many
SELECT id, user_id, title, brief, status, thumbnail, updated_at
FROM projects
WHERE user_id = $1
ORDER BY updated_at DESC
`
type ListProjectsRow struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Title string `json:"title"`
Brief string `json:"brief"`
Status string `json:"status"`
Thumbnail string `json:"thumbnail"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
func (q *Queries) ListProjects(ctx context.Context, userID pgtype.UUID) ([]ListProjectsRow, error) {
rows, err := q.db.Query(ctx, listProjects, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ListProjectsRow
for rows.Next() {
var i ListProjectsRow
if err := rows.Scan(
&i.ID,
&i.UserID,
&i.Title,
&i.Brief,
&i.Status,
&i.Thumbnail,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listProjectsPage = `-- name: ListProjectsPage :many
SELECT id, user_id, title, brief, status, thumbnail, updated_at
FROM projects
WHERE user_id = $1
ORDER BY updated_at DESC
LIMIT $2 OFFSET $3
`
type ListProjectsPageParams struct {
UserID pgtype.UUID `json:"user_id"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
type ListProjectsPageRow struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Title string `json:"title"`
Brief string `json:"brief"`
Status string `json:"status"`
Thumbnail string `json:"thumbnail"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
func (q *Queries) ListProjectsPage(ctx context.Context, arg ListProjectsPageParams) ([]ListProjectsPageRow, error) {
rows, err := q.db.Query(ctx, listProjectsPage, arg.UserID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ListProjectsPageRow
for rows.Next() {
var i ListProjectsPageRow
if err := rows.Scan(
&i.ID,
&i.UserID,
&i.Title,
&i.Brief,
&i.Status,
&i.Thumbnail,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const lockProject = `-- name: LockProject :one
SELECT id, user_id, version
FROM projects
WHERE id = $1 AND user_id = $2
FOR UPDATE
`
type LockProjectParams struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
}
type LockProjectRow struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Version string `json:"version"`
}
func (q *Queries) LockProject(ctx context.Context, arg LockProjectParams) (LockProjectRow, error) {
row := q.db.QueryRow(ctx, lockProject, arg.ID, arg.UserID)
var i LockProjectRow
err := row.Scan(&i.ID, &i.UserID, &i.Version)
return i, err
}
const updateProjectCanvasSnapshot = `-- name: UpdateProjectCanvasSnapshot :exec
UPDATE projects
SET thumbnail = $3,
canvas = $4,
version = $5,
snapshot_id = $6,
viewport_x = $7,
viewport_y = $8,
viewport_k = $9,
updated_at = $10
WHERE id = $1 AND user_id = $2
`
type UpdateProjectCanvasSnapshotParams struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Thumbnail string `json:"thumbnail"`
Canvas string `json:"canvas"`
Version string `json:"version"`
SnapshotID string `json:"snapshot_id"`
ViewportX float64 `json:"viewport_x"`
ViewportY float64 `json:"viewport_y"`
ViewportK float64 `json:"viewport_k"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
func (q *Queries) UpdateProjectCanvasSnapshot(ctx context.Context, arg UpdateProjectCanvasSnapshotParams) error {
_, err := q.db.Exec(ctx, updateProjectCanvasSnapshot,
arg.ID,
arg.UserID,
arg.Thumbnail,
arg.Canvas,
arg.Version,
arg.SnapshotID,
arg.ViewportX,
arg.ViewportY,
arg.ViewportK,
arg.UpdatedAt,
)
return err
}
const upsertCanvasNode = `-- name: UpsertCanvasNode :exec
INSERT INTO canvas_nodes (id, project_id, user_id, node_type, title, x, y, width, height, content, tone, status, sort_order, parent_id, layer_role, font_size, font_family, font_weight, color, text_align, line_height, letter_spacing, text_decoration, text_transform, opacity, fill_color, stroke_color, stroke_width, stroke_style, flip_x, flip_y, rotation)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32)
ON CONFLICT (id) DO UPDATE SET
project_id = EXCLUDED.project_id,
user_id = EXCLUDED.user_id,
node_type = EXCLUDED.node_type,
title = EXCLUDED.title,
x = EXCLUDED.x,
y = EXCLUDED.y,
width = EXCLUDED.width,
height = EXCLUDED.height,
content = EXCLUDED.content,
tone = EXCLUDED.tone,
status = EXCLUDED.status,
sort_order = EXCLUDED.sort_order,
parent_id = EXCLUDED.parent_id,
layer_role = EXCLUDED.layer_role,
font_size = EXCLUDED.font_size,
font_family = EXCLUDED.font_family,
font_weight = EXCLUDED.font_weight,
color = EXCLUDED.color,
text_align = EXCLUDED.text_align,
line_height = EXCLUDED.line_height,
letter_spacing = EXCLUDED.letter_spacing,
text_decoration = EXCLUDED.text_decoration,
text_transform = EXCLUDED.text_transform,
opacity = EXCLUDED.opacity,
fill_color = EXCLUDED.fill_color,
stroke_color = EXCLUDED.stroke_color,
stroke_width = EXCLUDED.stroke_width,
stroke_style = EXCLUDED.stroke_style,
flip_x = EXCLUDED.flip_x,
flip_y = EXCLUDED.flip_y,
rotation = EXCLUDED.rotation
`
type UpsertCanvasNodeParams struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
UserID pgtype.UUID `json:"user_id"`
NodeType string `json:"node_type"`
Title string `json:"title"`
X float64 `json:"x"`
Y float64 `json:"y"`
Width float64 `json:"width"`
Height float64 `json:"height"`
Content string `json:"content"`
Tone string `json:"tone"`
Status string `json:"status"`
SortOrder int32 `json:"sort_order"`
ParentID string `json:"parent_id"`
LayerRole string `json:"layer_role"`
FontSize float64 `json:"font_size"`
FontFamily string `json:"font_family"`
FontWeight string `json:"font_weight"`
Color string `json:"color"`
TextAlign string `json:"text_align"`
LineHeight float64 `json:"line_height"`
LetterSpacing float64 `json:"letter_spacing"`
TextDecoration string `json:"text_decoration"`
TextTransform string `json:"text_transform"`
Opacity float64 `json:"opacity"`
FillColor string `json:"fill_color"`
StrokeColor string `json:"stroke_color"`
StrokeWidth float64 `json:"stroke_width"`
StrokeStyle string `json:"stroke_style"`
FlipX bool `json:"flip_x"`
FlipY bool `json:"flip_y"`
Rotation float64 `json:"rotation"`
}
func (q *Queries) UpsertCanvasNode(ctx context.Context, arg UpsertCanvasNodeParams) error {
_, err := q.db.Exec(ctx, upsertCanvasNode,
arg.ID,
arg.ProjectID,
arg.UserID,
arg.NodeType,
arg.Title,
arg.X,
arg.Y,
arg.Width,
arg.Height,
arg.Content,
arg.Tone,
arg.Status,
arg.SortOrder,
arg.ParentID,
arg.LayerRole,
arg.FontSize,
arg.FontFamily,
arg.FontWeight,
arg.Color,
arg.TextAlign,
arg.LineHeight,
arg.LetterSpacing,
arg.TextDecoration,
arg.TextTransform,
arg.Opacity,
arg.FillColor,
arg.StrokeColor,
arg.StrokeWidth,
arg.StrokeStyle,
arg.FlipX,
arg.FlipY,
arg.Rotation,
)
return err
}
const upsertProject = `-- 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)
ON CONFLICT (id) DO UPDATE SET
user_id = EXCLUDED.user_id,
title = EXCLUDED.title,
brief = EXCLUDED.brief,
status = EXCLUDED.status,
thumbnail = EXCLUDED.thumbnail,
canvas = EXCLUDED.canvas,
version = EXCLUDED.version,
snapshot_id = EXCLUDED.snapshot_id,
last_thread_id = EXCLUDED.last_thread_id,
viewport_x = EXCLUDED.viewport_x,
viewport_y = EXCLUDED.viewport_y,
viewport_k = EXCLUDED.viewport_k,
updated_at = EXCLUDED.updated_at
`
type UpsertProjectParams struct {
ID string `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Title string `json:"title"`
Brief string `json:"brief"`
Status string `json:"status"`
Thumbnail string `json:"thumbnail"`
Canvas string `json:"canvas"`
Version string `json:"version"`
SnapshotID string `json:"snapshot_id"`
LastThreadID string `json:"last_thread_id"`
ViewportX float64 `json:"viewport_x"`
ViewportY float64 `json:"viewport_y"`
ViewportK float64 `json:"viewport_k"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
func (q *Queries) UpsertProject(ctx context.Context, arg UpsertProjectParams) error {
_, err := q.db.Exec(ctx, upsertProject,
arg.ID,
arg.UserID,
arg.Title,
arg.Brief,
arg.Status,
arg.Thumbnail,
arg.Canvas,
arg.Version,
arg.SnapshotID,
arg.LastThreadID,
arg.ViewportX,
arg.ViewportY,
arg.ViewportK,
arg.UpdatedAt,
)
return err
}