diff --git a/package-lock.json b/package-lock.json index bcc2a27..48f2e8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@eslint/js": "^9.18.0", "@playwright/test": "^1.49.1", "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/adapter-node": "^5.2.12", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@vitest/browser": "^3.2.3", @@ -404,6 +405,126 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz", + "integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs/node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", + "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.44.1", "cpu": [ @@ -444,6 +565,22 @@ "@sveltejs/kit": "^2.0.0" } }, + "node_modules/@sveltejs/adapter-node": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.12.tgz", + "integrity": "sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-commonjs": "^28.0.1", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "rollup": "^4.9.5" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.4.0" + } + }, "node_modules/@sveltejs/kit": { "version": "2.22.2", "dev": true, @@ -592,6 +729,13 @@ "undici-types": "~7.8.0" } }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.35.0", "dev": true, @@ -1173,6 +1317,13 @@ "dev": true, "license": "MIT" }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "dev": true, @@ -1651,6 +1802,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "dev": true, @@ -1686,6 +1847,19 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ignore": { "version": "5.3.2", "dev": true, @@ -1717,6 +1891,22 @@ "node": ">=0.8.19" } }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "dev": true, @@ -1736,6 +1926,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "dev": true, @@ -2053,6 +2250,13 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, "node_modules/pathe": { "version": "2.0.3", "dev": true, @@ -2328,6 +2532,27 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "dev": true, @@ -2525,6 +2750,19 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/svelte": { "version": "5.34.8", "dev": true, diff --git a/package.json b/package.json index 5ce5f71..2acf39e 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@eslint/js": "^9.18.0", "@playwright/test": "^1.49.1", "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/adapter-node": "^5.2.12", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@vitest/browser": "^3.2.3", diff --git a/src/lib/server/broker-utils/FileSystem/Endpoint.ts b/src/lib/server/broker-utils/FileSystem/Endpoint.ts new file mode 100644 index 0000000..5707c5d --- /dev/null +++ b/src/lib/server/broker-utils/FileSystem/Endpoint.ts @@ -0,0 +1,30 @@ +import type { IEndpointFS } from "$lib/server/broker-utils/FileSystem/endpoints/endpoints" + +export class EndpointBrokerManager { + + private static initialized = false + private static endpoints: Map + private static lastNginxReload: Date = new Date() + + public static get ready() { + return EndpointBrokerManager.initialized + } + + public static init() { + // TODO: Read all files + + // TODO: QUICK parse them + + // TODO: Initialize a file watcher + + } + + + public async getEndpointByName(name: string): IEndpoint { + + + + } + +} + diff --git a/src/lib/server/broker-utils/FileSystem/SSLTerminations.ts b/src/lib/server/broker-utils/FileSystem/SSLTerminations.ts index e69de29..7e2b9aa 100644 --- a/src/lib/server/broker-utils/FileSystem/SSLTerminations.ts +++ b/src/lib/server/broker-utils/FileSystem/SSLTerminations.ts @@ -0,0 +1,57 @@ +import { type ISSLTerminationBroker, type SSLTerminationChanges } from "$lib/server/classes/endpoints/ssl-termination-endpoint"; +import type { NginxProtocol } from "$lib/server/enums/protocols"; + +export class SSLTerminationBroker implements ISSLTerminationBroker { + + private static initialized = false + + async init(): Promise { + + if (SSLTerminationBroker.initialized) { + // UGLY: more specific + throw new Error("SSLTerminationBroker was already initialized") + } + + } + + + async createSSLTerminationSimple( + name: string, + servicePort: number, + serviceEndpoint: string, + certificateURI: string, + privateKeyURI: string + ): Promise { + throw new Error("Method not implemented."); + } + + + async createSSLTerminationComplete( + name: string, + sslPort: number, + clearPort: number, + servicePort: number, + serviceEndpoint: string, + certificateURI: string, + privateKeyURI: string + ): Promise { + throw new Error("Method not implemented."); + } + + async getSSLTerminationByName(name: string): Promise { + throw new Error("Method not implemented."); + } + + async modifySSLTerminationByName(name: string, changes: SSLTerminationChanges): Promise { + throw new Error("Method not implemented."); + } + + async deleteSSLTerminationByName(name: string): Promise { + throw new Error("Method not implemented."); + } + + async getAllSSLTerminations(): Promise { + throw new Error("Method not implemented."); + } + +} \ No newline at end of file diff --git a/src/lib/server/broker-utils/FileSystem/endpoints/endpoints.ts b/src/lib/server/broker-utils/FileSystem/endpoints/endpoints.ts new file mode 100644 index 0000000..6c73ac9 --- /dev/null +++ b/src/lib/server/broker-utils/FileSystem/endpoints/endpoints.ts @@ -0,0 +1,37 @@ +import { EndpointType } from "$lib/server/enums/endpoints"; +import type { FileStats } from "$lib/server/utils/filesystem-utils"; + +export interface IEndpointFS { + + /** + * Specifies which Enpoint this is + */ + type: EndpointType + /** File name */ + name: string + /** + * File stats, + * useful to understand + * whether the file has been actually + * parsed by nginx during last reload + */ + stats: FileStats + /** File path */ + path: string + /** Hash of the file */ + hash: string + + /** converts the IEndpoint to */ + toConf(): string + + ports(): number[] + +} + +export type FSHeader = { + name: string, + stats: FileStats + path: string, + hash: string +} + diff --git a/src/lib/server/broker-utils/FileSystem/endpoints/ssltermination-fs.ts b/src/lib/server/broker-utils/FileSystem/endpoints/ssltermination-fs.ts new file mode 100644 index 0000000..ce141e6 --- /dev/null +++ b/src/lib/server/broker-utils/FileSystem/endpoints/ssltermination-fs.ts @@ -0,0 +1,198 @@ +import { EndpointType } from "$lib/server/enums/endpoints" +import { httpVersion, proxyProtocol, secureProtocol, type NginxProtocol } from "$lib/server/enums/protocols" +import { validatePort } from "$lib/server/utils/ports-utils" +import type { Stats } from "fs" +import type { FSHeader, IEndpointFS } from "./endpoints" +import { createHeader } from "../utils" + +export class SSLTerminationFS implements IEndpointFS { + + private static __type = EndpointType.SSL_TERMINATION + + public get type() { + return SSLTerminationFS.__type + } + + + public name: string + public stats: Stats + public path: string + public hash: string + public sslPort: number + public clearPort: number + public servicePort: number + public serviceEndpoint: string + public protocol: NginxProtocol + public certificateURI: string + public privateKeyURI: string + + + + constructor( + name: string, + stats: Stats, + path: string, + hash: string, + sslPort: number, + clearPort: number, + servicePort: number, + serviceEndpoint: string, + protocol: NginxProtocol, + certificateURI: string, + privateKeyURI: string + ) { + + validatePort(sslPort) + validatePort(clearPort) + validatePort(servicePort) + + this.name = name + this.stats = stats + this.path = path + this.hash = hash + this.sslPort = sslPort + this.clearPort = clearPort + this.servicePort = servicePort + this.serviceEndpoint = serviceEndpoint + this.protocol = protocol + this.certificateURI = certificateURI + this.privateKeyURI = privateKeyURI + + } + + toConf(): string { + + const HEADER = this.createHeader() + const SSL_SERVER = this.createSSLServer() + const CLEAR_SERVER = this.createClearServer() + + const CONF = HEADER + SSL_SERVER + CLEAR_SERVER + + return CONF + } + + ports(): number[]{ + + return [ + this.sslPort, + this.clearPort + ] + + } + + public static parseConf(fsHeader: FSHeader, conf: string) : SSLTerminationFS { + // TODO: parse header + } + + + private createHeader() { + return createHeader( + this, + [ + { + key: "NAME", + value: this.name + }, + { + key: "PROTOCOL", + value: this.protocol + }, + { + key: "SSL_PORT", + value: this.sslPort + }, + { + key: "CLEAR_PORT", + value: this.clearPort + }, + { + key: "SERVICE_PORT", + value: this.servicePort + }, + { + key: "SERVICE_ENDPOINT", + value: this.serviceEndpoint + }, + { + key: "CERTIFICATE_PATH", + value: this.certificateURI + }, + { + key: "KEY_PATH", + value: this.privateKeyURI + } + ] + ) + } + + // UGLY: refactor into a flexible method + private createSSLServer() { + + const CLEAR_PROTOCOL = `${this.protocol}` + const HTTP_VERSION = httpVersion(this.protocol) + const PROXY_OPTION = proxyProtocol(this.protocol) + + // UGLY: put to constants + let conf = [ + "server {\n", + "\tmore_clear_headers Server;\n", + `\tlisten ${this.sslPort};` + ] + + if (HTTP_VERSION !== 1) { + conf.push( + `\thttp${HTTP_VERSION} on;` + ) + } + + // TODO: check if we should support less protocols + conf.push( + "\n", + "\tlocation / {", + `\t\t${PROXY_OPTION} ${CLEAR_PROTOCOL}://127.0.0.1:${this.clearPort};`, + "\t}", + "\n", + `ssl_certificate ${this.certificateURI};`, + `ssl_certificate_key ${this.privateKeyURI};`, + "\tssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;", + "\n", + "}" + ) + + return conf.join("\n") + } + + private createClearServer() { + + const SSL_PROTOCOL = secureProtocol(this.protocol) + const HTTP_VERSION = httpVersion(this.protocol) + const PROXY_OPTION = proxyProtocol(this.protocol) + + // UGLY: put to constants + let conf = [ + "server {\n", + "\tmore_clear_headers Server;\n", + `\tlisten ${this.clearPort};` + ] + + if (HTTP_VERSION !== 1) { + conf.push( + `\thttp${HTTP_VERSION} on;` + ) + } + + // TODO: check if we should support less protocols + conf.push( + "\n", + "\tlocation / {", + `\t\t${PROXY_OPTION} ${SSL_PROTOCOL}://${this.serviceEndpoint}:${this.servicePort};`, + "\t}", + "\n", + "}" + ) + + return conf.join("\n") + } + + +} \ No newline at end of file diff --git a/src/lib/server/broker-utils/FileSystem/utils.ts b/src/lib/server/broker-utils/FileSystem/utils.ts new file mode 100644 index 0000000..33ca074 --- /dev/null +++ b/src/lib/server/broker-utils/FileSystem/utils.ts @@ -0,0 +1,77 @@ +import { EndpointType, matchEndpoint } from "$lib/server/enums/endpoints" +import type { FSHeader, IEndpointFS } from "./endpoints/endpoints" +import { SSLTerminationFS } from "./endpoints/ssltermination-fs" + +export const HEADER_BOUNDARY = "**********************************************************" +export const HEADER_UPPER = `/*${HEADER_BOUNDARY}` +export const HEADER_LOWER = `${HEADER_BOUNDARY}*/` + +export type HeaderKeyValueFS = { + key: string, + value: any +} + +export function parseConf( + fsHeader: FSHeader, + conf: string +): IEndpointFS { + + const TYPE = parseDefaultConf(conf) + + switch (TYPE) { + + case EndpointType.SSL_TERMINATION: + return SSLTerminationFS.parseConf(fsHeader, conf) + default: + // TODO: add manual endpoint + } +} + +export function createHeader(endpoint: IEndpointFS, variables?: HeaderKeyValueFS[]): string { + + if (!variables) { + variables = [] + } + + let header = [ + HEADER_UPPER, + "SSL-SNIFFER AUTOMATICALLY GENERATED", + `TYPE: ${endpoint.type}`, + HEADER_BOUNDARY + ] + + for (const variable of variables) { + header.push( + `${variable.key}: ${variable.value}` + ) + } + + + header.push(HEADER_LOWER) + return header.join("\n") + +} + +function parseDefaultConf(conf: string): EndpointType { + /** Row (remember array order) where we find the type */ + const TYPE_DATA_ROW = 2 + /** What separator was used */ + const TYPE_DATA_SEPARATOR = " " + /** Whether the value is on the left(0) of right(1) */ + const TYPE_DATA_ARRAY_POSITION = 1 + const DEFAULT_LABEL = EndpointType.MANUAL + + const confLines = conf.split("\n") + + if (confLines.length < 4 ) { + return matchEndpoint(DEFAULT_LABEL) + } + + // + const label = confLines[TYPE_DATA_ROW] + .split(TYPE_DATA_SEPARATOR)[TYPE_DATA_ARRAY_POSITION] + + return matchEndpoint(label) + + +} \ No newline at end of file diff --git a/src/lib/server/classes/endpoints.ts b/src/lib/server/classes/endpoints/endpoints-interfaces.ts similarity index 79% rename from src/lib/server/classes/endpoints.ts rename to src/lib/server/classes/endpoints/endpoints-interfaces.ts index 04e65ef..fee0821 100644 --- a/src/lib/server/classes/endpoints.ts +++ b/src/lib/server/classes/endpoints/endpoints-interfaces.ts @@ -4,7 +4,5 @@ export interface IEndpoint { type: EndpointType name: string - path: string - hash: string } \ No newline at end of file diff --git a/src/lib/server/classes/ssl-termination-endpoint.ts b/src/lib/server/classes/endpoints/ssl-termination-endpoint.ts similarity index 91% rename from src/lib/server/classes/ssl-termination-endpoint.ts rename to src/lib/server/classes/endpoints/ssl-termination-endpoint.ts index ee64d0d..77b4610 100644 --- a/src/lib/server/classes/ssl-termination-endpoint.ts +++ b/src/lib/server/classes/endpoints/ssl-termination-endpoint.ts @@ -1,9 +1,7 @@ import { EndpointType } from "$lib/server/enums/endpoints" import type { NginxProtocol } from "$lib/server/enums/protocols" -import { doesFileExist } from "$lib/server/utils/filesystem-utils" -import { isPortAvailable, validatePort } from "$lib/server/utils/ports-utils" -import type { init } from "../../../hooks.server" -import type { IEndpoint } from "./endpoints" +import { validatePort } from "$lib/server/utils/ports-utils" +import type { IEndpoint } from "./endpoints-interfaces" // TODO: inherit from a super class export interface ISSLTerminationBroker { @@ -63,12 +61,14 @@ export interface ISSLTerminationBroker { * discouraged in favor of the Factory methods as it does * more checks than this class */ -export class SSLTermination implements IEndpoint { +export class SSLTermination implements IEndpoint { - private static _type: EndpointType = EndpointType.SSL_TERMINATION + private static __type = EndpointType.SSL_TERMINATION - public path: string - public hash: string + public get type() { + return SSLTermination.__type + } + public name: string public sslPort: number public clearPort: number @@ -78,14 +78,10 @@ export class SSLTermination implements IEndpoint { public certificateURI: string public privateKeyURI: string - public get type() { - return SSLTermination._type - } + constructor( name: string, - path: string, - hash: string, sslPort: number, clearPort: number, servicePort: number, @@ -100,8 +96,6 @@ export class SSLTermination implements IEndpoint { validatePort(servicePort) this.name = name - this.path = path - this.hash = hash this.sslPort = sslPort this.clearPort = clearPort this.servicePort = servicePort @@ -115,7 +109,7 @@ export class SSLTermination implements IEndpoint { } -type SSLTerminationChanges = { +export type SSLTerminationChanges = { name?: string, sslPort?: number, clearPort?: number, @@ -126,6 +120,8 @@ type SSLTerminationChanges = { privateKeyURI?: string } + + export class SSLTerminationEndpointApp { private static initialized: boolean = false diff --git a/src/lib/server/enums/endpoints.ts b/src/lib/server/enums/endpoints.ts index 8ad991b..e98fe31 100644 --- a/src/lib/server/enums/endpoints.ts +++ b/src/lib/server/enums/endpoints.ts @@ -1,6 +1,17 @@ export enum EndpointType { - SSL_TERMINATION = "SSL-Termination", + SSL_TERMINATION = "SSLTermination", MANUAL = "Manual" +} + +export function matchEndpoint(label: string) { + + switch(label) { + + case "SSLTermination": + return EndpointType.SSL_TERMINATION + default: + return EndpointType.MANUAL + } } \ No newline at end of file diff --git a/src/lib/server/enums/protocols.ts b/src/lib/server/enums/protocols.ts index 3efc362..852b316 100644 --- a/src/lib/server/enums/protocols.ts +++ b/src/lib/server/enums/protocols.ts @@ -3,4 +3,31 @@ export enum NginxProtocol { HTTP2 = "http", QUIC = "http", GRPC = "grpc" -} \ No newline at end of file +} + +// UGLY: move these fnction into utils +export function httpVersion(protocol: NginxProtocol): number { + + switch(protocol) { + case NginxProtocol.HTTP2: + case NginxProtocol.GRPC: + return 2 + case NginxProtocol.QUIC: + return 3 + default: + return 1 + } +} + +export function secureProtocol(protocol: NginxProtocol) { + return `${protocol}s` +} + +export function proxyProtocol(protocol: NginxProtocol) { + switch(protocol) { + case NginxProtocol.GRPC: + return "grpc_pass" + default: + return "proxy_pass" + } +} diff --git a/src/lib/server/utils/filesystem-utils.ts b/src/lib/server/utils/filesystem-utils.ts index a25a442..8b8a980 100644 --- a/src/lib/server/utils/filesystem-utils.ts +++ b/src/lib/server/utils/filesystem-utils.ts @@ -1,6 +1,7 @@ -import type { OpenMode } from 'node:fs'; +import { Stats, type OpenMode } from 'node:fs'; import * as Node from 'node:fs/promises'; +export type FileStats = Stats export class FileHandle { @@ -38,6 +39,16 @@ export class FileHandle { } + public async lastChange() { + const stats = await Node.stat(this.path) + return stats.mtime + } + + public async getStats() { + const stats = await Node.stat(this.path) + return stats + } + } diff --git a/src/routes/api/program/register/+server.ts b/src/routes/api/program/register/+server.ts new file mode 100644 index 0000000..4f8f2e8 --- /dev/null +++ b/src/routes/api/program/register/+server.ts @@ -0,0 +1,114 @@ +import { error, json, redirect, text, type Cookies } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; +import { UserApp, User } from '$lib/server/classes/users'; +import { SessionApp, Session } from '$lib/server/classes/sessions'; +import { AppData } from '$lib/server/classes/appdata'; +import { logger } from '$lib/server/utils/logger'; +import { DEBUG } from '$lib/server/utils/constants'; + +/*********************************************************** + * + * Author: Christian Risi 26/06/2025 + * + * + * + * +***********************************************************/ + +export const POST: RequestHandler = async ({ request, locals, cookies }) => { + + const req: Request = request + const local: App.Locals = locals + const cookie: Cookies = cookies + + const session = local.session + + if (DEBUG) { + return redirect(307, "api/register") + } + + if (!session) { + // The user is not providing credentials + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/403 + return error(401, "Unauthorized") + } + + + let userJson: { username: string, password: string } + let tmpJSON: any + + try { + tmpJSON = await req.json() + } catch { + return error(400, "Bad Request") + } + + if (!tmpJSON.username || !tmpJSON.password) { + return error(400, "Bad Request") + } + + userJson = tmpJSON + + // If this fails, should be a 400? + let user: User + try { + user = await UserApp.createUser( + userJson.username, + userJson.password + ) + } catch { + return error(400, "The user already exists") + } + + + let newSession + try { + newSession = SessionApp.createSessionFromUserID( + user.userID + ) + } catch(err){ + logger.debug(`error: ${err}`, "API Register") + return error(500, "Internal Server Error") + } + + + const sessionCookie = await new AppData( + newSession, + user + ).toCookie() + + cookie.set( + "session", + sessionCookie, + { + path: "/" + } + ) + + const res = new Response( + null, + { + status: 201, + statusText: "Created", + } + ) + + return res + +} + +export const fallback: RequestHandler = async ({ request }) => { + + // TODO: return method not allowed + const res = new Response( + null, + { + status: 405, + statusText: "Method Not Allowed", + headers: { + Allow: "POST" + } + } + ) + return res +}; \ No newline at end of file diff --git a/src/routes/api/register/+server.ts b/src/routes/api/register/+server.ts index 793b06e..60b32e4 100644 --- a/src/routes/api/register/+server.ts +++ b/src/routes/api/register/+server.ts @@ -1,9 +1,10 @@ -import { error, json, text, type Cookies } from '@sveltejs/kit'; +import { error, json, redirect, text, type Cookies } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { UserApp, User } from '$lib/server/classes/users'; import { SessionApp, Session } from '$lib/server/classes/sessions'; import { AppData } from '$lib/server/classes/appdata'; import { logger } from '$lib/server/utils/logger'; +import { DEBUG } from '$lib/server/utils/constants'; /*********************************************************** * @@ -22,6 +23,10 @@ export const POST: RequestHandler = async ({ request, locals, cookies }) => { const session = local.session + if (!DEBUG) { + return redirect(307, "api/program/register") + } + if (session) { // The user is providing valid credentials // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/403