mirror of
https://github.com/photoprism/photoprism.git
synced 2026-01-23 10:26:03 +00:00
|
|
||
|---|---|---|
| .. | ||
| download | ||
| embed | ||
| testdata | ||
| abort.go | ||
| albums.go | ||
| albums_search.go | ||
| albums_search_test.go | ||
| albums_test.go | ||
| api.go | ||
| api_auth.go | ||
| api_auth_jwt.go | ||
| api_auth_jwt_test.go | ||
| api_auth_test.go | ||
| api_client_config.go | ||
| api_client_config_test.go | ||
| api_event.go | ||
| api_log.go | ||
| api_methods.go | ||
| api_request.go | ||
| api_response.go | ||
| api_response_headers.go | ||
| api_response_test.go | ||
| api_test.go | ||
| auth_tokens.go | ||
| batch_albums.go | ||
| batch_labels.go | ||
| batch_photos.go | ||
| batch_photos_edit.go | ||
| batch_photos_edit_test.go | ||
| batch_photos_test.go | ||
| cache.go | ||
| cache_test.go | ||
| cluster_metrics.go | ||
| cluster_metrics_test.go | ||
| cluster_nodes.go | ||
| cluster_nodes_redaction_test.go | ||
| cluster_nodes_register.go | ||
| cluster_nodes_register_test.go | ||
| cluster_nodes_register_url_test.go | ||
| cluster_nodes_test.go | ||
| cluster_nodes_update_siteurl_test.go | ||
| cluster_permissions_test.go | ||
| cluster_summary.go | ||
| cluster_theme.go | ||
| cluster_theme_test.go | ||
| config_options.go | ||
| config_options_test.go | ||
| config_settings.go | ||
| config_settings_test.go | ||
| connect.go | ||
| connect_test.go | ||
| covers.go | ||
| covers_test.go | ||
| doc_overrides.go | ||
| docs.go | ||
| download.go | ||
| download_album.go | ||
| download_album_test.go | ||
| download_test.go | ||
| echo.go | ||
| echo_test.go | ||
| errors.go | ||
| errors_test.go | ||
| faces.go | ||
| faces_search.go | ||
| faces_search_test.go | ||
| faces_test.go | ||
| feedback.go | ||
| feedback_test.go | ||
| file_delete.go | ||
| file_delete_test.go | ||
| file_orientation.go | ||
| files.go | ||
| files_test.go | ||
| folders_cover.go | ||
| folders_cover_test.go | ||
| folders_search.go | ||
| folders_search_test.go | ||
| health.go | ||
| import.go | ||
| import_test.go | ||
| index.go | ||
| index_test.go | ||
| labels.go | ||
| labels_search.go | ||
| labels_search_test.go | ||
| labels_test.go | ||
| links.go | ||
| links_test.go | ||
| markers.go | ||
| markers_test.go | ||
| metrics.go | ||
| metrics_test.go | ||
| moments_time.go | ||
| moments_time_test.go | ||
| oauth_authorize.go | ||
| oauth_authorize_test.go | ||
| oauth_revoke.go | ||
| oauth_revoke_test.go | ||
| oauth_token.go | ||
| oauth_token_ratelimit_test.go | ||
| oauth_token_test.go | ||
| oauth_userinfo.go | ||
| oauth_userinfo_test.go | ||
| oidc_login.go | ||
| oidc_login_test.go | ||
| oidc_redirect.go | ||
| oidc_redirect_test.go | ||
| options.go | ||
| photo_label.go | ||
| photo_label_test.go | ||
| photo_unstack.go | ||
| photo_unstack_test.go | ||
| photos.go | ||
| photos_search.go | ||
| photos_search_geo.go | ||
| photos_search_geo_test.go | ||
| photos_search_test.go | ||
| photos_test.go | ||
| places_reverse.go | ||
| places_search.go | ||
| places_test.go | ||
| reactions.go | ||
| README.md | ||
| server.go | ||
| services.go | ||
| services_search.go | ||
| services_search_test.go | ||
| services_test.go | ||
| services_upload.go | ||
| services_upload_test.go | ||
| session.go | ||
| session_create.go | ||
| session_delete.go | ||
| session_get.go | ||
| session_ratelimit_test.go | ||
| session_response.go | ||
| session_test.go | ||
| share.go | ||
| share_preview.go | ||
| share_preview_test.go | ||
| share_test.go | ||
| status.go | ||
| status_test.go | ||
| subjects.go | ||
| subjects_search.go | ||
| subjects_search_test.go | ||
| subjects_test.go | ||
| svg.go | ||
| svg_test.go | ||
| swagger.json | ||
| thumbnails.go | ||
| thumbnails_test.go | ||
| users_avatar.go | ||
| users_avatar_test.go | ||
| users_passcode.go | ||
| users_passcode_test.go | ||
| users_password.go | ||
| users_password_test.go | ||
| users_sessions.go | ||
| users_sessions_test.go | ||
| users_update.go | ||
| users_update_test.go | ||
| users_upload.go | ||
| users_upload_multipart_test.go | ||
| users_upload_test.go | ||
| video.go | ||
| video_test.go | ||
| vision_caption.go | ||
| vision_face.go | ||
| vision_face_test.go | ||
| vision_labels.go | ||
| vision_labels_test.go | ||
| vision_nsfw.go | ||
| vision_nsfw_test.go | ||
| websocket.go | ||
| websocket_create.go | ||
| websocket_reader.go | ||
| websocket_test.go | ||
| websocket_topics_test.go | ||
| websocket_writer.go | ||
| zip.go | ||
| zip_test.go | ||
API Package Guide
Overview
The API package exposes PhotoPrism’s HTTP endpoints via Gin handlers. Each file under internal/api contains the handlers, request/response DTOs, and Swagger annotations for a specific feature area. Handlers remain thin: they validate input, enforce security or ACL checks, and delegate domain work to services in internal/photoprism, internal/service, or other internal packages. Keep exported types aligned with the REST schema and avoid embedding business logic directly in handlers.
Routing & Wiring
- Register handlers in
internal/server/routes.goby attaching them to the proper router group (for example,APIv1 := router.Group(conf.BaseUri("/api/v1"), Api(conf))). - Group endpoints by resource to match existing patterns: sessions, cluster, photos, labels, files, downloads, metadata, and technical routes.
- Apply middleware stacks (
Api,AuthRequired,limiter.Auth, etc.) at the router level to keep handlers focused on request handling. - Use
conf.BaseUri()when constructing route prefixes so configuration overrides propagate consistently. - When new endpoints require feature toggles, gate them in the router rather than inside the handler so disabled routes remain undiscoverable.
Handler Implementation Patterns
- Accept and return JSON using the shared response helpers. Set
header.ContentTypeJSONand ensure responses include proper cache headers (no-storefor sensitive payloads). - Parse parameters with Gin binding and validate inputs before delegating work. For complex payloads, define dedicated request structs with validation tags.
- Use the shared download helpers (
safe.Download,avatar.SafeDownload) when calling outward HTTP APIs to inherit timeout, size, and SSRF protections. - Query and persist data through the corresponding services or repositories; avoid ad-hoc SQL or GORM usage in handlers when dedicated functions exist elsewhere.
- Surface pagination consistently with
count,offset, andlimitfollowing the defaults (100 max 1000). Validateoffset >= 0and clampcountto the allowed range. - When responses need role-specific fields, build DTOs that redact sensitive data for non-admin roles so the handler stays deterministic.
Security & Middleware
- Authenticate requests using the standard middleware (
AuthRequired) and check roles via helpers ininternal/auth/acl(acl.ParseRole,acl.ScopePermits,acl.ScopeAttrPermits). - Never log secrets or tokens. Prefer structured logging through
event.Logand redact sensitive values before logging. - Enforce rate limiting with the shared limiters (
limiter.Auth,limiter.Login) and respond withlimiter.AbortJSONto maintain consistent 429 JSON payloads. - Derive client IPs through
api.ClientIPand extract bearer tokens withheader.BearerTokenor the helper setters. Use constant-time comparison for tokens and secrets. - For downloads or proxy endpoints, validate URLs against allowed schemes (
http,https) and reject private or loopback addresses unless explicitly required.
Audit Logging
- Emit security events via
event.Audit*(AuditInfo,AuditWarn,AuditErr,AuditDebug) and always build the slice as Who → What → Outcome.- Who:
ClientIP(c)followed by the most specific actor context ("session %s","client %s","user %s"). - What: Resource constant plus action segments (for example,
string(acl.ResourceCluster),"node", "%s"). Place extra context such as counts or error placeholders in separate segments before the outcome. - Outcome: End with a single token such as
status.Succeeded,status.Failed,status.Denied, orstatus.Error(err)when the sanitized error message should be the outcome; nothing comes after it.
- Who:
- Prefer existing helpers (
ClientIP,clean.Log,clean.LogQuote,clean.Error) instead of formatting values manually, and avoid inline=expressions. - Example patterns:
event.AuditInfo([]string{ ClientIP(c), "session %s", string(acl.ResourceCluster), "node", "%s", status.Deleted, }, s.RefID, uuid) event.AuditErr([]string{ clientIp, "session %s", string(acl.ResourceCluster), "download theme", status.Error(err), }, refID) - See
specs/common/audit-logs.mdfor the full conventions and additional examples that agents should follow.
Swagger Documentation
- Annotate handlers with Swagger comments that include full
/api/v1/...paths, request/response schemas, and security definitions. Only annotate routes that are externally accessible. - Regenerate docs after adding or updating handlers:
make fmt-go swag-fmt swag. This formats Go files, normalizes annotations, and updatesinternal/api/swagger.json. Do not edit the generated JSON manually. - When adding new DTOs, keep field names aligned with the JSON schema and update client documentation if serialized names change.
- Use enum annotations sparingly and ensure they reflect actual runtime constraints to avoid misleading generated clients.
Testing Strategy
- Build tests around the API harness (
NewApiTest) to obtain a configured Gin router, config, and dependencies. This isolates filesystem paths and avoids polluting global state. - Wrap requests with helper functions (for example,
PerformRequestJSON,PerformAuthenticatedRequest) to capture status codes, headers, and payloads. Assert headers using constants frompkg/http/header. - When handlers interact with the database, initialize fixtures through config helpers such as
config.NewTestConfig("api")orconfig.NewMinimalTestConfigWithDb("api", t.TempDir())depending on fixture needs. - Stub external dependencies (
httptest.Server) for remote calls and setAllowPrivate=trueexplicitly when the test server binds to loopback addresses. - Structure tests with table-driven subtests (
t.Run("CaseName", ...)) and use PascalCase names. Provide cleanup functions (t.Cleanup) to remove temporary files or databases created during tests.
Focused Test Runs
- Fast iteration:
go test ./internal/api -run '<Package|HandlerName>' -count=1 - Cluster endpoints:
go test ./internal/api -run 'Cluster' -count=1 - Downloads and zip streaming:
go test ./internal/api -run 'Download|Archive' -count=1 - Combined CLI and API validation: pair
go test ./internal/commands -run 'Cluster' -count=1with the matching API suite to ensure DTOs remain compatible.
Preflight Checklist
- Format and regenerate documentation:
make fmt-go swag-fmt swag - Compile backend:
go build ./... - Execute targeted API suites:
go test ./internal/api -run '<Name>' -count=1 - Run integration-heavy checks before release:
go test ./internal/service/cluster/registry -count=1alongside relevant API routes to confirm cluster DTOs stay aligned. - Verify that
photoprism show commands --jsonreflects any new API-driven flags or outputs when CLI exposure changes.