☰
PhotoPrism 后端代码地图(CODEMAP):从入口到源码的快速导航指南
2026/10/1 2:25:50 网站建设 项目流程
  • 后端
  • 前端
  • 图像处理
  • 人工智能
  • AI 应用

【免费下载链接】photoprism

AI-Powered Photos App 🌈💎✨

项目地址:https://gitcode.com/gh_mirrors/ph/photoprism
点击查看免费下载

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/apiGin 处理器与 Swagger 注解,只做胶水,不含业务逻辑
internal/commandsCLI 命令定义与编排(start、index、import、migrate等),catalog子包输出 CLI 文档
internal/serverHTTP 服务器、中间件、路由、静态资源/UI/WebDAV
internal/config配置、flags/env/options、客户端配置、DB 初始化与迁移
internal/entityGORM 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/authACL、会话、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 与节点引导共用。

服务器启动流程(正常路径)

  1. photoprism start(CLI)→internal/commands/start.go;
  2. 配置初始化、DB 初始化/迁移、会话清理 worker;
  3. internal/server/start.go构建 Gin 引擎、中间件、API 分组、模板;
  4. internal/server/routes.go注册 UI、WebDAV、分享、well-known 与全部/api/v1/*路由;
  5. Workers 与自动索引启动,/livez、/readyz健康端点可用。

/livez(及/health、/healthz)始终返回 200 OK;/readyz在conf.IsReady()为真时返回 200,否则返回 503(见 internal/server/start.go)。

常见开发任务实战

添加一个 CLI 命令

  1. 在internal/commands/<name>.go创建*cli.Command;
  2. 把它加入internal/commands/commands.go的PhotoPrism数组;
  3. 测试优先使用internal/commands/commands_test.go中的RunWithTestContext,避免os.Exit打断测试进程。

添加一个 REST 端点

  1. 在internal/api/<area>.go创建带 Swagger 注解的 handler;
  2. 在internal/server/routes.go注册;
  3. 复用辅助:api.ClientIP(c)、header.BearerToken(c)、Abort*系列函数;
  4. 列表端点校验分页边界(默认count=100,最大1000,offset>=0);
  5. 运行make fmt-go swag-fmt && make swag保持文档准确;
  6. 测试:go test ./internal/api -run <Name>,借助NewApiTest()、PerformRequest*聚焦辅助。

添加一个配置选项

  1. 在internal/config/options.go加带标签的字段;
  2. 在internal/config/flags.go通过EnvVars(...)注册 CLI 标志/环境变量;
  3. 暴露 getter(例如在config_server.go或对应主题文件);
  4. 按options.go中的同位顺序,把该选项追加到*config.Report()的rows;
  5. 若值需持久化,写回options.yml并重载到内存——优先用Config.SaveOptionsPatch(...)及相关配置自有辅助,不要临时写 YAML 逻辑;
  6. 需要 defaults/options/settings 文件路径时调用pkg/fs.ConfigFilePath,保证.yml与.yaml可互换;
  7. 测试覆盖 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 🌈💎✨

项目地址:https://gitcode.com/gh_mirrors/ph/photoprism
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询