Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
dbf2038
feat(web): rename web-next to web and ship Soft Product 2 UI refresh
nextroad-dev Oct 12, 2026
1e00da4
chore(audit): remediate security, boundaries and quality-gate findings
nextroad-dev Oct 16, 2026
66f83ec
feat(api): W0 默认媒体模型契约+DB+API(GET/PATCH admin/media-model-defaults,is…
nextroad-dev Oct 16, 2026
5d9d9bf
feat(web): F0 前端基础(useFlipList+readMotionEasing、brand mark、创作/图库导航统一、…
nextroad-dev Oct 16, 2026
6704af3
feat(web): F1 落地页作品条规格横条三步流程收尾区+法律页进度条
nextroad-dev Oct 16, 2026
f963978
feat(web): F3 创作台模型选择器与结果卡核心时刻
nextroad-dev Oct 16, 2026
9f38508
feat(web): F4 图库灯箱左右栏与渐进加载
nextroad-dev Oct 16, 2026
118e4c8
feat(web): F5 登录左右栏与6格验证码
nextroad-dev Oct 16, 2026
02a2297
feat(web): F7 后台可折叠侧栏与概览默认模型卡
nextroad-dev Oct 16, 2026
366960f
fix(web): V 视觉问题收尾(抽屉useDialog、危险区、token圆角字号)
nextroad-dev Oct 16, 2026
e37a5bf
feat(web): F2 公共页移动导航(640下菜单按钮+顶部落下面板)
nextroad-dev Oct 16, 2026
ea19d49
feat(web): F6 FLIP接入(toast/任务行/参考图排序)
nextroad-dev Oct 16, 2026
d1f5036
feat(web): F8b 媒体模型默认区(默认卡+设为默认+类型筛选)
nextroad-dev Oct 16, 2026
c3d83d9
feat(web): 落地页作品素材5张+showcase.json提示词
nextroad-dev Oct 16, 2026
d3aef90
feat(web): 登录拼图接真实作品图,两处移除画幅比例标注
nextroad-dev Oct 16, 2026
bf110ef
fix(deploy): images栈透传S3_*/SMTP_*环境兜底(全新安装无DB行时可用)
nextroad-dev Oct 16, 2026
488958a
ui: redesign workspace, admin, auth and library to match reference mocks
nextroad-dev Oct 17, 2026
f78538f
fix(web): center workspace header nav between logo and controls
nextroad-dev Oct 17, 2026
6913a7e
fix(worker): bind account id and propagate deletion transaction errors
nextroad-dev Oct 17, 2026
96dd1c1
perf(api): batch job output reads for task lists
nextroad-dev Oct 17, 2026
e03c77f
perf(api): reuse signed input URLs across compatible DTO fields
nextroad-dev Oct 17, 2026
71c50af
fix(api): preserve PostgreSQL microseconds in pagination cursors
nextroad-dev Oct 17, 2026
baac0f3
fix(web): restore responsive admin records and dialog focus recovery
nextroad-dev Oct 18, 2026
18145e6
fix(database): bound pool waits and handle idle connection errors
nextroad-dev Oct 18, 2026
99369d4
perf(api): share settings refresh and signing with bounded Redis access
nextroad-dev Oct 18, 2026
a1945e6
fix(worker): claim outbox atomically and fence concurrent provider ex…
nextroad-dev Oct 18, 2026
1dcf0dc
perf(database): add concurrent owner and pending outbox indexes
nextroad-dev Oct 18, 2026
e43347f
perf(database): remove ordering indexes covered by unique constraints
nextroad-dev Oct 18, 2026
872eb1c
fix(worker): isolate cleanup and monitor scheduler and consumer progress
nextroad-dev Oct 18, 2026
81965e0
feat(maintenance): add bounded opt-in outbox history pruning
nextroad-dev Oct 18, 2026
83c99f2
chore(deploy): configure recovery and add isolated backend reliabilit…
nextroad-dev Oct 18, 2026
4020fe4
feat(api): finalize media model defaults contract and openapi
nextroad-dev Oct 19, 2026
515c0a9
fix(web): align landing page and public nav with design mocks
nextroad-dev Oct 19, 2026
b6a323c
fix(web): align generate console and login with design mocks
nextroad-dev Oct 19, 2026
b24f318
fix(web): align library lightbox and admin pages with design mocks, r…
nextroad-dev Oct 19, 2026
5dab32c
fix(web): align landing, generate motion, library, login and admin wi…
nextroad-dev Oct 20, 2026
f9403d0
fix(web): read oauth providers from data.providers on the login page
nextroad-dev Oct 20, 2026
fb17052
feat(setup): interactive onboarding wizard with local-disk storage an…
nextroad-dev Oct 21, 2026
5cfc455
feat(setup): make the onboarding claim code optional behind SETUP_CLA…
nextroad-dev Oct 21, 2026
fce8be9
chore(deps): pin Node 22.23.4 for the October 2026 security release (…
nextroad-dev Oct 22, 2026
c745dbb
fix(ci): expect migration 0040 in PG fixture and satisfy prefer-as-co…
nextroad-dev Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
40 changes: 37 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,53 @@ MINIO_ROOT_PASSWORD=
# Local MinIO only: allowed browser origins for direct presigned POST upload (comma-separated or *)
# MINIO_API_CORS_ALLOW_ORIGIN=http://localhost:8080,http://127.0.0.1:8080,http://localhost:5173,http://127.0.0.1:5173,http://localhost:3000,http://127.0.0.1:3000

# Image deployment knobs (compose.images.yaml / compose.prod.yaml web-next build):
# Image deployment knobs (compose.images.yaml / compose.prod.yaml web build):
# MUSECANVAS_IMAGE_TAG=latest
# MUSECANVAS_HTTP_PORT=18080
# Local web-next dev only (apps/web-next/.env.local): point the dev rewrites

# First-run onboarding (/setup) needs a one-time claim code. In production nothing is minted
# unless you opt in. Set ONE of these for the first install only, then remove it:
# SETUP_CLAIM_DELIVERY=log prints the code once to the API container log
# (docker compose logs api | grep "MUSECANVAS SETUP CLAIM").
# Anyone who can read that log can redeem the code for 15 minutes
# (single use, 5 attempts), so clear the log line after use.
# SETUP_CLAIM_DELIVERY=file writes the code (mode 0600) to SETUP_CLAIM_FILE, a path inside
# the API container; read it with docker compose exec.
# SETUP_CLAIM_DELIVERY=
# SETUP_CLAIM_FILE=
# Leave SETUP_CLAIM_REQUIRED unset (the default) in every normal install: /setup then needs the claim code.
# SETUP_CLAIM_REQUIRED=false removes the claim code. WARNING: while no admin exists, anyone who can reach
# /setup can configure SMTP and create the first admin. Use it only on networks you control, and remove
# it once the first admin exists. The window closes by itself after that, but leave it unset elsewhere.
# SETUP_CLAIM_REQUIRED=
# Local web dev only (apps/web/.env.local): point the dev rewrites
# proxy directly at the API so write requests keep a valid X-Forwarded-Host.
# API_ORIGIN=http://localhost:3001
# In containers web-next SSR talks to the API directly via INTERNAL_API_BASE_URL
# In containers web SSR talks to the API directly via INTERNAL_API_BASE_URL
# (set by compose); browsers always use the same-origin /api proxied by Nginx.
# Source/image deployment stacks expect TLS and default cookies to Secure.
# Set false only when intentionally exposing Nginx directly over plain HTTP.
# Bundled local stacks default this to false for their documented HTTP ports.
# COOKIE_SECURE=true

# ===== Backend resource budgets (per process; pool totals scale with replicas) =====
DATABASE_POOL_MAX=12
DATABASE_CONNECTION_TIMEOUT_MS=5000
DATABASE_IDLE_TIMEOUT_MS=30000
DATABASE_STATEMENT_TIMEOUT_MS=30000
DATABASE_LOCK_TIMEOUT_MS=5000
DATABASE_IDLE_TRANSACTION_TIMEOUT_MS=30000
REDIS_CONNECT_TIMEOUT_MS=1000
REDIS_COMMAND_TIMEOUT_MS=1000
WORKER_REDIS_COMMAND_TIMEOUT_MS=5000
WORKER_CONCURRENCY=2
WORKER_STORAGE_TIMEOUT_MS=60000
# Only acknowledged/delivered Stream entries older than this are removed.
# Pending/unread entries and streams with unknown groups are never trimmed.
# Set 0 to disable automatic Stream deletion before deploying if required.
STREAM_RETENTION_SECONDS=604800
STREAM_TRIM_BATCH_SIZE=1000

# ===== Deployment security policies (default-deny; keep false unless required) =====
# Plaintext SMTP is only ever needed for the bundled Mailpit, which the
# bundled Compose files already allow. External deployments must use
Expand Down
58 changes: 58 additions & 0 deletions .github/workflows/backend-reliability.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Backend reliability fixtures

on:
pull_request:
branches: [main]
paths: ['apps/api/**', 'apps/worker/**', 'packages/database/**', 'tests/integration/backend-reliability*', 'deploy/**', '.github/workflows/backend-reliability.yml']
workflow_dispatch:

permissions:
contents: read

jobs:
fixtures:
runs-on: ubuntu-latest
timeout-minutes: 10
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: fixture
POSTGRES_PASSWORD: fixture-only
POSTGRES_DB: postgres
ports: ['5432:5432']
options: --health-cmd "pg_isready -U fixture -d postgres" --health-interval 3s --health-timeout 3s --health-retries 20
redis:
image: redis:7.4-alpine
ports: ['6379:6379']
options: --health-cmd "redis-cli ping" --health-interval 3s --health-timeout 3s --health-retries 20
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22.23.4
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Validate all Compose variants with synthetic configuration
env:
POSTGRES_PASSWORD: fixture-only
APP_MASTER_KEY: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
MINIO_ROOT_PASSWORD: fixture-only-password
run: |
for file in deploy/compose.yaml deploy/compose.dev.yaml deploy/compose.prod.yaml deploy/compose.images.yaml; do
docker compose --project-directory . -f "$file" config --quiet
done
- name: Test real PostgreSQL and Redis behavior in disposable fixtures
env:
MUSECANVAS_OPTIMIZATION_PG_URL: postgresql://fixture:fixture-only@127.0.0.1:5432/postgres
MUSECANVAS_OPTIMIZATION_REDIS_URL: redis://127.0.0.1:6379/15
run: pnpm --filter @musecanvas/providers exec tsx --test "../../tests/integration/backend-reliability*.test.ts"
- uses: actions/upload-artifact@v4
if: always()
with:
name: synthetic-backend-query-plans
path: .runtime/backend-benchmark.json
if-no-files-found: ignore
5 changes: 3 additions & 2 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
- name: Rotate password without replacing persistent data
env:
COMPOSE_PROJECT_NAME: musecanvas-password-sync
# 此为 CI 一次性容器的非生产占位密钥,切勿用于任何真实环境。
APP_MASTER_KEY: 0000000000000000000000000000000000000000000000000000000000000000
MINIO_ROOT_PASSWORD: unused-by-password-sync-test
OLD_PASSWORD: before-password-rotation
Expand Down Expand Up @@ -98,8 +99,8 @@ jobs:
dockerfile: deploy/docker/worker.Dockerfile
build_args: ""

- image: musecanvas-web-next
dockerfile: deploy/docker/web-next.Dockerfile
- image: musecanvas-web
dockerfile: deploy/docker/web.Dockerfile
build_args: ""

- image: musecanvas-nginx
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/media-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
- name: Set up Node.js with pnpm cache
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 22.23.4
cache: pnpm

- name: Install dependencies with frozen lockfile (no external services)
Expand All @@ -59,6 +59,9 @@ jobs:
- name: Typecheck workspaces (no external services)
run: pnpm -r --if-present typecheck

- name: Lint workspaces (no external services)
run: pnpm -r --if-present lint

- name: Test providers package incl. media contract (no external services)
run: pnpm --filter @musecanvas/providers test

Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/web-checks.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Web Checks CI

# Frontend-only gate for `apps/web-next`.
# Frontend-only gate for `apps/web`.
#
# `media-quality.yml` already runs `pnpm -r --if-present typecheck` and
# `pnpm -r --if-present build` for every workspace on the same PRs, so the
Expand All @@ -11,7 +11,7 @@ name: Web Checks CI
# a token regression is reported as a token failure rather than as a build error
# somewhere downstream.
#
# The web-next typecheck stays too, scoped with `--filter` (seconds, not minutes) so
# The web typecheck stays too, scoped with `--filter` (seconds, not minutes) so
# this workflow is meaningful on its own even if that workflow's path filters change.
#
# No secrets, no external services, no image build or push.
Expand All @@ -20,7 +20,7 @@ on:
push:
branches: ["main"]
paths:
- "apps/web-next/**"
- "apps/web/**"
- "scripts/check-web-tokens.mjs"
- "package.json"
- "pnpm-lock.yaml"
Expand All @@ -29,7 +29,7 @@ on:
pull_request:
branches: ["main"]
paths:
- "apps/web-next/**"
- "apps/web/**"
- "scripts/check-web-tokens.mjs"
- "package.json"
- "pnpm-lock.yaml"
Expand Down Expand Up @@ -62,7 +62,7 @@ jobs:
- name: Set up Node.js with pnpm cache
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 22.23.4
cache: pnpm

- name: Install dependencies with frozen lockfile (no external services)
Expand All @@ -72,4 +72,4 @@ jobs:
run: pnpm check:tokens

- name: Typecheck the web app (no external services)
run: pnpm --filter @musecanvas/web-next typecheck
run: pnpm --filter @musecanvas/web typecheck
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ CLAUDE.md
.codex/
.qoder/
.serena/
.pi/
/skills/
.runtime/
.zcode/
Expand Down
8 changes: 4 additions & 4 deletions agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
MuseCanvas
├── apps/
│ ├── api/ # Next.js 15 (App Router) 核心后端服务 (REST API, 鉴权, 路由)
│ ├── web-next/ # Next.js 15 (App Router, React 19) + Zustand + TanStack Query 混合渲染前端应用(唯一前端)
│ ├── web/ # Next.js 15 (App Router, React 19) + Zustand + TanStack Query 混合渲染前端应用(唯一前端)
│ └── worker/ # Redis Streams + 事务性 Outbox 异步任务消费端 (生成、轮询与维护任务)
├── packages/
│ ├── contracts/ # 共享 DTO (纯 TS 类型)、错误码、类型契约与 API_ENDPOINTS 端点路径常量表 (最底游依赖,无运行时校验库)
Expand All @@ -42,9 +42,9 @@ MuseCanvas

---

## 前端架构与状态设计规范 (Frontend Standards: web-next)
## 前端架构与状态设计规范 (Frontend Standards: web)

在开发与维护 `apps/web-next` 时,必须严格遵循以下混合渲染与状态管理准则:
在开发与维护 `apps/web` 时,必须严格遵循以下混合渲染与状态管理准则:

### 1. 业务驱动的混合渲染矩阵 (Hybrid Rendering Matrix)
- **公共营销与法律条款页(SSG 纯静态生成)**:
Expand Down Expand Up @@ -130,7 +130,7 @@ MuseCanvas
- 专注于 `apps/worker` 队列消费逻辑与 `packages/providers` 外部集成。
- 处理异步重试、幂等性、超时控制与任务状态流转。
- **前端交互 Agent (Frontend Coder)**:
- 专注于 `apps/web-next`(Next.js 15 + React 19 + Zustand + TanStack Query)的组件、状态、路由与页面逻辑。
- 专注于 `apps/web`(Next.js 15 + React 19 + Zustand + TanStack Query)的组件、状态、路由与页面逻辑。
- 严格保持与 `contracts` 定义的前后端交互类型一致,遵循混合渲染与双端 API 客户端隔离规范。
- **代码审查与质检 Agent (Reviewer / QA)**:
- 对变更实施独立审查,检查安全性、类型健壮性、边界用例覆盖与项目规范符合度。
Expand Down
2 changes: 1 addition & 1 deletion apps/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Applications

- `web-next`: Next.js 15 (React 19) 用户端和管理员后台。
- `web`: Next.js 15 (React 19) 用户端和管理员后台。
- `api`: Next.js API Routes,负责认证、授权和业务接口。
- `worker`: 独立 TypeScript Worker,负责队列消费和定时任务。

1 change: 0 additions & 1 deletion apps/api/app/api/.gitkeep

This file was deleted.

13 changes: 12 additions & 1 deletion apps/api/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
import type { NextConfig } from 'next'

const config: NextConfig = { serverExternalPackages: ['pg', 'nodemailer', 'sharp'] }
// `@musecanvas/*` packages export raw `.ts` sources, so Next must compile them
// rather than treat them as prebuilt externals. `serverExternalPackages` is kept
// for the native/Node-only deps that must stay outside the bundle.
const config: NextConfig = {
transpilePackages: [
'@musecanvas/contracts',
'@musecanvas/database',
'@musecanvas/domain',
'@musecanvas/providers',
],
serverExternalPackages: ['pg', 'nodemailer', 'sharp'],
}
export default config
9 changes: 5 additions & 4 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"build": "next build",
"start": "next start -p 3001",
"typecheck": "tsc --noEmit",
"test": "node --import tsx --test \"src/**/*.test.ts\""
"test": "node --import tsx --test \"src/**/*.test.ts\"",
"lint": "eslint ."
},
"dependencies": {
"@aws-sdk/client-s3": "^3.1073.0",
Expand All @@ -19,16 +20,16 @@
"@musecanvas/domain": "workspace:*",
"@musecanvas/providers": "workspace:*",
"jose": "^6.2.3",
"next": "^15.5.19",
"nodemailer": "^7.0.13",
"next": "^15.5.27",
"nodemailer": "^10.1.1",
"pg": "^8.22.0",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"redis": "^5.12.1"
},
"devDependencies": {
"@types/node": "^22.19.21",
"@types/nodemailer": "^7.0.12",
"@types/nodemailer": "^8.0.2",
"@types/pg": "^8.20.0",
"@types/react": "19.2.17",
"tsx": "^4.22.4",
Expand Down
Loading
Loading