diff --git a/.env-example b/.env-example new file mode 100644 index 0000000..4aed880 --- /dev/null +++ b/.env-example @@ -0,0 +1 @@ +INFISICAL_TOKEN=example \ No newline at end of file diff --git a/.gitea/workflow/publish.yml b/.gitea/workflow/publish.yml new file mode 100644 index 0000000..eb55f94 --- /dev/null +++ b/.gitea/workflow/publish.yml @@ -0,0 +1,36 @@ +name: Publish Docker Images + +on: + push: + branches: + - "master" + +jobs: + docker: + runs-on: ubuntu-22.04 + container: fascinated/docker-images:node-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Login to Repo + uses: docker/login-action@v2 + with: + username: ${{ secrets.REPO_USERNAME }} + password: ${{ secrets.REPO_TOKEN }} + + - name: Build and Push (Node) + uses: docker/build-push-action@v4 + with: + push: true + context: ./apps/node + file: ./apps/node/Dockerfile + tags: fascinated/proxy:node-latest + + - name: Build and Push (Proxy) + uses: docker/build-push-action@v4 + with: + push: true + context: ./apps/proxy + file: ./apps/proxy/Dockerfile + tags: fascinated/proxy:proxy-latest diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..34e503e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "configurations": [ + { + "name": "Docker Node.js Launch", + "type": "docker", + "request": "launch", + "preLaunchTask": "docker-run: debug", + "platform": "node", + "node": { + "package": "${workspaceFolder}/apps/node/package.json", + "localRoot": "${workspaceFolder}/apps/node" + } + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..f8bcf15 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "docker-build", + "label": "docker-build", + "platform": "node", + "dockerBuild": { + "dockerfile": "${workspaceFolder}/apps/node/Dockerfile", + "context": "${workspaceFolder}/apps/node", + "pull": true + }, + "node": { + "package": "${workspaceFolder}/apps/node/package.json" + } + }, + { + "type": "docker-run", + "label": "docker-run: release", + "dependsOn": ["docker-build"], + "platform": "node", + "node": { + "package": "${workspaceFolder}/apps/node/package.json" + } + }, + { + "type": "docker-run", + "label": "docker-run: debug", + "dependsOn": ["docker-build"], + "dockerRun": { + "env": { + "DEBUG": "*", + "NODE_ENV": "development" + } + }, + "node": { + "package": "${workspaceFolder}/apps/node/package.json", + "enableDebugging": true + } + } + ] +} diff --git a/apps/node/.dockerignore b/apps/node/.dockerignore new file mode 100644 index 0000000..809ba83 --- /dev/null +++ b/apps/node/.dockerignore @@ -0,0 +1,24 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/apps/node/.env-example b/apps/node/.env-example index f3e4346..34b4c76 100644 --- a/apps/node/.env-example +++ b/apps/node/.env-example @@ -1,4 +1,4 @@ NODE_ID=1 -API_PORT=3000 +API_PORT=3001 INFISICAL_TOKEN=YOUR_TOKEN \ No newline at end of file diff --git a/apps/node/Dockerfile b/apps/node/Dockerfile new file mode 100644 index 0000000..e82a399 --- /dev/null +++ b/apps/node/Dockerfile @@ -0,0 +1,10 @@ +FROM fascinated/docker-images:node-pnpm-latest + +WORKDIR /usr/src/app +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install +COPY . . +RUN pnpm run build +ENV PORT=3000 +EXPOSE 3000 +CMD [ "pnpm", "start" ] \ No newline at end of file diff --git a/apps/node/src/routes/proxy.ts b/apps/node/src/routes/proxy.ts index 5cffc53..1243b29 100644 --- a/apps/node/src/routes/proxy.ts +++ b/apps/node/src/routes/proxy.ts @@ -9,6 +9,7 @@ export default class ProxyRoute extends Route { } async handle(req: Request, res: Response) { + const before = Date.now(); const json = req.body; const secret = json.secret; if (!secret) { @@ -49,6 +50,13 @@ export default class ProxyRoute extends Route { delete headers["cf-ray"]; delete headers["alt-svc"]; + // Misc headers + delete headers["transfer-encoding"]; + + // Add node specific headers + headers["x-proxy-node"] = process.env.NODE_ID; + headers["x-proxy-response-time"] = Date.now() - before + "ms"; + // Return the JSON response res.status(response.status).set(headers).json(data); } diff --git a/apps/proxy/.dockerignore b/apps/proxy/.dockerignore new file mode 100644 index 0000000..809ba83 --- /dev/null +++ b/apps/proxy/.dockerignore @@ -0,0 +1,24 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/apps/proxy/.env-example b/apps/proxy/.env-example new file mode 100644 index 0000000..20ba0e9 --- /dev/null +++ b/apps/proxy/.env-example @@ -0,0 +1,2 @@ +API_PORT=3000 +INFISICAL_TOKEN=YOUR_TOKEN \ No newline at end of file diff --git a/apps/proxy/Dockerfile b/apps/proxy/Dockerfile new file mode 100644 index 0000000..e82a399 --- /dev/null +++ b/apps/proxy/Dockerfile @@ -0,0 +1,10 @@ +FROM fascinated/docker-images:node-pnpm-latest + +WORKDIR /usr/src/app +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install +COPY . . +RUN pnpm run build +ENV PORT=3000 +EXPOSE 3000 +CMD [ "pnpm", "start" ] \ No newline at end of file diff --git a/apps/proxy/package-lock.json b/apps/proxy/package-lock.json new file mode 100644 index 0000000..def2be6 --- /dev/null +++ b/apps/proxy/package-lock.json @@ -0,0 +1,241 @@ +{ + "name": "node", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "node", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } + }, + "../../node_modules/.pnpm/typescript@5.2.2/node_modules/typescript": { + "version": "5.2.2", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "devDependencies": { + "@esfx/canceltoken": "^1.0.0", + "@octokit/rest": "^19.0.13", + "@types/chai": "^4.3.4", + "@types/fs-extra": "^9.0.13", + "@types/glob": "^8.1.0", + "@types/microsoft__typescript-etw": "^0.1.1", + "@types/minimist": "^1.2.2", + "@types/mocha": "^10.0.1", + "@types/ms": "^0.7.31", + "@types/node": "latest", + "@types/source-map-support": "^0.5.6", + "@types/which": "^2.0.1", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@typescript-eslint/utils": "^6.0.0", + "azure-devops-node-api": "^12.0.0", + "c8": "^7.14.0", + "chai": "^4.3.7", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "del": "^6.1.1", + "diff": "^5.1.0", + "esbuild": "^0.18.1", + "eslint": "^8.22.0", + "eslint-formatter-autolinkable-stylish": "^1.2.0", + "eslint-plugin-local": "^1.0.0", + "eslint-plugin-no-null": "^1.0.2", + "eslint-plugin-simple-import-sort": "^10.0.0", + "fast-xml-parser": "^4.0.11", + "fs-extra": "^9.1.0", + "glob": "^8.1.0", + "hereby": "^1.6.4", + "jsonc-parser": "^3.2.0", + "minimist": "^1.2.8", + "mocha": "^10.2.0", + "mocha-fivemat-progress-reporter": "^0.1.0", + "ms": "^2.1.3", + "node-fetch": "^3.2.10", + "source-map-support": "^0.5.21", + "tslib": "^2.5.0", + "typescript": "^5.0.2", + "which": "^2.0.2" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + }, + "node_modules/@types/node": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", + "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "resolved": "../../node_modules/.pnpm/typescript@5.2.2/node_modules/typescript", + "link": true + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "peer": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/apps/proxy/package.json b/apps/proxy/package.json new file mode 100644 index 0000000..2c007ee --- /dev/null +++ b/apps/proxy/package.json @@ -0,0 +1,20 @@ +{ + "name": "proxy", + "version": "1.0.0", + "description": "The proxy server for the project", + "main": "dist/index.js", + "author": "fascinated", + "license": "MIT", + "scripts": { + "build": "tsc", + "start": "node dist/index.js", + "dev": "nodemon --exec ts-node src/index.ts" + }, + "dependencies": { + "axios": "^1.6.1", + "server": "workspace:*", + "utils": "workspace:*", + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } +} diff --git a/apps/proxy/src/index.ts b/apps/proxy/src/index.ts new file mode 100644 index 0000000..06748ec --- /dev/null +++ b/apps/proxy/src/index.ts @@ -0,0 +1,39 @@ +import dotenv from "dotenv"; +import { RouteManager, RouteMessages, createServer } from "server"; +import { checkEnvironmentVariables } from "utils"; +import NodeManager from "./node/nodeManager"; +import ProxyRoute from "./routes/proxy"; +import { initSecrets } from "./secrets"; + +dotenv.config(); // load .env file + +// Check environment variables +const envVarsValid = checkEnvironmentVariables("API_PORT", "INFISICAL_TOKEN"); +if (!envVarsValid) { + process.exit(1); +} + +/** + * The node manager for all of the loaded nodes + */ +export const nodeManager = new NodeManager(); + +const server = createServer({ + port: process.env.API_PORT || 3000, + onLoaded: async () => { + await initSecrets(process.env.INFISICAL_TOKEN!); // Load the infisical secrets + + const routeManager = new RouteManager(); + routeManager.addRoute(new ProxyRoute()); + + server.all("*", (req, res) => { + // Handle all paths to the proxy route + const routes = routeManager.getRoutes(); + if (!routes || routes.length === 0 || !routes[0]) { + res.status(500).json(RouteMessages.internalServerError("No routes")); + return; + } + routes[0].handle(req, res); + }); + }, +}); diff --git a/apps/proxy/src/node/node.ts b/apps/proxy/src/node/node.ts new file mode 100644 index 0000000..5d477ad --- /dev/null +++ b/apps/proxy/src/node/node.ts @@ -0,0 +1,48 @@ +import axios from "axios"; +import { PROXY_SECRET } from "../secrets"; + +type NodeOptions = { + /** + * The URL to node + */ + url: string; +}; + +export default class Node { + /** + * The URL to node + */ + private url: string; + + constructor({ url }: NodeOptions) { + this.url = url; + + // todo: ping node and check status + } + + /** + * Get the URL to node + * + * @returns the URL + */ + getUrl() { + return this.url; + } + + /** + * Fetch a URL from the node. + * The node will fetch the URL and return the response. + * + * @param url the URL to fetch + */ + async fetch(url: string) { + const response = await axios.get(this.getUrl() + "/proxy", { + data: { + url: url, + secret: PROXY_SECRET, + }, + }); + + return response; + } +} diff --git a/apps/proxy/src/node/nodeManager.ts b/apps/proxy/src/node/nodeManager.ts new file mode 100644 index 0000000..87da5a5 --- /dev/null +++ b/apps/proxy/src/node/nodeManager.ts @@ -0,0 +1,28 @@ +import Node from "./node"; + +export default class NodeManager { + /** + * The nodes + */ + private nodes: Node[]; + + constructor() { + const nodes = process.env.NODES; + if (!nodes) { + throw new Error("No nodes found in environment variable"); + } + + const nodeUrls = nodes.split(","); + this.nodes = nodeUrls.map((url) => new Node({ url })); + } + + /** + * Get a random node + * + * @returns the node + */ + getRandomNode() { + const randomIndex = Math.floor(Math.random() * this.nodes.length); + return this.nodes[randomIndex]; + } +} diff --git a/apps/proxy/src/routes/proxy.ts b/apps/proxy/src/routes/proxy.ts new file mode 100644 index 0000000..1f0d214 --- /dev/null +++ b/apps/proxy/src/routes/proxy.ts @@ -0,0 +1,31 @@ +import { Request, Response } from "express"; +import { Route, RouteMessages } from "server"; +import { nodeManager } from ".."; + +export default class ProxyRoute extends Route { + constructor() { + super({ path: "/" }); + } + + async handle(req: Request, res: Response) { + const url = req.url.substring(1); + if (!url) { + res.status(400).json(RouteMessages.badRequest("No URL provided")); + return; + } + + const node = nodeManager.getRandomNode(); + if (!node) { + res + .status(500) + .json(RouteMessages.internalServerError("No nodes available")); + return; + } + + const response = await node.fetch(url); + const data = response.data; + + // Return the JSON response + res.status(response.status).set(response.headers).json(data); + } +} diff --git a/apps/proxy/src/secrets.ts b/apps/proxy/src/secrets.ts new file mode 100644 index 0000000..4f40e3b --- /dev/null +++ b/apps/proxy/src/secrets.ts @@ -0,0 +1,20 @@ +import { createInfisicalClient } from "utils"; + +export let PROXY_SECRET: string; + +/** + * Initialize the secrets from Infisical + */ +export async function initSecrets(token: string) { + console.log("Initializing secrets..."); + + const infisicalClient = createInfisicalClient(token); + const proxySecret = (await infisicalClient.getSecret("PROXY_SECRET")) + .secretValue; + + if (!proxySecret) { + throw new Error("PROXY_SECRET not set in Infisical"); + } + + PROXY_SECRET = proxySecret; +} diff --git a/apps/proxy/tsconfig.json b/apps/proxy/tsconfig.json new file mode 100644 index 0000000..99c5ded --- /dev/null +++ b/apps/proxy/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "tsconfig/server.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ada2fb1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +version: "3.8" +services: + proxy: + image: fascinated/proxy:proxy-latest + container_name: Proxy + restart: always + ports: + - 3000:3000 + environment: + API_PORT: 3000 + INFISICAL_TOKEN: ${INFISICAL_TOKEN} + + # Add your nodes here (eg: http://localhost:3001,http://localhost:3002, etc.) + NODES: http://node-1:3000,http://node-2:3000 + + node-1: + image: fascinated/proxy:node-latest + container_name: Node 1 + restart: always + # Uncomment the ports if you want to access the node directly + #ports: + # - 3000:3000 + environment: + API_PORT: 3000 + INFISICAL_TOKEN: ${INFISICAL_TOKEN} + NODE_ID: 1 + + node-2: + image: fascinated/proxy:node-latest + container_name: Node 1 + restart: always + # Uncomment the ports if you want to access the node directly + #ports: + # - 3000:3000 + environment: + API_PORT: 3000 + INFISICAL_TOKEN: ${INFISICAL_TOKEN} + NODE_ID: 1 diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index b55c9ab..9f7d41d 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1,9 +1,11 @@ import { badRequest } from "./messages/badRequest"; +import { internalServerError } from "./messages/internalServerError"; import { unknownRoute } from "./messages/unknownRoute"; export const RouteMessages = { unknownRoute: unknownRoute, badRequest: badRequest, + internalServerError: internalServerError, }; export * from "./route/route"; diff --git a/packages/server/src/messages/internalServerError.ts b/packages/server/src/messages/internalServerError.ts new file mode 100644 index 0000000..e768203 --- /dev/null +++ b/packages/server/src/messages/internalServerError.ts @@ -0,0 +1,10 @@ +import { baseMessage } from "./baseMessage"; + +/** + * Creates a response for an internal server error + * + * @returns the internal server error message + */ +export function internalServerError(message: string) { + return baseMessage(true, message); +} diff --git a/packages/server/src/route/routeManager.ts b/packages/server/src/route/routeManager.ts index 6475266..78771f9 100644 --- a/packages/server/src/route/routeManager.ts +++ b/packages/server/src/route/routeManager.ts @@ -19,7 +19,7 @@ export class RouteManager { * @returns the route or undefined if not found */ getRoute(path: string) { - return this.routes.find((route) => route.getPath() === path); + return this.routes.find((route) => path.startsWith(route.getPath())); } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c9d7b9..0f07dc6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,6 +55,24 @@ importers: specifier: workspace:* version: link:../../packages/utils + apps/proxy: + dependencies: + axios: + specifier: ^1.6.1 + version: 1.6.1 + server: + specifier: workspace:* + version: link:../../packages/server + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@20.5.2)(typescript@5.2.2) + typescript: + specifier: ^5.2.2 + version: 5.2.2 + utils: + specifier: workspace:* + version: link:../../packages/utils + packages/eslint-config-custom: devDependencies: '@vercel/style-guide':