Skip to content

Commit

Permalink
build: use extendable ts configs
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrianAndersen committed Jan 19, 2025
1 parent ca6690c commit 63437ed
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 200 deletions.
88 changes: 86 additions & 2 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,87 @@
import { Server } from "@backend/server/server";
import "@backend/instrument";

new Server();
import { initAuthEndpoints } from "@backend/auth/initAuthEndpoints";
import { CollectionEndpointCreator } from "@backend/collection-endpoint/collection-endpoint-creator";
import { assertEnv, BlEnvironment } from "@backend/config/environment";
import { logger } from "@backend/logger/logger";
import {
connectToDbAndStartServer,
getCorsHandler,
getSessionHandler,
} from "@backend/server/server";
import express, { json, Request, Response, Router } from "express";
import passport from "passport";

logger.silly(" _ _ _");
logger.silly("| |__ | | __ _ _ __ (_)");
logger.silly("| '_ \\| |/ _` | '_ \\| |");
logger.silly("| |_) | | (_| | |_) | |");
logger.silly(String.raw`|_.__/|_|\__,_| .__/|_|`);
logger.silly(" |_|");

const app = express();
const router = Router();

app.use(json({ limit: "1mb" }));

process.on("unhandledRejection", (reason, p) => {
logger.error(
`unhandled rejection at: ${p}, reason: ${reason}` +
(reason instanceof Error ? `, stack: ${reason.stack}` : ""),
);
});

app.use(getCorsHandler());
app.use(getSessionHandler());
app.use(passport.initialize());
app.use(passport.session());

app.get("*", (request, res, next) => {
if (
request.headers["x-forwarded-proto"] !== "https" &&
assertEnv(BlEnvironment.API_ENV) === "production"
) {
res.redirect("https://" + request.hostname + request.url);
} else {
next();
}
});

app.use((request: Request, res: Response, next: () => void) => {
if (request.method !== "OPTIONS") {
// no point in showing all the preflight requests
logger.debug(`-> ${request.method} ${request.url}`);
if (
!(
request.url.includes("auth") &&
assertEnv(BlEnvironment.API_ENV) === "production"
)
) {
let body: string;
try {
body = JSON.stringify(request.body);
} catch {
body = request.body.toString("utf8");
}

logger.silly(`-> ${body}`);
}
}
next();
});

app.use(router);

passport.serializeUser((user, done) => {
done(null, user);
});

passport.deserializeUser((user, done) => {
// @ts-expect-error fixme: auto ignored
done(null, user);
});

initAuthEndpoints(router);
new CollectionEndpointCreator(router).create();

connectToDbAndStartServer(app);
10 changes: 10 additions & 0 deletions backend/src/instrument.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { assertEnv, BlEnvironment } from "@backend/config/environment";
import * as Sentry from "@sentry/node";
import { nodeProfilingIntegration } from "@sentry/profiling-node";

Sentry.init({
dsn: "https://[email protected]/4508653655293952",
integrations: [nodeProfilingIntegration()],
tracesSampleRate: 1.0,
enabled: assertEnv(BlEnvironment.API_ENV) === "production",
});
11 changes: 0 additions & 11 deletions backend/src/server/instrument.mjs

This file was deleted.

192 changes: 42 additions & 150 deletions backend/src/server/server.ts
Original file line number Diff line number Diff line change
@@ -1,166 +1,58 @@
import "backend/src/server/instrument.mjs";

import { initAuthEndpoints } from "@backend/auth/initAuthEndpoints";
import { CollectionEndpointCreator } from "@backend/collection-endpoint/collection-endpoint-creator";
import { assertEnv, BlEnvironment } from "@backend/config/environment";
import { logger } from "@backend/logger/logger";
import * as Sentry from "@sentry/node";
import cors from "cors";
import express, { Express, json, Request, Response, Router } from "express";
import { Express, RequestHandler } from "express";
import session from "express-session";
import mongoose from "mongoose";
import passport from "passport";

