This commit is contained in:
Sebastian Seedorf
2020-11-16 12:40:47 +01:00
parent 2629d1e8e9
commit 8d35ced40f
36 changed files with 134 additions and 721 deletions

View File

@@ -10,6 +10,36 @@ npm install git+https://git.biotronik.int/scm/coe-bs-website/node-pkg-express-ut
##### 2. Copy the types ##### 2. Copy the types
Copy the folder `src/types` to some location within the source folder of your project. Copy the content below to any location within the source folder of your project and name it `extend-request.d.ts`.
It just needs to sit there with no more action required. It just needs to sit there with no more action required.
```ts
// extend-request.d.ts
declare global {
namespace Express {
interface UserInfo {
email: string,
email_verified: boolean,
family_name: string,
given_name: string,
groups: string[],
name: string,
preferred_username: string,
sub: string,
}
interface Request {
getUserInfo(): Promise<UserInfo|undefined>;
noLogging: boolean|undefined;
permissionDetails?: import('role-acl').Permission;
}
interface Response {
initLogout(): boolean;
}
}
}
export {};
```

5
out/auth-proxy.d.ts vendored
View File

@@ -1,5 +0,0 @@
/// <reference types="qs" />
import { RequestHandler } from 'express';
export declare const AuthProxy: {
router: RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs>;
};

View File

@@ -1,49 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthProxy = void 0;
const _1 = require(".");
const node_fetch_1 = require("node-fetch");
const router = (req, res, next) => {
const resolvable = new _1.Resolvable(() => __awaiter(void 0, void 0, void 0, function* () {
if (!_1.DefaultConfig.USERINFO_HEADER) {
return undefined;
}
const token = req.header(_1.DefaultConfig.USERINFO_HEADER);
const url = _1.DefaultConfig.AUTH_PROXY_USERINFO_URL ||
_1.DefaultConfig.AUTH_PROXY_URL && _1.urlJoin(_1.DefaultConfig.AUTH_PROXY_URL, "userinfo");
if (token === undefined || url === undefined) {
return undefined;
}
try {
const res = yield node_fetch_1.default(url, { headers: [[_1.DefaultConfig.USERINFO_HEADER, token]] });
return yield res.json();
}
catch (e) {
_1.Logger.warn(e);
return undefined;
}
}));
req.getUserInfo = () => resolvable.resolve();
res.initLogout = function () {
const url = _1.DefaultConfig.AUTH_PROXY_INIT_LOGOUT_URL ||
_1.DefaultConfig.AUTH_PROXY_URL && _1.urlJoin(_1.DefaultConfig.AUTH_PROXY_URL, "init-logout");
if (url === undefined) {
return false;
}
this.redirect(307, url);
return true;
};
next();
};
exports.AuthProxy = {
router,
};

View File

@@ -1,3 +0,0 @@
export declare const AutoReloader: {
router: import("express-serve-static-core").Router;
};

View File

