Commit Graph

41 Commits

Author SHA1 Message Date
Mavis
02f0260dfc docs(android): 完整方案 + logo 资源 + 启动屏
新增 docs/android/ 目录:
- README.md 总入口(快速上手 + 决策摘要 + 数据流)
- 01-architecture.md 模块划分 + 数据流 + 选型理由
- 02-api-contract.md 每个接口的请求/响应 + DTO 字段映射
- 03-build-run.md Gradle/SDK/网络安全白名单/真机调试
- 04-milestones.md 7 天里程碑 + DoD + E2E 测试场景

新增 assets/:
- logo/: 主图标 master + adaptive icon + 5 DPI launcher (方/圆)
- splash/: 启动屏 logo + 完整背景预览 + 5 DPI 资源
- android_resources/: 集成所需的 XML(adaptive icon/主题/颜色/字符串/drawable/layout)
- INTEGRATION.md 集成指南
- logo.svg + _make_logo.py 设计源

设计风格:参考用户提供的木质方块字母积木图,米色木纹底 +
深棕色字母 D,代表 'Diary',温暖私人日记感。

服务器体检:所有容器/API/DB/翻译主链路正常,TMT 本月已用 0.37%。
MaaS 备用通道上次已验证可用。
2026-06-10 14:11:43 +08:00
Mavis
81c83ced8d feat(feed): 列表展示翻译正文摘要 + 页码分页
首页 Feed.vue 改造:
- 卡片在中文标题下直接展示 body_zh_text(前 220 字)
  用户不进详情就能看到译文正文,提升阅读效率
- 配图(image_ai_url 或 image_url)也直接显示在卡片中
- 把原标题作为副标题(灰色,辅助参考)

分页从 cursor 无限滚动换成 page + page_size:
- 后端 /articles 加 page/page_size 参数,返回 total/total_pages
- 干掉 _encode_cursor/_decode_cursor
- 前端用 n-pagination,显示 1,2,3,4,5 + 快速跳转
- 筛选/搜索变化自动回到第 1 页
- 切页自动滚到顶部
2026-06-10 12:07:04 +08:00
Mavis
3e56fed541 feat(translate): 接入腾讯 MaaS u2 作为 TMT 备用翻译通道
新通道:腾讯 MaaS u2 模型(云知声),OpenAI 兼容协议
- 端点:https://maas-api.hivoice.cn/v1
- 模型:u2(翻译专用,实测 + 锁定 prompt 后译文质量稳定)
- 备用链路:TMT 配额耗尽 / TMT 失败时自动降级到 MaaS

关键 prompt 工程(锁定):
- 必须用 user 提供的固定中文 prompt,否则 u2 会把译文放进 reasoning_content 而 content 返乱码
- 限定只接 EN/JA → ZH
- 中文输入固定返回拒绝文案

新增/改动:
- backend/app/services/translation/tencent_maas.py: 新建
- backend/app/services/translation/service.py: 备用链 maas → local,初始化失败友好降级
- backend/app/config.py: 加 tencent_maas_* 4 个配置
- .env.example: 文档化
2026-06-09 17:33:45 +08:00
Mavis
a5bfb7d49a fix(worker): enrichment_loop 永远只扫老文章(已 enrich),新文章被排到最后
bug 复现:
- order_by translated_at asc nullslast
- 老文章已 enrich,translated_at 有值,排前
- 新文章 translated_at=NULL,nullslast 排最后,limit5 永远拿不到

修复:
- order_by 改为 Article.id.asc()(新文章 id 大)
- ENRICHMENT_BATCH_SIZE 1→3(并发候选)
- 文章间 sleep 0.5→0.2s

效果:enrichment_loop 现在会持续 enrich 新进文章,首页列表会逐步有分类/评论
2026-06-09 17:07:07 +08:00
Mavis
474299baf9 feat(feed): 首页列表展示分类标签 + LLM 评论预览
- 后端 ArticleListItem schema 加 commentary / commentary_status / image_ai_url
- 后端 articles.list 接口把以上字段写入响应
- 前端 API 类型同步
- 前端 Feed.vue 卡片:
  * 分类 tag(逗号分隔,多 tag)
  * 评论预览(蓝色引线块,140 字截断,带状态点)
  * 用户点进详情页前就能看到 LLM 点评钩子
