Starsoup Image Platform (SIP) — 主文件
版本: 0.2 日期: 2026-03-24 狀態: 遷移進行中
目錄
一、產品定義
項目名稱
Starsoup Image Platform(代號:sip)
定位
一個自托管的 AI 輔助圖片自動化平台,核心由三層組成:
【Layer 0】AI 素材生成(SIP 內建,從 Jaaz 遷移)
└─→ 連接 OpenAI / Anthropic / Ollama / ComfyUI 等供應商
└─→ 在畫布上生成水晶背景圖、商品去背圖等素材
【Layer 1】Template Studio(SIP 新增功能)
└─→ 在生成的背景圖上,視覺化定義 zones(文字插槽 / 圖片插槽)
└─→ 設定每個 zone 的字體、顏色、尺寸、位置
└─→ 即時預覽效果,存入 Supabase
【Layer 2】Render API(SIP 新增服務)
└─→ 接收 { templateId, data }
└─→ 合成背景圖 + zones → 輸出 PNG
└─→ 返回圖片 URL,供 @crystal/erp、@crystal/ec 等項目直調與現有 SaaS 的差異
| 功能 | Bannerbear / Placid | SIP |
|---|---|---|
| AI 素材生成 | ❌ | ✅ 內建(多供應商) |
| 視覺化模版編輯 | ✅ 雲端 | ✅ 自建 |
| 程式化渲染 API | ✅ | ✅ 自建 |
| 自托管 | ❌ | ✅ |
| 費用 | $49/月起 + 渲染費 | Vercel + R2 成本 |
二、現有技術棧(遷移後)
前端(已遷移自 Jaaz)
| 類別 | 技術 |
|---|---|
| 框架 | Vue 3(Composition API + <script setup>) |
| 構建 | Vite 8 |
| 語言 | TypeScript 5.9 |
| 路由 | Vue Router 4 |
| 狀態管理 | Pinia 3 |
| 樣式 | Tailwind CSS 4 |
| 畫布 | Fabric.js 7 |
| 即時通訊 | Socket.IO Client 4 |
| 國際化 | vue-i18n 10 |
後端(目前問題所在)
| 現狀 | 目標 |
|---|---|
Jaaz 的 FastAPI(Python)jaaz/server/ | 獨立 Python FastAPI,不依賴 Jaaz repo |
硬綁定 http://localhost:57988 | 可配置,支持 Vercel / Docker 部署 |
認證耦合 jaaz.app 雲端設備流程 | 改為本地 API Key 配置,去除 Jaaz 帳戶依賴 |
新增技術(Layer 1 & 2,待開發)
| 技術 | 用途 | 授權 |
|---|---|---|
| vue-konva / Konva.js | Template Studio zone 編輯器 | MIT |
| Hono | Render API server | MIT |
| Satori(Vercel) | HTML/CSS → SVG 渲染引擎 | Apache 2.0 |
| @resvg/resvg-js | SVG → PNG 轉換 | Apache 2.0 |
| Supabase JS | 模版資料庫 | MIT |
| Cloudflare R2 | 圖片儲存 | 商業服務 |
| Google Fonts / Noto TC | 中文字體(OFL,可商用) | OFL 1.1 |
三、當前問題清單
從 README 和你描述的情況,整理出以下問題,按優先級排序:
P0:服務完全無法啟動
| # | 問題 | 根因 |
|---|---|---|
| P0-1 | 所有 API 呼叫返回 502 | 前端指向 localhost:57988,但後端(Jaaz Python server)未在本機運行 |
| P0-2 | WebSocket 連線失敗 | Socket.IO 無法連上 localhost:57988 |
P1:功能缺失
| # | 問題 | 根因 |
|---|---|---|
| P1-1 | 新建模型無法選擇模型名稱 | 模型列表 API(/api/models)返回失敗或空值 |
| P1-2 | 測試連接無法成功 | 供應商配置 API 依賴後端轉發,後端未啟動 |
| P1-3 | 畫布資產無法讀取 | 媒體資源 API(/api/assets)無回應 |
| P1-4 | 畫布操作失敗 | 畫布 CRUD(/api/canvas)無回應 |
P2:架構依賴問題
| # | 問題 | 影響 |
|---|---|---|
| P2-1 | 認證流程依賴 jaaz.app 雲端 | 離線或 Jaaz 服務中斷即失效 |
| P2-2 | 後端仍在 Jaaz repo,非獨立服務 | 無法獨立部署,版本升級有衝突風險 |
| P2-3 | VITE_JAAZ_CLOUD_URL 硬編碼 jaaz.app | 無法完全去除 Jaaz 品牌依賴 |
四、後端遷移計劃(核心任務)
這是解決所有 P0/P1 問題的根本。目標是把 Jaaz 的 FastAPI 後端剝離出來,變成 SIP 自己的後端服務。
Step 1:在本機先讓現有後端跑通(臨時方案)
在找到 Jaaz repo 的情況下,先讓服務跑起來,確認前端哪些端點能通、哪些不通:
# 在 jaaz/server/ 目錄
pip install -r requirements.txt
python main.py --port 57988確認以下端點是否正常回應:
GET /api/models→ 模型列表GET /api/tools→ 工具列表POST /api/config→ 供應商配置GET /api/canvas→ 畫布列表POST /api/canvas→ 新建畫布GET /api/assets→ 資產列表POST /api/upload→ 圖片上傳POST /api/chat→ 聊天發送WS /socket.io/→ WebSocket
Step 2:建立 SIP 獨立後端
建立新的後端項目,不依賴 Jaaz repo:
sip-server/ ← 新建,SIP 自己的後端
main.py
routers/
canvas.py
chat.py
config.py
models.py
assets.py
upload.py
services/
llm_service.py ← 多供應商 LLM 呼叫(從 Jaaz 移植)
image_service.py ← 圖片生成(從 Jaaz 移植)
socket_service.py ← WebSocket(從 Jaaz 移植)
models/
schemas.py
storage/
local.py ← 本地檔案儲存(開發用)
r2.py ← Cloudflare R2(生產用)
requirements.txt
.env.example從 Jaaz 移植的核心邏輯:
| Jaaz 原始文件 | SIP 對應位置 | 移植難度 |
|---|---|---|
server/services/llm_service.py | sip-server/services/llm_service.py | 低(直接複製,去除 Jaaz 帳戶 token 邏輯) |
server/routers/chat_router.py | sip-server/routers/chat.py | 低 |
server/routers/config_router.py | sip-server/routers/config.py | 低 |
server/routers/canvas_router.py | sip-server/routers/canvas.py | 中(儲存層需替換) |
server/socket_manager.py | sip-server/services/socket_service.py | 低 |
server/routers/upload_router.py | sip-server/routers/upload.py | 中(改為 R2) |
Step 3:認證去耦
現有的設備授權流程完全依賴 jaaz.app,對 SIP 來說是不必要的外部依賴。
目標方案:移除設備授權流程,改為純 API Key 配置模式:
用戶在 Settings 中配置各供應商 API Key
→ 存入 localStorage(前端)/ .env(後端)
→ 無需任何帳戶登錄前端需修改:
src/api/auth.ts— 移除設備授權流程src/components/auth/— 移除登錄對話框,或改為「輸入 API Key」的簡化介面src/composables/useAuth.ts— 簡化為「是否已配置至少一個供應商」的狀態src/stores/configs.ts— 移除 Jaaz token 相關欄位
Step 4:供應商列表修復(解決 P1-1 模型名稱無法選擇)
這個問題最常見的原因是:前端呼叫 /api/models 取得模型列表時,後端需要先知道你配置了哪個供應商,才能返回對應的模型名稱。
後端 GET /api/models?provider=openai 應返回:
{
"models": [
{ "id": "gpt-4.1", "name": "GPT-4.1", "type": "text" },
{ "id": "gpt-4o-mini", "name": "GPT-4o Mini", "type": "text" },
{ "id": "dall-e-3", "name": "DALL-E 3", "type": "image" }
]
}如果後端無法動態查詢,可先改為前端靜態模型列表,在 src/constants/ 中硬編碼各供應商的已知模型名稱,待後端穩定後再改回動態查詢。
五、前端修復計劃
後端跑通後,前端需要做以下修復:
5.1 環境變數整理
新增 .env.example:
# 後端 API 地址
VITE_API_BASE_URL=http://localhost:57988
# 去除 Jaaz 雲端依賴(Step 3 完成後可留空)
VITE_JAAZ_CLOUD_URL=
# Supabase(Layer 1 Template Studio 啟用後填入)
VITE_SUPABASE_URL=
VITE_SUPABASE_ANON_KEY=
# Cloudflare R2(Layer 2 Render API 啟用後填入)
VITE_R2_PUBLIC_URL=5.2 API 層統一錯誤處理
目前 src/api/ 各文件的錯誤處理分散,建議在 src/lib/http.ts 建立統一的 fetch wrapper:
// src/lib/http.ts
const BASE = import.meta.env.VITE_API_BASE_URL
export async function apiFetch<T>(
path: string,
options?: RequestInit
): Promise<T> {
const res = await fetch(`${BASE}${path}`, {
headers: { 'Content-Type': 'application/json' },
...options,
})
if (!res.ok) {
const error = await res.text()
throw new Error(`[${res.status}] ${path}: ${error}`)
}
return res.json()
}5.3 路由新增(Layer 1 Template Studio)
在現有路由基礎上新增:
// src/router/index.ts 新增
{
path: '/templates',
name: 'TemplateList',
component: () => import('../views/TemplateListView.vue'),
},
{
path: '/templates/:id/edit',
name: 'TemplateEditor',
component: () => import('../views/TemplateEditorView.vue'),
},5.4 導航欄新增入口
在 src/components/common/ 的頂部導航欄中新增「Templates」入口,與現有的 Assets、Knowledge 同層級。
六、目標架構
遷移完成後,SIP 的完整架構如下:
starsoup-image-platform/
├── src/ ← Vue 前端(現有,已遷移自 Jaaz)
│ ├── views/
│ │ ├── HomeView.vue
│ │ ├── CanvasView.vue ← AI 生圖(Layer 0)
│ │ ├── TemplateListView.vue ← 新增(Layer 1)
│ │ ├── TemplateEditorView.vue ← 新增(Layer 1,vue-konva)
│ │ ├── AgentStudioView.vue
│ │ ├── AssetsView.vue
│ │ └── KnowledgeView.vue
│ └── ...
│
├── sip-server/ ← Python FastAPI 後端(從 Jaaz 剝離)
│ ├── main.py
│ ├── routers/
│ ├── services/
│ └── requirements.txt
│
├── render-api/ ← Hono + Satori 渲染 API(新增,Layer 2)
│ ├── src/
│ │ ├── renderer/
│ │ │ ├── satori.ts
│ │ │ └── compositor.ts
│ │ └── index.ts ← Hono app
│ └── package.json
│
└── supabase/ ← DB schema migrations
└── migrations/部署拓撲:
Cloudflare R2
├── templates/backgrounds/ ← 背景底圖(AI 生成後上傳)
└── generated/ ← Render API 輸出
Vercel(前端)
└── starsoup-image-platform ← Vue 前端
Vercel / Railway(後端)
├── sip-server(Python FastAPI) ← 現有功能的後端
└── render-api(Hono) ← Layer 2 渲染服務
Supabase
└── templates table ← 模版 JSON 定義七、核心數據結構(SIP 特有)
以下是 Layer 1 & 2 的新增數據結構,與現有 Jaaz 功能互不干涉。
Template JSON Schema
interface Template {
id: string
name: string
width: number // 例如 1080
height: number // 例如 1080
background_url: string // R2 URL
zones: Zone[]
project_id?: string // 多項目隔離(未來用)
created_at: string
updated_at: string
}
interface Zone {
id: string // 對應 Render API 的 data key
type: 'text' | 'image'
x: number
y: number
width: number
height: number
// text zone
font_family?: string // 例如 "Noto Serif TC"
font_size?: number
font_weight?: string // "400" | "700"
color?: string // hex,例如 "#2D1B4E"
align?: 'left' | 'center' | 'right'
line_height?: number
max_lines?: number
prefix?: string // 例如 "¥"
// image zone
border_radius?: number
object_fit?: 'cover' | 'contain' | 'fill'
}Supabase Tables
CREATE TABLE templates (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
width INT NOT NULL,
height INT NOT NULL,
background_url TEXT NOT NULL,
zones JSONB NOT NULL DEFAULT '[]',
project_id TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE render_logs (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
template_id TEXT REFERENCES templates(id),
caller TEXT, -- 例如 "crystal-erp"
image_url TEXT,
rendered_at TIMESTAMPTZ DEFAULT NOW()
);Render API Contract
POST /v1/render
Authorization: Bearer <sip-internal-key>
Request Body:
{
"template_id": "crystal-detail-1x1",
"data": {
"product_image": "https://r2.../amethyst.jpg",
"crystal_name": "天然紫水晶原石",
"price": "3,800",
"description": "來自烏拉圭深礦..."
}
}
Success Response (200):
{
"image_url": "https://r2.../generated/uuid.png",
"template_id": "crystal-detail-1x1",
"rendered_at": "2026-03-24T10:00:00Z"
}八、開發 Phase 計劃
Phase 0:現有功能跑通(當前優先)
目標:解決所有 P0/P1 問題,讓從 Jaaz 遷移過來的功能完全正常運作。
- [ ] 建立
sip-server/,從 Jaaz 剝離後端代碼 - [ ] 修復 502:確保
sip-server在localhost:57988正常啟動 - [ ] 修復模型名稱無法選擇:
GET /api/models正確返回模型列表 - [ ] 修復測試連接:
POST /api/config/test正確轉發供應商 API - [ ] 修復 WebSocket:Socket.IO 連線穩定
- [ ] 修復畫布 CRUD:新建、讀取、保存畫布正常
- [ ] 修復資產管理:圖片上傳、瀏覽正常
- [ ] 去除
jaaz.app設備認證依賴(改為純 API Key 配置) - [ ] 更新
.env.example,整理環境變數
Phase 1:Layer 1 Template Studio
目標:在 SIP 內建立視覺化模版編輯器。
- [ ] 安裝 vue-konva、Supabase JS
- [ ] 建立
TemplateListView.vue(模版列表 + 新建入口) - [ ] 建立
TemplateEditorView.vue(主編輯器)- [ ] 從 R2 載入背景圖至 Konva canvas
- [ ] 拖拽新增 text zone / image zone
- [ ] Zone 樣式面板(字體、顏色、字號、對齊)
- [ ] 即時預覽(canvas 直接顯示樣式)
- [ ] 存入 Supabase
- [ ] 導航欄新增 Templates 入口
Phase 2:Layer 2 Render API
目標:建立程式化圖片生成服務,供 ERP/EC 直調。
- [ ] 建立
render-api/(Hono + TypeScript) - [ ] 整合 Satori + @resvg/resvg-js
- [ ] 嵌入 Noto Serif TC / Noto Sans TC 字體
- [ ] 實現
POST /v1/render端點- [ ] 從 Supabase 取 template JSON
- [ ] 從 R2 取背景圖(base64)
- [ ] Satori 渲染合成
- [ ] PNG 上傳 R2,返回永久 URL
- [ ] 部署至 Vercel(獨立 project)
- [ ]
@crystal/erp整合呼叫測試
Phase 3:Starsoup 化(未來)
- [ ] 多項目隔離(
project_id) - [ ] API Key 管理
- [ ] 渲染統計後台
- [ ] 懶人包長圖支援
九、待確認事項
Q1:sip-server 你有 Jaaz 的完整 repo 可以參照移植嗎?還是需要從頭重寫後端?
Q2:Template Studio(/templates)是獨立頁面,還是要整合進現有的 CanvasView 工作流?(例如:在畫布上完成 AI 生圖後,直接有「存為模版」按鈕)
Q3:Phase 0 的後端,先在本機跑(Python),還是直接目標部署到 Railway / Vercel?
Q4:Supabase 使用現有 ERP 的 Supabase project,還是為 SIP 建立獨立的 project?
本文件合併自 SIP 產品定義文件(v0.1)與遷移技術分析,Phase 0 完成後升版至 v0.3。