Move packages/apps back
This commit is contained in:
parent
6eaaf8e9be
commit
5535d6b575
348 changed files with 0 additions and 0 deletions
31
packages/metamigo-common/config/app-meta.ts
Normal file
31
packages/metamigo-common/config/app-meta.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { ConvictSchema } from "./types";
|
||||
|
||||
export interface IAppMetaConfig {
|
||||
name: string;
|
||||
version: string;
|
||||
figletFont: string;
|
||||
}
|
||||
|
||||
export const AppMetaConfig: ConvictSchema<IAppMetaConfig> = {
|
||||
version: {
|
||||
doc: "The current application version",
|
||||
format: String,
|
||||
env: "npm_package_version",
|
||||
default: null,
|
||||
skipGenerate: true,
|
||||
},
|
||||
name: {
|
||||
doc: "Application name",
|
||||
format: String,
|
||||
env: "npm_package_name",
|
||||
default: null,
|
||||
skipGenerate: true,
|
||||
},
|
||||
figletFont: {
|
||||
doc: "The figlet font name used to print the site name on boot",
|
||||
format: String,
|
||||
env: "FIGLET_FONT",
|
||||
default: "Sub-Zero",
|
||||
skipGenerate: true,
|
||||
},
|
||||
};
|
||||
23
packages/metamigo-common/config/auth.ts
Normal file
23
packages/metamigo-common/config/auth.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { ConvictSchema } from "./types";
|
||||
|
||||
export interface ISessionConfig {
|
||||
sessionMaxAgeSeconds: number;
|
||||
sessionUpdateAgeSeconds: number;
|
||||
}
|
||||
|
||||
export const SessionConfig: ConvictSchema<ISessionConfig> = {
|
||||
sessionMaxAgeSeconds: {
|
||||
doc: "How long in seconds until an idle session expires and is no longer valid.",
|
||||
format: "positiveInt",
|
||||
default: 30 * 24 * 60 * 60, // 30 days
|
||||
env: "SESSION_MAX_AGE_SECONDS",
|
||||
},
|
||||
sessionUpdateAgeSeconds: {
|
||||
doc: `Throttle how frequently in seconds to write to database to extend a session.
|
||||
Use it to limit write operations. Set to 0 to always update the database.
|
||||
Note: This option is ignored if using JSON Web Tokens`,
|
||||
format: "positiveInt",
|
||||
default: 24 * 60 * 60, // 24 hours
|
||||
env: "SESSION_UPDATE_AGE_SECONDS",
|
||||
},
|
||||
};
|
||||
32
packages/metamigo-common/config/cors.ts
Normal file
32
packages/metamigo-common/config/cors.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { ConvictSchema } from "./types";
|
||||
|
||||
export interface ICorsConfig {
|
||||
allowedMethods: Array<string>;
|
||||
allowedOrigins: Array<string>;
|
||||
allowedHeaders: Array<string>;
|
||||
}
|
||||
|
||||
export const CorsConfig: ConvictSchema<ICorsConfig> = {
|
||||
allowedMethods: {
|
||||
doc: "The allowed CORS methods",
|
||||
format: "Array",
|
||||
env: "CORS_ALLOWED_METHODS",
|
||||
default: ["GET", "PUT", "POST", "PATCH", "DELETE", "HEAD", "OPTIONS"],
|
||||
},
|
||||
allowedOrigins: {
|
||||
doc: "The allowed origins",
|
||||
format: "Array",
|
||||
env: "CORS_ALLOWED_ORIGINS",
|
||||
default: [],
|
||||
},
|
||||
allowedHeaders: {
|
||||
doc: "The allowed headers",
|
||||
format: "Array",
|
||||
env: "CORS_ALLOWED_HEADERS",
|
||||
default: [
|
||||
"content-type",
|
||||
"authorization",
|
||||
"cf-access-authenticated-user-email",
|
||||
],
|
||||
},
|
||||
};
|
||||
58
packages/metamigo-common/config/formats.ts
Normal file
58
packages/metamigo-common/config/formats.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import * as Joi from "joi";
|
||||
import type { Format } from "convict";
|
||||
|
||||
const coerceString = (v: any): string => v.toString();
|
||||
const validator = (s: any) => (v: any) => Joi.assert(v, s);
|
||||
|
||||
const url = Joi.string().uri({
|
||||
scheme: ["http", "https"],
|
||||
});
|
||||
const ip = Joi.string().ip({ version: ["ipv4", "ipv6"], cidr: "optional" });
|
||||
|
||||
/**
|
||||
* Additional configuration value formats for convict.
|
||||
*
|
||||
* You can use these to achieve richer validation for your configuration.
|
||||
*/
|
||||
export const MetamigoConvictFormats: { [index: string]: Format } = {
|
||||
positiveInt: {
|
||||
name: "positveInt",
|
||||
coerce: (n: string): number => Number.parseInt(n, 10),
|
||||
validate: validator(Joi.number().positive().integer()),
|
||||
},
|
||||
port: {
|
||||
name: "port",
|
||||
coerce: (n: string): number => Number.parseInt(n, 10),
|
||||
validate: validator(Joi.number().port()),
|
||||
},
|
||||
ipaddress: {
|
||||
name: "ipaddress",
|
||||
coerce: coerceString,
|
||||
validate: validator(ip),
|
||||
},
|
||||
url: {
|
||||
name: "url",
|
||||
coerce: coerceString,
|
||||
validate: validator(url),
|
||||
},
|
||||
uri: {
|
||||
name: "uri",
|
||||
coerce: coerceString,
|
||||
validate: validator(Joi.string().uri()),
|
||||
},
|
||||
optionalUri: {
|
||||
name: "uri",
|
||||
coerce: coerceString,
|
||||
validate: validator(Joi.string().uri().allow("")),
|
||||
},
|
||||
email: {
|
||||
name: "email",
|
||||
coerce: coerceString,
|
||||
validate: validator(Joi.string().email()),
|
||||
},
|
||||
uuid: {
|
||||
name: "uuid",
|
||||
coerce: coerceString,
|
||||
validate: validator(Joi.string().guid()),
|
||||
},
|
||||
};
|
||||
44
packages/metamigo-common/config/generate.ts
Normal file
44
packages/metamigo-common/config/generate.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import convict from "convict";
|
||||
|
||||
const visitLeaf = (acc: any, key: any, leaf: any) => {
|
||||
if (leaf.skipGenerate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (leaf.default === undefined) {
|
||||
acc[key] = undefined;
|
||||
} else {
|
||||
acc[key] = leaf.default;
|
||||
}
|
||||
};
|
||||
|
||||
const visitNode = (acc: any, node: any, key = "") => {
|
||||
if (node._cvtProperties) {
|
||||
const keys = Object.keys(node._cvtProperties);
|
||||
let subacc: any;
|
||||
if (key === "") {
|
||||
subacc = acc;
|
||||
} else {
|
||||
subacc = {};
|
||||
acc[key] = subacc;
|
||||
}
|
||||
|
||||
keys.forEach((key) => {
|
||||
visitNode(subacc, node._cvtProperties[key], key);
|
||||
});
|
||||
// In the case that the entire sub-tree specified skipGenerate, remove the empty node
|
||||
if (Object.keys(subacc).length === 0) {
|
||||
delete acc[key];
|
||||
}
|
||||
} else {
|
||||
visitLeaf(acc, key, node);
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const generateConfig = (conf: convict.Config<any>): unknown => {
|
||||
const schema = conf.getSchema();
|
||||
const generated = {};
|
||||
visitNode(generated, schema);
|
||||
return JSON.stringify(generated, undefined, 1);
|
||||
};
|
||||
142
packages/metamigo-common/config/index.ts
Normal file
142
packages/metamigo-common/config/index.ts
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
import process from "process";
|
||||
import convict, { SchemaObj } from "convict";
|
||||
import { IServerConfig, ServerConfig } from "./server";
|
||||
import { IMetricsConfig, MetricsConfig } from "./metrics-server";
|
||||
import { IAppMetaConfig, AppMetaConfig } from "./app-meta";
|
||||
import { ICorsConfig, CorsConfig } from "./cors";
|
||||
import { ILoggingConfig, LoggingConfig } from "./logging";
|
||||
import { ExtendedConvict } from "./types";
|
||||
import { MetamigoConvictFormats } from "./formats";
|
||||
|
||||
type IEnvConfig = "production" | "development" | "test";
|
||||
|
||||
const EnvConfig: SchemaObj<IEnvConfig> = {
|
||||
doc: "The application environment",
|
||||
format: ["production", "development", "test"],
|
||||
default: "development",
|
||||
env: "NODE_ENV",
|
||||
};
|
||||
|
||||
export const configBaseSchema = {
|
||||
env: EnvConfig,
|
||||
server: ServerConfig,
|
||||
meta: AppMetaConfig,
|
||||
cors: CorsConfig,
|
||||
metrics: MetricsConfig,
|
||||
logging: LoggingConfig,
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* The metamigo base configuration object. Use this for easy typed access to your
|
||||
* config.
|
||||
*
|
||||
*/
|
||||
interface IMetamigoConfig {
|
||||
env: IEnvConfig;
|
||||
server: IServerConfig;
|
||||
meta: IAppMetaConfig;
|
||||
cors: ICorsConfig;
|
||||
metrics: IMetricsConfig;
|
||||
logging: ILoggingConfig;
|
||||
isProd?: boolean;
|
||||
isTest?: boolean;
|
||||
isDev?: boolean;
|
||||
frontend: any;
|
||||
nextAuth: any;
|
||||
}
|
||||
export type IMetamigoConvict = ExtendedConvict<IMetamigoConfig>;
|
||||
|
||||
export type {
|
||||
IServerConfig,
|
||||
IMetricsConfig,
|
||||
IAppMetaConfig,
|
||||
ICorsConfig,
|
||||
ILoggingConfig,
|
||||
IMetamigoConfig,
|
||||
};
|
||||
|
||||
export * from "./formats";
|
||||
export * from "./generate";
|
||||
export * from "./print";
|
||||
export * from "./types";
|
||||
|
||||
/**
|
||||
* Loads your applications configuration from environment variables and configuration files (see METAMIGO_CONFIG).
|
||||
*
|
||||
* @param schema your schema definition
|
||||
* @param override an optional object with config value that will override defaults but not config files and env vars (see [convict precedence docs](https://github.com/mozilla/node-convict/tree/master/packages/convict#precedence-order ))
|
||||
* @returns the raw convict config object
|
||||
*/
|
||||
export const loadConfigurationRaw = async <T extends IMetamigoConfig>(
|
||||
schema: convict.Schema<T>,
|
||||
override?: Partial<T>
|
||||
): Promise<ExtendedConvict<T>> => {
|
||||
convict.addFormats(MetamigoConvictFormats);
|
||||
const config: ExtendedConvict<T> = convict(schema);
|
||||
|
||||
const env = config.get("env");
|
||||
|
||||
config.isProd = env === "production";
|
||||
config.isTest = env === "test";
|
||||
config.isDev = env === "development";
|
||||
|
||||
try {
|
||||
if (process.env.METAMIGO_CONFIG) {
|
||||
config.loadFile(process.env.METAMIGO_CONFIG);
|
||||
}
|
||||
} catch (error) {
|
||||
const msg = `
|
||||
|
||||
|
||||
🚫 Your application's configuration is invalid JSON. 🚫
|
||||
|
||||
${error}
|
||||
|
||||
`;
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
if (override) {
|
||||
config.load(override);
|
||||
}
|
||||
|
||||
try {
|
||||
config.validate({ allowed: "strict" });
|
||||
} catch (error: any) {
|
||||
const msg = `
|
||||
|
||||
|
||||
🚫 Your application's configuration is invalid. 🚫
|
||||
|
||||
${error.message}
|
||||
|
||||
`;
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
// set our helpers
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const configDirty = config as any;
|
||||
|
||||
configDirty.set("isProd", config.isProd);
|
||||
configDirty.set("isTest", config.isTest);
|
||||
configDirty.set("isDev", config.isDev);
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads your applications configuration from environment variables and configuration files (see METAMIGO_CONFIG).
|
||||
*
|
||||
* @param schema your schema definition
|
||||
* @param override an optional object with config value that will override defaults but not config files and env vars (see [convict precedence docs](https://github.com/mozilla/node-convict/tree/master/packages/convict#precedence-order ))
|
||||
* @returns a vanilla javascript object with the config loaded values
|
||||
*/
|
||||
export const loadConfiguration = async <T extends IMetamigoConfig>(
|
||||
schema: convict.Schema<T>,
|
||||
override?: Partial<T>
|
||||
): Promise<T> => {
|
||||
const c = await loadConfigurationRaw(schema, override);
|
||||
return c.getProperties();
|
||||
};
|
||||
90
packages/metamigo-common/config/logging.ts
Normal file
90
packages/metamigo-common/config/logging.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import { ConvictSchema } from "./types";
|
||||
|
||||
export interface ILoggingConfig {
|
||||
level: string;
|
||||
sql: boolean;
|
||||
redact: string[];
|
||||
ignorePaths: string[];
|
||||
ignoreTags: string[];
|
||||
requestIdHeader: string;
|
||||
logRequestStart: boolean;
|
||||
logRequestComplete: boolean;
|
||||
logRequestPayload: boolean;
|
||||
logRequestQueryParams: boolean;
|
||||
prettyPrint: boolean | "auto";
|
||||
}
|
||||
|
||||
export const LoggingConfig: ConvictSchema<ILoggingConfig> = {
|
||||
level: {
|
||||
doc: "The logging level",
|
||||
format: ["trace", "debug", "info", "warn", "error"],
|
||||
default: "info",
|
||||
env: "LOG_LEVEL",
|
||||
},
|
||||
sql: {
|
||||
doc: "Whether to log sql statements",
|
||||
format: "Boolean",
|
||||
default: false,
|
||||
env: "LOG_SQL",
|
||||
},
|
||||
redact: {
|
||||
doc: "Pino redaction array. These are always redacted. see https://getpino.io/#/docs/redaction",
|
||||
format: "Array",
|
||||
default: [
|
||||
"req.remoteAddress",
|
||||
"req.headers.authorization",
|
||||
`req.headers["cf-access-jwt-assertion"]`,
|
||||
`req.headers["cf-access-authenticated-user-email"]`,
|
||||
`req.headers["cf-connecting-ip"]`,
|
||||
`req.headers["cf-ipcountry"]`,
|
||||
`req.headers["x-forwarded-for"]`,
|
||||
"req.headers.cookie",
|
||||
],
|
||||
},
|
||||
ignorePaths: {
|
||||
doc: "Ignore http paths (exact) when logging requests",
|
||||
format: "Array",
|
||||
default: ["/graphql"],
|
||||
},
|
||||
ignoreTags: {
|
||||
doc: "Ignore routes tagged with these tags when logging requests",
|
||||
format: "Array",
|
||||
default: ["status", "swagger", "nolog"],
|
||||
},
|
||||
requestIdHeader: {
|
||||
doc: "The header where the request id lives",
|
||||
format: String,
|
||||
default: "x-request-id",
|
||||
env: "REQUEST_ID_HEADER",
|
||||
},
|
||||
logRequestStart: {
|
||||
doc: "Whether hapi-pino should add a log.info() at the beginning of Hapi requests for the given Request.",
|
||||
format: "Boolean",
|
||||
default: false,
|
||||
env: "LOG_REQUEST_START",
|
||||
},
|
||||
logRequestComplete: {
|
||||
doc: "Whether hapi-pino should add a log.info() at the completion of Hapi requests for the given Request.",
|
||||
format: "Boolean",
|
||||
default: true,
|
||||
env: "LOG_REQUEST_COMPLETE",
|
||||
},
|
||||
logRequestPayload: {
|
||||
doc: "When enabled, add the request payload as payload to the response event log.",
|
||||
format: "Boolean",
|
||||
default: false,
|
||||
env: "LOG_REQUEST_PAYLOAD",
|
||||
},
|
||||
logRequestQueryParams: {
|
||||
doc: "When enabled, add the request query as queryParams to the response event log.",
|
||||
format: "Boolean",
|
||||
default: false,
|
||||
env: "LOG_REQUEST_QUERY_PARAMS",
|
||||
},
|
||||
prettyPrint: {
|
||||
doc: "Pretty print the logs",
|
||||
format: ["auto", true, false],
|
||||
default: "auto",
|
||||
env: "LOG_PRETTY_PRINT",
|
||||
},
|
||||
};
|
||||
22
packages/metamigo-common/config/metrics-server.ts
Normal file
22
packages/metamigo-common/config/metrics-server.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { ConvictSchema } from "./types";
|
||||
|
||||
export interface IMetricsConfig {
|
||||
address: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export const MetricsConfig: ConvictSchema<IMetricsConfig> = {
|
||||
address: {
|
||||
doc: "The ip address to bind the prometheus metrics to",
|
||||
format: "ipaddress",
|
||||
default: "127.0.0.1",
|
||||
env: "METRICS_ADDRESS",
|
||||
},
|
||||
port: {
|
||||
doc: "The port to bind the prometheus metrics to",
|
||||
format: "port",
|
||||
default: 3002,
|
||||
env: "METRICS_PORT",
|
||||
arg: "port",
|
||||
},
|
||||
};
|
||||
41
packages/metamigo-common/config/print.ts
Normal file
41
packages/metamigo-common/config/print.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import chalk from "chalk";
|
||||
import convict from "convict";
|
||||
|
||||
const visitLeaf = (path: any, key: any, leaf: any) => {
|
||||
if (leaf.skipGenerate) {
|
||||
return;
|
||||
}
|
||||
|
||||
let name = `${path}.${key}`;
|
||||
if (path.length === 0) name = key;
|
||||
console.log(chalk.green(name));
|
||||
console.log(leaf.doc);
|
||||
if (leaf.default === undefined) {
|
||||
console.log(chalk.red("\t required"));
|
||||
} else {
|
||||
console.log(`\tdefault: ${JSON.stringify(leaf.default)}`);
|
||||
}
|
||||
|
||||
console.log(`\tformat: ${leaf.format}`);
|
||||
console.log(`\tenv: ${leaf.env}`);
|
||||
};
|
||||
|
||||
const visitNode = (path: any, node: any, key = "") => {
|
||||
if (node._cvtProperties) {
|
||||
const keys = Object.keys(node._cvtProperties);
|
||||
const subpath = key === "" ? path : `${key}`;
|
||||
|
||||
keys.forEach((key) => {
|
||||
visitNode(subpath, node._cvtProperties[key], key);
|
||||
});
|
||||
console.log();
|
||||
} else {
|
||||
visitLeaf(path, key, node);
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const printConfigOptions = (conf: convict.Config<any>): void => {
|
||||
const schema = conf.getSchema();
|
||||
visitNode("", schema);
|
||||
};
|
||||
21
packages/metamigo-common/config/server.ts
Normal file
21
packages/metamigo-common/config/server.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { ConvictSchema } from "./types";
|
||||
|
||||
export interface IServerConfig {
|
||||
address: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export const ServerConfig: ConvictSchema<IServerConfig> = {
|
||||
address: {
|
||||
doc: "The IP address to bind the server to",
|
||||
format: "ipaddress",
|
||||
default: "0.0.0.0",
|
||||
env: "SERVER_ADDRESS",
|
||||
},
|
||||
port: {
|
||||
doc: "The port to bind the server to",
|
||||
format: "port",
|
||||
default: 3001,
|
||||
env: "SERVER_PORT",
|
||||
},
|
||||
};
|
||||
26
packages/metamigo-common/config/types.ts
Normal file
26
packages/metamigo-common/config/types.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import convict from "convict";
|
||||
|
||||
/*
|
||||
interface SSMObj {
|
||||
path: string;
|
||||
}
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
interface ConvictSchemaObj<T = any> extends convict.SchemaObj<T> {
|
||||
// ssm?: SSMObj;
|
||||
/**
|
||||
* The config item will be ignored for purposes of config file generation
|
||||
*/
|
||||
skipGenerate?: boolean;
|
||||
}
|
||||
|
||||
export type ConvictSchema<T> = {
|
||||
[P in keyof T]: convict.Schema<T[P]> | ConvictSchemaObj<T[P]>;
|
||||
};
|
||||
|
||||
export interface ExtendedConvict<T> extends convict.Config<T> {
|
||||
isProd?: boolean;
|
||||
isTest?: boolean;
|
||||
isDev?: boolean;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue