Cleanup doc of users routes

This commit is contained in:
Zoe Roux 2025-03-29 20:05:09 +01:00
parent f53e71afff
commit b81c94f2da
No known key found for this signature in database
3 changed files with 150 additions and 131 deletions

View File

@ -159,14 +159,14 @@ const docTemplate = `{
"$ref": "#/definitions/main.Session" "$ref": "#/definitions/main.Session"
} }
}, },
"403": { "401": {
"description": "Invalid jwt token (or expired)", "description": "Missing jwt token",
"schema": { "schema": {
"$ref": "#/definitions/main.KError" "$ref": "#/definitions/main.KError"
} }
}, },
"422": { "403": {
"description": "Invalid session id", "description": "Invalid jwt token (or expired)",
"schema": { "schema": {
"$ref": "#/definitions/main.KError" "$ref": "#/definitions/main.KError"
} }
@ -258,9 +258,11 @@ const docTemplate = `{
"$ref": "#/definitions/main.User" "$ref": "#/definitions/main.User"
} }
}, },
"400": { "422": {
"description": "Invalid after id", "description": "Invalid after id",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
}, },
@ -279,6 +281,7 @@ const docTemplate = `{
"parameters": [ "parameters": [
{ {
"type": "string", "type": "string",
"example": "android",
"description": "The device the created session will be used on", "description": "The device the created session will be used on",
"name": "device", "name": "device",
"in": "query" "in": "query"
@ -296,16 +299,20 @@ const docTemplate = `{
"201": { "201": {
"description": "Created", "description": "Created",
"schema": { "schema": {
"$ref": "#/definitions/dbc.Session" "$ref": "#/definitions/main.SessionWToken"
} }
}, },
"400": {
"description": "Invalid register body",
"schema": {}
},
"409": { "409": {
"description": "Duplicated email or username", "description": "Duplicated email or username",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
},
"422": {
"description": "Invalid register body",
"schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
} }
@ -334,11 +341,15 @@ const docTemplate = `{
}, },
"401": { "401": {
"description": "Missing jwt token", "description": "Missing jwt token",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
}, },
"403": { "403": {
"description": "Invalid jwt token (or expired)", "description": "Invalid jwt token (or expired)",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
}, },
@ -405,7 +416,15 @@ const docTemplate = `{
}, },
"404": { "404": {
"description": "No user with the given id found", "description": "No user with the given id found",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
},
"422": {
"description": "Invalid id (not a uuid)",
"schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
}, },
@ -446,39 +465,15 @@ const docTemplate = `{
}, },
"404": { "404": {
"description": "Invalid user id", "description": "Invalid user id",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
} }
} }
}, },
"definitions": { "definitions": {
"dbc.Session": {
"type": "object",
"properties": {
"createdDate": {
"type": "string"
},
"device": {
"type": "string"
},
"id": {
"type": "string"
},
"lastUsed": {
"type": "string"
},
"pk": {
"type": "integer"
},
"token": {
"type": "string"
},
"userPk": {
"type": "integer"
}
}
},
"main.JwkSet": { "main.JwkSet": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -565,16 +560,19 @@ const docTemplate = `{
"properties": { "properties": {
"id": { "id": {
"description": "Id of this oidc handle.", "description": "Id of this oidc handle.",
"type": "string" "type": "string",
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
}, },
"profileUrl": { "profileUrl": {
"description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.", "description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.",
"type": "string", "type": "string",
"format": "url" "format": "url",
"example": "https://myanimelist.net/profile/zoriya"
}, },
"username": { "username": {
"description": "Username of the user on the external service.", "description": "Username of the user on the external service.",
"type": "string" "type": "string",
"example": "zoriya"
} }
} }
}, },
@ -589,15 +587,18 @@ const docTemplate = `{
"email": { "email": {
"description": "Valid email that could be used for forgotten password requests. Can be used for login.", "description": "Valid email that could be used for forgotten password requests. Can be used for login.",
"type": "string", "type": "string",
"format": "email" "format": "email",
"example": "kyoo@zoriya.dev"
}, },
"password": { "password": {
"description": "Password to use.", "description": "Password to use.",
"type": "string" "type": "string",
"example": "password1234"
}, },
"username": { "username": {
"description": "Username of the new account, can't contain @ signs. Can be used for login.", "description": "Username of the new account, can't contain @ signs. Can be used for login.",
"type": "string" "type": "string",
"example": "zoriya"
} }
} }
}, },
@ -663,24 +664,31 @@ const docTemplate = `{
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"type": "string" "type": "string"
},
"example": {
"isAdmin": " true"
} }
}, },
"createdDate": { "createdDate": {
"description": "When was this account created?", "description": "When was this account created?",
"type": "string" "type": "string",
"example": "2025-03-29T18:20:05.267Z"
}, },
"email": { "email": {
"description": "Email of the user. Can be used as a login.", "description": "Email of the user. Can be used as a login.",
"type": "string", "type": "string",
"format": "email" "format": "email",
"example": "kyoo@zoriya.dev"
}, },
"id": { "id": {
"description": "Id of the user.", "description": "Id of the user.",
"type": "string" "type": "string",
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
}, },
"lastSeen": { "lastSeen": {
"description": "When was the last time this account made any authorized request?", "description": "When was the last time this account made any authorized request?",
"type": "string" "type": "string",
"example": "2025-03-29T18:20:05.267Z"
}, },
"oidc": { "oidc": {
"description": "List of other login method available for this user. Access tokens wont be returned here.", "description": "List of other login method available for this user. Access tokens wont be returned here.",
@ -691,7 +699,8 @@ const docTemplate = `{
}, },
"username": { "username": {
"description": "Username of the user. Can be used as a login.", "description": "Username of the user. Can be used as a login.",
"type": "string" "type": "string",
"example": "zoriya"
} }
} }
} }

View File

@ -153,14 +153,14 @@
"$ref": "#/definitions/main.Session" "$ref": "#/definitions/main.Session"
} }
}, },
"403": { "401": {
"description": "Invalid jwt token (or expired)", "description": "Missing jwt token",
"schema": { "schema": {
"$ref": "#/definitions/main.KError" "$ref": "#/definitions/main.KError"
} }
}, },
"422": { "403": {
"description": "Invalid session id", "description": "Invalid jwt token (or expired)",
"schema": { "schema": {
"$ref": "#/definitions/main.KError" "$ref": "#/definitions/main.KError"
} }
@ -252,9 +252,11 @@
"$ref": "#/definitions/main.User" "$ref": "#/definitions/main.User"
} }
}, },
"400": { "422": {
"description": "Invalid after id", "description": "Invalid after id",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
}, },
@ -273,6 +275,7 @@
"parameters": [ "parameters": [
{ {
"type": "string", "type": "string",
"example": "android",
"description": "The device the created session will be used on", "description": "The device the created session will be used on",
"name": "device", "name": "device",
"in": "query" "in": "query"
@ -290,16 +293,20 @@
"201": { "201": {
"description": "Created", "description": "Created",
"schema": { "schema": {
"$ref": "#/definitions/dbc.Session" "$ref": "#/definitions/main.SessionWToken"
} }
}, },
"400": {
"description": "Invalid register body",
"schema": {}
},
"409": { "409": {
"description": "Duplicated email or username", "description": "Duplicated email or username",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
},
"422": {
"description": "Invalid register body",
"schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
} }
@ -328,11 +335,15 @@
}, },
"401": { "401": {
"description": "Missing jwt token", "description": "Missing jwt token",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
}, },
"403": { "403": {
"description": "Invalid jwt token (or expired)", "description": "Invalid jwt token (or expired)",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
}, },
@ -399,7 +410,15 @@
}, },
"404": { "404": {
"description": "No user with the given id found", "description": "No user with the given id found",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
},
"422": {
"description": "Invalid id (not a uuid)",
"schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
}, },
@ -440,39 +459,15 @@
}, },
"404": { "404": {
"description": "Invalid user id", "description": "Invalid user id",
"schema": {} "schema": {
"$ref": "#/definitions/main.KError"
}
} }
} }
} }
} }
}, },
"definitions": { "definitions": {
"dbc.Session": {
"type": "object",
"properties": {
"createdDate": {
"type": "string"
},
"device": {
"type": "string"
},
"id": {
"type": "string"
},
"lastUsed": {
"type": "string"
},
"pk": {
"type": "integer"
},
"token": {
"type": "string"
},
"userPk": {
"type": "integer"
}
}
},
"main.JwkSet": { "main.JwkSet": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -559,16 +554,19 @@
"properties": { "properties": {
"id": { "id": {
"description": "Id of this oidc handle.", "description": "Id of this oidc handle.",
"type": "string" "type": "string",
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
}, },
"profileUrl": { "profileUrl": {
"description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.", "description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.",
"type": "string", "type": "string",
"format": "url" "format": "url",
"example": "https://myanimelist.net/profile/zoriya"
}, },
"username": { "username": {
"description": "Username of the user on the external service.", "description": "Username of the user on the external service.",
"type": "string" "type": "string",
"example": "zoriya"
} }
} }
}, },
@ -583,15 +581,18 @@
"email": { "email": {
"description": "Valid email that could be used for forgotten password requests. Can be used for login.", "description": "Valid email that could be used for forgotten password requests. Can be used for login.",
"type": "string", "type": "string",
"format": "email" "format": "email",
"example": "kyoo@zoriya.dev"
}, },
"password": { "password": {
"description": "Password to use.", "description": "Password to use.",
"type": "string" "type": "string",
"example": "password1234"
}, },
"username": { "username": {
"description": "Username of the new account, can't contain @ signs. Can be used for login.", "description": "Username of the new account, can't contain @ signs. Can be used for login.",
"type": "string" "type": "string",
"example": "zoriya"
} }
} }
}, },
@ -657,24 +658,31 @@
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"type": "string" "type": "string"
},
"example": {
"isAdmin": " true"
} }
}, },
"createdDate": { "createdDate": {
"description": "When was this account created?", "description": "When was this account created?",
"type": "string" "type": "string",
"example": "2025-03-29T18:20:05.267Z"
}, },
"email": { "email": {
"description": "Email of the user. Can be used as a login.", "description": "Email of the user. Can be used as a login.",
"type": "string", "type": "string",
"format": "email" "format": "email",
"example": "kyoo@zoriya.dev"
}, },
"id": { "id": {
"description": "Id of the user.", "description": "Id of the user.",
"type": "string" "type": "string",
"example": "e05089d6-9179-4b5b-a63e-94dd5fc2a397"
}, },
"lastSeen": { "lastSeen": {
"description": "When was the last time this account made any authorized request?", "description": "When was the last time this account made any authorized request?",
"type": "string" "type": "string",
"example": "2025-03-29T18:20:05.267Z"
}, },
"oidc": { "oidc": {
"description": "List of other login method available for this user. Access tokens wont be returned here.", "description": "List of other login method available for this user. Access tokens wont be returned here.",
@ -685,7 +693,8 @@
}, },
"username": { "username": {
"description": "Username of the user. Can be used as a login.", "description": "Username of the user. Can be used as a login.",
"type": "string" "type": "string",
"example": "zoriya"
} }
} }
} }

View File

@ -18,37 +18,37 @@ type User struct {
// Primary key in database // Primary key in database
Pk int32 `json:"-"` Pk int32 `json:"-"`
// Id of the user. // Id of the user.
Id uuid.UUID `json:"id"` Id uuid.UUID `json:"id" example:"e05089d6-9179-4b5b-a63e-94dd5fc2a397"`
// Username of the user. Can be used as a login. // Username of the user. Can be used as a login.
Username string `json:"username"` Username string `json:"username" example:"zoriya"`
// Email of the user. Can be used as a login. // Email of the user. Can be used as a login.
Email string `json:"email" format:"email"` Email string `json:"email" format:"email" example:"kyoo@zoriya.dev"`
// When was this account created? // When was this account created?
CreatedDate time.Time `json:"createdDate"` CreatedDate time.Time `json:"createdDate" example:"2025-03-29T18:20:05.267Z"`
// When was the last time this account made any authorized request? // When was the last time this account made any authorized request?
LastSeen time.Time `json:"lastSeen"` LastSeen time.Time `json:"lastSeen" example:"2025-03-29T18:20:05.267Z"`
// List of custom claims JWT created via get /jwt will have // List of custom claims JWT created via get /jwt will have
Claims jwt.MapClaims `json:"claims"` Claims jwt.MapClaims `json:"claims" example:"isAdmin: true"`
// List of other login method available for this user. Access tokens wont be returned here. // List of other login method available for this user. Access tokens wont be returned here.
Oidc map[string]OidcHandle `json:"oidc,omitempty"` Oidc map[string]OidcHandle `json:"oidc,omitempty"`
} }
type OidcHandle struct { type OidcHandle struct {
// Id of this oidc handle. // Id of this oidc handle.
Id string `json:"id"` Id string `json:"id" example:"e05089d6-9179-4b5b-a63e-94dd5fc2a397"`
// Username of the user on the external service. // Username of the user on the external service.
Username string `json:"username"` Username string `json:"username" example:"zoriya"`
// Link to the profile of the user on the external service. Null if unknown or irrelevant. // Link to the profile of the user on the external service. Null if unknown or irrelevant.
ProfileUrl *string `json:"profileUrl" format:"url"` ProfileUrl *string `json:"profileUrl" format:"url" example:"https://myanimelist.net/profile/zoriya"`
} }
type RegisterDto struct { type RegisterDto struct {
// Username of the new account, can't contain @ signs. Can be used for login. // Username of the new account, can't contain @ signs. Can be used for login.
Username string `json:"username" validate:"required,excludes=@"` Username string `json:"username" validate:"required,excludes=@" example:"zoriya"`
// Valid email that could be used for forgotten password requests. Can be used for login. // Valid email that could be used for forgotten password requests. Can be used for login.
Email string `json:"email" validate:"required,email" format:"email"` Email string `json:"email" validate:"required,email" format:"email" example:"kyoo@zoriya.dev"`
// Password to use. // Password to use.
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required" example:"password1234"`
} }
func MapDbUser(user *dbc.User) User { func MapDbUser(user *dbc.User) User {
@ -80,7 +80,7 @@ func MapOidc(oidc *dbc.GetUserRow) OidcHandle {
// @Security Jwt[users.read] // @Security Jwt[users.read]
// @Param afterId query string false "used for pagination." Format(uuid) // @Param afterId query string false "used for pagination." Format(uuid)
// @Success 200 {object} User[] // @Success 200 {object} User[]
// @Failure 400 {object} problem.Problem "Invalid after id" // @Failure 422 {object} KError "Invalid after id"
// @Router /users [get] // @Router /users [get]
func (h *Handler) ListUsers(c echo.Context) error { func (h *Handler) ListUsers(c echo.Context) error {
err := CheckPermissions(c, []string{"user.read"}) err := CheckPermissions(c, []string{"user.read"})
@ -98,7 +98,7 @@ func (h *Handler) ListUsers(c echo.Context) error {
} else { } else {
uid, uerr := uuid.Parse(id) uid, uerr := uuid.Parse(id)
if uerr != nil { if uerr != nil {
return echo.NewHTTPError(400, "Invalid `afterId` parameter, uuid was expected") return echo.NewHTTPError(http.StatusUnprocessableEntity, "Invalid `afterId` parameter, uuid was expected")
} }
users, err = h.db.GetAllUsersAfter(ctx, dbc.GetAllUsersAfterParams{ users, err = h.db.GetAllUsersAfter(ctx, dbc.GetAllUsersAfterParams{
Limit: limit, Limit: limit,
@ -125,7 +125,8 @@ func (h *Handler) ListUsers(c echo.Context) error {
// @Security Jwt[users.read] // @Security Jwt[users.read]
// @Param id path string true "The id of the user" Format(uuid) // @Param id path string true "The id of the user" Format(uuid)
// @Success 200 {object} User // @Success 200 {object} User
// @Failure 404 {object} problem.Problem "No user with the given id found" // @Failure 404 {object} KError "No user with the given id found"
// @Failure 422 {object} KError "Invalid id (not a uuid)"
// @Router /users/{id} [get] // @Router /users/{id} [get]
func (h *Handler) GetUser(c echo.Context) error { func (h *Handler) GetUser(c echo.Context) error {
err := CheckPermissions(c, []string{"user.read"}) err := CheckPermissions(c, []string{"user.read"})
@ -135,7 +136,7 @@ func (h *Handler) GetUser(c echo.Context) error {
id, err := uuid.Parse(c.Param("id")) id, err := uuid.Parse(c.Param("id"))
if err != nil { if err != nil {
return echo.NewHTTPError(400, "Invalid id") return echo.NewHTTPError(http.StatusUnprocessableEntity, "Invalid id")
} }
dbuser, err := h.db.GetUser(context.Background(), id) dbuser, err := h.db.GetUser(context.Background(), id)
if err != nil { if err != nil {
@ -158,8 +159,8 @@ func (h *Handler) GetUser(c echo.Context) error {
// @Produce json // @Produce json
// @Security Jwt // @Security Jwt
// @Success 200 {object} User // @Success 200 {object} User
// @Failure 401 {object} problem.Problem "Missing jwt token" // @Failure 401 {object} KError "Missing jwt token"
// @Failure 403 {object} problem.Problem "Invalid jwt token (or expired)" // @Failure 403 {object} KError "Invalid jwt token (or expired)"
// @Router /users/me [get] // @Router /users/me [get]
func (h *Handler) GetMe(c echo.Context) error { func (h *Handler) GetMe(c echo.Context) error {
id, err := GetCurrentUserId(c) id, err := GetCurrentUserId(c)
@ -186,17 +187,17 @@ func (h *Handler) GetMe(c echo.Context) error {
// @Tags users // @Tags users
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param device query string false "The device the created session will be used on" // @Param device query string false "The device the created session will be used on" Example(android)
// @Param user body RegisterDto false "Registration informations" // @Param user body RegisterDto false "Registration informations"
// @Success 201 {object} dbc.Session // @Success 201 {object} SessionWToken
// @Failure 400 {object} problem.Problem "Invalid register body" // @Success 409 {object} KError "Duplicated email or username"
// @Success 409 {object} problem.Problem "Duplicated email or username" // @Failure 422 {object} KError "Invalid register body"
// @Router /users [post] // @Router /users [post]
func (h *Handler) Register(c echo.Context) error { func (h *Handler) Register(c echo.Context) error {
var req RegisterDto var req RegisterDto
err := c.Bind(&req) err := c.Bind(&req)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
} }
if err = c.Validate(&req); err != nil { if err = c.Validate(&req); err != nil {
return err return err
@ -230,8 +231,8 @@ func (h *Handler) Register(c echo.Context) error {
// @Security Jwt[users.delete] // @Security Jwt[users.delete]
// @Param id path string false "User id of the user to delete" Format(uuid) // @Param id path string false "User id of the user to delete" Format(uuid)
// @Success 200 {object} User // @Success 200 {object} User
// @Failure 404 {object} problem.Problem "Invalid id format" // @Failure 404 {object} KError "Invalid id format"
// @Failure 404 {object} problem.Problem "Invalid user id" // @Failure 404 {object} KError "Invalid user id"
// @Router /users/{id} [delete] // @Router /users/{id} [delete]
func (h *Handler) DeleteUser(c echo.Context) error { func (h *Handler) DeleteUser(c echo.Context) error {
uid, err := uuid.Parse(c.Param("id")) uid, err := uuid.Parse(c.Param("id"))