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": {
|
||||
"extensions": [
|
||||
"svelte.svelte-vscode",
|
||||
"vitest.explorer",
|
||||
"william-voyek.vscode-nginx",
|
||||
"fabiospampinato.vscode-highlight",
|
||||
"fabiospampinato.vscode-todo-plus"
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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<string, IEndpointFS>
|
||||
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<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.");
|
||||
}
|
||||
|
||||
async getSSLTerminationByName(name: string): Promise<SSLTermination | null> {
|
||||
async getSSLTerminationByPath(name: string): Promise<SSLTermination | null> {
|
||||
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.");
|
||||
}
|
||||
|
||||
async deleteSSLTerminationByName(name: string): Promise<SSLTermination | null> {
|
||||
async deleteSSLTerminationByPath(name: string): Promise<SSLTermination | null> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,8 @@ export interface IEndpointFS {
|
||||
|
||||
ports(): number[]
|
||||
|
||||
headerHash(): string
|
||||
|
||||
}
|
||||
|
||||
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 { 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()
|
||||
|
||||
@ -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
|
||||
]
|
||||
|
||||
|
||||
@ -4,5 +4,6 @@ export interface IEndpoint {
|
||||
|
||||
type: EndpointType
|
||||
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>
|
||||
|
||||
// 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<SSLTermination>
|
||||
|
||||
activateEndpointByPath(
|
||||
path: string
|
||||
): Promise<boolean>
|
||||
|
||||
deactivateEndpointByPath(
|
||||
path: string
|
||||
): Promise<boolean>
|
||||
|
||||
|
||||
// Getting endpoints may be null, react over them
|
||||
getSSLTerminationByName(
|
||||
name: string
|
||||
getSSLTerminationByPath(
|
||||
path: string
|
||||
): Promise<SSLTermination|null>
|
||||
|
||||
|
||||
// Throw if something goes wrong
|
||||
modifySSLTerminationByName(
|
||||
name: string,
|
||||
modifySSLTerminationByPath(
|
||||
path: string,
|
||||
changes: SSLTerminationChanges
|
||||
): Promise<SSLTermination>
|
||||
|
||||
|
||||
deleteSSLTerminationByName(
|
||||
name: string
|
||||
deleteSSLTerminationByPath(
|
||||
path: string
|
||||
): Promise<SSLTermination|null>
|
||||
|
||||
|
||||
@ -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<SSLTermination|null> {
|
||||
|
||||
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<SSLTermination> {
|
||||
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<SSLTermination|null> {
|
||||
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<SSLTermination[]> {
|
||||
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 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
|
||||
|
||||
@ -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<boolean> {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
let fd : Node.FileHandle | null = null
|
||||
let fd: Node.FileHandle | null = null
|
||||
|
||||
try {
|
||||
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
|
||||
fd = await Node.open(path, "w", DEFAULT_MODE)
|
||||
await fd.close()
|
||||
|
||||
|
||||
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 { 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";
|
||||
|
||||
export async function openSSLInit() {
|
||||
@ -12,7 +12,9 @@ export async function openSSLInit() {
|
||||
export async function openSSLCreatePrivateKey() {
|
||||
|
||||
// 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 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) {
|
||||
// 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")
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
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