No require any ENV_VAR
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
"lint-fix": "eslint . --ext .ts --fix",
|
||||
"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 --project ./src --onSuccess \"node --enable-source-maps --use-openssl-ca --unhandled-rejections=strict ./out/index\"",
|
||||
"debug-server": "tsc-watch --project . --onSuccess \"node --enable-source-maps --use-openssl-ca --unhandled-rejections=strict ./out/index\"",
|
||||
"debug": "concurrently npm:debug-*",
|
||||
"build": "tsc",
|
||||
"production": "node --use-openssl-ca --unhandled-rejections=strict ./out/index",
|
||||
|
||||
10
src/app.ts
10
src/app.ts
@@ -5,7 +5,7 @@ import * as path from 'path';
|
||||
import * as sassMiddleware from 'node-sass-middleware';
|
||||
import * as compression from 'compression';
|
||||
import indexRouter from './routes';
|
||||
import {HttpLogger, Config, AuthProxy, AutoReloader, Polyfill, Session} from './utils';
|
||||
import {AuthProxy, AutoReloader, DefaultConfig, HttpLogger, Polyfill} from './utils';
|
||||
|
||||
export const app = express();
|
||||
|
||||
@@ -13,7 +13,7 @@ export const app = express();
|
||||
app.set('views', path.join(__dirname, '../views'));
|
||||
app.set('view engine', 'pug');
|
||||
app.use((req, res, next) => {
|
||||
res.locals.Config = Config;
|
||||
res.locals.Config = DefaultConfig;
|
||||
next();
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ router.use(AuthProxy.router);
|
||||
router.use(AutoReloader.router);
|
||||
|
||||
// session
|
||||
router.use(Session.getRouter());
|
||||
//router.use(Session.getRouter());
|
||||
|
||||
// static config
|
||||
router.use("/js/polyfill.js", Polyfill.router);
|
||||
@@ -46,7 +46,7 @@ router.use(sassMiddleware({
|
||||
router.use(express.static(path.join(__dirname, '../public')));
|
||||
|
||||
router.use(indexRouter);
|
||||
app.use(Config.BASE_PATH, router);
|
||||
app.use(DefaultConfig.BASE_PATH, router);
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
app.use((req, res, next) => {
|
||||
@@ -60,7 +60,7 @@ app.use((err: Error&{status?: number}, req: Request, res: Response, next: NextFu
|
||||
}
|
||||
// set locals, only providing error in development
|
||||
res.locals.message = err.message;
|
||||
res.locals.error = !Config.isProduction ? err : {};
|
||||
res.locals.error = !DefaultConfig.isProduction ? err : {};
|
||||
|
||||
// render the error page
|
||||
res.status(err.status || 500);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable no-process-exit,no-console */
|
||||
import * as http from "http";
|
||||
import urlJoin from 'proper-url-join';
|
||||
import {Config} from './utils';
|
||||
import {DefaultConfig} from './utils';
|
||||
|
||||
const options = {
|
||||
host: Config.HOSTNAME,
|
||||
port: Config.PORT,
|
||||
path: urlJoin(Config.BASE_PATH, '/health'),
|
||||
host: DefaultConfig.HOSTNAME,
|
||||
port: DefaultConfig.PORT,
|
||||
path: urlJoin(DefaultConfig.BASE_PATH, '/health'),
|
||||
timeout: 2000,
|
||||
};
|
||||
|
||||
|
||||
10
src/index.ts
10
src/index.ts
@@ -4,12 +4,12 @@
|
||||
import * as http from 'http';
|
||||
import {app} from './app';
|
||||
import {HttpError} from 'http-errors';
|
||||
import {Config, Logger} from './utils';
|
||||
import {DefaultConfig, Logger} from './utils';
|
||||
|
||||
app.set('port', Config.PORT);
|
||||
app.set('port', DefaultConfig.PORT);
|
||||
const server = http.createServer(app);
|
||||
|
||||
app.listen(Config.PORT);
|
||||
app.listen(DefaultConfig.PORT);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
|
||||
@@ -20,11 +20,11 @@ function onError(error: HttpError): void {
|
||||
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
Logger.error(Config.PORT + ' requires elevated privileges');
|
||||
Logger.error(DefaultConfig.PORT + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
Logger.error(Config.PORT + ' is already in use');
|
||||
Logger.error(DefaultConfig.PORT + ' is already in use');
|
||||
process.exit(2);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as express from 'express';
|
||||
import userRouter from './api/user';
|
||||
import healthRouter from './healthcheck';
|
||||
import {Config} from '../utils';
|
||||
|
||||
const router = express.Router();
|
||||
export default router;
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import {Request, RequestHandler} from 'express';
|
||||
import {Config, Logger} from '.';
|
||||
import {DefaultConfig, Logger} from '.';
|
||||
import {Resolvable} from './helpers/resolvable';
|
||||
import fetch from 'node-fetch';
|
||||
import {urlJoin} from './helpers/urlJoin';
|
||||
|
||||
const router: RequestHandler = (req: Request, res, next) => {
|
||||
const resolvable = new Resolvable(async () => {
|
||||
if (!Config.USERINFO_HEADER) {
|
||||
if (!DefaultConfig.USERINFO_HEADER) {
|
||||
return undefined;
|
||||
}
|
||||
const token = req.header(Config.USERINFO_HEADER);
|
||||
const url = Config.AUTH_PROXY_USERINFO_URL || Config.AUTH_PROXY_URL && urlJoin(Config.AUTH_PROXY_URL, "userinfo");
|
||||
const token = req.header(DefaultConfig.USERINFO_HEADER);
|
||||
const url = DefaultConfig.AUTH_PROXY_USERINFO_URL ||
|
||||
DefaultConfig.AUTH_PROXY_URL && urlJoin(DefaultConfig.AUTH_PROXY_URL, "userinfo");
|
||||
if (token === undefined || url === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const res = await fetch(url, {headers: [[Config.USERINFO_HEADER, token]]});
|
||||
const res = await fetch(url, {headers: [[DefaultConfig.USERINFO_HEADER, token]]});
|
||||
return await res.json() as UserInfo;
|
||||
} catch (e) {
|
||||
Logger.warn(e);
|
||||
@@ -25,7 +26,8 @@ const router: RequestHandler = (req: Request, res, next) => {
|
||||
|
||||
req.getUserInfo = () => resolvable.resolve();
|
||||
res.initLogout = function() {
|
||||
const url = Config.AUTH_PROXY_INIT_LOGOUT_URL || Config.AUTH_PROXY_URL && urlJoin(Config.AUTH_PROXY_URL, "init-logout");
|
||||
const url = DefaultConfig.AUTH_PROXY_INIT_LOGOUT_URL ||
|
||||
DefaultConfig.AUTH_PROXY_URL && urlJoin(DefaultConfig.AUTH_PROXY_URL, "init-logout");
|
||||
if (url === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {Router} from 'express';
|
||||
import {Config, Logger} from '.';
|
||||
import {DefaultConfig, Logger} from '.';
|
||||
import {urlJoin} from './helpers/urlJoin';
|
||||
import {v4} from 'uuid';
|
||||
import Timeout = NodeJS.Timeout;
|
||||
|
||||
const router = Router();
|
||||
if (!Config.isProduction) {
|
||||
if (!DefaultConfig.isProduction) {
|
||||
let uuid = v4();
|
||||
let updateTimeout: Timeout|undefined = undefined;
|
||||
import("node-watch").then((watch) => {
|
||||
|
||||
@@ -32,10 +32,10 @@ const envs = {
|
||||
};
|
||||
|
||||
function requireEnv(name: string, onlyInProduction = false): void {
|
||||
env.get(name).required(!onlyInProduction || isProduction);
|
||||
env.get(name).required(!onlyInProduction || isProduction).asString();
|
||||
}
|
||||
|
||||
export const Config = {
|
||||
export const DefaultConfig = {
|
||||
...envs,
|
||||
EXTERNAL_BASE_URL:
|
||||
envs.EXTERNAL_BASE_URL ||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export {Config} from './config';
|
||||
export {DefaultConfig} from './config';
|
||||
export {Redis} from './redis';
|
||||
export {Logger, HttpLogger} from './logging';
|
||||
export {AuthProxy} from './auth-proxy';
|
||||
|
||||
@@ -3,11 +3,11 @@ import {LeveledLogMethod} from 'winston';
|
||||
import prune = require('json-prune');
|
||||
import {RequestHandler} from 'express';
|
||||
import * as colors from 'colors';
|
||||
import {Config} from '.';
|
||||
import {DefaultConfig} from '.';
|
||||
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: Config.isProduction ? "info" : "silly",
|
||||
level: DefaultConfig.isProduction ? "info" : "silly",
|
||||
format: winston.format.json(),
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {AccessControl, AccessControlError, IQueryInfo, Permission, Query} from 'role-acl';
|
||||
import {AccessControl, AccessControlError, IQueryInfo, Permission} from 'role-acl';
|
||||
import {Query} from 'role-acl/lib/src/core/Query';
|
||||
import {Request, RequestHandler} from 'express';
|
||||
|
||||
// see https://www.npmjs.com/package/role-acl
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {RequestHandler} from 'express';
|
||||
import * as polyfillLibrary from 'polyfill-library';
|
||||
import {Config, Logger} from '.';
|
||||
import {DefaultConfig, Logger} from '.';
|
||||
import {PolyfillFeatureList} from 'polyfill-library';
|
||||
import {WaitForSync} from './helpers/resolvable';
|
||||
import {spawn, Thread, Worker} from 'threads';
|
||||
@@ -26,7 +26,7 @@ const router: RequestHandler = async (req, res) => {
|
||||
const polyfillBundle = await polyfillLibrary.getPolyfillString({
|
||||
uaString: req.header("user-agent"),
|
||||
features: await features.resolve(),
|
||||
minify: Config.isProduction,
|
||||
minify: DefaultConfig.isProduction,
|
||||
unknown: "polyfill",
|
||||
stream: false,
|
||||
});
|
||||
|
||||
@@ -1,38 +1,70 @@
|
||||
import * as redis from 'redis';
|
||||
import {Config} from '.';
|
||||
import {DefaultConfig} from '.';
|
||||
import {promisify} from 'util';
|
||||
|
||||
Config.requireEnv('REDIS_URL', true);
|
||||
|
||||
const redisClient = Config.REDIS_URL && redis.createClient({url: Config.REDIS_URL});
|
||||
const inMemory: {[key: string]: string} = {};
|
||||
|
||||
type RedisType = {
|
||||
client: redis.RedisClient | undefined,
|
||||
get: (key: string) => Promise<string | undefined>,
|
||||
set: (key: string, value: string) => Promise<void>,
|
||||
del: (...keys: string[]) => Promise<void>
|
||||
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 const Redis: RedisType = redisClient ? {
|
||||
client: redisClient,
|
||||
get: (async (key: string) => {
|
||||
const value = await promisify(redisClient.get)(key);
|
||||
class RealRedis implements RedisType {
|
||||
private _client: redis.RedisClient|undefined;
|
||||
private _promGet: ((key: string) => Promise<string|null>)|undefined;
|
||||
private _promSet: RedisType["set"]|undefined;
|
||||
private _promDel: RedisType["del"]|undefined;
|
||||
|
||||
get client(): redis.RedisClient {
|
||||
if (this._client !== undefined) return this._client;
|
||||
DefaultConfig.requireEnv('REDIS_URL');
|
||||
this._client = redis.createClient({url: DefaultConfig.REDIS_URL});
|
||||
return this._client;
|
||||
}
|
||||
|
||||
async get(key: string): Promise<string | undefined> {
|
||||
if (!this._promGet) this._promGet = promisify(this.client.get);
|
||||
const value = await this._promGet(key);
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
return value === null ? undefined : value;
|
||||
}),
|
||||
set: promisify(redisClient.set),
|
||||
del: promisify(redisClient.del) as unknown as (...keys: string[]) => Promise<void>,
|
||||
} : {
|
||||
client: undefined,
|
||||
get: (async (key: string) => inMemory[key] || undefined),
|
||||
set: (async (key: string, value: string) => {
|
||||
inMemory[key] = value;
|
||||
}),
|
||||
del: (async (...keys: string[]) => {
|
||||
for (const key of keys) {
|
||||
delete inMemory[key];
|
||||
}
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
async set(key: string, value: string): Promise<void> {
|
||||
if (!this._promSet) this._promSet = promisify(this.client.set);
|
||||
await this._promSet(key, value);
|
||||
}
|
||||
|
||||
async del(...keys: string[]): Promise<number> {
|
||||
if (!this._promDel) this._promDel = promisify(this.client.del);
|
||||
return await this._promDel(...keys);
|
||||
}
|
||||
}
|
||||
|
||||
class MockRedis implements RedisType {
|
||||
private inMemory: {[key: string]: string} = {};
|
||||
|
||||
get client(): undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async get(key: string): Promise<string | undefined> {
|
||||
return this.inMemory[key] || undefined;
|
||||
}
|
||||
|
||||
async set(key: string, value: string): Promise<void> {
|
||||
this.inMemory[key] = value;
|
||||
}
|
||||
|
||||
async del(...keys: string[]): Promise<number> {
|
||||
for (const key of keys) {
|
||||
delete this.inMemory[key];
|
||||
}
|
||||
return keys.length;
|
||||
}
|
||||
}
|
||||
|
||||
export const Redis: RedisType =
|
||||
DefaultConfig.REDIS_URL || DefaultConfig.isProduction
|
||||
? new RealRedis()
|
||||
: new MockRedis();
|
||||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import {Store} from 'express-session';
|
||||
import {Redis, Config} from '.';
|
||||
import {Redis, DefaultConfig} from '.';
|
||||
import * as redisStore from "connect-redis";
|
||||
import * as session from 'express-session';
|
||||
import {RequestHandler} from 'express';
|
||||
|
||||
Config.requireEnv('SESSION_SECRET', true);
|
||||
|
||||
|
||||
let sessionStore: Store|undefined = undefined;
|
||||
|
||||
function getRouter(options?: Partial<session.SessionOptions>): RequestHandler {
|
||||
DefaultConfig.requireEnv('SESSION_SECRET', true);
|
||||
if (Redis.client && sessionStore !== undefined) {
|
||||
const RedisStore = redisStore(session);
|
||||
sessionStore = new RedisStore({client: Redis.client});
|
||||
}
|
||||
return session({
|
||||
store: sessionStore,
|
||||
secret: Config.SESSION_SECRET || 'keyboard cat',
|
||||
secret: DefaultConfig.SESSION_SECRET || 'keyboard cat',
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
cookie: {secure: false},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- const baseUrl = Config.EXTERNAL_BASE_URL;
|
||||
- const baseUrl = DefaultConfig.EXTERNAL_BASE_URL;
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
@@ -13,5 +13,5 @@ html
|
||||
require(['src/index'], function (index) {
|
||||
index.setConfig({EXTERNAL_BASE_URL: '#{baseUrl}'});
|
||||
});
|
||||
if !Config.isProduction
|
||||
if !DefaultConfig.isProduction
|
||||
script(type='text/javascript', src=baseUrl+'/auto-reload/client.js')
|
||||
|
||||
Reference in New Issue
Block a user