Source: Router/orgaSettings.js

// @ts-check
/**
 * @import {ExpressRequestAuthorized, ExpressResponse} from '../types.js'
 */

/**
 * OrgaSettings Router
 *
 * Endpoints for managing per-organisation settings stored in Vault.
 * All routes require admin privileges (checkAdmin middleware).
 *
 * Mounted at: /api/kpe20/orgaSettings  (see http-server.js)
 *
 * @swagger
 * tags:
 *   - name: OrgaSettings
 *     description: >
 *       Per-organisation settings stored in Vault —
 *       domain mappings and SMTP configuration.
 *
 * components:
 *   schemas:
 *     DomainMap:
 *       type: object
 *       description: Map of domain names to their type (internal or external)
 *       additionalProperties:
 *         type: string
 *         enum: [internal, external]
 *       example:
 *         myclub: internal
 *         myclub.de: external
 *     DomainValidationError:
 *       type: object
 *       properties:
 *         domain:
 *           type: string
 *           example: myclub
 *         error:
 *           type: string
 *           example: '"myclub" is a reserved system name.'
 *     AppEntry:
 *       type: object
 *       required: [domain, roles, title]
 *       properties:
 *         domain:
 *           type: string
 *           description: App URL or hostname
 *           example: db2.app.kpe.de
 *         roles:
 *           type: array
 *           items:
 *             type: string
 *           description: Keycloak roles that grant access
 *           example: [db-admin, db-user]
 *         title:
 *           type: string
 *           example: KPE Mitglieder Datenbank
 *         description:
 *           type: string
 *           example: KPE Filesharing
 *         port:
 *           type: integer
 *           example: 80
 *     AppsMap:
 *       type: object
 *       description: Map of app identifiers to their configuration
 *       additionalProperties:
 *         $ref: '#/components/schemas/AppEntry'
 *       example:
 *         member.app:
 *           domain: db2.app.kpe.de
 *           roles: [db-admin, db-user]
 *           title: KPE Mitglieder Datenbank
 *     IconUploadResponse:
 *       type: object
 *       properties:
 *         success:
 *           type: boolean
 *           example: true
 *         iconUrl:
 *           type: string
 *           format: uri
 *           description: Public URL of the uploaded icon in the commtool-public bucket
 *           example: https://minio.commtool.org/commtool-public/abc123/icons/member.app.png
 *     MailSettings:
 *       type: object
 *       properties:
 *         host:
 *           type: string
 *           example: smtp.example.com
 *         port:
 *           type: integer
 *           example: 587
 *         user:
 *           type: string
 *           example: noreply@example.com
 *         password:
 *           type: string
 *           description: Masked as •••••••• when reading; pass the sentinel to keep the existing password unchanged.
 *           example: ••••••••
 *         userName:
 *           type: string
 *           example: No-Reply
 *         from:
 *           type: string
 *           example: noreply@example.com
 *         secure:
 *           type: boolean
 *           example: true
 */

import express from 'express';
import { checkAdmin }               from '../utils/authChecks.js';
import { requestUpdateLogger }      from '../utils/requestLogger.js';
import * as orgaSettingsController  from './orgaSettings/controller.js';

/** @type {express.Express} */
const api = express();

// ── Domains ───────────────────────────────────────────────────────────────────

/**
 * @swagger
 * /api/kpe20/orgaSettings/domains:
 *   get:
 *     summary: Load domain settings for the current organisation
 *     description: >
 *       Returns the full domain map (domain → type) stored in Vault
 *       for the authenticated user's organisation.
 *     tags: [OrgaSettings]
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Domain map retrieved successfully
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 result:
 *                   $ref: '#/components/schemas/DomainMap'
 *       400:
 *         description: No organisation in session
 *       500:
 *         description: Failed to load domain settings
 */
// @ts-ignore
api.get('/domains', checkAdmin, orgaSettingsController.getDomainsController);

/**
 * @swagger
 * /api/kpe20/orgaSettings/domains:
 *   put:
 *     summary: Save domain settings for the current organisation
 *     description: >
 *       Validates and persists the domain map for the authenticated user's
 *       organisation in Vault.  Each entry maps a domain name to its type
 *       ("internal" or "external").  Validation checks format rules and
 *       conflicts with other organisations.
 *     tags: [OrgaSettings]
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             $ref: '#/components/schemas/DomainMap'
 *           example:
 *             myclub: internal
 *             myclub.de: external
 *     responses:
 *       200:
 *         description: Domains saved successfully
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       400:
 *         description: Validation errors or missing organisation
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: false
 *                 errors:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/DomainValidationError'
 *       500:
 *         description: Failed to save domain settings
 */