2026-06-09 15:59:48 +08:00
Mavis
76e95908e8 fix(llm): _safe_format 防 ValueError,模板里示例 JSON 也能正常 format
bug: classify_prompt 默认值里含示例 JSON {\\"categories\\": [...]},str.format
     看到花括号就试图解析为 placeholder/format spec,遇到 \\" 时抛:
       ValueError: Invalid format specifier ' [\\"时政\\"]' for object of type 'str'

修复:
- 引入 placeholder_re 提取所有合法 {varname} 占位符,stash 成 sentinel
- 剩余的 { / } 一律 escape 成 {{ / }},str.format 自然还原
- 用户显式写的 {{ / }}(标准转义)单独 stash,不被重复 escape
- 极端情况(KeyError/IndexError/ValueError)兜底:按原文返回,记录 warn

8 个本地单测全过(含示例 JSON 模板 / 老 prompt 缺变量 / 用户显式 {{ 场景)
2026-06-09 15:14:53 +08:00
Mavis
8dbc7c4ab2 feat(web): ArticleDetail 三段式(评论/译文/原文) + LLM 屏蔽词配置 + .diary-para 兜底
- types: Source / LlmSetting 加 blocklist_tags 字段
- AdminLlmSettings:
  - 新增 '全局屏蔽分类(命中即删文章)' 卡片(逗号/换行分隔,双向绑 blocklist_tags)
  - 分类 prompt 提示加 {blocklist} / drop 字段说明
- ArticleDetail 三段式:
  - 顶部:评论(LLM 点评)
  - 中部:文章译文(优先 LLM 排版版 / fallback 原始译文)
  - 底部:文章原文
  - AI 插图挂在译文卡片下作附属
- style.css: .diary-para 兜底规则(margin 0 0 1.5em 0 / line-height 1.7 / color #3e3e3e)
2026-06-09 14:38:29 +08:00
Mavis
728e8c9be3 feat(api): LlmSetting/Source 暴露 blocklist_tags,admin 编辑入口就绪
- schemas/source.SourceOut/In/Update 加 blocklist_tags
- admin.create_source 透传 blocklist_tags
- admin.update_source 走 setattr 通用,自动支持新字段
- admin_llm.get_settings 在 row=None 分支返回默认值时补 blocklist_tags=[]
- update_settings 走 setattr 通用,自动支持新字段
2026-06-09 14:35:54 +08:00
Mavis
da895c2c5f feat(llm): classify 前置 + 黑名单 drop 删文章 + 排版用 .diary-para
- enrichment._enrich_classify 前置,返回 (drop, categories)
  - 注入 {blocklist} 占位符到 prompt(全局 + per-source 合并)
  - drop=True → 整篇 DELETE,后 3 步直接 skip
  - 兜底:即使 LLM 没正确返回 drop 字段,本地也匹配一次
- enrichment._enrich_format 排版段落 class 名固定为 diary-para
  - CSS 仍内联到 style,前端 .diary-para 兜底
- enrichment._merge_blocklist: 全局 + per-source 合并去重保序
- schemas/llm.LlmSettingOut/Update 暴露 blocklist_tags
- DEFAULT_PROMPTS.classify_prompt 加 {blocklist} + drop 字段说明
2026-06-09 14:34:18 +08:00
Mavis
d0d1014505 feat(db): 0003 migration + LlmSetting/Source 模型加 blocklist_tags
- 新增 alembic 0003: sources.blocklist_tags + llm_settings.blocklist_tags(JSONB)
- 两层配置:全局(llm_settings) + per-source(sources),合并去重后注入 classify prompt
- 默认空数组,不影响存量数据;admin API 在下个 commit 暴露编辑入口
2026-06-09 14:30:38 +08:00
Mavis
6da59da934 chore(docker): 所有容器加 logging rotation(max-size 10m, max-file 3, 总 30MB/容器) 2026-06-09 10:52:51 +08:00
Mavis
8d73f4fb28 fix(llm+worker+deploy): 兼容老 prompt 模板 + 消除 startup_run 日志噪音
- enrichment: 新增 _safe_format (基于 _SafeDict),缺失占位符保留原样不抛 KeyError。
  _enrich_format / _enrich_classify / _enrich_image / _enrich_commentary
  全部走 _safe_format,数据库里老 prompt(不支持 {body})不再让整条 article 卡住。
  复现: 388183 classify 一直 KeyError,enrichment_loop 反复重试它,316 篇全卡在 n/a。
- workers.__main__: startup_run 从 IntervalTrigger(minutes=0) 改成 DateTrigger
  (只跑一次),消除 'maximum number of running instances reached' 刷屏 WARNING。
- deploy_pull: 改 _connect 自动识别 RSA / Ed25519 / ECDSA key(原硬编码 Ed25519Key)
2026-06-08 21:20:43 +08:00
Mavis
380e8b124e feat(llm): 排版容器固定CSS + 插图用正文第一段 + 适中尺寸
- enrichment._enrich_format:把排版好的段落包到带固定 CSS 的 <div class=article-body> 里
  (font: system-ui / 17px / line-height 1.7 / color #3e3e3e / p margin-bottom 1.5em)
  CSS 同时内联到 style 属性,前端 .article-body 全局类做兑底
- enrichment._enrich_image:prompt 改用 body_zh_text 的第一段(原为 title);
  新增 {body} 占位符,image_prompt_template 默认模板同步改写
- 插图尺寸写死为 768x512(适中);image_size 字段保留供用户手改但默认行为不依赖它
- 分类明确多标签(2-5 个),提示词加 {body} 变量,容错读 categories/tags 两种 key
- AdminLlmSettings.vue:placeholder / 变量说明同步更新
2026-06-08 20:53:21 +08:00
Mavis
82a92032bb fix(scripts): backfill_body 也回填翻译过但 body 短的文章(让 worker 重译) 2026-06-08 16:09:22 +08:00
Mavis
4cc5d41e39 fix(orm): Article 模型加 body_zh_formatted/image_ai_url/4 个 *_status 字段(同步 0002 migration) 2026-06-08 16:08:39 +08:00
Mavis
35c0da1670 feat(scripts): 新增 backfill_body.py 回填 body 短的文章(重新抓全文) 2026-06-08 15:59:14 +08:00
Mavis
a5548d6e64 fix(fetcher): fulltext 抓取用真实浏览器 UA,绕过 NHK 等 403 2026-06-08 15:55:30 +08:00
Mavis
6b5828c1c0 fix(translation): 规范化 BCP-47 lang_src(避免 en-gb/zh-cn 等被 TMT 拒) 2026-06-08 15:49:03 +08:00
Mavis
e83d7f4285 docs: 重写 README,详细覆盖 LLM 增强 / 数据模型 / 开发部署工作流 / 运维工具 2026-06-08 14:54:56 +08:00
Mavis
8d2c0855ac feat(scripts): 新增 deploy_pull.py 远程服务器拉取/回滚工具(免密) 2026-06-08 14:44:09 +08:00
Mavis
38609ff36f feat(ui): 新增 LLM 智能增强设置页 + 路由/侧栏 + ArticleDetail 展示排版/分类/插图/点评 2026-06-08 14:24:25 +08:00
Mavis
ba2298da0a chore: 集成 LLM 增强 — config/main/articles schema/workers + .env.example 加 Agnes 配置 2026-06-08 14:24:23 +08:00
Mavis
ffd667f0dc feat(llm): 新增 LLM 智能增强服务(Agnes client + 4 项 enrichment 任务 + admin API + migration) 2026-06-08 14:24:00 +08:00
Mavis
40be1e6861 feat(scripts): 新增 server_init.py 远程服务器初始化工具(推公钥+7项运维,幂等可重跑) 2026-06-08 11:13:50 +08:00
Mavis
97c370c58c refactor(scripts): push_ssh_key 改用 SSH fingerprint 去重,ed25519 优先 2026-06-08 11:13:49 +08:00
Mavis
9328f02a1f chore(scripts): 忽略并撤追踪 scripts/_*.py 临时调试脚本 2026-06-08 11:13:31 +08:00
Mavis
523c82f7a5 fix: NHK 源配置改 ja(seed 写错了); translate_article 加强 lang_src 兜底 2026-06-08 00:54:02 +08:00
Mavis
639562593e fix: 翻译失败/降级文本不再写 cache(避免 30 天污染)
之前 service.translate 写 cache 无条件,导致:
- 第一次翻译失败时,'[翻译失败: ...]' 占位符被写进 cache
- 30 天内相同文本的请求(新文章 title 与老文章 title 相同时)全部返回占位符
- 触发 200+ 文章 title_zh 字段被永久污染

修法:仅在 engine ∈ {tencent, nllb, cache} 且文本不含错误标记时,才写 cache。
2026-06-08 00:48:36 +08:00
Mavis
9862a92423 perf: 翻译独立后台循环(1 篇/秒)+ Semaphore 1
之前 fetch_one_source 入库后立即调翻译(可能并发触发腾讯 TMT 限速)
改为独立 translation_loop 后台循环:
- 完全不和 RSS 抓取并行
- 1 篇/秒节拍(Semaphore 1 + sleep 1.0)
- 没活时空闲 5 秒再轮询
- pending/failed 都重试
2026-06-08 00:27:09 +08:00
Mavis
e79cfaa5f7 fix: articles.py get_article 链式 await coroutine 报错(.first()) 2026-06-08 00:19:03 +08:00
Mavis
cc02d39d29 fix: 翻译主流程失败时 raise(不再返回占位符); add_usage TTL 用 replace(day=1) 防 0 TTL 2026-06-07 23:58:13 +08:00
Mavis
501713a3e8 fix: deps.py 修 await chain (3 处 .scalars()) 2026-06-07 23:38:04 +08:00
Mavis
3ebf280278 fix: pipeline INSERT 去掉不存在的 translate_to 字段 2026-06-07 23:32:13 +08:00
Mavis
30acd6af54 fix: create_user 默认 role=owner(而非 member) 2026-06-07 23:27:52 +08:00
Mavis
ce903ac58e fix: 修剩余的 (await ...)).scalar_one_or_none() 链式 + bookmark 2026-06-07 23:25:53 +08:00
Mavis
5109d6f824 fix: API 全部改用显式两步走 await session.execute + result.scalars()
之前 (await ...).scalars() 链式在 SQLAlchemy 2.0 async 下报
'coroutine' has no attribute 'scalars' 错误。改为先 await 拿 result
再 .scalars(),这是 SQLAlchemy 2.0 推荐的 async 写法。
2026-06-07 23:22:56 +08:00
Mavis
2e75985a3c fix: healthz 路径改成 /api/v1/healthz(归到 API 前缀下) 2026-06-07 23:15:33 +08:00
Mavis
eaa4aa6604 fix: Caddy 用 handle_path 自动 strip /api 前缀
之前的 reverse_proxy /api/* 直接转发保留路径,导致 api 收到 /api/v1/articles
而实际路由是 /v1/articles(API_PREFIX)。改用 handle_path。
2026-06-07 23:13:51 +08:00
Mavis
6635b8fea8 fix: enum 写入 PG 用 value 而非 name
Source/SourceKind、UserRole、SubscriptionMatch 三个 enum 改用 values_callable
保证 PG 看到 'rss' / 'owner' / 'any' 等小写 value,而非 'RSS' 大写 name。
seed_sources 同步改为显式字符串。
2026-06-07 23:11:32 +08:00
Mavis
427e1f5cf2 fix: 前端类型修复(@types/node + vite-env.d.ts + ufw SSHD_PORT)
- frontend: 加 @types/node / vite/client 类型声明
- frontend: tsconfig 加 types: [node, vite/client]
- scripts: deploy_remote.sh 用 sg docker + dc() 函数避免引号问题
- scripts: deploy_remote.sh ufw 改用 \ 变量
2026-06-07 23:04:06 +08:00
Mavis
60b062daf2 feat: initial MVP - FastAPI backend + Vue3 frontend + docker-compose
- backend: FastAPI + SQLAlchemy 2.0(async) + asyncpg + Alembic
- 7 API routes: auth/me/articles/sources/bookmarks/subscriptions/admin
- models: User/Source/Article/Bookmark/Subscription/ApiToken
- services: RSS fetcher (feedparser) + Tencent TMT translator with quota + cache + local NLLB fallback
- workers: APScheduler + asyncio pipeline (fetch -> dedupe -> insert -> translate)
- seed scripts: create_user, seed_sources (5 RSS: Reuters/BBC/Al Jazeera/NHK/DW)
- frontend: Vue 3 + Vite + Naive UI + Pinia + vue-router
- pages: Login, Feed (24h), ArticleDetail, Sources, Bookmarks, AdminSources
- deploy: docker-compose (postgres/redis/api/worker/frontend/caddy)
- docs: README, DEPLOY, architecture, acceptance
2026-06-07 21:51:01 +08:00