// ============================================================ // OPENAPI — what each endpoint is for // ============================================================ // Paths and methods are read from the router (src/utils/openapi.js). This is // the half introspection cannot supply: what an operation does, what it takes, // and whether it needs a session. // // Keyed by "METHOD /path" exactly as the route is mounted, with :params left as // they appear in the code. A route with no entry here fails test/openapi.test.js // — that is the point. A new endpoint cannot ship without a sentence saying what // it is, and an entry for an endpoint that no longer exists fails the same test // from the other direction. // // `public: true` marks an operation that works with no session. Say it // explicitly: guessing from middleware gets it wrong in the direction that // hides a hole. // Path parameters, described once rather than at every route that takes them. var parameters = { id: 'Identifier of the record, scoped to the signed-in account.', workflow: 'Which feature the job belongs to (for example my_resources).', key: 'Setting key, for example clinical_assistant.chat_model.', slug: 'URL-safe name of the document.' }; var operations = { // ── Speech ────────────────────────────────────────────────────────── 'GET /api/nextcloud/config': { summary: 'The site\'s Nextcloud address, if one is set', description: 'nextcloud.url (or NEXTCLOUD_URL). When present the settings page hides the address field and the sign-in and app-password routes use it when none is given.' }, 'PUT /api/admin/config/tts/default': { summary: 'Choose the default speech model and voice', description: 'Sets tts.model and tts.voice together and puts the model on the speech roster (tts.roster) if it is not there. Refused when the model does not accept the voice; the error lists the voices it does accept.', requestBody: { required: true, content: { 'application/json': { schema: { type: 'object', required: ['model'], properties: { model: { type: 'string', description: 'Gateway model id, for example local-kokoro-tts.' }, voice: { type: 'string', description: 'A voice of that model. Omitted: its first voice.' } } } } } } }, // ── Session ───────────────────────────────────────────────────────── 'POST /api/auth/login': { summary: 'Sign in with a password', description: 'Returns a token and sets the ped_auth cookie. Rate limited.', public: true }, 'POST /api/auth/register': { summary: 'Create a local password account', description: 'Refused while sign-in is SSO-only; accounts are then created at the SSO from an invitation link.', public: true }, 'GET /api/auth/registration-status': { summary: 'Whether local registration is open', description: 'Read by the sign-in screen to decide whether to offer the register link.', public: true }, 'POST /api/auth/logout': { summary: 'End this session' }, 'GET /api/auth/me': { summary: 'The signed-in account' }, // ── Clinical assistant ────────────────────────────────────────────── 'POST /api/clinical-assistant/ask': { summary: 'Ask a question against the clinical corpus', description: 'Streams the answer as server-sent events, with the retrieved sources.' }, // ── My Resources ──────────────────────────────────────────────────── 'GET /api/my-resources': { summary: 'Teaching material belonging to this account' }, 'GET /api/my-resources/:id': { summary: 'One saved resource' }, 'POST /api/my-resources/generate': { summary: 'Generate a deck or handout' }, 'POST /api/my-resources/:id/refine': { summary: 'Revise a saved resource' }, 'GET /api/my-resources/:id/export': { summary: 'Download as PowerPoint, Word or PDF' }, 'GET /api/my-resources/theme-sample/:id': { summary: 'A sample deck in one theme', description: 'Every slide layout with placeholder text, as a PowerPoint file, so a theme can be judged before it is used.' }, 'POST /api/my-resources/:id/to-nextcloud': { summary: 'Send the rendered file to the owner\'s Nextcloud' }, // ── Health ────────────────────────────────────────────────────────── 'GET /api/health': { summary: 'Liveness', public: true }, 'GET /api/build': { summary: 'The revision this container is running', public: true } }; module.exports = { operations: operations, parameters: parameters };