feat(config): make the config as a singleton class overridable by a json config
This commit is contained in:
parent
b3f143da57
commit
ae2dd8d364
7 changed files with 126 additions and 12 deletions
|
@ -60,7 +60,6 @@ services:
|
|||
INTERNAL_IP: ${INTERNAL_IP}
|
||||
TIPI_VERSION: ${TIPI_VERSION}
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
ROOT_FOLDER_HOST: ${ROOT_FOLDER_HOST}
|
||||
NGINX_PORT: ${NGINX_PORT}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_USERNAME: tipi
|
||||
|
|
|
@ -56,7 +56,8 @@
|
|||
"type-graphql": "^1.1.1",
|
||||
"typeorm": "^0.3.6",
|
||||
"validator": "^13.7.0",
|
||||
"winston": "^3.7.2"
|
||||
"winston": "^3.7.2",
|
||||
"zod": "^3.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^7.3.0",
|
||||
|
|
102
packages/system-api/src/core/config/TipiConfig.ts
Normal file
102
packages/system-api/src/core/config/TipiConfig.ts
Normal file
|
@ -0,0 +1,102 @@
|
|||
import { z } from 'zod';
|
||||
import * as dotenv from 'dotenv';
|
||||
import fs from 'fs-extra';
|
||||
import config from '../../config';
|
||||
import { readJsonFile } from '../../modules/fs/fs.helpers';
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
dotenv.config({ path: '.env.dev' });
|
||||
} else {
|
||||
dotenv.config({ path: '.env' });
|
||||
}
|
||||
const {
|
||||
LOGS_FOLDER = 'logs',
|
||||
LOGS_APP = 'app.log',
|
||||
LOGS_ERROR = 'error.log',
|
||||
NODE_ENV = 'development',
|
||||
JWT_SECRET = '',
|
||||
INTERNAL_IP = '',
|
||||
TIPI_VERSION = '',
|
||||
NGINX_PORT = '80',
|
||||
APPS_REPO_ID = '',
|
||||
APPS_REPO_URL = '',
|
||||
DOMAIN = '',
|
||||
} = process.env;
|
||||
|
||||
const configSchema = z.object({
|
||||
NODE_ENV: z.string(),
|
||||
repo: z.string(),
|
||||
logs: z.object({
|
||||
LOGS_FOLDER: z.string(),
|
||||
LOGS_APP: z.string(),
|
||||
LOGS_ERROR: z.string(),
|
||||
}),
|
||||
rootFolder: z.string(),
|
||||
internalIp: z.string(),
|
||||
version: z.string(),
|
||||
jwtSecret: z.string(),
|
||||
clientUrls: z.array(z.string()),
|
||||
appsRepoId: z.string(),
|
||||
appsRepoUrl: z.string(),
|
||||
domain: z.string(),
|
||||
});
|
||||
|
||||
class Config {
|
||||
private static instance: Config;
|
||||
|
||||
private config: z.infer<typeof configSchema>;
|
||||
|
||||
constructor() {
|
||||
const fileConfig = readJsonFile('/tipi/state/settings.json');
|
||||
const envConfig: z.infer<typeof configSchema> = {
|
||||
logs: {
|
||||
LOGS_FOLDER,
|
||||
LOGS_APP,
|
||||
LOGS_ERROR,
|
||||
},
|
||||
NODE_ENV,
|
||||
repo: APPS_REPO_URL,
|
||||
rootFolder: '/tipi',
|
||||
internalIp: INTERNAL_IP,
|
||||
version: TIPI_VERSION,
|
||||
jwtSecret: JWT_SECRET,
|
||||
clientUrls: ['http://localhost:3000', `http://${INTERNAL_IP}`, `http://${INTERNAL_IP}:${NGINX_PORT}`, `http://${INTERNAL_IP}:3000`, `https://${DOMAIN}`],
|
||||
appsRepoId: APPS_REPO_ID,
|
||||
appsRepoUrl: APPS_REPO_URL,
|
||||
domain: DOMAIN,
|
||||
};
|
||||
|
||||
const parsed = configSchema.parse({
|
||||
...envConfig,
|
||||
...fileConfig,
|
||||
});
|
||||
|
||||
this.config = parsed;
|
||||
}
|
||||
|
||||
public static getInstance(): Config {
|
||||
if (!Config.instance) {
|
||||
Config.instance = new Config();
|
||||
}
|
||||
return Config.instance;
|
||||
}
|
||||
|
||||
public getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
public setConfig(key: keyof typeof configSchema.shape, value: any) {
|
||||
const newConf = { ...this.getConfig() };
|
||||
newConf[key] = value;
|
||||
|
||||
this.config = configSchema.parse(newConf);
|
||||
|
||||
fs.writeFileSync(`${config.ROOT_FOLDER}/state/settings.json`, JSON.stringify(newConf));
|
||||
}
|
||||
}
|
||||
|
||||
export const setConfig = (key: keyof typeof configSchema.shape, value: any) => {
|
||||
Config.getInstance().setConfig(key, value);
|
||||
};
|
||||
|
||||
export const getConfig = () => Config.getInstance().getConfig();
|
|
@ -57,7 +57,7 @@ export const checkEnvFile = (appName: string) => {
|
|||
|
||||
export const runAppScript = async (params: string[]): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
runScript('/scripts/app.sh', [...params, config.ROOT_FOLDER_HOST, config.APPS_REPO_ID], (err: string) => {
|
||||
runScript('/scripts/app.sh', [...params], (err: string) => {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
reject(err);
|
||||
|
|
|
@ -5,6 +5,7 @@ import config from '../../config';
|
|||
export const getAbsolutePath = (path: string) => `${config.ROOT_FOLDER}${path}`;
|
||||
|
||||
export const readJsonFile = (path: string): any => {
|
||||
try {
|
||||
const rawFile = fs.readFileSync(getAbsolutePath(path))?.toString();
|
||||
|
||||
if (!rawFile) {
|
||||
|
@ -12,6 +13,9 @@ export const readJsonFile = (path: string): any => {
|
|||
}
|
||||
|
||||
return JSON.parse(rawFile);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const readFile = (path: string): string => {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'reflect-metadata';
|
||||
import express from 'express';
|
||||
import { ApolloServerPluginLandingPageGraphQLPlayground as Playground } from 'apollo-server-core';
|
||||
import config from './config';
|
||||
import { ApolloServer } from 'apollo-server-express';
|
||||
import { createSchema } from './schema';
|
||||
import { ApolloLogs } from './config/logger/apollo.logger';
|
||||
|
@ -17,6 +16,9 @@ import { runUpdates } from './core/updates/run';
|
|||
import recover from './core/updates/recover-migrations';
|
||||
import { cloneRepo, updateRepo } from './helpers/repo-helpers';
|
||||
import startJobs from './core/jobs/jobs';
|
||||
import { getConfig } from './core/config/TipiConfig';
|
||||
|
||||
const { clientUrls, rootFolder, appsRepoId, appsRepoUrl } = getConfig();
|
||||
|
||||
let corsOptions = {
|
||||
credentials: true,
|
||||
|
@ -27,7 +29,7 @@ let corsOptions = {
|
|||
// disallow requests with no origin
|
||||
if (!origin) return callback(new Error('Not allowed by CORS'), false);
|
||||
|
||||
if (config.CLIENT_URLS.includes(origin)) {
|
||||
if (clientUrls.includes(origin)) {
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
|
@ -41,7 +43,7 @@ const main = async () => {
|
|||
const app = express();
|
||||
const port = 3001;
|
||||
|
||||
app.use(express.static(`${config.ROOT_FOLDER}/repos/${config.APPS_REPO_ID}`));
|
||||
app.use(express.static(`${rootFolder}/repos/${appsRepoId}`));
|
||||
app.use(cors(corsOptions));
|
||||
app.use(getSessionMiddleware());
|
||||
|
||||
|
@ -75,8 +77,8 @@ const main = async () => {
|
|||
await runUpdates();
|
||||
|
||||
httpServer.listen(port, async () => {
|
||||
await cloneRepo(config.APPS_REPO_URL);
|
||||
await updateRepo(config.APPS_REPO_URL);
|
||||
await cloneRepo(appsRepoUrl);
|
||||
await updateRepo(appsRepoId);
|
||||
startJobs();
|
||||
// Start apps
|
||||
appsService.startAllApps();
|
||||
|
|
|
@ -193,6 +193,7 @@ importers:
|
|||
typescript: 4.6.4
|
||||
validator: ^13.7.0
|
||||
winston: ^3.7.2
|
||||
zod: ^3.19.1
|
||||
dependencies:
|
||||
apollo-server-core: 3.10.0_graphql@15.8.0
|
||||
apollo-server-express: 3.9.0_jfj6k5cqxqbusbdzwqjdzioxzm
|
||||
|
@ -226,6 +227,7 @@ importers:
|
|||
typeorm: 0.3.6_pg@8.7.3+ts-node@10.8.2
|
||||
validator: 13.7.0
|
||||
winston: 3.7.2
|
||||
zod: 3.19.1
|
||||
devDependencies:
|
||||
'@faker-js/faker': 7.3.0
|
||||
'@swc/cli': 0.1.57_@swc+core@1.2.210
|
||||
|
@ -12920,6 +12922,10 @@ packages:
|
|||
resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==}
|
||||
dev: false
|
||||
|
||||
/zod/3.19.1:
|
||||
resolution: {integrity: sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==}
|
||||
dev: false
|
||||
|
||||
/zustand/3.7.2_react@18.1.0:
|
||||
resolution: {integrity: sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==}
|
||||
engines: {node: '>=12.7.0'}
|
||||
|
|
Loading…
Reference in a new issue