export class Server {
public readonly app: Express = express();
private readonly router = Router();

constructor() {
logger.silly(" _ _ _");
logger.silly("| |__ | | __ _ _ __ (_)");
logger.silly("| '_ \\| |/ _` | '_ \\| |");
logger.silly("| |_) | | (_| | |_) | |");
logger.silly(String.raw`|_.__/|_|\__,_| .__/|_|`);
logger.silly(" |_|");

this.initialServerConfig();
this.initialPassportConfig();
initAuthEndpoints(this.router);
this.generateEndpoints();
this.connectToDbAndStartServer();
}

private async connectToDbAndStartServer() {
const mongoUri = assertEnv(BlEnvironment.MONGODB_URI);

mongoose.connection.on("disconnected", () => {
logger.error("mongoose connection was disconnected");
});

mongoose.connection.on("reconnected", () => {
logger.warn("mongoose connection was reconnected");
});

mongoose.connection.on("error", () => {
logger.error("mongoose connection has error");
});

await mongoose.connect(mongoUri, {
maxPoolSize: 10,
connectTimeoutMS: 10_000,
socketTimeoutMS: 45_000,
});
this.serverStart();
}

private initialServerConfig() {
this.app.use(json({ limit: "1mb" }));

process.on("unhandledRejection", (reason, p) => {
logger.error(
`unhandled rejection at: ${p}, reason: ${reason}` +
(reason instanceof Error ? `, stack: ${reason.stack}` : ""),
);
});
const whitelist = assertEnv(BlEnvironment.URI_WHITELIST).split(" ");
const allowedMethods = ["GET", "PUT", "PATCH", "POST", "DELETE"];
const allowedHeaders = [
"Content-Type",
"Authorization",
"X-Requested-With",
];

this.app.use(
assertEnv(BlEnvironment.API_ENV) === "production"
? cors({
origin: whitelist,
methods: allowedMethods,
allowedHeaders: allowedHeaders,
preflightContinue: true,
optionsSuccessStatus: 204,
})
: cors(),
);

this.app.use(
session({
resave: false,
saveUninitialized: false,
secret: assertEnv(BlEnvironment.SESSION_SECRET),
cookie: { secure: assertEnv(BlEnvironment.API_ENV) === "production" },
}),
);
this.app.use(passport.initialize());
this.app.use(passport.session());

const debugLogPath = (
request: Request,
res: Response,
next: () => void,
) => {
if (request.method !== "OPTIONS") {
// no point in showing all the preflight requests
logger.debug(`-> ${request.method} ${request.url}`);
if (
!(
request.url.includes("auth") &&
assertEnv(BlEnvironment.API_ENV) === "production"
)
) {
let body: string;
try {
body = JSON.stringify(request.body);
} catch {
body = request.body.toString("utf8");
}
export function getCorsHandler(): RequestHandler {
return assertEnv(BlEnvironment.API_ENV) === "production"
? cors({
origin: assertEnv(BlEnvironment.URI_WHITELIST).split(" "),
methods: ["GET", "PUT", "PATCH", "POST", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
preflightContinue: true,
optionsSuccessStatus: 204,
})
: cors();
}

logger.silly(`-> ${body}`);
}
}
next();
};
export function getSessionHandler(): RequestHandler {
return session({
resave: false,
saveUninitialized: false,
secret: assertEnv(BlEnvironment.SESSION_SECRET),
cookie: { secure: assertEnv(BlEnvironment.API_ENV) === "production" },
});
}

this.app.get("*", (request, res, next) => {
if (
request.headers["x-forwarded-proto"] !== "https" &&
assertEnv(BlEnvironment.API_ENV) === "production"
) {
res.redirect("https://" + request.hostname + request.url);
} else {
next();
}
});
export async function connectToDbAndStartServer(app: Express) {
mongoose.connection.on("disconnected", () => {
logger.error("mongoose connection was disconnected");
});

this.app.use(debugLogPath);
this.app.use(this.router);
}
mongoose.connection.on("reconnected", () => {
logger.warn("mongoose connection was reconnected");
});

private generateEndpoints() {
const collectionEndpointCreator = new CollectionEndpointCreator(
this.router,
);
collectionEndpointCreator.create();
}
mongoose.connection.on("error", () => {
logger.error("mongoose connection has error");
});

private initialPassportConfig() {
passport.serializeUser((user, done) => {
done(null, user);
});
await mongoose.connect(assertEnv(BlEnvironment.MONGODB_URI), {
maxPoolSize: 10,
connectTimeoutMS: 10_000,
socketTimeoutMS: 45_000,
});

passport.deserializeUser((user, done) => {
// @ts-expect-error fixme: auto ignored
done(null, user);
});
if (assertEnv(BlEnvironment.API_ENV) === "production") {
Sentry.profiler.startProfiler();
Sentry.setupExpressErrorHandler(app);
}

private serverStart() {
if (assertEnv(BlEnvironment.API_ENV) === "production") {
Sentry.profiler.startProfiler();
Sentry.setupExpressErrorHandler(this.app);
}
this.app.set("port", assertEnv(BlEnvironment.PORT));
this.app.listen(this.app.get("port"), () => {
logger.info("ready to take requests!");
});
}
app.set("port", assertEnv(BlEnvironment.PORT));
app.listen(app.get("port"), () => {
logger.info("ready to take requests!");
});
}
1 change: 1 addition & 0 deletions backend/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"resolveJsonModule": true,
"composite": true,
"declaration": true,
"outDir": "./dist",
Expand Down
3 changes: 3 additions & 0 deletions frontend/cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "@tsconfig/cypress/tsconfig.json"
}
1 change: 0 additions & 1 deletion frontend/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export default withSentryConfig(nextConfig, {
enabled: true,
},
tunnelRoute: "/monitoring",
hideSourceMaps: true,
disableLogger: true,
automaticVercelMonitors: true,
});
4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"license": "UNLICENSED",
"scripts": {
"dev": "next dev --turbopack",
"dev": "next dev",
"build": "next build",
"serve": "next start",
"lint": "next lint"
Expand Down Expand Up @@ -40,6 +40,8 @@
},
"devDependencies": {
"@testing-library/react": "^16.1.0",
"@tsconfig/cypress": "^1.0.2",
"@tsconfig/next": "^2.0.3",
"@types/draft-js": "^0.11.18",
"@types/react": "19.0.6",
"@types/react-draft-wysiwyg": "^1.13.8",
Expand Down
26 changes: 8 additions & 18 deletions frontend/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
{
"extends": "../tsconfig.base.json",
"extends": [
"../tsconfig.base.json",
"@tsconfig/next/tsconfig.json"
],
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"checkJs": true,
"noEmit": true,
"module": "esnext",
"moduleResolution": "bundler",
"jsx": "preserve",
"composite": true,
"declaration": true,
"incremental": true,
"plugins": [
{
"name": "next"
}
]
"declaration": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"],
"references": [
{
"path": "../shared"
}
]
],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"],
}
Loading

0 comments on commit 63437ed

Please sign in to comment.