backend/lib/seed/graphql/Settings.ts
2025-05-14 21:45:16 +02:00

203 lines
6.6 KiB
TypeScript

/* 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<GetSettingsOutput> => {
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<GetSettingsOutput> => {
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<void> {
// 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<any>[] = [];
// 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<any>[]) {
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<any>[]) {
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;
}