first commit
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
# 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
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
```python
|
||||
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
|
||||
|
||||
### Admin (Cookie auth)
|
||||
- `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:
|
||||
```python
|
||||
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
|
||||
101
content-ops/content-ops-agent/references/meta-prompt-pattern.md
Normal file
101
content-ops/content-ops-agent/references/meta-prompt-pattern.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Meta-Prompt Pattern for Prompt Entries
|
||||
|
||||
When the user asks to organize/copy prompts from external sources, convert them to meta-prompt format before publishing to prompt.ephron.ren.
|
||||
|
||||
## What is a Meta-Prompt?
|
||||
|
||||
A meta-prompt is a **template that generates prompts**, not a static prompt itself. Users input their specific requirements, and the AI generates a tailored prompt.
|
||||
|
||||
## Conversion Workflow
|
||||
|
||||
1. **Extract** the original prompt from external platform (小黑盒, etc.)
|
||||
2. **Remove personal specifics** — names (e.g., "Harry"), specific roles, hardcoded values
|
||||
3. **Identify customizable dimensions** — what varies between users
|
||||
4. **Restructure** into template + input fields
|
||||
5. **Add "preset + custom" options** for visual/style parameters
|
||||
6. **Publish** to prompt.ephron.ren with `is_template: true`
|
||||
|
||||
## Template Structure
|
||||
|
||||
```
|
||||
你是一个专业的[领域]提示词生成器。根据用户提供的[输入类型],生成[输出类型]的完整提示词。
|
||||
|
||||
请按以下结构生成:
|
||||
|
||||
---
|
||||
[Template body with {variable} placeholders]
|
||||
---
|
||||
|
||||
用户提供的信息:
|
||||
- [Core field 1]:
|
||||
- [Core field 2]:
|
||||
|
||||
视觉风格:
|
||||
- [Style param 1]:预设A / 预设B / 预设C / 自定义:____
|
||||
- [Style param 2]:预设A / 预设B / 预设C / 自定义:____
|
||||
```
|
||||
|
||||
## "Preset + Custom" Format for Style Parameters
|
||||
|
||||
Every visual/style parameter should offer presets plus a custom option:
|
||||
|
||||
```
|
||||
- 背景色调:纯黑高级感 / 暖白干净风 / 深蓝冷调 / 木纹自然风 / 自定义:____
|
||||
- 整体配色:黑白金经典 / 暖色系食物色 / 冷色系高级灰 / 自定义:____
|
||||
- 灯光氛围:聚光灯突出主体 / 柔光温馨感 / 逆光通透感 / 自定义:____
|
||||
- 文字风格:金色衬线优雅 / 简约黑白现代 / 手写随性 / 自定义:____
|
||||
```
|
||||
|
||||
Benefits:
|
||||
- Casual users pick from presets (low friction)
|
||||
- Advanced users type custom values (full control)
|
||||
- Presets teach users what's possible
|
||||
|
||||
## Example: 食材海报图
|
||||
|
||||
**Original prompt** (from @芝士大白兔 on 小黑盒):
|
||||
```
|
||||
这是一张展示中式鸡汤炖菜食材和成品的食品成分信息图。
|
||||
图中采用了高端商业食品摄影风格,高对比度,干净的工作室合成,戏剧性的垂直布局。
|
||||
背景为纯黑色,表面是深哑光黑色,带有微小的悬浮液滴和柔和的蒸汽。
|
||||
...
|
||||
```
|
||||
|
||||
**Converted meta-prompt:**
|
||||
- Removed specific dish (中式鸡汤炖菜)
|
||||
- Extracted reusable structure (layout, lighting, effects)
|
||||
- Added customizable parameters (background, color palette, lighting style)
|
||||
- Added preset options for each parameter
|
||||
|
||||
## Example: 领英感证件照
|
||||
|
||||
**Original prompt** had hardcoded:
|
||||
- Name: [Harry]
|
||||
- Title: [产品经理]
|
||||
- Department: [产品管理部]
|
||||
- Background: 纯白色素色
|
||||
- Style: 深蓝色粗体大字号
|
||||
|
||||
**Converted meta-prompt:**
|
||||
- Moved all personal info to input fields
|
||||
- Made background, font style, layout style customizable
|
||||
- Added presets for professional contexts (商务利落 / 学术自然 / 创意时尚)
|
||||
|
||||
## Publishing Checklist
|
||||
|
||||
When publishing meta-prompts to prompt.ephron.ren:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "[Prompt Name]",
|
||||
"content": "[Full meta-prompt text]",
|
||||
"description": "[What this meta-prompt generates]",
|
||||
"category": "图像生成",
|
||||
"tags": "[relevant tags, comma-separated]",
|
||||
"is_template": true,
|
||||
"variables": "[comma-separated variable names]",
|
||||
"example_input": "[sample user input]",
|
||||
"example_output": "[truncated sample output]",
|
||||
"recommended_model": "[target model or 通用]"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,132 @@
|
||||
# Prompt 服务架构参考
|
||||
|
||||
> 基于 2026-05-05 代码分析,源码位于 `/home/ubuntu/projects/ephron.ren/prompt/`
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
prompt/
|
||||
├── src/
|
||||
│ ├── main.py # FastAPI 入口,挂载路由和中间件
|
||||
│ ├── config.py # 环境变量配置(AUTH_SECRET_KEY, DATABASE_PATH)
|
||||
│ ├── routes/
|
||||
│ │ ├── pages.py # 页面路由(Jinja2 模板渲染,HTMLResponse)
|
||||
│ │ ├── api.py # 公开 API(/api/prompts, /api/prompts/{key})
|
||||
│ │ ├── admin.py # 管理后台路由(/admin/*,需 Cookie 认证)
|
||||
│ │ └── service_api.py # 服务端 API(/api/service/*,需 Bearer Token)
|
||||
│ └── services/
|
||||
│ ├── prompts.py # 提示词 CRUD + 版本管理
|
||||
│ ├── db.py # SQLite 连接 + 建表
|
||||
│ └── auth.py # 认证辅助
|
||||
├── templates/
|
||||
│ ├── base.html # 基础模板(暗色主题、Inter + JetBrains Mono)
|
||||
│ ├── public/
|
||||
│ │ ├── index.html # 列表页(搜索、分类筛选、标签过滤、卡片网格)
|
||||
│ │ └── detail.html # 详情页(内容展示、复制按钮、示例区域)
|
||||
│ └── admin/
|
||||
│ ├── index.html # 管理列表
|
||||
│ ├── edit.html # 编辑表单
|
||||
│ ├── new.html # 新建表单
|
||||
│ └── versions.html # 版本历史
|
||||
├── static/
|
||||
│ ├── css/ds/ # 设计系统 CSS(tokens, components, layout, motion)
|
||||
│ └── js/ds/ui.js # UI 交互(modal、toast、通用组件)
|
||||
└── tests/
|
||||
```
|
||||
|
||||
## 数据模型
|
||||
|
||||
### prompts 表
|
||||
```sql
|
||||
CREATE TABLE prompts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
key TEXT NOT NULL UNIQUE, -- URL 标识,如 "deep-research-prompt"
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
category TEXT NOT NULL DEFAULT '未分类',
|
||||
tags TEXT, -- 逗号分隔
|
||||
is_template INTEGER NOT NULL DEFAULT 0,
|
||||
variables TEXT, -- 模板变量(逗号分隔)
|
||||
example_input TEXT,
|
||||
example_output TEXT,
|
||||
recommended_model TEXT NOT NULL DEFAULT '通用',
|
||||
is_active INTEGER NOT NULL DEFAULT 1,
|
||||
draft INTEGER NOT NULL DEFAULT 0,
|
||||
created_by TEXT,
|
||||
updated_by TEXT,
|
||||
ownership_type TEXT NOT NULL DEFAULT 'human', -- 'human' | 'service'
|
||||
handoff_to_human INTEGER NOT NULL DEFAULT 0,
|
||||
current_version_id INTEGER,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
```
|
||||
|
||||
### prompt_versions 表
|
||||
```sql
|
||||
CREATE TABLE prompt_versions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
prompt_key TEXT NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_by TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (prompt_key) REFERENCES prompts(key) ON DELETE CASCADE,
|
||||
UNIQUE(prompt_key, version)
|
||||
);
|
||||
```
|
||||
|
||||
## API 端点
|
||||
|
||||
### 公开 API(无需认证)
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/api/prompts` | 列表(支持 search, tag, category, limit, offset) |
|
||||
| GET | `/api/prompts/{key}` | 详情(支持 version 查询参数) |
|
||||
|
||||
### 服务端 API(Bearer Token)
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/api/service/prompts` | 列表 |
|
||||
| GET | `/api/service/prompts/{key}` | 详情 |
|
||||
| POST | `/api/service/prompts` | 创建草稿 |
|
||||
| PATCH | `/api/service/prompts/{key}` | 更新草稿 |
|
||||
| DELETE | `/api/service/prompts/{key}` | 删除草稿 |
|
||||
|
||||
### 页面路由(返回 HTML)
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/` 或 `/prompts` | 列表页(支持 q, category, tag 查询参数) |
|
||||
| GET | `/prompts/{key}` | 详情页 |
|
||||
| GET | `/admin/*` | 管理后台(需登录 + 权限) |
|
||||
|
||||
## 设计系统
|
||||
|
||||
- **主题**: 暗色(bg-primary: #09090b, accent: #3b82f6)
|
||||
- **字体**: Inter(正文)+ JetBrains Mono(代码)
|
||||
- **CSS 变量**: 定义在 `:root` 中,所有组件引用变量
|
||||
- **卡片组件**: `.prompt-card` 使用 `bg-secondary` + border + hover 效果
|
||||
- **标签**: `.tag` 类,带 `tag-bg` 背景
|
||||
- **复制按钮**: `.copy-btn`,绝对定位在内容块右上角
|
||||
- **CSP**: `connect-src 'self'`,`script-src 'self' 'unsafe-inline'`,`cdn.jsdelivr.net` 已白名单
|
||||
|
||||
## 现有功能特性
|
||||
|
||||
1. **版本管理**: 每次编辑创建新版本,支持版本切换
|
||||
2. **模板变量**: `is_template=true` 时,`variables` 字段定义可替换变量
|
||||
3. **草稿系统**: `draft` 字段 + `ownership_type` 区分人类/服务创建
|
||||
4. **搜索过滤**: 支持关键词搜索、分类筛选、标签过滤
|
||||
5. **复制功能**: 前端 `navigator.clipboard.writeText()` 实现
|
||||
|
||||
## 待实现功能(PRD 已写)
|
||||
|
||||
- **调用测试**: 详情页测试 Tab,填变量 → 调 LLM → 流式输出
|
||||
- **集合**: `collections` + `collection_items` 表,组织相关提示词
|
||||
|
||||
## 开发注意事项
|
||||
|
||||
- 路由区分:`response_class=HTMLResponse` → 页面路由,返回 dict/Pydantic → API
|
||||
- Key 自动生成:创建 prompt 时 key 可能被截取/简化,以响应返回值为准
|
||||
- Key 不可变:PATCH 不能修改 key 字段
|
||||
- 服务端只能编辑自己创建的草稿(`created_by == actor_id` + `ownership_type == "service"` + `draft == true`)
|
||||
- 所有时间使用 SQLite `datetime('now')` 存储
|
||||
@@ -0,0 +1,58 @@
|
||||
# WeChat Article Extraction Techniques
|
||||
|
||||
## Problem
|
||||
WeChat articles (mp.weixin.qq.com) trigger CAPTCHA verification when accessed from server IPs. Both curl and headless Playwright hit this wall.
|
||||
|
||||
## What DOESN'T work
|
||||
- `curl` directly → returns verification page (even with realistic User-Agent)
|
||||
- Playwright headless with default settings → "环境异常" CAPTCHA
|
||||
- Playwright with mobile UA + `--disable-blink-features=AutomationControlled` → still CAPTCHA
|
||||
- Accessing `#comment` anchor → loads article content but NOT comments
|
||||
|
||||
## What DOES work
|
||||
|
||||
### 1. QQ Mirror (best option for content)
|
||||
```
|
||||
https://so.html5.qq.com/page/real/search_news?docid=<DOCID>
|
||||
```
|
||||
- Search for the article title on QQ search to find the docid
|
||||
- Renders full article text without verification
|
||||
- **Does NOT include comments**
|
||||
|
||||
### 2. Playwright + `#comment` anchor (partial)
|
||||
```
|
||||
await page.goto("https://mp.weixin.qq.com/s/<HASH>#comment")
|
||||
```
|
||||
- Sometimes loads the article body text (server-side rendered)
|
||||
- Still no comments — those require WeChat JS runtime + login
|
||||
|
||||
### 3. OG metadata extraction
|
||||
Even on the verification page, meta tags are available:
|
||||
```python
|
||||
og_title = await page.evaluate('document.querySelector(\'meta[property="og:title"]\')?.content')
|
||||
og_desc = await page.evaluate('document.querySelector(\'meta[property="og:description"]\')?.content')
|
||||
```
|
||||
Also available in HTML: `msg_title`, `msg_desc`
|
||||
|
||||
### 4. mmx search for indirect sources
|
||||
```bash
|
||||
mmx search query '"exact article title" site:csdn.net OR site:zhihu.com'
|
||||
```
|
||||
Many WeChat articles get cross-posted to CSDN, 知乎, 今日头条, etc.
|
||||
|
||||
## Comments
|
||||
WeChat article comments are **never accessible without login**. They require:
|
||||
- WeChat JS runtime (not available in headless browser)
|
||||
- Authenticated WeChat session
|
||||
- Comments API calls with specific token/session parameters
|
||||
|
||||
**Workaround**: Search for user discussions on other platforms (GitHub issues, 知乎, 小红书, 即刻, B站) using `mmx search`.
|
||||
|
||||
## Example extraction flow
|
||||
```
|
||||
1. Try mmx search for article title → find QQ mirror or cross-post
|
||||
2. If found: Playwright fetch from QQ mirror → get full text
|
||||
3. If not found: Playwright + #comment → get article body (no comments)
|
||||
4. For comments: mmx search for "article title 评价 OR 反馈 OR 体验"
|
||||
5. For community data: GitHub API for related repos (stars, forks, issues)
|
||||
```
|
||||
Reference in New Issue
Block a user