@@ -1,61 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AutoReloader = void 0;
const express_1 = require("express");
const _1 = require(".");
const uuid_1 = require("uuid");
const router = express_1.Router();
if (!_1.DefaultConfig.isProduction) {
let uuid = uuid_1.v4();
let updateTimeout = undefined;
Promise.resolve().then(() => require("node-watch")).then((watch) => {
watch.default('public', { recursive: true }, () => {
if (updateTimeout !== undefined)
clearTimeout(updateTimeout);
updateTimeout = setTimeout(() => {
uuid = uuid_1.v4();
}, 200);
});
}).catch((err) => { _1.Logger.error(err); });
router.get("/auto-reload/client.js", (req, res) => {
_1.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+'${_1.urlJoin(req.baseUrl, "/auto-reload")}';
// const parse = async res => (await res.json()).uuid;
const parse = function(res) {
return res.json()
.then(function(json) {return json.uuid;}) }
let hash = undefined;
let hadError = false;
setInterval(function() {
try {
fetch(url)
.then(function(res) { return parse(res) })
.then(function(data) {
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, ""));
});
router.get("/auto-reload", (req, res) => {
req.noLogging = true;
res.json({ uuid });
});
}
exports.AutoReloader = {
router,
};

17
out/config.d.ts vendored
View File

@@ -1,17 +0,0 @@
declare function requireEnv(name: string, onlyInProduction?: boolean): void;
export declare const DefaultConfig: {
EXTERNAL_BASE_URL: string;
isProduction: boolean;
requireEnv: typeof requireEnv;
NODE_ENV: string;
PORT: number;
HOSTNAME: string;
BASE_PATH: string;
REDIS_URL: string | undefined;
SESSION_SECRET: string | undefined;
USERINFO_HEADER: string | undefined;
AUTH_PROXY_URL: string | undefined;
AUTH_PROXY_USERINFO_URL: string | undefined;
AUTH_PROXY_INIT_LOGOUT_URL: string | undefined;
};
export {};

View File

@@ -1,36 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultConfig = void 0;
const env = require("env-var");
const _1 = require(".");
const NODE_ENV = env.get('NODE_ENV').default("development").asString();
const isProduction = NODE_ENV === 'production';
const envs = {
NODE_ENV,
// port of the server
PORT: env.get('PORT').default('3000').asPortNumber(),
// hostname of the server ('0.0.0.0' listens on all network interfaces)
HOSTNAME: env.get('HOSTNAME').default('0.0.0.0').asString(),
// base path
BASE_PATH: env.get('BASE_PATH').default('/').asString(),
// external base url
EXTERNAL_BASE_URL: env.get('EXTERNAL_BASE_URL').asString(),
// url of redis session store (required in production using InMemory)
REDIS_URL: env.get('REDIS_URL').asString() || undefined,
// cookie secret for the session id (required in production using Session)
SESSION_SECRET: env.get('SESSION_SECRET').asString() || undefined,
// header where user info token is stored to request auth proxy
USERINFO_HEADER: env.get('USERINFO_HEADER').asString() || undefined,
// base url to init a logout or request user info
AUTH_PROXY_URL: env.get('AUTH_PROXY_URL').asString() || undefined,
// override base url to request user info
AUTH_PROXY_USERINFO_URL: env.get('AUTH_PROXY_USERINFO_URL').asString() || undefined,
// override base url to init a logout
AUTH_PROXY_INIT_LOGOUT_URL: env.get('AUTH_PROXY_INIT_LOGOUT_URL').asString() || undefined,
};
function requireEnv(name, onlyInProduction = false) {
env.get(name).required(!onlyInProduction || isProduction).asString();
}
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,
requireEnv });

View File

@@ -1,27 +0,0 @@
declare enum ResolvableState {
WAITING = 0,
PENDING = 1,
ERROR = 2,
DONE = 3
}
declare class FetchOnce<T, U extends Array<unknown>> {
protected fetchMethod?: ((...args: U) => Promise<T>) | undefined;
protected data: T | undefined;
protected error: unknown | undefined;
protected state: ResolvableState;
protected pendings: [(res: Promise<T> | T) => void, (reason: unknown) => void][];
constructor(fetchMethod?: ((...args: U) => Promise<T>) | undefined);
resolve(...args: U): Promise<T>;
protected isFinished(): boolean;
protected parsePromise(promise: Promise<T>): void;
}
export declare class Resolvable<T, U extends Array<unknown>> extends FetchOnce<T, U> {
constructor(fetchMethod: (...args: U) => Promise<T>);
}
export declare class WaitForSync<T> extends FetchOnce<T, never> {
protected state: ResolvableState;
constructor();
setData(data: T): void;
setError(error: unknown): void;
}
export {};

View File

@@ -1,85 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WaitForSync = exports.Resolvable = void 0;
var ResolvableState;
(function (ResolvableState) {
ResolvableState[ResolvableState["WAITING"] = 0] = "WAITING";
ResolvableState[ResolvableState["PENDING"] = 1] = "PENDING";
ResolvableState[ResolvableState["ERROR"] = 2] = "ERROR";
ResolvableState[ResolvableState["DONE"] = 3] = "DONE";
})(ResolvableState || (ResolvableState = {}));
class FetchOnce {
constructor(fetchMethod) {
this.fetchMethod = fetchMethod;
this.state = ResolvableState.WAITING;
this.pendings = [];
}
resolve(...args) {
// 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(...args));
break;
case ResolvableState.PENDING:
this.pendings.push([resolve, reject]);
break;
case ResolvableState.DONE:
resolve(this.data);
break;
case ResolvableState.ERROR:
reject(this.error);
break;
}
});
}
isFinished() {
return this.state === ResolvableState.DONE || this.state === ResolvableState.ERROR;
}
parsePromise(promise) {
promise.then((data) => {
this.data = data;
this.state = ResolvableState.DONE;
this.pendings.forEach(pending => pending[0](data));
}).catch(err => {
this.error = err;
this.state = ResolvableState.ERROR;
this.pendings.forEach(pending => pending[1](err));
});
}
}
class Resolvable extends FetchOnce {
constructor(fetchMethod) {
super(fetchMethod);
}
}
exports.Resolvable = Resolvable;
class WaitForSync extends FetchOnce {
constructor() {
super(undefined);
this.state = ResolvableState.PENDING;
}
setData(data) {
if (!this.isFinished()) {
this.parsePromise((() => __awaiter(this, void 0, void 0, function* () { return data; }))());
}
}
setError(error) {
if (!this.isFinished()) {
this.parsePromise((() => __awaiter(this, void 0, void 0, function* () { throw error; }))());
}
}
}
exports.WaitForSync = WaitForSync;

