Merge branch 'dev' into feature-email-verification

This commit is contained in:
Stefan
2022-04-29 11:40:28 +02:00
committed by GitHub
8 changed files with 184 additions and 19 deletions
+1
View File
@@ -50,6 +50,7 @@ if [ "$RECREATE" = true ]; then
--network detleph_server_default \ --network detleph_server_default \
-e DATABASE_PASSWORD=server \ -e DATABASE_PASSWORD=server \
-e DATABASE_URL="postgresql://server:server@postgres:5432/management?schema=public" \ -e DATABASE_URL="postgresql://server:server@postgres:5432/management?schema=public" \
-e NODE_ENV="development" \
--entrypoint "/app/scripts/docker-entrypoint.dev.sh" \ --entrypoint "/app/scripts/docker-entrypoint.dev.sh" \
node node
fi fi
+3 -1
View File
@@ -30,11 +30,13 @@
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"express": "^4.17.1", "express": "^4.17.1",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"express-async-errors": "^3.1.1",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"mjml": "^4.11.0", "mjml": "^4.11.0",
"nanoid": "^3.3.3", "nanoid": "^3.3.3",
"nodemailer": "^6.7.0", "nodemailer": "^6.7.0",
"redis": "^3.1.2" "redis": "^3.1.2",
"winston": "^3.7.2"
}, },
"devDependencies": { "devDependencies": {
"@types/chai": "^4.2.22", "@types/chai": "^4.2.22",
+7 -7
View File
@@ -19,13 +19,13 @@ export const getAllEvents = async (req: Request, res: Response) => {
id: false, id: false,
}, },
}); });
if (events.length > 0)
res.status(200).json({ res.status(200).json({
type: "success", type: "success",
payload: { payload: {
events, events,
}, },
}); });
}; };
export const getEvent = async (req: Request, res: Response) => { export const getEvent = async (req: Request, res: Response) => {
+11
View File
@@ -0,0 +1,11 @@
import { NextFunction, Request, Response } from "express";
import logger from "../error/logger";
export default function debugLogger(req: Request, res: Response, next: NextFunction) {
// Only log when in development mode
if (process.env.NODE_ENV === "development") {
logger.debug(`Request to: ${req.url}`);
}
next();
}
+16
View File
@@ -0,0 +1,16 @@
export default class ForwardableError extends Error {
// If in different context
private readonly __id = "CUSTOM_ERROR";
public readonly status: number;
constructor(status: number, message: string) {
super(message);
this.status = status;
}
static isForwardableError(error: any): error is ForwardableError {
return error.__id === "CUSTOM_ERROR";
}
}
+82
View File
@@ -0,0 +1,82 @@
import { PrismaClientUnknownRequestError } from "@prisma/client/runtime";
import { NextFunction, Request, Response } from "express";
import ForwardableError from "./ForwardableError";
import logger from "./logger";
const env = process.env.NODE_ENV || "production";
type ErrorHandler = (err: any, req: Request, res: Response) => boolean;
const customHandlers: ErrorHandler[] = [];
export function addCustomHandler(handler: ErrorHandler) {
customHandlers.push(handler);
}
export function removeCustomHandler(handler: ErrorHandler): Boolean {
const index = customHandlers.findIndex((h) => h === handler);
if (index < 0) {
return false;
}
customHandlers.splice(index);
return true;
}
export default function defaultErrorHandler(err: any, req: Request, res: Response, next: NextFunction) {
for (const handler of customHandlers) {
// Run custom handler
if (handler(err, req, res)) {
return;
}
}
if (ForwardableError.isForwardableError(err)) {
return res.status(err.status).json({
type: "error",
payload: {
message: err.message,
...(env === "development"
? {
stack: err.stack,
}
: {}),
},
});
}
if (err instanceof PrismaClientUnknownRequestError) {
logger.warning(err);
return res.status(404).json({
type: "error",
payload: {
message: "An unknown error occured. This could be due to malformed IDs",
...(env === "development"
? {
prisma: err.message,
stack: err.stack,
}
: {}),
},
});
}
logger.error("--- Unhandled error ---");
logger.error(err);
return res.status(err.status).json({
type: "error",
payload: {
message: err.message,
...(env === "development"
? {
notice: "This error was not caught by any handler, please add handling!",
stack: err.stack,
}
: {}),
},
});
}
+41
View File
@@ -0,0 +1,41 @@
import winston, { createLogger } from "winston";
const {
format: { printf, colorize, combine, timestamp, json, errors, prettyPrint },
} = winston;
const defaultJsonFormat = combine(errors({ stack: true }), timestamp(), json({ space: 2 }));
const customCLIFormat = printf(({ level, message, label, timestamp, stack }) => {
let output = `${level}${stack ? `(1/2:message)` : ""} at ${timestamp}${label ? ` (#${label})` : ""}: ${message}${
stack ? "\n" : ""
}`;
if (stack) {
output += `\n${level}(2/2:stack)${label ? ` (#${label})` : ""}: ${stack}\n\n`;
}
return output;
});
export default createLogger({
levels: winston.config.syslog.levels,
format: combine(errors({ stack: true }), timestamp()),
transports: [
new winston.transports.Console({
level: "debug",
format: combine(timestamp(), colorize(), customCLIFormat),
}),
new winston.transports.File({
filename: "/app/error.log",
level: "error",
format: defaultJsonFormat,
}),
new winston.transports.File({
level: "debug",
filename: "/app/debug.log",
format: defaultJsonFormat,
silent: !(process.env.NODE_ENV === "development"), // Silent when not in development
}),
],
});
+23 -11
View File
@@ -11,11 +11,21 @@ import argon2 from "argon2";
import adminRouter from "./Routes/admin.routes"; import adminRouter from "./Routes/admin.routes";
import organisationRouter from "./Routes/organisation.routes"; import organisationRouter from "./Routes/organisation.routes";
import groupRouter from "./Routes/group.routes"; import groupRouter from "./Routes/group.routes";
import defaultErrorHandler from "./Middleware/error/handler";
import logger from "./Middleware/error/logger";
import debugLogger from "./Middleware/debug/logger";
// Set up async error handling
require("express-async-errors");
require("dotenv").config(); // Load dotenv config require("dotenv").config(); // Load dotenv config
const app = express(); const app = express();
if (process.env.NODE_ENV === "development") {
logger.info("Using development mode");
}
async function main() { async function main() {
// Dev // Dev
await prisma.admin.upsert({ await prisma.admin.upsert({
@@ -34,16 +44,7 @@ async function main() {
app.use(express.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
app.use(express.json()); app.use(express.json());
app.use((err: any, req: Request, res: Response, next: NextFunction) => { app.use(debugLogger);
if (err) {
res.status(400).send({
type: "error",
payload: "The body of your request did not contain valid data",
});
} else {
next();
}
});
// Admin authentication endpoints // Admin authentication endpoints
app.use("/api/authentication", adminAuthRouter); app.use("/api/authentication", adminAuthRouter);
@@ -56,13 +57,24 @@ async function main() {
app.use("/api/organisations", organisationRouter); app.use("/api/organisations", organisationRouter);
app.use("/api/groups", groupRouter); app.use("/api/groups", groupRouter);
// Error handling
app.use(defaultErrorHandler);
app.listen(process.env.PORT, () => { app.listen(process.env.PORT, () => {
console.log(`Listening on Port: ${process.env.PORT}`); logger.info(`Listening on port ${process.env.PORT}`);
});
logger.info("Server started");
process.on("exit", () => {
logger.info("Server stopping...");
}); });
} }
main() main()
.catch((e) => { .catch((e) => {
logger.crit(e);
throw e; throw e;
}) })
.finally(async () => { .finally(async () => {