diff --git a/server/swagger/swagger-output.json b/server/swagger/swagger-output.json index 1e36cec..9e1f6a0 100644 --- a/server/swagger/swagger-output.json +++ b/server/swagger/swagger-output.json @@ -1179,6 +1179,110 @@ } } }, + "/api/v1/public/pages/{id}/preview/{token}": { + "get": { + "tags": [ + "Public" + ], + "summary": "Render a page from a draft-preview token", + "description": "", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "description": "Page id." + }, + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Preview token from POST /admin/pages/:id/preview." + } + ], + "responses": { + "200": { + "description": "The page (any status)", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "404": { + "description": "Token invalid/expired or page missing", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/public/pages/{slug}": { + "get": { + "tags": [ + "Public" + ], + "summary": "Get a published CMS page by slug", + "description": "Drafts 404 for the public; staff sessions see drafts. Gated by site mode.", + "parameters": [ + { + "name": "slug", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Page slug." + } + ], + "responses": { + "200": { + "description": "The page", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "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": [ @@ -1206,6 +1310,9 @@ } } } + }, + "500": { + "description": "Internal Server Error" } }, "security": [ @@ -3194,6 +3301,464 @@ ] } }, + "/api/v1/admin/pages": { + "get": { + "tags": [ + "Admin · Pages" + ], + "summary": "List all CMS pages (summaries)", + "description": "", + "responses": { + "200": { + "description": "Page summaries", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "bearerAuth": [] + } + ] + }, + "post": { + "tags": [ + "Admin · Pages" + ], + "summary": "Create a CMS page", + "description": "", + "responses": { + "201": { + "description": "Created page", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "400": { + "description": "Invalid slug / title / blocks / metadata / settings", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "409": { + "description": "Slug already exists", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "draft", + "published" + ] + }, + "blocks": { + "type": "array", + "items": { + "type": "object" + } + }, + "metadata": { + "type": "object" + }, + "settings": { + "type": "object" + } + } + } + } + } + } + } + }, + "/api/v1/admin/pages/{id}": { + "get": { + "tags": [ + "Admin · Pages" + ], + "summary": "Get a CMS page by id (full, incl. blocks)", + "description": "", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "description": "Page id." + } + ], + "responses": { + "200": { + "description": "The page", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "bearerAuth": [] + } + ] + }, + "patch": { + "tags": [ + "Admin · Pages" + ], + "summary": "Update a CMS page (title, status, blocks, metadata, settings)", + "description": "slug is immutable; disabling protection is rejected here (use /unprotect).", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "description": "Page id." + } + ], + "responses": { + "200": { + "description": "Updated page", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "400": { + "description": "Validation error (slug immutable, invalid blocks, etc.)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "403": { + "description": "Disabling protection requires /unprotect", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "delete": { + "tags": [ + "Admin · Pages" + ], + "summary": "Delete a CMS page (blocked if protected)", + "description": "", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "description": "Page id." + } + ], + "responses": { + "200": { + "description": "Deleted (echoes the id)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedId" + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "403": { + "description": "Page is protected", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/admin/pages/{id}/unprotect": { + "post": { + "tags": [ + "Admin · Pages" + ], + "summary": "Disable page protection (password step-up re-auth)", + "description": "Verifies the current admin password server-side, then flips protected → false.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "description": "Page id." + } + ], + "responses": { + "200": { + "description": "Updated page (protected=false)", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "401": { + "description": "Password incorrect", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "required": [ + "password" + ] + } + } + } + } + } + }, + "/api/v1/admin/pages/{id}/preview": { + "post": { + "tags": [ + "Admin · Pages" + ], + "summary": "Mint a 1h draft-preview link for a page", + "description": "", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "description": "Page id." + } + ], + "responses": { + "200": { + "description": "Preview token + path", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "expiresInSeconds": { + "type": "integer" + }, + "path": { + "type": "string" + } + } + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "bearerAuth": [] + } + ] + } + }, "/api/v1/admin/settings": { "get": { "tags": [ @@ -3638,6 +4203,367 @@ } } }, + "/api/v1/admin/email/config": { + "get": { + "tags": [ + "Admin · Email" + ], + "summary": "Get email delivery config + status (admin only)", + "description": "", + "responses": { + "200": { + "description": "Config (refresh token stripped) + status", + "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 · Email" + ], + "summary": "Update email delivery config (admin only)", + "description": "Set the From display name and enabled toggle. Enabling requires a connected Gmail account.", + "responses": { + "200": { + "description": "Updated config", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "400": { + "description": "Cannot enable before connecting a mailbox", + "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": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "senderName": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + } + } + } + } + } + } + }, + "/api/v1/admin/email/connect/start": { + "get": { + "tags": [ + "Admin · Email" + ], + "summary": "Begin the Gmail OAuth2 connect flow (admin only)", + "description": "Returns { url } to redirect the browser to Google. Reuses the google SSO OAuth client.", + "responses": { + "200": { + "description": "Authorization URL", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + } + } + } + }, + "400": { + "description": "Google OAuth client not configured", + "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": [] + } + ] + } + }, + "/api/v1/admin/email/connect/callback": { + "get": { + "tags": [ + "Admin · Email" + ], + "summary": "OAuth2 callback — stores the refresh token, redirects to Settings", + "description": "", + "parameters": [ + { + "name": "code", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "state", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "error", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "302": { + "description": "Redirect back to /admin/settings" + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/admin/email/test": { + "post": { + "tags": [ + "Admin · Email" + ], + "summary": "Send a test email (admin only)", + "description": "", + "responses": { + "200": { + "description": "Sent", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sent": { + "type": "boolean" + }, + "to": { + "type": "string" + } + } + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "502": { + "description": "Send failed / not configured", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "to": { + "type": "string", + "format": "email" + } + } + } + } + } + } + } + }, + "/api/v1/admin/email/disconnect": { + "post": { + "tags": [ + "Admin · Email" + ], + "summary": "Disconnect Gmail and disable email (admin only)", + "description": "", + "responses": { + "200": { + "description": "Disconnected config", + "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": [] + } + ] + } + }, "/api/v1/admin/auth/providers": { "get": { "tags": [