From f23cb0e1d2e15d6eebe666b551c36a9f83f6b5ff Mon Sep 17 00:00:00 2001 From: Christian Risi <75698846+CnF-Gris@users.noreply.github.com> Date: Thu, 3 Jul 2025 18:25:50 +0000 Subject: [PATCH] V 0.1.1 intermediate changes Co-authored-by: Oscar Urselli --- .devcontainer/devcontainer.json | 1 + nginx/active/automatic/http2/.gitkeep | 0 nginx/active/automatic/stream/.gitkeep | 0 nginx/active/manual/custom/.gitkeep | 0 nginx/active/manual/grpc/.gitkeep | 0 nginx/active/manual/http/.gitkeep | 0 nginx/active/manual/http2/.gitkeep | 0 nginx/active/manual/stream/.gitkeep | 0 nginx/inactive/automatic/grpc/.gitkeep | 0 nginx/inactive/automatic/http/.gitkeep | 0 nginx/inactive/automatic/http2/.gitkeep | 0 nginx/inactive/automatic/stream/.gitkeep | 0 nginx/nginx.conf | 18 +- .../broker-utils/FileSystem/Endpoint.ts | 209 +++++++++++++++++- .../FileSystem/SSLTerminations.ts | 6 +- .../FileSystem/endpoints/endpoints.ts | 2 + .../FileSystem/endpoints/manual-fs.ts | 60 +++++ .../FileSystem/endpoints/ssltermination-fs.ts | 15 +- .../server/broker-utils/FileSystem/utils.ts | 4 +- .../classes/endpoints/endpoints-interfaces.ts | 1 + .../classes/endpoints/enpoint-manager.ts | 55 +++++ .../classes/endpoints/manual-endpoint.ts | 115 ++++++++++ .../endpoints/ssl-termination-endpoint.ts | 61 +++-- src/lib/server/utils/constants.ts | 1 + src/lib/server/utils/filesystem-utils.ts | 65 +++++- src/lib/server/utils/openssl-utils.ts | 6 +- .../lib/shared/utils/cookies-utils.ts | 0 .../api/program/endpoint/activate/+server.ts | 6 + .../api/program/endpoint/all/+server.ts | 3 + .../program/endpoint/deactivate/+server.ts | 0 src/routes/api/program/register/+server.ts | 2 +- src/routes/api/register/+server.ts | 2 + tests/unit/filewatch.test.ts | 27 +++ 33 files changed, 603 insertions(+), 56 deletions(-) delete mode 100644 nginx/active/automatic/http2/.gitkeep delete mode 100644 nginx/active/automatic/stream/.gitkeep delete mode 100644 nginx/active/manual/custom/.gitkeep delete mode 100644 nginx/active/manual/grpc/.gitkeep delete mode 100644 nginx/active/manual/http/.gitkeep delete mode 100644 nginx/active/manual/http2/.gitkeep delete mode 100644 nginx/active/manual/stream/.gitkeep delete mode 100644 nginx/inactive/automatic/grpc/.gitkeep delete mode 100644 nginx/inactive/automatic/http/.gitkeep delete mode 100644 nginx/inactive/automatic/http2/.gitkeep delete mode 100644 nginx/inactive/automatic/stream/.gitkeep create mode 100644 src/lib/server/broker-utils/FileSystem/endpoints/manual-fs.ts create mode 100644 src/lib/server/classes/endpoints/enpoint-manager.ts create mode 100644 src/lib/server/classes/endpoints/manual-endpoint.ts rename nginx/active/automatic/grpc/.gitkeep => src/lib/shared/utils/cookies-utils.ts (100%) create mode 100644 src/routes/api/program/endpoint/activate/+server.ts create mode 100644 src/routes/api/program/endpoint/all/+server.ts rename nginx/active/automatic/http/.gitkeep => src/routes/api/program/endpoint/deactivate/+server.ts (100%) create mode 100644 tests/unit/filewatch.test.ts diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 686e4dc..2235a49 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,6 +13,7 @@ "vscode": { "extensions": [ "svelte.svelte-vscode", + "vitest.explorer", "william-voyek.vscode-nginx", "fabiospampinato.vscode-highlight", "fabiospampinato.vscode-todo-plus" diff --git a/nginx/active/automatic/http2/.gitkeep b/nginx/active/automatic/http2/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/active/automatic/stream/.gitkeep b/nginx/active/automatic/stream/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/active/manual/custom/.gitkeep b/nginx/active/manual/custom/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/active/manual/grpc/.gitkeep b/nginx/active/manual/grpc/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/active/manual/http/.gitkeep b/nginx/active/manual/http/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/active/manual/http2/.gitkeep b/nginx/active/manual/http2/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/active/manual/stream/.gitkeep b/nginx/active/manual/stream/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/inactive/automatic/grpc/.gitkeep b/nginx/inactive/automatic/grpc/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/inactive/automatic/http/.gitkeep b/nginx/inactive/automatic/http/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/inactive/automatic/http2/.gitkeep b/nginx/inactive/automatic/http2/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/inactive/automatic/stream/.gitkeep b/nginx/inactive/automatic/stream/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nginx/nginx.conf b/nginx/nginx.conf index bf5d242..cc71813 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -34,25 +34,15 @@ http { '"$http_user_agent" "$http_x_forwarded_for"'; - # Includes automatic virtual hosts configs. - include /etc/nginx/active/automatic/grpc/*.conf; - include /etc/nginx/active/automatic/http/*.conf; - include /etc/nginx/active/automatic/http2/*.conf; + # Includes virtual hosts configs. + include /etc/nginx/active/http/*.conf; - # Includes manual configs - include /etc/nginx/active/manual/grpc/*.conf; - include /etc/nginx/active/manual/http/*.conf; - include /etc/nginx/active/manual/http2/*.conf; - include /etc/nginx/active/manual/custom/*.conf; - } stream { - # Include automatic stream config - include /etc/nginx/active/automatic/stream/*.conf; + # Include stream config + include /etc/nginx/active/stream/*.conf; - # Include manual configs - include /etc/nginx/active/manual/stream/*.conf; } \ No newline at end of file diff --git a/src/lib/server/broker-utils/FileSystem/Endpoint.ts b/src/lib/server/broker-utils/FileSystem/Endpoint.ts index 5707c5d..74cfe5c 100644 --- a/src/lib/server/broker-utils/FileSystem/Endpoint.ts +++ b/src/lib/server/broker-utils/FileSystem/Endpoint.ts @@ -1,13 +1,25 @@ -import type { IEndpointFS } from "$lib/server/broker-utils/FileSystem/endpoints/endpoints" +import type { FSHeader, IEndpointFS } from "$lib/server/broker-utils/FileSystem/endpoints/endpoints" +import { NGINX_BASE } from "$lib/server/utils/constants" +import { doesFileExist, isDir, loadFile } from "$lib/server/utils/filesystem-utils" +import { watch, type FSWatcher } from "node:fs" +import { parseConf } from "./utils" +import { logger } from "$lib/server/utils/logger" +import { EndpointType } from "$lib/server/enums/endpoints" -export class EndpointBrokerManager { +export class EndpointBrokerManagerFS { private static initialized = false + private static watcher: FSWatcher + /** Here we store all endpoints + * - Key: path + * - Value: any IEndpointFS + */ private static endpoints: Map + private static usedPorts: number[] private static lastNginxReload: Date = new Date() public static get ready() { - return EndpointBrokerManager.initialized + return EndpointBrokerManagerFS.initialized } public static init() { @@ -16,15 +28,204 @@ export class EndpointBrokerManager { // TODO: QUICK parse them // TODO: Initialize a file watcher + + } + + public static async createEndpoint(endpoint: IEndpointFS) { + + } + + public static async changeEndpoint(path: string, newEndpoint: IEndpointFS) { + + } + + public static async activateEndpoint(path: string): Promise { + return true + } + + public static async deactivateEndpoint(path: string): Promise { + return true + } + + + public static getEndpointByPath(path: string): IEndpointFS | null { + + const endpoint = EndpointBrokerManagerFS.endpoints.get( + path + ) + + if (!endpoint) { + return null + } + + return endpoint } - public async getEndpointByName(name: string): IEndpoint { + public static async getAll(): Promise { + return Array.from(EndpointBrokerManagerFS.endpoints.values()) + } + // MARK: private methods + + /** + * This method is the only one that adds or removes files from + * our manager. No other method should mess up with the mapping + * of the manager + * + * @returns FSWatcher, to avoid having it garbage collected + */ + private static watchNginxDirectory(): FSWatcher { + + const OPTIONS = { + recursive: true + } + + const WATCHER = watch(NGINX_BASE, OPTIONS, async (eventType, filename) => { + + const RELATIVE_PATH = filename + const FULL_PATH = `${NGINX_BASE}/${RELATIVE_PATH}` + + // TODO: check if it's a directory, if so, skip + + if (await isDir(FULL_PATH)) { + return + } + + // UGLY: there may be race conditions, rarely, but + // UGLY: there may be + // UGLY: probably solved + + // TODO: Find a way to lock files + // TODO: probably solved + switch (eventType) { + + case "change": { + + const oldEndpoint = EndpointBrokerManagerFS.endpoints.get( + FULL_PATH + ) + + + if (!oldEndpoint) { + logger.debug(`File changed but was never tracked\nPATH: ${FULL_PATH}`, "EP Manager") + return + } + + // Nothing to do, it's not managed by us + if(oldEndpoint.type === EndpointType.MANUAL) { + return + } + + + const file = await loadFile(FULL_PATH) + await file.lock() + + // NOTE: HERE USE FILE + const newHash = await file.hash() + const oldHash = oldEndpoint.hash + + + if (newHash === oldHash) { + // Files are equal + // or we are very unlucky + await file.release() + return + } + + // NOTE: HERE USE FILE + const stats = await file.getStats() + const hash = await file.hash() + const conf = await file.text() + + const fsHeader: FSHeader = { + name: filename!.split("/").pop()!, + stats: stats, + path: FULL_PATH, + hash: hash + } + + const newEndpoint = parseConf(fsHeader, conf) + + // Check if files are trying to represent + // the same endpoint + if (oldEndpoint.headerHash() !== newEndpoint.headerHash()) { + // Files are not equal + // or we are very unlucky + await file.release() + return + } + + // Endpoints are different, but changes were never + // coming from here + logger.debug( + "Corrupted file detected", "EP Manager Corruption Detected" + ) + await file.write(oldEndpoint.toConf()) + + + await file.release() + // NOTE: HERE DO NOT USE FILE + break + } + + // Basically it's rename + default: { + const isNew = await doesFileExist(FULL_PATH) + + if (!isNew) { + // This means that the file doesn't exist + // let's just acknowledge this + + // UGLY: not checking for false values + // UGLY: hints that something went wrong + EndpointBrokerManagerFS.endpoints.delete( + FULL_PATH + ) + + } + + // Technically the file is new + const file = await loadFile(FULL_PATH) + await file.lock() + + // NOTE: HERE USE FILE + const stats = await file.getStats() + const hash = await file.hash() + const conf = await file.text() + + await file.release() + + // NOTE: HERE DO NOT USE FILE + + // UGLY: forcing typecasting + const fsHeader: FSHeader = { + name: filename!.split("/").pop()!, + stats: stats, + path: FULL_PATH, + hash: hash + } + const endpoint = parseConf(fsHeader, conf) + + EndpointBrokerManagerFS.endpoints.set( + FULL_PATH, + endpoint + ) + + return + } + + + } + }) + + return WATCHER } + + } diff --git a/src/lib/server/broker-utils/FileSystem/SSLTerminations.ts b/src/lib/server/broker-utils/FileSystem/SSLTerminations.ts index 7e2b9aa..89c2b49 100644 --- a/src/lib/server/broker-utils/FileSystem/SSLTerminations.ts +++ b/src/lib/server/broker-utils/FileSystem/SSLTerminations.ts @@ -38,15 +38,15 @@ export class SSLTerminationBroker implements ISSLTerminationBroker { throw new Error("Method not implemented."); } - async getSSLTerminationByName(name: string): Promise { + async getSSLTerminationByPath(name: string): Promise { throw new Error("Method not implemented."); } - async modifySSLTerminationByName(name: string, changes: SSLTerminationChanges): Promise { + async modifySSLTerminationByPath(name: string, changes: SSLTerminationChanges): Promise { throw new Error("Method not implemented."); } - async deleteSSLTerminationByName(name: string): Promise { + async deleteSSLTerminationByPath(name: string): Promise { throw new Error("Method not implemented."); } diff --git a/src/lib/server/broker-utils/FileSystem/endpoints/endpoints.ts b/src/lib/server/broker-utils/FileSystem/endpoints/endpoints.ts index 6c73ac9..a7d45ed 100644 --- a/src/lib/server/broker-utils/FileSystem/endpoints/endpoints.ts +++ b/src/lib/server/broker-utils/FileSystem/endpoints/endpoints.ts @@ -26,6 +26,8 @@ export interface IEndpointFS { ports(): number[] + headerHash(): string + } export type FSHeader = { diff --git a/src/lib/server/broker-utils/FileSystem/endpoints/manual-fs.ts b/src/lib/server/broker-utils/FileSystem/endpoints/manual-fs.ts new file mode 100644 index 0000000..fb98d5f --- /dev/null +++ b/src/lib/server/broker-utils/FileSystem/endpoints/manual-fs.ts @@ -0,0 +1,60 @@ +import { EndpointType } from "$lib/server/enums/endpoints"; +import type { Stats } from "fs"; +import type { FSHeader, IEndpointFS } from "./endpoints"; + +// TODO: add broker implementation + +export class ManualFS implements IEndpointFS { + + private static __type = EndpointType.MANUAL + private static __hash = "emanuel" + + public get type() { + return ManualFS.__type + } + + public get hash() { + return ManualFS.__hash + } + + name: string; + stats: Stats; + path: string; + body: string; + + constructor( + name: string, + stats: Stats, + path: string, + body: string + ) { + this.name = name + this.stats = stats + this.path = path + this.body = body + } + + toConf(): string { + return this.body + } + + ports(): number[] { + return [] + } + + headerHash(): string { + return this.hash + } + + public static parseConf(fsHeader: FSHeader, conf: string) { + return new ManualFS( + fsHeader.name, + fsHeader.stats, + fsHeader.path, + conf + ) + } + + + +} \ No newline at end of file diff --git a/src/lib/server/broker-utils/FileSystem/endpoints/ssltermination-fs.ts b/src/lib/server/broker-utils/FileSystem/endpoints/ssltermination-fs.ts index ce141e6..93fcc8a 100644 --- a/src/lib/server/broker-utils/FileSystem/endpoints/ssltermination-fs.ts +++ b/src/lib/server/broker-utils/FileSystem/endpoints/ssltermination-fs.ts @@ -4,6 +4,10 @@ import { validatePort } from "$lib/server/utils/ports-utils" import type { Stats } from "fs" import type { FSHeader, IEndpointFS } from "./endpoints" import { createHeader } from "../utils" +import { hashUtil } from "$lib/server/utils/filesystem-utils" + +// TODO: add broker implementation + export class SSLTerminationFS implements IEndpointFS { @@ -42,10 +46,6 @@ export class SSLTerminationFS implements IEndpointFS { privateKeyURI: string ) { - validatePort(sslPort) - validatePort(clearPort) - validatePort(servicePort) - this.name = name this.stats = stats this.path = path @@ -60,6 +60,13 @@ export class SSLTerminationFS implements IEndpointFS { } + + headerHash(): string { + return hashUtil( + this.createHeader() + ) + } + toConf(): string { const HEADER = this.createHeader() diff --git a/src/lib/server/broker-utils/FileSystem/utils.ts b/src/lib/server/broker-utils/FileSystem/utils.ts index 33ca074..ba349cf 100644 --- a/src/lib/server/broker-utils/FileSystem/utils.ts +++ b/src/lib/server/broker-utils/FileSystem/utils.ts @@ -1,5 +1,6 @@ import { EndpointType, matchEndpoint } from "$lib/server/enums/endpoints" import type { FSHeader, IEndpointFS } from "./endpoints/endpoints" +import { ManualFS } from "./endpoints/manual-fs" import { SSLTerminationFS } from "./endpoints/ssltermination-fs" export const HEADER_BOUNDARY = "**********************************************************" @@ -23,7 +24,7 @@ export function parseConf( case EndpointType.SSL_TERMINATION: return SSLTerminationFS.parseConf(fsHeader, conf) default: - // TODO: add manual endpoint + return ManualFS.parseConf(fsHeader, conf) } } @@ -37,6 +38,7 @@ export function createHeader(endpoint: IEndpointFS, variables?: HeaderKeyValueFS HEADER_UPPER, "SSL-SNIFFER AUTOMATICALLY GENERATED", `TYPE: ${endpoint.type}`, + `NAME: ${endpoint.name}`, HEADER_BOUNDARY ] diff --git a/src/lib/server/classes/endpoints/endpoints-interfaces.ts b/src/lib/server/classes/endpoints/endpoints-interfaces.ts index fee0821..1cacd9c 100644 --- a/src/lib/server/classes/endpoints/endpoints-interfaces.ts +++ b/src/lib/server/classes/endpoints/endpoints-interfaces.ts @@ -4,5 +4,6 @@ export interface IEndpoint { type: EndpointType name: string + path: string } \ No newline at end of file diff --git a/src/lib/server/classes/endpoints/enpoint-manager.ts b/src/lib/server/classes/endpoints/enpoint-manager.ts new file mode 100644 index 0000000..237390a --- /dev/null +++ b/src/lib/server/classes/endpoints/enpoint-manager.ts @@ -0,0 +1,55 @@ +import { EndpointBrokerManagerFS } from "$lib/server/broker-utils/FileSystem/Endpoint" +import type { IEndpoint } from "./endpoints-interfaces" + + +// UGLY: refactor this +export class EndpointManagerApp { + + private static initialized = false + /** Here we store all endpoints + * - Key: path + * - Value: any IEndpointFS + */ + + public static get ready() { + return EndpointManagerApp.initialized + } + + public static init() { + // TODO: Read all files + + // TODO: QUICK parse them + + // TODO: Initialize a file watcher + + } + + + public static async activateEndpoint(path: string): Promise { + return await EndpointBrokerManagerFS.activateEndpoint(path) + } + + public static async deactivateEndpoint(path: string): Promise { + return await EndpointBrokerManagerFS.deactivateEndpoint(path) + } + + + public static getEndpointByPath(path: string): IEndpoint | null { + // UGLY: parse + return EndpointBrokerManagerFS.getEndpointByPath(path) + + } + + + public static async getAll(): Promise { + // UGLY: parse + return await EndpointBrokerManagerFS.getAll() + } + + + + + + +} + diff --git a/src/lib/server/classes/endpoints/manual-endpoint.ts b/src/lib/server/classes/endpoints/manual-endpoint.ts new file mode 100644 index 0000000..3dcc215 --- /dev/null +++ b/src/lib/server/classes/endpoints/manual-endpoint.ts @@ -0,0 +1,115 @@ +import { EndpointType } from "$lib/server/enums/endpoints"; +import type { init } from "../../../../hooks.server"; +import type { IEndpoint } from "./endpoints-interfaces"; + +export interface IManualBroker { + + init(): Promise + + getManualByPath(path: string): Promise + + getAllManuals(): Promise + + activateEndpointByPath( + path: string + ): Promise + + deactivateEndpointByPath( + path: string + ): Promise + +} + + +export class Manual implements IEndpoint { + + + private static __type = EndpointType.SSL_TERMINATION + + public get type() { + return Manual.__type + } + + public name: string; + public path: string; + public body: string; + + constructor( + name: string, + path: string, + body: string + ) { + this.name = name + this.path = path + this.body = body + } + + + +} + + +export class ManualEndpointApp { + + private static initialized: boolean = false + private static broker: IManualBroker + + public static get ready() { + return ManualEndpointApp.initialized + } + + public static init(broker: IManualBroker) { + ManualEndpointApp.assureNotInitialized() + + ManualEndpointApp.broker = broker + broker.init() + ManualEndpointApp.initialized = true + } + + + public static async getManualByPath(path: string) { + ManualEndpointApp.assureInitialized() + + return ManualEndpointApp.broker.getManualByPath(path) + } + + public static async getAllManual() { + ManualEndpointApp.assureInitialized() + + return await ManualEndpointApp.broker.getAllManuals() + } + + public static async activateEndpointByPath( + path: string + ): Promise { + ManualEndpointApp.assureInitialized() + + return await ManualEndpointApp.broker.activateEndpointByPath(path) + + } + + public static async deactivateEndpointByPath( + path: string + ): Promise { + ManualEndpointApp.assureInitialized() + + return await ManualEndpointApp.broker.deactivateEndpointByPath(path) + + } + + + private static assureNotInitialized() { + if (ManualEndpointApp.initialized) { + // UGLY: more specific + throw new Error("SSLTerminationEndpointApp has been already initialized") + } + } + + private static assureInitialized() { + if (ManualEndpointApp.initialized) { + // UGLY: more specific + throw new Error("SSLTerminationEndpointApp has not been initialized yet") + } + } + +} \ No newline at end of file diff --git a/src/lib/server/classes/endpoints/ssl-termination-endpoint.ts b/src/lib/server/classes/endpoints/ssl-termination-endpoint.ts index 77b4610..35c93ce 100644 --- a/src/lib/server/classes/endpoints/ssl-termination-endpoint.ts +++ b/src/lib/server/classes/endpoints/ssl-termination-endpoint.ts @@ -11,6 +11,9 @@ export interface ISSLTerminationBroker { */ init(): Promise + // TODO: in the next version support + // TODO: creation of endpoints + // TODO: according to path // Creation should throw if something goes wrong // with reasons why createSSLTerminationSimple( @@ -31,22 +34,30 @@ export interface ISSLTerminationBroker { privateKeyURI: string ): Promise + activateEndpointByPath( + path: string + ): Promise + + deactivateEndpointByPath( + path: string + ): Promise + // Getting endpoints may be null, react over them - getSSLTerminationByName( - name: string + getSSLTerminationByPath( + path: string ): Promise // Throw if something goes wrong - modifySSLTerminationByName( - name: string, + modifySSLTerminationByPath( + path: string, changes: SSLTerminationChanges ): Promise - deleteSSLTerminationByName( - name: string + deleteSSLTerminationByPath( + path: string ): Promise @@ -70,6 +81,7 @@ export class SSLTermination implements IEndpoint { } public name: string + public path: string public sslPort: number public clearPort: number public servicePort: number @@ -82,6 +94,7 @@ export class SSLTermination implements IEndpoint { constructor( name: string, + path: string, sslPort: number, clearPort: number, servicePort: number, @@ -91,11 +104,8 @@ export class SSLTermination implements IEndpoint { privateKeyURI: string ) { - validatePort(sslPort) - validatePort(clearPort) - validatePort(servicePort) - this.name = name + this.path = path this.sslPort = sslPort this.clearPort = clearPort this.servicePort = servicePort @@ -111,6 +121,7 @@ export class SSLTermination implements IEndpoint { export type SSLTerminationChanges = { name?: string, + path?: string, sslPort?: number, clearPort?: number, servicePort?: number, @@ -187,13 +198,13 @@ export class SSLTerminationEndpointApp { // Getting endpoints may be null, react over them - public static async getSSLTerminationByName( + public static async getSSLTerminationByPath( name: string ): Promise { SSLTerminationEndpointApp.assureInitialized() - return await this.broker.getSSLTerminationByName( + return await this.broker.getSSLTerminationByPath( name ) @@ -201,25 +212,25 @@ export class SSLTerminationEndpointApp { // Throw if something goes wrong - public static async modifySSLTerminationByName( + public static async modifySSLTerminationByPath( name: string, changes: SSLTerminationChanges ): Promise { SSLTerminationEndpointApp.assureInitialized() - return await this.broker.modifySSLTerminationByName( + return await this.broker.modifySSLTerminationByPath( name, changes ) } - public static async deleteSSLTerminationByName( + public static async deleteSSLTerminationByPath( name: string ): Promise { SSLTerminationEndpointApp.assureInitialized() - return await this.broker.deleteSSLTerminationByName( + return await this.broker.deleteSSLTerminationByPath( name ) } @@ -228,7 +239,23 @@ export class SSLTerminationEndpointApp { public static async getAllSSLTerminations(): Promise { SSLTerminationEndpointApp.assureInitialized() - return await this.broker.getAllSSLTerminations() + return await SSLTerminationEndpointApp.broker.getAllSSLTerminations() + } + + public static async activateEndpointByPath(path: string) { + SSLTerminationEndpointApp.assureInitialized() + + return await SSLTerminationEndpointApp.broker.activateEndpointByPath( + path + ) + } + + public static async deactivateEndpointByPath(path: string) { + SSLTerminationEndpointApp.assureInitialized() + + return await SSLTerminationEndpointApp.broker.deactivateEndpointByPath( + path + ) } diff --git a/src/lib/server/utils/constants.ts b/src/lib/server/utils/constants.ts index 11c0a9c..8dba53f 100644 --- a/src/lib/server/utils/constants.ts +++ b/src/lib/server/utils/constants.ts @@ -1,6 +1,7 @@ export const PKG = __PKG__ export const DB_PATH = "src/db/db.sqlite" export const SERVER_PRIVATE_DIR = "src/private" +export const SERVER_TMP_FILE = `${SERVER_PRIVATE_DIR}/tmp.pem` export const SERVER_PRIVATE_KEY_PATH = `${SERVER_PRIVATE_DIR}/key.pem` export const SERVER_PUBLIC_KEY_PATH = `${SERVER_PRIVATE_DIR}/pub.pem` export const DEBUG = import.meta.env.DEV diff --git a/src/lib/server/utils/filesystem-utils.ts b/src/lib/server/utils/filesystem-utils.ts index 8b8a980..236f05f 100644 --- a/src/lib/server/utils/filesystem-utils.ts +++ b/src/lib/server/utils/filesystem-utils.ts @@ -1,27 +1,37 @@ -import { Stats, type OpenMode } from 'node:fs'; +import { Stats, type OpenMode } from 'node:fs'; import * as Node from 'node:fs/promises'; +import { createHash, type BinaryLike } from 'node:crypto'; export type FileStats = Stats export class FileHandle { private path: string + private fd: Node.FileHandle | null + + private get file() { + if (!this.fd) { + return this.path + } + return this.fd + } constructor( path: string ) { this.path = path + this.fd = null } public async text(encoding?: BufferEncoding) { - + if (!encoding) { encoding = "utf-8" } - const fileContent = await Node.readFile(this.path, {encoding: encoding}) + const fileContent = await Node.readFile(this.path, { encoding: encoding }) return fileContent } @@ -33,9 +43,9 @@ export class FileHandle { append = true } - const flag : OpenMode = append ? "a+" : "w" + const flag: OpenMode = append ? "a+" : "w" - await Node.writeFile(this.path, text, {flag: flag}) + await Node.writeFile(this.file, text, { flag: flag }) } @@ -49,6 +59,26 @@ export class FileHandle { return stats } + public async hash() { + return hashUtil( + await Node.readFile(this.file) + ) + + } + + public async lock() { + this.fd = await Node.open(this.path, "r+") + } + + public async release() { + if (!this.fd) { + return + } + + await this.fd.close() + this.fd = null + } + } @@ -67,7 +97,13 @@ export async function doesFileExist(path: string): Promise { } return true - + +} + +export async function isDir(path: string): Promise { + const stats = await Node.stat(path) + + return stats.isDirectory() } @@ -81,7 +117,7 @@ export async function loadFile(path: string, create?: boolean): Promise { + +}; \ No newline at end of file diff --git a/src/routes/api/program/endpoint/all/+server.ts b/src/routes/api/program/endpoint/all/+server.ts new file mode 100644 index 0000000..0cc850c --- /dev/null +++ b/src/routes/api/program/endpoint/all/+server.ts @@ -0,0 +1,3 @@ +export const GET: RequestHandler = ({}) => { + +} \ No newline at end of file diff --git a/nginx/active/automatic/http/.gitkeep b/src/routes/api/program/endpoint/deactivate/+server.ts similarity index 100% rename from nginx/active/automatic/http/.gitkeep rename to src/routes/api/program/endpoint/deactivate/+server.ts diff --git a/src/routes/api/program/register/+server.ts b/src/routes/api/program/register/+server.ts index 4f8f2e8..6a7a057 100644 --- a/src/routes/api/program/register/+server.ts +++ b/src/routes/api/program/register/+server.ts @@ -29,7 +29,7 @@ export const POST: RequestHandler = async ({ request, locals, cookies }) => { if (!session) { // The user is not providing credentials - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/403 + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/401 return error(401, "Unauthorized") } diff --git a/src/routes/api/register/+server.ts b/src/routes/api/register/+server.ts index 60b32e4..5ef6d92 100644 --- a/src/routes/api/register/+server.ts +++ b/src/routes/api/register/+server.ts @@ -27,6 +27,8 @@ export const POST: RequestHandler = async ({ request, locals, cookies }) => { return redirect(307, "api/program/register") } + console.log(session) + if (session) { // The user is providing valid credentials // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/403 diff --git a/tests/unit/filewatch.test.ts b/tests/unit/filewatch.test.ts new file mode 100644 index 0000000..f79dc13 --- /dev/null +++ b/tests/unit/filewatch.test.ts @@ -0,0 +1,27 @@ + +import { describe, it, expect } from 'vitest'; +import { watch } from "node:fs" +import * as readline from "node:readline" + +// TODO: make tests for Database +describe('watch nginx', () => { + + const OPTIONS = { + recursive: true + } + + const NGINX_BASE = "/etc/nginx" + + const input = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }) + + watch(NGINX_BASE, OPTIONS, (eventType, filename) => { + console.log(`event: ${eventType}`) + console.log(`file: ${filename}`) + }) + + console.log("Done") + +});