View File

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

View File

@@ -1,5 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.urlJoin = void 0;
const properUrlJoin = require("proper-url-join");
exports.urlJoin = properUrlJoin;

View File

@@ -1,10 +0,0 @@
export declare type UserInfo = {
email: string;
email_verified: boolean;
family_name: string;
given_name: string;
groups: string[];
name: string;
preferred_username: string;
sub: string;
};

View File

@@ -1,2 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

11
out/index.d.ts vendored
View File

@@ -1,11 +0,0 @@
export { DefaultConfig } from './config';
export { Redis } from './redis';
export { Logger, HttpLogger } from './logging';
export { AuthProxy } from './auth-proxy';
export { Resolvable, WaitForSync } from './helpers/resolvable';
export { urlJoin } from './helpers/urlJoin';
export { AutoReloader } from './auto-reload';
export { Polyfill } from './polyfill';
export { Session } from './session';
export { Permissions } from './permissions';
export { UserInfo } from './helpers/userinfo';

View File

@@ -1,25 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Permissions = exports.Session = exports.Polyfill = exports.AutoReloader = exports.urlJoin = exports.WaitForSync = exports.Resolvable = exports.AuthProxy = exports.HttpLogger = exports.Logger = exports.Redis = exports.DefaultConfig = void 0;
var config_1 = require("./config");
Object.defineProperty(exports, "DefaultConfig", { enumerable: true, get: function () { return config_1.DefaultConfig; } });
var redis_1 = require("./redis");
Object.defineProperty(exports, "Redis", { enumerable: true, get: function () { return redis_1.Redis; } });
var logging_1 = require("./logging");
Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logging_1.Logger; } });
Object.defineProperty(exports, "HttpLogger", { enumerable: true, get: function () { return logging_1.HttpLogger; } });
var auth_proxy_1 = require("./auth-proxy");
Object.defineProperty(exports, "AuthProxy", { enumerable: true, get: function () { return auth_proxy_1.AuthProxy; } });
var resolvable_1 = require("./helpers/resolvable");
Object.defineProperty(exports, "Resolvable", { enumerable: true, get: function () { return resolvable_1.Resolvable; } });
Object.defineProperty(exports, "WaitForSync", { enumerable: true, get: function () { return resolvable_1.WaitForSync; } });
var urlJoin_1 = require("./helpers/urlJoin");
Object.defineProperty(exports, "urlJoin", { enumerable: true, get: function () { return urlJoin_1.urlJoin; } });
var auto_reload_1 = require("./auto-reload");
Object.defineProperty(exports, "AutoReloader", { enumerable: true, get: function () { return auto_reload_1.AutoReloader; } });
var polyfill_1 = require("./polyfill");
Object.defineProperty(exports, "Polyfill", { enumerable: true, get: function () { return polyfill_1.Polyfill; } });
var session_1 = require("./session");
Object.defineProperty(exports, "Session", { enumerable: true, get: function () { return session_1.Session; } });
var permissions_1 = require("./permissions");
Object.defineProperty(exports, "Permissions", { enumerable: true, get: function () { return permissions_1.Permissions; } });

