diff --git a/.gitea/workflows/wiki.yml b/.gitea/workflows/waterfall-wiki.yml similarity index 58% rename from .gitea/workflows/wiki.yml rename to .gitea/workflows/waterfall-wiki.yml index 1abefb7..0292d7b 100644 --- a/.gitea/workflows/wiki.yml +++ b/.gitea/workflows/waterfall-wiki.yml @@ -1,10 +1,10 @@ -name: wiki CI and deployment +name: waterfall-wiki CI and deployment on: push: branches: [main] paths: - - 'apps/wiki/**' - - '.gitea/workflows/wiki.yml' + - 'apps/waterfall-wiki/**' + - '.gitea/workflows/waterfall-wiki.yml' - 'Dockerfile' - 'compose.yml' - 'scripts/deploy.sh' @@ -14,8 +14,8 @@ on: - 'pnpm-workspace.yaml' pull_request: paths: - - 'apps/wiki/**' - - '.gitea/workflows/wiki.yml' + - 'apps/waterfall-wiki/**' + - '.gitea/workflows/waterfall-wiki.yml' - 'Dockerfile' - 'compose.yml' - 'scripts/deploy.sh' @@ -34,16 +34,16 @@ jobs: with: fetch-depth: 0 - name: Validate configuration - run: docker compose --env-file apps/wiki/.env.example config --quiet - - name: Build and verify Wiki - run: docker build --target integration -t "prompt-tests:${{ gitea.sha }}" . + run: docker compose --env-file apps/waterfall-wiki/.env.example config --quiet + - name: Build and verify waterfall-wiki + run: docker build --target integration -t "waterfall-wiki-tests:${{ gitea.sha }}" . - name: Build runtime - run: docker build --target runtime -t "prompt:${{ gitea.sha }}" . + run: docker build --target runtime -t "waterfall-wiki:${{ gitea.sha }}" . - name: Integration stack env: - PROMPT_IMAGE: prompt:${{ gitea.sha }} - PROMPT_TEST_IMAGE: prompt-tests:${{ gitea.sha }} + WATERFALL_WIKI_IMAGE: waterfall-wiki:${{ gitea.sha }} + WATERFALL_WIKI_TEST_IMAGE: waterfall-wiki-tests:${{ gitea.sha }} run: bash scripts/integration.sh - - name: Deploy Wiki + - name: Deploy waterfall-wiki if: gitea.ref == 'refs/heads/main' && (gitea.event_name == 'push' || gitea.event_name == 'workflow_dispatch') run: bash scripts/deploy.sh "${{ gitea.sha }}" diff --git a/Dockerfile b/Dockerfile index 61cd7fc..0a98de4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,16 +7,16 @@ RUN apt-get update \ FROM base AS dependencies RUN corepack enable && corepack prepare pnpm@11.25.0 --activate -COPY apps/wiki/pnpm-lock.yaml ./ +COPY apps/waterfall-wiki/pnpm-lock.yaml ./ COPY pnpm-workspace.yaml /tmp/monorepo-policy.yaml RUN node -e "const fs=require('fs');const text=fs.readFileSync('/tmp/monorepo-policy.yaml','utf8');fs.writeFileSync('pnpm-workspace.yaml', text.slice(text.indexOf('allowBuilds:')));" RUN pnpm fetch --frozen-lockfile --fetch-timeout=300000 --network-concurrency=8 -COPY apps/wiki/package.json ./ +COPY apps/waterfall-wiki/package.json ./ RUN pnpm install --offline --frozen-lockfile FROM dependencies AS build # .dockerignore excludes docs and operational notes without extra legacy COPY snapshots. -COPY apps/wiki/ . +COPY apps/waterfall-wiki/ . RUN pnpm typecheck && pnpm test && pnpm build && pnpm test:smoke FROM build AS integration @@ -29,9 +29,9 @@ ENV NODE_ENV=production HOSTNAME=0.0.0.0 PORT=3000 \ COPY --from=build --chown=node:node /app/.next/standalone/ ./ COPY --from=build --chown=node:node /app/dist/worker.cjs /app/dist/bootstrap.cjs /app/dist/publish-learning-series.cjs /app/dist/auth-cli.cjs ./ COPY --from=build --chown=node:node /app/db/ ./db/ -COPY --chown=node:node apps/wiki/scripts/container-health.mjs ./ -COPY --chown=node:node apps/wiki/scripts/import-character-packs.mjs ./scripts/import-character-packs.mjs -COPY --chown=node:node apps/wiki/seed/characters/ ./seed/characters/ +COPY --chown=node:node apps/waterfall-wiki/scripts/container-health.mjs ./ +COPY --chown=node:node apps/waterfall-wiki/scripts/import-character-packs.mjs ./scripts/import-character-packs.mjs +COPY --chown=node:node apps/waterfall-wiki/seed/characters/ ./seed/characters/ USER node EXPOSE 3000 CMD ["node", "server.js"] diff --git a/README.md b/README.md index f0e1943..949ade5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,102 @@ -# PROMPT monorepo +# PROMPT -Wiki、创作站和 Python 导出工具。 +把提示词、参考图、分步文档和创作工具收进同一个可连接的知识库。人可以浏览瀑布流、搜索和编辑;AI 可以从同一篇 post 读取纯文本、图片与 API 说明,再调用 Studio 生成作品。 -详见 [项目结构与运行方式](docs/monorepo.md)。 +**PROMPT 是网站与项目品牌;`waterfall-wiki` 是知识库应用的内部名称。** 目录、包、CI、镜像和 Compose 服务使用内部名称,网站导航与页面标题使用 PROMPT。 -业务接口详见 [API 说明](docs/api.md),线上纯文本目录为 `/api?agent_mode=True`。 +线上入口:[PROMPT](http://8.211.154.134:18131/)。内容、媒体、API 与 Studio 共用登录入口。 + +## 从一篇 post 开始 + +每篇内容都有稳定的 `/explore/` 地址。标题、标签、正文和封面可以修改,链接保持不变。数据库内容可以保留 24 位 public ID,内部仍使用 UUID;旧 `/p/`、`/skills/...` 等地址保留兼容跳转。 + +- **浏览**:首页按不同笔记的反向引用数优先排序。重复引用和自引用不重复计数,代码、图片与外站链接不冒充笔记引用。详情可以查看引用来源。 +- **搜索**:标题、简介、正文、普通标签和系统标签共同匹配。支持多个关键词及双引号短语,先按相关性、再按引用数和时间排序;先筛选再分页。 +- **标签**:普通标签使用 `#中国风`、`#贴纸` 等名称,可以创建、添加或移除。系统标签是固定列表,目前为“文档入口”和“参考图”,只能搜索并勾选,不能通过 `#` 创建。系统标签显示在图片角标,普通标签显示在文字区;分类页顶部按系统标签筛选。 +- **编辑**:卡片与详情都能直接编辑标签;完整编辑器可以修改标题、简介、Markdown。保存使用 revision 检测并发冲突,避免旧窗口覆盖新修改。 +- **素材**:原件和加工结果都是普通资产,可被多篇 post 引用。文档封面与实际参考素材分开,子文档可以有自己的封面,同时保留原参考图。 + +## 给 AI 的入口 + +在同一个 post 地址加 `?agent_mode=True` 可以读取纯文本正文和媒体链接。业务 API 的相同参数会先返回接口说明;已有查询参数时使用 `&agent_mode=True`。这些入口同样需要登录或有效 Bearer JWT。 + +| 地址 | 用途 | +| --- | --- | +| `/explore/` | 人看的 post;加 `?agent_mode=True` 读纯文本 | +| `/api?agent_mode=True` | 业务 API 目录 | +| `/api/explore-feed` | 统一搜索、系统/普通标签筛选与分页 | +| `/api/notes/` | GET 读取;PUT 编辑全文;PATCH 仅更新标签 | +| `/api/tags` | GET 查看普通/系统标签;POST 创建或复用普通标签 | +| `/api/upload` | 上传媒体;`assetOnly=true` 独立保存,不创建 post | +| `/api/posts` | 用正文和已有资产创建普通 post | +| `/api/media/` | 媒体原件与衍生文件,支持 Range | +| `/api/preview` | 有尺寸上限的 WebP 预览,不接受任意远程 URL | + +标签 PATCH 请求为 `{ "tags": ["标签 UUID"], "revision": "GET 返回的 revision" }`。它不会要求用户重输正文。Tag 对象的 `kind` 为 `normal` 或 `system`;系统标签还有稳定的 `system_key`。普通创建接口不允许提交 `kind` 或 `system_key`。 + +Wiki 中的教程是普通 post 与媒体数据,不需要为每份资料增加专用网页路由、JSON 文件端点或代码注册。 + +## Studio:从教程进入工具 + +Studio 负责交互式排版与无状态渲染,知识库负责教程、来源和入口。`/studio` 返回 PROMPT 首页;请从对应 post 打开具体工具。 + +现有工具: + +- `/studio/ideas/learning-skills-template`:ref-17 同一母版的四张占位封面。 +- `/studio/ideas/layout-selection`:ref-01、ref-07、ref-26 三种构图;可修改文字与配色,核对构件边界。 +- `/studio/api/export/`:GET 读取方案或初始 SVG;POST 校验自定义参数并生成 SVG/JSON。详细说明读取该地址的 `?agent_mode=True`。 + +渲染不自动保存作品,也不自动发布 post。需要保留的结果应下载后再通过普通媒体和 post API 发布。原图的 T/I/D 构件、画板比例与来源保留为可读取数据;文字替换和矢量重建的范围应在教程中明确。 + +## 仓库结构 + +| 路径 | 职责 | +| --- | --- | +| `apps/waterfall-wiki` | Next.js Pages Router 知识库、搜索、标签、媒体 API、认证与后台视频加工 | +| `apps/studio` | 独立 Next.js 创作工具与封面渲染 API | +| `apps/python` | 按需运行的 PNG/视频导出 CLI,没有常驻网络服务 | +| `content` | 本地作者资料、教程及排版源图,不打入 Wiki 运行镜像 | +| `scripts` | CI 部署、运维、API 发布与作者工具 | +| `docs` | 数据模型、接口、认证与部署说明 | + +两个 JavaScript 应用分别维护冻结的 pnpm 锁文件。根 Dockerfile 是 waterfall-wiki 的构建入口;应用目录的 Dockerfile 与它保持一致。 + +## 环境与检查 + +运行环境:Node.js 24、pnpm 11.25.0、PostgreSQL 17、S3 兼容对象存储;视频处理使用 FFmpeg,部分导出使用 Chromium。Python 工具需要 Python 3.12+。 + +```sh +pnpm install +pnpm --dir apps/waterfall-wiki typecheck +pnpm --dir apps/waterfall-wiki test +pnpm --dir apps/studio typecheck +bash -n scripts/deploy.sh scripts/integration.sh scripts/deploy-studio.sh +``` + +完整集成测试只针对显式隔离的数据库、对象存储和生产容器运行,覆盖存储、视频加工、编辑冲突、真实搜索与引用、标签规则和登录保护。不要把生产存储配置拿来运行集成测试。 + +本项目以指定服务器为唯一网页运行与验收入口:先推送,再等待 ces-control CI/CD,最后直接检查服务器。不要启动本地网页预览,不用 localhost 页面作为视觉验收或交付入口。具体约定见 [AGENTS.md](AGENTS.md)。 + +## 部署与持久化 + +Gitea 工作流按应用路径触发:`waterfall-wiki.yml`、`studio.yml` 和 `python.yml`。waterfall-wiki 完成类型检查、单元测试、生产构建、冒烟和真实 PostgreSQL/S3 集成测试后部署;Studio 独立构建和部署。文档与作者数据本身不触发应用部署。 + +| 服务 | 入口/端口 | +| --- | --- | +| `waterfall-wiki` | 公网 `18131`,也是 Studio 的统一入口 | +| PostgreSQL | `127.0.0.1:18132` | +| 对象存储 | `127.0.0.1:18133` | +| Studio | `127.0.0.1:18134`;公网通过 `/studio` 代理 | +| Python | 无网络端口,按需运行 | + +生产 Compose 项目仍为 `prompt`,部署根目录仍为 `/opt/prompt`。既有 `prompt_postgres_data`、`prompt_objects`、`prompt_auth_data` 卷与数据库身份保留;应用更名不会重新创建一套空存储。Compose 应用服务改为 `waterfall-wiki` 和 `waterfall-wiki-worker`,镜像参数为 `WATERFALL_WIKI_IMAGE` / `WATERFALL_WIKI_TEST_IMAGE`。 + +部署在停止写入后备份数据库,再运行迁移和启动新版。失败时恢复兼容旧镜像的备份并回滚服务。生产不执行 `down -v`,也不清空对象文件。早期合集验证会遍历分页,不假定旧文章一定在第一页。 + +## 登录与自动化 + +共享验证器的 6 位 TOTP 动态码登录,JWT 保存在 HttpOnly Cookie 中;这属于单因素动态码登录,不是 2FA。密钥保存在服务器认证卷,不进入 Git、镜像或 HTTP 二维码接口。 + +服务器管理员在交互式 SSH 终端运行 `sudo /opt/prompt/auth qr` 绑定验证器。不要把二维码、动态密钥或 JWT 粘贴到聊天、日志和仓库。服务端自动化可使用 `node auth-cli.cjs run <脚本>`,在进程内为本机 API 请求附加短期凭据,不输出令牌。 + +更多说明:[登录与 SSH 绑定](docs/dynamic-code-login.md) · [API](docs/api.md) · [数据模型](docs/data-model.md) · [项目结构与部署](docs/monorepo.md)。 diff --git a/apps/studio/src/components/LayoutSelection.tsx b/apps/studio/src/components/LayoutSelection.tsx index c24686b..06b7812 100644 --- a/apps/studio/src/components/LayoutSelection.tsx +++ b/apps/studio/src/components/LayoutSelection.tsx @@ -9,5 +9,5 @@ export function LayoutSelection(){ async function download(format:'svg'|'json'){ try{setStatus('正在生成…');const r=await fetch('/studio/api/export/layout-selection?format='+format,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({layoutId:layout.id,...current})});if(!r.ok)throw Error();const url=URL.createObjectURL(await r.blob()),a=document.createElement('a');a.href=url;a.download=layout.id+'.'+format;a.click();setTimeout(()=>URL.revokeObjectURL(url),500);setStatus('已导出当前模板。')}catch{setStatus('导出失败,请重试。')} } - return
三种排版 · Studio 工具
← waterfall-wiki 使用教程STUDIO / LAYOUT SELECTION
REF-01 / REF-07 / REF-26

三个构图,各有位置。

切换真实原图对应的模板,分别试文字与配色。图位与装饰按教程构件重建为干净矢量;当前只放占位内容。