Files
agent-skills/content-ops/content-ops-agent/references/canvas-service-architecture.md
Hermes Agent ccc63d1e70 first commit
2026-05-10 13:52:46 +08:00

3.7 KiB

Canvas Service Architecture

Overview

Canvas is an HTML tool showcase platform at canvas.ephron.ren. Unlike Blog (markdown files) and Prompt (SQLite), Canvas uses a file-based storage system.

Storage Structure

content/pages/
├── meta.json          # Metadata for all pages
├── slug-1.html        # HTML content files
├── slug-2.html
└── .gitkeep
  • Each canvas is a standalone HTML file (filename = slug)
  • meta.json stores all metadata in a single JSON file
  • No database involved

meta.json Format

{
  "pages": {
    "hermes-agent-ai": {
      "title": "Hermes Agent — 自我进化的 AI 智能体",
      "description": "介绍页",
      "source": "other",
      "category": "tool",
      "tags": ["AI", "Agent"],
      "draft": false,
      "created_at": "2026-05-06T00:00:00",
      "updated_at": "2026-05-06T00:00:00",
      "created_by": "svc_xxx",
      "updated_by": "svc_xxx",
      "ownership_type": "service",
      "handoff_to_human": false,
      "views": 42
    }
  }
}

Valid Categories (hardcoded)

CANVAS_CATEGORIES = [
    ("tool", "🔧 实用工具"),
    ("game", "🎮 小游戏"),
    ("visual", "🎨 可视化"),
    ("learning", "📚 学习教育"),
    ("productivity", "⚡ 效率提升"),
    ("fun", "🎉 趣味娱乐"),
    ("other", "📦 其他"),
]

Source: canvas/src/services/canvas.py lines 624-632.

Route Structure

Route file Auth Purpose
pages.py None (public) Homepage list, view page, raw HTML
service_api.py Bearer Token Draft CRUD only
admin.py Cookie (ephron_auth) Full CRUD + publish toggle

Key Endpoints

Public (no auth)

  • GET / — Homepage, shows non-draft canvases grouped by category
  • GET /view/{slug} — View page with iframe embedding /raw/{slug}
  • GET /raw/{slug} — Raw HTML content (iframe src target)

Service API (Bearer Token)

  • GET /api/service/canvas — List own drafts only
  • GET /api/service/canvas/{slug} — Get own draft
  • POST /api/service/canvas — Create draft
  • PATCH /api/service/canvas/{slug} — Update own draft (fails if published)
  • DELETE /api/service/canvas/{slug} — Delete own draft
  • GET /admin — Admin dashboard with all canvases (incl. drafts)
  • GET /admin/new — New canvas form
  • POST /admin/new — Create canvas
  • GET /admin/edit/{slug} — Edit form
  • POST /admin/edit/{slug} — Save edits
  • POST /admin/toggle-draft — Toggle draft status (publish/unpublish)
  • POST /admin/delete — Delete canvas

Auth Flow (Admin)

  1. Login at auth.ephron.ren/api/login with form data (username + password)
  2. Response sets ephron_auth cookie on .ephron.ren domain
  3. Admin routes check cookie via is_authenticated()
  4. CSRF token required for all POST forms (generated per-request)

iframe Embedding Issue

The /view/{slug} page uses <iframe src="/raw/{slug}"> to display canvas content. The shared security_headers.py middleware blocks iframe embedding with X-Frame-Options: DENY and frame-ancestors 'none'.

Solution: Override headers in the /raw/{slug} route handler:

headers={
    "X-Frame-Options": "SAMEORIGIN",
    "Content-Security-Policy": "...frame-ancestors 'self'...",
}

Source Files

  • canvas/src/routes/pages.py — Public page routes
  • canvas/src/routes/service_api.py — Service API (Bearer Token)
  • canvas/src/routes/admin.py — Admin routes (Cookie auth)
  • canvas/src/services/canvas.py — Core service (storage, CRUD, categories)
  • canvas/src/services/auth.py — Auth helpers
  • canvas/src/config.py — Config (CONTENT_DIR, COOKIE_NAME, etc.)
  • shared/security_headers.py — Shared security middleware