Files
core/src/modules/posts/config.js
T
2026-04-12 12:50:14 -04:00

135 lines
4.1 KiB
JavaScript

/**
* Posts Module - Config Parser
* Parses ZEN_MODULE_ZEN_MODULE_POSTS_TYPES and ZEN_MODULE_POSTS_TYPE_* environment variables into a structured config.
*
* .env format:
* ZEN_MODULE_ZEN_MODULE_POSTS_TYPES=blogue|cve|emploi
* ZEN_MODULE_POSTS_TYPE_BLOGUE=title:title|slug:slug|category:category|date:date|resume:text|content:markdown|image:image
* ZEN_MODULE_POSTS_TYPE_CVE=title:title|slug:slug|cve_id:text|severity:text|description:markdown|date:date
*
* Supported field types: title, slug, text, markdown, date, datetime, color, category, image, relation
*
* Relation field format: name:relation:target_post_type
* e.g. keywords:relation:mots-cle
*/
const VALID_FIELD_TYPES = ['title', 'slug', 'text', 'markdown', 'date', 'datetime', 'color', 'category', 'image', 'relation'];
let _cachedConfig = null;
/**
* Parse a single type's field string into an array of field definitions.
* Format: "name:type" or "name:type:param" (param used for relation target)
* e.g. "title:title|slug:slug|date:date|keywords:relation:mots-cle"
* -> [{ name: 'title', type: 'title' }, ..., { name: 'keywords', type: 'relation', target: 'mots-cle' }]
* @param {string} fieldString
* @returns {Array<{name: string, type: string, target?: string}>}
*/
function parseFields(fieldString) {
if (!fieldString) return [];
return fieldString
.split('|')
.map(part => {
const segments = part.trim().split(':');
const name = segments[0];
const type = segments[1];
const param = segments[2] || null;
if (!name) return null;
const resolvedType = VALID_FIELD_TYPES.includes(type) ? type : 'text';
const field = { name: name.trim(), type: resolvedType };
if (resolvedType === 'relation' && param) {
field.target = param.trim().toLowerCase();
}
return field;
})
.filter(Boolean);
}
/**
* Parse all ZEN_MODULE_POSTS_TYPE_* env vars and build the config object.
* @returns {Object} Parsed config
*/
function buildConfig() {
const enabled = process.env.ZEN_MODULE_POSTS === 'true';
if (!enabled) {
return { enabled: false, types: {} };
}
const typesRaw = process.env.ZEN_MODULE_ZEN_MODULE_POSTS_TYPES || '';
// Each entry can be "key" or "key:Label" — only lowercase the key part
const typeKeys = typesRaw
.split('|')
.map(k => {
const [key, ...rest] = k.trim().split(':');
const label = rest.join(':'); // preserve colons in label if any
return label ? `${key.toLowerCase()}:${label}` : key.toLowerCase();
})
.filter(Boolean);
const types = {};
for (const entry of typeKeys) {
// Support "key:Label" format (label is optional)
const [key, customLabel] = entry.split(':');
const envKey = `ZEN_MODULE_POSTS_TYPE_${key.toUpperCase()}`;
const fieldString = process.env[envKey] || '';
const fields = parseFields(fieldString);
const titleField = fields.find(f => f.type === 'title')?.name || null;
const slugField = fields.find(f => f.type === 'slug')?.name || null;
const hasCategory = fields.some(f => f.type === 'category');
const hasRelations = fields.some(f => f.type === 'relation');
const label = customLabel || (key.charAt(0).toUpperCase() + key.slice(1));
types[key] = {
key,
label,
fields,
hasCategory,
hasRelations,
titleField,
slugField,
};
}
return { enabled: true, types };
}
/**
* Get the parsed posts config (cached after first call).
* @returns {{ enabled: boolean, types: Object }}
*/
export function getPostsConfig() {
if (!_cachedConfig) {
_cachedConfig = buildConfig();
}
return _cachedConfig;
}
/**
* Get a single post type config by key.
* @param {string} key - Type key (e.g. 'blogue')
* @returns {Object|null}
*/
export function getPostType(key) {
const config = getPostsConfig();
return config.types[key] || null;
}
/**
* Check if the posts module is enabled.
* @returns {boolean}
*/
export function isPostsEnabled() {
return process.env.ZEN_MODULE_POSTS === 'true';
}
/**
* Reset cached config (useful for testing).
*/
export function resetConfig() {
_cachedConfig = null;
}