/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable prettier/prettier */ /* eslint-disable @typescript-eslint/no-use-before-define */ import { DateTime } from 'luxon'; import _ from 'lodash'; import env from '@config/.env.json'; import packageFile from 'package.json'; import { SettingsType } from '@seed/interfaces/components'; import { ErrorsConfig, NotificationEnum } from '@config/config'; import { EngineNotificationEnum } from '@seed/interfaces/components.notifications'; import { TranslatableComponent } from '@src/__components/components'; import { promiseAll } from '@seed/helpers/Utils'; import DB from '@seed/services/database/DBService'; import { v4 as uuid } from 'uuid'; export interface GetSettingsOutput { env: any[]; errors: any[]; emails: any[]; } export interface EngineServerCacheInfo { emailsContent: { [key in NotificationEnum | EngineNotificationEnum]: any; }; errorsContent: { [key: string]: { message: string; translations?: { subject?: TranslatableComponent; body?: TranslatableComponent; }; }; }; } export const getSettingsFromDB = async (): Promise => { const settings = (await await (await DB.getInstance()).db.collection('settings').find().toArray()) as (any | any | any)[]; // filtering client side because not too much data and no need to index db return { env: _.filter(settings, { type: SettingsType.env }) as any[], errors: _.filter(settings, { type: SettingsType.errors }) as any[], emails: _.filter(settings, { type: SettingsType.emails }) as any[], }; }; export const syncEnvFromDB = (currentEnvInDB: GetSettingsOutput): void => { // update env currentEnvInDB.env.map((env) => { process.env[env.key] = env.value; }); }; export const initEnvironement = async (env: any): Promise => { const currentEnv = process.env.NODE_ENV && env[process.env.NODE_ENV] ? env[process.env.NODE_ENV] : env.default; for (const k in currentEnv) { process.env[k] = currentEnv[k]; } // Init also Package.json process.env.awsRegion = packageFile.devops.awsRegion; const currentSettings = await getSettingsFromDB(); syncEnvFromDB(currentSettings); // Force the local to be > DB if (process.env.NODE_ENV == 'local') { for (const k in currentEnv) { process.env[k] = currentEnv[k]; } } console.log('[Server - Env] Loaded'); return currentSettings; }; export class SettingsCache { private static instance: SettingsCache; private lastCacheDate: DateTime; public cache: EngineServerCacheInfo; // eslint-disable-next-line @typescript-eslint/no-empty-function private constructor() {} public static getInstance(): SettingsCache { if (!this.instance) { this.instance = new SettingsCache(); } return this.instance; } public async refreshCache(): Promise { // check if needs to refresh the cache if (!this.lastCacheDate || this.lastCacheDate.diff(DateTime.local(), 'minutes').minutes > 5) { const settings = await initEnvironement(env); const promises: Promise[] = []; // Map for errors const errorsContent = await mapErrorContent(settings, promises); // Map for emails const emailsContent: { [key in NotificationEnum | EngineNotificationEnum]: any; } = await mapEmailSettings(settings, promises); this.cache = { errorsContent, emailsContent, }; this.lastCacheDate = DateTime.local(); if (promises.length > 0) { console.log('Creating defaut in DB'); await promiseAll(promises); } } } } async function mapErrorContent(settings: GetSettingsOutput, newData: Promise[]) { const errorsContent: { [key: string]: { message: string; translations?: { subject?: TranslatableComponent; body?: TranslatableComponent; }; }; } = {}; for (const key in ErrorsConfig) { if (Object.prototype.hasOwnProperty.call(ErrorsConfig, key)) { const errIndex = _.findIndex(settings.errors, { key: key }); errorsContent[key] = { message: ErrorsConfig[key], }; if (errIndex === -1) { console.error('[SETTINGS - ERROR] Creating default', key); newData.push( (await DB.getInstance()).db.collection('settings').insertOne({ key, type: SettingsType.errors, value: ErrorsConfig[key], body: new TranslatableComponent(), _id: uuid(), r: ['admin'], w: ['admin'], d: ['admin'], createdAt: new Date(), updatedAt: new Date(), }), ); } else { errorsContent[key].translations = { subject: settings.errors[errIndex].subject, body: settings.errors[errIndex].body, }; } } } return errorsContent; } async function mapEmailSettings(settings: GetSettingsOutput, newData: Promise[]) { const emails = _.groupBy(settings.emails, 'key'); const emailsContent: { [key in NotificationEnum | EngineNotificationEnum]: any; } = {} as any; const enumConcat = [...Object.keys(NotificationEnum), ...Object.keys(EngineNotificationEnum)]; enumConcat.forEach(async (element) => { if (!emails[element]) { console.error('[SETTINGS - EMAIL] Creating default', element); newData.push( (await DB.getInstance()).db.collection('settings').insertOne({ key: element, type: SettingsType.emails, body: new TranslatableComponent(), custom: false, fromEmail: '', fromName: '', replyToEmail: '', r: ['admin'], w: ['admin'], d: ['admin'], _id: uuid(), createdAt: new Date(), updatedAt: new Date(), }), ); } else emailsContent[element] = emails[element]; }); return emailsContent; }