Generate an OpenAPI 3.0 spec from route annotations and serve it with Swagger UI so the full REST API is browsable and testable. - Add swagger-ui-express (runtime) and swagger-autogen (dev) deps, plus an `npm run swagger` script. - server/swagger/swagger.js: generator config with API metadata, servers, 14 tag groups, cookie + bearer security schemes, and 28 reusable component schemas. Follows the Express mount chain from src/app.js so generated paths are fully-qualified (/api/v1/...). - Annotate every route (auth, mobile, sso, public, admin, health) with #swagger tags/summaries/parameters/request bodies/security and the actual response codes each handler returns (400/401/403/404/409/429/ 302/502, multipart uploads). - Serve Swagger UI at /api/docs and the raw spec at /api/docs.json, guarded so a missing spec disables docs instead of crashing. - Commit the generated swagger-output.json so docs work with no build step; swagger-autogen stays dev-only and is not needed at runtime. - README: new "API documentation (Swagger)" section plus tech-stack and project-structure entries. Covers 51 paths / 64 operations. Existing test suite (83) still passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6059 lines
157 KiB
JSON
6059 lines
157 KiB
JSON
{
|
|
"openapi": "3.0.0",
|
|
"info": {
|
|
"title": "UOMysticmoon API",
|
|
"version": "1.0.0",
|
|
"description": "REST API for the UOMysticmoon website, wiki and admin panel — a private Ultima Online shard.\n\n### Authentication\n- **Web / admin panel** uses an httpOnly session cookie (`uomm_token`) issued by `POST /api/v1/auth/login` (plus `/login/totp` when 2FA is enabled).\n- **Native / mobile clients** use bearer access tokens from `POST /api/v1/auth/mobile/login`, refreshed via `/auth/mobile/refresh`.\n\nEndpoints under `/api/v1/admin/**` require a valid session; some are further restricted to the `admin` role (editors are limited to content)."
|
|
},
|
|
"servers": [
|
|
{
|
|
"url": "/",
|
|
"description": "Same-origin (current host)"
|
|
},
|
|
{
|
|
"url": "http://localhost:3000",
|
|
"description": "Local development"
|
|
}
|
|
],
|
|
"tags": [
|
|
{
|
|
"name": "Health",
|
|
"description": "Liveness probe"
|
|
},
|
|
{
|
|
"name": "Auth",
|
|
"description": "Web session login/logout (cookie + TOTP)"
|
|
},
|
|
{
|
|
"name": "Auth · Mobile",
|
|
"description": "Native bearer-token login, refresh and logout"
|
|
},
|
|
{
|
|
"name": "Auth · SSO",
|
|
"description": "OAuth2 / OIDC provider discovery and redirect flow"
|
|
},
|
|
{
|
|
"name": "Public",
|
|
"description": "Unauthenticated site content (settings, posts, wiki, contact)"
|
|
},
|
|
{
|
|
"name": "Admin · Account",
|
|
"description": "Self-service account security (2FA, linked identities)"
|
|
},
|
|
{
|
|
"name": "Admin · Dashboard",
|
|
"description": "Dashboard summary and site mode"
|
|
},
|
|
{
|
|
"name": "Admin · Posts",
|
|
"description": "News / five-on-friday / newsletter / screenshots + uploads"
|
|
},
|
|
{
|
|
"name": "Admin · Wiki",
|
|
"description": "Wiki pages, categories, tags and revisions"
|
|
},
|
|
{
|
|
"name": "Admin · Settings",
|
|
"description": "Site settings (admin only)"
|
|
},
|
|
{
|
|
"name": "Admin · Activity",
|
|
"description": "Admin activity log"
|
|
},
|
|
{
|
|
"name": "Admin · Bot Activity",
|
|
"description": "Bot-scoring/ban state and emergency unban (admin only)"
|
|
},
|
|
{
|
|
"name": "Admin · Auth Providers",
|
|
"description": "SSO provider configuration (admin only)"
|
|
},
|
|
{
|
|
"name": "Admin · Users",
|
|
"description": "User management (admin only)"
|
|
}
|
|
],
|
|
"paths": {
|
|
"/api/health": {
|
|
"get": {
|
|
"tags": [
|
|
"Health"
|
|
],
|
|
"summary": "Liveness probe",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Service is up",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"status": {
|
|
"type": "string",
|
|
"example": "ok"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/login": {
|
|
"post": {
|
|
"tags": [
|
|
"Auth"
|
|
],
|
|
"summary": "Log in with username and password",
|
|
"description": "On success sets the httpOnly session cookie. If the account has 2FA enabled, returns { totpRequired, challenge } instead and no cookie is set — complete login at POST /login/totp. Rate limited and behind bot/backoff guards.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Session issued, or TOTP challenge required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/LoginResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Incorrect username or password",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Too many attempts (rate limited / backoff)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/LoginRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/login/totp": {
|
|
"post": {
|
|
"tags": [
|
|
"Auth"
|
|
],
|
|
"summary": "Complete login with a TOTP code",
|
|
"description": "Second step for 2FA accounts. Exchange the challenge from /login plus the current authenticator code for a session cookie.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Session issued",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/LoginResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Invalid code or expired challenge",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Too many attempts (rate limited / backoff)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/TotpLoginRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/logout": {
|
|
"post": {
|
|
"tags": [
|
|
"Auth"
|
|
],
|
|
"summary": "Log out (clear the session cookie)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Logged out",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/me": {
|
|
"get": {
|
|
"tags": [
|
|
"Auth"
|
|
],
|
|
"summary": "Current authenticated user",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "The signed-in user",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"user": {
|
|
"$ref": "#/components/schemas/User"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/auth/mobile/login": {
|
|
"post": {
|
|
"tags": [
|
|
"Auth · Mobile"
|
|
],
|
|
"summary": "Native login → access + refresh tokens",
|
|
"description": "Bearer-token login for native clients. Single-request 2FA: if the account has TOTP on and no/invalid code is supplied, returns 401 { totpRequired: true } and the client retries with a code.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Access + refresh tokens",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/MobileTokenResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Invalid credentials, or a TOTP code is required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Too many attempts (rate limited / backoff)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/MobileLoginRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/mobile/refresh": {
|
|
"post": {
|
|
"tags": [
|
|
"Auth · Mobile"
|
|
],
|
|
"summary": "Rotate a refresh token for a fresh token pair",
|
|
"description": "Refresh tokens are single-use: the presented token is revoked and a new access + refresh pair is issued. Reusing a rotated token fails with 401.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "New access + refresh tokens",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/MobileTokenResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Invalid or expired session",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Too many refresh attempts",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/MobileRefreshRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/mobile/logout": {
|
|
"post": {
|
|
"tags": [
|
|
"Auth · Mobile"
|
|
],
|
|
"summary": "Revoke the current (or all) refresh tokens",
|
|
"description": "Requires a valid bearer access token. Revokes the given refresh token, or every session for the user when { all: true }. Idempotent.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Logged out",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request"
|
|
},
|
|
"401": {
|
|
"description": "Missing or invalid bearer token",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/MobileLogoutRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/providers": {
|
|
"get": {
|
|
"tags": [
|
|
"Auth · SSO"
|
|
],
|
|
"summary": "List enabled SSO providers",
|
|
"description": "Public discovery used by the login page to render provider buttons. Never exposes secrets.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Enabled, valid providers",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/Provider"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/sso/{provider}/start": {
|
|
"get": {
|
|
"tags": [
|
|
"Auth · SSO"
|
|
],
|
|
"summary": "Begin SSO login (redirect to the IdP)",
|
|
"description": "Sets a short-lived signed transaction cookie and 302-redirects to the provider authorize URL. On error redirects back to the login page with an sso_error query param.",
|
|
"parameters": [
|
|
{
|
|
"name": "provider",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Provider id (e.g. google, discord)."
|
|
},
|
|
{
|
|
"name": "returnTo",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Internal /admin path to return to after login."
|
|
}
|
|
],
|
|
"responses": {
|
|
"302": {
|
|
"description": "Redirect to the identity provider (or back to the login page on error)"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/sso/{provider}/link": {
|
|
"get": {
|
|
"tags": [
|
|
"Auth · SSO"
|
|
],
|
|
"summary": "Begin linking an SSO identity to the current account",
|
|
"description": "Requires an authenticated session; the signed transaction captures the acting user so the callback can attach the external identity.",
|
|
"parameters": [
|
|
{
|
|
"name": "provider",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Provider id (e.g. google, discord)."
|
|
}
|
|
],
|
|
"responses": {
|
|
"302": {
|
|
"description": "Redirect to the identity provider (or back to the account page on error)"
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/auth/sso/{provider}/callback": {
|
|
"get": {
|
|
"tags": [
|
|
"Auth · SSO"
|
|
],
|
|
"summary": "OAuth redirect target — completes login or linking",
|
|
"description": "The provider redirects here with code + state. On success sets the session cookie (login) or links the identity (link), then 302-redirects into /admin. Login is link-only: unknown identities are refused (sso_error=not_linked).",
|
|
"parameters": [
|
|
{
|
|
"name": "provider",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Provider id (e.g. google, discord)."
|
|
},
|
|
{
|
|
"name": "error",
|
|
"in": "query",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"name": "code",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "OAuth authorization code."
|
|
},
|
|
{
|
|
"name": "state",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "OAuth state (matched against the tx cookie)."
|
|
}
|
|
],
|
|
"responses": {
|
|
"302": {
|
|
"description": "Redirect into /admin on success, or back to login/account with an error code"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/public/settings": {
|
|
"get": {
|
|
"tags": [
|
|
"Public"
|
|
],
|
|
"summary": "Public site settings",
|
|
"description": "Whitelisted, non-sensitive settings the client needs to render the site.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Key/value settings",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/public/status": {
|
|
"get": {
|
|
"tags": [
|
|
"Public"
|
|
],
|
|
"summary": "Site mode / status",
|
|
"description": "Current site mode (live or maintenance) so the client can show the maintenance page.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Site status",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"mode": {
|
|
"type": "string",
|
|
"example": "live"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/public/contact": {
|
|
"post": {
|
|
"tags": [
|
|
"Public"
|
|
],
|
|
"summary": "Send a contact message",
|
|
"description": "Emails the site owner (or falls back to a mailto). Rate limited.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Message sent",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Too many messages (rate limited)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"502": {
|
|
"description": "Mail delivery failed",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ContactRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/public/posts/{category}": {
|
|
"get": {
|
|
"tags": [
|
|
"Public"
|
|
],
|
|
"summary": "List published posts in a category",
|
|
"description": "Gated by site mode: during maintenance only admins with a valid session see content.",
|
|
"parameters": [
|
|
{
|
|
"name": "category",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "news | five-on-friday | newsletter | screenshots"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Published posts",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/Post"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Unknown category",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
},
|
|
"503": {
|
|
"description": "Service Unavailable"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/public/posts/{category}/{idOrSlug}": {
|
|
"get": {
|
|
"tags": [
|
|
"Public"
|
|
],
|
|
"summary": "Get a single published post",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "category",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Post category."
|
|
},
|
|
{
|
|
"name": "idOrSlug",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Numeric id or slug."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "The post",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Post"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Unknown category or post not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
},
|
|
"503": {
|
|
"description": "Service Unavailable"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/public/wiki": {
|
|
"get": {
|
|
"tags": [
|
|
"Public"
|
|
],
|
|
"summary": "List published wiki pages",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "q",
|
|
"in": "query",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"name": "category",
|
|
"in": "query",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"name": "tag",
|
|
"in": "query",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Published wiki pages",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/WikiPage"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
},
|
|
"503": {
|
|
"description": "Service Unavailable"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/public/wiki/categories": {
|
|
"get": {
|
|
"tags": [
|
|
"Public"
|
|
],
|
|
"summary": "List wiki categories",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Wiki categories",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/WikiCategory"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
},
|
|
"503": {
|
|
"description": "Service Unavailable"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/public/wiki/tags": {
|
|
"get": {
|
|
"tags": [
|
|
"Public"
|
|
],
|
|
"summary": "List wiki tags",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Wiki tags",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
},
|
|
"503": {
|
|
"description": "Service Unavailable"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/public/wiki/{slug}": {
|
|
"get": {
|
|
"tags": [
|
|
"Public"
|
|
],
|
|
"summary": "Get a single published wiki page",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "slug",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Wiki page slug."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "The wiki page",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiPage"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
},
|
|
"503": {
|
|
"description": "Service Unavailable"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/account": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Account"
|
|
],
|
|
"summary": "Get the current account (self)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "The account",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/User"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/account/totp/setup": {
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Account"
|
|
],
|
|
"summary": "Begin 2FA enrollment (returns secret + QR)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "otpauth URL and QR data to scan",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"otpauth_url": {
|
|
"type": "string"
|
|
},
|
|
"qr": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Two-factor already enabled",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/account/totp/enable": {
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Account"
|
|
],
|
|
"summary": "Enable 2FA by confirming a code",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "2FA enabled",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Setup not started, or invalid code",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Two-factor already enabled",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/TotpCodeRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/account/totp/disable": {
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Account"
|
|
],
|
|
"summary": "Disable 2FA by confirming a code",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "2FA disabled",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Not enabled, or invalid code",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/TotpCodeRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/account/identities": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Account"
|
|
],
|
|
"summary": "List linked SSO identities (self)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Linked identities",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"provider": {
|
|
"type": "string"
|
|
},
|
|
"email": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/account/identities/{provider}": {
|
|
"delete": {
|
|
"tags": [
|
|
"Admin · Account"
|
|
],
|
|
"summary": "Unlink an SSO identity (self)",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "provider",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Provider id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Unlinked",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request"
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "No linked account for that provider",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/dashboard": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Dashboard"
|
|
],
|
|
"summary": "Dashboard summary counts",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Summary counts (posts, wiki, users, site mode)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/site-mode": {
|
|
"put": {
|
|
"tags": [
|
|
"Admin · Dashboard"
|
|
],
|
|
"summary": "Set site mode (admin only)",
|
|
"description": "Switch the site between live and maintenance.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Updated site mode",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"mode": {
|
|
"type": "string",
|
|
"example": "maintenance"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/SiteModeRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/posts": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Posts"
|
|
],
|
|
"summary": "List all posts (including unpublished)",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "category",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Optional category filter."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Posts",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/Post"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request"
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
},
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Posts"
|
|
],
|
|
"summary": "Create a post",
|
|
"description": "",
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created post",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Post"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error or unknown category",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/PostCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/posts/upload": {
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Posts"
|
|
],
|
|
"summary": "Upload a post image (multipart)",
|
|
"description": "",
|
|
"responses": {
|
|
"201": {
|
|
"description": "Stored image URL",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"image_url": {
|
|
"type": "string",
|
|
"example": "/uploads/1700000000-abcd.png"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "No image / disallowed type",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"multipart/form-data": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"image": {
|
|
"type": "string",
|
|
"format": "binary"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/uploads": {
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Posts"
|
|
],
|
|
"summary": "Upload an image for rich-text editors (multipart)",
|
|
"description": "",
|
|
"responses": {
|
|
"201": {
|
|
"description": "Stored file URL",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/UploadResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "No file / disallowed type",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"multipart/form-data": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"image": {
|
|
"type": "string",
|
|
"format": "binary"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/posts/{id}": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Posts"
|
|
],
|
|
"summary": "Get a post by id",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "Post id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "The post",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Post"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request"
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
},
|
|
"put": {
|
|
"tags": [
|
|
"Admin · Posts"
|
|
],
|
|
"summary": "Update a post",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "Post id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Updated post",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Post"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error or unknown category",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/PostCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"tags": [
|
|
"Admin · Posts"
|
|
],
|
|
"summary": "Delete a post",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "Post id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Deleted",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request"
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/posts/{id}/publish": {
|
|
"patch": {
|
|
"tags": [
|
|
"Admin · Posts"
|
|
],
|
|
"summary": "Publish / unpublish a post",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "Post id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Updated post",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Post"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/PublishRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/wiki/categories": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "List wiki categories",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Wiki categories",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/WikiCategory"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
},
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Create a wiki category",
|
|
"description": "",
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created category",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiCategory"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Slug already exists",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiCategoryCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/wiki/categories/{id}": {
|
|
"put": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Update a wiki category",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "Category id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Updated category",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiCategory"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Slug already exists",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiCategoryCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Delete a wiki category",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "Category id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Deleted",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request"
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/wiki/tags": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "List wiki tags",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Wiki tags",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/wiki": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "List all wiki pages (including unpublished)",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "q",
|
|
"in": "query",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"name": "category",
|
|
"in": "query",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"name": "tag",
|
|
"in": "query",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"name": "status",
|
|
"in": "query",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Wiki pages",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/WikiPage"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
},
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Create a wiki page",
|
|
"description": "",
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created wiki page",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiPage"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error or unknown category",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Slug already exists",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiPageCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/wiki/{slug}": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Get a wiki page by slug",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "slug",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Wiki page slug."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "The wiki page",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiPage"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
},
|
|
"put": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Update a wiki page (creates a revision)",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "slug",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Wiki page slug."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Updated wiki page",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiPage"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error or unknown category",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"allOf": [
|
|
{
|
|
"$ref": "#/components/schemas/WikiPageCreateRequest"
|
|
},
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"change_note": {
|
|
"type": "string",
|
|
"maxLength": 280
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Delete a wiki page",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "slug",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Wiki page slug."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Deleted",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/wiki/{slug}/publish": {
|
|
"patch": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Publish / unpublish a wiki page",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "slug",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Wiki page slug."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Updated wiki page",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiPage"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/PublishRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/wiki/{slug}/revisions": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "List revisions of a wiki page",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "slug",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Wiki page slug."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Revisions",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/wiki/{slug}/revisions/{id}": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Get a single wiki revision",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "slug",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Wiki page slug."
|
|
},
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "Revision id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "The revision",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request"
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/wiki/{slug}/revisions/{id}/restore": {
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Wiki"
|
|
],
|
|
"summary": "Restore a wiki page to a revision",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "slug",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Wiki page slug."
|
|
},
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "Revision id to restore."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Restored wiki page",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/WikiPage"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request"
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/settings": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Settings"
|
|
],
|
|
"summary": "Get all site settings (admin only)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "All settings",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
},
|
|
"put": {
|
|
"tags": [
|
|
"Admin · Settings"
|
|
],
|
|
"summary": "Update site settings (admin only)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Updated settings",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Body must be an object of key/value settings",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true,
|
|
"description": "An object of key/value settings."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/activity": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Activity"
|
|
],
|
|
"summary": "List recent admin activity",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "offset",
|
|
"in": "query",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"name": "limit",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "Max rows to return."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Activity entries",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/bot-activity": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Bot Activity"
|
|
],
|
|
"summary": "Bot-scoring / ban state and recent events (admin only)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Banned IPs, scores and recent events",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/bot-activity/unban": {
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Bot Activity"
|
|
],
|
|
"summary": "Emergency unban an IP (admin only)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Unbanned",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid IP",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/UnbanRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/auth/providers": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Auth Providers"
|
|
],
|
|
"summary": "List configured SSO providers (admin only)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Providers (secrets stripped)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/ProviderConfig"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
},
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Auth Providers"
|
|
],
|
|
"summary": "Create a custom SSO provider (admin only)",
|
|
"description": "Built-in providers (google, discord) are configured via PUT, not created here.",
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created provider",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ProviderConfig"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error, or a built-in/invalid kind",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Provider id already exists",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ProviderCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/auth/providers/{id}": {
|
|
"put": {
|
|
"tags": [
|
|
"Admin · Auth Providers"
|
|
],
|
|
"summary": "Update an SSO provider (admin only)",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Provider id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Updated provider",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ProviderConfig"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Provider not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ProviderCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"tags": [
|
|
"Admin · Auth Providers"
|
|
],
|
|
"summary": "Delete a custom SSO provider (admin only)",
|
|
"description": "Built-in providers cannot be deleted — disable them instead.",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string"
|
|
},
|
|
"description": "Provider id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Deleted",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Built-in provider cannot be deleted",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Provider not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/admin/users": {
|
|
"get": {
|
|
"tags": [
|
|
"Admin · Users"
|
|
],
|
|
"summary": "List users (admin only)",
|
|
"description": "",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Users",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/User"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
},
|
|
"post": {
|
|
"tags": [
|
|
"Admin · Users"
|
|
],
|
|
"summary": "Create a user (admin only)",
|
|
"description": "",
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created user",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/User"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Username already taken",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/UserCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/admin/users/{id}": {
|
|
"put": {
|
|
"tags": [
|
|
"Admin · Users"
|
|
],
|
|
"summary": "Update a user (admin only)",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "User id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Updated user",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/User"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Validation error, or cannot demote the last admin",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Username already taken",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/UserCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"tags": [
|
|
"Admin · Users"
|
|
],
|
|
"summary": "Delete a user (admin only)",
|
|
"description": "",
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer"
|
|
},
|
|
"description": "User id."
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Deleted",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Message"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Cannot delete your own account or the last admin",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Not authenticated",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Admin role required",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Error"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error"
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"cookieAuth": []
|
|
},
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"components": {
|
|
"securitySchemes": {
|
|
"cookieAuth": {
|
|
"type": "apiKey",
|
|
"in": "cookie",
|
|
"name": "uomm_token",
|
|
"description": "Session JWT set as an httpOnly cookie by POST /api/v1/auth/login."
|
|
},
|
|
"bearerAuth": {
|
|
"type": "http",
|
|
"scheme": "bearer",
|
|
"bearerFormat": "JWT",
|
|
"description": "Access token from POST /api/v1/auth/mobile/login (or /refresh)."
|
|
}
|
|
},
|
|
"schemas": {
|
|
"Error": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"message": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Not found"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"ValidationError": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"errors": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "array"
|
|
},
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "field"
|
|
}
|
|
}
|
|
},
|
|
"msg": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Invalid value"
|
|
}
|
|
}
|
|
},
|
|
"path": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "username"
|
|
}
|
|
}
|
|
},
|
|
"location": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "body"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"SafeUser": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 1
|
|
}
|
|
}
|
|
},
|
|
"username": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "admin"
|
|
}
|
|
}
|
|
},
|
|
"role": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"enum": {
|
|
"type": "array",
|
|
"example": [
|
|
"admin",
|
|
"editor"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "admin"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"LoginRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"username",
|
|
"password"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"username": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "admin"
|
|
}
|
|
}
|
|
},
|
|
"password": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "password"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "super-secret"
|
|
}
|
|
}
|
|
},
|
|
"company": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Honeypot — must be empty for humans."
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"LoginResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Either a session (user) or, for 2FA accounts, a TOTP challenge to complete at /login/totp."
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"user": {
|
|
"$ref": "#/components/schemas/SafeUser"
|
|
},
|
|
"totpRequired": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": true
|
|
}
|
|
}
|
|
},
|
|
"challenge": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Signed challenge token for the TOTP step."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"TotpLoginRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"challenge",
|
|
"code"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"challenge": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Token returned by /login when totpRequired."
|
|
}
|
|
}
|
|
},
|
|
"code": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "123456"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"MobileLoginRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"username",
|
|
"password"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"username": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "admin"
|
|
}
|
|
}
|
|
},
|
|
"password": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "password"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "super-secret"
|
|
}
|
|
}
|
|
},
|
|
"code": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "TOTP code (only when 2FA is enabled)."
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "123456"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"MobileTokenResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"accessToken": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Short-lived bearer JWT."
|
|
}
|
|
}
|
|
},
|
|
"refreshToken": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Long-lived, revocable refresh token."
|
|
}
|
|
}
|
|
},
|
|
"expiresIn": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Access token lifetime in seconds."
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 900
|
|
}
|
|
}
|
|
},
|
|
"user": {
|
|
"$ref": "#/components/schemas/SafeUser"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"MobileRefreshRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"refreshToken"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"refreshToken": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"MobileLogoutRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"refreshToken": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Revoke a single session."
|
|
}
|
|
}
|
|
},
|
|
"all": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Revoke every session for the user."
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"Message": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"message": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Logged out."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"ContactRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"message"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"message": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 5000
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "When does the shard launch?"
|
|
}
|
|
}
|
|
},
|
|
"email": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "email"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "player@example.com"
|
|
}
|
|
}
|
|
},
|
|
"name": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 100
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Lord British"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"Provider": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "google"
|
|
}
|
|
}
|
|
},
|
|
"name": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Google"
|
|
}
|
|
}
|
|
},
|
|
"kind": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"enum": {
|
|
"type": "array",
|
|
"example": [
|
|
"oidc",
|
|
"oauth2"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "oidc"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"ProviderConfig": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "okta"
|
|
}
|
|
}
|
|
},
|
|
"kind": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"enum": {
|
|
"type": "array",
|
|
"example": [
|
|
"oidc",
|
|
"oauth2"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "oidc"
|
|
}
|
|
}
|
|
},
|
|
"name": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Okta"
|
|
}
|
|
}
|
|
},
|
|
"enabled": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": true
|
|
}
|
|
}
|
|
},
|
|
"clientId": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
},
|
|
"authorizeUrl": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "uri"
|
|
}
|
|
}
|
|
},
|
|
"tokenUrl": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "uri"
|
|
}
|
|
}
|
|
},
|
|
"userinfoUrl": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "uri"
|
|
}
|
|
}
|
|
},
|
|
"scopes": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "openid email profile"
|
|
}
|
|
}
|
|
},
|
|
"priority": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 10
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"ProviderCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"id",
|
|
"kind",
|
|
"name"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"pattern": {
|
|
"type": "string",
|
|
"example": "^[a-z0-9-]+$"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "okta"
|
|
}
|
|
}
|
|
},
|
|
"kind": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"enum": {
|
|
"type": "array",
|
|
"example": [
|
|
"oidc",
|
|
"oauth2"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "oidc"
|
|
}
|
|
}
|
|
},
|
|
"name": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 80
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Okta"
|
|
}
|
|
}
|
|
},
|
|
"enabled": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": true
|
|
}
|
|
}
|
|
},
|
|
"clientId": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
},
|
|
"secret": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "password"
|
|
}
|
|
}
|
|
},
|
|
"authorizeUrl": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "uri"
|
|
}
|
|
}
|
|
},
|
|
"tokenUrl": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "uri"
|
|
}
|
|
}
|
|
},
|
|
"userinfoUrl": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "uri"
|
|
}
|
|
}
|
|
},
|
|
"scopes": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 500
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "openid email profile"
|
|
}
|
|
}
|
|
},
|
|
"priority": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 10
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"Post": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 12
|
|
}
|
|
}
|
|
},
|
|
"category": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "news"
|
|
}
|
|
}
|
|
},
|
|
"title": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Server maintenance this weekend"
|
|
}
|
|
}
|
|
},
|
|
"slug": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "server-maintenance-this-weekend"
|
|
}
|
|
}
|
|
},
|
|
"body": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
},
|
|
"image_url": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "/uploads/1700000000-abcd.png"
|
|
}
|
|
}
|
|
},
|
|
"published": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": true
|
|
}
|
|
}
|
|
},
|
|
"created_at": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "date-time"
|
|
}
|
|
}
|
|
},
|
|
"updated_at": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "date-time"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"PostCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"category",
|
|
"title"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"category": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "news"
|
|
}
|
|
}
|
|
},
|
|
"title": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 200
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Server maintenance this weekend"
|
|
}
|
|
}
|
|
},
|
|
"body": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
},
|
|
"image_url": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"example": "Required for the screenshots category."
|
|
}
|
|
}
|
|
},
|
|
"published": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"PublishRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"published"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"published": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"UploadResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"url": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "/uploads/1700000000-abcd.png"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"WikiPage": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 3
|
|
}
|
|
}
|
|
},
|
|
"slug": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "getting-started"
|
|
}
|
|
}
|
|
},
|
|
"title": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Getting Started"
|
|
}
|
|
}
|
|
},
|
|
"excerpt": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
},
|
|
"body": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
},
|
|
"category_id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"nullable": {
|
|
"type": "boolean",
|
|
"example": true
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 2
|
|
}
|
|
}
|
|
},
|
|
"published": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": true
|
|
}
|
|
}
|
|
},
|
|
"tags": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "array"
|
|
},
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
},
|
|
"example": {
|
|
"type": "array",
|
|
"example": [
|
|
"newbie",
|
|
"guide"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"created_at": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "date-time"
|
|
}
|
|
}
|
|
},
|
|
"updated_at": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "date-time"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"WikiPageCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"slug",
|
|
"title"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"slug": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"pattern": {
|
|
"type": "string",
|
|
"example": "^[a-z0-9-]+$"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "getting-started"
|
|
}
|
|
}
|
|
},
|
|
"title": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 200
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Getting Started"
|
|
}
|
|
}
|
|
},
|
|
"excerpt": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 400
|
|
}
|
|
}
|
|
},
|
|
"body": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
},
|
|
"category_id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"nullable": {
|
|
"type": "boolean",
|
|
"example": true
|
|
}
|
|
}
|
|
},
|
|
"published": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": false
|
|
}
|
|
}
|
|
},
|
|
"tags": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "array"
|
|
},
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"WikiCategory": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 2
|
|
}
|
|
}
|
|
},
|
|
"slug": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "guides"
|
|
}
|
|
}
|
|
},
|
|
"title": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Guides"
|
|
}
|
|
}
|
|
},
|
|
"description": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
}
|
|
}
|
|
},
|
|
"sort_order": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"WikiCategoryCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"slug",
|
|
"title"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"slug": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"pattern": {
|
|
"type": "string",
|
|
"example": "^[a-z0-9-]+$"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "guides"
|
|
}
|
|
}
|
|
},
|
|
"title": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 200
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "Guides"
|
|
}
|
|
}
|
|
},
|
|
"description": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 400
|
|
}
|
|
}
|
|
},
|
|
"sort_order": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"User": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "integer"
|
|
},
|
|
"example": {
|
|
"type": "number",
|
|
"example": 1
|
|
}
|
|
}
|
|
},
|
|
"username": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "admin"
|
|
}
|
|
}
|
|
},
|
|
"role": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"enum": {
|
|
"type": "array",
|
|
"example": [
|
|
"admin",
|
|
"editor"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "admin"
|
|
}
|
|
}
|
|
},
|
|
"totp_enabled": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "boolean"
|
|
},
|
|
"example": {
|
|
"type": "boolean",
|
|
"example": true
|
|
}
|
|
}
|
|
},
|
|
"last_login_at": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "date-time"
|
|
},
|
|
"nullable": {
|
|
"type": "boolean",
|
|
"example": true
|
|
}
|
|
}
|
|
},
|
|
"created_at": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "date-time"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"UserCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"username",
|
|
"password"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"username": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"minLength": {
|
|
"type": "number",
|
|
"example": 3
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 32
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "editor1"
|
|
}
|
|
}
|
|
},
|
|
"password": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"example": "password"
|
|
},
|
|
"minLength": {
|
|
"type": "number",
|
|
"example": 8
|
|
},
|
|
"maxLength": {
|
|
"type": "number",
|
|
"example": 64
|
|
}
|
|
}
|
|
},
|
|
"role": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"enum": {
|
|
"type": "array",
|
|
"example": [
|
|
"admin",
|
|
"editor"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "editor"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"TotpCodeRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"code"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"code": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "123456"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"SiteModeRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"mode"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"mode": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"enum": {
|
|
"type": "array",
|
|
"example": [
|
|
"live",
|
|
"maintenance"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "live"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"UnbanRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "object"
|
|
},
|
|
"required": {
|
|
"type": "array",
|
|
"example": [
|
|
"ip"
|
|
],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"properties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"ip": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"example": "string"
|
|
},
|
|
"example": {
|
|
"type": "string",
|
|
"example": "203.0.113.5"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |