From 4afd3a2c849eb653770e31fbb01bf3e950376ea3 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 20 Nov 2023 10:59:32 +0000 Subject: [PATCH] impl basic server and route handling --- package.json | 3 +- pnpm-lock.yaml | 69 +++++++++++++++++++++++++++++++ src/index.ts | 5 ++- src/route/impl/testRoute.ts | 14 +++++++ src/route/route.ts | 34 ++++++++++++++++ src/server/impl/ssrServer.ts | 19 +++++++++ src/server/server.ts | 79 ++++++++++++++++++++++++++++++++++++ 7 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 src/route/impl/testRoute.ts create mode 100644 src/route/route.ts create mode 100644 src/server/impl/ssrServer.ts create mode 100644 src/server/server.ts diff --git a/package.json b/package.json index b077681..7523a0b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "dist/index.js", "license": "MIT", "scripts": { - "build": "tsup src/index.ts --format cjs,esm --dts", + "build": "tsup src/index.ts --format cjs", "start": "node dist/index.js", "dev": "nodemon --exec ts-node src/index.ts" }, @@ -16,6 +16,7 @@ "typescript": "^5.2.2" }, "devDependencies": { + "@types/express": "^4.17.21", "nodemon": "^3.0.1", "ts-node": "^10.9.1" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c361b7f..bb479f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,9 @@ dependencies: version: 5.2.2 devDependencies: + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 nodemon: specifier: ^3.0.1 version: 3.0.1 @@ -394,11 +397,77 @@ packages: /@tsconfig/node16@1.0.4: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + dependencies: + '@types/connect': 3.4.38 + '@types/node': 20.9.2 + dev: true + + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + dependencies: + '@types/node': 20.9.2 + dev: true + + /@types/express-serve-static-core@4.17.41: + resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} + dependencies: + '@types/node': 20.9.2 + '@types/qs': 6.9.10 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + dev: true + + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.17.41 + '@types/qs': 6.9.10 + '@types/serve-static': 1.15.5 + dev: true + + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + dev: true + + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + dev: true + + /@types/mime@3.0.4: + resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + dev: true + /@types/node@20.9.2: resolution: {integrity: sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==} dependencies: undici-types: 5.26.5 + /@types/qs@6.9.10: + resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} + dev: true + + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 20.9.2 + dev: true + + /@types/serve-static@1.15.5: + resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + dependencies: + '@types/http-errors': 2.0.4 + '@types/mime': 3.0.4 + '@types/node': 20.9.2 + dev: true + /@types/webidl-conversions@7.0.3: resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} dev: false diff --git a/src/index.ts b/src/index.ts index d914c60..b7c9626 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,4 @@ -console.log("hi"); +import { SsrServer } from "./server/impl/ssrServer"; + +// Init the SSR Server +const server = new SsrServer(); diff --git a/src/route/impl/testRoute.ts b/src/route/impl/testRoute.ts new file mode 100644 index 0000000..8badb9f --- /dev/null +++ b/src/route/impl/testRoute.ts @@ -0,0 +1,14 @@ +import { Request, Response } from "express"; +import { Route } from "../route"; + +export default class TestRoute extends Route { + constructor() { + super({ + path: "/test", + }); + } + + async handle(req: Request, res: Response) { + res.send("Hello World!"); + } +} diff --git a/src/route/route.ts b/src/route/route.ts new file mode 100644 index 0000000..751cd19 --- /dev/null +++ b/src/route/route.ts @@ -0,0 +1,34 @@ +import { Request, Response } from "express"; + +type RouteType = { + /** + * The path to handle + */ + path: string; +}; + +export abstract class Route { + private path: string; + + constructor({ path }: RouteType) { + this.path = path; + console.log(`Created route handler for ${path}`); + } + + /** + * Handle the incoming request + * + * @param req the request + * @param res the response + */ + abstract handle(req: Request, res: Response): void; + + /** + * Get the path of the route + * + * @returns the path + */ + getPath() { + return this.path; + } +} diff --git a/src/server/impl/ssrServer.ts b/src/server/impl/ssrServer.ts new file mode 100644 index 0000000..6890c7c --- /dev/null +++ b/src/server/impl/ssrServer.ts @@ -0,0 +1,19 @@ +import TestRoute from "../../route/impl/testRoute"; +import Server from "../server"; + +export class SsrServer extends Server { + constructor() { + super({ + port: 3000, + routes: [new TestRoute()], + }); + } + + public preInit(): void { + console.log("preInit"); + } + + public postInit(): void { + console.log("postInit"); + } +} diff --git a/src/server/server.ts b/src/server/server.ts new file mode 100644 index 0000000..542fac2 --- /dev/null +++ b/src/server/server.ts @@ -0,0 +1,79 @@ +import express, { Express } from "express"; +import { Route } from "../route/route"; + +type ServerConfig = { + port: number; + routes?: Route[]; +}; + +export default class Server { + /** + * The port that this server is listening on. + */ + private port: number; + + /** + * The express server. + */ + private server: Express; + + /** + * The routes that this server is serving. + */ + private routes: Route[] = []; + + constructor({ port, routes }: ServerConfig) { + this.port = port; + if (routes) { + this.routes.push(...routes); + } + + // Create the express server + this.server = express(); + this.preInit(); + + // Handle the routes + for (const route of this.routes) { + this.server.all(route.getPath(), (req, res) => route.handle(req, res)); + } + console.log(`Registered ${this.routes.length} routes`); + + // Start listening on the specified port + this.server.listen(this.port, () => { + console.log(`Server listening on port ${this.port}`); + this.postInit(); + }); + } + + /** + * Returns the port that this server is listening on. + * + * @returns {number} the port + */ + public getPort() { + return this.port; + } + + /** + * Registers a list of routes. + * + * @param route the routes to register + */ + public registerRoute(...routes: Route[]) { + this.routes.push(...routes); + } + + public getRoute(path: string): Route | undefined { + return this.routes.find((route) => route.getPath() === path); + } + + /** + * Gets called before the server starts listening. + */ + public preInit(): void {} + + /** + * Gets called after the server starts listening. + */ + public postInit(): void {} +}