Auto reload and bug fixes

This commit is contained in:
Sebastian Seedorf
2020-11-14 19:38:16 +01:00
parent 5a56fc26d2
commit 6189b95b6e
22 changed files with 265 additions and 47 deletions

View File

@@ -2,5 +2,16 @@
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="2">
<item index="0" class="java.lang.String" itemvalue="src" />
<item index="1" class="java.lang.String" itemvalue="async" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
</profile> </profile>
</component> </component>

131
package-lock.json generated
View File

@@ -528,6 +528,15 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
}, },
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@@ -715,6 +724,28 @@
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
}, },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"dependencies": {
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"character-parser": { "character-parser": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
@@ -826,6 +857,52 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
}, },
"concurrently": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.3.0.tgz",
"integrity": "sha512-8MhqOB6PWlBfA2vJ8a0bSFKATOdWlHiQlk11IfmQBPaHVP8oP2gsh2MObE6UR3hqDHqvaIvLTyceNW6obVuFHQ==",
"dev": true,
"requires": {
"chalk": "^2.4.2",
"date-fns": "^2.0.1",
"lodash": "^4.17.15",
"read-pkg": "^4.0.1",
"rxjs": "^6.5.2",
"spawn-command": "^0.0.2-1",
"supports-color": "^6.1.0",
"tree-kill": "^1.2.2",
"yargs": "^13.3.0"
},
"dependencies": {
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"read-pkg": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz",
"integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=",
"dev": true,
"requires": {
"normalize-package-data": "^2.3.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0"
}
}
}
},
"connect-redis": { "connect-redis": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-5.0.0.tgz", "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-5.0.0.tgz",
@@ -904,6 +981,12 @@
"assert-plus": "^1.0.0" "assert-plus": "^1.0.0"
} }
}, },
"date-fns": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz",
"integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==",
"dev": true
},
"debug": { "debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -1980,6 +2063,12 @@
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
}, },
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
"json-prune": { "json-prune": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/json-prune/-/json-prune-1.1.0.tgz", "resolved": "https://registry.npmjs.org/json-prune/-/json-prune-1.1.0.tgz",
@@ -2324,6 +2413,12 @@
"node-sass": "^4.3.0" "node-sass": "^4.3.0"
} }
}, },
"node-watch": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.0.tgz",
"integrity": "sha512-OOBiglke5SlRQT5WYfwXTmYqTfXjcTNBHpalyHLtLxDpQYVpVRkJqabcch1kmwJsjV/J4OZuzEafeb4soqtFZA==",
"dev": true
},
"nopt": { "nopt": {
"version": "3.0.6", "version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -2929,6 +3024,15 @@
"integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==",
"dev": true "dev": true
}, },
"rxjs": {
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
"integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -3082,6 +3186,12 @@
"amdefine": ">=0.0.4" "amdefine": ">=0.0.4"
} }
}, },
"spawn-command": {
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
"dev": true
},
"spdx-correct": { "spdx-correct": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
@@ -3232,6 +3342,15 @@
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true "dev": true
}, },
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"table": { "table": {
"version": "5.4.6", "version": "5.4.6",
"resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
@@ -3333,6 +3452,12 @@
"punycode": "^2.1.1" "punycode": "^2.1.1"
} }
}, },
"tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true
},
"trim-newlines": { "trim-newlines": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
@@ -3667,6 +3792,12 @@
"mkdirp": "^0.5.1" "mkdirp": "^0.5.1"
} }
}, },
"ws": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz",
"integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==",
"dev": true
},
"y18n": { "y18n": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",

View File

@@ -5,7 +5,10 @@
"scripts": { "scripts": {
"lint": "eslint . --ext .ts", "lint": "eslint . --ext .ts",
"lint-fix": "eslint . --ext .ts --fix", "lint-fix": "eslint . --ext .ts --fix",
"debug": "tsc-watch --onSuccess \"node --enable-source-maps --use-openssl-ca --unhandled-rejections=strict ./out/index\"", "update-client-hash": "node -e \"require('fs').writeFileSync('public/misc/hash.txt', require('randomstring').generate())\"",
"debug-client": "tsc-watch --project ./public/js-source",
"debug-server": "tsc-watch --onSuccess \"node --enable-source-maps --use-openssl-ca --unhandled-rejections=strict ./out/index\"",
"debug": "concurrently \"npm run debug-client\" \"npm run debug-server\"",
"build": "tsc", "build": "tsc",
"production": "node --use-openssl-ca --unhandled-rejections=strict ./out/index", "production": "node --use-openssl-ca --unhandled-rejections=strict ./out/index",
"install-debug": "npm install && npm run build", "install-debug": "npm install && npm run build",
@@ -41,10 +44,13 @@
"@types/uuid": "^8.3.0", "@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^4.7.0", "@typescript-eslint/eslint-plugin": "^4.7.0",
"@typescript-eslint/parser": "^4.7.0", "@typescript-eslint/parser": "^4.7.0",
"concurrently": "^5.3.0",
"eslint": "^7.13.0", "eslint": "^7.13.0",
"eslint-plugin-no-null": "^1.0.2", "eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-promise": "^4.2.1", "eslint-plugin-promise": "^4.2.1",
"node-watch": "^0.7.0",
"tsc-watch": "^4.2.9", "tsc-watch": "^4.2.9",
"typescript": "^4.0.5" "typescript": "^4.0.5",
"ws": "^7.4.0"
} }
} }

