OpenAPI spec at /api/docs with a Scalar viewer, opt-in via FEAT_API_DOCS

This commit is contained in:
jarek committed 2026-08-17 09:50:34 +02:00
1 parent ba8670af66
commit da26f7f764
251 files changed
+32497 -66

No files matched your search

+17
View File
@@ -10,6 +10,13 @@ import { authorize } from '$lib/server/authorize';
import { auditNotification } from '$lib/server/audit';
import type { RequestHandler } from './$types';
/**
* @openapi
* summary: List all notification settings (SMTP and Apprise) with SMTP passwords masked
* resp-200: array<{id:integer!, type:string!, name:string!, enabled:boolean!, config:{host:string, port:integer, from_email:string, to_emails:array<string>, urls:array<string>, password:string}, eventTypes:array<string>}>
* resp-403: Permission denied (requires the notifications:view permission when auth is enabled)
* resp-500: Failed to fetch notification settings
*/
export const GET: RequestHandler = async ({ cookies }) => {
const auth = await authorize(cookies);
if (auth.authEnabled && !await auth.can('notifications', 'view')) {
@@ -33,6 +40,16 @@ export const GET: RequestHandler = async ({ cookies }) => {
}
};
/**
* @openapi
* summary: Create a notification setting (SMTP or Apprise); the response masks any SMTP password
* body: {type:string!, name:string!, enabled:boolean, config:{host:string, port:integer, secure:boolean, username:string, password:string, from_email:string, from_name:string, to_emails:array<string>, urls:array<string>}, eventTypes:array<string>, event_types:array<string>}
* body-example: {"type":"smtp","name":"Ops Alerts","enabled":true,"config":{"host":"smtp.example.com","port":587,"secure":false,"from_email":"dockhand@example.com","to_emails":["ops@example.com"],"password":"***"},"eventTypes":["container_unhealthy"]}
* resp-200: {id:integer!, type:string!, name:string!, enabled:boolean!, config:{}, eventTypes:array<string>}
* resp-400: Missing or invalid fields — type/name/config required, type must be smtp or apprise, SMTP needs host/port/from_email/to_emails, Apprise needs at least one URL
* resp-403: Permission denied (requires the notifications:create permission)
* resp-500: Failed to create notification setting
*/
export const POST: RequestHandler = async (event) => {
const { request, cookies } = event;
const auth = await authorize(cookies);
@@ -12,6 +12,16 @@ import { auditNotification } from '$lib/server/audit';
import { computeAuditDiff } from '$lib/utils/diff';
import type { RequestHandler } from './$types';
/**
* @openapi
* summary: Get a single notification setting by ID, with any SMTP password masked
* path: id:integer! Notification setting ID (from GET /api/notifications)
* resp-200: {id:integer!, type:string!, name:string!, enabled:boolean!, config:{host:string, port:integer, from_email:string, to_emails:array<string>, urls:array<string>, password:string}, eventTypes:array<string>}
* resp-400: Invalid ID (not a number)
* resp-403: Permission denied (requires the notifications:view permission)
* resp-404: Notification setting not found
* resp-500: Failed to fetch notification setting
*/
export const GET: RequestHandler = async ({ params, cookies }) => {
const auth = await authorize(cookies);
if (auth.authEnabled && !await auth.can('notifications', 'view')) {
@@ -45,6 +55,18 @@ export const GET: RequestHandler = async ({ params, cookies }) => {
}
};
/**
* @openapi
* summary: Update a notification setting; a masked SMTP password ("********") keeps the stored value
* path: id:integer! Notification setting ID (from GET /api/notifications)
* body: {name:string, enabled:boolean, config:{host:string, port:integer, secure:boolean, username:string, password:string, from_email:string, from_name:string, to_emails:array<string>, urls:array<string>}, eventTypes:array<string>, event_types:array<string>}
* body-example: {"name":"Ops Alerts","enabled":false,"config":{"host":"smtp.example.com","port":587,"from_email":"dockhand@example.com","to_emails":["ops@example.com"],"password":"********"},"eventTypes":["container_unhealthy","container_oom"]}
* resp-200: {id:integer!, type:string!, name:string!, enabled:boolean!, config:{}, eventTypes:array<string>}
* resp-400: Invalid ID, or config validation failed (SMTP needs host/port/from_email/to_emails, Apprise needs at least one URL)
* resp-403: Permission denied (requires the notifications:edit permission)
* resp-404: Notification setting not found
* resp-500: Failed to update notification setting
*/
export const PUT: RequestHandler = async (event) => {
const { params, request, cookies } = event;
const auth = await authorize(cookies);
@@ -122,6 +144,17 @@ export const PUT: RequestHandler = async (event) => {
}
};
/**
* @openapi
* summary: Delete a notification setting by ID
* path: id:integer! Notification setting ID (from GET /api/notifications)
* resp-200: {success:boolean!}
* resp-200-example: {"success":true}
* resp-400: Invalid ID (not a number)
* resp-403: Permission denied (requires the notifications:delete permission)
* resp-404: Notification setting not found
* resp-500: Failed to delete notification setting
*/
export const DELETE: RequestHandler = async (event) => {
const { params, cookies } = event;
const auth = await authorize(cookies);
@@ -3,6 +3,16 @@ import { getNotificationSetting } from '$lib/server/db';
import { testNotification } from '$lib/server/notifications';
import type { RequestHandler } from './$types';
/**
* @openapi
* summary: Send a test notification through an already-saved notification setting
* path: id:integer! Notification setting ID (from GET /api/notifications)
* resp-200: {success:boolean!, message:string, error:string}
* resp-200-example: {"success":true,"message":"Test notification sent successfully"}
* resp-400: Invalid ID (not a number)
* resp-404: Notification setting not found
* resp-500: Failed to test notification
*/
export const POST: RequestHandler = async ({ params }) => {
try {
const id = parseInt(params.id);
@@ -4,6 +4,17 @@ import { authorize } from '$lib/server/authorize';
import type { RequestHandler } from './$types';
// Test notification with provided config (without saving)
/**
* @openapi
* summary: Send a test notification using an ad-hoc config supplied in the body (nothing is saved)
* body: {type:string!, name:string, config:{host:string, port:integer, secure:boolean, username:string, password:string, from_email:string, from_name:string, to_emails:array<string>, urls:array<string>}}
* body-example: {"type":"smtp","name":"Test","config":{"host":"smtp.example.com","port":587,"from_email":"dockhand@example.com","to_emails":["ops@example.com"],"password":"***"}}
* resp-200: {success:boolean!, message:string, error:string}
* resp-200-example: {"success":true,"message":"Test notification sent successfully"}
* resp-400: Missing/invalid fields — type and config required; SMTP needs host/from_email/to_emails; Apprise needs at least one URL
* resp-403: Permission denied (requires the settings:edit permission)
* resp-500: Failed to test notification
*/
export const POST: RequestHandler = async ({ request, cookies }) => {
const auth = await authorize(cookies);
if (auth.authEnabled && !await auth.can('settings', 'edit')) {
@@ -6,12 +6,15 @@ import { NOTIFICATION_EVENT_TYPES, type NotificationEventType } from '$lib/serve
* Test endpoint to trigger notifications for any event type.
* This is intended for development/testing purposes only.
*
* POST /api/notifications/trigger-test
* Body: {
* eventType: string,
* environmentId?: number,
* payload: { title: string, message: string, type?: string }
* }
* @openapi
* summary: Trigger a real notification for a given event type (development/testing helper)
* description: environmentId from GET /api/environments.
* body: {eventType:string!, environmentId:integer, payload:{title:string!, message:string!, type:string}}
* body-example: {"eventType":"container_unhealthy","environmentId":1,"payload":{"title":"Container unhealthy","message":"web-1 is unhealthy","type":"warning"}}
* resp-200: {success:boolean!, sent:integer, eventType:string!, environmentId:integer}
* resp-200-example: {"success":true,"sent":1,"eventType":"container_unhealthy","environmentId":1}
* resp-400: eventType required, payload with title and message required, unknown event type, or environmentId missing for a non-system event
* resp-500: Unknown error while sending the notification
*/
export const POST: RequestHandler = async ({ request }) => {
try {
@@ -83,6 +86,10 @@ export const POST: RequestHandler = async ({ request }) => {
/**
* GET endpoint to list all available event types
*
* @openapi
* summary: List all available notification event types, grouped into categories
* resp-200: {eventTypes:array<{id:string!, label:string}>, categories:{container:array<string>, autoUpdate:array<string>, gitStack:array<string>, stack:array<string>, security:array<string>, system:array<string>}}
*/
export const GET: RequestHandler = async () => {
return json({