// @ts-ignore
api.put('/domains', checkAdmin, requestUpdateLogger, orgaSettingsController.saveDomainsController);

// ── Apps ─────────────────────────────────────────────────────────────────────

/**
 * @swagger
 * /api/kpe20/orgaSettings/apps:
 *   get:
 *     summary: Load app registry for the current organisation
 *     description: >
 *       Returns all configured apps (domain, roles, title, etc.) stored in
 *       Vault for the authenticated user's organisation.
 *     tags: [OrgaSettings]
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: App registry retrieved successfully
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 result:
 *                   $ref: '#/components/schemas/AppsMap'
 *       400:
 *         description: No organisation in session
 *       500:
 *         description: Failed to load app settings
 */
// @ts-ignore
api.get('/apps', checkAdmin, orgaSettingsController.getAppsController);

/**
 * @swagger
 * /api/kpe20/orgaSettings/apps:
 *   put:
 *     summary: Save app registry for the current organisation
 *     description: >
 *       Persists the full app registry for the authenticated user's organisation
 *       in Vault.  The entire object is replaced; pass the full updated map.
 *     tags: [OrgaSettings]
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             $ref: '#/components/schemas/AppsMap'
 *     responses:
 *       200:
 *         description: App registry saved
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       400:
 *         description: Invalid request body or missing organisation
 *       500:
 *         description: Failed to save app settings
 */
// @ts-ignore
api.post('/apps', checkAdmin, requestUpdateLogger, orgaSettingsController.saveAppsController);

/**
 * @swagger
 * /api/kpe20/orgaSettings/apps/{appId}/icon:
 *   post:
 *     summary: Upload an icon for an app
 *     description: >
 *       Uploads an image file (PNG, JPG, SVG, GIF or WebP) to the
 *       `commtool-public` MinIO bucket under `{orgId}/icons/{appId}.{ext}`
 *       and persists the resulting public URL in Vault under
 *       `apps[appId].icon` for the current organisation.
 *     tags: [OrgaSettings]
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: appId
 *         required: true
 *         schema:
 *           type: string
 *         description: The app identifier key (e.g. member.app)
 *     requestBody:
 *       required: true
 *       content:
 *         multipart/form-data:
 *           schema:
 *             type: object
 *             properties:
 *               files:
 *                 type: string
 *                 format: binary
 *                 description: Image file (PNG / JPG / SVG / GIF / WebP, max recommended 512 KB)
 *     responses:
 *       200:
 *         description: Icon uploaded and URL stored in Vault
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/IconUploadResponse'
 *       400:
 *         description: Unsupported file type, missing app or missing organisation
 *       500:
 *         description: Upload or Vault write failed
 */
// @ts-ignore
api.post('/apps/:appId/icon', checkAdmin, requestUpdateLogger, orgaSettingsController.uploadAppIconController);

// ── Mail / SMTP ───────────────────────────────────────────────────────────────

/**
 * @swagger
 * /api/kpe20/orgaSettings/mail:
 *   get:
 *     summary: Load SMTP settings for the current organisation
 *     description: >
 *       Returns the SMTP configuration stored in Vault for the authenticated
 *       user's organisation.  The `password` field is masked before being
 *       returned.
 *     tags: [OrgaSettings]
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: SMTP settings retrieved (password masked)
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 result:
 *                   $ref: '#/components/schemas/MailSettings'
 *       400:
 *         description: No organisation in session
 *       500:
 *         description: Failed to load mail settings
 */
// @ts-ignore
api.get('/mail', checkAdmin, orgaSettingsController.getMailController);

/**
 * @swagger
 * /api/kpe20/orgaSettings/mail:
 *   post:
 *     summary: Save SMTP settings for the current organisation
 *     description: >
 *       Validates and persists SMTP configuration for the authenticated
 *       user's organisation in Vault.  If `password` is the mask sentinel
 *       (••••••••) the existing password is preserved unchanged.
 *     tags: [OrgaSettings]
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             $ref: '#/components/schemas/MailSettings'
 *     responses:
 *       200:
 *         description: SMTP settings saved
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       400:
 *         description: Validation error (missing host/user or invalid port)
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: false
 *                 message:
 *                   type: string
 *                   example: 'Field "host" is required.'
 *       500:
 *         description: Failed to save mail settings
 */
// @ts-ignore
api.post('/mail', checkAdmin, requestUpdateLogger, orgaSettingsController.saveMailController);

export default api;