# 照片管线与管理界面 · 四路线实测论证报告 > 侦察员 @agent-4f37d7 | 2026-10-24 | 博客架构 = Astro SSG + 自建 gitea;照片库存 21 张;需求 = **上传管理界面 + 图库按宽高比自适应** > 所有结论基于 2026-10-24 实抓的官方源码 / OpenAPI spec / GitHub API / README,标注「推断」处为合理推理而非实抓。 --- ## TL;DR | | P1 gitea 原生 + 构建时处理 | P2 自建/现成管理端 | P3 Immich | P4 自托管图床 | |---|---|---|---|---| | 上传体验(手机) | ⭐⭐⭐ 拖拽+多选,手机走系统相册(推断) | ⭐⭐⭐⭐⭐ Sveltia 移动支持(自述)/自写可 PWA | ⭐⭐⭐⭐⭐ App 自动备份(但不是进博客仓) | ⭐⭐⭐ 网页上传 | | EXIF/缩略图 | 构建时 exifr + sharp,宽高比天然可得 | 入库前处理完(自写)/ Sveltia 无 EXIF 提取 | ⭐ API 直出全套 EXIF | 部分自带,非结构化 | | 数据归属/版本控制 | ✅ git 仓 | ✅ git 仓 | ❌ Immich DB | ❌ 图床自有存储 | | 新增运维组件 | **零** | 零(Sveltia 纯前端)或 1 个小服务 | 重型全家桶 | 1 个 PHP 应用 | | 工作量 | 半天(一个 loader 脚本) | 0~3 天 | 1 天(仅当已有 Immich) | 1 天部署 + 长期割裂 | **推荐**:P1 起步(当天可用)→ 管理面先用 Sveltia CMS 试水(Gitea 后端已是一等公民)→ 不合手再自写照片专用小服务(可开源)。P3 仅当本来就部署 Immich;P4 不推荐。 --- ## P1 · gitea 原生上传 + 构建时处理 ### 上传能力(实抓 gitea main 分支源码) - **Web UI 上传页存在且走 dropzone**:`templates/repo/editor/upload.tmpl` 定义 `repo-file-upload` 表单,内嵌 `templates/repo/upload.tmpl` 的 dropzone 容器(`data-accepts` / `data-max-file` / `data-max-size`);`web_src/js/features/repo-editor.ts:64-65` 对 `.repository.editor.upload .dropzone` 调 `initDropzone`。 → **拖拽 ✅、多文件 ✅**(dropzone 标准能力);入口 = 仓库页「Add File → Upload Files」,需选目标目录+写 commit message。 - **默认限制(`modules/setting/repository.go` 约 170-177 行,实抓)**: ```go Upload: struct { Enabled bool; AllowedTypes string; FileMaxSize int64; MaxFiles int }{ Enabled: true, AllowedTypes: "", FileMaxSize: 50, MaxFiles: 5, } ``` → 默认**单次 5 个文件、单文件 50MB、不限类型**;`[repository.upload]` 可调大(照片场景建议 `MAX_FILES` 调到 20+)。 - **手机浏览器**:dropzone 底层是标准 ``(可多选),手机 Safari/Chrome 弹系统相册、支持多选——**推断**(未实机测)。 - **API 上传(实抓 swagger.v1.json,953KB)**: - 单文件:`PUT/POST /repos/{owner}/{repo}/contents/{filepath}`,内容 base64; - **多文件批提交:`POST /repos/{owner}/{repo}/contents`**(summary: *Modify multiple files in a repository*),body = `ChangeFilesOptions{ files: [{operation: create|update|upload|rename|delete, path, content(base64), from_path}], branch, author/committer, dates }`——脚本化上传/搬移/删除全覆盖,且一次 commit 完成多文件。 - 仓库 webhook:官方文档站有 `/usage/repository/webhooks/` 页(目录实抓),可触发构建。 ### 构建时 EXIF + 缩略图(实抓 Astro 官方文档与源码) - **astro:assets 默认 image service = sharp**(官方 images 指南 L670-672),`` 构建时转换尺寸/格式/质量,**自动推断宽高写进 `` 防 CLS**(L211/214)——缩略图多尺寸(`srcset`/responsive layout)**官方原生,零自写**。 - **宽高比能力(关键,正中需求)**:导入图片的 `ImageMetadata = { src, width, height, format, orientation }`(images.mdx L497-503);源码 `packages/astro/src/assets/utils/metadata.ts` 实证:用内置 image-size 探测,**orientation ≥ 5 时自动交换 width/height** → 拍竖片时宽高比正确。masonry 组件构建时直接读 `width/height` 算 `aspect-ratio`,零运行时 JS。 - **能力边界(如实)**:astro:assets **只给尺寸/格式/方向,不吐相机/镜头/光圈等 EXIF**。这一层要 `exifr`(npm v7.1.3,**零依赖**,npm registry 实查)在 content collection loader 或 prebuild 脚本里提,写入数据层(20 行级别代码)。 - **public/ 与未授权远程图不处理**(images.mdx L16/36/49):照片必须放 `src/` 下(或经 `image.domains` 授权)才享受缩略图管线。 - **Astro 生态现成「照片上传→EXIF」集成**:npm 搜 `astro exif` 无官方/主流插件;GitHub 上仅有小模板(如 hant121/memori ★9 静态相册模板)。**没有一等公民现成集成,但缺的部分(exifr 提取)是 20-50 行的事。** ### 评估 - 上传体验:能用但纯 gitea——无缩略图预览、无 caption/alt 编辑、无分组管理;commit message 手写。 - 数据归属:✅ 21 张原图进 git 仓(21 张对仓库体积无压力;数百张后再谈 LFS)。 - 新增运维组件:**零**;构建时间 += sharp 处理 21 张(秒级,可忽略)。 - 工作量:**半天到一天**(photo collection loader + exifr + masonry 组件)。 --- ## P2 · 自建上传/管理端点 ### 现成开源「git-based 媒体管理器」搜索结果(GitHub API 实查) **没有专精「git 照片管线」的主流项目**(搜 `git media manager upload`、`gitea cms` 等只出 teabag ★6——Static CMS 的 gitea 认证 shim,2023 停更;nbg ★1 等)。**但存在重要降维解**: - **Sveltia CMS(sveltia/sveltia-cms,★2990,2026-10-24 仍在推提交)**:自称「Netlify/Decap CMS 后继,feature complete」(README 实抓 + discussions #957)。badge 自述:Fast / Git-based / Headless / Modern UX / **Mobile Support** / i18n。 - **Gitea 后端是一等公民(源码级实锤)**:GitHub contents API 列出 `src/lib/services/backends/git/providers/` 下 **`gitea` `github` `gitlab` 三目录并列**。 - 活跃度佐证:v0.251.0(2026-10-23)release notes 原文 "…approves its pull/merge request on GitHub, GitLab, **Gitea or Forgejo**"。 - 风险如实记录:Gitea 后端相对新——#1031(2026-10-01 才关闭)曾因 "Gitea 28 被误识别为 Forgejo 导致拉文件 404";#721(已关)涉 token 认证。**self-host gitea + Sveltia 的组合需实测后才能定案**(本次未能实机验证;sveltia-cms.app 文档站 SSL 握手失败,抓取未遂,已如实标注)。 - 能覆盖:图片上传(media folder 入 git 同 commit)+ markdown/frontmatter 编辑(caption/分组/alt 可做成字段)→ **「管理界面」需求的开箱即用候选**。不覆盖:EXIF 自动提取、缩略图预览策略定制。 ### 自写小服务成本评估(基于 P1 实抓的 API 面) 管线 = 拖拽上传 → 服务端 exifr 提 EXIF + sharp 出缩略图预览 → 元数据表单(caption/分组)→ 组装 frontmatter + 原图 → gitea `POST /contents` 多文件一次 commit → webhook 触发构建。 - 技术面全部有实抓依据(contents 多文件 API、webhook、exifr、sharp),无未知阻塞; - 工作量:**Node/Go 约 300-600 行,2-3 天到「自用够用」**;真正成本不在上传而在管理面(列表/编辑/删除/分组/精选标记); - 手机:标准 `` + PWA manifest 可达「拍照→直接进仓」; - **自写最大红利:这个工具本身就是开源作品**(用户热爱开源 + 博客本就开源在 gitea 的定调),且可按自己的 21 张照片的真实工作流打磨。 --- ## P3 · Immich 集成 ### API 能力(实抓 immich-openapi-specs.json,973KB,199 路径) - 认证:`x-api-key` header(securitySchemes 实查); - `GET /albums`、`GET /albums/{id}`、`GET /assets/{id}` → `AssetResponseDto` **内含 `exifInfo`**; - `ExifResponseDto` 字段全:make / model / **lensModel** / iso / fNumber / exposureTime / focalLength / dateTimeOriginal / GPS 经纬度 / orientation / city/state/country —— **EXIF 直出,结构化,比自提还全**; - `GET /assets/{id}/thumbnail`(preview 尺寸)+ `/assets/{id}/original` 原图下载; - `POST /search/smart`、`/search/metadata`(按相册/标签/日期筛);`/shared-links` CRUD(公开分享面)。 - 生态:eleventy-immich(★6,2026-10-21 仍活跃)、若干 astro+immich 小项目(★0-1)——**模式真实存在但都小众,无大一统方案**。 ### 代价评估(如实) - **数据不在 git**:精选集 = Immich 里的相册状态,改精选不改博客仓;缓解办法 = 构建时把拉到的元数据 JSON 提交进 content collection(照片本体仍可 gitignore),得到「元数据版本化 + 原图在 Immich」的折中; - 构建时强依赖 Immich 在线(静态站本身不受影响,发布链路受影响); - **新增运维组件最重**:Immich 全家桶(server + ML + Postgres + Redis),为 21 张照片引入纯属杀鸡用牛刀——**除非本来就部署 Immich 管理手机相册**(若已有,P3 成本骤降为 1 天集成); - 公开图库还需管理 API key / shared link 暴露面。 --- ## P4 · 自托管图床(Lychee / Chevereto / EasyImages) ### 实抓现状 | 项目 | 状态(2026-10-24 实查) | 要点 | |---|---|---| | LycheeOrg/Lychee | 活跃,**v7.0**(7.0 docker 镜像有 breaking change,README 升级公告),PHP 8.4/8.5,MIT | 功能完整的相册管理;**与静态博客集成的真实案例:未找到**(如实标注) | | chevereto/chevereto | 活跃(pushed 2026-10-21,★1002,默认分支 4.5,**AGPL-3.0**,README 自述 "pro edition" 商业功能分层) | 老 chevereto-free 已退役(README 挂 Legacy 徽章指向 rodber/chevereto-free);为 21 张照片过重 | | icret/EasyImages2.0 | README 实抓:PHP 5.6-8.0、**无数据库**、GPL-2.0,支持 API/多文件上传/在线管理/水印/压缩/格式转换,定位「游客上传和个人使用」 | 无版本控制;无结构化 EXIF 输出;PHP 上传类工具历史上有上传绕过类漏洞记录(**评估性提醒,未逐一核 CVE**),若用必须隔离部署 | ### 共同问题 - **版本控制缺失**:图片在图床自有存储(文件系统/DB),文章与图的关系无 git 级完整性——图床端误删/误改,博客构建照样成功、线上静默坏图;备份责任独立于博客仓; - 唯一优点 = 构建不依赖图床在线(引用 URL 即可); - 与「数据可控、全自托管、最少外部依赖」的选型轴相悖度最高(多一个有状态服务,且该服务只服务照片这一条内容线)。 --- ## 推荐分阶段路径 **阶段 0(当天可用)— P1 全家桶** 1. 照片进仓:`src/assets/photos/`(或 `content/photos/` collection),gitea web 上传页拖拽(先把 `[repository.upload] MAX_FILES` 调到 20+);API 通路 `POST /contents` 多文件批提交留给脚本; 2. 构建时:content collection loader 用 **exifr** 提 EXIF(相机/镜头/参数/拍摄时间)进数据层;**sharp**(astro:assets 默认)出多尺寸缩略图 + `srcset`; 3. 宽高比自适应图库:masonry 组件从 `ImageMetadata.width/height`(已处理 orientation)算 `aspect-ratio` + grid row-span,零运行时 JS;lightbox 用原生 ``。 → 交付物 = 能用的图库页;新增代码约一个 loader + 一个组件。 **阶段 1(要管理界面时)— 先 Sveltia 后自写** 1. 零成本试 **Sveltia CMS**(纯前端 SPA,无新运维组件,Gitea 后端一等支持):把照片目录配成 media folder、caption/分组配成 frontmatter 字段;**验收点:self-host gitea 登录认证 + 媒体库上传 21 张照片实测**(Gitea 后端 2026-10 初还有兼容 bug 修复记录,需过一遍真机); 2. Sveltia 不合手(比如要「上传即预览缩略图+EXIF 自动回填」的照片专用体验)→ 自写 **photo-pipe** 小服务(上传→EXIF→缩略图预览→元数据表单→`POST /contents` 一次 commit→webhook 构建,PWA 手机可用),**作为独立开源项目发布**——这是「热爱开源」的最自然表达,且有 P1 打底保证任何时刻可退回纯手工流程。 **不推荐**:P4(数据归属最弱、与 git 哲学相悖);P3 仅在「本来就部署/计划部署 Immich 管手机相册」时启用,届时构建时拉精选相册 + EXIF 直出是省力方案。 --- ## 未验证项与残余风险(如实标注) 1. gitea 上传页**手机浏览器实机**未测(理论上标准 file input 可用); 2. **Sveltia × self-hosted gitea** 未实机验证(认证流、媒体库体验、#1031 类兼容问题是否复发);sveltia-cms.app 文档站本次抓取失败(SSL),功能细节以 README/issues/release notes 为据; 3. homelab gitea **当前运行版本**未知——contents API 与 upload 页均为老牌功能(1.x 早期即有),风险低,但 `[repository.upload]` 可调项以实际版本 config cheat sheet 为准(main 分支源码与文档站页面结构本次均核过,cheat sheet 内容段未单独核); 4. Immich 版本迭代快、API breaking change 风险未逐一考证(API 面本身已实抓确认); 5. EasyImages 安全记录未逐一核 CVE,仅有泛化提醒; 6. astro-imagetools 仓库 API 查询未返回(疑改名/迁移),生态备选项未深究——不影响主方案(astro:assets 原生已够)。 ## 抓取物存档 `/tmp/scout-photos/`:gitea(upload.tmpl / repo-editor.ts / dropzone.ts / setting-repository.go / swagger.v1.json)、astro(images.mdx / assets metadata.ts)、immich(openapi specs)、p4(lychee / easyimages / chevereto README)、sveltia(README / releases)。