ts-comply
Version:
TypeTools is a Typescript library for providing extensible tooling runtime validations and type helpers.
153 lines (140 loc) • 6.25 kB
text/typescript
/*
* Copyright 2014-2021 Firestack, all rights reserved.
*/
import { passthru, passthruError, ReturnCodeFamily } from '../../../src/common/globals.ix';
import { HttpServerShim, HTTP, HttpBaseLib, HttpOp, ReqProcessor, ParamDef, HttpOpType } from '../http.shim';
import * as fs from 'fs';
import { HttpCode } from '../http.models';
import { envVar } from '../../../src/common/env/env.profile';
import { as, required } from '../../../src/type-transform';
import { parseProperties } from '../../../src/common/util/convenience/properties.parse';
import { pathNavigate } from '../../util/node-util';
import { SecureChannelTypes } from '../../../src/common/security/security.common';
const scopeName = `destor;pid=${process.pid}`;
enum DestorCodeEnum {
DESTOR_RESOLVE_CONFIG_APP_PROFILE_NOT_FOUND,
DESTOR_AUTH_SERVER_APP_PROFILE_NOT_FOUND,
DESTOR_SECRET_NOT_FOUND,
}
export const DestorCode = ReturnCodeFamily('DestorCode', DestorCodeEnum);
let localData;
let destorData;
function getConf() {
localData = parseProperties(fs.readFileSync(process.env.DESTOR_SERVER_CONFIG_FILE, 'utf8'));
destorData = localData.destor;
return localData;
}
export class DestorServer extends HttpServerShim {
dataResolution: 'local-file' | 'remote' = envVar('DESTOR_SERVER_DATA_RESOLUTION', 'local-file');
constructor() {
super({
name: 'destor',
type: HttpBaseLib.EXPRESS,
scopeName,
security: {
accessor: { required: true, baseToken: process.env.DESTOR_SERVER_BASE_TOKEN },
secureChannel: { enabled: true, required: true, encryption: SecureChannelTypes.ECC_4Q, signingKey: process.env.DESTOR_SERVER_SIGNING_KEY },
},
startOptions: { port: process.env.DESTOR_SERVER_PORT ? parseInt(process.env.DESTOR_SERVER_PORT, 10) : 17070 },
skipConfigSecretResolution: true,
});
this.apiVersion = 'v1';
this.apiPath = this.configGlobal.destor.basePath;
this.addDefaultProcessor(ReqProcessor.BASIC);
getConf();
if (this.dataResolution === 'local-file') {
setInterval(() => { try { getConf() } catch (e) { console.error(e); } }, 30000);
}
}
.GET(`/`, { rootMount: true })
async rootProofOfAuthenticity(op: HttpOp) {
const accessorResult = this.checkAccessor(op); if (accessorResult.bad) { return passthruError(accessorResult); }
const stamp = await this.stamp();
return op.res.returnJson({ ...stamp });
}
getResolvConfig_iface = {
path: `/resolve-config/:env`,
description: ``,
params: {
env: { required, type: `string`, default: as<string>(), description: `` },
targets: { required, type: `string`, default: as<string>(), description: `` },
},
returns: { type: `{[key: string]: {value: any, error?: string}}`,
default: as<{[key: string]: {value: any, error?: string}}>() },
};
.GET(`/resolve-config/:env`, { rootMount: true })
async getResolveConfig(op: HttpOpType<typeof this.getResolvConfig_iface>) {
const targets = op.params.targets.split('|').map(target => {
target = target.trim();
while (target.startsWith('<') && target.endsWith('>')) { target = target.slice(1, -1); }
return target;
});
if (this.dataResolution === 'local-file') {
if (!localData?.profiles?.[op.params.env]) {
return op.raise(DestorCode.error('DESTOR_RESOLVE_CONFIG_APP_PROFILE_NOT_FOUND',
`application profile env '${op.params.env}' not found on destor config`));
}
const result: {[key: string]: {value: any, error?: string}} = {};
for (const targetStr of targets) {
const lit = targetStr.split('?:');
const targetPathStr = lit[0]?.trim();
const defaultValueDef = lit[1]?.trimStart();
let defaultValue: any;
if (defaultValueDef) {
if (defaultValueDef.indexOf(':') >= 0) {
const dfLit = defaultValueDef.split(':');
const dfType = dfLit[0];
const dfValueStr = dfLit.slice(1).join(':');
if (dfType === 'number') {
defaultValue = +dfValueStr;
} else if (dfType === 'boolean') {
defaultValue = dfValueStr.toLowerCase() === 'true' ? true : false;
} else if (dfType === 'string') {
defaultValue = dfValueStr;
}
} else {
defaultValue = defaultValueDef;
}
}
const targetPath = targetPathStr.split('.');
if (targetPath[0] !== 'config' && targetPath[0] !== 'secret') {
result[targetStr] = { value: null, error: `resolvable path must start with 'config' or 'secret', got '${targetPath[0]}'` };
continue;
}
let resolved = pathNavigate(targetPath, localData?.profiles?.[op.params.env]);
if (resolved === null) { resolved = pathNavigate(targetPath, localData?.global); }
const resObj: {value: any, error?: string} = { value: resolved };
if (resolved === null) {
if (defaultValueDef) {
resObj.value = defaultValue;
} else {
resObj.error = `not_found`;
}
}
result[targetStr] = resObj;
}
op.returnJson(result);
}
}
getAuthServer_iface = {
path: `/auth-servers/:env`,
description: ``,
params: {
env: { required, type: `string`, default: as<string>(), description: `` },
},
returns: { type: `{[name: string]: { url: string, type: string, publicKey: string }}`,
default: as<{[name: string]: { url: string, type: string, publicKey: string }}>() },
};
.GET(`/auth-servers/:env`, { rootMount: true })
async getAuthServer(op: HttpOpType<typeof this.getAuthServer_iface>) {
if (this.dataResolution === 'local-file') {
if (!localData?.profiles?.[op.params.env]) {
return op.raise(DestorCode.error('DESTOR_AUTH_SERVER_APP_PROFILE_NOT_FOUND',
`application profile env '${op.params.env}' not found on destor config`));
}
let data = pathNavigate(['config', 'authServers'], localData?.profiles?.[op.params.env]);
if (data === null) { data = pathNavigate(['config', 'authServers'], localData?.global); }
return op.returnJson(data);
}
}
}