/**
* Language File Service Layer
*
* Handles business logic for language file storage, retrieval and deletion
* via the configured S3/MinIO bucket.
*
* @module LanguageFileService
*/
// @ts-check
import { myMinioClient } from '../../utils/s3Client.js';
const bucket = process.env.bucket ? process.env.bucket : 'kpe20';
/**
* Generate the public URL for an uploaded language file.
*
* @param {{ fields: any, filename: { filename: string }, extension: string, params: { api: string } }} opts
* @returns {string}
*/
export const languageUrlGen = ({ fields, filename, extension, params }) => {
return `${params.api}/languages/${filename.filename}`;
};
/**
* MIME-type filter that accepts only JSON files.
*
* @param {string} mimeType
* @param {string} extension
* @returns {boolean}
*/
export const filterJSON = (mimeType, extension) => {
return ['application/json'].includes(mimeType) && ['json'].includes(extension);
};
/**
* Build the scoped storage key from a raw key and a UID prefix.
*
* @param {string} key - The raw object key returned by S3/MinIO.
* @param {string} UID - The owner UID used as a path prefix.
* @returns {string}
*/
export const keyComponents = (key, UID) => {
return `${UID}/${encodeURIComponent(key.replace(`${UID}/`, ''))}`;
};
/**
* Retrieve a language file stream from the object store.
*
* @param {string} app - Application identifier (bucket sub-path).
* @param {string} filename - File name inside the languages directory.
* @returns {Promise<import('stream').Readable>} Readable stream of the object.
* @throws {Error} When the object cannot be found or accessed.
*/
export const getLanguageFile = (app, filename) => {
return new Promise((resolve, reject) => {
myMinioClient.getObject(
bucket,
`${app}/languages/${filename}`,
/** @param {any} err @param {any} data */
(err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
}
);
});
};
/**
* List all language files across all applications.
*
* @returns {Promise<Array<{name: string, size: number, lastModified: Date}>>}
*/
export const listLanguageFiles = () => {
return new Promise((resolve, reject) => {
/** @type {{name: string, size: number, lastModified: Date}[]} */
const objects = [];
const stream = myMinioClient.extensions.listObjectsV2WithMetadata(bucket, '', true);
stream.on('data', /** @param {any} obj */ (obj) => {
if (obj.name && obj.name.includes('/languages/')) {
objects.push({ name: obj.name, size: obj.size, lastModified: obj.lastModified });
}
});
stream.on('end', () => resolve(objects));
stream.on('error', /** @param {any} err */ (err) => reject(err));
});
};
/**
* List language files for a specific application.
*
* @param {string} app - Application identifier (bucket sub-path prefix).
* @returns {Promise<Array<{name: string, size: number, lastModified: Date}>>}
*/
export const listLanguageFilesByApp = (app) => {
return new Promise((resolve, reject) => {
/** @type {{name: string, size: number, lastModified: Date}[]} */
const objects = [];
const stream = myMinioClient.extensions.listObjectsV2WithMetadata(bucket, `${app}/languages/`, true);
stream.on('data', /** @param {any} obj */ (obj) => {
if (obj.name) {
objects.push({ name: obj.name, size: obj.size, lastModified: obj.lastModified });
}
});
stream.on('end', () => resolve(objects));
stream.on('error', /** @param {any} err */ (err) => reject(err));
});
};
/**
* Delete a language file from the object store.
*
* @param {string} app - Application identifier (bucket sub-path).
* @param {string} filename - File name inside the languages directory.
* @returns {Promise<void>}
*/
export const deleteLanguageFile = async (app, filename) => {
return myMinioClient.removeObject(bucket, `${app}/languages/${filename}`);
};
export { bucket };