diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 03d9549..476dc0f 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -2,5 +2,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 3b2421e..44046d5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -528,6 +528,15 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"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": {
"version": "1.2.0",
"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",
"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": {
"version": "2.2.0",
"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",
"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": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-5.0.0.tgz",
@@ -904,6 +981,12 @@
"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": {
"version": "2.6.9",
"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",
"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": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/json-prune/-/json-prune-1.1.0.tgz",
@@ -2324,6 +2413,12 @@
"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": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -2929,6 +3024,15 @@
"integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==",
"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": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -3082,6 +3186,12 @@
"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": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
@@ -3232,6 +3342,15 @@
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"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": {
"version": "5.4.6",
"resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
@@ -3333,6 +3452,12 @@
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
@@ -3667,6 +3792,12 @@
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
diff --git a/package.json b/package.json
index 1b1c6d7..9d502f4 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,10 @@
"scripts": {
"lint": "eslint . --ext .ts",
"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",
"production": "node --use-openssl-ca --unhandled-rejections=strict ./out/index",
"install-debug": "npm install && npm run build",
@@ -41,10 +44,13 @@
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^4.7.0",
"@typescript-eslint/parser": "^4.7.0",
+ "concurrently": "^5.3.0",
"eslint": "^7.13.0",
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-promise": "^4.2.1",
+ "node-watch": "^0.7.0",
"tsc-watch": "^4.2.9",
- "typescript": "^4.0.5"
+ "typescript": "^4.0.5",
+ "ws": "^7.4.0"
}
}
diff --git a/public/js-source/.eslintrc.js b/public/js-source/.eslintrc.js
index 5a8ae8a..401a44e 100644
--- a/public/js-source/.eslintrc.js
+++ b/public/js-source/.eslintrc.js
@@ -29,6 +29,8 @@ module.exports = {
"no-void": "error",
"comma-spacing": "error",
"comma-dangle": ["error", "always-multiline"],
+ "comma-style": "error",
+ "semi": "error",
"no-restricted-imports": ["error",
"assert", "buffer", "child_process", "cluster", "crypto", "dgram", "dns", "domain", "events", "freelist",
diff --git a/public/js-source/src/SomeModule.ts b/public/js-source/src/SomeModule.ts
index 884d9de..48fdee2 100644
--- a/public/js-source/src/SomeModule.ts
+++ b/public/js-source/src/SomeModule.ts
@@ -1,4 +1,4 @@
-import {getUserInfo} from './utils';
+import {getUserInfo} from './utils/utils';
export async function getUserName(): Promise {
const info = await getUserInfo();
diff --git a/public/js-source/src/index.ts b/public/js-source/src/index.ts
index c4cef9a..6090cd0 100644
--- a/public/js-source/src/index.ts
+++ b/public/js-source/src/index.ts
@@ -1,7 +1,5 @@
-// Export setConfig so that the external url can set set automatically
-
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export {setConfig} from './utils';
+// Export setConfig so that the external url can set set automatically by the server
+export {setConfig} from './utils/utils';
import {getUserName} from './SomeModule';
diff --git a/public/js-source/src/resolvable.ts b/public/js-source/src/utils/resolvable.ts
similarity index 100%
rename from public/js-source/src/resolvable.ts
rename to public/js-source/src/utils/resolvable.ts
diff --git a/public/js-source/src/types/userinfo.d.ts b/public/js-source/src/utils/types/userinfo.d.ts
similarity index 100%
rename from public/js-source/src/types/userinfo.d.ts
rename to public/js-source/src/utils/types/userinfo.d.ts
diff --git a/public/js-source/src/utils.ts b/public/js-source/src/utils/utils.ts
similarity index 81%
rename from public/js-source/src/utils.ts
rename to public/js-source/src/utils/utils.ts
index 58f6d97..3ec5ed0 100644
--- a/public/js-source/src/utils.ts
+++ b/public/js-source/src/utils/utils.ts
@@ -17,7 +17,7 @@ export function getConfig(): Promise {
}
export async function getUserInfo(): Promise {
- const getBaseUrl = await getConfig();
- const res = await fetch(getBaseUrl.EXTERNAL_BASE_URL + "/api/user");
+ const config = await getConfig();
+ const res = await fetch(config.EXTERNAL_BASE_URL + "/api/user");
return res.json();
}
diff --git a/src/.eslintrc.js b/src/.eslintrc.js
index eb74958..2ce8160 100644
--- a/src/.eslintrc.js
+++ b/src/.eslintrc.js
@@ -11,6 +11,7 @@ module.exports = {
'eslint:recommended',
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
+ "plugin:promise/recommended",
],
rules: {
"no-console": "error",
@@ -30,16 +31,8 @@ module.exports = {
"no-void": "error",
"comma-spacing": "error",
"comma-dangle": ["error", "always-multiline"],
- "promise/no-return-wrap": "error",
- "promise/param-names": "error",
- "promise/catch-or-return": "error",
- "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",
+ "comma-style": "error",
+ "semi": "error",
+ "promise/always-return": "off",
},
};
diff --git a/src/app.ts b/src/app.ts
index 255c4ce..85777f3 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -6,15 +6,18 @@ import * as redisStore from 'connect-redis';
import * as session from 'express-session';
import * as sassMiddleware from 'node-sass-middleware';
import indexRouter from './routes';
-import {HttpLogger, Redis, Config, setupAuthProxy} from './utils';
+import {HttpLogger, Redis, Config, setupAuthProxy, getReloadRouter} from './utils';
import {Store} from 'express-session';
-
export const app = express();
// view engine setup
app.set('views', path.join(__dirname, '../views'));
app.set('view engine', 'pug');
+app.use((req, res, next) => {
+ res.locals.Config = Config;
+ next();
+});
const router = express.Router();
@@ -24,7 +27,9 @@ app.use(express.json());
app.use(express.urlencoded({extended: false}));
// auth proxy middleware
-app.use(setupAuthProxy);
+router.use(setupAuthProxy);
+// auto reloader (when running in debug mode)
+router.use(getReloadRouter());
// session
let sessionStore: Store|undefined = undefined;
@@ -32,7 +37,7 @@ if (Redis.client) {
const RedisStore = redisStore(session);
sessionStore = new RedisStore({client: Redis.client});
}
-app.use(session({
+router.use(session({
store: sessionStore,
secret: Config.SESSION_SECRET,
resave: false,
diff --git a/src/index.ts b/src/index.ts
index 3127e54..86bd82e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,7 +9,7 @@ import {Config, Logger} from './utils';
app.set('port', Config.PORT);
const server = http.createServer(app);
-server.listen(Config.PORT);
+app.listen(Config.PORT);
server.on('error', onError);
server.on('listening', onListening);
diff --git a/src/routes/index.ts b/src/routes/index.ts
index 5ca3f00..64f893b 100644
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -12,7 +12,7 @@ router.use("/health", healthRouter);
/* GET home page. */
router.get('/', async (req, res) => {
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) => {
diff --git a/src/utils/auth-proxy.ts b/src/utils/auth-proxy.ts
index 8a54a7f..95f01f4 100644
--- a/src/utils/auth-proxy.ts
+++ b/src/utils/auth-proxy.ts
@@ -2,11 +2,10 @@ import {Request, RequestHandler} from 'express';
import {Config, Logger} from '.';
import {Resolvable} from './helpers/resolvable';
import fetch from 'node-fetch';
-import * as properUrlJoin from 'proper-url-join';
-const urlJoin = properUrlJoin as unknown as properUrlJoin.default;
+import {urlJoin} from './helpers/urlJoin';
export const setupAuthProxy: RequestHandler = (req: Request, res, next) => {
- const resolvable = new Resolvable(async () => {
+ const resolvable = new Resolvable(async () => {
if (!Config.USERINFO_HEADER) {
return undefined;
}
@@ -17,7 +16,7 @@ export const setupAuthProxy: RequestHandler = (req: Request, res, next) => {
}
try {
const res = await fetch(url, {headers: [[Config.USERINFO_HEADER, token]]});
- return await res.json();
+ return await res.json() as UserInfo;
} catch (e) {
Logger.warn(e);
return undefined;
@@ -32,6 +31,6 @@ export const setupAuthProxy: RequestHandler = (req: Request, res, next) => {
}
this.redirect(307, url);
return true;
- }
+ };
next();
-}
+};
diff --git a/src/utils/auto-reload.ts b/src/utils/auto-reload.ts
new file mode 100644
index 0000000..4ea331b
--- /dev/null
+++ b/src/utils/auto-reload.ts
@@ -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;
+}
diff --git a/src/utils/config.ts b/src/utils/config.ts
index 8dd3ffe..d396065 100644
--- a/src/utils/config.ts
+++ b/src/utils/config.ts
@@ -36,5 +36,7 @@ const envs = {
export const Config = {
...envs,
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),
}
diff --git a/src/utils/helpers/resolvable.ts b/src/utils/helpers/resolvable.ts
index 5a99fb3..42a55bd 100644
--- a/src/utils/helpers/resolvable.ts
+++ b/src/utils/helpers/resolvable.ts
@@ -5,22 +5,22 @@ enum ResolvableState {
DONE
}
-class FetchOnce {
+class FetchOnce> {
protected data: T|undefined;
protected error: unknown|undefined;
protected state: ResolvableState = ResolvableState.WAITING;
protected pendings: [(res: Promise|T) => void, (reason: unknown) => void][] = [];
- constructor(protected fetchMethod?: () => Promise) { }
+ constructor(protected fetchMethod?: (...args: U) => Promise) { }
- public resolve(): Promise {
+ public resolve(...args: U): Promise {
// eslint-disable-next-line promise/avoid-new
return new Promise((resolve, reject) => {
switch (this.state) {
case ResolvableState.WAITING:
this.state = ResolvableState.PENDING;
this.pendings.push([resolve, reject]);
- if (this.fetchMethod) this.parsePromise(this.fetchMethod());
+ if (this.fetchMethod) this.parsePromise(this.fetchMethod(...args));
break;
case ResolvableState.PENDING:
this.pendings.push([resolve, reject]);
@@ -52,13 +52,13 @@ class FetchOnce {
}
}
-export class Resolvable extends FetchOnce {
- constructor(fetchMethod: () => Promise) {
+export class Resolvable> extends FetchOnce {
+ constructor(fetchMethod: (...args: U) => Promise) {
super(fetchMethod);
}
}
-export class WaitForSync extends FetchOnce {
+export class WaitForSync> extends FetchOnce {
protected state: ResolvableState = ResolvableState.PENDING;
constructor() {
diff --git a/src/utils/helpers/urlJoin.ts b/src/utils/helpers/urlJoin.ts
new file mode 100644
index 0000000..0fc1991
--- /dev/null
+++ b/src/utils/helpers/urlJoin.ts
@@ -0,0 +1,2 @@
+import * as properUrlJoin from 'proper-url-join';
+export const urlJoin = properUrlJoin as unknown as properUrlJoin.default;
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 8841858..ef022b5 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -2,3 +2,5 @@ export {Config} from './config';
export {Redis} from './redis';
export {Logger, HttpLogger} from './logging';
export {setupAuthProxy} from './auth-proxy';
+export {Resolvable, WaitForSync} from './helpers/resolvable';
+export {getReloadRouter} from './auto-reload';
diff --git a/src/utils/logging.ts b/src/utils/logging.ts
index 2282404..b2ad7c8 100644
--- a/src/utils/logging.ts
+++ b/src/utils/logging.ts
@@ -33,6 +33,7 @@ for (const level of levels) {
export const HttpLogger: RequestHandler = (req, res, next) => {
const start = Date.now();
+ const path = req.path;
type Callback = (() => void)|undefined;
const end = res.end;
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 method = req.method.toUpperCase().padEnd(6, " ");
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);
};
next();
-}
+};
diff --git a/src/utils/types/logging.d.ts b/src/utils/types/logging.d.ts
new file mode 100644
index 0000000..83b2916
--- /dev/null
+++ b/src/utils/types/logging.d.ts
@@ -0,0 +1,5 @@
+declare namespace Express {
+ interface Request {
+ noLogging: boolean|undefined;
+ }
+}
diff --git a/views/layout.pug b/views/layout.pug
index 2c5cdea..2f8e0e2 100644
--- a/views/layout.pug
+++ b/views/layout.pug
@@ -1,14 +1,16 @@
+- const baseUrl = Config.EXTERNAL_BASE_URL;
doctype html
html
head
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
block content
- script(type='text/javascript', src='/js/require-2.3.6.min.js')
- script(type='text/javascript', src='/js/bundle.js')
+ script(type='text/javascript', src=baseUrl+'/js/require-2.3.6.min.js')
+ script(type='text/javascript', src=baseUrl+'/js/bundle.js')
script.
require(['src/index'], function (index) {
- console.log(index);
- index.setConfig({EXTERNAL_BASE_URL: '#{externalUrl}'});
+ index.setConfig({EXTERNAL_BASE_URL: '#{baseUrl}'});
});