Compare commits
10 Commits
bc83ca46a2
...
924dfc7def
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
924dfc7def | ||
|
|
88ca594900 | ||
|
|
d6ceb9152e | ||
|
|
8dc481b551 | ||
|
|
91ba224f79 | ||
|
|
055d11b9b1 | ||
|
|
3a58260d12 | ||
|
|
40b148b7c7 | ||
|
|
7f44df42e7 | ||
|
|
e2662b1c3b |
52
.eslintrc.js
52
.eslintrc.js
@@ -9,32 +9,38 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
extends: [
|
extends: [
|
||||||
'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",
|
'plugin:promise/recommended',
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
"no-console": "error",
|
'no-console': 'error',
|
||||||
"max-len": ["error", {"code": 128}],
|
'max-len': ['error', {'code': 128}],
|
||||||
"no-process-env": "error",
|
'no-process-env': 'error',
|
||||||
"no-process-exit": "error",
|
'no-process-exit': 'error',
|
||||||
"no-null/no-null": "error",
|
'no-null/no-null': 'error',
|
||||||
"no-useless-return": "error",
|
'no-useless-return': 'error',
|
||||||
"prefer-arrow-callback": "warn",
|
'prefer-arrow-callback': 'warn',
|
||||||
"object-curly-spacing": "error",
|
'consistent-return': 'error',
|
||||||
"consistent-return": "error",
|
'@typescript-eslint/explicit-function-return-type': [
|
||||||
"@typescript-eslint/explicit-function-return-type": [
|
'error', {
|
||||||
"error", {
|
'allowExpressions': true,
|
||||||
"allowExpressions": true,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"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",
|
'comma-style': 'error',
|
||||||
"semi": "error",
|
'semi': 'error',
|
||||||
"no-implicit-coercion": "error",
|
'no-implicit-coercion': 'error',
|
||||||
|
'quotes': ['error', 'single', 'avoid-escape'],
|
||||||
"promise/always-return": "off",
|
'keyword-spacing': 'error',
|
||||||
|
'semi-spacing': 'error',
|
||||||
|
'arrow-spacing': 'error',
|
||||||
|
'object-curly-spacing': 'error',
|
||||||
|
'array-bracket-spacing': 'error',
|
||||||
|
'key-spacing': 'error',
|
||||||
|
'block-spacing': 'error',
|
||||||
|
'promise/always-return': 'off',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,19 +12,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|||||||
exports.AuthProxy = void 0;
|
exports.AuthProxy = void 0;
|
||||||
const _1 = require(".");
|
const _1 = require(".");
|
||||||
const node_fetch_1 = require("node-fetch");
|
const node_fetch_1 = require("node-fetch");
|
||||||
|
const cachedUsers = {};
|
||||||
const router = (req, res, next) => {
|
const router = (req, res, next) => {
|
||||||
const resolvable = new _1.Resolvable(() => __awaiter(void 0, void 0, void 0, function* () {
|
const resolvable = new _1.Resolvable(() => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
if (!_1.DefaultConfig.USERINFO_HEADER) {
|
if (!_1.DefaultConfig.USERINFO_HEADER) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const token = req.header(_1.DefaultConfig.USERINFO_HEADER);
|
const token = req.header(_1.DefaultConfig.USERINFO_HEADER);
|
||||||
|
if (token && cachedUsers[token])
|
||||||
|
return cachedUsers[token];
|
||||||
const url = _1.DefaultConfig.AUTH_PROXY_USERINFO_URL;
|
const url = _1.DefaultConfig.AUTH_PROXY_USERINFO_URL;
|
||||||
if (token === undefined || url === undefined) {
|
if (token === undefined || url === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
const expireHeader = req.header(_1.DefaultConfig.EXPIRE_HEADER);
|
||||||
|
const expireSecDate = expireHeader && parseInt(expireHeader, 10) || undefined;
|
||||||
|
const expiresIn = expireSecDate && expireSecDate * 1000 > Date.now() && expireSecDate * 1000 - Date.now();
|
||||||
const res = yield node_fetch_1.default(url, { headers: [[_1.DefaultConfig.USERINFO_HEADER, token]] });
|
const res = yield node_fetch_1.default(url, { headers: [[_1.DefaultConfig.USERINFO_HEADER, token]] });
|
||||||
return yield res.json();
|
const userinfo = yield res.json();
|
||||||
|
if (expiresIn) {
|
||||||
|
cachedUsers[token] = userinfo;
|
||||||
|
setTimeout(() => delete cachedUsers[token], expiresIn);
|
||||||
|
}
|
||||||
|
return userinfo;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
_1.Logger.warn(e);
|
_1.Logger.warn(e);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ function getRouter(opts) {
|
|||||||
if (!_1.DefaultConfig.isProduction) {
|
if (!_1.DefaultConfig.isProduction) {
|
||||||
let uuid = uuid_1.v4();
|
let uuid = uuid_1.v4();
|
||||||
let updateTimeout = undefined;
|
let updateTimeout = undefined;
|
||||||
Promise.resolve().then(() => require("node-watch")).then((watch) => {
|
Promise.resolve().then(() => require('node-watch')).then((watch) => {
|
||||||
for (const frontendDir of config.frontendDirs) {
|
for (const frontendDir of config.frontendDirs) {
|
||||||
watch.default(frontendDir, { recursive: true }, () => {
|
watch.default(frontendDir, { recursive: true }, () => {
|
||||||
if (updateTimeout !== undefined)
|
if (updateTimeout !== undefined)
|
||||||
@@ -28,7 +28,7 @@ function getRouter(opts) {
|
|||||||
// /auto-reload/client.js
|
// /auto-reload/client.js
|
||||||
router.get(_1.urlJoin(config.subPath, config.jsName), (req, res) => {
|
router.get(_1.urlJoin(config.subPath, config.jsName), (req, res) => {
|
||||||
_1.Logger.debug(req.url, req.originalUrl, req.baseUrl);
|
_1.Logger.debug(req.url, req.originalUrl, req.baseUrl);
|
||||||
res.setHeader('Content-Type', "application/javascript");
|
res.setHeader('Content-Type', 'application/javascript');
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
res.send(`
|
res.send(`
|
||||||
const loc = window.location;
|
const loc = window.location;
|
||||||
@@ -58,7 +58,7 @@ function getRouter(opts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
`.replace(/\t/g, ""));
|
`.replace(/\t/g, ''));
|
||||||
});
|
});
|
||||||
// /auto-reload
|
// /auto-reload
|
||||||
router.get(_1.urlJoin(config.subPath), (req, res) => {
|
router.get(_1.urlJoin(config.subPath), (req, res) => {
|
||||||
|
|||||||
1
out/config.d.ts
vendored
1
out/config.d.ts
vendored
@@ -12,6 +12,7 @@ export declare const DefaultConfig: {
|
|||||||
REDIS_URL: string | undefined;
|
REDIS_URL: string | undefined;
|
||||||
SESSION_SECRET: string | undefined;
|
SESSION_SECRET: string | undefined;
|
||||||
USERINFO_HEADER: string;
|
USERINFO_HEADER: string;
|
||||||
|
EXPIRE_HEADER: string;
|
||||||
AUTH_PROXY_URL: string | undefined;
|
AUTH_PROXY_URL: string | undefined;
|
||||||
};
|
};
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.DefaultConfig = void 0;
|
exports.DefaultConfig = void 0;
|
||||||
const env = require("env-var");
|
const env = require("env-var");
|
||||||
const _1 = require(".");
|
const urlJoin_1 = require("./helpers/urlJoin");
|
||||||
const NODE_ENV = env.get('NODE_ENV').default("development").asString();
|
const NODE_ENV = env.get('NODE_ENV').default('development').asString();
|
||||||
const isProduction = NODE_ENV === 'production';
|
const isProduction = NODE_ENV === 'production';
|
||||||
const envs = {
|
const envs = {
|
||||||
NODE_ENV,
|
NODE_ENV,
|
||||||
@@ -20,7 +20,9 @@ const envs = {
|
|||||||
// cookie secret for the session id (required in production using Session)
|
// cookie secret for the session id (required in production using Session)
|
||||||
SESSION_SECRET: env.get('SESSION_SECRET').asString() || undefined,
|
SESSION_SECRET: env.get('SESSION_SECRET').asString() || undefined,
|
||||||
// header where user info token is stored to request auth proxy
|
// header where user info token is stored to request auth proxy
|
||||||
USERINFO_HEADER: env.get('USERINFO_HEADER').default("X-Userinfo-Token").asString(),
|
USERINFO_HEADER: env.get('USERINFO_HEADER').default('X-Userinfo-Token').asString(),
|
||||||
|
// header when user info token expires (seconds after 01/01/1970)
|
||||||
|
EXPIRE_HEADER: env.get('EXPIRE_HEADER').default('X-Userinfo-Expire').asString(),
|
||||||
// base url to init a logout or request user info
|
// base url to init a logout or request user info
|
||||||
AUTH_PROXY_URL: env.get('AUTH_PROXY_URL').asString() || undefined,
|
AUTH_PROXY_URL: env.get('AUTH_PROXY_URL').asString() || undefined,
|
||||||
// override AUTH_PROXY_URL to request user info
|
// override AUTH_PROXY_URL to request user info
|
||||||
@@ -32,7 +34,7 @@ function requireEnv(name, onlyInProduction = false) {
|
|||||||
env.get(name).required(!onlyInProduction || isProduction).asString();
|
env.get(name).required(!onlyInProduction || isProduction).asString();
|
||||||
}
|
}
|
||||||
exports.DefaultConfig = Object.assign(Object.assign({}, envs), { EXTERNAL_BASE_URL: envs.EXTERNAL_BASE_URL ||
|
exports.DefaultConfig = Object.assign(Object.assign({}, envs), { EXTERNAL_BASE_URL: envs.EXTERNAL_BASE_URL ||
|
||||||
_1.urlJoin(`http://${envs.HOSTNAME}${envs.PORT !== 80 ? `:${envs.PORT}` : ""}`, envs.BASE_PATH), isProduction,
|
urlJoin_1.urlJoin(`http://${envs.HOSTNAME}${envs.PORT !== 80 ? `:${envs.PORT}` : ''}`, envs.BASE_PATH), isProduction,
|
||||||
requireEnv, AUTH_PROXY_USERINFO_URL: envs.AUTH_PROXY_USERINFO_URL ||
|
requireEnv, AUTH_PROXY_USERINFO_URL: envs.AUTH_PROXY_USERINFO_URL ||
|
||||||
envs.AUTH_PROXY_URL && _1.urlJoin(envs.AUTH_PROXY_URL, "userinfo"), AUTH_PROXY_INIT_LOGOUT_URL: envs.AUTH_PROXY_INIT_LOGOUT_URL ||
|
envs.AUTH_PROXY_URL && urlJoin_1.urlJoin(envs.AUTH_PROXY_URL, 'userinfo'), AUTH_PROXY_INIT_LOGOUT_URL: envs.AUTH_PROXY_INIT_LOGOUT_URL ||
|
||||||
envs.AUTH_PROXY_URL && _1.urlJoin(envs.AUTH_PROXY_URL, "init-logout") });
|
envs.AUTH_PROXY_URL && urlJoin_1.urlJoin(envs.AUTH_PROXY_URL, 'init-logout') });
|
||||||
|
|||||||
4
out/helpers/resolvable.d.ts
vendored
4
out/helpers/resolvable.d.ts
vendored
@@ -11,7 +11,10 @@ declare class FetchOnce<T, U extends Array<unknown>> {
|
|||||||
protected state: ResolvableState;
|
protected state: ResolvableState;
|
||||||
protected pendings: [(res: Promise<T> | T) => void, (reason: unknown) => void][];
|
protected pendings: [(res: Promise<T> | T) => void, (reason: unknown) => void][];
|
||||||
constructor(fetchMethod?: ((...args: U) => Promise<T>) | undefined);
|
constructor(fetchMethod?: ((...args: U) => Promise<T>) | undefined);
|
||||||
|
reset(): void;
|
||||||
resolve(...args: U): Promise<T>;
|
resolve(...args: U): Promise<T>;
|
||||||
|
getDataOrUndefined(): T | undefined;
|
||||||
|
getErrorOrUndefined(): unknown | undefined;
|
||||||
protected isFinished(): boolean;
|
protected isFinished(): boolean;
|
||||||
protected parsePromise(promise: Promise<T>): void;
|
protected parsePromise(promise: Promise<T>): void;
|
||||||
}
|
}
|
||||||
@@ -23,5 +26,6 @@ export declare class WaitForSync<T> extends FetchOnce<T, never> {
|
|||||||
constructor();
|
constructor();
|
||||||
setData(data: T): void;
|
setData(data: T): void;
|
||||||
setError(error: unknown): void;
|
setError(error: unknown): void;
|
||||||
|
reset(): void;
|
||||||
}
|
}
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
@@ -23,8 +23,12 @@ class FetchOnce {
|
|||||||
this.state = ResolvableState.WAITING;
|
this.state = ResolvableState.WAITING;
|
||||||
this.pendings = [];
|
this.pendings = [];
|
||||||
}
|
}
|
||||||
|
reset() {
|
||||||
|
this.data = undefined;
|
||||||
|
this.error = undefined;
|
||||||
|
this.state = ResolvableState.WAITING;
|
||||||
|
}
|
||||||
resolve(...args) {
|
resolve(...args) {
|
||||||
// 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:
|
||||||
@@ -45,6 +49,12 @@ class FetchOnce {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
getDataOrUndefined() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
getErrorOrUndefined() {
|
||||||
|
return this.error;
|
||||||
|
}
|
||||||
isFinished() {
|
isFinished() {
|
||||||
return this.state === ResolvableState.DONE || this.state === ResolvableState.ERROR;
|
return this.state === ResolvableState.DONE || this.state === ResolvableState.ERROR;
|
||||||
}
|
}
|
||||||
@@ -81,5 +91,9 @@ class WaitForSync extends FetchOnce {
|
|||||||
this.parsePromise((() => __awaiter(this, void 0, void 0, function* () { throw error; }))());
|
this.parsePromise((() => __awaiter(this, void 0, void 0, function* () { throw error; }))());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reset() {
|
||||||
|
super.reset();
|
||||||
|
this.state = ResolvableState.PENDING;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.WaitForSync = WaitForSync;
|
exports.WaitForSync = WaitForSync;
|
||||||
|
|||||||
2
out/logging.d.ts
vendored
2
out/logging.d.ts
vendored
@@ -1,7 +1,7 @@
|
|||||||
import * as winston from 'winston';
|
import * as winston from 'winston';
|
||||||
import { RequestHandler } from 'express';
|
import { RequestHandler } from 'express';
|
||||||
export declare const levels: readonly ["error", "warn", "info", "http", "verbose", "debug", "silly"];
|
export declare const levels: readonly ["error", "warn", "info", "http", "verbose", "debug", "silly"];
|
||||||
export declare type LogLevels = typeof levels[number] | "log";
|
export declare type LogLevels = typeof levels[number] | 'log';
|
||||||
export declare const Logger: {
|
export declare const Logger: {
|
||||||
error: (...args: unknown[]) => winston.Logger;
|
error: (...args: unknown[]) => winston.Logger;
|
||||||
http: (...args: unknown[]) => winston.Logger;
|
http: (...args: unknown[]) => winston.Logger;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const colors = require("colors");
|
|||||||
const _1 = require(".");
|
const _1 = require(".");
|
||||||
const prune = require("json-prune");
|
const prune = require("json-prune");
|
||||||
const logger = winston.createLogger({
|
const logger = winston.createLogger({
|
||||||
level: _1.DefaultConfig.isProduction ? "info" : "silly",
|
level: _1.DefaultConfig.isProduction ? 'info' : 'silly',
|
||||||
format: winston.format.json(),
|
format: winston.format.json(),
|
||||||
transports: [
|
transports: [
|
||||||
new winston.transports.Console({
|
new winston.transports.Console({
|
||||||
@@ -14,17 +14,17 @@ const logger = winston.createLogger({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
exports.levels = ["error", "warn", "info", "http", "verbose", "debug", "silly"];
|
exports.levels = ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly'];
|
||||||
const wrapper = (original) => {
|
const wrapper = (original) => {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
return original(args.map((obj) => typeof obj === "string" ? obj : prune(obj)).join(" "));
|
return original(args.map((obj) => typeof obj === 'string' ? obj : prune(obj)).join(' '));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
exports.Logger = {};
|
exports.Logger = {};
|
||||||
for (const level of exports.levels) {
|
for (const level of exports.levels) {
|
||||||
exports.Logger[level] = wrapper(logger[level]);
|
exports.Logger[level] = wrapper(logger[level]);
|
||||||
}
|
}
|
||||||
exports.Logger.log = wrapper(logger["silly"]);
|
exports.Logger.log = wrapper(logger['silly']);
|
||||||
exports.HttpLogger = (req, res, next) => {
|
exports.HttpLogger = (req, res, next) => {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const path = req.path;
|
const path = req.path;
|
||||||
@@ -37,8 +37,8 @@ exports.HttpLogger = (req, res, next) => {
|
|||||||
: statusCode >= 200 ? colors.green
|
: statusCode >= 200 ? colors.green
|
||||||
: colors.gray;
|
: colors.gray;
|
||||||
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, ' ');
|
||||||
if (!req.noHttpLogging)
|
if (!req.noHttpLogging)
|
||||||
exports.Logger.http(`${status} ${method} ${responseTime}ms ${path}`);
|
exports.Logger.http(`${status} ${method} ${responseTime}ms ${path}`);
|
||||||
end.apply(res, args);
|
end.apply(res, args);
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class PermQuery extends Query_1.Query {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
sync() {
|
sync() {
|
||||||
throw new role_acl_1.AccessControlError("Sync method is not allowed on PermissionManager!");
|
throw new role_acl_1.AccessControlError('Sync method is not allowed on PermissionManager!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.PermQuery = PermQuery;
|
exports.PermQuery = PermQuery;
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ const polyfill_analyzer_1 = require("@10xjs/polyfill-analyzer");
|
|||||||
const polyfills_1 = require("@10xjs/polyfill-analyzer/dist/polyfills");
|
const polyfills_1 = require("@10xjs/polyfill-analyzer/dist/polyfills");
|
||||||
worker_1.expose((fileToWatch) => {
|
worker_1.expose((fileToWatch) => {
|
||||||
const exclude = [
|
const exclude = [
|
||||||
"console.markTimeline",
|
'console.markTimeline',
|
||||||
"console.timeline",
|
'console.timeline',
|
||||||
"console.timelineEnd",
|
'console.timelineEnd',
|
||||||
];
|
];
|
||||||
const featureList = polyfill_analyzer_1.analyze({
|
const featureList = polyfill_analyzer_1.analyze({
|
||||||
source: fs.readFileSync(fileToWatch, 'utf-8'),
|
source: fs.readFileSync(fileToWatch, 'utf-8'),
|
||||||
|
|||||||
@@ -16,20 +16,20 @@ const threads_1 = require("threads");
|
|||||||
function getRouter(fileToWatch, opts) {
|
function getRouter(fileToWatch, opts) {
|
||||||
const features = new _1.WaitForSync();
|
const features = new _1.WaitForSync();
|
||||||
(() => __awaiter(this, void 0, void 0, function* () {
|
(() => __awaiter(this, void 0, void 0, function* () {
|
||||||
const worker = yield threads_1.spawn(new threads_1.Worker("./polyfill-worker"));
|
const worker = yield threads_1.spawn(new threads_1.Worker('./polyfill-worker'));
|
||||||
const feats = yield worker(fileToWatch);
|
const feats = yield worker(fileToWatch);
|
||||||
yield threads_1.Thread.terminate(worker);
|
yield threads_1.Thread.terminate(worker);
|
||||||
return feats;
|
return feats;
|
||||||
}))()
|
}))()
|
||||||
.then(feats => {
|
.then(feats => {
|
||||||
feats["fetch"] = {};
|
feats['fetch'] = {};
|
||||||
_1.Logger.debug("Polyfill analysed:", Object.keys(feats));
|
_1.Logger.debug('Polyfill analysed:', Object.keys(feats));
|
||||||
features.setData(feats);
|
features.setData(feats);
|
||||||
})
|
})
|
||||||
.catch(err => features.setError(err));
|
.catch(err => features.setError(err));
|
||||||
const options = Object.assign({ minify: _1.DefaultConfig.isProduction, unknown: "polyfill" }, opts);
|
const options = Object.assign({ minify: _1.DefaultConfig.isProduction, unknown: 'polyfill' }, opts);
|
||||||
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
||||||
const polyfillBundle = yield polyfillLibrary.getPolyfillString(Object.assign(Object.assign({}, options), { uaString: req.header("user-agent"), features: yield features.resolve(), stream: false }));
|
const polyfillBundle = yield polyfillLibrary.getPolyfillString(Object.assign(Object.assign({}, options), { uaString: req.header('user-agent'), features: yield features.resolve(), stream: false }));
|
||||||
res.setHeader('Content-Type', 'text/javascript');
|
res.setHeader('Content-Type', 'text/javascript');
|
||||||
res.send(polyfillBundle);
|
res.send(polyfillBundle);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ function getRouter(options) {
|
|||||||
const sessionStore = req.sessionStore;
|
const sessionStore = req.sessionStore;
|
||||||
if (sessionStore) {
|
if (sessionStore) {
|
||||||
const getSession = util_1.promisify(sessionStore.get);
|
const getSession = util_1.promisify(sessionStore.get);
|
||||||
const session = yield getSession(sessionId);
|
const session = yield getSession.call(sessionStore, sessionId);
|
||||||
if (session) {
|
if (session) {
|
||||||
sessionStore.createSession(req, session);
|
sessionStore.createSession(req, session);
|
||||||
return true; // success
|
return true; // success
|
||||||
|
|||||||
@@ -2,19 +2,30 @@ import {Request, RequestHandler} from 'express';
|
|||||||
import {DefaultConfig, Logger, Resolvable, UserInfo} from '.';
|
import {DefaultConfig, Logger, Resolvable, UserInfo} from '.';
|
||||||
import fetch from 'node-fetch';
|
import fetch from 'node-fetch';
|
||||||
|
|
||||||
|
const cachedUsers: {[token: string]: UserInfo} = {};
|
||||||
|
|
||||||
const router: RequestHandler = (req: Request, res, next) => {
|
const router: RequestHandler = (req: Request, res, next) => {
|
||||||
const resolvable = new Resolvable(async () => {
|
const resolvable = new Resolvable(async () => {
|
||||||
if (!DefaultConfig.USERINFO_HEADER) {
|
if (!DefaultConfig.USERINFO_HEADER) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const token = req.header(DefaultConfig.USERINFO_HEADER);
|
const token = req.header(DefaultConfig.USERINFO_HEADER);
|
||||||
|
if (token && cachedUsers[token]) return cachedUsers[token];
|
||||||
const url = DefaultConfig.AUTH_PROXY_USERINFO_URL;
|
const url = DefaultConfig.AUTH_PROXY_USERINFO_URL;
|
||||||
if (token === undefined || url === undefined) {
|
if (token === undefined || url === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
const expireHeader = req.header(DefaultConfig.EXPIRE_HEADER);
|
||||||
|
const expireSecDate = expireHeader && parseInt(expireHeader, 10) || undefined;
|
||||||
|
const expiresIn = expireSecDate && expireSecDate * 1000 > Date.now() && expireSecDate * 1000 - Date.now();
|
||||||
const res = await fetch(url, {headers: [[DefaultConfig.USERINFO_HEADER, token]]});
|
const res = await fetch(url, {headers: [[DefaultConfig.USERINFO_HEADER, token]]});
|
||||||
return await res.json() as UserInfo;
|
const userinfo = await res.json() as UserInfo;
|
||||||
|
if (expiresIn) {
|
||||||
|
cachedUsers[token] = userinfo;
|
||||||
|
setTimeout(() => delete cachedUsers[token], expiresIn);
|
||||||
|
}
|
||||||
|
return userinfo;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.warn(e);
|
Logger.warn(e);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function getRouter(opts: ReloaderConfig): Router {
|
|||||||
if (!DefaultConfig.isProduction) {
|
if (!DefaultConfig.isProduction) {
|
||||||
let uuid = v4();
|
let uuid = v4();
|
||||||
let updateTimeout: Timeout|undefined = undefined;
|
let updateTimeout: Timeout|undefined = undefined;
|
||||||
import("node-watch").then((watch) => {
|
import('node-watch').then((watch) => {
|
||||||
for (const frontendDir of config.frontendDirs) {
|
for (const frontendDir of config.frontendDirs) {
|
||||||
watch.default(frontendDir, {recursive: true}, () => {
|
watch.default(frontendDir, {recursive: true}, () => {
|
||||||
if (updateTimeout !== undefined) clearTimeout(updateTimeout);
|
if (updateTimeout !== undefined) clearTimeout(updateTimeout);
|
||||||
@@ -46,7 +46,7 @@ function getRouter(opts: ReloaderConfig): Router {
|
|||||||
// /auto-reload/client.js
|
// /auto-reload/client.js
|
||||||
router.get(urlJoin(config.subPath, config.jsName), (req, res) => {
|
router.get(urlJoin(config.subPath, config.jsName), (req, res) => {
|
||||||
Logger.debug(req.url, req.originalUrl, req.baseUrl);
|
Logger.debug(req.url, req.originalUrl, req.baseUrl);
|
||||||
res.setHeader('Content-Type', "application/javascript");
|
res.setHeader('Content-Type', 'application/javascript');
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
res.send(`
|
res.send(`
|
||||||
const loc = window.location;
|
const loc = window.location;
|
||||||
@@ -76,7 +76,7 @@ function getRouter(opts: ReloaderConfig): Router {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
`.replace(/\t/g, ""));
|
`.replace(/\t/g, ''));
|
||||||
});
|
});
|
||||||
|
|
||||||
// /auto-reload
|
// /auto-reload
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as env from 'env-var';
|
import * as env from 'env-var';
|
||||||
import {urlJoin} from '.';
|
import {urlJoin} from './helpers/urlJoin';
|
||||||
|
|
||||||
const NODE_ENV = env.get('NODE_ENV').default("development").asString();
|
const NODE_ENV = env.get('NODE_ENV').default('development').asString();
|
||||||
const isProduction = NODE_ENV === 'production';
|
const isProduction = NODE_ENV === 'production';
|
||||||
|
|
||||||
const envs = {
|
const envs = {
|
||||||
@@ -21,9 +21,9 @@ const envs = {
|
|||||||
SESSION_SECRET: env.get('SESSION_SECRET').asString() || undefined,
|
SESSION_SECRET: env.get('SESSION_SECRET').asString() || undefined,
|
||||||
|
|
||||||
// header where user info token is stored to request auth proxy
|
// header where user info token is stored to request auth proxy
|
||||||
USERINFO_HEADER: env.get('USERINFO_HEADER').default("X-Userinfo-Token").asString(),
|
USERINFO_HEADER: env.get('USERINFO_HEADER').default('X-Userinfo-Token').asString(),
|
||||||
// header when user info token expires (seconds after 01/01/1970)
|
// header when user info token expires (seconds after 01/01/1970)
|
||||||
EXPIRE_HEADER: env.get('EXPIRE_HEADER').default("X-Userinfo-Expire").asString(),
|
EXPIRE_HEADER: env.get('EXPIRE_HEADER').default('X-Userinfo-Expire').asString(),
|
||||||
// base url to init a logout or request user info
|
// base url to init a logout or request user info
|
||||||
AUTH_PROXY_URL: env.get('AUTH_PROXY_URL').asString() || undefined,
|
AUTH_PROXY_URL: env.get('AUTH_PROXY_URL').asString() || undefined,
|
||||||
// override AUTH_PROXY_URL to request user info
|
// override AUTH_PROXY_URL to request user info
|
||||||
@@ -40,11 +40,11 @@ export const DefaultConfig = {
|
|||||||
...envs,
|
...envs,
|
||||||
EXTERNAL_BASE_URL:
|
EXTERNAL_BASE_URL:
|
||||||
envs.EXTERNAL_BASE_URL ||
|
envs.EXTERNAL_BASE_URL ||
|
||||||
urlJoin(`http://${envs.HOSTNAME}${envs.PORT !== 80 ? `:${envs.PORT}` : ""}`, envs.BASE_PATH),
|
urlJoin(`http://${envs.HOSTNAME}${envs.PORT !== 80 ? `:${envs.PORT}` : ''}`, envs.BASE_PATH),
|
||||||
isProduction,
|
isProduction,
|
||||||
requireEnv,
|
requireEnv,
|
||||||
AUTH_PROXY_USERINFO_URL: envs.AUTH_PROXY_USERINFO_URL ||
|
AUTH_PROXY_USERINFO_URL: envs.AUTH_PROXY_USERINFO_URL ||
|
||||||
envs.AUTH_PROXY_URL && urlJoin(envs.AUTH_PROXY_URL, "userinfo"),
|
envs.AUTH_PROXY_URL && urlJoin(envs.AUTH_PROXY_URL, 'userinfo'),
|
||||||
AUTH_PROXY_INIT_LOGOUT_URL: envs.AUTH_PROXY_INIT_LOGOUT_URL ||
|
AUTH_PROXY_INIT_LOGOUT_URL: envs.AUTH_PROXY_INIT_LOGOUT_URL ||
|
||||||
envs.AUTH_PROXY_URL && urlJoin(envs.AUTH_PROXY_URL, "init-logout"),
|
envs.AUTH_PROXY_URL && urlJoin(envs.AUTH_PROXY_URL, 'init-logout'),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,8 +13,13 @@ class FetchOnce<T, U extends Array<unknown>> {
|
|||||||
|
|
||||||
constructor(protected fetchMethod?: (...args: U) => Promise<T>) { }
|
constructor(protected fetchMethod?: (...args: U) => Promise<T>) { }
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
this.data = undefined;
|
||||||
|
this.error = undefined;
|
||||||
|
this.state = ResolvableState.WAITING;
|
||||||
|
}
|
||||||
|
|
||||||
public resolve(...args: U): Promise<T> {
|
public resolve(...args: U): Promise<T> {
|
||||||
// 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:
|
||||||
@@ -35,6 +40,14 @@ class FetchOnce<T, U extends Array<unknown>> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDataOrUndefined(): T|undefined {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getErrorOrUndefined(): unknown|undefined {
|
||||||
|
return this.error;
|
||||||
|
}
|
||||||
|
|
||||||
protected isFinished(): boolean {
|
protected isFinished(): boolean {
|
||||||
return this.state === ResolvableState.DONE || this.state === ResolvableState.ERROR;
|
return this.state === ResolvableState.DONE || this.state === ResolvableState.ERROR;
|
||||||
}
|
}
|
||||||
@@ -76,4 +89,9 @@ export class WaitForSync<T> extends FetchOnce<T, never> {
|
|||||||
this.parsePromise((async () => { throw error; })());
|
this.parsePromise((async () => { throw error; })());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
super.reset();
|
||||||
|
this.state = ResolvableState.PENDING;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import prune = require('json-prune');
|
|||||||
|
|
||||||
|
|
||||||
const logger = winston.createLogger({
|
const logger = winston.createLogger({
|
||||||
level: DefaultConfig.isProduction ? "info" : "silly",
|
level: DefaultConfig.isProduction ? 'info' : 'silly',
|
||||||
format: winston.format.json(),
|
format: winston.format.json(),
|
||||||
transports: [
|
transports: [
|
||||||
new winston.transports.Console({
|
new winston.transports.Console({
|
||||||
@@ -16,11 +16,11 @@ const logger = winston.createLogger({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const levels = ["error", "warn", "info", "http", "verbose", "debug", "silly"] as const;
|
export const levels = ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly'] as const;
|
||||||
export type LogLevels = typeof levels[number]|"log";
|
export type LogLevels = typeof levels[number]|'log';
|
||||||
const wrapper = (original: LeveledLogMethod) => {
|
const wrapper = (original: LeveledLogMethod) => {
|
||||||
return (...args: unknown[]) => {
|
return (...args: unknown[]) => {
|
||||||
return original(args.map((obj) => typeof obj === "string" ? obj : prune(obj)).join(" "));
|
return original(args.map((obj) => typeof obj === 'string' ? obj : prune(obj)).join(' '));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ export const Logger = {
|
|||||||
for (const level of levels) {
|
for (const level of levels) {
|
||||||
Logger[level] = wrapper(logger[level]);
|
Logger[level] = wrapper(logger[level]);
|
||||||
}
|
}
|
||||||
Logger.log = wrapper(logger["silly"]);
|
Logger.log = wrapper(logger['silly']);
|
||||||
|
|
||||||
export const HttpLogger: RequestHandler = (req, res, next) => {
|
export const HttpLogger: RequestHandler = (req, res, next) => {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
@@ -45,8 +45,8 @@ export const HttpLogger: RequestHandler = (req, res, next) => {
|
|||||||
: statusCode >= 200 ? colors.green
|
: statusCode >= 200 ? colors.green
|
||||||
: colors.gray;
|
: colors.gray;
|
||||||
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, ' ');
|
||||||
if (!req.noHttpLogging)
|
if (!req.noHttpLogging)
|
||||||
Logger.http(`${status} ${method} ${responseTime}ms ${path}`);
|
Logger.http(`${status} ${method} ${responseTime}ms ${path}`);
|
||||||
end.apply(res, args);
|
end.apply(res, args);
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export class PermQuery extends Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public sync(): PermQuery {
|
public sync(): PermQuery {
|
||||||
throw new AccessControlError("Sync method is not allowed on PermissionManager!");
|
throw new AccessControlError('Sync method is not allowed on PermissionManager!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
// workers/add.js
|
// workers/add.js
|
||||||
import {expose} from "threads/worker";
|
import {expose} from 'threads/worker';
|
||||||
import * as fs from "fs";
|
import * as fs from 'fs';
|
||||||
import {analyze} from '@10xjs/polyfill-analyzer';
|
import {analyze} from '@10xjs/polyfill-analyzer';
|
||||||
import allPolyfills from '@10xjs/polyfill-analyzer/dist/polyfills';
|
import allPolyfills from '@10xjs/polyfill-analyzer/dist/polyfills';
|
||||||
import {PolyfillFeatureList} from 'polyfill-library';
|
import {PolyfillFeatureList} from 'polyfill-library';
|
||||||
|
|
||||||
expose((fileToWatch) => {
|
expose((fileToWatch) => {
|
||||||
const exclude = [
|
const exclude = [
|
||||||
"console.markTimeline",
|
'console.markTimeline',
|
||||||
"console.timeline",
|
'console.timeline',
|
||||||
"console.timelineEnd",
|
'console.timelineEnd',
|
||||||
];
|
];
|
||||||
const featureList = analyze({
|
const featureList = analyze({
|
||||||
source: fs.readFileSync(fileToWatch, 'utf-8'),
|
source: fs.readFileSync(fileToWatch, 'utf-8'),
|
||||||
|
|||||||
@@ -22,27 +22,27 @@ export type PolyfillOptions = {
|
|||||||
function getRouter(fileToWatch: string, opts?: Partial<PolyfillOptions>): RequestHandler {
|
function getRouter(fileToWatch: string, opts?: Partial<PolyfillOptions>): RequestHandler {
|
||||||
const features = new WaitForSync<PolyfillFeatureList>();
|
const features = new WaitForSync<PolyfillFeatureList>();
|
||||||
(async () => {
|
(async () => {
|
||||||
const worker = await spawn<WorkerFunction>(new Worker("./polyfill-worker"));
|
const worker = await spawn<WorkerFunction>(new Worker('./polyfill-worker'));
|
||||||
const feats = await worker(fileToWatch) as PolyfillFeatureList;
|
const feats = await worker(fileToWatch) as PolyfillFeatureList;
|
||||||
await Thread.terminate(worker);
|
await Thread.terminate(worker);
|
||||||
return feats;
|
return feats;
|
||||||
})()
|
})()
|
||||||
.then(feats => {
|
.then(feats => {
|
||||||
feats["fetch"] = {};
|
feats['fetch'] = {};
|
||||||
Logger.debug("Polyfill analysed:", Object.keys(feats));
|
Logger.debug('Polyfill analysed:', Object.keys(feats));
|
||||||
features.setData(feats);
|
features.setData(feats);
|
||||||
})
|
})
|
||||||
.catch(err => features.setError(err));
|
.catch(err => features.setError(err));
|
||||||
|
|
||||||
const options: Partial<PolyfillOptions> = {
|
const options: Partial<PolyfillOptions> = {
|
||||||
minify: DefaultConfig.isProduction,
|
minify: DefaultConfig.isProduction,
|
||||||
unknown: "polyfill",
|
unknown: 'polyfill',
|
||||||
...opts,
|
...opts,
|
||||||
};
|
};
|
||||||
return async (req, res) => {
|
return async (req, res) => {
|
||||||
const polyfillBundle = await polyfillLibrary.getPolyfillString({
|
const polyfillBundle = await polyfillLibrary.getPolyfillString({
|
||||||
...options,
|
...options,
|
||||||
uaString: req.header("user-agent"),
|
uaString: req.header('user-agent'),
|
||||||
features: await features.resolve(),
|
features: await features.resolve(),
|
||||||
stream: false,
|
stream: false,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ interface RedisType {
|
|||||||
class RealRedis implements RedisType {
|
class RealRedis implements RedisType {
|
||||||
private _client: redis.RedisClient|undefined;
|
private _client: redis.RedisClient|undefined;
|
||||||
private _promGet: ((key: string) => Promise<string|null>)|undefined;
|
private _promGet: ((key: string) => Promise<string|null>)|undefined;
|
||||||
private _promSet: RedisType["set"]|undefined;
|
private _promSet: RedisType['set']|undefined;
|
||||||
private _promDel: RedisType["del"]|undefined;
|
private _promDel: RedisType['del']|undefined;
|
||||||
|
|
||||||
get client(): redis.RedisClient {
|
get client(): redis.RedisClient {
|
||||||
if (this._client !== undefined) return this._client;
|
if (this._client !== undefined) return this._client;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as session from 'express-session';
|
import * as session from 'express-session';
|
||||||
import {Store} from 'express-session';
|
import {Store} from 'express-session';
|
||||||
import {DefaultConfig, Redis} from '.';
|
import {DefaultConfig, Redis} from '.';
|
||||||
import * as redisStore from "connect-redis";
|
import * as redisStore from 'connect-redis';
|
||||||
import {RequestHandler, Router} from 'express';
|
import {RequestHandler, Router} from 'express';
|
||||||
import {promisify} from "util";
|
import {promisify} from 'util';
|
||||||
|
|
||||||
|
|
||||||
let sessionStore: Store|undefined = undefined;
|
let sessionStore: Store|undefined = undefined;
|
||||||
@@ -29,7 +29,7 @@ function getRouter(options?: Partial<session.SessionOptions>): RequestHandler {
|
|||||||
const sessionStore = (req as { sessionStore?: session.Store }).sessionStore;
|
const sessionStore = (req as { sessionStore?: session.Store }).sessionStore;
|
||||||
if (sessionStore) {
|
if (sessionStore) {
|
||||||
const getSession = promisify(sessionStore.get);
|
const getSession = promisify(sessionStore.get);
|
||||||
const session = await getSession(sessionId);
|
const session = await getSession.call(sessionStore, sessionId);
|
||||||
if (session) {
|
if (session) {
|
||||||
sessionStore.createSession(req, session);
|
sessionStore.createSession(req, session);
|
||||||
return true; // success
|
return true; // success
|
||||||
|
|||||||
2
src/types/json-prune.d.ts
vendored
2
src/types/json-prune.d.ts
vendored
@@ -1,4 +1,4 @@
|
|||||||
declare module "json-prune" {
|
declare module 'json-prune' {
|
||||||
function prune(object: unknown): string;
|
function prune(object: unknown): string;
|
||||||
|
|
||||||
export = prune;
|
export = prune;
|
||||||
|
|||||||
2
src/types/polyfill.d.ts
vendored
2
src/types/polyfill.d.ts
vendored
@@ -1,4 +1,4 @@
|
|||||||
declare module "polyfill-library" {
|
declare module 'polyfill-library' {
|
||||||
import {Readable} from 'stream';
|
import {Readable} from 'stream';
|
||||||
|
|
||||||
function listAllPolyfills(): string[];
|
function listAllPolyfills(): string[];
|
||||||
|
|||||||
Reference in New Issue
Block a user