View File

@@ -29,6 +29,8 @@ module.exports = {
"no-void": "error", "no-void": "error",
"comma-spacing": "error", "comma-spacing": "error",
"comma-dangle": ["error", "always-multiline"], "comma-dangle": ["error", "always-multiline"],
"comma-style": "error",
"semi": "error",
"no-restricted-imports": ["error", "no-restricted-imports": ["error",
"assert", "buffer", "child_process", "cluster", "crypto", "dgram", "dns", "domain", "events", "freelist", "assert", "buffer", "child_process", "cluster", "crypto", "dgram", "dns", "domain", "events", "freelist",

View File

@@ -1,4 +1,4 @@
import {getUserInfo} from './utils'; import {getUserInfo} from './utils/utils';
export async function getUserName(): Promise<string> { export async function getUserName(): Promise<string> {
const info = await getUserInfo(); const info = await getUserInfo();

View File

@@ -1,7 +1,5 @@
// Export setConfig so that the external url can set set automatically // Export setConfig so that the external url can set set automatically by the server
export {setConfig} from './utils/utils';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export {setConfig} from './utils';
import {getUserName} from './SomeModule'; import {getUserName} from './SomeModule';

View File

@@ -17,7 +17,7 @@ export function getConfig(): Promise<ClientConfig> {
} }
export async function getUserInfo(): Promise<UserInfo|undefined> { export async function getUserInfo(): Promise<UserInfo|undefined> {
const getBaseUrl = await getConfig(); const config = await getConfig();
const res = await fetch(getBaseUrl.EXTERNAL_BASE_URL + "/api/user"); const res = await fetch(config.EXTERNAL_BASE_URL + "/api/user");
return res.json(); return res.json();
} }

View File

@@ -11,6 +11,7 @@ module.exports = {
'eslint:recommended', 'eslint:recommended',
"plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:promise/recommended",
], ],
rules: { rules: {
"no-console": "error", "no-console": "error",
@@ -30,16 +31,8 @@ module.exports = {
"no-void": "error", "no-void": "error",
"comma-spacing": "error", "comma-spacing": "error",
"comma-dangle": ["error", "always-multiline"], "comma-dangle": ["error", "always-multiline"],
"promise/no-return-wrap": "error", "comma-style": "error",
"promise/param-names": "error", "semi": "error",
"promise/catch-or-return": "error", "promise/always-return": "off",
"promise/no-native": "off",
"promise/no-nesting": "warn",
"promise/no-promise-in-callback": "warn",
"promise/no-callback-in-promise": "warn",
"promise/avoid-new": "warn",
"promise/no-new-statics": "error",
"promise/no-return-in-finally": "warn",
"promise/valid-params": "warn",
}, },
}; };

View File

@@ -6,15 +6,18 @@ import * as redisStore from 'connect-redis';
import * as session from 'express-session'; import * as session from 'express-session';
import * as sassMiddleware from 'node-sass-middleware'; import * as sassMiddleware from 'node-sass-middleware';
import indexRouter from './routes'; import indexRouter from './routes';
import {HttpLogger, Redis, Config, setupAuthProxy} from './utils'; import {HttpLogger, Redis, Config, setupAuthProxy, getReloadRouter} from './utils';
import {Store} from 'express-session'; import {Store} from 'express-session';
export const app = express(); export const app = express();
// view engine setup // view engine setup
app.set('views', path.join(__dirname, '../views')); app.set('views', path.join(__dirname, '../views'));
app.set('view engine', 'pug'); app.set('view engine', 'pug');
app.use((req, res, next) => {
res.locals.Config = Config;
next();
});
const router = express.Router(); const router = express.Router();
@@ -24,7 +27,9 @@ app.use(express.json());
app.use(express.urlencoded({extended: false})); app.use(express.urlencoded({extended: false}));
// auth proxy middleware // auth proxy middleware
app.use(setupAuthProxy); router.use(setupAuthProxy);
// auto reloader (when running in debug mode)
router.use(getReloadRouter());
// session // session
let sessionStore: Store|undefined = undefined; let sessionStore: Store|undefined = undefined;
@@ -32,7 +37,7 @@ if (Redis.client) {
const RedisStore = redisStore(session); const RedisStore = redisStore(session);
sessionStore = new RedisStore({client: Redis.client}); sessionStore = new RedisStore({client: Redis.client});
} }
app.use(session({ router.use(session({
store: sessionStore, store: sessionStore,
secret: Config.SESSION_SECRET, secret: Config.SESSION_SECRET,
resave: false, resave: false,

View File

@@ -9,7 +9,7 @@ import {Config, Logger} from './utils';
app.set('port', Config.PORT); app.set('port', Config.PORT);
const server = http.createServer(app); const server = http.createServer(app);
server.listen(Config.PORT); app.listen(Config.PORT);
server.on('error', onError); server.on('error', onError);
server.on('listening', onListening); server.on('listening', onListening);

View File

@@ -12,7 +12,7 @@ router.use("/health", healthRouter);
/* GET home page. */ /* GET home page. */
router.get('/', async (req, res) => { router.get('/', async (req, res) => {
const email = (await req.getUserInfo())?.email ?? "No email found!"; const email = (await req.getUserInfo())?.email ?? "No email found!";
res.render('index', {title: 'Express', email, externalUrl: Config.EXTERNAL_BASE_URL}); res.render('index', {title: 'Express', email});
}); });
router.get('/logout', (req, res) => { router.get('/logout', (req, res) => {

View File

@@ -2,11 +2,10 @@ import {Request, RequestHandler} from 'express';
import {Config, Logger} from '.'; import {Config, Logger} from '.';
import {Resolvable} from './helpers/resolvable'; import {Resolvable} from './helpers/resolvable';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import * as properUrlJoin from 'proper-url-join'; import {urlJoin} from './helpers/urlJoin';
const urlJoin = properUrlJoin as unknown as properUrlJoin.default;
export const setupAuthProxy: RequestHandler = (req: Request, res, next) => { export const setupAuthProxy: RequestHandler = (req: Request, res, next) => {
const resolvable = new Resolvable<UserInfo|undefined>(async () => { const resolvable = new Resolvable(async () => {
if (!Config.USERINFO_HEADER) { if (!Config.USERINFO_HEADER) {
return undefined; return undefined;
} }
@@ -17,7 +16,7 @@ export const setupAuthProxy: RequestHandler = (req: Request, res, next) => {
} }
try { try {
const res = await fetch(url, {headers: [[Config.USERINFO_HEADER, token]]}); const res = await fetch(url, {headers: [[Config.USERINFO_HEADER, token]]});
return await res.json(); return await res.json() as UserInfo;
} catch (e) { } catch (e) {
Logger.warn(e); Logger.warn(e);
return undefined; return undefined;
@@ -32,6 +31,6 @@ export const setupAuthProxy: RequestHandler = (req: Request, res, next) => {
} }
this.redirect(307, url); this.redirect(307, url);
return true; return true;
} };
next(); next();
} };

58
src/utils/auto-reload.ts Normal file
View File

@@ -0,0 +1,58 @@
import {Router} from 'express';
import {Config} from './config';
import {Logger} from './logging';
import {urlJoin} from './helpers/urlJoin';
import {v4} from 'uuid';
import Timeout = NodeJS.Timeout;
export function getReloadRouter(): Router {
const reloadRouter = Router();
if (!Config.isProduction) {
let uuid = v4();
let updateTimeout: Timeout|undefined = undefined;
import("node-watch").then((watch) => {
watch.default('public', {recursive: true}, (evt, name) => {
if (updateTimeout !== undefined) clearTimeout(updateTimeout);
updateTimeout = setTimeout(() => {
uuid = v4();
}, 200);
});
}).catch((err) => { Logger.error(err); });
reloadRouter.get("/auto-reload/client.js", (req, res) => {
Logger.debug(req.url, req.originalUrl, req.baseUrl);
res.setHeader('Content-Type', "application/javascript");
// language=JavaScript
res.send(`
const loc = window.location;
const url = loc.protocol+'//'+loc.host+'${urlJoin(req.baseUrl, "/auto-reload")}';
const parse = async res => (await res.json()).uuid;
let hash = undefined;
let hadError = false;
setInterval(async () => {
try {
const data = await parse(await fetch(url));
if (data) {
hash = hash === undefined ? data : hash;
if (hash !== data) {
window.location.reload();
}
}
} catch (e) {
if (hadError === false) {
console.log(e);
hadError = true;
}
}
}, 3000);
`.replace(/\t/g, ""));
});
reloadRouter.get("/auto-reload", (req, res) => {
req.noLogging = true;
res.json({uuid});
});
}
return reloadRouter;
}

View File

@@ -36,5 +36,7 @@ const envs = {
export const Config = { export const Config = {
...envs, ...envs,
isProduction, isProduction,
EXTERNAL_BASE_URL: envs.EXTERNAL_BASE_URL || urlJoin(`http://${envs.HOSTNAME}${envs.PORT !== 80 ? `:${envs.PORT}` : ""}`), EXTERNAL_BASE_URL:
envs.EXTERNAL_BASE_URL ||
urlJoin(`http://${envs.HOSTNAME}${envs.PORT !== 80 ? `:${envs.PORT}` : ""}`, envs.BASE_PATH),
} }

View File

@@ -5,22 +5,22 @@ enum ResolvableState {
DONE DONE
} }
class FetchOnce<T> { class FetchOnce<T, U extends Array<unknown>> {
protected data: T|undefined; protected data: T|undefined;
protected error: unknown|undefined; protected error: unknown|undefined;
protected state: ResolvableState = ResolvableState.WAITING; protected state: ResolvableState = ResolvableState.WAITING;
protected pendings: [(res: Promise<T>|T) => void, (reason: unknown) => void][] = []; protected pendings: [(res: Promise<T>|T) => void, (reason: unknown) => void][] = [];
constructor(protected fetchMethod?: () => Promise<T>) { } constructor(protected fetchMethod?: (...args: U) => Promise<T>) { }
public resolve(): Promise<T> { public resolve(...args: U): Promise<T> {
// eslint-disable-next-line promise/avoid-new // eslint-disable-next-line promise/avoid-new
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
switch (this.state) { switch (this.state) {
case ResolvableState.WAITING: case ResolvableState.WAITING:
this.state = ResolvableState.PENDING; this.state = ResolvableState.PENDING;
this.pendings.push([resolve, reject]); this.pendings.push([resolve, reject]);
if (this.fetchMethod) this.parsePromise(this.fetchMethod()); if (this.fetchMethod) this.parsePromise(this.fetchMethod(...args));
break; break;
case ResolvableState.PENDING: case ResolvableState.PENDING:
this.pendings.push([resolve, reject]); this.pendings.push([resolve, reject]);
@@ -52,13 +52,13 @@ class FetchOnce<T> {
} }
} }
export class Resolvable<T> extends FetchOnce<T> { export class Resolvable<T, U extends Array<unknown>> extends FetchOnce<T, U> {
constructor(fetchMethod: () => Promise<T>) { constructor(fetchMethod: (...args: U) => Promise<T>) {
super(fetchMethod); super(fetchMethod);
} }
} }
export class WaitForSync<T> extends FetchOnce<T> { export class WaitForSync<T, U extends Array<unknown>> extends FetchOnce<T, U> {
protected state: ResolvableState = ResolvableState.PENDING; protected state: ResolvableState = ResolvableState.PENDING;
constructor() { constructor() {

View File

@@ -0,0 +1,2 @@
import * as properUrlJoin from 'proper-url-join';
export const urlJoin = properUrlJoin as unknown as properUrlJoin.default;

View File

@@ -2,3 +2,5 @@ export {Config} from './config';
export {Redis} from './redis'; export {Redis} from './redis';
export {Logger, HttpLogger} from './logging'; export {Logger, HttpLogger} from './logging';
export {setupAuthProxy} from './auth-proxy'; export {setupAuthProxy} from './auth-proxy';
export {Resolvable, WaitForSync} from './helpers/resolvable';
export {getReloadRouter} from './auto-reload';

View File

@@ -33,6 +33,7 @@ for (const level of levels) {
export const HttpLogger: RequestHandler = (req, res, next) => { export const HttpLogger: RequestHandler = (req, res, next) => {
const start = Date.now(); const start = Date.now();
const path = req.path;
type Callback = (() => void)|undefined; type Callback = (() => void)|undefined;
const end = res.end; const end = res.end;
res.end = function(...args: [Callback] & [unknown, Callback] & [unknown, BufferEncoding, Callback]) { res.end = function(...args: [Callback] & [unknown, Callback] & [unknown, BufferEncoding, Callback]) {
@@ -45,8 +46,9 @@ export const HttpLogger: RequestHandler = (req, res, next) => {
const status = colorFunction(res.statusCode.toString(10)); const status = colorFunction(res.statusCode.toString(10));
const method = req.method.toUpperCase().padEnd(6, " "); const method = req.method.toUpperCase().padEnd(6, " ");
const responseTime = (Date.now()-start).toString(10).padStart(3, " "); const responseTime = (Date.now()-start).toString(10).padStart(3, " ");
Logger.http(`${status} ${method} ${responseTime}ms ${req.path}`); if (!req.noLogging)
Logger.http(`${status} ${method} ${responseTime}ms ${path}`);
end.apply(res, args); end.apply(res, args);
}; };
next(); next();
} };

5
src/utils/types/logging.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
declare namespace Express {
interface Request {
noLogging: boolean|undefined;
}
}

View File

@@ -1,14 +1,16 @@
- const baseUrl = Config.EXTERNAL_BASE_URL;
doctype html doctype html
html html
head head
title= title title= title
link(rel='stylesheet', href='/styles/style.css') link(rel='stylesheet', href=baseUrl+'/styles/style.css')
if !Config.isProduction
script(type='text/javascript', async, src=baseUrl+'/auto-reload/client.js')
body body
block content block content
script(type='text/javascript', src='/js/require-2.3.6.min.js') script(type='text/javascript', src=baseUrl+'/js/require-2.3.6.min.js')
script(type='text/javascript', src='/js/bundle.js') script(type='text/javascript', src=baseUrl+'/js/bundle.js')
script. script.
require(['src/index'], function (index) { require(['src/index'], function (index) {
console.log(index); index.setConfig({EXTERNAL_BASE_URL: '#{baseUrl}'});
index.setConfig({EXTERNAL_BASE_URL: '#{externalUrl}'});
}); });