UNPKG

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