diff --git a/pkg/swagger/docs.go b/pkg/swagger/docs.go index 6478c8f99..dd526292a 100644 --- a/pkg/swagger/docs.go +++ b/pkg/swagger/docs.go @@ -23,6 +23,411 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/admin/overview": { + "get": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Returns per-instance counts (users, projects, shares) plus version and license info. Instance-admin only, gated by the admin_panel feature.", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Admin overview", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.Overview" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/projects": { + "get": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Paginated list of every project on the instance, regardless of ownership.", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List projects (admin)", + "parameters": [ + { + "type": "integer", + "description": "Page number, defaults to 1.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Items per page, defaults to the service setting.", + "name": "per_page", + "in": "query" + }, + { + "type": "string", + "description": "Search projects by title, description or identifier.", + "name": "s", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Project" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/projects/{id}/owner": { + "patch": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Reassign a project's owner. The existing update endpoint doesn't allow owner changes — this is the admin-only escape hatch.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Reassign project owner (admin)", + "parameters": [ + { + "type": "integer", + "description": "Project ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "New owner", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.OwnerPatch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Project" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/users": { + "get": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Paginated list of all users on the instance. Supports search by username/email. Exposes fields hidden from the normal user API (is_admin, status).", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List users (admin)", + "parameters": [ + { + "type": "string", + "description": "Search string matched against username and email.", + "name": "s", + "in": "query" + }, + { + "type": "integer", + "description": "Page number, defaults to 1.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Items per page, defaults to the service setting.", + "name": "per_page", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/admin.User" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + }, + "post": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Create a new local user account. Respects the admin-only fields ` + "`" + `is_admin` + "`" + ` and ` + "`" + `skip_email_confirm` + "`" + `. The public registration toggle is bypassed.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create a user (admin)", + "parameters": [ + { + "description": "The user to create", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.CreateUserBody" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/users/{id}": { + "delete": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Delete a user. With mode=now the user is removed immediately. With mode=scheduled (the default, matching the CLI) the user receives a confirmation email and is scheduled for deletion just like a self-initiated account deletion.", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete a user (admin)", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Deletion mode: 'now' for immediate deletion, 'scheduled' (default) to trigger the email-confirmation self-deletion flow.", + "name": "mode", + "in": "query" + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/users/{id}/admin": { + "patch": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Toggle the instance-admin flag on a user. Demoting the last remaining admin is refused with 400.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Promote or demote a user (admin)", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "New admin value", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.IsAdminPatch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/users/{id}/status": { + "patch": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Change a user's status without requiring them to log in.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Set a user's status (admin)", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Status", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.StatusPatch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, "/auth/openid/{provider}/callback": { "post": { "security": [ @@ -8479,6 +8884,158 @@ const docTemplate = `{ } }, "definitions": { + "admin.CreateUserBody": { + "type": "object", + "properties": { + "email": { + "description": "The user's email address", + "type": "string", + "maxLength": 250 + }, + "is_admin": { + "description": "Mark the new user as an instance admin.", + "type": "boolean" + }, + "language": { + "description": "The language of the new user. Must be a valid IETF BCP 47 language code and exist in Vikunja.", + "type": "string" + }, + "name": { + "description": "The full name of the new user. Optional.", + "type": "string" + }, + "password": { + "description": "The user's password in clear text. Only used when registering the user. The maximum limi is 72 bytes, which may be less than 72 characters. This is due to the limit in the bcrypt hashing algorithm used to store passwords in Vikunja.", + "type": "string", + "maxLength": 72, + "minLength": 8 + }, + "skip_email_confirm": { + "description": "Activate the new user immediately without email confirmation.", + "type": "boolean" + }, + "username": { + "description": "The user's username. Cannot contain anything that looks like an url or whitespaces.", + "type": "string", + "maxLength": 250, + "minLength": 3 + } + } + }, + "admin.IsAdminPatch": { + "type": "object", + "properties": { + "is_admin": { + "description": "Pointer to distinguish \"omitted\" from false; an empty body would silently demote otherwise.", + "type": "boolean" + } + } + }, + "admin.Overview": { + "type": "object", + "properties": { + "license": { + "$ref": "#/definitions/license.Info" + }, + "projects": { + "type": "integer" + }, + "shares": { + "$ref": "#/definitions/admin.ShareCounts" + }, + "tasks": { + "type": "integer" + }, + "teams": { + "type": "integer" + }, + "users": { + "type": "integer" + } + } + }, + "admin.OwnerPatch": { + "type": "object", + "properties": { + "owner_id": { + "type": "integer" + } + } + }, + "admin.ShareCounts": { + "type": "object", + "properties": { + "link_shares": { + "type": "integer" + }, + "team_shares": { + "type": "integer" + }, + "user_shares": { + "type": "integer" + } + } + }, + "admin.StatusPatch": { + "type": "object", + "properties": { + "status": { + "description": "Pointer to distinguish \"omitted\" from StatusActive; an empty body would silently re-enable otherwise.", + "allOf": [ + { + "$ref": "#/definitions/user.Status" + } + ] + } + } + }, + "admin.User": { + "type": "object", + "properties": { + "auth_provider": { + "type": "string" + }, + "created": { + "description": "A timestamp when this task was created. You cannot change this value.", + "type": "string" + }, + "email": { + "description": "The user's email address.", + "type": "string", + "maxLength": 250 + }, + "id": { + "description": "The unique, numeric id of this user.", + "type": "integer" + }, + "is_admin": { + "type": "boolean" + }, + "issuer": { + "type": "string" + }, + "name": { + "description": "The full name of the user.", + "type": "string" + }, + "status": { + "$ref": "#/definitions/user.Status" + }, + "subject": { + "type": "string" + }, + "updated": { + "description": "A timestamp when this task was last updated. You cannot change this value.", + "type": "string" + }, + "username": { + "description": "The username of the user. Is always unique.", + "type": "string", + "maxLength": 250, + "minLength": 1 + } + } + }, "auth.Token": { "type": "object", "properties": { @@ -8695,6 +9252,50 @@ const docTemplate = `{ } } }, + "license.Feature": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "FeatureUnknown", + "FeatureAdminPanel", + "FeatureTimeTracking", + "FeatureAuditLogs" + ] + }, + "license.Info": { + "type": "object", + "properties": { + "expires_at": { + "type": "string" + }, + "features": { + "type": "array", + "items": { + "type": "string" + } + }, + "instance_id": { + "type": "string" + }, + "last_check_failed": { + "type": "boolean" + }, + "licensed": { + "type": "boolean" + }, + "max_users": { + "type": "integer" + }, + "validated_at": { + "type": "string" + } + } + }, "microsofttodo.Migration": { "type": "object", "properties": { @@ -10232,6 +10833,21 @@ const docTemplate = `{ } } }, + "user.Status": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "StatusActive", + "StatusEmailConfirmationRequired", + "StatusDisabled", + "StatusAccountLocked" + ] + }, "user.TOTP": { "type": "object", "properties": { @@ -10466,6 +11082,9 @@ const docTemplate = `{ "description": "The unique, numeric id of this user.", "type": "integer" }, + "is_admin": { + "type": "boolean" + }, "is_local_user": { "type": "boolean" }, @@ -10573,6 +11192,12 @@ const docTemplate = `{ "type": "string" } }, + "enabled_pro_features": { + "type": "array", + "items": { + "$ref": "#/definitions/license.Feature" + } + }, "frontend_url": { "type": "string" }, diff --git a/pkg/swagger/swagger.json b/pkg/swagger/swagger.json index 93b1eef4d..270d5343c 100644 --- a/pkg/swagger/swagger.json +++ b/pkg/swagger/swagger.json @@ -15,6 +15,411 @@ }, "basePath": "/api/v1", "paths": { + "/admin/overview": { + "get": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Returns per-instance counts (users, projects, shares) plus version and license info. Instance-admin only, gated by the admin_panel feature.", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Admin overview", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.Overview" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/projects": { + "get": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Paginated list of every project on the instance, regardless of ownership.", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List projects (admin)", + "parameters": [ + { + "type": "integer", + "description": "Page number, defaults to 1.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Items per page, defaults to the service setting.", + "name": "per_page", + "in": "query" + }, + { + "type": "string", + "description": "Search projects by title, description or identifier.", + "name": "s", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Project" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/projects/{id}/owner": { + "patch": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Reassign a project's owner. The existing update endpoint doesn't allow owner changes — this is the admin-only escape hatch.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Reassign project owner (admin)", + "parameters": [ + { + "type": "integer", + "description": "Project ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "New owner", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.OwnerPatch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Project" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/users": { + "get": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Paginated list of all users on the instance. Supports search by username/email. Exposes fields hidden from the normal user API (is_admin, status).", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List users (admin)", + "parameters": [ + { + "type": "string", + "description": "Search string matched against username and email.", + "name": "s", + "in": "query" + }, + { + "type": "integer", + "description": "Page number, defaults to 1.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Items per page, defaults to the service setting.", + "name": "per_page", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/admin.User" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + }, + "post": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Create a new local user account. Respects the admin-only fields `is_admin` and `skip_email_confirm`. The public registration toggle is bypassed.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create a user (admin)", + "parameters": [ + { + "description": "The user to create", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.CreateUserBody" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/users/{id}": { + "delete": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Delete a user. With mode=now the user is removed immediately. With mode=scheduled (the default, matching the CLI) the user receives a confirmation email and is scheduled for deletion just like a self-initiated account deletion.", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete a user (admin)", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Deletion mode: 'now' for immediate deletion, 'scheduled' (default) to trigger the email-confirmation self-deletion flow.", + "name": "mode", + "in": "query" + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/users/{id}/admin": { + "patch": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Toggle the instance-admin flag on a user. Demoting the last remaining admin is refused with 400.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Promote or demote a user (admin)", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "New admin value", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.IsAdminPatch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, + "/admin/users/{id}/status": { + "patch": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Change a user's status without requiring them to log in.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Set a user's status (admin)", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Status", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.StatusPatch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.User" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/web.HTTPError" + } + } + } + } + }, "/auth/openid/{provider}/callback": { "post": { "security": [ @@ -8471,6 +8876,158 @@ } }, "definitions": { + "admin.CreateUserBody": { + "type": "object", + "properties": { + "email": { + "description": "The user's email address", + "type": "string", + "maxLength": 250 + }, + "is_admin": { + "description": "Mark the new user as an instance admin.", + "type": "boolean" + }, + "language": { + "description": "The language of the new user. Must be a valid IETF BCP 47 language code and exist in Vikunja.", + "type": "string" + }, + "name": { + "description": "The full name of the new user. Optional.", + "type": "string" + }, + "password": { + "description": "The user's password in clear text. Only used when registering the user. The maximum limi is 72 bytes, which may be less than 72 characters. This is due to the limit in the bcrypt hashing algorithm used to store passwords in Vikunja.", + "type": "string", + "maxLength": 72, + "minLength": 8 + }, + "skip_email_confirm": { + "description": "Activate the new user immediately without email confirmation.", + "type": "boolean" + }, + "username": { + "description": "The user's username. Cannot contain anything that looks like an url or whitespaces.", + "type": "string", + "maxLength": 250, + "minLength": 3 + } + } + }, + "admin.IsAdminPatch": { + "type": "object", + "properties": { + "is_admin": { + "description": "Pointer to distinguish \"omitted\" from false; an empty body would silently demote otherwise.", + "type": "boolean" + } + } + }, + "admin.Overview": { + "type": "object", + "properties": { + "license": { + "$ref": "#/definitions/license.Info" + }, + "projects": { + "type": "integer" + }, + "shares": { + "$ref": "#/definitions/admin.ShareCounts" + }, + "tasks": { + "type": "integer" + }, + "teams": { + "type": "integer" + }, + "users": { + "type": "integer" + } + } + }, + "admin.OwnerPatch": { + "type": "object", + "properties": { + "owner_id": { + "type": "integer" + } + } + }, + "admin.ShareCounts": { + "type": "object", + "properties": { + "link_shares": { + "type": "integer" + }, + "team_shares": { + "type": "integer" + }, + "user_shares": { + "type": "integer" + } + } + }, + "admin.StatusPatch": { + "type": "object", + "properties": { + "status": { + "description": "Pointer to distinguish \"omitted\" from StatusActive; an empty body would silently re-enable otherwise.", + "allOf": [ + { + "$ref": "#/definitions/user.Status" + } + ] + } + } + }, + "admin.User": { + "type": "object", + "properties": { + "auth_provider": { + "type": "string" + }, + "created": { + "description": "A timestamp when this task was created. You cannot change this value.", + "type": "string" + }, + "email": { + "description": "The user's email address.", + "type": "string", + "maxLength": 250 + }, + "id": { + "description": "The unique, numeric id of this user.", + "type": "integer" + }, + "is_admin": { + "type": "boolean" + }, + "issuer": { + "type": "string" + }, + "name": { + "description": "The full name of the user.", + "type": "string" + }, + "status": { + "$ref": "#/definitions/user.Status" + }, + "subject": { + "type": "string" + }, + "updated": { + "description": "A timestamp when this task was last updated. You cannot change this value.", + "type": "string" + }, + "username": { + "description": "The username of the user. Is always unique.", + "type": "string", + "maxLength": 250, + "minLength": 1 + } + } + }, "auth.Token": { "type": "object", "properties": { @@ -8687,6 +9244,50 @@ } } }, + "license.Feature": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "FeatureUnknown", + "FeatureAdminPanel", + "FeatureTimeTracking", + "FeatureAuditLogs" + ] + }, + "license.Info": { + "type": "object", + "properties": { + "expires_at": { + "type": "string" + }, + "features": { + "type": "array", + "items": { + "type": "string" + } + }, + "instance_id": { + "type": "string" + }, + "last_check_failed": { + "type": "boolean" + }, + "licensed": { + "type": "boolean" + }, + "max_users": { + "type": "integer" + }, + "validated_at": { + "type": "string" + } + } + }, "microsofttodo.Migration": { "type": "object", "properties": { @@ -10224,6 +10825,21 @@ } } }, + "user.Status": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "StatusActive", + "StatusEmailConfirmationRequired", + "StatusDisabled", + "StatusAccountLocked" + ] + }, "user.TOTP": { "type": "object", "properties": { @@ -10458,6 +11074,9 @@ "description": "The unique, numeric id of this user.", "type": "integer" }, + "is_admin": { + "type": "boolean" + }, "is_local_user": { "type": "boolean" }, @@ -10565,6 +11184,12 @@ "type": "string" } }, + "enabled_pro_features": { + "type": "array", + "items": { + "$ref": "#/definitions/license.Feature" + } + }, "frontend_url": { "type": "string" }, diff --git a/pkg/swagger/swagger.yaml b/pkg/swagger/swagger.yaml index b708cc619..6d0b62a65 100644 --- a/pkg/swagger/swagger.yaml +++ b/pkg/swagger/swagger.yaml @@ -1,5 +1,119 @@ basePath: /api/v1 definitions: + admin.CreateUserBody: + properties: + email: + description: The user's email address + maxLength: 250 + type: string + is_admin: + description: Mark the new user as an instance admin. + type: boolean + language: + description: The language of the new user. Must be a valid IETF BCP 47 language + code and exist in Vikunja. + type: string + name: + description: The full name of the new user. Optional. + type: string + password: + description: The user's password in clear text. Only used when registering + the user. The maximum limi is 72 bytes, which may be less than 72 characters. + This is due to the limit in the bcrypt hashing algorithm used to store passwords + in Vikunja. + maxLength: 72 + minLength: 8 + type: string + skip_email_confirm: + description: Activate the new user immediately without email confirmation. + type: boolean + username: + description: The user's username. Cannot contain anything that looks like + an url or whitespaces. + maxLength: 250 + minLength: 3 + type: string + type: object + admin.IsAdminPatch: + properties: + is_admin: + description: Pointer to distinguish "omitted" from false; an empty body would + silently demote otherwise. + type: boolean + type: object + admin.Overview: + properties: + license: + $ref: '#/definitions/license.Info' + projects: + type: integer + shares: + $ref: '#/definitions/admin.ShareCounts' + tasks: + type: integer + teams: + type: integer + users: + type: integer + type: object + admin.OwnerPatch: + properties: + owner_id: + type: integer + type: object + admin.ShareCounts: + properties: + link_shares: + type: integer + team_shares: + type: integer + user_shares: + type: integer + type: object + admin.StatusPatch: + properties: + status: + allOf: + - $ref: '#/definitions/user.Status' + description: Pointer to distinguish "omitted" from StatusActive; an empty + body would silently re-enable otherwise. + type: object + admin.User: + properties: + auth_provider: + type: string + created: + description: A timestamp when this task was created. You cannot change this + value. + type: string + email: + description: The user's email address. + maxLength: 250 + type: string + id: + description: The unique, numeric id of this user. + type: integer + is_admin: + type: boolean + issuer: + type: string + name: + description: The full name of the user. + type: string + status: + $ref: '#/definitions/user.Status' + subject: + type: string + updated: + description: A timestamp when this task was last updated. You cannot change + this value. + type: string + username: + description: The username of the user. Is always unique. + maxLength: 250 + minLength: 1 + type: string + type: object auth.Token: properties: token: @@ -149,6 +263,37 @@ definitions: url: type: string type: object + license.Feature: + enum: + - 0 + - 1 + - 2 + - 3 + type: integer + x-enum-varnames: + - FeatureUnknown + - FeatureAdminPanel + - FeatureTimeTracking + - FeatureAuditLogs + license.Info: + properties: + expires_at: + type: string + features: + items: + type: string + type: array + instance_id: + type: string + last_check_failed: + type: boolean + licensed: + type: boolean + max_users: + type: integer + validated_at: + type: string + type: object microsofttodo.Migration: properties: code: @@ -1335,6 +1480,18 @@ definitions: maxLength: 250 type: string type: object + user.Status: + enum: + - 0 + - 1 + - 2 + - 3 + type: integer + x-enum-varnames: + - StatusActive + - StatusEmailConfirmationRequired + - StatusDisabled + - StatusAccountLocked user.TOTP: properties: enabled: @@ -1520,6 +1677,8 @@ definitions: id: description: The unique, numeric id of this user. type: integer + is_admin: + type: boolean is_local_user: type: boolean name: @@ -1592,6 +1751,10 @@ definitions: items: type: string type: array + enabled_pro_features: + items: + $ref: '#/definitions/license.Feature' + type: array frontend_url: type: string legal: @@ -1811,6 +1974,272 @@ paths: summary: User Avatar tags: - user + /admin/overview: + get: + description: Returns per-instance counts (users, projects, shares) plus version + and license info. Instance-admin only, gated by the admin_panel feature. + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/admin.Overview' + "404": + description: Not Found + schema: + $ref: '#/definitions/web.HTTPError' + security: + - JWTKeyAuth: [] + summary: Admin overview + tags: + - admin + /admin/projects: + get: + description: Paginated list of every project on the instance, regardless of + ownership. + parameters: + - description: Page number, defaults to 1. + in: query + name: page + type: integer + - description: Items per page, defaults to the service setting. + in: query + name: per_page + type: integer + - description: Search projects by title, description or identifier. + in: query + name: s + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Project' + type: array + "404": + description: Not Found + schema: + $ref: '#/definitions/web.HTTPError' + security: + - JWTKeyAuth: [] + summary: List projects (admin) + tags: + - admin + /admin/projects/{id}/owner: + patch: + consumes: + - application/json + description: Reassign a project's owner. The existing update endpoint doesn't + allow owner changes — this is the admin-only escape hatch. + parameters: + - description: Project ID + in: path + name: id + required: true + type: integer + - description: New owner + in: body + name: body + required: true + schema: + $ref: '#/definitions/admin.OwnerPatch' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.Project' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.HTTPError' + "404": + description: Not Found + schema: + $ref: '#/definitions/web.HTTPError' + security: + - JWTKeyAuth: [] + summary: Reassign project owner (admin) + tags: + - admin + /admin/users: + get: + description: Paginated list of all users on the instance. Supports search by + username/email. Exposes fields hidden from the normal user API (is_admin, + status). + parameters: + - description: Search string matched against username and email. + in: query + name: s + type: string + - description: Page number, defaults to 1. + in: query + name: page + type: integer + - description: Items per page, defaults to the service setting. + in: query + name: per_page + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/admin.User' + type: array + "404": + description: Not Found + schema: + $ref: '#/definitions/web.HTTPError' + security: + - JWTKeyAuth: [] + summary: List users (admin) + tags: + - admin + post: + consumes: + - application/json + description: Create a new local user account. Respects the admin-only fields + `is_admin` and `skip_email_confirm`. The public registration toggle is bypassed. + parameters: + - description: The user to create + in: body + name: body + required: true + schema: + $ref: '#/definitions/admin.CreateUserBody' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/admin.User' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.HTTPError' + security: + - JWTKeyAuth: [] + summary: Create a user (admin) + tags: + - admin + /admin/users/{id}: + delete: + description: Delete a user. With mode=now the user is removed immediately. With + mode=scheduled (the default, matching the CLI) the user receives a confirmation + email and is scheduled for deletion just like a self-initiated account deletion. + parameters: + - description: User ID + in: path + name: id + required: true + type: integer + - description: 'Deletion mode: ''now'' for immediate deletion, ''scheduled'' + (default) to trigger the email-confirmation self-deletion flow.' + in: query + name: mode + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.HTTPError' + "404": + description: Not Found + schema: + $ref: '#/definitions/web.HTTPError' + security: + - JWTKeyAuth: [] + summary: Delete a user (admin) + tags: + - admin + /admin/users/{id}/admin: + patch: + consumes: + - application/json + description: Toggle the instance-admin flag on a user. Demoting the last remaining + admin is refused with 400. + parameters: + - description: User ID + in: path + name: id + required: true + type: integer + - description: New admin value + in: body + name: body + required: true + schema: + $ref: '#/definitions/admin.IsAdminPatch' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/admin.User' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.HTTPError' + "404": + description: Not Found + schema: + $ref: '#/definitions/web.HTTPError' + security: + - JWTKeyAuth: [] + summary: Promote or demote a user (admin) + tags: + - admin + /admin/users/{id}/status: + patch: + consumes: + - application/json + description: Change a user's status without requiring them to log in. + parameters: + - description: User ID + in: path + name: id + required: true + type: integer + - description: Status + in: body + name: body + required: true + schema: + $ref: '#/definitions/admin.StatusPatch' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/admin.User' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.HTTPError' + "404": + description: Not Found + schema: + $ref: '#/definitions/web.HTTPError' + security: + - JWTKeyAuth: [] + summary: Set a user's status (admin) + tags: + - admin /auth/openid/{provider}/callback: post: consumes: