V 0.1.1 intermediate changes
Co-authored-by: Oscar Urselli <oscar0urselli@users.noreply.github.com>
This commit is contained in:
parent
19457d97ae
commit
f23cb0e1d2
@ -13,6 +13,7 @@
|
|||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"svelte.svelte-vscode",
|
"svelte.svelte-vscode",
|
||||||
|
"vitest.explorer",
|
||||||
"william-voyek.vscode-nginx",
|
"william-voyek.vscode-nginx",
|
||||||
"fabiospampinato.vscode-highlight",
|
"fabiospampinato.vscode-highlight",
|
||||||
"fabiospampinato.vscode-todo-plus"
|
"fabiospampinato.vscode-todo-plus"
|
||||||
|
|||||||
@ -34,25 +34,15 @@ http {
|
|||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||||
|
|
||||||
|
|
||||||
# Includes automatic virtual hosts configs.
|
# Includes virtual hosts configs.
|
||||||
include /etc/nginx/active/automatic/grpc/*.conf;
|
include /etc/nginx/active/http/*.conf;
|
||||||
include /etc/nginx/active/automatic/http/*.conf;
|
|
||||||
include /etc/nginx/active/automatic/http2/*.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 {
|
stream {
|
||||||
|
|
||||||
# Include automatic stream config
|
# Include stream config
|
||||||
include /etc/nginx/active/automatic/stream/*.conf;
|
include /etc/nginx/active/stream/*.conf;
|
||||||
|
|
||||||
# Include manual configs
|
|
||||||
include /etc/nginx/active/manual/stream/*.conf;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -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 initialized = false
|
||||||
|
private static watcher: FSWatcher
|
||||||
|
/** Here we store all endpoints
|
||||||
|
* - Key: path
|
||||||
|
* - Value: any IEndpointFS
|
||||||
|
*/
|
||||||
private static endpoints: Map<string, IEndpointFS>
|
private static endpoints: Map<string, IEndpointFS>
|
||||||
|
private static usedPorts: number[]
|
||||||
private static lastNginxReload: Date = new Date()
|
private static lastNginxReload: Date = new Date()
|
||||||
|
|
||||||
public static get ready() {
|
public static get ready() {
|
||||||
return EndpointBrokerManager.initialized
|
return EndpointBrokerManagerFS.initialized
|
||||||
}
|
}
|
||||||
|
|
||||||
public static init() {
|
public static init() {
|
||||||
@ -16,15 +28,204 @@ export class EndpointBrokerManager {
|
|||||||
// TODO: QUICK parse them
|
// TODO: QUICK parse them
|
||||||
|
|
||||||
// TODO: Initialize a file watcher
|
// 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<boolean> {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async deactivateEndpoint(path: string): Promise<boolean> {
|
||||||
|
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<IEndpointFS[]> {
|
||||||
|
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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -38,15 +38,15 @@ export class SSLTerminationBroker implements ISSLTerminationBroker {
|
|||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSSLTerminationByName(name: string): Promise<SSLTermination | null> {
|
async getSSLTerminationByPath(name: string): Promise<SSLTermination | null> {
|
||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
async modifySSLTerminationByName(name: string, changes: SSLTerminationChanges): Promise<SSLTermination> {
|
async modifySSLTerminationByPath(name: string, changes: SSLTerminationChanges): Promise<SSLTermination> {
|
||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSSLTerminationByName(name: string): Promise<SSLTermination | null> {
|
async deleteSSLTerminationByPath(name: string): Promise<SSLTermination | null> {
|
||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,8 @@ export interface IEndpointFS {
|
|||||||
|
|
||||||
ports(): number[]
|
ports(): number[]
|
||||||
|
|
||||||
|
headerHash(): string
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FSHeader = {
|
export type FSHeader = {
|
||||||
|
|||||||
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -4,6 +4,10 @@ import { validatePort } from "$lib/server/utils/ports-utils"
|
|||||||
import type { Stats } from "fs"
|
import type { Stats } from "fs"
|
||||||
import type { FSHeader, IEndpointFS } from "./endpoints"
|
import type { FSHeader, IEndpointFS } from "./endpoints"
|
||||||
import { createHeader } from "../utils"
|
import { createHeader } from "../utils"
|
||||||
|
import { hashUtil } from "$lib/server/utils/filesystem-utils"
|
||||||
|
|
||||||
|
// TODO: add broker implementation
|
||||||
|
|
||||||
|
|
||||||
export class SSLTerminationFS implements IEndpointFS {
|
export class SSLTerminationFS implements IEndpointFS {
|
||||||
|
|
||||||
@ -42,10 +46,6 @@ export class SSLTerminationFS implements IEndpointFS {
|
|||||||
privateKeyURI: string
|
privateKeyURI: string
|
||||||
) {
|
) {
|
||||||
|
|
||||||
validatePort(sslPort)
|
|
||||||
validatePort(clearPort)
|
|
||||||
validatePort(servicePort)
|
|
||||||
|
|
||||||
this.name = name
|
this.name = name
|
||||||
this.stats = stats
|
this.stats = stats
|
||||||
this.path = path
|
this.path = path
|
||||||
@ -60,6 +60,13 @@ export class SSLTerminationFS implements IEndpointFS {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
headerHash(): string {
|
||||||
|
return hashUtil(
|
||||||
|
this.createHeader()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
toConf(): string {
|
toConf(): string {
|
||||||
|
|
||||||
const HEADER = this.createHeader()
|
const HEADER = this.createHeader()
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { EndpointType, matchEndpoint } from "$lib/server/enums/endpoints"
|
import { EndpointType, matchEndpoint } from "$lib/server/enums/endpoints"
|
||||||
import type { FSHeader, IEndpointFS } from "./endpoints/endpoints"
|
import type { FSHeader, IEndpointFS } from "./endpoints/endpoints"
|
||||||
|
import { ManualFS } from "./endpoints/manual-fs"
|
||||||
import { SSLTerminationFS } from "./endpoints/ssltermination-fs"
|
import { SSLTerminationFS } from "./endpoints/ssltermination-fs"
|
||||||
|
|
||||||
export const HEADER_BOUNDARY = "**********************************************************"
|
export const HEADER_BOUNDARY = "**********************************************************"
|
||||||
@ -23,7 +24,7 @@ export function parseConf(
|
|||||||
case EndpointType.SSL_TERMINATION:
|
case EndpointType.SSL_TERMINATION:
|
||||||
return SSLTerminationFS.parseConf(fsHeader, conf)
|
return SSLTerminationFS.parseConf(fsHeader, conf)
|
||||||
default:
|
default:
|
||||||
// TODO: add manual endpoint
|
return ManualFS.parseConf(fsHeader, conf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ export function createHeader(endpoint: IEndpointFS, variables?: HeaderKeyValueFS
|
|||||||
HEADER_UPPER,
|
HEADER_UPPER,
|
||||||
"SSL-SNIFFER AUTOMATICALLY GENERATED",
|
"SSL-SNIFFER AUTOMATICALLY GENERATED",
|
||||||
`TYPE: ${endpoint.type}`,
|
`TYPE: ${endpoint.type}`,
|
||||||
|
`NAME: ${endpoint.name}`,
|
||||||
HEADER_BOUNDARY
|
HEADER_BOUNDARY
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -4,5 +4,6 @@ export interface IEndpoint {
|
|||||||
|
|
||||||
type: EndpointType
|
type: EndpointType
|
||||||
name: string
|
name: string
|
||||||
|
path: string
|
||||||
|
|
||||||
}
|
}
|
||||||
55
src/lib/server/classes/endpoints/enpoint-manager.ts
Normal file
55
src/lib/server/classes/endpoints/enpoint-manager.ts
Normal file
@ -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<boolean> {
|
||||||
|
return await EndpointBrokerManagerFS.activateEndpoint(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async deactivateEndpoint(path: string): Promise<boolean> {
|
||||||
|
return await EndpointBrokerManagerFS.deactivateEndpoint(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static getEndpointByPath(path: string): IEndpoint | null {
|
||||||
|
// UGLY: parse
|
||||||
|
return EndpointBrokerManagerFS.getEndpointByPath(path)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async getAll(): Promise<IEndpoint[]> {
|
||||||
|
// UGLY: parse
|
||||||
|
return await EndpointBrokerManagerFS.getAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
115
src/lib/server/classes/endpoints/manual-endpoint.ts
Normal file
115
src/lib/server/classes/endpoints/manual-endpoint.ts
Normal file
@ -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<void>
|
||||||
|
|
||||||
|
getManualByPath(path: string): Promise<Manual>
|
||||||
|
|
||||||
|
getAllManuals(): Promise<Manual[]>
|
||||||
|
|
||||||
|
activateEndpointByPath(
|
||||||
|
path: string
|
||||||
|
): Promise<boolean>
|
||||||
|
|
||||||
|
deactivateEndpointByPath(
|
||||||
|
path: string
|
||||||
|
): Promise<boolean>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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<boolean> {
|
||||||
|
ManualEndpointApp.assureInitialized()
|
||||||
|
|
||||||
|
return await ManualEndpointApp.broker.activateEndpointByPath(path)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async deactivateEndpointByPath(
|
||||||
|
path: string
|
||||||
|
): Promise<boolean> {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -11,6 +11,9 @@ export interface ISSLTerminationBroker {
|
|||||||
*/
|
*/
|
||||||
init(): Promise<void>
|
init(): Promise<void>
|
||||||
|
|
||||||
|
// TODO: in the next version support
|
||||||
|
// TODO: creation of endpoints
|
||||||
|
// TODO: according to path
|
||||||
// Creation should throw if something goes wrong
|
// Creation should throw if something goes wrong
|
||||||
// with reasons why
|
// with reasons why
|
||||||
createSSLTerminationSimple(
|
createSSLTerminationSimple(
|
||||||
@ -31,22 +34,30 @@ export interface ISSLTerminationBroker {
|
|||||||
privateKeyURI: string
|
privateKeyURI: string
|
||||||
): Promise<SSLTermination>
|
): Promise<SSLTermination>
|
||||||
|
|
||||||
|
activateEndpointByPath(
|
||||||
|
path: string
|
||||||
|
): Promise<boolean>
|
||||||
|
|
||||||
|
deactivateEndpointByPath(
|
||||||
|
path: string
|
||||||
|
): Promise<boolean>
|
||||||
|
|
||||||
|
|
||||||
// Getting endpoints may be null, react over them
|
// Getting endpoints may be null, react over them
|
||||||
getSSLTerminationByName(
|
getSSLTerminationByPath(
|
||||||
name: string
|
path: string
|
||||||
): Promise<SSLTermination|null>
|
): Promise<SSLTermination|null>
|
||||||
|
|
||||||
|
|
||||||
// Throw if something goes wrong
|
// Throw if something goes wrong
|
||||||
modifySSLTerminationByName(
|
modifySSLTerminationByPath(
|
||||||
name: string,
|
path: string,
|
||||||
changes: SSLTerminationChanges
|
changes: SSLTerminationChanges
|
||||||
): Promise<SSLTermination>
|
): Promise<SSLTermination>
|
||||||
|
|
||||||
|
|
||||||
deleteSSLTerminationByName(
|
deleteSSLTerminationByPath(
|
||||||
name: string
|
path: string
|
||||||
): Promise<SSLTermination|null>
|
): Promise<SSLTermination|null>
|
||||||
|
|
||||||
|
|
||||||
@ -70,6 +81,7 @@ export class SSLTermination implements IEndpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public name: string
|
public name: string
|
||||||
|
public path: string
|
||||||
public sslPort: number
|
public sslPort: number
|
||||||
public clearPort: number
|
public clearPort: number
|
||||||
public servicePort: number
|
public servicePort: number
|
||||||
@ -82,6 +94,7 @@ export class SSLTermination implements IEndpoint {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
name: string,
|
name: string,
|
||||||
|
path: string,
|
||||||
sslPort: number,
|
sslPort: number,
|
||||||
clearPort: number,
|
clearPort: number,
|
||||||
servicePort: number,
|
servicePort: number,
|
||||||
@ -91,11 +104,8 @@ export class SSLTermination implements IEndpoint {
|
|||||||
privateKeyURI: string
|
privateKeyURI: string
|
||||||
) {
|
) {
|
||||||
|
|
||||||
validatePort(sslPort)
|
|
||||||
validatePort(clearPort)
|
|
||||||
validatePort(servicePort)
|
|
||||||
|
|
||||||
this.name = name
|
this.name = name
|
||||||
|
this.path = path
|
||||||
this.sslPort = sslPort
|
this.sslPort = sslPort
|
||||||
this.clearPort = clearPort
|
this.clearPort = clearPort
|
||||||
this.servicePort = servicePort
|
this.servicePort = servicePort
|
||||||
@ -111,6 +121,7 @@ export class SSLTermination implements IEndpoint {
|
|||||||
|
|
||||||
export type SSLTerminationChanges = {
|
export type SSLTerminationChanges = {
|
||||||
name?: string,
|
name?: string,
|
||||||
|
path?: string,
|
||||||
sslPort?: number,
|
sslPort?: number,
|
||||||
clearPort?: number,
|
clearPort?: number,
|
||||||
servicePort?: number,
|
servicePort?: number,
|
||||||
@ -187,13 +198,13 @@ export class SSLTerminationEndpointApp {
|
|||||||
|
|
||||||
|
|
||||||
// Getting endpoints may be null, react over them
|
// Getting endpoints may be null, react over them
|
||||||
public static async getSSLTerminationByName(
|
public static async getSSLTerminationByPath(
|
||||||
name: string
|
name: string
|
||||||
): Promise<SSLTermination|null> {
|
): Promise<SSLTermination|null> {
|
||||||
|
|
||||||
SSLTerminationEndpointApp.assureInitialized()
|
SSLTerminationEndpointApp.assureInitialized()
|
||||||
|
|
||||||
return await this.broker.getSSLTerminationByName(
|
return await this.broker.getSSLTerminationByPath(
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -201,25 +212,25 @@ export class SSLTerminationEndpointApp {
|
|||||||
|
|
||||||
|
|
||||||
// Throw if something goes wrong
|
// Throw if something goes wrong
|
||||||
public static async modifySSLTerminationByName(
|
public static async modifySSLTerminationByPath(
|
||||||
name: string,
|
name: string,
|
||||||
changes: SSLTerminationChanges
|
changes: SSLTerminationChanges
|
||||||
): Promise<SSLTermination> {
|
): Promise<SSLTermination> {
|
||||||
SSLTerminationEndpointApp.assureInitialized()
|
SSLTerminationEndpointApp.assureInitialized()
|
||||||
|
|
||||||
return await this.broker.modifySSLTerminationByName(
|
return await this.broker.modifySSLTerminationByPath(
|
||||||
name,
|
name,
|
||||||
changes
|
changes
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static async deleteSSLTerminationByName(
|
public static async deleteSSLTerminationByPath(
|
||||||
name: string
|
name: string
|
||||||
): Promise<SSLTermination|null> {
|
): Promise<SSLTermination|null> {
|
||||||
SSLTerminationEndpointApp.assureInitialized()
|
SSLTerminationEndpointApp.assureInitialized()
|
||||||
|
|
||||||
return await this.broker.deleteSSLTerminationByName(
|
return await this.broker.deleteSSLTerminationByPath(
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -228,7 +239,23 @@ export class SSLTerminationEndpointApp {
|
|||||||
public static async getAllSSLTerminations(): Promise<SSLTermination[]> {
|
public static async getAllSSLTerminations(): Promise<SSLTermination[]> {
|
||||||
SSLTerminationEndpointApp.assureInitialized()
|
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
export const PKG = __PKG__
|
export const PKG = __PKG__
|
||||||
export const DB_PATH = "src/db/db.sqlite"
|
export const DB_PATH = "src/db/db.sqlite"
|
||||||
export const SERVER_PRIVATE_DIR = "src/private"
|
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_PRIVATE_KEY_PATH = `${SERVER_PRIVATE_DIR}/key.pem`
|
||||||
export const SERVER_PUBLIC_KEY_PATH = `${SERVER_PRIVATE_DIR}/pub.pem`
|
export const SERVER_PUBLIC_KEY_PATH = `${SERVER_PRIVATE_DIR}/pub.pem`
|
||||||
export const DEBUG = import.meta.env.DEV
|
export const DEBUG = import.meta.env.DEV
|
||||||
|
|||||||
@ -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 * as Node from 'node:fs/promises';
|
||||||
|
import { createHash, type BinaryLike } from 'node:crypto';
|
||||||
|
|
||||||
export type FileStats = Stats
|
export type FileStats = Stats
|
||||||
|
|
||||||
export class FileHandle {
|
export class FileHandle {
|
||||||
|
|
||||||
private path: string
|
private path: string
|
||||||
|
private fd: Node.FileHandle | null
|
||||||
|
|
||||||
|
private get file() {
|
||||||
|
if (!this.fd) {
|
||||||
|
return this.path
|
||||||
|
}
|
||||||
|
return this.fd
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
path: string
|
path: string
|
||||||
) {
|
) {
|
||||||
this.path = path
|
this.path = path
|
||||||
|
this.fd = null
|
||||||
}
|
}
|
||||||
|
|
||||||
public async text(encoding?: BufferEncoding) {
|
public async text(encoding?: BufferEncoding) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!encoding) {
|
if (!encoding) {
|
||||||
encoding = "utf-8"
|
encoding = "utf-8"
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileContent = await Node.readFile(this.path, {encoding: encoding})
|
const fileContent = await Node.readFile(this.path, { encoding: encoding })
|
||||||
return fileContent
|
return fileContent
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -33,9 +43,9 @@ export class FileHandle {
|
|||||||
append = true
|
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
|
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<boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isDir(path: string): Promise<boolean> {
|
||||||
|
const stats = await Node.stat(path)
|
||||||
|
|
||||||
|
return stats.isDirectory()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -81,7 +117,7 @@ export async function loadFile(path: string, create?: boolean): Promise<FileHand
|
|||||||
create = false
|
create = false
|
||||||
}
|
}
|
||||||
|
|
||||||
let fd : Node.FileHandle | null = null
|
let fd: Node.FileHandle | null = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fd = await Node.open(path, "r+", DEFAULT_MODE)
|
fd = await Node.open(path, "r+", DEFAULT_MODE)
|
||||||
@ -104,7 +140,16 @@ export async function loadFile(path: string, create?: boolean): Promise<FileHand
|
|||||||
// We do want this to throw without catching here
|
// We do want this to throw without catching here
|
||||||
fd = await Node.open(path, "w", DEFAULT_MODE)
|
fd = await Node.open(path, "w", DEFAULT_MODE)
|
||||||
await fd.close()
|
await fd.close()
|
||||||
|
|
||||||
return new FileHandle(path)
|
return new FileHandle(path)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UGLY: move this to a new file
|
||||||
|
export function hashUtil(data: BinaryLike) {
|
||||||
|
const hash = createHash("sha256")
|
||||||
|
.update(
|
||||||
|
data
|
||||||
|
).digest('base64')
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { doesFileExist, loadFile, type FileHandle } from "./filesystem-utils";
|
import { doesFileExist, loadFile, type FileHandle } from "./filesystem-utils";
|
||||||
import { SERVER_PRIVATE_KEY_PATH, SERVER_PUBLIC_KEY_PATH } from "./constants";
|
import { SERVER_PRIVATE_KEY_PATH, SERVER_PUBLIC_KEY_PATH, SERVER_TMP_FILE } from "./constants";
|
||||||
import { shell, type shellOutput } from "./shell-commands";
|
import { shell, type shellOutput } from "./shell-commands";
|
||||||
|
|
||||||
export async function openSSLInit() {
|
export async function openSSLInit() {
|
||||||
@ -12,7 +12,9 @@ export async function openSSLInit() {
|
|||||||
export async function openSSLCreatePrivateKey() {
|
export async function openSSLCreatePrivateKey() {
|
||||||
|
|
||||||
// UGLY: may be refactored to output only the private key
|
// UGLY: may be refactored to output only the private key
|
||||||
const outputPromise = shell(`openssl ecparam -genkey -name secp521r1 -noout | openssl pkcs8 -topk8 -nocrypt`)
|
const pemPromise = await shell(`openssl ecparam -genkey -name secp521r1 -noout -out ${SERVER_TMP_FILE}`)
|
||||||
|
const outputPromise = shell(`openssl pkcs8 -topk8 -nocrypt < ${SERVER_TMP_FILE}`)
|
||||||
|
|
||||||
// const outputPromise = $`openssl ecparam -genkey -name secp521r1 -noout | openssl pkcs8 -topk8 -nocrypt`.text()
|
// const outputPromise = $`openssl ecparam -genkey -name secp521r1 -noout | openssl pkcs8 -topk8 -nocrypt`.text()
|
||||||
const filePromise = loadFile(SERVER_PRIVATE_KEY_PATH, true)
|
const filePromise = loadFile(SERVER_PRIVATE_KEY_PATH, true)
|
||||||
|
|
||||||
|
|||||||
6
src/routes/api/program/endpoint/activate/+server.ts
Normal file
6
src/routes/api/program/endpoint/activate/+server.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { RequestHandler } from "@sveltejs/kit";
|
||||||
|
|
||||||
|
|
||||||
|
export const PATCH: RequestHandler = ({ url }) => {
|
||||||
|
|
||||||
|
};
|
||||||
3
src/routes/api/program/endpoint/all/+server.ts
Normal file
3
src/routes/api/program/endpoint/all/+server.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const GET: RequestHandler = ({}) => {
|
||||||
|
|
||||||
|
}
|
||||||
@ -29,7 +29,7 @@ export const POST: RequestHandler = async ({ request, locals, cookies }) => {
|
|||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
// The user is not providing credentials
|
// 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")
|
return error(401, "Unauthorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,8 @@ export const POST: RequestHandler = async ({ request, locals, cookies }) => {
|
|||||||
return redirect(307, "api/program/register")
|
return redirect(307, "api/program/register")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(session)
|
||||||
|
|
||||||
if (session) {
|
if (session) {
|
||||||
// The user is providing valid credentials
|
// The user is providing valid credentials
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/403
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/403
|
||||||
|
|||||||
27
tests/unit/filewatch.test.ts
Normal file
27
tests/unit/filewatch.test.ts
Normal file
@ -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")
|
||||||
|
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user