# 照片管线与管理界面 · 四路线实测论证报告
> 侦察员 @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 用原生 `