UNPKG

17.4 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.App = void 0;
4const utils_1 = require("./utils");
5const EventEmitter = require("eventemitter3");
6const express = require("express");
7const fs = require("fs");
8const http = require("http");
9const https = require("https");
10const jaul = require("jaul");
11const logger = require("anyhow");
12const path = require("path");
13const setmeup = require("setmeup");
14let settings;
15class App {
16 constructor() {
17 this.events = new EventEmitter();
18 this.on = (eventName, callback) => {
19 this.events.on(eventName, callback);
20 };
21 this.once = (eventName, callback) => {
22 this.events.on(eventName, callback);
23 };
24 this.off = (eventName, callback) => {
25 this.events.off(eventName, callback);
26 };
27 this.init = (middlewares) => {
28 let mw;
29 if (settings.general.debug && logger.levels.indexOf("debug") < 0) {
30 logger.levels.push("debug");
31 }
32 if (!logger.preprocessor) {
33 logger.preprocessor = require("./logger").clean;
34 }
35 logger.errorStack = settings.logger.errorStack;
36 this.expressApp = express();
37 middlewares = middlewares || { append: [], prepend: [] };
38 if (middlewares.prepend && !utils_1.isArray(middlewares.prepend)) {
39 middlewares.prepend = [middlewares.prepend];
40 }
41 if (middlewares.append && !utils_1.isArray(middlewares.append)) {
42 middlewares.append = [middlewares.append];
43 }
44 if (middlewares.prepend && middlewares.prepend.length > 0) {
45 for (mw of middlewares.prepend) {
46 if (mw) {
47 this.expressApp.use(mw);
48 logger.debug("App.init", "Prepended middleware");
49 }
50 }
51 }
52 this.expressApp.set("trust proxy", settings.app.trustProxy);
53 this.expressApp.set("views", settings.app.viewPath);
54 if (settings.app.viewEngine) {
55 this.expressApp.set("view engine", settings.app.viewEngine);
56 this.expressApp.set("view options", settings.app.viewOptions);
57 }
58 if (settings.app.bodyParser && settings.app.bodyParser.enabled) {
59 try {
60 const midBodyParser = require("body-parser");
61 if (settings.app.bodyParser.rawTypes) {
62 this.expressApp.use(midBodyParser.raw({ limit: settings.app.bodyParser.rawLimit, type: settings.app.bodyParser.rawTypes }));
63 }
64 this.expressApp.use(midBodyParser.json({ limit: settings.app.bodyParser.limit }));
65 this.expressApp.use(midBodyParser.text({ limit: settings.app.bodyParser.limit }));
66 this.expressApp.use(midBodyParser.urlencoded({ limit: settings.app.bodyParser.limit, extended: settings.app.bodyParser.extended }));
67 }
68 catch (ex) {
69 logger.warn("App.init", "Can't load 'body-parser' module.");
70 }
71 }
72 if (settings.app.cookie && settings.app.cookie.enabled) {
73 try {
74 const midCookieParser = require("cookie-parser");
75 this.expressApp.use(midCookieParser(settings.app.secret));
76 }
77 catch (ex) {
78 ex.friendlyMessage = "Can't load 'cookie-parser' module.";
79 logger.error("App.init", ex);
80 }
81 }
82 if (settings.app.session && settings.app.session.enabled) {
83 try {
84 const midSession = require("express-session");
85 const memoryStore = require("memorystore")(midSession);
86 this.expressApp.use(midSession({
87 store: new memoryStore({ checkPeriod: settings.app.session.checkPeriod }),
88 proxy: settings.app.session.proxy,
89 resave: settings.app.session.resave,
90 saveUninitialized: settings.app.session.saveUninitialized,
91 secret: settings.app.secret,
92 ttl: settings.app.session.maxAge * 1000,
93 cookie: {
94 secure: settings.app.session.secure,
95 httpOnly: settings.app.session.httpOnly,
96 maxAge: settings.app.session.maxAge * 1000
97 }
98 }));
99 }
100 catch (ex) {
101 ex.friendlyMessage = "Can't load 'express-session' and 'memorystore' modules.";
102 logger.error("App.init", ex);
103 }
104 }
105 if (settings.app.compression && settings.app.compression.enabled) {
106 try {
107 const midCompression = require("compression");
108 this.expressApp.use(midCompression());
109 }
110 catch (ex) {
111 ex.friendlyMessage = "Can't load 'compression' module.";
112 logger.error("App.init", ex);
113 }
114 }
115 if (settings.app.publicPath) {
116 this.expressApp.use(express.static(settings.app.publicPath));
117 }
118 if (middlewares.append && middlewares.append.length > 0) {
119 for (mw of middlewares.append) {
120 if (mw) {
121 this.expressApp.use(mw);
122 logger.debug("App.init", "Appended middleware");
123 }
124 }
125 }
126 if (settings.general.debug) {
127 this.expressApp.use(function (req, res, next) {
128 const { method } = req;
129 const { url } = req;
130 const ip = jaul.network.getClientIP(req);
131 const msg = `Request from ${ip}`;
132 if (res) {
133 logger.debug("App", msg, method, url);
134 }
135 if (next) {
136 next();
137 }
138 return url;
139 });
140 }
141 this.expressApp.disable("x-powered-by");
142 this.events.emit("init");
143 this.start();
144 };
145 this.start = () => {
146 if (this.server) {
147 logger.warn("App.start", "Server is already running, abort start.");
148 return this.server;
149 }
150 let serverRef;
151 if (settings.app.ssl && settings.app.ssl.enabled && settings.app.ssl.keyFile && settings.app.ssl.certFile) {
152 const sslKeyFile = jaul.io.getFilePath(settings.app.ssl.keyFile);
153 const sslCertFile = jaul.io.getFilePath(settings.app.ssl.certFile);
154 if (sslKeyFile && sslCertFile) {
155 const sslKey = fs.readFileSync(sslKeyFile, { encoding: settings.general.encoding });
156 const sslCert = fs.readFileSync(sslCertFile, { encoding: settings.general.encoding });
157 const sslOptions = { key: sslKey, cert: sslCert };
158 serverRef = https.createServer(sslOptions, this.expressApp);
159 }
160 else {
161 throw new Error("Invalid certificate filename, please check paths defined on settings.app.ssl.");
162 }
163 }
164 else {
165 serverRef = http.createServer(this.expressApp);
166 }
167 this.server = serverRef;
168 let listenCb = () => {
169 if (settings.app.ip) {
170 logger.info("App.start", settings.app.title, `Listening on ${settings.app.ip} port ${settings.app.port}`, `URL ${settings.app.url}`);
171 }
172 else {
173 logger.info("App.start", settings.app.title, `Listening on port ${settings.app.port}`, `URL ${settings.app.url}`);
174 }
175 };
176 let listenError = (err) => {
177 logger.error("App.start", "Can't start", err);
178 };
179 if (settings.app.ip) {
180 serverRef.listen(settings.app.port, settings.app.ip, listenCb).on("error", listenError);
181 }
182 else {
183 serverRef.listen(settings.app.port, listenCb).on("error", listenError);
184 }
185 serverRef.timeout = settings.app.timeout;
186 this.events.emit("start");
187 return this.server;
188 };
189 this.kill = () => {
190 if (!this.server) {
191 logger.warn("App.kill", "Server was not running");
192 return;
193 }
194 try {
195 this.server.close();
196 this.server = null;
197 this.events.emit("kill");
198 }
199 catch (ex) {
200 logger.error("App.kill", ex);
201 }
202 };
203 this.all = (...args) => {
204 logger.debug("App.all", args[1], args[2]);
205 return this.expressApp.all.apply(this.expressApp, args);
206 };
207 this.get = (...args) => {
208 logger.debug("App.get", args[1], args[2]);
209 return this.expressApp.get.apply(this.expressApp, args);
210 };
211 this.post = (...args) => {
212 logger.debug("App.post", args[1], args[2]);
213 return this.expressApp.post.apply(this.expressApp, args);
214 };
215 this.put = (...args) => {
216 logger.debug("App.put", args[1], args[2]);
217 return this.expressApp.put.apply(this.expressApp, args);
218 };
219 this.patch = (...args) => {
220 logger.debug("App.patch", args[1], args[2]);
221 return this.expressApp.patch.apply(this.expressApp, args);
222 };
223 this.delete = (...args) => {
224 logger.debug("App.delete", args[1], args[2]);
225 return this.expressApp.delete.apply(this.expressApp, args);
226 };
227 this.use = (...args) => {
228 logger.debug("App.use", args[1], args[2]);
229 return this.expressApp.use.apply(this.expressApp, args);
230 };
231 this.set = (...args) => {
232 logger.debug("App.set", args[1], args[2]);
233 return this.expressApp.set.apply(this.expressApp, args);
234 };
235 this.route = (reqPath) => {
236 logger.debug("App.route", reqPath);
237 return this.expressApp.route.apply(this.expressApp, reqPath);
238 };
239 this.renderView = (req, res, view, options, status) => {
240 logger.debug("App.renderView", req.originalUrl, view, options);
241 try {
242 if (!options) {
243 options = {};
244 }
245 if (options.title == null) {
246 options.title = settings.app.title;
247 }
248 if (status) {
249 res.status(status);
250 }
251 res.render(view, options);
252 }
253 catch (ex) {
254 logger.error("App.renderView", view, ex);
255 this.renderError(req, res, ex);
256 }
257 if (settings.app.events.render) {
258 this.events.emit("renderView", req, res, view, options, status);
259 }
260 };
261 this.renderText = (req, res, text, status) => {
262 logger.debug("App.renderText", req.originalUrl, text);
263 try {
264 if (text == null) {
265 logger.debug("App.renderText", "Called with empty text parameter");
266 text = "";
267 }
268 else if (!utils_1.isString(text)) {
269 text = text.toString();
270 }
271 if (status) {
272 res.status(status);
273 }
274 res.setHeader("content-type", "text/plain");
275 res.send(text);
276 }
277 catch (ex) {
278 logger.error("App.renderText", text, ex);
279 this.renderError(req, res, ex);
280 }
281 if (settings.app.events.render) {
282 this.events.emit("renderText", req, res, text, status);
283 }
284 };
285 this.renderJson = (req, res, data, status) => {
286 logger.debug("App.renderJson", req.originalUrl, data);
287 if (utils_1.isString(data)) {
288 try {
289 data = JSON.parse(data);
290 }
291 catch (ex) {
292 logger.error("App.renderJson", ex);
293 return this.renderError(req, res, ex, 500);
294 }
295 }
296 var cleanJson = function (obj, depth) {
297 if (depth >= settings.logger.maxDepth) {
298 return;
299 }
300 if (utils_1.isArray(obj)) {
301 return Array.from(obj).map((i) => cleanJson(i, depth + 1));
302 }
303 else if (utils_1.isObject(obj)) {
304 return (() => {
305 const result = [];
306 for (let k in obj) {
307 const v = obj[k];
308 if (utils_1.isFunction(v)) {
309 result.push(delete obj[k]);
310 }
311 else {
312 result.push(cleanJson(v, depth + 1));
313 }
314 }
315 return result;
316 })();
317 }
318 };
319 cleanJson(data, 0);
320 if (status) {
321 res.status(status);
322 }
323 if (settings.app.allowOriginHeader) {
324 res.setHeader("Access-Control-Allow-Origin", settings.app.allowOriginHeader);
325 }
326 res.json(data);
327 if (settings.app.events.render) {
328 this.events.emit("renderJson", req, res, data, status);
329 }
330 };
331 this.renderImage = (req, res, filename, options) => {
332 logger.debug("App.renderImage", req.originalUrl, filename, options);
333 let mimetype = options ? options.mimetype : null;
334 if (!mimetype) {
335 let extname = path.extname(filename).toLowerCase().replace(".", "");
336 if (extname == "jpg") {
337 extname = "jpeg";
338 }
339 mimetype = `image/${extname}`;
340 }
341 res.type(mimetype);
342 res.sendFile(filename);
343 if (settings.app.events.render) {
344 this.events.emit("renderImage", req, res, filename, options);
345 }
346 };
347 this.renderError = (req, res, error, status) => {
348 let message;
349 logger.debug("App.renderError", req.originalUrl, status, error);
350 if (typeof error == "undefined" || error == null) {
351 error = "Unknown error";
352 logger.warn("App.renderError", "Called with null error");
353 }
354 if (status == null) {
355 status = error.statusCode || error.status || error.code;
356 }
357 if (status == "ETIMEDOUT") {
358 status = 408;
359 }
360 if (isNaN(status)) {
361 status = 500;
362 }
363 else {
364 status = parseInt(status);
365 }
366 try {
367 if (error.error && !error.message && !error.error_description && !error.reason) {
368 error = error.error;
369 }
370 if (utils_1.isString(error)) {
371 message = { message: error };
372 }
373 else {
374 message = {};
375 message.message = error.message || error.error_description || error.description;
376 if (!message.message) {
377 message.message = error.toString();
378 }
379 if (error.friendlyMessage) {
380 message.friendlyMessage = error.friendlyMessage;
381 }
382 if (error.reason) {
383 message.reason = error.reason;
384 }
385 if (error.code) {
386 message.code = error.code;
387 }
388 else if (error.status) {
389 message.code = error.status;
390 }
391 }
392 }
393 catch (ex) {
394 logger.error("App.renderError", ex);
395 }
396 res.status(status).json(message);
397 if (settings.app.events.render) {
398 this.events.emit("renderError", req, res, error, status);
399 }
400 };
401 if (!logger.isReady) {
402 logger.setup();
403 }
404 setmeup.load(__dirname + "/../settings.default.json", { overwrite: false });
405 settings = setmeup.settings;
406 }
407 static get Instance() {
408 return this._instance || (this._instance = new this());
409 }
410 newInstance() {
411 return new App();
412 }
413}
414exports.App = App;
415exports.default = App.Instance;