-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapp.ts
162 lines (138 loc) · 4.88 KB
/
app.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import dotenv from "dotenv";
import config from "./configuration/appConfig";
// Load .env
dotenv.config();
// Preload app config here to set needed env variables
// before some modules require them.
config();
import {Router} from 'express';
import {Database} from "./database";
import {ServiceManager} from "./engine";
import loadAddons from "./addon";
import loadAppRoutes from './router';
import createDbManager from './database';
import initServiceManager from './engine';
import loadSecurity from "./security";
import redis from "./redis";
import * as r from "./configuration/resources";
import * as manager from "./engine";
import * as logging from "./logger";
import winston from "winston";
import {Application} from "express-ws";
import fs from "fs";
import isInsideContainer from "@nsm/lib/isInsideContainer";
export type AppBootContext = AppContext & { steps: any };
// Passed context to the routes
export type AppContext = {
router: Router;
manager: ServiceManager;
database: Database;
appConfig: any;
logger: winston.Logger;
debug: boolean;
workers: boolean;
};
export type AppBootOptions = {
test?: boolean;
disableWorkers?: boolean;
}
let currentContext: AppContext;
function prepareServiceLogs(appConfig: any, logger: winston.Logger) {
if (appConfig.service_logs === true) {
logger.info('Service logs are enabled');
const path = process.cwd() + '/service_logs';
if (!fs.existsSync(path)) {
fs.mkdirSync(path);
}
}
}
function initGlobalLogger() {
logging.createNewLatest();
return logging.createLogger();
}
function initRedis({ appConfig, logger, manager }: AppContext) {
if (appConfig['redis'] == "true") {
logger.info('Using redis');
redis(manager)
.then(cl => {
logger.info('Redis connected');
})
.catch(err => {
logger.error(err);
process.exit(1);
});
}
}
// Decorate all manager functions except those excluded to disallow using them
// before manager.engine is initialized. This is necessary as the manager is being
// used (mainly for expandEngine()) even before manager.init() is called.
function managerForUnsafeUse() {
const excludeKeys: (keyof ServiceManager)[] = ["expandEngine", "initEngineForcibly", "engine"];
//
const managerRef = { ...manager };
const handler: ProxyHandler<any> = {
get(target, prop, receiver) {
// If it's key of base manager, not expanded object and is not excluded, deny access
if ((Object.keys(managerRef) as any[]).includes(prop) && !(excludeKeys as any[]).includes(prop)) {
throw new Error("ServiceManager is not initialized yet! " +
"You can only access those members now: " + excludeKeys.join(", "));
}
return Reflect.get(target, prop, receiver);
}
}
return new Proxy(manager, handler);
}
// App orchestration code
export default async function (router: Application, options?: AppBootOptions): Promise<AppBootContext> {
// Prepare logging
const logger = initGlobalLogger();
r.prepareResources(options?.test === true); // Copy resources, etc.
// Load addon steps
const steps = await loadAddons(logger);
steps('BEFORE_CONFIG', { logger });
const appConfig = config();
prepareServiceLogs(appConfig, logger);
// Database connection layer
steps('BEFORE_DB', { logger, appConfig });
const database = createDbManager();
// Temporarily lock manager until it's initialized
const ctx = currentContext = {
router,
manager: managerForUnsafeUse(),
database,
appConfig,
logger,
debug: process.env.DEBUG === 'true',
workers: !options?.disableWorkers && !isInsideContainer()
};
// Service (virtualization) layer
steps('BEFORE_ENGINE', ctx);
await initServiceManager({ db: database, appConfig, logger });
// Bring back original manager
ctx.manager = currentContext.manager = manager;
// Load security
steps('BEFORE_SECURITY', ctx);
await loadSecurity(ctx);
// Load HTTP routes
steps('BEFORE_ROUTES', ctx);
await loadAppRoutes(ctx);
// Start the server
steps('BEFORE_SERVER', ctx);
if (isInsideContainer()) {
logger.info('Running in container! Worker threads will be unavailable.');
} else if(!ctx.workers) {
logger.info('Worker threads are forcibly disabled.');
}
let srv = undefined;
if (options?.test == undefined || options.test == false) {
logger.info(`Starting server`);
srv = router.listen(appConfig.port, () => {
logger.info(`Server started on port ${appConfig.port}`);
// Enable redis support
initRedis(ctx);
});
}
steps('BOOT', ctx, srv);
return { ...ctx, steps };
}
export { Database, ServiceManager, currentContext }