13
out/logging.d.ts vendored
View File

@@ -1,13 +0,0 @@
import * as winston from 'winston';
import { RequestHandler } from 'express';
export declare const Logger: {
error: (...args: unknown[]) => winston.Logger;
http: (...args: unknown[]) => winston.Logger;
info: (...args: unknown[]) => winston.Logger;
silly: (...args: unknown[]) => winston.Logger;
warn: (...args: unknown[]) => winston.Logger;
verbose: (...args: unknown[]) => winston.Logger;
debug: (...args: unknown[]) => winston.Logger;
log: (...args: unknown[]) => winston.Logger;
};
export declare const HttpLogger: RequestHandler;

View File

@@ -1,47 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpLogger = exports.Logger = void 0;
const winston = require("winston");
const colors = require("colors");
const _1 = require(".");
const prune = require("json-prune");
const logger = winston.createLogger({
level: _1.DefaultConfig.isProduction ? "info" : "silly",
format: winston.format.json(),
transports: [
new winston.transports.Console({
format: winston.format.simple(),
}),
],
});
const levels = ["error", "warn", "info", "http", "verbose", "debug", "silly"];
const wrapper = (original) => {
return (...args) => {
return original(args.map((obj) => typeof obj === "string" ? obj : prune(obj)).join(" "));
};
};
exports.Logger = {};
for (const level of levels) {
exports.Logger[level] = wrapper(logger[level]);
}
exports.Logger.log = wrapper(logger["silly"]);
exports.HttpLogger = (req, res, next) => {
const start = Date.now();
const path = req.path;
const end = res.end;
res.end = function (...args) {
const statusCode = res.statusCode;
const colorFunction = statusCode >= 500 ? colors.red
: statusCode >= 400 ? colors.yellow
: statusCode >= 300 ? colors.cyan
: statusCode >= 200 ? colors.green
: colors.gray;
const status = colorFunction(res.statusCode.toString(10));
const method = req.method.toUpperCase().padEnd(6, " ");
const responseTime = (Date.now() - start).toString(10).padStart(3, " ");
if (!req.noLogging)
exports.Logger.http(`${status} ${method} ${responseTime}ms ${path}`);
end.apply(res, args);
};
next();
};

24
out/permissions.d.ts vendored
View File

@@ -1,24 +0,0 @@
import { AccessControl, IQueryInfo, Permission } from 'role-acl';
import { Query } from 'role-acl/lib/src/core/Query';
import { Request, RequestHandler } from 'express';
declare class PermissionManager extends AccessControl {
can(roleOrRequest: Request | string | string[] | IQueryInfo): PermQuery;
getRouter(resource: string, opts: Partial<RermRouterOpts>): RequestHandler;
}
export declare type RermRouterOpts = {
context: unknown;
action: string;
skipConditions: boolean;
};
export declare class PermQuery extends Query {
protected resolveRequest: Request | undefined;
constructor(grants: unknown, roleOrRequest: Request | string | string[] | IQueryInfo);
on(resource: string, skipConditions?: boolean): Promise<Permission>;
context(context: unknown): PermQuery;
skipConditions(value: boolean): PermQuery;
with(context: unknown): PermQuery;
execute(action: string): PermQuery;
sync(): PermQuery;
}
export declare const Permissions: PermissionManager;
export {};

View File

