Source: Router/extraApi/extraController.js

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

/**
 * @fileoverview Controllers for extra API endpoints
 * @description Contains controller functions for utility and system endpoints
 */

import { query, UUID2hex, HEX2uuid } from '@commtool/sql-query';
import { errorLoggerRead, errorLoggerUpdate } from '../../utils/requestLogger.js';
import { isAdmin, isBaseAdmin } from '../../utils/authChecks.js';
import { validateUserForOrganization } from '../../utils/userUtils.js';

/**
 * Generate a single unique identifier (UID)
 * @param {ExpressRequest} req - Express request object
 * @param {ExpressResponse} res - Express response object
 * @returns {Promise<any>}
 */
export const generateUID = async (req, res) => {
    try {
        const [{ UID }] = await query(`SELECT UIDV1() AS UID`, []);
        if (!req.query.base64) {
            res.json({ UID: HEX2uuid(UID) });
        } else {
            res.json({ UID: UID.toString('base64') });
        }
    } catch (e) {
        errorLoggerUpdate(e);
        res.status(500).json({ 
            success: false, 
            message: 'Failed to generate UID' 
        });
    }
};

/**
 * Generate multiple unique identifiers (UIDs)
 * @param {ExpressRequest} req - Express request object
 * @param {ExpressResponse} res - Express response object
 * @returns {Promise<any>}
 */
export const generateMultipleUIDs = async (req, res) => {
    try {
        const count = parseInt(req.params.count);
        if (isNaN(count) || count < 1 || count > 100) {
            return res.status(400).json({
                success: false,
                message: 'Count must be a number between 1 and 100'
            });
        }

        const uid = [];
        for (let i = 0; i < count; i++) {
            const [{ UID }] = await query(`SELECT UIDV1() AS UID`, []);
            uid.push(HEX2uuid(UID));
        }
        res.json(uid);
    } catch (e) {
        errorLoggerRead(e);
        res.status(500).json({ 
            success: false, 
            message: 'Failed to generate UIDs' 
        });
    }
};

/**
 * Get current server timestamp
 * @param {ExpressRequest} req - Express request object
 * @param {ExpressResponse} res - Express response object
 * @returns {void}
 */
export const getTimestamp = (req, res) => {
    res.json({ timestamp: Date.now() });
};

/**
 * Validate user access for an organization and populate cache
 * @param {ExpressRequest} req - Express request object
 * @param {ExpressResponse} res - Express response object
 * @returns {Promise<any>}
 */
export const validateUser = async (req, res) => {
    try {
        const { userUID, orgaUID } = req.body;

        if (!userUID || !orgaUID) {
            return res.status(400).json({
                success: false,
                message: 'userUID and orgaUID are required'
            });
        }

        // Validate UUID format
        const uuidRegex = /^UUID-[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
        if (!uuidRegex.test(userUID) || !uuidRegex.test(orgaUID)) {
            return res.status(400).json({
                success: false,
                message: 'Invalid UUID format'
            });
        }

        // Use the validation function (this will populate cache)
        const validationResult = await validateUserForOrganization(userUID, orgaUID);

        res.json({
            success: true,
            valid: validationResult.valid,
            isAdmin: validationResult.isAdmin,
            userData: validationResult.userData,
            userUID: validationResult.userUID,
            cached: true
        });

    } catch (error) {
        console.error('[validate-user] Error:', error);
        res.status(500).json({
            success: false,
            message: 'Internal server error'
        });
    }
};

/**
 * Check if user is admin
 * @param {ExpressRequestAuthorized} req - Express request object with session
 * @param {ExpressResponse} res - Express response object
 * @returns {Promise<any>}
 */
export const checkIsAdmin = async (req, res) => {
    try {
        const result = await isAdmin(req.session);
        const result2 = await isBaseAdmin(req.session);
        res.json({ 
            success: true, 
            result: result, 
            resultBase: result2 
        });
    } catch (e) {
        errorLoggerRead(e);
        res.status(500).json({ 
            success: false, 
            message: 'Failed to check admin status' 
        });
    }
};

/**
 * Get the count of pending items in the processing queue
 * @param {ExpressRequestAuthorized} req - Express request object with session
 * @param {ExpressResponse} res - Express response object
 * @returns {Promise<any>}
 */
export const getQueueCount = async (req, res) => {
    try {
        if (!req.session?.root) {
            return res.status(400).json({
                success: false, 
                message: 'missing orga root: call prior to this GET /api/kpe20/root/:UIDroot'
            });
        }

        const [q] = await query(
            'SELECT COUNT(*) AS count FROM TreeQueue WHERE UIDRoot=?',
            [UUID2hex(req.session.root)]
        );
        res.json({ result: q.count, success: true });
    } catch (e) {
        errorLoggerRead(e);
        res.status(500).json({ 
            success: false, 
            message: 'Failed to get queue count' 
        });
    }
};

/**
 * Get users who have specific visibility permissions for an entity
 * @param {ExpressRequestAuthorized} req - Express request object
 * @param {ExpressResponse} res - Express response object
 * @returns {Promise<void>}
 */
export const getUsersWithVisibility = async (req, res) => {
    try {
        let visible = ['visible', 'changeable', 'admin'];
        if (req.params.vtype === 'changeable')
            visible = ['admin', 'changeable'];
        if (req.params.vtype === 'admin')
            visible = ['admin'];
            
        const result = await query(
            `SELECT UIDuser FROM Visible WHERE Type IN(?) AND UID=?`,
            [visible, UUID2hex(req.params.UID)],
            { cast: ['UUID'] }
        );
        res.json({ 
            success: true, 
            result: result.map(el => el.UIDuser) 
        });
    } catch (e) {
        errorLoggerRead(e);
        res.status(500).json({ 
            success: false, 
            message: 'Failed to get users with visibility' 
        });
    }
};

/**
 * Get users and their data who have specific visibility permissions for an entity
 * @param {ExpressRequestAuthorized} req - Express request object
 * @param {ExpressResponse} res - Express response object
 * @returns {Promise<void>}
 */
export const getUsersWithVisibilityData = async (req, res) => {
    try {
        let visible = ['visible', 'changeable', 'admin'];
        if (req.params.vtype === 'changeable')
            visible = ['admin', 'changeable'];
        if (req.params.vtype === 'admin')
            visible = ['admin'];
            
        const result = await query(`
            SELECT Visible.UIDuser, Member.Data
            FROM Visible
            INNER JOIN Member ON (Member.UID=Visible.UIDUser)
            WHERE Visible.Type IN(?) AND Visible.UID=?
        `, [visible, UUID2hex(req.params.UID)], { cast: ['UUID', 'json'] });
        
        res.json({ success: true, result });
    } catch (e) {
        errorLoggerRead(e);
        res.status(500).json({ 
            success: false, 
            message: 'Failed to get users with visibility data' 
        });
    }
};