Source: RouterProject/credential/controller.js

// @ts-check
/**
 * Credential controllers (contract envelope).
 * @import {ExpressRequestAuthorized, ExpressResponse} from '../../types.js'
 */

import { sendOk, sendErrorFrom } from '../../utils/apiEnvelope.js';
import * as service from './service.js';

/**
 * @swagger
 * /api/project/credential:
 *   get:
 *     summary: List credentials (SSH keys for Git hosts)
 *     description: |
 *       Lists all credentials of the organization the caller can see. Returns
 *       the public key and host metadata — never the private key (which lives
 *       in Vault).
 *     tags:
 *       - Project Credentials
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: List of credentials
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './credential.swagger.yaml#/components/schemas/SuccessResponse'
 *   put:
 *     summary: Create a credential (automatically generates an ed25519 SSH key pair)
 *     description: |
 *       Generates a new ed25519 key pair and persists it entirely in Vault at
 *       `orgas/data/{orgId}/credentials/{UID}/{name}`. The returned credentialsRef
 *       (`{UID}/{name}`) is stored on repository shares. The public key must be
 *       registered at the Git host (deploy key / user key) to allow access.
 *     tags:
 *       - Project Credentials
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             $ref: './credential.swagger.yaml#/components/schemas/CredentialCreateBody'
 *     responses:
 *       200:
 *         description: Credential created
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './credential.swagger.yaml#/components/schemas/SuccessResponse'
 *       422:
 *         description: Invalid name/host or credential already exists
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './credential.swagger.yaml#/components/schemas/ErrorResponse'
 *       500:
 *         description: Vault write failed
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './credential.swagger.yaml#/components/schemas/ErrorResponse'
 */
/**
 * @swagger
 * /api/project/credential/hostkey:
 *   get:
 *     summary: Scan SSH host keys of a Git host
 *     description: |
 *       Runs `ssh-keyscan` on the backend and returns the ed25519 host keys of
 *       the given Git host as known_hosts lines. Used by the credential forms
 *       to auto-fill the `hostKey` field after the URL is entered. The host key
 *       is the server identity of the Git host (for StrictHostKeyChecking) —
 *       not the credential key pair.
 *     tags:
 *       - Project Credentials
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: host
 *         required: true
 *         schema:
 *           type: string
 *         description: Hostname, optionally with `:port` (e.g. `git.commtool.org` or `git.commtool.org:2222`)
 *     responses:
 *       200:
 *         description: Host keys found
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 result:
 *                   type: object
 *                   properties:
 *                     host:
 *                       type: string
 *                     keys:
 *                       type: array
 *                       items:
 *                         type: string
 *                         example: "git.commtool.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA..."
 *       422:
 *         description: Invalid host parameter
 *       502:
 *         description: Host unreachable or scan failed
 */
/**
 * GET /project/credential/hostkey?host=...
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 */
export const scanHostKey = async (req, res) => {
    try {
        const result = await service.scanHostKey(req.query.host);
        sendOk(res, result);
    } catch (e) {
        sendErrorFrom(res, e);
    }
};

/**
 * PUT /project/credential
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 */
export const putCredential = async (req, res) => {
    try {
        const result = await service.createCredential(req);
        sendOk(res, result.result);
    } catch (e) {
        sendErrorFrom(res, e);
    }
};

/**
 * GET /project/credential
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 */
export const listCredentials = async (req, res) => {
    try {
        const result = await service.listCredentials(req);
        sendOk(res, result.result);
    } catch (e) {
        sendErrorFrom(res, e);
    }
};

/**
 * @swagger
 * /api/project/credential/{ref}:
 *   get:
 *     summary: Get a credential
 *     description: |
 *       Returns metadata + public key, never the private key. `ref` is the
 *       URL-encoded credentialsRef `{UID}/{name}`.
 *     tags:
 *       - Project Credentials
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: ref
 *         required: true
 *         schema:
 *           type: string
 *     responses:
 *       200:
 *         description: Credential
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './credential.swagger.yaml#/components/schemas/SuccessResponse'
 *       404:
 *         description: Credential not found
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './credential.swagger.yaml#/components/schemas/ErrorResponse'
 *   delete:
 *     summary: Delete a credential
 *     description: Removes the Vault secret.
 *     tags:
 *       - Project Credentials
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: ref
 *         required: true
 *         schema:
 *           type: string
 *     responses:
 *       200:
 *         description: Credential deleted
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './credential.swagger.yaml#/components/schemas/SuccessResponse'
 */
/**
 * GET /project/credential/:ref
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 */
export const getCredential = async (req, res) => {
    try {
        const result = await service.getCredential(req, req.params.ref);
        sendOk(res, result.result);
    } catch (e) {
        sendErrorFrom(res, e);
    }
};

/**
 * DELETE /project/credential/:ref
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 */
export const deleteCredential = async (req, res) => {
    try {
        const result = await service.deleteCredential(req, req.params.ref);
        sendOk(res, result.result);
    } catch (e) {
        sendErrorFrom(res, e);
    }
};