@@ -1,92 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Permissions = exports.PermQuery = void 0;
const role_acl_1 = require("role-acl");
const Query_1 = require("role-acl/lib/src/core/Query");
// see https://www.npmjs.com/package/role-acl
class PermissionManager extends role_acl_1.AccessControl {
can(roleOrRequest) {
return new PermQuery(this.getGrants(), roleOrRequest);
}
getRouter(resource, opts) {
return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
let query = this.can(req);
if (opts.context)
query = query.context(opts.context);
if (opts.action)
query = query.execute(opts.action);
if (opts.skipConditions)
query = query.skipConditions(opts.skipConditions);
const permission = yield query.on(resource);
if (permission.granted) {
req.permissionDetails = permission;
next();
}
else {
res.sendStatus(403);
}
});
}
}
class PermQuery extends Query_1.Query {
constructor(grants, roleOrRequest) {
function isRequest(obj) {
// eslint-disable-next-line no-prototype-builtins
return typeof obj === 'object' && obj && obj.hasOwnProperty('path') || false;
}
if (isRequest(roleOrRequest)) {
super(grants, []);
this.resolveRequest = roleOrRequest;
}
else {
super(grants, roleOrRequest);
}
}
on(resource, skipConditions) {
const _super = Object.create(null, {
on: { get: () => super.on }
});
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (this.resolveRequest) {
const userInfo = yield this.resolveRequest.getUserInfo();
this.role((_a = userInfo === null || userInfo === void 0 ? void 0 : userInfo.groups) !== null && _a !== void 0 ? _a : []);
}
if (typeof this._.role === 'object' && this._.role.includes('noaccess') ||
typeof this._.role === 'string' && this._.role === 'noaccess') {
this.role([]);
}
return _super.on.call(this, resource, skipConditions);
});
}
context(context) {
super.context(context);
return this;
}
skipConditions(value) {
super.skipConditions(value);
return this;
}
with(context) {
super.with(context);
return this;
}
execute(action) {
super.execute(action);
return this;
}
sync() {
throw new role_acl_1.AccessControlError("Sync method is not allowed on PermissionManager!");
}
}
exports.PermQuery = PermQuery;
exports.Permissions = new PermissionManager();

View File

@@ -1 +0,0 @@
export {};

View File

@@ -1,25 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// workers/add.js
const worker_1 = require("threads/worker");
const fs = require("fs");
const polyfill_analyzer_1 = require("@10xjs/polyfill-analyzer");
const polyfills_1 = require("@10xjs/polyfill-analyzer/dist/polyfills");
worker_1.expose(() => {
const exclude = [
"console.markTimeline",
"console.timeline",
"console.timelineEnd",
];
const featureList = polyfill_analyzer_1.analyze({
source: fs.readFileSync('./public/js/bundle.js', 'utf-8'),
include: polyfills_1.default.filter(x => !exclude.includes(x)),
// Not all features listed by polyfillLibrary.listAllPolyfills()` can be detected.
unsupportedPolyfill: 'ignore',
});
const feats = {};
for (const feature of featureList) {
feats[feature] = {};
}
return feats;
});

7
out/polyfill.d.ts vendored
View File

@@ -1,7 +0,0 @@
import { RequestHandler } from 'express';
import { PolyfillOptions } from 'polyfill-library';
declare function getRouter(opts?: Partial<PolyfillOptions>): RequestHandler;
export declare const Polyfill: {
getRouter: typeof getRouter;
};
export {};

View File

@@ -1,39 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Polyfill = void 0;
const polyfillLibrary = require("polyfill-library");
const _1 = require(".");
const threads_1 = require("threads");
const features = new _1.WaitForSync();
(() => __awaiter(void 0, void 0, void 0, function* () {
const worker = yield threads_1.spawn(new threads_1.Worker("./polyfill-worker"));
const feats = yield worker();
yield threads_1.Thread.terminate(worker);
return feats;
}))()
.then(feats => {
feats["fetch"] = {};
_1.Logger.debug("Polyfill analysed:", Object.keys(feats));
features.setData(feats);
})
.catch(err => features.setError(err));
function getRouter(opts) {
const options = Object.assign({ minify: _1.DefaultConfig.isProduction, unknown: "polyfill" }, opts);
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 }));
res.setHeader('Content-Type', 'text/javascript');
res.send(polyfillBundle);
});
}
exports.Polyfill = {
getRouter: getRouter,
};

9
out/redis.d.ts vendored
View File

@@ -1,9 +0,0 @@
import * as redis from 'redis';
interface RedisType {
client: redis.RedisClient | undefined;
get(key: string): Promise<string | undefined>;
set(key: string, value: string): Promise<void>;
del(...keys: string[]): Promise<number>;
}
export declare const Redis: RedisType;
export {};

View File

