- 后端
- 前端
- 图像处理
- 人工智能
- AI 应用
【免费下载链接】photoprism
AI-Powered Photos App 🌈💎✨
PhotoPrism 是一个基于 Go 与 Vue 构建的 AI 照片管理应用,其仓库体量庞大(internal/、pkg/、cmd/、frontend/下分布着数百个包与数千个文件)。为了让贡献者、Agent 与调试者快速定位代码、理解模块之间的协作关系,仓库在根目录维护了 CODEMAP.md(后端部分,另有 frontend/CODEMAP.md 覆盖前端)。本文以该文档为骨架,结合仓库源码与 Makefile,系统梳理 PhotoPrism 后端的入口、包地图、配置体系、API 组织、安全热区与常见开发任务,帮助你"不用地毯式搜索"就能增改功能、修复缺陷并编写测试。
快速开始:两种开发环境的启动路径
CODEMAP 明确给出了两条开发路径——推荐在 dev container 内工作,宿主机则负责管理 Docker。
容器内开发(推荐):
make dep # 安装 Go / JS 依赖 make build-go # 构建后端二进制 make lint-go # 运行 golangci-lint(使用 .golangci.yml,只输出问题不失败) make lint # 运行全部 lint 与仓库检查 ./photoprism start # 启动服务启动后访问http://localhost:2342/,或在使用 Traefik 时访问https://app.localssl.dev/(本地 TLS 由 Traefik 提供,见 AGENTS.md)。
宿主机开发(管理 Docker):
make docker-build # 构建镜像 docker compose up -d # 启动服务 docker compose logs -f --tail=100 photoprism从 Makefile 可以看到,dep实际是dep-models dep-js,build-go走build-develop,test-go通过scripts/test/time.sh包裹执行(Makefile)。这也是 CODEMAP 反复强调的准则:Makefile targets 是构建、格式化与测试的权威入口。
可执行文件与入口点
CLI 应用
二进制名统一为photoprism,主入口在 cmd/photoprism/photoprism.go。该文件用 urfave/cli v2 组装应用:
app.Flags = config.Flags.Cli() app.Commands = commands.PhotoPrism命令注册表是internal/commands/commands.go中的commands.PhotoPrism数组(commands.go),其中包含Start、Index、Import、Migrate、Backup、Users、Clients、Cluster、Auth、MCP、Show等 30+ 个子命令。internal/commands/catalog子包则提供 DTO 与构建器,用于枚举命令/标志并渲染 Markdown 形式的 CLI 文档(最终由photoprism show commands输出)。
CLI 还封装了完整的交互确认机制:ConfirmAction会在破坏性操作前弹出 yes/no 提示,支持通过PHOTOPRISM_CLI=noninteractive环境变量或--yes跳过(见 commands.go);ExitCode统一将"取消操作/中断提示"映射为退出码 0、缺少必需标志映射为 2(commands.go)。
Web 服务器
- 启动链路:
internal/commands/start.go→server.Start(同时拉起 HTTP(S)、后台 workers 与 session 清理)。 - HTTP 服务器:internal/server/start.go 负责压缩、安全中间件、healthz/readiness、TLS/AutoTLS(
golang.org/x/crypto/acme/autocert)与 Unix socket。 - 路由注册:internal/server/routes.go 统一注册 UI、WebDAV、分享(
/s开头)、.well-known发现路由,以及全部/api/v1/*分组。 - API 分组:
APIv1 = router.Group(conf.BaseUri("/api/v1"), Api(conf)),该分组变量定义于 internal/server/routes.go,在 internal/server/start.go 中创建。
Go 包地图:各司其职的分层
CODEMAP 提供了一张高层包地图,是理解整个后端的关键:
| 包 | 职责 |
|---|---|
internal/api | Gin 处理器与 Swagger 注解,只做胶水,不含业务逻辑 |
internal/commands | CLI 命令定义与编排(start、index、import、migrate等),catalog子包输出 CLI 文档 |
internal/server | HTTP 服务器、中间件、路由、静态资源/UI/WebDAV |
internal/config | 配置、flags/env/options、客户端配置、DB 初始化与迁移 |
internal/entity | GORM v1 模型、查询、搜索辅助与迁移 |
internal/photoprism | 核心领域逻辑(索引、导入、人脸、缩略图、清理) |
internal/ai/vision | 多引擎计算机视觉管线(模型、适配器、schema),适配器文档见 internal/ai/vision/openai/README.md 与 internal/ai/vision/ollama/README.md |
internal/ai/onnx | 共享 ONNX 模型描述与会话构建:工件身份/校验和、图检查、预处理契约、运行时加载、执行提供者选择,当前由internal/ai/face消费,见 internal/ai/onnx/README.md |
internal/ai/face | 人脸检测与特征嵌入:由FACE_DETECTOR选择的检测器注册表、由FACE_MODEL选择的嵌入模型注册表、关键点对齐与距离阈值,见 internal/ai/face/README.md |
internal/workers | 后台调度器(index、vision、sync、meta、backup) |
internal/auth | ACL、会话、OIDC |
internal/service | 集群/门户、地图、hub、webdav |
internal/event | 日志、pub/sub、审计;规范化的结果令牌位于pkg/log/status(用status.Error(err)这类辅助函数把净化后的消息作为 outcome),文档见 internal/event/README.md |
internal/ffmpeg、internal/thumb、internal/meta、internal/form、internal/mutex | 媒体、缩略图、元数据、表单、协调 |
pkg/* | 可复用工具,严禁 importinternal/*(例如pkg/clean、pkg/enum、pkg/fs、pkg/txt、pkg/http/header、pkg/authn/authtoken) |
几个值得注意的实现细节:
internal/entity中的标签查找辅助函数集中在internal/entity/label*.go,应复用FindLabels(...)、FindLabelIDs(...)与LabelSlugs(...)做同音词感知的精确名称/slug 解析,而不是在调用方重复手写 slug SQL。- WebDAV 客户端(internal/service/webdav/README.md)的递归目录发现优先使用
PROPFIND Depth: infinity,对不兼容服务器回退为迭代式Depth: 1遍历;隐藏点文件与隐藏点目录内的条目会被排除(它们通常是锁文件、半截上传或服务商元数据);控制类操作(Files、Directories、Mkdir、Delete)受服务超时约束,而Upload/Download刻意避免总请求 deadline,改用连接级保护。
模板与静态资源
- 入口 HTML 位于 assets/templates/index.gohtml,其中包含
app.gohtml的 splash 标记与app.js.gohtml的 SPA 加载器。 - OIDC 登录完成经由 assets/templates/auth.gohtml 桥接:它会清除 legacy/namespaced 会话键,并把会话写入
frontend/src/page/auth/login.vue登录 UI 开关所选择的浏览器存储。 - 浏览器能力检查逻辑在 assets/static/js/browser-check.js,经
app.js.gohtml引入,在主 bundle 运行前检查 Promise、fetch、AbortController、script.noModule等能力。脚本标签顺序不能改动,确保浏览器检查先于主 bundle 执行。 - assets/templates/splash.gohtml 渲染 bundle 加载期间的加载屏文字,样式在 frontend/src/css/splash.css。调整浏览器支持提示时,需要同步更新 loader partial 与 splash 样式,保证跨版本的警告文案一致。
- Service worker 路由位于
internal/server/routes_webapp.go,sw.js、sw-scope-cleanup.js与 Workbox 运行时文件(/workbox-:hash)的处理器都定义在那里,使 service worker 在站点根路径与 base URI 下都能运行;注意 Gin 的:hash参数不含.js后缀,handler/test 需要手动匹配完整文件名。
HTTP API:组织、注解与生成
- Handler 全部位于
internal/api/*.go,注册于internal/server/routes.go。 - 新端点必须在 handler 文件中加 Swagger 注解,然后用
make fmt-go swag-fmt && make swag生成文档;不要手工编辑internal/api/swagger.json。 - Swagger 注意事项:
- 每个
@Router注解都要写完整的/api/v1/...前缀(与分组前缀一致); - 只注解公开 handler,跳过内部辅助函数以免生成杂散通用路径;
make swag-json会运行swaggerfix稳定化步骤,去重time.Duration的重复枚举——API 中时长一律使用整型纳秒。
- 每个
/api/v1/metrics(internal/api/metrics.go)暴露 Prometheus 指标,包括来自config.Usage()的缓存文件系统/账户用量、注册用户/访客总数,以及NodeRole=portal时的门户集群节点数;响应使用标准 Prometheus 文本格式(text/plain; version=0.0.4)。routes.go中的常见分组:sessions、OAuth/OIDC、config、users、services、thumbnails、video、downloads/zip、index/import、photos/files/labels/subjects/faces、batch ops、cluster,以及技术类(metrics、status、echo)。- 隐藏搜索行为(供配置的前端 URI 下的隐藏路由使用,CE/Plus/Pro 默认
/library/hidden、Portal 默认/portal/hidden)实现在internal/entity/search/photos.go:frm.Hidden强制photos.photo_quality = -1且photos.deleted_at IS NULL;非隐藏搜索默认排除出错文件(files.file_error = ''),除非显式设置frm.Error。搜索 DTO(internal/entity/search/photos_results.go)暴露FileError(files.file_error),客户端无需先加载完整文件详情即可渲染隐藏原因。
配置与标志:三层来源与优先级
配置系统是 PhotoPrism 最常被触碰的部分,核心结构如下:
- Options 结构体:internal/config/options.go 中每个字段同时带有
yaml:"…"(用于defaults.yml/options.yml)、json:"…"(用于客户端/API)与flag:"…"(用于 CLI 标志/环境变量)三类标签。关键约定:- 密钥/内部字段用
json:"-"禁止 JSON 序列化,防止值经 API 泄露(见 internal/api/config_options.go); - 必要时
yaml:"-"禁用 YAML 处理;flag:"-"阻止ApplyCliContext()为字段赋 CLI 值(不影响 internal/config/flags.go 中的标志定义); - 注解可携带版本标签如
tags:"plus,pro"控制可见性(逻辑见internal/config/options_report.go)。
- 密钥/内部字段用
- 全局标志/环境变量:internal/config/flags.go 中的
EnvVars(...);可用清单由internal/config/cli_flags_report.go+internal/config/report_sections.go汇总,经photoprism show config-options --md/--json输出;YAML 选项映射经internal/config/options_report.go输出为photoprism show config-yaml --md/--json;当前值报告在internal/config/report.go,经photoprism show config(别名photoprism config --md)输出;命令目录在internal/commands/show_commands.go,经photoprism show commands输出(默认 Markdown,--json可选,--nested输出树形,--all含隐藏命令/标志)。 - 优先级:
defaults.yml< CLI/env <options.yml(全局 options 规则,即高优先级覆盖低优先级)。 - 配置自有的持久化辅助:
Config.SaveOptionsPatch(...)(internal/config/config.go)负责通用options.yml合并/写入/重载;Config.SaveClusterOptionsUpdate(...)(internal/config/config_cluster.go)负责集群元数据更新(ClusterUUID、NodeUUID、NodeClientID、DB 字段等)。 - Getter 按主题分组:DB 在
internal/config/config_db.go、服务器在config_server.go、TLS 在config_tls.go等。
Client Config(只读)
- 端点:
GET /api/v1/config(internal/api/api_client_config.go)。 - CDN 行为:携带 CDN 头的请求一律返回
404,防止公开与会话专属配置负载之间的中间缓存串扰。 - 组装:由
internal/config/client_config.go构建(不是 Options 的直接序列化),叠加通过config.Register在internal/config/extensions.go注册的扩展值。 - 更新:后端在变更后调用
UpdateClientConfig(),经 websocket 发布"config.updated"事件(见internal/api/config_options.go与internal/api/config_settings.go)。 - ACL/模式感知:值按用户/会话过滤,公开用户与认证用户的返回可能不同;不要暴露密钥——把它视为客户端可见数据,新增字段应通过
config.Register扩展而非直接暴露 Options。 - 刷新节奏:Web UI(非移动端)每 10 分钟通过
frontend/src/app.js中的$config.update()轮询,作为 websocket 推送的补充。
OIDC Groups(Pro 专属)
- 配置项(
pro标签,CE 中隐藏标志):oidc-group-claim(默认groups)、oidc-group(必需的成员列表)、oidc-group-role(GROUP=ROLE映射)。 - 解析辅助:
internal/auth/oidc/groups.go规范化 ID、检测 Entra_claim_names溢出、映射 group→role,并在internal/api/oidc_redirect.go中强制执行必需成员关系。 - 溢出场景:如果存在
_claim_names.groups但未返回任何 groups,且配置了必需 group,则登录失败(Graph 拉取尚未实现)。
数据库与迁移
- 驱动:GORM v1(
github.com/jinzhu/gorm),没有WithContext;裸 SQL 用db.Raw(stmt).Scan(&nop)。 - 实体与辅助:
internal/entity/*.go及子包(query、search、sortby)。 - 迁移引擎:
internal/entity/migrate/*,通过config.MigrateDb()执行;CLI 为photoprism migrate/photoprism migrations。 - DB 初始化/迁移流程:
internal/config/config_db.go选择驱动/DSN、设置gorm:table_options,然后entity.InitDb(migrate.Opt(...))。
认证授权与会话
- 会话模型与缓存:
internal/entity/auth_session*与internal/auth/session/*(含清理 worker);internal/entity/auth_session_jwt.go从门户签发的 JWT 构建瞬时会话,供 internal/api/api_auth_jwt.go 在节点认证门户请求时使用。 - ACL:internal/auth/acl/* 提供角色、授权、作用域;务必使用常量,避免记录密钥,令牌比较要常数时间;作用域检查用
acl.ScopePermits/ScopeAttrPermits,不要自己写解析逻辑。 - OIDC:
internal/auth/oidc/*。 - URL 令牌(签名下载、预览):
pkg/authn/authtoken是无依赖的底层原语,签发/校验 bunny.net 兼容的 HMAC-SHA256 令牌格式(文档见 pkg/authn/authtoken/README.md);internal/auth/tokens是应用层装配——每种用途一个Signer(目前是Download,预览将跟进)、投递策略(DownloadToken/SignDownload/VerifyDownload/IsCoarseDownload)以及Derive(用于尚未签名的预览令牌)。完整细节(含投递规则与测试注意点)见 internal/auth/tokens/README.md。下载令牌是无状态的——不存储每个会话/用户的数据。请求侧解析在 internal/api/auth_tokens.go:AuthDownload(c)是端点使用的合并门禁(session-or-coarse 授权 + 单次调用内解析会话,集中审计拒绝,类似AuthAny),InvalidDownloadToken是薄封装,DownloadSession把签名的?t=令牌解析为会话——在令牌之前还接受请求头中的 Portal 集群 JWT(authAnyJWT要求acl.AccessAll文件权限,因此只有可信的全权限主体才合格)。消费方:DownloadAlbum(internal/api/download_album.go)、GetDownload(internal/api/download.go)、GetPhotoDownload(internal/api/photos.go)、ZipDownload(internal/api/zip.go)。
媒体处理
- 缩略图:
internal/thumb/*与internal/photoprism/mediafile_thumbs.go中的辅助函数。 - 元数据:
internal/meta/*;FFmpeg 集成:internal/ffmpeg/*(文档见 internal/ffmpeg/README.md、internal/meta/README.md)。 - 360° 原始文件(Insta360
.insp/.insv与.lrv代理、鱼眼 DNG):在pkg/fs/file_types.go与pkg/media/insta360.go中识别,投影词汇在pkg/media/projection;检测与拍摄分组逻辑在internal/photoprism/mediafile_insta360.go/mediafile_projection.go,fs.StackPrefix(pkg/fs/stack.go)给一次拍摄的所有文件统一的_00stack 名;internal/ffmpeg/v360.go构建去畸变命令,由convert_image*.go与convert_video_avc.go执行,始终写入衍生文件、绝不触碰原始文件。只有等距柱状(equirectangular)衍生文件才上报给查看器(internal/entity/search/photos_results.go中的sphereProjection),fisheye:则定位其背后的原始文件。 - HEIF 工具链:发行二进制位于
scripts/dist/install-libheif.sh;发布前用make build-libheif-*(封装scripts/dist/build-libheif.sh)为每个支持的发行版/架构重新生成归档。 - 文件夹相册一致性:
internal/entity/folder.go让FindFolder(...)保持 unscoped,以处理创建/索引冲突——软删除行不会导致反复 insert/fail/not-found 循环;internal/photoprism/index.go只在强制重扫时、文件遍历之后运行entity.ReconcileOriginalsFolderAlbums(...),让常规索引保持轻量,而完整重扫修复过期/缺失的文件夹相册。
后台 Worker
调度器与 worker 在internal/workers/*.go(index、vision、meta、sync、backup、share),由internal/commands/start.go启动;自动索引器在internal/workers/auto/*。
集群 / 门户(Cluster / Portal)
- 节点类型:
internal/service/cluster/const.go(cluster.RoleInstance、cluster.RolePortal、cluster.RoleService)。 - 节点引导与注册:
internal/service/cluster/node/*(经 HTTP 与 Portal 通信,不 import Portal 内部实现)。注册现在会在 401/403 时用 join token 轮换节点客户端密钥并重试一次,持久化新凭据(若 secrets 目录只读则回退到内存存储);主题同步会在刷新/轮换发生时显式记日志,便于运维在标准日志级别追踪凭据变动。 - 注册表/供应器:
internal/service/cluster/registry/*、internal/service/cluster/provisioner/*。 - 主题端点(服务端):
GET /api/v1/cluster/theme;客户端/CLI 仅在主题缺失或没有app.js时安装。 - Portal 专属扩展:
portal/internal/portal(Portal 默认值、标志、供应选项、/i/*代理路由)。 - 集群注册表速查:UUID 优先贯穿所有 API 路径(
{uuid})与注册表Get/Delete/RotateSecret,OAuth 用显式FindByClientID;供应器命名使用 HMAC(base32(ClusterUUID+NodeUUID))而非 slug:数据库cluster_d<hmac11>、用户名cluster_u<hmac11>,驱动目前为mysql|mariadb;DSN 构建用BuildDSN(driver, host, port, user, pass, name),不支持驱动时告警并回退 MySQL 格式;公共 API 与内部注册表 DTO 使用规范化字段名(Database含Name/User/Driver/RotatedAt,节点级轮换时间戳为RotatedAt);photoprism cluster register支持--site-url与--advertise-url(两者总是转发给 Portal);自动 MariaDB 凭据轮换逻辑在config.ShouldAutoRotateDatabase(),CLI 与节点引导共用。
服务器启动流程(正常路径)
photoprism start(CLI)→internal/commands/start.go;- 配置初始化、DB 初始化/迁移、会话清理 worker;
internal/server/start.go构建 Gin 引擎、中间件、API 分组、模板;internal/server/routes.go注册 UI、WebDAV、分享、well-known 与全部/api/v1/*路由;- Workers 与自动索引启动,
/livez、/readyz健康端点可用。
/livez(及/health、/healthz)始终返回 200 OK;/readyz在conf.IsReady()为真时返回 200,否则返回 503(见 internal/server/start.go)。
常见开发任务实战
添加一个 CLI 命令
- 在
internal/commands/<name>.go创建*cli.Command; - 把它加入
internal/commands/commands.go的PhotoPrism数组; - 测试优先使用
internal/commands/commands_test.go中的RunWithTestContext,避免os.Exit打断测试进程。
添加一个 REST 端点
- 在
internal/api/<area>.go创建带 Swagger 注解的 handler; - 在
internal/server/routes.go注册; - 复用辅助:
api.ClientIP(c)、header.BearerToken(c)、Abort*系列函数; - 列表端点校验分页边界(默认
count=100,最大1000,offset>=0); - 运行
make fmt-go swag-fmt && make swag保持文档准确; - 测试:
go test ./internal/api -run <Name>,借助NewApiTest()、PerformRequest*聚焦辅助。
添加一个配置选项
- 在
internal/config/options.go加带标签的字段; - 在
internal/config/flags.go通过EnvVars(...)注册 CLI 标志/环境变量; - 暴露 getter(例如在
config_server.go或对应主题文件); - 按
options.go中的同位顺序,把该选项追加到*config.Report()的rows; - 若值需持久化,写回
options.yml并重载到内存——优先用Config.SaveOptionsPatch(...)及相关配置自有辅助,不要临时写 YAML 逻辑; - 需要 defaults/options/settings 文件路径时调用
pkg/fs.ConfigFilePath,保证.yml与.yaml可互换; - 测试覆盖 CLI/env/file 优先级(见
internal/config/test.go辅助)。
触碰 DB schema
- 使用 GORM 自动迁移,或在
internal/entity/migrate/<dialect>/...添加自定义迁移,然后运行go generate或make generate(对全部包执行go generate); - 通过
config_db.go审查/提升migrate.Version的版本门控; - 测试默认跑 SQLite;MySQL 场景需恰当门控。
测试:全量、聚焦与技巧
- 全量套件:
make test(前端 + 后端);仅后端:make test-go;make test-short跳过在 fixture 媒体上跑索引器/导入器的测试。 - 聚焦包:
go test ./internal/<pkg> -run <Name>。 - CLI 测试:设
PHOTOPRISM_CLI=noninteractive或传--yes避免提示;用RunWithTestContext防止os.Exit。 - 测试中的 SQLite DSN 每套件独立(非空),若捕获了 DSN 要清理文件。
- 前端单测用 Vitest,独立于后端,见 frontend/CODEMAP.md。
- 配置辅助会自动禁用测试中的 Hub 服务调用(
hub.ApplyTestConfig());测试配置自动发现仓库assets/目录,除非布局特殊,否则不要为各包添加PHOTOPRISM_ASSETS_PATHshim。 - 快速测试配方:
go test ./pkg/fs -run 'Copy|Move|Unzip' -count=1(文件系统+归档)、go test ./pkg/media/... -count=1(媒体辅助)、go test ./internal/thumb/... -count=1(libvips 缩略图)、go test ./internal/ffmpeg -run 'Remux|Transcode|Extract' -count=1(FFmpeg 命令构建)。
安全热区:加固要点速查
- Zip 解压(路径穿越防护):pkg/fs/zip.go 用
safeJoin拒绝绝对/卷路径与..穿越,并强制单文件与总量上限;测试pkg/fs/zip_test.go覆盖 abs/volume/..与限额。 - 强感知 Copy/Move 与防截断写入:应用辅助在
internal/photoprism/mediafile.go(MediaFile.Copy/Move带force);工具在pkg/fs/copy_move.go(fs.Copy/fs.Move,用O_TRUNC避免尾部残留字节)。 - FFmpeg 命令构建与编码器:核心在
internal/ffmpeg/transcode_cmd.go、remux.go、v360.go;编码器(仅字符串构建)在internal/ffmpeg/{apple,intel,nvidia,vaapi,v4l}/avc.go;测试用PHOTOPRISM_FFMPEG_ENCODER门控硬件运行,否则只断言命令字符串与负向路径。 - libvips 缩略图:管线在
internal/thumb/vips.go(Vips渲染入口、导出参数)、初始化vips_init.go(VipsInit)、旋转vips_rotate.go(VipsRotate)、格式转换vips_convert.go(vipsConvert,HEIC/AVIF 经 libheif);尺寸与命名在internal/thumb/sizes.go(MaxSize、MaxRenderSize、InvalidSize)、size.go(Uncached、ExceedsLimit、Clamp、Limit)、fit.go(FitSizes、FitBounds)、names.go、filter.go;人脸/标记裁剪辅助在internal/thumb/crop(如ParseThumb、IsCroppedThumb);端点:internal/api/thumbnails.go(GetThumb)、albums_cover.go(AlbumCover、共享coverSize)、labels_cover.go(LabelCover)、folders_cover.go(FolderCover),响应与封面缓存在internal/api/cache.go。 - 安全 HTTP 下载器:共享工具
pkg/http/safe(Download、Options):scheme 白名单(http/https)、DNS 前 + 每次重定向的 hostname/IP 校验、最终对端 IP 检查、大小与超时强制、临时文件0600+ rename;头像封装internal/thumb/avatar.SafeDownload采用更严格默认(15s、10 MiB、AllowPrivate=false、面向图片的Accept);测试:go test ./pkg/http/safe -count=1(含重定向 SSRF 用例)、go test ./internal/thumb/avatar -count=1。 - 凭据流的 CDN 防护:认证/会话与 OAuth/OIDC 端点拒绝 CDN 标记请求;集群引导端点
POST /api/v1/cluster/nodes/register同样拒绝 CDN 标记请求,避免缓存可能含引导密钥的响应。
通用约定与规则
- 包边界:
pkg/*不得 importinternal/*(Go 工具链也会阻止internal/被/tmp等目录 import,临时辅助代码请放internal/tmp/之类的路径)。 - HTTP 头:优先使用
pkg/http/header的常量/辅助,不要写字符串字面量。 - 安全:绝不记录密钥;令牌比较用常数时间。
- 集群:实例/服务引导不要 import Portal 内部实现,一律走 HTTP。
- 测试:偏好小型封闭单测,用
t.TempDir()与PHOTOPRISM_STORAGE_PATH等环境变量隔离文件系统路径。 - 集群节点标识:用 UUID v7(内部存为
NodeUUID,API/CLI 暴露为UUID);OAuth 客户端 ID(NodeClientID,暴露为ClientID)仅用于 OAuth;注册表查询与 CLI 命令接受 UUID、ClientID 或 DNS 标签名(按该优先级)。 - 文件系统权限与 io/fs 别名:创建文件/目录必须使用
pkg/fs权限变量——fs.ModeDir(umask 前 0o777)、fs.ModeFile(0o666)、fs.ModeConfigFile(0o664)、fs.ModeSecretFile(0o600)、fs.ModeBackupFile(0o600)——这些是传给 create 调用的模式,之后由进程 umask 过滤;不要把 stdlibio/fs的 mode 位当权限参数用,import stdlibio/fs时要别名(iofs/gofs)避免与自有包冲突;文件系统路径用filepath.Join,URL 路径用path.Join。 - 命名与测试风格:Go 测试放在源码旁(
<file>_test.go),相关用例用t.Run(...)子测试分组(表驱动更佳),子测试名用 PascalCase。 - 集群 DTO 字段:
Database(而非db)含Name、User、Driver、RotatedAt;注册返回Secrets.ClientSecret,CLI 持久化到配置NodeClientSecret;管理员响应可能含AdvertiseUrl与Database,非管理员响应默认脱敏。
常用 Make Targets 速查
| Target | 作用 |
|---|---|
make help | 常用目标概览(make list列出全部) |
make dep | 在容器内安装 Go/JS 依赖 |
make build-go | 构建后端 |
make test-go | 后端测试(SQLite) |
make test-short | 快速测试(跳过 fixture 媒体上的索引/导入) |
make swag | 生成internal/api/swagger.json |
make fmt-go swag-fmt | 格式化 Go 代码与 Swagger 注解 |
make lint/make lint-go | 全量 / Go 专项 lint(golangci-lint) |
make docker-build | 构建 Docker 镜像 |
下载(CLI)与 yt-dlp 辅助也有专门的地图:命令与核心在internal/commands/download.go与download_impl.go;yt-dlp 封装在internal/photoprism/dl/(options.go的参数接线含FFmpegPostArgs钩子、info.go元数据发现、file.go的--output/--print文件方法、meta.go的CreatedFromInfo回退与RemuxOptionsFromInfo);导入器在internal/photoprism/get/import.go(工作池)与import_options.go(ImportOptionsMove/Copy)。测试提示:go test ./internal/photoprism/dl -run 'Options|Created|PostprocessorArgs' -count=1、go test ./internal/commands -run 'DownloadImpl|HelpFlags' -count=1;不需要 FFmpeg 时设FFmpegBin = "/bin/false"、Settings.Index.Convert=false;用打印 JSON 的 shell 脚本 stub yt-dlp;避免导入去重可改变文件字节(如YTDLP_DUMMY_CONTENT)或目标路径。
延伸阅读
- AGENTS.md:仓库级规则与 Agent 提示(Sources of Truth 中列出了 Makefile、Setup/Test/API 指南、GLOSSARY.md 等权威来源);
- frontend/CODEMAP.md:前端(Vue 3 + Vuetify 4)代码地图;
- 包级文档:
internal/、pkg/、frontend/下各README.md,尤其是 internal/ai/face/README.md、internal/ai/onnx/README.md、internal/ai/vision/README.md、internal/ffmpeg/README.md、internal/meta/README.md、pkg/authn/authtoken/README.md 与 internal/auth/tokens/README.md。
这份代码地图的价值在于:它把"文件在哪里"和"它们如何协作"压缩成一张可执行的知识表。无论是新增配置项、注册 REST 端点、扩展 OIDC 组映射,还是排查缩略图渲染与安全下载的边界,沿着 CODEMAP 标注的路径都能在几次跳转内抵达目标源码——这正是为 Agent 与贡献者设计的导航效率。
- 后端
- 前端
- 图像处理
- 人工智能
- AI 应用
【免费下载链接】photoprism
AI-Powered Photos App 🌈💎✨
相关推荐
PhotoPrism 前端代码地图:Vue 3 + Vuetify 4 架构、模块导航与安全改动指南
PhotoPrism 前端代码地图:Vue 3 + Vuetify 4 架构、模块导航与安全改动指南 导读 frontend/CODEMAP.md https:
后端前端图像处理人工智能AI 应用如何替换游戏里的 DLSS 版本:免费开源工具 DLSS Swapper 完整指南
如何替换游戏里的 DLSS 版本:免费开源工具 DLSS Swapper 完整指南 你有没有过这种时刻:游戏买了大半年,里面用的还是发售那天的旧版 DLSS,显
桌面应用wandb SDK 源码地图:从 Python 侧到 Go 核心的导航指南
wandb SDK 源码地图:从 Python 侧到 Go 核心的导航指南 导读 本文基于 wandb 仓库中的 docs/sdk/source map.md
机器学习深度学习数据可视化可观测性
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考