Source: Router/languageFile/controller.js

/**
 * Language File Controller Layer
 * Handles HTTP request/response and delegates to the service layer.
 */

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

import { s3 } from '../../utils/s3Client.js';
import { uploadLanguage } from '../../utils/uploadLanguage.js';
import { errorLoggerUpdate } from '../../utils/requestLogger.js';
import * as languageFileService from './service.js';

/**
 * List all available language files.
 * Handles GET /
 *
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 * @returns {Promise<void>}
 */
export const listLanguageFilesController = async (req, res) => {
    try {
        const result = await languageFileService.listLanguageFiles();
        res.json({ success: true, result });
    } catch (e) {
        errorLoggerUpdate(e);
        const err = /** @type {Error} */ (e);
        res.status(500).json({ success: false, message: err.message });
    }
};

/**
 * List language files for a specific application.
 * Handles GET /:app
 *
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 * @returns {Promise<void>}
 */
export const listLanguageFilesByAppController = async (req, res) => {
    try {
        const result = await languageFileService.listLanguageFilesByApp(req.params.app);
        res.json({ success: true, result });
    } catch (e) {
        errorLoggerUpdate(e);
        const err = /** @type {Error} */ (e);
        res.status(500).json({ success: false, message: err.message });
    }
};

/**
 * Upload a language file.
 * Handles POST /:api
 *
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 * @returns {Promise<void>}
 */
export const uploadLanguageFileController = async (req, res) => {
    try {
        const uploadedFileData = await uploadLanguage(req, /** @type {any} */ ({
            s3,
            mimeFilter: languageFileService.filterJSON,
            uploadUrlGen: languageFileService.languageUrlGen,
            Bucket: languageFileService.bucket,
            width: 300
        }));

        const urls = /** @type {any[]} */ (uploadedFileData).map(
            /** @param {{Key: string}} el */ el => process.env.baseUrl + '/api/kpe20/languages/' + languageFileService.keyComponents(el.Key, req.params.UID)
        ) + '/languages';

        const fileNames = /** @type {any[]} */ (uploadedFileData).map(
            /** @param {{Key: string}} el */ el => el.Key.split('/')[el.Key.split('/').length - 1]
        );

        if (uploadedFileData.length > 0) {
            res.json({
                success: true,
                message: 'OK',
                files: uploadedFileData,
                name: uploadedFileData[0].originalname,
                url: urls[1] ? urls[1] : urls[0],
                fileNames,
                urls,
                Data: uploadedFileData,
                prefix: req.params.UID + '/avatar'
            });
        } else {
            res.json({ success: true, message: 'file already existed' });
        }
    } catch (e) {
        errorLoggerUpdate(e);
        const err = /** @type {Error} */ (e);
        res.status(500).json({ success: false, message: err.message });
    }
};

/**
 * Stream a language file to the client.
 * Handles GET /:app/:filename
 *
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 * @returns {Promise<void>}
 */
export const getLanguageFileController = async (req, res) => {
    try {
        const data = await languageFileService.getLanguageFile(
            req.params.app,
            req.params.filename
        );

        res.set('Content-Type', 'application/octetstream');
        res.set('Content-Disposition', `attachment; filename="${req.params.filename}"`);

        data.on('error', (streamErr) => {
            console.error('Stream error:', streamErr);
            if (!res.headersSent) {
                res.status(500).json({ success: false, message: 'Stream error' });
            }
        });

        data.pipe(res);
    } catch (e) {
        const err = /** @type {any} */ (e);
        console.error(`S3 error for ${req.params.app}/languages/${req.params.filename}:`, err.code);
        if (!res.headersSent) {
            const status = err.code === 'NoSuchKey' || err.code === 'NotFound' ? 410 : 500;
            res.status(status).json({ success: false, message: 'not available', code: err.code });
        }
    }
};

/**
 * Delete a language file from the object store.
 * Handles DELETE /:app/:filename
 *
 * @param {ExpressRequestAuthorized} req
 * @param {ExpressResponse} res
 * @returns {Promise<void>}
 */
export const deleteLanguageFileController = async (req, res) => {
    try {
        const result = await languageFileService.deleteLanguageFile(
            req.params.app,
            req.params.filename
        );

        res.json({ success: true, result });
    } catch (e) {
        console.error('Error deleting language file:', e);
        errorLoggerUpdate(e);
        const err = /** @type {Error} */ (e);
        if (!res.headersSent) {
            res.status(500).json({ success: false, message: 'Error deleting file', error: err.message });
        }
    }
};