@@ -1,76 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Redis = void 0;
const redis = require("redis");
const _1 = require(".");
const util_1 = require("util");
class RealRedis {
get client() {
if (this._client !== undefined)
return this._client;
_1.DefaultConfig.requireEnv('REDIS_URL');
this._client = redis.createClient({ url: _1.DefaultConfig.REDIS_URL });
return this._client;
}
get(key) {
return __awaiter(this, void 0, void 0, function* () {
if (!this._promGet)
this._promGet = util_1.promisify(this.client.get);
const value = yield this._promGet(key);
// eslint-disable-next-line no-null/no-null
return value === null ? undefined : value;
});
}
set(key, value) {
return __awaiter(this, void 0, void 0, function* () {
if (!this._promSet)
this._promSet = util_1.promisify(this.client.set);
yield this._promSet(key, value);
});
}
del(...keys) {
return __awaiter(this, void 0, void 0, function* () {
if (!this._promDel)
this._promDel = util_1.promisify(this.client.del);
return yield this._promDel(...keys);
});
}
}
class MockRedis {
constructor() {
this.inMemory = {};
}
get client() {
return undefined;
}
get(key) {
return __awaiter(this, void 0, void 0, function* () {
return this.inMemory[key] || undefined;
});
}
set(key, value) {
return __awaiter(this, void 0, void 0, function* () {
this.inMemory[key] = value;
});
}
del(...keys) {
return __awaiter(this, void 0, void 0, function* () {
for (const key of keys) {
delete this.inMemory[key];
}
return keys.length;
});
}
}
exports.Redis = _1.DefaultConfig.REDIS_URL || _1.DefaultConfig.isProduction
? new RealRedis()
: new MockRedis();

7
out/session.d.ts vendored
View File

@@ -1,7 +0,0 @@
import * as session from 'express-session';
import { RequestHandler } from 'express';
declare function getRouter(options?: Partial<session.SessionOptions>): RequestHandler;
export declare const Session: {
getRouter: typeof getRouter;
};
export {};

View File

@@ -1,18 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Session = void 0;
const session = require("express-session");
const _1 = require(".");
const redisStore = require("connect-redis");
let sessionStore = undefined;
function getRouter(options) {
_1.DefaultConfig.requireEnv('SESSION_SECRET', true);
if (_1.Redis.client && sessionStore !== undefined) {
const RedisStore = redisStore(session);
sessionStore = new RedisStore({ client: _1.Redis.client });
}
return session(Object.assign({ store: sessionStore, secret: _1.DefaultConfig.SESSION_SECRET || 'keyboard cat', resave: false, saveUninitialized: true, cookie: { secure: false } }, options));
}
exports.Session = {
getRouter,
};

60
package-lock.json generated
View File

