// @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'
});
}
};