- 迁移 v10 新增 nav_links 与 nav_link_translations 两张表,文案按语言存储 - 公开 GET /api/nav-links 返回启用链接(按 sort/id 排序);管理员可增删改查与启停 - URL 仅允许站内路径、http(s) 与 mailto,拒绝 javascript: 等危险协议;译文替换与校验 - 前端删除写死的“主页”按钮,头部按当前语言渲染动态链接,新窗口加 rel=noopener noreferrer - 后台管理页新增“头部导航链接”卡片(三语文案、URL、打开方式、排序、状态),改动即时生效 - 补充 nav 接口与迁移测试、三语文案,重新生成 Swagger 文档
2613 lines
91 KiB
JSON
2613 lines
91 KiB
JSON
{
|
|
"swagger": "2.0",
|
|
"info": {
|
|
"description": "Rill server HTTP API documentation. All endpoints are prefixed with api.prefix (default /api); requests and responses are JSON.\nSwagger UI: {prefix}/swagger/index.html; OpenAPI JSON: {prefix}/swagger/doc.json.\nEndpoints are grouped by required permission: public needs no authentication, user needs a Bearer JWT obtained from /auth/login, admin needs an admin user.",
|
|
"title": "Rill API",
|
|
"contact": {},
|
|
"version": "1.0"
|
|
},
|
|
"basePath": "/api",
|
|
"paths": {
|
|
"/auth/login": {
|
|
"post": {
|
|
"description": "Login with username or email; returns a JWT (TTL from auth.token_ttl) and the user.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"public"
|
|
],
|
|
"summary": "Login",
|
|
"parameters": [
|
|
{
|
|
"description": "Login credentials",
|
|
"name": "credentials",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/auth.LoginRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/auth.LoginResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "incorrect account or password",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/auth/register": {
|
|
"post": {
|
|
"description": "Public registration. Creates a regular user in the default user group (id 1). username is 3-50 chars and unique; email is unique; password is 6-72 chars.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"public"
|
|
],
|
|
"summary": "Register",
|
|
"parameters": [
|
|
{
|
|
"description": "Registration payload",
|
|
"name": "user",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/auth.RegisterRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "username or email already exists",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/files": {
|
|
"post": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Upload a file (multipart field file). Content is deduplicated by sha256; the returned file has ref_count 0 until a business reference is acquired. Size limit from storage.max_size_mb.",
|
|
"consumes": [
|
|
"multipart/form-data"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Upload a file",
|
|
"parameters": [
|
|
{
|
|
"type": "file",
|
|
"description": "File content",
|
|
"name": "file",
|
|
"in": "formData",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.File"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request or empty file",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"413": {
|
|
"description": "file too large",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/files/{id}": {
|
|
"get": {
|
|
"description": "Public file content. Images, videos, audio, PDF and plain text are served inline; other types are served as attachments. Disabled files return 404.",
|
|
"produces": [
|
|
"application/octet-stream"
|
|
],
|
|
"tags": [
|
|
"public"
|
|
],
|
|
"summary": "Get file content",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "File ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"type": "file"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Delete a file physically and keep the record with status 0; uploader or admin only. Files still referenced (ref_count \u003e 0) return 409.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Delete a file",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "File ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"204": {
|
|
"description": "Deleted"
|
|
},
|
|
"400": {
|
|
"description": "invalid id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "permission denied or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "file is in use",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/health": {
|
|
"get": {
|
|
"description": "Check service and database connectivity; returns 503 when the database is unavailable.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"public"
|
|
],
|
|
"summary": "Health check",
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/api.HealthResponse"
|
|
}
|
|
},
|
|
"503": {
|
|
"description": "Service Unavailable",
|
|
"schema": {
|
|
"$ref": "#/definitions/api.HealthResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/me": {
|
|
"get": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Return the authenticated user's profile, including groups.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Get current user profile",
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"put": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Update the authenticated user's nickname, gender, and birthday. birthday accepts YYYY-MM-DD, an empty string clears it, and omitting it keeps the current value.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Update current user profile",
|
|
"parameters": [
|
|
{
|
|
"description": "Fields to update",
|
|
"name": "profile",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/auth.UpdateProfileRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/me/avatar": {
|
|
"put": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Upload an image as the authenticated user's avatar (multipart field file, image only). The avatar URL is stored on the user and the file reference count is managed automatically.",
|
|
"consumes": [
|
|
"multipart/form-data"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Update current user avatar",
|
|
"parameters": [
|
|
{
|
|
"type": "file",
|
|
"description": "Avatar image",
|
|
"name": "file",
|
|
"in": "formData",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request, empty file, or not an image",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"413": {
|
|
"description": "file too large",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Clear the authenticated user's avatar and release the file reference when it points to a local file.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Delete current user avatar",
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/nav-links": {
|
|
"get": {
|
|
"description": "Public header navigation links (status enabled), ordered by sort ASC then id ASC, with translations.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"public"
|
|
],
|
|
"summary": "List nav links",
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/model.NavLink"
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Admin only. Create a header navigation link. url accepts site-relative paths (/...), http(s) URLs, and mailto links; translations must include at least one non-empty label.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Create a nav link",
|
|
"parameters": [
|
|
{
|
|
"description": "Nav link payload",
|
|
"name": "link",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/nav.Request"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.NavLink"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/nav-links/list": {
|
|
"get": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Admin only. List all header navigation links including disabled ones, with translations.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "List all nav links",
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/model.NavLink"
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/nav-links/{id}": {
|
|
"put": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Admin only. Update a header navigation link; translations are replaced by the provided list.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Update a nav link",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "Nav link ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Nav link payload",
|
|
"name": "link",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/nav.Request"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.NavLink"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request or id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Admin only. Delete a header navigation link and its translations.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Delete a nav link",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "Nav link ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"204": {
|
|
"description": "Deleted"
|
|
},
|
|
"400": {
|
|
"description": "invalid id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/notes": {
|
|
"get": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "List notes ordered by id DESC. page starts at 1; page_size is 1-100, default 20.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "List notes",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "Page number, default 1",
|
|
"name": "page",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"example": 20,
|
|
"description": "Page size, default 20, max 100",
|
|
"name": "page_size",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/note.ListResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Create a note. title is required (max 200 chars); content is optional.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Create a note",
|
|
"parameters": [
|
|
{
|
|
"description": "Note payload",
|
|
"name": "note",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/note.Request"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.Note"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/notes/{id}": {
|
|
"get": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Get a note by id.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Get a note",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "Note ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.Note"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"put": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Update title and content; validation is the same as create.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Update a note",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "Note ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Note payload",
|
|
"name": "note",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/note.Request"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.Note"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request or id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Delete a note by id; returns 204 with no body on success.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"user"
|
|
],
|
|
"summary": "Delete a note",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "Note ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"204": {
|
|
"description": "Deleted"
|
|
},
|
|
"400": {
|
|
"description": "invalid id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/site": {
|
|
"get": {
|
|
"description": "Public site settings: site name, logo URL, and footer text. Returns built-in defaults when the settings row is missing.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"public"
|
|
],
|
|
"summary": "Get site settings",
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.SiteSetting"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"put": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Admin only. Update the site name, logo URL, and footer text; logo files hosted on this site have their reference count managed automatically. Returns the updated settings.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Update site settings",
|
|
"parameters": [
|
|
{
|
|
"description": "Site settings",
|
|
"name": "site",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/site.UpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.SiteSetting"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request or logo file not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/site/logo": {
|
|
"put": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Admin only. Upload an image as the site logo (multipart field file, image only); the logo takes effect immediately and file references are managed automatically.",
|
|
"consumes": [
|
|
"multipart/form-data"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Upload site logo",
|
|
"parameters": [
|
|
{
|
|
"type": "file",
|
|
"description": "Logo image",
|
|
"name": "file",
|
|
"in": "formData",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.SiteSetting"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request, empty file, or not an image",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"413": {
|
|
"description": "file too large",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Admin only. Clear the site logo and release the reference of the locally stored logo file.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Delete site logo",
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.SiteSetting"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/user-groups": {
|
|
"get": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "List user groups ordered by id ASC. page starts at 1; page_size is 1-100, default 20.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "List user groups",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "Page number, default 1",
|
|
"name": "page",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"example": 20,
|
|
"description": "Page size, default 20, max 100",
|
|
"name": "page_size",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/usergroup.ListResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Create a user group. name is required and unique (max 50 chars); description is optional (max 255 chars); id is assigned by the server.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Create a user group",
|
|
"parameters": [
|
|
{
|
|
"description": "User group payload",
|
|
"name": "group",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/usergroup.Request"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.UserGroup"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "user group name already exists",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/user-groups/{id}": {
|
|
"get": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Get a user group by id; id 0 is the built-in admin group.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Get a user group",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "User group ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.UserGroup"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"put": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Update a user group's name and description; name is required and unique.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Update a user group",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "User group ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "User group payload",
|
|
"name": "group",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/usergroup.Request"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.UserGroup"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request or id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "user group name already exists",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Delete a user group by id; returns 204 with no body on success. Returns 409 for system groups or when the group still has members.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Delete a user group",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 2,
|
|
"description": "User group ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"204": {
|
|
"description": "Deleted"
|
|
},
|
|
"400": {
|
|
"description": "invalid id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "system group cannot be deleted or group still has members",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/users": {
|
|
"get": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "List users with their groups, ordered by id DESC. page starts at 1; page_size is 1-100, default 20.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "List users",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "Page number, default 1",
|
|
"name": "page",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"example": 20,
|
|
"description": "Page size, default 20, max 100",
|
|
"name": "page_size",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/user.ListResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Create a user and assign groups. username and email are unique; password is 6-72 chars; gender is male/female/other; birthday is YYYY-MM-DD and cannot be in the future; defaults to the regular user group (id 1) when group_ids is omitted.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Create a user",
|
|
"parameters": [
|
|
{
|
|
"description": "User payload",
|
|
"name": "user",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/user.CreateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request or user group not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "username or email already exists",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/users/{id}": {
|
|
"get": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Get a user by id, including groups.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Get a user",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "User ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"put": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Update user fields. group_ids replaces all groups; a non-empty password resets the password; birthday accepts YYYY-MM-DD, an empty string clears it, and omitting it keeps the current value.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Update a user",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "User ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Fields to update",
|
|
"name": "user",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/user.UpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "invalid request, id, or user group not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"security": [
|
|
{
|
|
"BearerAuth": []
|
|
}
|
|
],
|
|
"description": "Delete a user and their group memberships; returns 204 with no body on success.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"admin"
|
|
],
|
|
"summary": "Delete a user",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"example": 1,
|
|
"description": "User ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"204": {
|
|
"description": "Deleted"
|
|
},
|
|
"400": {
|
|
"description": "invalid id",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "unauthorized or session expired",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "admin permission required or account disabled",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "record not found",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"$ref": "#/definitions/httpx.ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"definitions": {
|
|
"api.HealthResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"error": {
|
|
"type": "string",
|
|
"example": "database unavailable"
|
|
},
|
|
"status": {
|
|
"type": "string",
|
|
"example": "ok"
|
|
}
|
|
}
|
|
},
|
|
"auth.LoginRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"account",
|
|
"password"
|
|
],
|
|
"properties": {
|
|
"account": {
|
|
"type": "string",
|
|
"example": "alice"
|
|
},
|
|
"password": {
|
|
"type": "string",
|
|
"example": "secret123"
|
|
}
|
|
}
|
|
},
|
|
"auth.LoginResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"expires_at": {
|
|
"type": "string",
|
|
"example": "2026-09-21T10:00:00+08:00"
|
|
},
|
|
"token": {
|
|
"type": "string",
|
|
"example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
},
|
|
"user": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
}
|
|
},
|
|
"auth.RegisterRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"email",
|
|
"password",
|
|
"username"
|
|
],
|
|
"properties": {
|
|
"email": {
|
|
"type": "string",
|
|
"maxLength": 255,
|
|
"example": "alice@example.com"
|
|
},
|
|
"password": {
|
|
"type": "string",
|
|
"maxLength": 72,
|
|
"minLength": 6,
|
|
"example": "secret123"
|
|
},
|
|
"username": {
|
|
"type": "string",
|
|
"maxLength": 50,
|
|
"minLength": 3,
|
|
"example": "alice"
|
|
}
|
|
}
|
|
},
|
|
"auth.UpdateProfileRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"birthday": {
|
|
"type": "string",
|
|
"example": "1995-06-15"
|
|
},
|
|
"gender": {
|
|
"type": "string",
|
|
"example": "male"
|
|
},
|
|
"nickname": {
|
|
"type": "string",
|
|
"maxLength": 50,
|
|
"example": "Alice"
|
|
}
|
|
}
|
|
},
|
|
"httpx.ErrorResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"error": {
|
|
"type": "string",
|
|
"example": "record not found"
|
|
}
|
|
}
|
|
},
|
|
"model.File": {
|
|
"type": "object",
|
|
"properties": {
|
|
"created_at": {
|
|
"type": "string"
|
|
},
|
|
"extension": {
|
|
"type": "string"
|
|
},
|
|
"hash": {
|
|
"type": "string"
|
|
},
|
|
"id": {
|
|
"type": "integer"
|
|
},
|
|
"last_referenced_at": {
|
|
"type": "string"
|
|
},
|
|
"metadata": {
|
|
"type": "string"
|
|
},
|
|
"mime_type": {
|
|
"type": "string"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"path": {
|
|
"type": "string"
|
|
},
|
|
"ref_count": {
|
|
"type": "integer"
|
|
},
|
|
"size": {
|
|
"type": "integer"
|
|
},
|
|
"status": {
|
|
"type": "integer"
|
|
},
|
|
"storage": {
|
|
"type": "string"
|
|
},
|
|
"updated_at": {
|
|
"type": "string"
|
|
},
|
|
"uploader_id": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"model.NavLink": {
|
|
"type": "object",
|
|
"properties": {
|
|
"created_at": {
|
|
"type": "string"
|
|
},
|
|
"id": {
|
|
"type": "integer"
|
|
},
|
|
"open_in_new_window": {
|
|
"type": "boolean"
|
|
},
|
|
"sort": {
|
|
"type": "integer"
|
|
},
|
|
"status": {
|
|
"type": "integer"
|
|
},
|
|
"translations": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/model.NavLinkTranslation"
|
|
}
|
|
},
|
|
"updated_at": {
|
|
"type": "string"
|
|
},
|
|
"url": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"model.NavLinkTranslation": {
|
|
"type": "object",
|
|
"properties": {
|
|
"label": {
|
|
"type": "string"
|
|
},
|
|
"locale": {
|
|
"type": "string"
|
|
},
|
|
"nav_link_id": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"model.Note": {
|
|
"type": "object",
|
|
"properties": {
|
|
"content": {
|
|
"type": "string"
|
|
},
|
|
"created_at": {
|
|
"type": "string"
|
|
},
|
|
"id": {
|
|
"type": "integer"
|
|
},
|
|
"title": {
|
|
"type": "string"
|
|
},
|
|
"updated_at": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"model.SiteSetting": {
|
|
"type": "object",
|
|
"properties": {
|
|
"footer": {
|
|
"type": "string",
|
|
"example": "Copyright © Rill"
|
|
},
|
|
"logo": {
|
|
"type": "string",
|
|
"example": "https://example.com/logo.png"
|
|
},
|
|
"site_name": {
|
|
"type": "string",
|
|
"example": "Rill"
|
|
},
|
|
"updated_at": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"model.User": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar": {
|
|
"type": "string"
|
|
},
|
|
"birthday": {
|
|
"type": "string",
|
|
"example": "1995-06-15"
|
|
},
|
|
"created_at": {
|
|
"type": "string"
|
|
},
|
|
"email": {
|
|
"type": "string"
|
|
},
|
|
"gender": {
|
|
"type": "string",
|
|
"example": "male"
|
|
},
|
|
"groups": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/model.UserGroup"
|
|
}
|
|
},
|
|
"id": {
|
|
"type": "integer"
|
|
},
|
|
"nickname": {
|
|
"type": "string"
|
|
},
|
|
"status": {
|
|
"type": "integer"
|
|
},
|
|
"updated_at": {
|
|
"type": "string"
|
|
},
|
|
"username": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"model.UserGroup": {
|
|
"type": "object",
|
|
"properties": {
|
|
"created_at": {
|
|
"type": "string"
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"id": {
|
|
"type": "integer"
|
|
},
|
|
"is_system": {
|
|
"type": "boolean"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"updated_at": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"nav.Request": {
|
|
"type": "object",
|
|
"required": [
|
|
"translations",
|
|
"url"
|
|
],
|
|
"properties": {
|
|
"open_in_new_window": {
|
|
"type": "boolean",
|
|
"example": false
|
|
},
|
|
"sort": {
|
|
"type": "integer",
|
|
"example": 0
|
|
},
|
|
"status": {
|
|
"type": "integer",
|
|
"enum": [
|
|
0,
|
|
1
|
|
],
|
|
"example": 1
|
|
},
|
|
"translations": {
|
|
"type": "array",
|
|
"minItems": 1,
|
|
"items": {
|
|
"$ref": "#/definitions/nav.TranslationRequest"
|
|
}
|
|
},
|
|
"url": {
|
|
"type": "string",
|
|
"maxLength": 512,
|
|
"example": "/profile"
|
|
}
|
|
}
|
|
},
|
|
"nav.TranslationRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"label",
|
|
"locale"
|
|
],
|
|
"properties": {
|
|
"label": {
|
|
"type": "string",
|
|
"maxLength": 100,
|
|
"example": "首页"
|
|
},
|
|
"locale": {
|
|
"type": "string",
|
|
"maxLength": 10,
|
|
"example": "zh-CN"
|
|
}
|
|
}
|
|
},
|
|
"note.ListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/model.Note"
|
|
}
|
|
},
|
|
"page": {
|
|
"type": "integer",
|
|
"example": 1
|
|
},
|
|
"page_size": {
|
|
"type": "integer",
|
|
"example": 20
|
|
},
|
|
"total": {
|
|
"type": "integer",
|
|
"example": 42
|
|
}
|
|
}
|
|
},
|
|
"note.Request": {
|
|
"type": "object",
|
|
"required": [
|
|
"title"
|
|
],
|
|
"properties": {
|
|
"content": {
|
|
"type": "string",
|
|
"example": "Milk, eggs"
|
|
},
|
|
"title": {
|
|
"type": "string",
|
|
"maxLength": 200,
|
|
"example": "Shopping list"
|
|
}
|
|
}
|
|
},
|
|
"site.UpdateRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"site_name"
|
|
],
|
|
"properties": {
|
|
"footer": {
|
|
"type": "string",
|
|
"maxLength": 1000,
|
|
"example": "Copyright © Rill"
|
|
},
|
|
"logo": {
|
|
"type": "string",
|
|
"maxLength": 500,
|
|
"example": "https://example.com/logo.png"
|
|
},
|
|
"site_name": {
|
|
"type": "string",
|
|
"maxLength": 100,
|
|
"example": "Rill"
|
|
}
|
|
}
|
|
},
|
|
"user.CreateRequest": {
|
|
"type": "object",
|
|
"required": [
|
|
"email",
|
|
"password",
|
|
"username"
|
|
],
|
|
"properties": {
|
|
"avatar": {
|
|
"type": "string",
|
|
"maxLength": 255,
|
|
"example": "https://example.com/avatar.png"
|
|
},
|
|
"birthday": {
|
|
"type": "string",
|
|
"example": "1995-06-15"
|
|
},
|
|
"email": {
|
|
"type": "string",
|
|
"maxLength": 255,
|
|
"example": "alice@example.com"
|
|
},
|
|
"gender": {
|
|
"type": "string",
|
|
"enum": [
|
|
"male",
|
|
"female",
|
|
"other"
|
|
],
|
|
"example": "male"
|
|
},
|
|
"group_ids": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "integer"
|
|
},
|
|
"example": [
|
|
1
|
|
]
|
|
},
|
|
"nickname": {
|
|
"type": "string",
|
|
"maxLength": 50,
|
|
"example": "Alice"
|
|
},
|
|
"password": {
|
|
"type": "string",
|
|
"maxLength": 72,
|
|
"minLength": 6,
|
|
"example": "secret123"
|
|
},
|
|
"status": {
|
|
"type": "integer",
|
|
"enum": [
|
|
0,
|
|
1
|
|
],
|
|
"example": 1
|
|
},
|
|
"username": {
|
|
"type": "string",
|
|
"maxLength": 50,
|
|
"example": "alice"
|
|
}
|
|
}
|
|
},
|
|
"user.ListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/model.User"
|
|
}
|
|
},
|
|
"page": {
|
|
"type": "integer",
|
|
"example": 1
|
|
},
|
|
"page_size": {
|
|
"type": "integer",
|
|
"example": 20
|
|
},
|
|
"total": {
|
|
"type": "integer",
|
|
"example": 42
|
|
}
|
|
}
|
|
},
|
|
"user.UpdateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar": {
|
|
"type": "string",
|
|
"maxLength": 255,
|
|
"example": "https://example.com/avatar.png"
|
|
},
|
|
"birthday": {
|
|
"type": "string",
|
|
"example": "1995-06-15"
|
|
},
|
|
"gender": {
|
|
"type": "string",
|
|
"enum": [
|
|
"male",
|
|
"female",
|
|
"other"
|
|
],
|
|
"example": "male"
|
|
},
|
|
"group_ids": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "integer"
|
|
},
|
|
"example": [
|
|
1
|
|
]
|
|
},
|
|
"nickname": {
|
|
"type": "string",
|
|
"maxLength": 50,
|
|
"example": "Alice"
|
|
},
|
|
"password": {
|
|
"type": "string",
|
|
"maxLength": 72,
|
|
"minLength": 6,
|
|
"example": "secret123"
|
|
},
|
|
"status": {
|
|
"type": "integer",
|
|
"enum": [
|
|
0,
|
|
1
|
|
],
|
|
"example": 1
|
|
}
|
|
}
|
|
},
|
|
"usergroup.ListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/model.UserGroup"
|
|
}
|
|
},
|
|
"page": {
|
|
"type": "integer",
|
|
"example": 1
|
|
},
|
|
"page_size": {
|
|
"type": "integer",
|
|
"example": 20
|
|
},
|
|
"total": {
|
|
"type": "integer",
|
|
"example": 42
|
|
}
|
|
}
|
|
},
|
|
"usergroup.Request": {
|
|
"type": "object",
|
|
"required": [
|
|
"name"
|
|
],
|
|
"properties": {
|
|
"description": {
|
|
"type": "string",
|
|
"maxLength": 255,
|
|
"example": "Handles daily operations"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"maxLength": 50,
|
|
"example": "Operations"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"securityDefinitions": {
|
|
"BearerAuth": {
|
|
"description": "Bearer JWT, format: Bearer {token}, obtained from /auth/login",
|
|
"type": "apiKey",
|
|
"name": "Authorization",
|
|
"in": "header"
|
|
}
|
|
},
|
|
"tags": [
|
|
{
|
|
"description": "No authentication required",
|
|
"name": "public"
|
|
},
|
|
{
|
|
"description": "Requires a logged-in user (Bearer JWT)",
|
|
"name": "user"
|
|
},
|
|
{
|
|
"description": "Requires an admin user (Bearer JWT + admin group)",
|
|
"name": "admin"
|
|
}
|
|
]
|
|
} |