@@ -2369,6 +2369,66 @@
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true "dev": true
}, },
"pkg-express-utils-types": {
"version": "file:../node-pkg-express-utils-types",
"dependencies": {
"@types/node": {
"version": "14.14.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz",
"integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"jsonpath-plus": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-0.18.1.tgz",
"integrity": "sha512-4yQiuV641HROc4z9YGvnsr8yAdmzbu8JjdAen8WfiXsXewKvTG4ie2bSygF2maek9PcbtROmS6aLQs31BD+oNQ=="
},
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
},
"lodash.flattendeep": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
"integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI="
},
"matcher": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-1.1.1.tgz",
"integrity": "sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg==",
"requires": {
"escape-string-regexp": "^1.0.4"
}
},
"notation": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/notation/-/notation-1.3.6.tgz",
"integrity": "sha512-DIuJmrP/Gg1DcXKaApsqcjsJD6jEccqKSfmU3BUx/f1GHsMiTJh70cERwYc64tOmTRTARCeMwkqNNzjh3AHhiw=="
},
"role-acl": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/role-acl/-/role-acl-4.5.4.tgz",
"integrity": "sha512-T7baCc5BCFzLaxUrtAEIDcfK8HM/WTt6l2SLiMts6zLADSp7wOrrU3op0HVKhwjKi01d0Q6T4NeZ0jdLii9WuQ==",
"requires": {
"jsonpath-plus": "^0.18.0",
"lodash.clonedeep": "^4.5.0",
"lodash.flattendeep": "^4.4.0",
"matcher": "^1.0.0",
"notation": "^1.3.5"
}
},
"typescript": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ=="
}
}
},
"polyfill-library": { "polyfill-library": {
"version": "3.97.0", "version": "3.97.0",
"resolved": "https://registry.npmjs.org/polyfill-library/-/polyfill-library-3.97.0.tgz", "resolved": "https://registry.npmjs.org/polyfill-library/-/polyfill-library-3.97.0.tgz",

View File

@@ -25,15 +25,16 @@
"express-session": "^1.17.1", "express-session": "^1.17.1",
"json-prune": "^1.1.0", "json-prune": "^1.1.0",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"pkg-express-utils-types": "file:../node-pkg-express-utils-types",
"polyfill-library": "^3.97.0", "polyfill-library": "^3.97.0",
"proper-url-join": "^2.1.1", "proper-url-join": "^2.1.1",
"redis": "^3.0.2", "redis": "^3.0.2",
"role-acl": "^4.5.4", "role-acl": "^4.5.4",
"run-script-os": "^1.1.3",
"threads": "^1.6.3", "threads": "^1.6.3",
"tiny-worker": "^2.3.0", "tiny-worker": "^2.3.0",
"uuid": "^8.3.1", "uuid": "^8.3.1",
"winston": "^3.3.3", "winston": "^3.3.3"
"run-script-os": "^1.1.3"
}, },
"devDependencies": { "devDependencies": {
"@types/connect-redis": "0.0.15", "@types/connect-redis": "0.0.15",

View File

@@ -5,7 +5,7 @@ export {AuthProxy} from './auth-proxy';
export {Resolvable, WaitForSync} from './helpers/resolvable'; export {Resolvable, WaitForSync} from './helpers/resolvable';
export {urlJoin} from './helpers/urlJoin'; export {urlJoin} from './helpers/urlJoin';
export {AutoReloader} from './auto-reload'; export {AutoReloader} from './auto-reload';
export {Polyfill} from './polyfill'; export {Polyfill, PolyfillOptions} from './polyfill';
export {Session} from './session'; export {Session} from './session';
export {Permissions} from './permissions'; export {Permissions} from './permissions';

View File

@@ -3,7 +3,7 @@ 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(() => { expose(() => {
const exclude = [ const exclude = [

View File

@@ -1,10 +1,19 @@
import {RequestHandler} from 'express'; import {RequestHandler} from 'express';
import * as polyfillLibrary from 'polyfill-library'; import * as polyfillLibrary from 'polyfill-library';
import {PolyfillFeatureList, PolyfillOptions} from 'polyfill-library'; import {PolyfillFeatureList} from 'polyfill-library';
import {DefaultConfig, Logger, WaitForSync} from '.'; import {DefaultConfig, Logger, WaitForSync} from '.';
import {spawn, Thread, Worker} from 'threads'; import {spawn, Thread, Worker} from 'threads';
import {WorkerFunction} from 'threads/dist/types/worker'; import {WorkerFunction} from 'threads/dist/types/worker';
export type PolyfillOptions = {
minify: boolean,
unknown: 'polyfill'|'ignore',
features: PolyfillFeatureList,
excludes: string[],
uaString: string,
rum: boolean,
}
const features = new WaitForSync<PolyfillFeatureList>(); const features = new WaitForSync<PolyfillFeatureList>();
(async () => { (async () => {

View File

@@ -1,9 +0,0 @@
declare namespace Express {
interface Request {
getUserInfo(): Promise<import('../helpers/userinfo').UserInfo|undefined>;
}
interface Response {
initLogout(): boolean;
}
}

28
src/types/extend-request.d.ts vendored Normal file
View File

@@ -0,0 +1,28 @@
// extend-request.d.ts
declare global {
namespace Express {
interface UserInfo {
email: string,
email_verified: boolean,
family_name: string,
given_name: string,
groups: string[],
name: string,
preferred_username: string,
sub: string,
}
interface Request {
getUserInfo(): Promise<UserInfo|undefined>;
noLogging: boolean|undefined;
permissionDetails?: import('role-acl').Permission;
}
interface Response {
initLogout(): boolean;
}
}
}
export {};

View File

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

View File

@@ -1,5 +0,0 @@
declare namespace Express {
interface Request {
permissionDetails?: import('role-acl').Permission;
}
}