Use AsyncLocalStorage to provide requestId context
This commit is contained in:
parent
6f9ecaa9c4
commit
8fab93e709
@ -35,6 +35,7 @@
|
|||||||
"@types/jest": "^26.0.4",
|
"@types/jest": "^26.0.4",
|
||||||
"@types/mjml": "^4.0.4",
|
"@types/mjml": "^4.0.4",
|
||||||
"@types/mysql": "^2.15.10",
|
"@types/mysql": "^2.15.10",
|
||||||
|
"@types/nanoid": "^2.1.0",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.5.7",
|
||||||
"@types/nodemailer": "^6.4.0",
|
"@types/nodemailer": "^6.4.0",
|
||||||
"@types/nunjucks": "^3.1.3",
|
"@types/nunjucks": "^3.1.3",
|
||||||
@ -69,6 +70,7 @@
|
|||||||
"geoip-lite": "^1.4.2",
|
"geoip-lite": "^1.4.2",
|
||||||
"mjml": "^4.6.2",
|
"mjml": "^4.6.2",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
|
"nanoid": "^3.1.20",
|
||||||
"nodemailer": "^6.4.6",
|
"nodemailer": "^6.4.6",
|
||||||
"nunjucks": "^3.2.1",
|
"nunjucks": "^3.2.1",
|
||||||
"on-finished": "^2.3.0",
|
"on-finished": "^2.3.0",
|
||||||
|
@ -16,7 +16,7 @@ import * as path from "path";
|
|||||||
import CacheProvider from "./CacheProvider";
|
import CacheProvider from "./CacheProvider";
|
||||||
import RedisComponent from "./components/RedisComponent";
|
import RedisComponent from "./components/RedisComponent";
|
||||||
import Extendable from "./Extendable";
|
import Extendable from "./Extendable";
|
||||||
import {log} from "./Logger";
|
import {logger, loggingContextMiddleware} from "./Logger";
|
||||||
import TemplateError = lib.TemplateError;
|
import TemplateError = lib.TemplateError;
|
||||||
|
|
||||||
export default abstract class Application implements Extendable<ApplicationComponent | WebSocketListener<Application>> {
|
export default abstract class Application implements Extendable<ApplicationComponent | WebSocketListener<Application>> {
|
||||||
@ -46,7 +46,7 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
const path = thing.path();
|
const path = thing.path();
|
||||||
this.webSocketListeners[path] = thing;
|
this.webSocketListeners[path] = thing;
|
||||||
thing.init(this);
|
thing.init(this);
|
||||||
log.info(`Added websocket listener on ${path}`);
|
logger.info(`Added websocket listener on ${path}`);
|
||||||
} else {
|
} else {
|
||||||
thing.setApp(this);
|
thing.setApp(this);
|
||||||
this.components.push(thing);
|
this.components.push(thing);
|
||||||
@ -58,7 +58,7 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
log.info(`${config.get('app.name')} v${this.version} - hi`);
|
logger.info(`${config.get('app.name')} v${this.version} - hi`);
|
||||||
process.once('SIGINT', () => {
|
process.once('SIGINT', () => {
|
||||||
this.stop().catch(console.error);
|
this.stop().catch(console.error);
|
||||||
});
|
});
|
||||||
@ -82,6 +82,11 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
|
|
||||||
// Init express
|
// Init express
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
// Logging context
|
||||||
|
app.use(loggingContextMiddleware);
|
||||||
|
|
||||||
|
// Routers
|
||||||
const initRouter = express.Router();
|
const initRouter = express.Router();
|
||||||
const handleRouter = express.Router();
|
const handleRouter = express.Router();
|
||||||
app.use(initRouter);
|
app.use(initRouter);
|
||||||
@ -191,7 +196,7 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
for (let i = 2; i < args.length; i++) {
|
for (let i = 2; i < args.length; i++) {
|
||||||
switch (args[i]) {
|
switch (args[i]) {
|
||||||
case '--verbose':
|
case '--verbose':
|
||||||
log.setSettings({minLevel: "trace"});
|
logger.setSettings({minLevel: "trace"});
|
||||||
break;
|
break;
|
||||||
case '--full-http-requests':
|
case '--full-http-requests':
|
||||||
LogRequestsComponent.logFullHttpRequests();
|
LogRequestsComponent.logFullHttpRequests();
|
||||||
@ -200,7 +205,7 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
await MysqlConnectionManager.migrationCommand(args.slice(i + 1));
|
await MysqlConnectionManager.migrationCommand(args.slice(i + 1));
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
log.warn('Unrecognized argument', args[i]);
|
logger.warn('Unrecognized argument', args[i]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,13 +233,13 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async stop(): Promise<void> {
|
public async stop(): Promise<void> {
|
||||||
log.info('Stopping application...');
|
logger.info('Stopping application...');
|
||||||
|
|
||||||
for (const component of this.components) {
|
for (const component of this.components) {
|
||||||
await component.stop?.();
|
await component.stop?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`${this.constructor.name} v${this.version} - bye`);
|
logger.info(`${this.constructor.name} v${this.version} - bye`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private routes(initRouter: Router, handleRouter: Router) {
|
private routes(initRouter: Router, handleRouter: Router) {
|
||||||
@ -242,7 +247,7 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
if (controller.hasGlobalMiddlewares()) {
|
if (controller.hasGlobalMiddlewares()) {
|
||||||
controller.setupGlobalHandlers(handleRouter);
|
controller.setupGlobalHandlers(handleRouter);
|
||||||
|
|
||||||
log.info(`Registered global middlewares for controller ${controller.constructor.name}`);
|
logger.info(`Registered global middlewares for controller ${controller.constructor.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +256,7 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
initRouter.use(controller.getRoutesPrefix(), fileUploadFormRouter);
|
initRouter.use(controller.getRoutesPrefix(), fileUploadFormRouter);
|
||||||
handleRouter.use(controller.getRoutesPrefix(), mainRouter);
|
handleRouter.use(controller.getRoutesPrefix(), mainRouter);
|
||||||
|
|
||||||
log.info(`> Registered routes for controller ${controller.constructor.name} at ${controller.getRoutesPrefix()}`);
|
logger.info(`> Registered routes for controller ${controller.constructor.name} at ${controller.getRoutesPrefix()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRouter.use((req: Request) => {
|
handleRouter.use((req: Request) => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {Express, Router} from "express";
|
import {Express, Router} from "express";
|
||||||
import {log} from "./Logger";
|
import {logger} from "./Logger";
|
||||||
import {sleep} from "./Utils";
|
import {sleep} from "./Utils";
|
||||||
import Application from "./Application";
|
import Application from "./Application";
|
||||||
import config from "config";
|
import config from "config";
|
||||||
@ -28,11 +28,11 @@ export default abstract class ApplicationComponent {
|
|||||||
err = null;
|
err = null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = e;
|
err = e;
|
||||||
log.error(err, `${name} failed to prepare; retrying in 5s...`);
|
logger.error(err, `${name} failed to prepare; retrying in 5s...`);
|
||||||
await sleep(5000);
|
await sleep(5000);
|
||||||
}
|
}
|
||||||
} while (err);
|
} while (err);
|
||||||
log.info(`${name} ready!`);
|
logger.info(`${name} ready!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async close(thingName: string, fn: (callback: (err?: Error | null) => void) => void): Promise<void> {
|
protected async close(thingName: string, fn: (callback: (err?: Error | null) => void) => void): Promise<void> {
|
||||||
@ -42,9 +42,9 @@ export default abstract class ApplicationComponent {
|
|||||||
else resolve();
|
else resolve();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
log.info(`${thingName} closed.`);
|
logger.info(`${thingName} closed.`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(e, `An error occurred while closing the ${thingName}.`);
|
logger.error(e, `An error occurred while closing the ${thingName}.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import express, {IRouter, RequestHandler, Router} from "express";
|
import express, {IRouter, RequestHandler, Router} from "express";
|
||||||
import {PathParams} from "express-serve-static-core";
|
import {PathParams} from "express-serve-static-core";
|
||||||
import config from "config";
|
import config from "config";
|
||||||
import {log} from "./Logger";
|
import {logger} from "./Logger";
|
||||||
import FileUploadMiddleware from "./FileUploadMiddleware";
|
import FileUploadMiddleware from "./FileUploadMiddleware";
|
||||||
import * as querystring from "querystring";
|
import * as querystring from "querystring";
|
||||||
import {ParsedUrlQueryInput} from "querystring";
|
import {ParsedUrlQueryInput} from "querystring";
|
||||||
@ -75,7 +75,7 @@ export default abstract class Controller {
|
|||||||
|
|
||||||
protected use(handler: RequestHandler): void {
|
protected use(handler: RequestHandler): void {
|
||||||
this.router.use(this.wrap(handler));
|
this.router.use(this.wrap(handler));
|
||||||
log.info('Installed anonymous middleware on ' + this.getRoutesPrefix());
|
logger.info('Installed anonymous middleware on ' + this.getRoutesPrefix());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected useMiddleware(...middlewares: MiddlewareType<Middleware>[]): void {
|
protected useMiddleware(...middlewares: MiddlewareType<Middleware>[]): void {
|
||||||
@ -87,7 +87,7 @@ export default abstract class Controller {
|
|||||||
this.router.use(this.wrap(instance.getRequestHandler()));
|
this.router.use(this.wrap(instance.getRequestHandler()));
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info('Installed ' + middleware.name + ' on ' + this.getRoutesPrefix());
|
logger.info('Installed ' + middleware.name + ' on ' + this.getRoutesPrefix());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,10 +176,10 @@ export default abstract class Controller {
|
|||||||
|
|
||||||
if (!Controller.routes[routeName]) {
|
if (!Controller.routes[routeName]) {
|
||||||
if (typeof routePath === 'string') {
|
if (typeof routePath === 'string') {
|
||||||
log.info(`Route ${routeName} has path ${routePath}`);
|
logger.info(`Route ${routeName} has path ${routePath}`);
|
||||||
Controller.routes[routeName] = routePath;
|
Controller.routes[routeName] = routePath;
|
||||||
} else {
|
} else {
|
||||||
log.warn(`Cannot assign path to route ${routeName}.`);
|
logger.warn(`Cannot assign path to route ${routeName}.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
import {v4 as uuid} from "uuid";
|
|
||||||
import {Logger as TsLogger} from "tslog";
|
import {Logger as TsLogger} from "tslog";
|
||||||
|
import {AsyncLocalStorage} from "async_hooks";
|
||||||
|
import {RequestHandler} from "express";
|
||||||
|
import {nanoid} from "nanoid";
|
||||||
|
|
||||||
export const log = new TsLogger();
|
const requestIdStorage: AsyncLocalStorage<string> = new AsyncLocalStorage();
|
||||||
|
|
||||||
export function makeUniqueLogger(): TsLogger {
|
export const logger = new TsLogger({
|
||||||
const id = uuid();
|
requestId: (): string => {
|
||||||
return log.getChildLogger({
|
return requestIdStorage.getStore() as string;
|
||||||
requestId: id,
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
export const loggingContextMiddleware: RequestHandler = (req, res, next) => {
|
||||||
|
requestIdStorage.run(nanoid(8), () => {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
@ -27,10 +27,10 @@ import CsrfProtectionComponent from "./components/CsrfProtectionComponent";
|
|||||||
import MailController from "./mail/MailController";
|
import MailController from "./mail/MailController";
|
||||||
import WebSocketServerComponent from "./components/WebSocketServerComponent";
|
import WebSocketServerComponent from "./components/WebSocketServerComponent";
|
||||||
import Controller from "./Controller";
|
import Controller from "./Controller";
|
||||||
import packageJson = require('../package.json');
|
|
||||||
import AccountController from "./auth/AccountController";
|
import AccountController from "./auth/AccountController";
|
||||||
import MakeMagicLinksSessionNotUniqueMigration from "./auth/magic_link/MakeMagicLinksSessionNotUniqueMigration";
|
import MakeMagicLinksSessionNotUniqueMigration from "./auth/magic_link/MakeMagicLinksSessionNotUniqueMigration";
|
||||||
import AddUsedToMagicLinksMigration from "./auth/magic_link/AddUsedToMagicLinksMigration";
|
import AddUsedToMagicLinksMigration from "./auth/magic_link/AddUsedToMagicLinksMigration";
|
||||||
|
import packageJson = require('../package.json');
|
||||||
|
|
||||||
export const MIGRATIONS = [
|
export const MIGRATIONS = [
|
||||||
CreateMigrationsTable,
|
CreateMigrationsTable,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {TooManyRequestsHttpError} from "./HttpError";
|
import {TooManyRequestsHttpError} from "./HttpError";
|
||||||
import {log} from "./Logger";
|
import {logger} from "./Logger";
|
||||||
|
|
||||||
export default class Throttler {
|
export default class Throttler {
|
||||||
private static readonly throttles: Record<string, Throttle | undefined> = {};
|
private static readonly throttles: Record<string, Throttle | undefined> = {};
|
||||||
@ -81,7 +81,7 @@ class Throttle {
|
|||||||
trigger.jailed = currentDate;
|
trigger.jailed = currentDate;
|
||||||
|
|
||||||
const unjailedIn = trigger.jailed + this.jailPeriod - currentDate;
|
const unjailedIn = trigger.jailed + this.jailPeriod - currentDate;
|
||||||
log.info(`Jail ${this.jailName} triggered by ${id} and will be unjailed in ${unjailedIn}ms.`);
|
logger.info(`Jail ${this.jailName} triggered by ${id} and will be unjailed in ${unjailedIn}ms.`);
|
||||||
|
|
||||||
return this.throw(unjailedIn);
|
return this.throw(unjailedIn);
|
||||||
}
|
}
|
||||||
|
13
src/Utils.ts
13
src/Utils.ts
@ -1,5 +1,3 @@
|
|||||||
import * as crypto from "crypto";
|
|
||||||
|
|
||||||
export async function sleep(ms: number): Promise<void> {
|
export async function sleep(ms: number): Promise<void> {
|
||||||
return await new Promise(resolve => {
|
return await new Promise(resolve => {
|
||||||
setTimeout(() => resolve(), ms);
|
setTimeout(() => resolve(), ms);
|
||||||
@ -23,17 +21,6 @@ export abstract class WrappingError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cryptoRandomDictionary(size: number, dictionary: string): string {
|
|
||||||
const randomBytes = crypto.randomBytes(size);
|
|
||||||
const output = new Array(size);
|
|
||||||
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
output[i] = dictionary[Math.floor(randomBytes[i] / 255 * dictionary.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return output.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Type<T> = { new(...args: never[]): T };
|
export type Type<T> = { new(...args: never[]): T };
|
||||||
|
|
||||||
export function bufferToUuid(buffer: Buffer): string {
|
export function bufferToUuid(buffer: Buffer): string {
|
||||||
|
@ -11,7 +11,7 @@ import UserEmail from "./models/UserEmail";
|
|||||||
import MagicLinkController from "./magic_link/MagicLinkController";
|
import MagicLinkController from "./magic_link/MagicLinkController";
|
||||||
import {MailTemplate} from "../mail/Mail";
|
import {MailTemplate} from "../mail/Mail";
|
||||||
import {ADD_EMAIL_MAIL_TEMPLATE} from "../Mails";
|
import {ADD_EMAIL_MAIL_TEMPLATE} from "../Mails";
|
||||||
import {log} from "../Logger";
|
import {logger} from "../Logger";
|
||||||
import AuthMagicLinkActionType from "./magic_link/AuthMagicLinkActionType";
|
import AuthMagicLinkActionType from "./magic_link/AuthMagicLinkActionType";
|
||||||
|
|
||||||
export default class AccountController extends Controller {
|
export default class AccountController extends Controller {
|
||||||
@ -65,7 +65,7 @@ export default class AccountController extends Controller {
|
|||||||
|
|
||||||
await passwordComponent.setPassword(req.body.new_password, 'new_password');
|
await passwordComponent.setPassword(req.body.new_password, 'new_password');
|
||||||
await user.save();
|
await user.save();
|
||||||
log.debug('user ' + user.id + ' changed their password and was saved.');
|
logger.debug('user ' + user.id + ' changed their password and was saved.');
|
||||||
|
|
||||||
req.flash('success', 'Password changed successfully.');
|
req.flash('success', 'Password changed successfully.');
|
||||||
res.redirectBack(Controller.route('account'));
|
res.redirectBack(Controller.route('account'));
|
||||||
|
@ -17,7 +17,7 @@ import AuthMagicLinkActionType from "./AuthMagicLinkActionType";
|
|||||||
import {QueryVariable} from "../../db/MysqlConnectionManager";
|
import {QueryVariable} from "../../db/MysqlConnectionManager";
|
||||||
import UserNameComponent from "../models/UserNameComponent";
|
import UserNameComponent from "../models/UserNameComponent";
|
||||||
import MagicLinkUserNameComponent from "../models/MagicLinkUserNameComponent";
|
import MagicLinkUserNameComponent from "../models/MagicLinkUserNameComponent";
|
||||||
import {log} from "../../Logger";
|
import {logger} from "../../Logger";
|
||||||
|
|
||||||
export default class MagicLinkController<A extends Application> extends Controller {
|
export default class MagicLinkController<A extends Application> extends Controller {
|
||||||
public static async sendMagicLink(
|
public static async sendMagicLink(
|
||||||
@ -233,7 +233,7 @@ export default class MagicLinkController<A extends Application> extends Controll
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.warn('Unknown magic link action type ' + magicLink.action_type);
|
logger.warn('Unknown magic link action type ' + magicLink.action_type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,12 @@ import config from "config";
|
|||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
import ApplicationComponent from "../ApplicationComponent";
|
import ApplicationComponent from "../ApplicationComponent";
|
||||||
import {ForbiddenHttpError} from "../HttpError";
|
import {ForbiddenHttpError} from "../HttpError";
|
||||||
import {log} from "../Logger";
|
import {logger} from "../Logger";
|
||||||
|
|
||||||
export default class AutoUpdateComponent extends ApplicationComponent {
|
export default class AutoUpdateComponent extends ApplicationComponent {
|
||||||
private static async runCommand(command: string): Promise<void> {
|
private static async runCommand(command: string): Promise<void> {
|
||||||
log.info(`> ${command}`);
|
logger.info(`> ${command}`);
|
||||||
log.info(child_process.execSync(command).toString());
|
logger.info(child_process.execSync(command).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkSecuritySettings(): Promise<void> {
|
public async checkSecuritySettings(): Promise<void> {
|
||||||
@ -21,7 +21,7 @@ export default class AutoUpdateComponent extends ApplicationComponent {
|
|||||||
if (!token || token !== config.get<string>('gitlab_webhook_token'))
|
if (!token || token !== config.get<string>('gitlab_webhook_token'))
|
||||||
throw new ForbiddenHttpError('Invalid token', req.url);
|
throw new ForbiddenHttpError('Invalid token', req.url);
|
||||||
|
|
||||||
this.update(req.body).catch(req.log.error);
|
this.update(req.body).catch(logger.error);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
'status': 'ok',
|
'status': 'ok',
|
||||||
@ -30,10 +30,10 @@ export default class AutoUpdateComponent extends ApplicationComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async update(params: { [p: string]: unknown }) {
|
private async update(params: { [p: string]: unknown }) {
|
||||||
log.info('Update params:', params);
|
logger.info('Update params:', params);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info('Starting auto update...');
|
logger.info('Starting auto update...');
|
||||||
|
|
||||||
// Fetch
|
// Fetch
|
||||||
await AutoUpdateComponent.runCommand(`git pull`);
|
await AutoUpdateComponent.runCommand(`git pull`);
|
||||||
@ -47,9 +47,9 @@ export default class AutoUpdateComponent extends ApplicationComponent {
|
|||||||
// Stop app
|
// Stop app
|
||||||
await this.getApp().stop();
|
await this.getApp().stop();
|
||||||
|
|
||||||
log.info('Success!');
|
logger.info('Success!');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(e, 'An error occurred while running the auto update.');
|
logger.error(e, 'An error occurred while running the auto update.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import ApplicationComponent from "../ApplicationComponent";
|
import ApplicationComponent from "../ApplicationComponent";
|
||||||
import express, {Express, Router} from "express";
|
import express, {Express, Router} from "express";
|
||||||
import {log, makeUniqueLogger} from "../Logger";
|
import {logger} from "../Logger";
|
||||||
import {Server} from "http";
|
import {Server} from "http";
|
||||||
import compression from "compression";
|
import compression from "compression";
|
||||||
import Middleware from "../Middleware";
|
import Middleware from "../Middleware";
|
||||||
@ -20,7 +20,7 @@ export default class ExpressAppComponent extends ApplicationComponent {
|
|||||||
|
|
||||||
public async start(app: Express): Promise<void> {
|
public async start(app: Express): Promise<void> {
|
||||||
this.server = app.listen(this.port, this.addr, () => {
|
this.server = app.listen(this.port, this.addr, () => {
|
||||||
log.info(`Web server running on ${this.addr}:${this.port}.`);
|
logger.info(`Web server running on ${this.addr}:${this.port}.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Proxy
|
// Proxy
|
||||||
@ -41,7 +41,6 @@ export default class ExpressAppComponent extends ApplicationComponent {
|
|||||||
router.use(compression());
|
router.use(compression());
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
req.log = makeUniqueLogger();
|
|
||||||
req.middlewares = [];
|
req.middlewares = [];
|
||||||
req.as = <M extends Middleware>(type: Type<M>): M => {
|
req.as = <M extends Middleware>(type: Type<M>): M => {
|
||||||
const middleware = req.middlewares.find(m => m.constructor === type);
|
const middleware = req.middlewares.find(m => m.constructor === type);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import ApplicationComponent from "../ApplicationComponent";
|
import ApplicationComponent from "../ApplicationComponent";
|
||||||
import onFinished from "on-finished";
|
import onFinished from "on-finished";
|
||||||
import {log} from "../Logger";
|
import {logger} from "../Logger";
|
||||||
import {Request, Response, Router} from "express";
|
import {Request, Response, Router} from "express";
|
||||||
import {HttpError} from "../HttpError";
|
import {HttpError} from "../HttpError";
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ export default class LogRequestsComponent extends ApplicationComponent {
|
|||||||
|
|
||||||
public static logFullHttpRequests(): void {
|
public static logFullHttpRequests(): void {
|
||||||
this.fullRequests = true;
|
this.fullRequests = true;
|
||||||
log.info('Http requests will be logged with more details.');
|
logger.info('Http requests will be logged with more details.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static logRequest(
|
public static logRequest(
|
||||||
@ -38,12 +38,12 @@ export default class LogRequestsComponent extends ApplicationComponent {
|
|||||||
}, null, 4);
|
}, null, 4);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
return req.log.error(err, requestObj, err).requestId;
|
return logger.error(err, requestObj, err).requestId;
|
||||||
} else {
|
} else {
|
||||||
return req.log.error(new Error(String(err)), requestObj).requestId;
|
return logger.error(new Error(String(err)), requestObj).requestId;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
req.log.info(requestObj);
|
logger.info(requestObj);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let codeDescription = '';
|
let codeDescription = '';
|
||||||
@ -59,15 +59,15 @@ export default class LogRequestsComponent extends ApplicationComponent {
|
|||||||
if (silent) {
|
if (silent) {
|
||||||
if (err instanceof HttpError) logStr += ` ${err.errorCode}`;
|
if (err instanceof HttpError) logStr += ` ${err.errorCode}`;
|
||||||
logStr += ` ${err.name}`;
|
logStr += ` ${err.name}`;
|
||||||
return req.log.info(err.name, logStr).requestId;
|
return logger.info(err.name, logStr).requestId;
|
||||||
} else {
|
} else {
|
||||||
return req.log.error(err, logStr, additionalStr, err).requestId;
|
return logger.error(err, logStr, additionalStr, err).requestId;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return req.log.error(new Error(String(err)), logStr).requestId;
|
return logger.error(new Error(String(err)), logStr).requestId;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
req.log.info(logStr);
|
logger.info(logStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import {ParsedUrlQueryInput} from "querystring";
|
|||||||
import * as util from "util";
|
import * as util from "util";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import {log} from "../Logger";
|
import {logger} from "../Logger";
|
||||||
import Middleware from "../Middleware";
|
import Middleware from "../Middleware";
|
||||||
|
|
||||||
export default class NunjucksComponent extends ApplicationComponent {
|
export default class NunjucksComponent extends ApplicationComponent {
|
||||||
@ -29,7 +29,7 @@ export default class NunjucksComponent extends ApplicationComponent {
|
|||||||
try {
|
try {
|
||||||
coreVersion = JSON.parse(fs.readFileSync(file).toString()).version;
|
coreVersion = JSON.parse(fs.readFileSync(file).toString()).version;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.warn('Couldn\'t determine coreVersion.', e);
|
logger.warn('Couldn\'t determine coreVersion.', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
|
@ -2,6 +2,7 @@ import ApplicationComponent from "../ApplicationComponent";
|
|||||||
import {Request, Router} from "express";
|
import {Request, Router} from "express";
|
||||||
import {ServerError} from "../HttpError";
|
import {ServerError} from "../HttpError";
|
||||||
import onFinished from "on-finished";
|
import onFinished from "on-finished";
|
||||||
|
import {logger} from "../Logger";
|
||||||
|
|
||||||
export default class RedirectBackComponent extends ApplicationComponent {
|
export default class RedirectBackComponent extends ApplicationComponent {
|
||||||
public static getPreviousURL(req: Request, defaultUrl?: string): string | undefined {
|
public static getPreviousURL(req: Request, defaultUrl?: string): string | undefined {
|
||||||
@ -28,10 +29,10 @@ export default class RedirectBackComponent extends ApplicationComponent {
|
|||||||
contentType && typeof contentType !== 'number' && contentType.indexOf('text/html') >= 0
|
contentType && typeof contentType !== 'number' && contentType.indexOf('text/html') >= 0
|
||||||
)) {
|
)) {
|
||||||
session.previousUrl = req.originalUrl;
|
session.previousUrl = req.originalUrl;
|
||||||
req.log.debug('Prev url set to', session.previousUrl);
|
logger.debug('Prev url set to', session.previousUrl);
|
||||||
session.save((err) => {
|
session.save((err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
req.log.error(err, 'Error while saving session');
|
logger.error(err, 'Error while saving session');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import ApplicationComponent from "../ApplicationComponent";
|
|||||||
import {Express} from "express";
|
import {Express} from "express";
|
||||||
import redis, {RedisClient} from "redis";
|
import redis, {RedisClient} from "redis";
|
||||||
import config from "config";
|
import config from "config";
|
||||||
import {log} from "../Logger";
|
import {logger} from "../Logger";
|
||||||
import session, {Store} from "express-session";
|
import session, {Store} from "express-session";
|
||||||
import connect_redis from "connect-redis";
|
import connect_redis from "connect-redis";
|
||||||
import CacheProvider from "../CacheProvider";
|
import CacheProvider from "../CacheProvider";
|
||||||
@ -18,7 +18,7 @@ export default class RedisComponent extends ApplicationComponent implements Cach
|
|||||||
password: config.has('redis.password') ? config.get<string>('redis.password') : undefined,
|
password: config.has('redis.password') ? config.get<string>('redis.password') : undefined,
|
||||||
});
|
});
|
||||||
this.redisClient.on('error', (err: Error) => {
|
this.redisClient.on('error', (err: Error) => {
|
||||||
log.error(err, 'An error occurred with redis.');
|
logger.error(err, 'An error occurred with redis.');
|
||||||
});
|
});
|
||||||
this.store = new RedisStore({
|
this.store = new RedisStore({
|
||||||
client: this.redisClient,
|
client: this.redisClient,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import ApplicationComponent from "../ApplicationComponent";
|
import ApplicationComponent from "../ApplicationComponent";
|
||||||
import {Express, Request} from "express";
|
import {Express, Request} from "express";
|
||||||
import WebSocket, {Server as WebSocketServer} from "ws";
|
import WebSocket, {Server as WebSocketServer} from "ws";
|
||||||
import {log} from "../Logger";
|
import {logger} from "../Logger";
|
||||||
import cookie from "cookie";
|
import cookie from "cookie";
|
||||||
import cookieParser from "cookie-parser";
|
import cookieParser from "cookie-parser";
|
||||||
import config from "config";
|
import config from "config";
|
||||||
@ -28,9 +28,9 @@ export default class WebSocketServerComponent extends ApplicationComponent {
|
|||||||
this.wss = new WebSocketServer({
|
this.wss = new WebSocketServer({
|
||||||
server: this.expressAppComponent.getServer(),
|
server: this.expressAppComponent.getServer(),
|
||||||
}, () => {
|
}, () => {
|
||||||
log.info(`Websocket server started over webserver.`);
|
logger.info(`Websocket server started over webserver.`);
|
||||||
}).on('error', (err) => {
|
}).on('error', (err) => {
|
||||||
log.error(err, 'An error occurred in the websocket server.');
|
logger.error(err, 'An error occurred in the websocket server.');
|
||||||
}).on('connection', (socket, request) => {
|
}).on('connection', (socket, request) => {
|
||||||
const listener = request.url ? listeners[request.url] : null;
|
const listener = request.url ? listeners[request.url] : null;
|
||||||
|
|
||||||
@ -39,12 +39,12 @@ export default class WebSocketServerComponent extends ApplicationComponent {
|
|||||||
return;
|
return;
|
||||||
} else if (!request.headers.cookie) {
|
} else if (!request.headers.cookie) {
|
||||||
listener.handle(socket, request, null).catch(err => {
|
listener.handle(socket, request, null).catch(err => {
|
||||||
log.error(err, 'Error in websocket listener.');
|
logger.error(err, 'Error in websocket listener.');
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug(`Websocket on ${request.url}`);
|
logger.debug(`Websocket on ${request.url}`);
|
||||||
|
|
||||||
const cookies = cookie.parse(request.headers.cookie);
|
const cookies = cookie.parse(request.headers.cookie);
|
||||||
const sid = cookieParser.signedCookie(cookies['connect.sid'], config.get('session.secret'));
|
const sid = cookieParser.signedCookie(cookies['connect.sid'], config.get('session.secret'));
|
||||||
@ -57,7 +57,7 @@ export default class WebSocketServerComponent extends ApplicationComponent {
|
|||||||
const store = this.storeComponent.getStore();
|
const store = this.storeComponent.getStore();
|
||||||
store.get(sid, (err, session) => {
|
store.get(sid, (err, session) => {
|
||||||
if (err || !session) {
|
if (err || !session) {
|
||||||
log.error(err, 'Error while initializing session in websocket.');
|
logger.error(err, 'Error while initializing session in websocket.');
|
||||||
socket.close(1011);
|
socket.close(1011);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ export default class WebSocketServerComponent extends ApplicationComponent {
|
|||||||
|
|
||||||
store.createSession(<Request>request, session);
|
store.createSession(<Request>request, session);
|
||||||
listener.handle(socket, request, (<Request>request).session).catch(err => {
|
listener.handle(socket, request, (<Request>request).session).catch(err => {
|
||||||
log.error(err, 'Error in websocket listener.');
|
logger.error(err, 'Error in websocket listener.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import mysql, {Connection, FieldInfo, MysqlError, Pool, PoolConnection} from 'mysql';
|
import mysql, {Connection, FieldInfo, MysqlError, Pool, PoolConnection} from 'mysql';
|
||||||
import config from 'config';
|
import config from 'config';
|
||||||
import Migration, {MigrationType} from "./Migration";
|
import Migration, {MigrationType} from "./Migration";
|
||||||
import {log} from "../Logger";
|
import {logger} from "../Logger";
|
||||||
import {Type} from "../Utils";
|
import {Type} from "../Utils";
|
||||||
|
|
||||||
export interface QueryResult {
|
export interface QueryResult {
|
||||||
@ -50,7 +50,7 @@ export default class MysqlConnectionManager {
|
|||||||
public static async prepare(runMigrations: boolean = true): Promise<void> {
|
public static async prepare(runMigrations: boolean = true): Promise<void> {
|
||||||
if (config.get('mysql.create_database_automatically') === true) {
|
if (config.get('mysql.create_database_automatically') === true) {
|
||||||
const dbName = config.get('mysql.database');
|
const dbName = config.get('mysql.database');
|
||||||
log.info(`Creating database ${dbName}...`);
|
logger.info(`Creating database ${dbName}...`);
|
||||||
const connection = mysql.createConnection({
|
const connection = mysql.createConnection({
|
||||||
host: config.get('mysql.host'),
|
host: config.get('mysql.host'),
|
||||||
user: config.get('mysql.user'),
|
user: config.get('mysql.user'),
|
||||||
@ -65,7 +65,7 @@ export default class MysqlConnectionManager {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
connection.end();
|
connection.end();
|
||||||
log.info(`Database ${dbName} created!`);
|
logger.info(`Database ${dbName} created!`);
|
||||||
}
|
}
|
||||||
this.databaseReady = true;
|
this.databaseReady = true;
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ export default class MysqlConnectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.currentPool.end(() => {
|
this.currentPool.end(() => {
|
||||||
log.info('Mysql connection pool ended.');
|
logger.info('Mysql connection pool ended.');
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
this.currentPool = undefined;
|
this.currentPool = undefined;
|
||||||
@ -110,7 +110,7 @@ export default class MysqlConnectionManager {
|
|||||||
connection?: Connection,
|
connection?: Connection,
|
||||||
): Promise<QueryResult> {
|
): Promise<QueryResult> {
|
||||||
return await new Promise<QueryResult>((resolve, reject) => {
|
return await new Promise<QueryResult>((resolve, reject) => {
|
||||||
log.debug('SQL:', log.settings.minLevel === 'trace' || log.settings.minLevel === 'silly' ?
|
logger.debug('SQL:', logger.settings.minLevel === 'trace' || logger.settings.minLevel === 'silly' ?
|
||||||
mysql.format(queryString, values) :
|
mysql.format(queryString, values) :
|
||||||
queryString);
|
queryString);
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ export default class MysqlConnectionManager {
|
|||||||
const currentVersion = await this.getCurrentMigrationVersion();
|
const currentVersion = await this.getCurrentMigrationVersion();
|
||||||
for (const migration of this.migrations) {
|
for (const migration of this.migrations) {
|
||||||
if (await migration.shouldRun(currentVersion)) {
|
if (await migration.shouldRun(currentVersion)) {
|
||||||
log.info('Running migration ', migration.version, migration.constructor.name);
|
logger.info('Running migration ', migration.version, migration.constructor.name);
|
||||||
await MysqlConnectionManager.wrapTransaction<void>(async c => {
|
await MysqlConnectionManager.wrapTransaction<void>(async c => {
|
||||||
migration.setCurrentConnection(c);
|
migration.setCurrentConnection(c);
|
||||||
await migration.install();
|
await migration.install();
|
||||||
@ -219,7 +219,7 @@ export default class MysqlConnectionManager {
|
|||||||
public static async rollbackMigration(migrationId: number = 0): Promise<void> {
|
public static async rollbackMigration(migrationId: number = 0): Promise<void> {
|
||||||
migrationId--;
|
migrationId--;
|
||||||
const migration = this.migrations[migrationId];
|
const migration = this.migrations[migrationId];
|
||||||
log.info('Rolling back migration ', migration.version, migration.constructor.name);
|
logger.info('Rolling back migration ', migration.version, migration.constructor.name);
|
||||||
await MysqlConnectionManager.wrapTransaction<void>(async c => {
|
await MysqlConnectionManager.wrapTransaction<void>(async c => {
|
||||||
migration.setCurrentConnection(c);
|
migration.setCurrentConnection(c);
|
||||||
await migration.rollback();
|
await migration.rollback();
|
||||||
@ -230,7 +230,7 @@ export default class MysqlConnectionManager {
|
|||||||
|
|
||||||
public static async migrationCommand(args: string[]): Promise<void> {
|
public static async migrationCommand(args: string[]): Promise<void> {
|
||||||
try {
|
try {
|
||||||
log.info('Current migration:', await this.getCurrentMigrationVersion());
|
logger.info('Current migration:', await this.getCurrentMigrationVersion());
|
||||||
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
if (args[i] === 'rollback') {
|
if (args[i] === 'rollback') {
|
||||||
|
@ -5,7 +5,7 @@ import {Environment} from 'nunjucks';
|
|||||||
import * as util from "util";
|
import * as util from "util";
|
||||||
import {WrappingError} from "../Utils";
|
import {WrappingError} from "../Utils";
|
||||||
import mjml2html from "mjml";
|
import mjml2html from "mjml";
|
||||||
import {log} from "../Logger";
|
import {logger} from "../Logger";
|
||||||
import Controller from "../Controller";
|
import Controller from "../Controller";
|
||||||
import {ParsedUrlQueryInput} from "querystring";
|
import {ParsedUrlQueryInput} from "querystring";
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ export default class Mail {
|
|||||||
throw new MailError('Connection to mail service unsuccessful.', e);
|
throw new MailError('Connection to mail service unsuccessful.', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Mail ready to be distributed via ${config.get('mail.host')}:${config.get('mail.port')}`);
|
logger.info(`Mail ready to be distributed via ${config.get('mail.host')}:${config.get('mail.port')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static end(): void {
|
public static end(): void {
|
||||||
@ -110,7 +110,7 @@ export default class Mail {
|
|||||||
this.data.app = config.get('app');
|
this.data.app = config.get('app');
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
log.debug('Send mail', this.options);
|
logger.debug('Send mail', this.options);
|
||||||
|
|
||||||
// Render email
|
// Render email
|
||||||
this.options.html = Mail.parse(this.environment, 'mails/' + this.template.template + '.mjml.njk',
|
this.options.html = Mail.parse(this.environment, 'mails/' + this.template.template + '.mjml.njk',
|
||||||
|
@ -6,15 +6,15 @@ process.env['NODE_CONFIG_DIR'] =
|
|||||||
+ delimiter
|
+ delimiter
|
||||||
+ (process.env['NODE_CONFIG_DIR'] || __dirname + '/../../config/');
|
+ (process.env['NODE_CONFIG_DIR'] || __dirname + '/../../config/');
|
||||||
|
|
||||||
import {log} from "./Logger";
|
import {logger} from "./Logger";
|
||||||
import TestApp from "./TestApp";
|
import TestApp from "./TestApp";
|
||||||
import config from "config";
|
import config from "config";
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
log.debug('Config path:', process.env['NODE_CONFIG_DIR']);
|
logger.debug('Config path:', process.env['NODE_CONFIG_DIR']);
|
||||||
|
|
||||||
const app = new TestApp(config.get<string>('listen_addr'), config.get<number>('port'));
|
const app = new TestApp(config.get<string>('listen_addr'), config.get<number>('port'));
|
||||||
await app.start();
|
await app.start();
|
||||||
})().catch(err => {
|
})().catch(err => {
|
||||||
log.error(err);
|
logger.error(err);
|
||||||
});
|
});
|
||||||
|
4
src/types/Express.d.ts
vendored
4
src/types/Express.d.ts
vendored
@ -2,16 +2,12 @@ import {Files} from "formidable";
|
|||||||
import {Type} from "../Utils";
|
import {Type} from "../Utils";
|
||||||
import Middleware from "../Middleware";
|
import Middleware from "../Middleware";
|
||||||
import {FlashMessages} from "../components/SessionComponent";
|
import {FlashMessages} from "../components/SessionComponent";
|
||||||
import {Logger} from "tslog";
|
|
||||||
import {Session, SessionData} from "express-session";
|
import {Session, SessionData} from "express-session";
|
||||||
import {PasswordAuthProofSessionData} from "../auth/password/PasswordAuthProof";
|
import {PasswordAuthProofSessionData} from "../auth/password/PasswordAuthProof";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace Express {
|
namespace Express {
|
||||||
export interface Request {
|
export interface Request {
|
||||||
log: Logger;
|
|
||||||
|
|
||||||
|
|
||||||
getSession(): Session & Partial<SessionData>;
|
getSession(): Session & Partial<SessionData>;
|
||||||
|
|
||||||
getSessionOptional(): Session & Partial<SessionData> | undefined;
|
getSessionOptional(): Session & Partial<SessionData> | undefined;
|
||||||
|
@ -2,7 +2,7 @@ import MysqlConnectionManager from "../src/db/MysqlConnectionManager";
|
|||||||
import Model from "../src/db/Model";
|
import Model from "../src/db/Model";
|
||||||
import ModelFactory from "../src/db/ModelFactory";
|
import ModelFactory from "../src/db/ModelFactory";
|
||||||
import {ValidationBag} from "../src/db/Validator";
|
import {ValidationBag} from "../src/db/Validator";
|
||||||
import {log} from "../src/Logger";
|
import {logger} from "../src/Logger";
|
||||||
import {ManyThroughModelRelation, OneModelRelation} from "../src/db/ModelRelation";
|
import {ManyThroughModelRelation, OneModelRelation} from "../src/db/ModelRelation";
|
||||||
import {MIGRATIONS} from "../src/TestApp";
|
import {MIGRATIONS} from "../src/TestApp";
|
||||||
import config from "config";
|
import config from "config";
|
||||||
@ -104,7 +104,7 @@ beforeAll(async () => {
|
|||||||
await MysqlConnectionManager.query('DROP DATABASE IF EXISTS ' + config.get<string>('mysql.database'));
|
await MysqlConnectionManager.query('DROP DATABASE IF EXISTS ' + config.get<string>('mysql.database'));
|
||||||
await MysqlConnectionManager.endPool();
|
await MysqlConnectionManager.endPool();
|
||||||
|
|
||||||
log.setSettings({minLevel: "trace"});
|
logger.setSettings({minLevel: "trace"});
|
||||||
MysqlConnectionManager.registerMigrations(MIGRATIONS);
|
MysqlConnectionManager.registerMigrations(MIGRATIONS);
|
||||||
ModelFactory.register(FakeDummyModel);
|
ModelFactory.register(FakeDummyModel);
|
||||||
ModelFactory.register(Post);
|
ModelFactory.register(Post);
|
||||||
|
12
yarn.lock
12
yarn.lock
@ -742,6 +742,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/nanoid@^2.1.0":
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/nanoid/-/nanoid-2.1.0.tgz#41edfda78986e9127d0dc14de982de766f994020"
|
||||||
|
integrity sha512-xdkn/oRTA0GSNPLIKZgHWqDTWZsVrieKomxJBOQUK9YDD+zfSgmwD5t4WJYra5S7XyhTw7tfvwznW+pFexaepQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/node-fetch@^2.5.7":
|
"@types/node-fetch@^2.5.7":
|
||||||
version "2.5.8"
|
version "2.5.8"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
|
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
|
||||||
@ -4815,6 +4822,11 @@ mysql@^2.18.1:
|
|||||||
safe-buffer "5.1.2"
|
safe-buffer "5.1.2"
|
||||||
sqlstring "2.3.1"
|
sqlstring "2.3.1"
|
||||||
|
|
||||||
|
nanoid@^3.1.20:
|
||||||
|
version "3.1.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
|
||||||
|
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
|
||||||
|
|
||||||
nanomatch@^1.2.9:
|
nanomatch@^1.2.9:
|
||||||
version "1.2.13"
|
version "1.2.13"
|
||||||
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||||
|
Loading…
Reference in New Issue
Block a user