Allow users to exclude specific paths from Authentik/CPM forward auth protection. When excluded_paths is set, all paths require authentication EXCEPT the excluded ones — useful for apps like Navidrome that need /share/* and /rest/* to bypass auth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2040 lines
77 KiB
TypeScript
2040 lines
77 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { requireApiAdmin, apiErrorResponse } from "@/src/lib/api-auth";
|
|
|
|
const spec = {
|
|
openapi: "3.1.0",
|
|
info: {
|
|
title: "Caddy Proxy Manager API",
|
|
version: "1.0.0",
|
|
description:
|
|
"REST API for managing Caddy reverse proxy configurations, certificates, access lists, and more.",
|
|
},
|
|
servers: [{ url: "/" }],
|
|
security: [{ bearerAuth: [] }, { sessionAuth: [] }],
|
|
tags: [
|
|
{ name: "Tokens", description: "API token management" },
|
|
{ name: "Proxy Hosts", description: "HTTP/HTTPS reverse proxy hosts" },
|
|
{ name: "L4 Proxy Hosts", description: "Layer 4 (TCP/UDP) proxy hosts" },
|
|
{ name: "Certificates", description: "TLS certificate management" },
|
|
{ name: "CA Certificates", description: "Certificate Authority certificates" },
|
|
{ name: "Client Certificates", description: "Client certificate management" },
|
|
{ name: "Access Lists", description: "HTTP basic-auth access lists" },
|
|
{ name: "Settings", description: "Application settings" },
|
|
{ name: "Instances", description: "Multi-instance management" },
|
|
{ name: "Users", description: "User management" },
|
|
{ name: "Groups", description: "User groups for forward auth access control" },
|
|
{ name: "mTLS Roles", description: "Role-based access control for mTLS client certificates" },
|
|
{ name: "Forward Auth", description: "Forward auth sessions and per-host access control" },
|
|
{ name: "Audit Log", description: "Audit log" },
|
|
{ name: "Caddy", description: "Caddy server operations" },
|
|
],
|
|
paths: {
|
|
// ── Tokens ──────────────────────────────────────────────────────
|
|
"/api/v1/tokens": {
|
|
get: {
|
|
tags: ["Tokens"],
|
|
summary: "List tokens",
|
|
operationId: "listTokens",
|
|
responses: {
|
|
"200": {
|
|
description: "List of tokens",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/Token" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["Tokens"],
|
|
summary: "Create a token",
|
|
operationId: "createToken",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/TokenInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"201": {
|
|
description: "Token created",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
token: { $ref: "#/components/schemas/Token" },
|
|
raw_token: {
|
|
type: "string",
|
|
description:
|
|
"Plain-text token value. Only returned at creation time.",
|
|
},
|
|
},
|
|
required: ["token", "raw_token"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/tokens/{id}": {
|
|
delete: {
|
|
tags: ["Tokens"],
|
|
summary: "Delete a token",
|
|
operationId: "deleteToken",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Proxy Hosts ─────────────────────────────────────────────────
|
|
"/api/v1/proxy-hosts": {
|
|
get: {
|
|
tags: ["Proxy Hosts"],
|
|
summary: "List proxy hosts",
|
|
operationId: "listProxyHosts",
|
|
responses: {
|
|
"200": {
|
|
description: "List of proxy hosts",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/ProxyHost" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["Proxy Hosts"],
|
|
summary: "Create a proxy host",
|
|
operationId: "createProxyHost",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ProxyHostInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"201": {
|
|
description: "Proxy host created",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ProxyHost" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/proxy-hosts/{id}": {
|
|
get: {
|
|
tags: ["Proxy Hosts"],
|
|
summary: "Get a proxy host",
|
|
operationId: "getProxyHost",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": {
|
|
description: "Proxy host",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ProxyHost" },
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ["Proxy Hosts"],
|
|
summary: "Update a proxy host",
|
|
operationId: "updateProxyHost",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ProxyHostInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"200": {
|
|
description: "Proxy host updated",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ProxyHost" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ["Proxy Hosts"],
|
|
summary: "Delete a proxy host",
|
|
operationId: "deleteProxyHost",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── L4 Proxy Hosts ──────────────────────────────────────────────
|
|
"/api/v1/l4-proxy-hosts": {
|
|
get: {
|
|
tags: ["L4 Proxy Hosts"],
|
|
summary: "List L4 proxy hosts",
|
|
operationId: "listL4ProxyHosts",
|
|
responses: {
|
|
"200": {
|
|
description: "List of L4 proxy hosts",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/L4ProxyHost" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["L4 Proxy Hosts"],
|
|
summary: "Create an L4 proxy host",
|
|
operationId: "createL4ProxyHost",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/L4ProxyHostInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"201": {
|
|
description: "L4 proxy host created",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/L4ProxyHost" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/l4-proxy-hosts/{id}": {
|
|
get: {
|
|
tags: ["L4 Proxy Hosts"],
|
|
summary: "Get an L4 proxy host",
|
|
operationId: "getL4ProxyHost",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": {
|
|
description: "L4 proxy host",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/L4ProxyHost" },
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ["L4 Proxy Hosts"],
|
|
summary: "Update an L4 proxy host",
|
|
operationId: "updateL4ProxyHost",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/L4ProxyHostInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"200": {
|
|
description: "L4 proxy host updated",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/L4ProxyHost" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ["L4 Proxy Hosts"],
|
|
summary: "Delete an L4 proxy host",
|
|
operationId: "deleteL4ProxyHost",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Certificates ────────────────────────────────────────────────
|
|
"/api/v1/certificates": {
|
|
get: {
|
|
tags: ["Certificates"],
|
|
summary: "List certificates",
|
|
operationId: "listCertificates",
|
|
responses: {
|
|
"200": {
|
|
description: "List of certificates",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/Certificate" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["Certificates"],
|
|
summary: "Create a certificate",
|
|
operationId: "createCertificate",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/CertificateInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"201": {
|
|
description: "Certificate created",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Certificate" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/certificates/{id}": {
|
|
get: {
|
|
tags: ["Certificates"],
|
|
summary: "Get a certificate",
|
|
operationId: "getCertificate",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": {
|
|
description: "Certificate",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Certificate" },
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ["Certificates"],
|
|
summary: "Update a certificate",
|
|
operationId: "updateCertificate",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/CertificateInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"200": {
|
|
description: "Certificate updated",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Certificate" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ["Certificates"],
|
|
summary: "Delete a certificate",
|
|
operationId: "deleteCertificate",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── CA Certificates ─────────────────────────────────────────────
|
|
"/api/v1/ca-certificates": {
|
|
get: {
|
|
tags: ["CA Certificates"],
|
|
summary: "List CA certificates",
|
|
operationId: "listCaCertificates",
|
|
responses: {
|
|
"200": {
|
|
description: "List of CA certificates",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/CaCertificate" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["CA Certificates"],
|
|
summary: "Create a CA certificate",
|
|
operationId: "createCaCertificate",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/CaCertificateInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"201": {
|
|
description: "CA certificate created",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/CaCertificate" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/ca-certificates/{id}": {
|
|
get: {
|
|
tags: ["CA Certificates"],
|
|
summary: "Get a CA certificate",
|
|
operationId: "getCaCertificate",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": {
|
|
description: "CA certificate",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/CaCertificate" },
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ["CA Certificates"],
|
|
summary: "Update a CA certificate",
|
|
operationId: "updateCaCertificate",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/CaCertificateInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"200": {
|
|
description: "CA certificate updated",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/CaCertificate" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ["CA Certificates"],
|
|
summary: "Delete a CA certificate",
|
|
operationId: "deleteCaCertificate",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Client Certificates ─────────────────────────────────────────
|
|
"/api/v1/client-certificates": {
|
|
get: {
|
|
tags: ["Client Certificates"],
|
|
summary: "List client certificates",
|
|
operationId: "listClientCertificates",
|
|
responses: {
|
|
"200": {
|
|
description: "List of client certificates",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/ClientCertificate" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["Client Certificates"],
|
|
summary: "Create a client certificate",
|
|
operationId: "createClientCertificate",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ClientCertificateInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"201": {
|
|
description: "Client certificate created",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ClientCertificate" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/client-certificates/{id}": {
|
|
get: {
|
|
tags: ["Client Certificates"],
|
|
summary: "Get a client certificate",
|
|
operationId: "getClientCertificate",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": {
|
|
description: "Client certificate",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ClientCertificate" },
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ["Client Certificates"],
|
|
summary: "Revoke a client certificate",
|
|
operationId: "revokeClientCertificate",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Access Lists ────────────────────────────────────────────────
|
|
"/api/v1/access-lists": {
|
|
get: {
|
|
tags: ["Access Lists"],
|
|
summary: "List access lists",
|
|
operationId: "listAccessLists",
|
|
responses: {
|
|
"200": {
|
|
description: "List of access lists",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/AccessList" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["Access Lists"],
|
|
summary: "Create an access list",
|
|
operationId: "createAccessList",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/AccessListInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"201": {
|
|
description: "Access list created",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/AccessList" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/access-lists/{id}": {
|
|
get: {
|
|
tags: ["Access Lists"],
|
|
summary: "Get an access list",
|
|
operationId: "getAccessList",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": {
|
|
description: "Access list",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/AccessList" },
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ["Access Lists"],
|
|
summary: "Update an access list",
|
|
operationId: "updateAccessList",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/AccessListInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"200": {
|
|
description: "Access list updated",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/AccessList" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ["Access Lists"],
|
|
summary: "Delete an access list",
|
|
operationId: "deleteAccessList",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/access-lists/{id}/entries": {
|
|
post: {
|
|
tags: ["Access Lists"],
|
|
summary: "Add an entry to an access list",
|
|
operationId: "addAccessListEntry",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
username: { type: "string" },
|
|
password: { type: "string" },
|
|
},
|
|
required: ["username", "password"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"201": {
|
|
description: "Entry added",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/AccessListEntry" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/access-lists/{id}/entries/{entryId}": {
|
|
delete: {
|
|
tags: ["Access Lists"],
|
|
summary: "Remove an entry from an access list",
|
|
operationId: "removeAccessListEntry",
|
|
parameters: [
|
|
{ $ref: "#/components/parameters/IdPath" },
|
|
{
|
|
name: "entryId",
|
|
in: "path",
|
|
required: true,
|
|
schema: { type: "integer" },
|
|
description: "Entry ID",
|
|
},
|
|
],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Settings ────────────────────────────────────────────────────
|
|
"/api/v1/settings/{group}": {
|
|
get: {
|
|
tags: ["Settings"],
|
|
summary: "Get settings for a group",
|
|
operationId: "getSettings",
|
|
parameters: [
|
|
{
|
|
name: "group",
|
|
in: "path",
|
|
required: true,
|
|
schema: {
|
|
type: "string",
|
|
enum: [
|
|
"general",
|
|
"cloudflare",
|
|
"authentik",
|
|
"metrics",
|
|
"logging",
|
|
"dns",
|
|
"upstream-dns",
|
|
"geoblock",
|
|
"waf",
|
|
"instance-mode",
|
|
"sync-token",
|
|
],
|
|
},
|
|
description: "Settings group name",
|
|
},
|
|
],
|
|
responses: {
|
|
"200": {
|
|
description: "Settings object (shape varies by group). For instance-mode: `{mode}`. For sync-token: `{has_token}`.",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
oneOf: [
|
|
{ $ref: "#/components/schemas/GeneralSettings" },
|
|
{ $ref: "#/components/schemas/CloudflareSettings" },
|
|
{ $ref: "#/components/schemas/AuthentikSettings" },
|
|
{ $ref: "#/components/schemas/MetricsSettings" },
|
|
{ $ref: "#/components/schemas/LoggingSettings" },
|
|
{ $ref: "#/components/schemas/DnsSettings" },
|
|
{ $ref: "#/components/schemas/UpstreamDnsSettings" },
|
|
{ $ref: "#/components/schemas/GeoBlockConfig" },
|
|
{ $ref: "#/components/schemas/WafSettings" },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ["Settings"],
|
|
summary: "Update settings for a group",
|
|
operationId: "updateSettings",
|
|
parameters: [
|
|
{
|
|
name: "group",
|
|
in: "path",
|
|
required: true,
|
|
schema: {
|
|
type: "string",
|
|
enum: [
|
|
"general",
|
|
"cloudflare",
|
|
"authentik",
|
|
"metrics",
|
|
"logging",
|
|
"dns",
|
|
"upstream-dns",
|
|
"geoblock",
|
|
"waf",
|
|
"instance-mode",
|
|
"sync-token",
|
|
],
|
|
},
|
|
description: "Settings group name",
|
|
},
|
|
],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
oneOf: [
|
|
{ $ref: "#/components/schemas/GeneralSettings" },
|
|
{ $ref: "#/components/schemas/CloudflareSettings" },
|
|
{ $ref: "#/components/schemas/AuthentikSettings" },
|
|
{ $ref: "#/components/schemas/MetricsSettings" },
|
|
{ $ref: "#/components/schemas/LoggingSettings" },
|
|
{ $ref: "#/components/schemas/DnsSettings" },
|
|
{ $ref: "#/components/schemas/UpstreamDnsSettings" },
|
|
{ $ref: "#/components/schemas/GeoBlockConfig" },
|
|
{ $ref: "#/components/schemas/WafSettings" },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"200": {
|
|
description: "Settings updated",
|
|
content: {
|
|
"application/json": { schema: { $ref: "#/components/responses/Ok" } },
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Instances ───────────────────────────────────────────────────
|
|
"/api/v1/instances": {
|
|
get: {
|
|
tags: ["Instances"],
|
|
summary: "List instances",
|
|
operationId: "listInstances",
|
|
responses: {
|
|
"200": {
|
|
description: "List of instances",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/Instance" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["Instances"],
|
|
summary: "Create an instance",
|
|
operationId: "createInstance",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/InstanceInput" },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"201": {
|
|
description: "Instance created",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Instance" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/instances/{id}": {
|
|
delete: {
|
|
tags: ["Instances"],
|
|
summary: "Delete an instance",
|
|
operationId: "deleteInstance",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/instances/sync": {
|
|
post: {
|
|
tags: ["Instances"],
|
|
summary: "Trigger instance sync",
|
|
operationId: "syncInstances",
|
|
responses: {
|
|
"200": {
|
|
description: "Sync result",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
total: { type: "integer" },
|
|
success: { type: "integer" },
|
|
failed: { type: "integer" },
|
|
skippedHttp: { type: "integer" },
|
|
},
|
|
required: ["total", "success", "failed", "skippedHttp"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Users ───────────────────────────────────────────────────────
|
|
"/api/v1/users": {
|
|
get: {
|
|
tags: ["Users"],
|
|
summary: "List users",
|
|
operationId: "listUsers",
|
|
responses: {
|
|
"200": {
|
|
description: "List of users",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/User" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/users/{id}": {
|
|
get: {
|
|
tags: ["Users"],
|
|
summary: "Get a user",
|
|
operationId: "getUser",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": {
|
|
description: "User",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/User" },
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ["Users"],
|
|
summary: "Update a user",
|
|
operationId: "updateUser",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: ["string", "null"] },
|
|
email: { type: "string" },
|
|
role: { type: "string", enum: ["admin", "user"] },
|
|
status: { type: "string", enum: ["active", "disabled"] },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
"200": {
|
|
description: "User updated",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/User" },
|
|
},
|
|
},
|
|
},
|
|
"400": { $ref: "#/components/responses/BadRequest" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"403": { $ref: "#/components/responses/Forbidden" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Audit Log ───────────────────────────────────────────────────
|
|
"/api/v1/audit-log": {
|
|
get: {
|
|
tags: ["Audit Log"],
|
|
summary: "List audit log events",
|
|
operationId: "listAuditLog",
|
|
parameters: [
|
|
{
|
|
name: "page",
|
|
in: "query",
|
|
schema: { type: "integer", default: 1 },
|
|
description: "Page number",
|
|
},
|
|
{
|
|
name: "per_page",
|
|
in: "query",
|
|
schema: { type: "integer", default: 50 },
|
|
description: "Items per page",
|
|
},
|
|
{
|
|
name: "search",
|
|
in: "query",
|
|
schema: { type: "string" },
|
|
description: "Search term",
|
|
},
|
|
],
|
|
responses: {
|
|
"200": {
|
|
description: "Paginated audit log",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/AuditLogResponse" },
|
|
},
|
|
},
|
|
},
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Groups ──────────────────────────────────────────────────────
|
|
"/api/v1/groups": {
|
|
get: {
|
|
tags: ["Groups"],
|
|
summary: "List groups",
|
|
operationId: "listGroups",
|
|
responses: {
|
|
"200": { description: "List of groups", content: { "application/json": { schema: { type: "array", items: { $ref: "#/components/schemas/Group" } } } } },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["Groups"],
|
|
summary: "Create a group",
|
|
operationId: "createGroup",
|
|
requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["name"], properties: { name: { type: "string" }, description: { type: "string" } } } } } },
|
|
responses: {
|
|
"201": { description: "Group created", content: { "application/json": { schema: { $ref: "#/components/schemas/Group" } } } },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/groups/{id}": {
|
|
get: {
|
|
tags: ["Groups"],
|
|
summary: "Get a group",
|
|
operationId: "getGroup",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { description: "Group details", content: { "application/json": { schema: { $ref: "#/components/schemas/Group" } } } },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
patch: {
|
|
tags: ["Groups"],
|
|
summary: "Update a group",
|
|
operationId: "updateGroup",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: { name: { type: "string" }, description: { type: "string" } } } } } },
|
|
responses: {
|
|
"200": { description: "Group updated", content: { "application/json": { schema: { $ref: "#/components/schemas/Group" } } } },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ["Groups"],
|
|
summary: "Delete a group",
|
|
operationId: "deleteGroup",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/groups/{id}/members": {
|
|
post: {
|
|
tags: ["Groups"],
|
|
summary: "Add a member to a group",
|
|
operationId: "addGroupMember",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["userId"], properties: { userId: { type: "integer" } } } } } },
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/groups/{id}/members/{userId}": {
|
|
delete: {
|
|
tags: ["Groups"],
|
|
summary: "Remove a member from a group",
|
|
operationId: "removeGroupMember",
|
|
parameters: [
|
|
{ $ref: "#/components/parameters/IdPath" },
|
|
{ name: "userId", in: "path", required: true, schema: { type: "integer" }, description: "User ID to remove" },
|
|
],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── mTLS Roles ─────────────────────────────────────────────────
|
|
"/api/v1/mtls-roles": {
|
|
get: {
|
|
tags: ["mTLS Roles"],
|
|
summary: "List mTLS roles",
|
|
operationId: "listMtlsRoles",
|
|
responses: {
|
|
"200": { description: "List of roles", content: { "application/json": { schema: { type: "array", items: { $ref: "#/components/schemas/MtlsRole" } } } } },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["mTLS Roles"],
|
|
summary: "Create an mTLS role",
|
|
operationId: "createMtlsRole",
|
|
requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["name"], properties: { name: { type: "string" }, description: { type: "string" } } } } } },
|
|
responses: {
|
|
"201": { description: "Role created", content: { "application/json": { schema: { $ref: "#/components/schemas/MtlsRole" } } } },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/mtls-roles/{id}": {
|
|
get: {
|
|
tags: ["mTLS Roles"],
|
|
summary: "Get an mTLS role",
|
|
operationId: "getMtlsRole",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { description: "Role details", content: { "application/json": { schema: { $ref: "#/components/schemas/MtlsRole" } } } },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ["mTLS Roles"],
|
|
summary: "Update an mTLS role",
|
|
operationId: "updateMtlsRole",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: { name: { type: "string" }, description: { type: "string" } } } } } },
|
|
responses: {
|
|
"200": { description: "Role updated", content: { "application/json": { schema: { $ref: "#/components/schemas/MtlsRole" } } } },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ["mTLS Roles"],
|
|
summary: "Delete an mTLS role",
|
|
operationId: "deleteMtlsRole",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/mtls-roles/{id}/certificates": {
|
|
post: {
|
|
tags: ["mTLS Roles"],
|
|
summary: "Assign a certificate to an mTLS role",
|
|
operationId: "assignMtlsRoleCertificate",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["certificateId"], properties: { certificateId: { type: "integer" } } } } } },
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/mtls-roles/{id}/certificates/{certId}": {
|
|
delete: {
|
|
tags: ["mTLS Roles"],
|
|
summary: "Remove a certificate from an mTLS role",
|
|
operationId: "removeMtlsRoleCertificate",
|
|
parameters: [
|
|
{ $ref: "#/components/parameters/IdPath" },
|
|
{ name: "certId", in: "path", required: true, schema: { type: "integer" }, description: "Client certificate ID" },
|
|
],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Forward Auth ───────────────────────────────────────────────
|
|
"/api/v1/proxy-hosts/{id}/forward-auth-access": {
|
|
get: {
|
|
tags: ["Forward Auth"],
|
|
summary: "Get forward auth access list for a proxy host",
|
|
operationId: "getForwardAuthAccess",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { description: "Access list with user IDs and group IDs", content: { "application/json": { schema: { type: "object", properties: { userIds: { type: "array", items: { type: "integer" } }, groupIds: { type: "array", items: { type: "integer" } } } } } } },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ["Forward Auth"],
|
|
summary: "Set forward auth access list for a proxy host",
|
|
operationId: "setForwardAuthAccess",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: { userIds: { type: "array", items: { type: "integer" } }, groupIds: { type: "array", items: { type: "integer" } } } } } } },
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/forward-auth-sessions": {
|
|
get: {
|
|
tags: ["Forward Auth"],
|
|
summary: "List forward auth sessions",
|
|
operationId: "listForwardAuthSessions",
|
|
parameters: [{ name: "userId", in: "query", schema: { type: "integer" }, description: "Filter by user ID" }],
|
|
responses: {
|
|
"200": { description: "List of sessions", content: { "application/json": { schema: { type: "array", items: { type: "object" } } } } },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ["Forward Auth"],
|
|
summary: "Delete forward auth sessions",
|
|
operationId: "deleteForwardAuthSessions",
|
|
parameters: [{ name: "userId", in: "query", schema: { type: "integer" }, description: "Delete sessions for a specific user" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/forward-auth-sessions/{id}": {
|
|
delete: {
|
|
tags: ["Forward Auth"],
|
|
summary: "Delete a specific forward auth session",
|
|
operationId: "deleteForwardAuthSession",
|
|
parameters: [{ $ref: "#/components/parameters/IdPath" }],
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"404": { $ref: "#/components/responses/NotFound" },
|
|
},
|
|
},
|
|
},
|
|
|
|
// ── Caddy ───────────────────────────────────────────────────────
|
|
"/api/v1/caddy/apply": {
|
|
post: {
|
|
tags: ["Caddy"],
|
|
summary: "Apply Caddy configuration",
|
|
operationId: "applyCaddyConfig",
|
|
responses: {
|
|
"200": { $ref: "#/components/responses/Ok" },
|
|
"401": { $ref: "#/components/responses/Unauthorized" },
|
|
"500": { $ref: "#/components/responses/InternalError" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
components: {
|
|
securitySchemes: {
|
|
bearerAuth: {
|
|
type: "http",
|
|
scheme: "bearer",
|
|
description: "API token created from the Profile page",
|
|
},
|
|
sessionAuth: {
|
|
type: "apiKey",
|
|
in: "cookie",
|
|
name: "authjs.session-token",
|
|
description: "Cookie-based session from browser login",
|
|
},
|
|
},
|
|
parameters: {
|
|
IdPath: {
|
|
name: "id",
|
|
in: "path",
|
|
required: true,
|
|
schema: { type: "integer" },
|
|
description: "Resource ID",
|
|
},
|
|
},
|
|
responses: {
|
|
Ok: {
|
|
description: "Success",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "object",
|
|
properties: { ok: { type: "boolean", enum: [true] } },
|
|
required: ["ok"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
BadRequest: {
|
|
description: "Bad request",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
Unauthorized: {
|
|
description: "Unauthorized",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
Forbidden: {
|
|
description: "Forbidden",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
NotFound: {
|
|
description: "Not found",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
InternalError: {
|
|
description: "Internal server error",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
schemas: {
|
|
Error: {
|
|
type: "object",
|
|
properties: { error: { type: "string" } },
|
|
required: ["error"],
|
|
},
|
|
Token: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
created_by: { type: "integer" },
|
|
created_at: { type: "string", format: "date-time" },
|
|
last_used_at: { type: ["string", "null"], format: "date-time" },
|
|
expires_at: { type: ["string", "null"], format: "date-time" },
|
|
},
|
|
required: ["id", "name", "created_by", "created_at"],
|
|
},
|
|
TokenInput: {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: "string", example: "CI/CD Pipeline" },
|
|
expires_at: { type: "string", format: "date-time", description: "Optional expiration date" },
|
|
},
|
|
required: ["name"],
|
|
},
|
|
|
|
// ── Shared sub-schemas ──────────────────────────────────────
|
|
AuthentikConfig: {
|
|
type: "object",
|
|
description: "Authentik SSO forward-auth configuration",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
outpostDomain: { type: ["string", "null"], example: "auth.example.com" },
|
|
outpostUpstream: { type: ["string", "null"], example: "http://authentik:9000" },
|
|
authEndpoint: { type: ["string", "null"] },
|
|
copyHeaders: { type: "array", items: { type: "string" }, description: "Headers to copy from Authentik response" },
|
|
trustedProxies: { type: "array", items: { type: "string" }, example: ["private_ranges"] },
|
|
setOutpostHostHeader: { type: "boolean" },
|
|
protectedPaths: { type: ["array", "null"], items: { type: "string" }, description: "Paths to protect (null = all)" },
|
|
excludedPaths: { type: ["array", "null"], items: { type: "string" }, description: "Paths to exclude from auth (bypassed while rest is protected)" },
|
|
},
|
|
},
|
|
LoadBalancerConfig: {
|
|
type: "object",
|
|
description: "Load balancing configuration for multiple upstreams",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
policy: { type: "string", enum: ["random", "round_robin", "least_conn", "ip_hash", "first", "header", "cookie", "uri_hash"] },
|
|
policyHeaderField: { type: ["string", "null"], description: "Header name for 'header' policy" },
|
|
policyCookieName: { type: ["string", "null"], description: "Cookie name for 'cookie' policy" },
|
|
policyCookieSecret: { type: ["string", "null"] },
|
|
tryDuration: { type: ["string", "null"], example: "5s" },
|
|
tryInterval: { type: ["string", "null"], example: "250ms" },
|
|
retries: { type: ["integer", "null"] },
|
|
activeHealthCheck: {
|
|
type: ["object", "null"],
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
uri: { type: ["string", "null"], example: "/health" },
|
|
port: { type: ["integer", "null"] },
|
|
interval: { type: ["string", "null"], example: "30s" },
|
|
timeout: { type: ["string", "null"], example: "5s" },
|
|
status: { type: ["integer", "null"], example: 200 },
|
|
body: { type: ["string", "null"] },
|
|
},
|
|
},
|
|
passiveHealthCheck: {
|
|
type: ["object", "null"],
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
failDuration: { type: ["string", "null"], example: "30s" },
|
|
maxFails: { type: ["integer", "null"], example: 3 },
|
|
unhealthyStatus: { type: ["array", "null"], items: { type: "integer" } },
|
|
unhealthyLatency: { type: ["string", "null"], example: "5s" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
L4LoadBalancerConfig: {
|
|
type: "object",
|
|
description: "L4 load balancing configuration",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
policy: { type: "string", enum: ["random", "round_robin", "least_conn", "ip_hash", "first"] },
|
|
tryDuration: { type: ["string", "null"] },
|
|
tryInterval: { type: ["string", "null"] },
|
|
retries: { type: ["integer", "null"] },
|
|
activeHealthCheck: {
|
|
type: ["object", "null"],
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
port: { type: ["integer", "null"] },
|
|
interval: { type: ["string", "null"] },
|
|
timeout: { type: ["string", "null"] },
|
|
},
|
|
},
|
|
passiveHealthCheck: {
|
|
type: ["object", "null"],
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
failDuration: { type: ["string", "null"] },
|
|
maxFails: { type: ["integer", "null"] },
|
|
unhealthyLatency: { type: ["string", "null"] },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DnsResolverConfig: {
|
|
type: "object",
|
|
description: "Custom DNS resolver for upstream resolution",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
resolvers: { type: "array", items: { type: "string" }, example: ["1.1.1.1", "8.8.8.8"] },
|
|
fallbacks: { type: ["array", "null"], items: { type: "string" } },
|
|
timeout: { type: ["string", "null"], example: "5s" },
|
|
},
|
|
},
|
|
UpstreamDnsResolutionConfig: {
|
|
type: "object",
|
|
description: "Upstream DNS address family preference",
|
|
properties: {
|
|
enabled: { type: ["boolean", "null"] },
|
|
family: { type: ["string", "null"], enum: ["ipv4", "ipv6", "both", null] },
|
|
},
|
|
},
|
|
GeoBlockConfig: {
|
|
type: "object",
|
|
description: "Geographic/network-based access control",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
block_countries: { type: "array", items: { type: "string" }, example: ["CN", "RU"], description: "ISO 3166-1 alpha-2 codes" },
|
|
block_continents: { type: "array", items: { type: "string" }, example: ["AS"], description: "AF, AN, AS, EU, NA, OC, SA" },
|
|
block_asns: { type: "array", items: { type: "integer" } },
|
|
block_cidrs: { type: "array", items: { type: "string" }, example: ["10.0.0.0/8"] },
|
|
block_ips: { type: "array", items: { type: "string" } },
|
|
allow_countries: { type: "array", items: { type: "string" } },
|
|
allow_continents: { type: "array", items: { type: "string" } },
|
|
allow_asns: { type: "array", items: { type: "integer" } },
|
|
allow_cidrs: { type: "array", items: { type: "string" } },
|
|
allow_ips: { type: "array", items: { type: "string" } },
|
|
trusted_proxies: { type: "array", items: { type: "string" }, description: "Trusted proxy CIDRs for X-Forwarded-For" },
|
|
fail_closed: { type: "boolean", description: "Block when client IP cannot be determined" },
|
|
response_status: { type: "integer", example: 403 },
|
|
response_body: { type: "string", example: "Forbidden" },
|
|
response_headers: { type: "object", additionalProperties: { type: "string" }, example: { "Content-Type": "text/plain", "X-Custom": "blocked" }, description: "Custom response headers (header name → value)" },
|
|
redirect_url: { type: "string", description: "If set, 302 redirect instead of status/body" },
|
|
},
|
|
},
|
|
WafConfig: {
|
|
type: "object",
|
|
description: "Web Application Firewall configuration",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
mode: { type: "string", enum: ["Off", "On"] },
|
|
load_owasp_crs: { type: "boolean", description: "Load OWASP Core Rule Set" },
|
|
custom_directives: { type: "string", description: "Custom WAF directives" },
|
|
excluded_rule_ids: { type: "array", items: { type: "integer" }, description: "Rule IDs to exclude" },
|
|
waf_mode: { type: "string", enum: ["merge", "override"], description: "How per-host WAF merges with global" },
|
|
},
|
|
},
|
|
MtlsConfig: {
|
|
type: "object",
|
|
description: "Mutual TLS (client certificate) configuration",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
ca_certificate_ids: { type: "array", items: { type: "integer" }, description: "CA certificate IDs to trust" },
|
|
},
|
|
},
|
|
RedirectRule: {
|
|
type: "object",
|
|
description: "HTTP redirect rule",
|
|
properties: {
|
|
from: { type: "string", example: "/.well-known/carddav", description: "Path pattern to match" },
|
|
to: { type: "string", example: "/remote.php/dav/", description: "Redirect destination" },
|
|
status: { type: "integer", enum: [301, 302, 307, 308], example: 301 },
|
|
},
|
|
required: ["from", "to", "status"],
|
|
},
|
|
RewriteConfig: {
|
|
type: "object",
|
|
description: "Path rewrite (strip prefix)",
|
|
properties: {
|
|
path_prefix: { type: "string", example: "/app", description: "Prefix to strip from request path" },
|
|
},
|
|
required: ["path_prefix"],
|
|
},
|
|
LocationRule: {
|
|
type: "object",
|
|
description: "Route a path pattern to specific upstream servers (like nginx location blocks)",
|
|
properties: {
|
|
path: { type: "string", example: "/ws/*", description: "Caddy path pattern to match" },
|
|
upstreams: { type: "array", items: { type: "string" }, example: ["ws-backend:8080", "ws-backend2:8080"], description: "Upstream servers for this path" },
|
|
},
|
|
required: ["path", "upstreams"],
|
|
},
|
|
|
|
// ── Main resource schemas ───────────────────────────────────
|
|
ProxyHost: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
domains: { type: "array", items: { type: "string" }, example: ["example.com", "www.example.com"] },
|
|
upstreams: { type: "array", items: { type: "string" }, example: ["localhost:8080"] },
|
|
certificate_id: { type: ["integer", "null"] },
|
|
access_list_id: { type: ["integer", "null"] },
|
|
ssl_forced: { type: "boolean" },
|
|
hsts_enabled: { type: "boolean" },
|
|
hsts_subdomains: { type: "boolean" },
|
|
allow_websocket: { type: "boolean" },
|
|
preserve_host_header: { type: "boolean" },
|
|
skip_https_hostname_validation: { type: "boolean" },
|
|
enabled: { type: "boolean" },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
custom_reverse_proxy_json: { type: ["string", "null"], description: "Raw Caddy JSON for reverse_proxy handler" },
|
|
custom_pre_handlers_json: { type: ["string", "null"], description: "Raw Caddy JSON for handlers before reverse_proxy" },
|
|
authentik: { oneOf: [{ $ref: "#/components/schemas/AuthentikConfig" }, { type: "null" }] },
|
|
load_balancer: { oneOf: [{ $ref: "#/components/schemas/LoadBalancerConfig" }, { type: "null" }] },
|
|
dns_resolver: { oneOf: [{ $ref: "#/components/schemas/DnsResolverConfig" }, { type: "null" }] },
|
|
upstream_dns_resolution: { oneOf: [{ $ref: "#/components/schemas/UpstreamDnsResolutionConfig" }, { type: "null" }] },
|
|
geoblock: { oneOf: [{ $ref: "#/components/schemas/GeoBlockConfig" }, { type: "null" }] },
|
|
geoblock_mode: { type: "string", enum: ["merge", "override"], description: "How per-host geoblock merges with global" },
|
|
waf: { oneOf: [{ $ref: "#/components/schemas/WafConfig" }, { type: "null" }] },
|
|
mtls: { oneOf: [{ $ref: "#/components/schemas/MtlsConfig" }, { type: "null" }] },
|
|
redirects: { type: "array", items: { $ref: "#/components/schemas/RedirectRule" } },
|
|
rewrite: { oneOf: [{ $ref: "#/components/schemas/RewriteConfig" }, { type: "null" }] },
|
|
location_rules: { type: "array", items: { $ref: "#/components/schemas/LocationRule" }, description: "Path-based routing rules (routes specific paths to different upstreams)" },
|
|
},
|
|
required: ["id", "name", "domains", "upstreams", "enabled", "created_at", "updated_at"],
|
|
},
|
|
ProxyHostInput: {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: "string", example: "My App" },
|
|
domains: { type: "array", items: { type: "string" }, example: ["app.example.com"] },
|
|
upstreams: { type: "array", items: { type: "string" }, example: ["localhost:3000"] },
|
|
certificate_id: { type: ["integer", "null"] },
|
|
access_list_id: { type: ["integer", "null"] },
|
|
ssl_forced: { type: "boolean" },
|
|
hsts_enabled: { type: "boolean" },
|
|
hsts_subdomains: { type: "boolean" },
|
|
allow_websocket: { type: "boolean" },
|
|
preserve_host_header: { type: "boolean" },
|
|
skip_https_hostname_validation: { type: "boolean" },
|
|
enabled: { type: "boolean" },
|
|
custom_reverse_proxy_json: { type: ["string", "null"] },
|
|
custom_pre_handlers_json: { type: ["string", "null"] },
|
|
authentik: { oneOf: [{ $ref: "#/components/schemas/AuthentikConfig" }, { type: "null" }] },
|
|
load_balancer: { oneOf: [{ $ref: "#/components/schemas/LoadBalancerConfig" }, { type: "null" }] },
|
|
dns_resolver: { oneOf: [{ $ref: "#/components/schemas/DnsResolverConfig" }, { type: "null" }] },
|
|
upstream_dns_resolution: { oneOf: [{ $ref: "#/components/schemas/UpstreamDnsResolutionConfig" }, { type: "null" }] },
|
|
geoblock: { oneOf: [{ $ref: "#/components/schemas/GeoBlockConfig" }, { type: "null" }] },
|
|
geoblock_mode: { type: "string", enum: ["merge", "override"] },
|
|
waf: { oneOf: [{ $ref: "#/components/schemas/WafConfig" }, { type: "null" }] },
|
|
mtls: { oneOf: [{ $ref: "#/components/schemas/MtlsConfig" }, { type: "null" }] },
|
|
redirects: { type: "array", items: { $ref: "#/components/schemas/RedirectRule" } },
|
|
rewrite: { oneOf: [{ $ref: "#/components/schemas/RewriteConfig" }, { type: "null" }] },
|
|
location_rules: { type: "array", items: { $ref: "#/components/schemas/LocationRule" }, description: "Path-based routing rules (routes specific paths to different upstreams)" },
|
|
},
|
|
required: ["name", "domains", "upstreams"],
|
|
},
|
|
L4ProxyHost: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
listen_addresses: { type: "array", items: { type: "string" }, example: [":5432"] },
|
|
matchers: { type: "array", items: { type: "string" } },
|
|
upstreams: { type: "array", items: { type: "string" }, example: ["db-server:5432"] },
|
|
protocol: { type: "string", enum: ["tcp", "udp"] },
|
|
matcher_type: { type: "string", enum: ["none", "tls_sni", "http_host", "proxy_protocol"] },
|
|
tls_termination: { type: "boolean" },
|
|
proxy_protocol_version: { type: ["string", "null"], enum: ["v1", "v2", null] },
|
|
enabled: { type: "boolean" },
|
|
load_balancer: { oneOf: [{ $ref: "#/components/schemas/L4LoadBalancerConfig" }, { type: "null" }] },
|
|
dns_resolver: { oneOf: [{ $ref: "#/components/schemas/DnsResolverConfig" }, { type: "null" }] },
|
|
upstream_dns_resolution: { oneOf: [{ $ref: "#/components/schemas/UpstreamDnsResolutionConfig" }, { type: "null" }] },
|
|
geoblock: { oneOf: [{ $ref: "#/components/schemas/GeoBlockConfig" }, { type: "null" }] },
|
|
geoblock_mode: { type: "string", enum: ["merge", "override"] },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "name", "listen_addresses", "upstreams", "protocol", "enabled", "created_at", "updated_at"],
|
|
},
|
|
L4ProxyHostInput: {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: "string", example: "PostgreSQL Proxy" },
|
|
listen_addresses: { type: "array", items: { type: "string" }, example: [":5432"] },
|
|
matchers: { type: "array", items: { type: "string" } },
|
|
upstreams: { type: "array", items: { type: "string" }, example: ["db:5432"] },
|
|
protocol: { type: "string", enum: ["tcp", "udp"] },
|
|
matcher_type: { type: "string", enum: ["none", "tls_sni", "http_host", "proxy_protocol"] },
|
|
tls_termination: { type: "boolean" },
|
|
proxy_protocol_version: { type: ["string", "null"], enum: ["v1", "v2", null] },
|
|
enabled: { type: "boolean" },
|
|
load_balancer: { oneOf: [{ $ref: "#/components/schemas/L4LoadBalancerConfig" }, { type: "null" }] },
|
|
dns_resolver: { oneOf: [{ $ref: "#/components/schemas/DnsResolverConfig" }, { type: "null" }] },
|
|
upstream_dns_resolution: { oneOf: [{ $ref: "#/components/schemas/UpstreamDnsResolutionConfig" }, { type: "null" }] },
|
|
geoblock: { oneOf: [{ $ref: "#/components/schemas/GeoBlockConfig" }, { type: "null" }] },
|
|
geoblock_mode: { type: "string", enum: ["merge", "override"] },
|
|
},
|
|
required: ["name", "listen_addresses", "upstreams", "protocol"],
|
|
},
|
|
Certificate: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
type: { type: "string", enum: ["managed", "imported"] },
|
|
domain_names: { type: "array", items: { type: "string" }, example: ["example.com", "*.example.com"] },
|
|
auto_renew: { type: "boolean" },
|
|
provider_options: {
|
|
type: ["object", "null"],
|
|
description: "Provider-specific options (e.g. Cloudflare API token)",
|
|
properties: {
|
|
api_token: { type: "string", description: "DNS provider API token" },
|
|
},
|
|
},
|
|
certificate_pem: { type: ["string", "null"], description: "PEM-encoded certificate (imported type only)" },
|
|
private_key_pem: { type: ["string", "null"], description: "PEM-encoded private key (imported type only)" },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "name", "type", "domain_names", "created_at", "updated_at"],
|
|
},
|
|
CertificateInput: {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: "string", example: "Wildcard Cert" },
|
|
type: { type: "string", enum: ["managed", "imported"] },
|
|
domain_names: { type: "array", items: { type: "string" } },
|
|
auto_renew: { type: "boolean" },
|
|
provider_options: { type: ["object", "null"], properties: { api_token: { type: "string" } } },
|
|
certificate_pem: { type: ["string", "null"] },
|
|
private_key_pem: { type: ["string", "null"] },
|
|
},
|
|
required: ["name", "type", "domain_names"],
|
|
},
|
|
CaCertificate: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
certificate_pem: { type: "string", description: "PEM-encoded CA certificate" },
|
|
has_private_key: { type: "boolean", description: "Whether a private key is stored (for issuing client certs)" },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "name", "certificate_pem", "has_private_key", "created_at", "updated_at"],
|
|
},
|
|
CaCertificateInput: {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: "string", example: "Internal CA" },
|
|
certificate_pem: { type: "string", description: "PEM-encoded CA certificate" },
|
|
private_key_pem: { type: "string", description: "PEM-encoded private key (optional, needed for issuing client certs)" },
|
|
},
|
|
required: ["name", "certificate_pem"],
|
|
},
|
|
ClientCertificate: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
ca_certificate_id: { type: "integer" },
|
|
common_name: { type: "string", example: "client-device-01" },
|
|
serial_number: { type: "string" },
|
|
fingerprint_sha256: { type: "string" },
|
|
certificate_pem: { type: "string" },
|
|
valid_from: { type: "string", format: "date-time" },
|
|
valid_to: { type: "string", format: "date-time" },
|
|
revoked_at: { type: ["string", "null"], format: "date-time" },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "ca_certificate_id", "common_name", "serial_number", "fingerprint_sha256", "certificate_pem", "valid_from", "valid_to", "created_at", "updated_at"],
|
|
},
|
|
ClientCertificateInput: {
|
|
type: "object",
|
|
properties: {
|
|
ca_certificate_id: { type: "integer", description: "ID of the CA certificate to issue from" },
|
|
common_name: { type: "string", example: "client-device-01" },
|
|
},
|
|
required: ["ca_certificate_id", "common_name"],
|
|
},
|
|
AccessList: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
description: { type: ["string", "null"] },
|
|
entries: { type: "array", items: { $ref: "#/components/schemas/AccessListEntry" } },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "name", "entries", "created_at", "updated_at"],
|
|
},
|
|
AccessListInput: {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: "string", example: "Internal Users" },
|
|
description: { type: ["string", "null"] },
|
|
users: {
|
|
type: "array",
|
|
description: "Seed members (only used during creation)",
|
|
items: {
|
|
type: "object",
|
|
properties: {
|
|
username: { type: "string" },
|
|
password: { type: "string" },
|
|
},
|
|
required: ["username", "password"],
|
|
},
|
|
},
|
|
},
|
|
required: ["name"],
|
|
},
|
|
AccessListEntry: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
username: { type: "string" },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "username", "created_at", "updated_at"],
|
|
},
|
|
AccessListEntryInput: {
|
|
type: "object",
|
|
properties: {
|
|
username: { type: "string", example: "admin" },
|
|
password: { type: "string", example: "secret123" },
|
|
},
|
|
required: ["username", "password"],
|
|
},
|
|
|
|
// ── Settings schemas ────────────────────────────────────────
|
|
GeneralSettings: {
|
|
type: "object",
|
|
properties: {
|
|
primaryDomain: { type: "string", example: "example.com" },
|
|
acmeEmail: { type: "string", format: "email", example: "admin@example.com" },
|
|
},
|
|
required: ["primaryDomain"],
|
|
},
|
|
CloudflareSettings: {
|
|
type: "object",
|
|
properties: {
|
|
apiToken: { type: "string", description: "Cloudflare API token" },
|
|
zoneId: { type: "string" },
|
|
accountId: { type: "string" },
|
|
},
|
|
required: ["apiToken"],
|
|
},
|
|
AuthentikSettings: {
|
|
type: "object",
|
|
properties: {
|
|
outpostDomain: { type: "string", example: "auth.example.com" },
|
|
outpostUpstream: { type: "string", example: "http://authentik:9000" },
|
|
authEndpoint: { type: "string" },
|
|
},
|
|
required: ["outpostDomain", "outpostUpstream"],
|
|
},
|
|
MetricsSettings: {
|
|
type: "object",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
port: { type: "integer", example: 9090, description: "Prometheus metrics port" },
|
|
},
|
|
required: ["enabled"],
|
|
},
|
|
LoggingSettings: {
|
|
type: "object",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
format: { type: "string", enum: ["json", "console"] },
|
|
},
|
|
required: ["enabled"],
|
|
},
|
|
DnsSettings: {
|
|
type: "object",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
resolvers: { type: "array", items: { type: "string" }, example: ["1.1.1.1", "8.8.8.8"] },
|
|
fallbacks: { type: "array", items: { type: "string" } },
|
|
timeout: { type: "string", example: "5s" },
|
|
},
|
|
required: ["enabled", "resolvers"],
|
|
},
|
|
UpstreamDnsSettings: {
|
|
type: "object",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
family: { type: "string", enum: ["ipv4", "ipv6", "both"] },
|
|
},
|
|
required: ["enabled", "family"],
|
|
},
|
|
WafSettings: {
|
|
type: "object",
|
|
description: "Global WAF settings",
|
|
properties: {
|
|
enabled: { type: "boolean" },
|
|
mode: { type: "string", enum: ["Off", "On"] },
|
|
load_owasp_crs: { type: "boolean" },
|
|
custom_directives: { type: "string" },
|
|
excluded_rule_ids: { type: "array", items: { type: "integer" } },
|
|
},
|
|
required: ["enabled", "mode", "load_owasp_crs", "custom_directives"],
|
|
},
|
|
|
|
// ── Groups & Roles ─────────────────────────────────────────
|
|
Group: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
description: { type: ["string", "null"] },
|
|
member_count: { type: "integer" },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "name", "created_at", "updated_at"],
|
|
},
|
|
MtlsRole: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
description: { type: ["string", "null"] },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "name", "created_at", "updated_at"],
|
|
},
|
|
|
|
// ── Other resources ─────────────────────────────────────────
|
|
Instance: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
base_url: { type: "string", example: "https://slave.example.com:3000" },
|
|
enabled: { type: "boolean" },
|
|
has_token: { type: "boolean" },
|
|
last_sync_at: { type: ["string", "null"], format: "date-time" },
|
|
last_sync_error: { type: ["string", "null"] },
|
|
created_at: { type: "string", format: "date-time" },
|
|
updated_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "name", "base_url", "enabled", "has_token", "created_at", "updated_at"],
|
|
},
|
|
InstanceInput: {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: "string", example: "Slave 1" },
|
|
baseUrl: { type: "string", example: "https://slave.example.com:3000" },
|
|
apiToken: { type: "string", description: "Sync token for the slave instance" },
|
|
enabled: { type: "boolean" },
|
|
},
|
|
required: ["name", "baseUrl", "apiToken"],
|
|
},
|
|
SyncResult: {
|
|
type: "object",
|
|
properties: {
|
|
total: { type: "integer" },
|
|
success: { type: "integer" },
|
|
failed: { type: "integer" },
|
|
skippedHttp: { type: "integer" },
|
|
},
|
|
required: ["total", "success", "failed", "skippedHttp"],
|
|
},
|
|
User: {
|
|
type: "object",
|
|
description: "User account (passwordHash is never exposed)",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
email: { type: "string" },
|
|
name: { type: ["string", "null"] },
|
|
role: { type: "string", enum: ["admin", "user", "viewer"] },
|
|
provider: { type: "string", example: "credentials" },
|
|
subject: { type: "string" },
|
|
avatarUrl: { type: ["string", "null"] },
|
|
status: { type: "string", enum: ["active", "inactive"] },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
updatedAt: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "email", "role", "provider", "subject", "status", "createdAt", "updatedAt"],
|
|
},
|
|
AuditLogEvent: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
user_id: { type: ["integer", "null"] },
|
|
action: { type: "string", example: "proxy_host_created" },
|
|
entity_type: { type: "string", example: "proxy_host" },
|
|
entity_id: { type: ["integer", "null"] },
|
|
summary: { type: ["string", "null"] },
|
|
created_at: { type: "string", format: "date-time" },
|
|
},
|
|
required: ["id", "action", "entity_type", "created_at"],
|
|
},
|
|
AuditLogResponse: {
|
|
type: "object",
|
|
properties: {
|
|
events: { type: "array", items: { $ref: "#/components/schemas/AuditLogEvent" } },
|
|
total: { type: "integer" },
|
|
page: { type: "integer" },
|
|
perPage: { type: "integer" },
|
|
},
|
|
required: ["events", "total", "page", "perPage"],
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
await requireApiAdmin(request);
|
|
} catch (error) {
|
|
return apiErrorResponse(error);
|
|
}
|
|
return NextResponse.json(spec, {
|
|
headers: {
|
|
"Cache-Control": "private, max-age=3600",
|
|
},
|
|
});
|
|
}
|