API: Add missing Swagger endpoint annotations and update swagger.json

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-09-22 04:12:02 +02:00
parent c8964fdc6b
commit 578fbe4d10
17 changed files with 2394 additions and 86 deletions

View File

@@ -171,11 +171,16 @@ func CreateMarker(router *gin.RouterGroup) {
// UpdateMarker updates an existing file area marker to assign faces or other subjects.
//
// The request parameters are:
//
// - marker_uid: string Marker UID as returned by the API
//
// PUT /api/v1/markers/:marker_uid
// @Summary update a marker (face/subject region)
// @Id UpdateMarker
// @Tags Files
// @Accept json
// @Produce json
// @Param marker_uid path string true "marker uid"
// @Param marker body form.Marker true "marker properties"
// @Success 200 {object} entity.Marker
// @Failure 400,401,403,404,429 {object} i18n.Response
// @Router /api/v1/markers/{marker_uid} [put]
func UpdateMarker(router *gin.RouterGroup) {
router.PUT("/markers/:marker_uid", func(c *gin.Context) {
// Abort if workers runs less than once per hour.
@@ -261,15 +266,16 @@ func UpdateMarker(router *gin.RouterGroup) {
})
}
// ClearMarkerSubject removes an existing marker subject association.
// ClearMarkerSubject removes the subject association from a marker.
//
// The request parameters are:
//
// - uid: string Photo UID as returned by the API
// - file_uid: string File UID as returned by the API
// - id: int Marker ID as returned by the API
//
// DELETE /api/v1/markers/:marker_uid/subject
// @Summary clear the subject of a marker
// @Id ClearMarkerSubject
// @Tags Files
// @Produce json
// @Param marker_uid path string true "marker uid"
// @Success 200 {object} entity.Marker
// @Failure 400,401,403,404,429 {object} i18n.Response
// @Router /api/v1/markers/{marker_uid}/subject [delete]
func ClearMarkerSubject(router *gin.RouterGroup) {
router.DELETE("/markers/:marker_uid/subject", func(c *gin.Context) {
// Abort if workers runs less than once per hour.

View File

@@ -12,10 +12,14 @@ import (
"github.com/photoprism/photoprism/pkg/service/http/header"
)
// OAuthAuthorize should gather consent and authorization from resource owners when using the
// Authorization Code Grant flow, see https://github.com/photoprism/photoprism/issues/4368.
// OAuthAuthorize (placeholder) for Authorization Code Grant consent.
//
// GET /api/v1/oauth/authorize
// @Summary OAuth2 authorization endpoint (not implemented)
// @Id OAuthAuthorize
// @Tags Authentication
// @Produce json
// @Failure 405 {object} i18n.Response
// @Router /api/v1/oauth/authorize [get]
func OAuthAuthorize(router *gin.RouterGroup) {
router.GET("/oauth/authorize", func(c *gin.Context) {
// Prevent CDNs from caching this endpoint.

View File

@@ -18,10 +18,17 @@ import (
"github.com/photoprism/photoprism/pkg/service/http/header"
)
// OAuthRevoke takes an access token and deletes it. A client may only delete its own tokens.
// OAuthRevoke revokes an access token or session. A client may only revoke its own tokens.
//
// @Tags Authentication
// @Router /api/v1/oauth/revoke [post]
// @Summary revoke an OAuth2 access token or session
// @Id OAuthRevoke
// @Tags Authentication
// @Accept json
// @Produce json
// @Param request body form.OAuthRevokeToken true "revoke request"
// @Success 200 {object} gin.H
// @Failure 400,401,403,404,429 {object} i18n.Response
// @Router /api/v1/oauth/revoke [post]
func OAuthRevoke(router *gin.RouterGroup) {
router.POST("/oauth/revoke", func(c *gin.Context) {
// Prevent CDNs from caching this endpoint.

View File

@@ -19,10 +19,17 @@ import (
"github.com/photoprism/photoprism/pkg/service/http/header"
)
// OAuthToken creates a new access token for clients that authenticate with valid OAuth2 client credentials.
// OAuthToken creates a new access token for clients using OAuth2 grant types.
//
// @Tags Authentication
// @Router /api/v1/oauth/token [post]
// @Summary create an OAuth2 access token
// @Id OAuthToken
// @Tags Authentication
// @Accept json
// @Produce json
// @Param request body form.OAuthCreateToken true "token request (supports client_credentials, password, or session grant)"
// @Success 200 {object} gin.H
// @Failure 400,401,429 {object} i18n.Response
// @Router /api/v1/oauth/token [post]
func OAuthToken(router *gin.RouterGroup) {
router.POST("/oauth/token", func(c *gin.Context) {
// Prevent CDNs from caching this endpoint.

View File

@@ -13,9 +13,15 @@ import (
"github.com/photoprism/photoprism/pkg/service/http/header"
)
// OIDCLogin redirects a browser to the login page of the configured OpenID Connect provider, if any.
// OIDCLogin redirects a browser to the login page of the configured OpenID Connect provider.
//
// GET /api/v1/oidc/login
// @Summary start OpenID Connect login (browser redirect)
// @Id OIDCLogin
// @Tags Authentication
// @Produce html
// @Success 307 {string} string "redirect to provider login page"
// @Failure 401,403,429 {object} i18n.Response
// @Router /api/v1/oidc/login [get]
func OIDCLogin(router *gin.RouterGroup) {
router.GET("/oidc/login", func(c *gin.Context) {
// Prevent CDNs from caching this endpoint.

View File

@@ -23,10 +23,17 @@ import (
"github.com/photoprism/photoprism/pkg/txt"
)
// OIDCRedirect creates a new API access token when a user has been successfully authenticated via OIDC,
// and then redirects the browser back to the app.
// OIDCRedirect completes the OIDC flow, creates a session, and renders a page that stores the token client-side.
//
// GET /api/v1/oidc/redirect
// @Summary complete OIDC login (callback)
// @Id OIDCRedirect
// @Tags Authentication
// @Produce html
// @Param state query string true "opaque OAuth2 state value"
// @Param code query string true "authorization code"
// @Success 200 {string} string "HTML page bootstrapping token storage"
// @Failure 401,403,429 {string} string "rendered error page"
// @Router /api/v1/oidc/redirect [get]
func OIDCRedirect(router *gin.RouterGroup) {
router.GET("/oidc/redirect", func(c *gin.Context) {
// Prevent CDNs from caching this endpoint.

View File

@@ -16,11 +16,17 @@ import (
"github.com/photoprism/photoprism/pkg/service/http/header"
)
// CreateSession creates a new client session and returns it as JSON if authentication was successful.
// CreateSession creates a new client session (login) and returns session data.
//
// @Tags Authentication
// @Router /api/v1/session [post]
// @Router /api/v1/sessions [post]
// @Summary create a session (login)
// @Tags Authentication
// @Accept json
// @Produce json
// @Param credentials body form.Login true "login credentials"
// @Success 200 {object} gin.H
// @Failure 400,401,429 {object} i18n.Response
// @Router /api/v1/session [post]
// @Router /api/v1/sessions [post]
func CreateSession(router *gin.RouterGroup) {
createSessionHandler := func(c *gin.Context) {
// Prevent CDNs from caching this endpoint.

View File

@@ -20,9 +20,15 @@ import (
// DeleteSession deletes an existing client session (logout).
//
// DELETE /api/v1/session
// DELETE /api/v1/session/:id
// DELETE /api/v1/sessions/:id
// @Summary delete a session (logout)
// @Tags Authentication
// @Produce json
// @Param id path string false "session id or ref id"
// @Success 200 {object} gin.H
// @Failure 401,403,404,429 {object} i18n.Response
// @Router /api/v1/session [delete]
// @Router /api/v1/session/{id} [delete]
// @Router /api/v1/sessions/{id} [delete]
func DeleteSession(router *gin.RouterGroup) {
deleteSessionHandler := func(c *gin.Context) {
// Prevent CDNs from caching this endpoint.

View File

@@ -12,11 +12,17 @@ import (
"github.com/photoprism/photoprism/pkg/service/http/header"
)
// GetSession returns the session data as JSON if authentication was successful.
// GetSession returns session data for the current or specified session.
//
// GET /api/v1/session
// GET /api/v1/session/:id
// GET /api/v1/sessions/:id
// @Summary get the current session or a session by id
// @Tags Authentication
// @Produce json
// @Param id path string false "session id"
// @Success 200 {object} gin.H
// @Failure 401,403,404,429 {object} i18n.Response
// @Router /api/v1/session [get]
// @Router /api/v1/session/{id} [get]
// @Router /api/v1/sessions/{id} [get]
func GetSession(router *gin.RouterGroup) {
getSessionHandler := func(c *gin.Context) {
// Prevent CDNs from caching this endpoint.

File diff suppressed because it is too large Load Diff

View File

@@ -22,10 +22,10 @@ import (
// @Description Fore more information see:
// @Description - https://docs.photoprism.app/developer-guide/api/thumbnails/#image-endpoint-uri
// @Id GetThumb
// @Produce image/jpeg
// @Produce image/jpeg, image/svg+xml
// @Tags Images, Files
// @Failure 403 {file} image/svg+xml
// @Failure 200 {file} image/svg+xml
// @Success 200 {file} image/svg+xml
// @Success 200 {file} image/jpg
// @Param thumb path string true "SHA1 file hash, optionally with a crop area suffixed, e.g. '-016014058037'"
// @Param token path string true "user-specific security token provided with session or 'public' when running PhotoPrism in public mode"

View File

@@ -17,10 +17,19 @@ import (
"github.com/photoprism/photoprism/pkg/service/http/header"
)
// UploadUserAvatar updates the avatar image of the currently authenticated user.
// UploadUserAvatar updates the avatar image of the specified user.
//
// @Tags Users
// @Router /api/v1/users/{uid}/avatar [post]
// @Summary upload a new avatar image for a user
// @Description Accepts a single PNG or JPEG file (max 20 MB) in a multipart form field named "files" and sets it as the user's avatar.
// @Id UploadUserAvatar
// @Tags Users
// @Accept multipart/form-data
// @Produce json
// @Param uid path string true "user uid"
// @Param files formData file true "avatar image (png or jpeg, <= 20 MB)"
// @Success 200 {object} entity.User
// @Failure 400,401,403,404,429 {object} i18n.Response
// @Router /api/v1/users/{uid}/avatar [post]
func UploadUserAvatar(router *gin.RouterGroup) {
router.POST("/users/:uid/avatar", func(c *gin.Context) {
conf := get.Config()

View File

@@ -21,10 +21,18 @@ import (
"github.com/photoprism/photoprism/pkg/service/http/header"
)
// CreateUserPasscode sets up a new two-factor authentication passcode.
// CreateUserPasscode sets up a new two-factor authentication passcode for a user.
//
// @Tags Users
// @Router /api/v1/users/{uid}/passcode [post]
// @Summary create a new 2FA passcode for a user
// @Id CreateUserPasscode
// @Tags Users
// @Accept json
// @Produce json
// @Param uid path string true "user uid"
// @Param request body form.Passcode true "passcode setup (password required)"
// @Success 200 {object} entity.Passcode
// @Failure 400,401,403,429 {object} i18n.Response
// @Router /api/v1/users/{uid}/passcode [post]
func CreateUserPasscode(router *gin.RouterGroup) {
router.POST("/users/:uid/passcode", func(c *gin.Context) {
// Check authentication and authorization.
@@ -80,10 +88,18 @@ func CreateUserPasscode(router *gin.RouterGroup) {
})
}
// ConfirmUserPasscode checks a new passcode and flags it as verified so that it can be activated.
// ConfirmUserPasscode verifies a newly created passcode so that it can be activated.
//
// @Tags Users
// @Router /api/v1/users/{uid}/passcode/confirm [post]
// @Summary verify a new 2FA passcode
// @Id ConfirmUserPasscode
// @Tags Users
// @Accept json
// @Produce json
// @Param uid path string true "user uid"
// @Param request body form.Passcode true "verification code"
// @Success 200 {object} entity.Passcode
// @Failure 400,401,403,429 {object} i18n.Response
// @Router /api/v1/users/{uid}/passcode/confirm [post]
func ConfirmUserPasscode(router *gin.RouterGroup) {
router.POST("/users/:uid/passcode/confirm", func(c *gin.Context) {
// Check authentication and authorization.
@@ -126,10 +142,16 @@ func ConfirmUserPasscode(router *gin.RouterGroup) {
})
}
// ActivateUserPasscode activates two-factor authentication if a passcode has been created and verified.
// ActivateUserPasscode activates 2FA after a passcode has been created and verified.
//
// @Tags Users
// @Router /api/v1/users/{uid}/passcode/activate [post]
// @Summary activate 2FA with a verified passcode
// @Id ActivateUserPasscode
// @Tags Users
// @Produce json
// @Param uid path string true "user uid"
// @Success 200 {object} entity.Passcode
// @Failure 401,403,404,429 {object} i18n.Response
// @Router /api/v1/users/{uid}/passcode/activate [post]
func ActivateUserPasscode(router *gin.RouterGroup) {
router.POST("/users/:uid/passcode/activate", func(c *gin.Context) {
// Check authentication and authorization.
@@ -163,10 +185,18 @@ func ActivateUserPasscode(router *gin.RouterGroup) {
})
}
// DeactivateUserPasscode disables removes a passcode key to disable two-factor authentication.
// DeactivateUserPasscode removes a passcode key to disable two-factor authentication.
//
// @Tags Users
// @Router /api/v1/users/{uid}/passcode/deactivate [post]
// @Summary deactivate 2FA and remove the passcode
// @Id DeactivateUserPasscode
// @Tags Users
// @Accept json
// @Produce json
// @Param uid path string true "user uid"
// @Param request body form.Passcode true "password for confirmation"
// @Success 200 {object} i18n.Response
// @Failure 400,401,403,404,429 {object} i18n.Response
// @Router /api/v1/users/{uid}/passcode/deactivate [post]
func DeactivateUserPasscode(router *gin.RouterGroup) {
router.POST("/users/:uid/passcode/deactivate", func(c *gin.Context) {
// Check authentication and authorization.

View File

@@ -16,10 +16,18 @@ import (
"github.com/photoprism/photoprism/pkg/i18n"
)
// UpdateUserPassword changes the password of the currently authenticated user.
// UpdateUserPassword changes the password of the specified user.
//
// @Tags Users, Authentication
// @Router /api/v1/users/{uid}/password [put]
// @Summary change a user's password
// @Id UpdateUserPassword
// @Tags Users, Authentication
// @Accept json
// @Produce json
// @Param uid path string true "user uid"
// @Param request body form.ChangePassword true "old and new password"
// @Success 200 {object} i18n.Response
// @Failure 400,401,403,404,429 {object} i18n.Response
// @Router /api/v1/users/{uid}/password [put]
func UpdateUserPassword(router *gin.RouterGroup) {
router.PUT("/users/:uid/password", func(c *gin.Context) {
conf := get.Config()

View File

@@ -18,8 +18,17 @@ import (
// FindUserSessions finds user sessions and returns them as JSON.
//
// @Tags Users, Authentication
// @Router /api/v1/users/{uid}/sessions [get]
// @Summary list sessions for a user
// @Id FindUserSessions
// @Tags Users, Authentication
// @Produce json
// @Param uid path string true "user uid"
// @Param count query int true "maximum number of results" minimum(1) maximum(100000)
// @Param offset query int false "result offset" minimum(0)
// @Param q query string false "filter by username or client name"
// @Success 200 {object} entity.Sessions
// @Failure 401,403,429 {object} i18n.Response
// @Router /api/v1/users/{uid}/sessions [get]
func FindUserSessions(router *gin.RouterGroup) {
router.GET("/users/:uid/sessions", func(c *gin.Context) {
// Check if the session user is has user management privileges.

View File

@@ -16,10 +16,18 @@ import (
"github.com/photoprism/photoprism/pkg/i18n"
)
// UpdateUser updates the profile information of the currently authenticated user.
// UpdateUser updates profile information for the specified user.
//
// @Tags Users
// @Router /api/v1/users/{uid} [put]
// @Summary update user profile information
// @Id UpdateUser
// @Tags Users
// @Accept json
// @Produce json
// @Param uid path string true "user uid"
// @Param user body form.User true "properties to be updated"
// @Success 200 {object} entity.User
// @Failure 400,401,403,404,409,429 {object} i18n.Response
// @Router /api/v1/users/{uid} [put]
func UpdateUser(router *gin.RouterGroup) {
router.PUT("/users/:uid", func(c *gin.Context) {
conf := get.Config()

View File

@@ -25,10 +25,19 @@ import (
"github.com/photoprism/photoprism/pkg/media"
)
// UploadUserFiles adds files to the user upload folder, from where they can be moved and indexed.
// UploadUserFiles adds files to the user's upload folder from where they can be processed and indexed.
//
// @Tags Users, Files
// @Router /users/{uid}/upload/{token} [post]
// @Summary upload files to a user's upload folder
// @Id UploadUserFiles
// @Tags Users, Files
// @Accept multipart/form-data
// @Produce json
// @Param uid path string true "user uid"
// @Param token path string true "upload token"
// @Param files formData file true "one or more files to upload (repeat the field for multiple files)"
// @Success 200 {object} i18n.Response
// @Failure 400,401,403,413,429,507 {object} i18n.Response
// @Router /users/{uid}/upload/{token} [post]
func UploadUserFiles(router *gin.RouterGroup) {
router.POST("/users/:uid/upload/:token", func(c *gin.Context) {
conf := get.Config()
@@ -252,9 +261,19 @@ func UploadCheckFile(destName string, rejectRaw bool, totalSizeLimit int64) (rem
}
}
// ProcessUserUpload triggers processing once all files have been uploaded.
// ProcessUserUpload triggers processing and import of previously uploaded files.
//
// PUT /users/:uid/upload/:token
// @Summary process previously uploaded files for a user
// @Id ProcessUserUpload
// @Tags Users, Files
// @Accept json
// @Produce json
// @Param uid path string true "user uid"
// @Param token path string true "upload token"
// @Param options body form.UploadOptions true "processing options"
// @Success 200 {object} i18n.Response
// @Failure 400,401,403,404,409,429 {object} i18n.Response
// @Router /users/{uid}/upload/{token} [put]
func ProcessUserUpload(router *gin.RouterGroup) {
router.PUT("/users/:uid/upload/:token", func(c *gin.Context) {
s := AuthAny(c, acl.ResourceFiles, acl.Permissions{acl.ActionManage, acl.ActionUpload})