V 0.1.1 intermediate changes

Co-authored-by: Oscar Urselli <oscar0urselli@users.noreply.github.com>
This commit is contained in:
Christian Risi 2025-07-03 18:25:50 +00:00
parent 19457d97ae
commit f23cb0e1d2
33 changed files with 603 additions and 56 deletions

View File

@ -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"

View File

@ -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;
} }

View File

@ -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() {
@ -19,12 +31,201 @@ export class EndpointBrokerManager {
} }
public static async createEndpoint(endpoint: IEndpointFS) {
public async getEndpointByName(name: string): IEndpoint { }
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 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
}
} }
}

View File

@ -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.");
} }

View File

@ -26,6 +26,8 @@ export interface IEndpointFS {
ports(): number[] ports(): number[]
headerHash(): string
} }
export type FSHeader = { export type FSHeader = {

View File

@ -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
)
}
}

View File

@ -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()

View File

@ -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
] ]

View File

@ -4,5 +4,6 @@ export interface IEndpoint {
type: EndpointType type: EndpointType
name: string name: string
path: string
} }

View 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()
}
}

View 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")
}
}
}

View File

@ -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
)
} }

View File

@ -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

View File

@ -1,16 +1,26 @@
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) {
@ -35,7 +45,7 @@ export class FileHandle {
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
}
} }
@ -70,6 +100,12 @@ export async function doesFileExist(path: string): Promise<boolean> {
} }
export async function isDir(path: string): Promise<boolean> {
const stats = await Node.stat(path)
return stats.isDirectory()
}
export async function loadFile(path: string, create?: boolean): Promise<FileHandle> { export async function loadFile(path: string, create?: boolean): Promise<FileHandle> {
@ -108,3 +144,12 @@ export async function loadFile(path: string, create?: boolean): Promise<FileHand
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
}

View File

@ -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)

View File

@ -0,0 +1,6 @@
import { RequestHandler } from "@sveltejs/kit";
export const PATCH: RequestHandler = ({ url }) => {
};

View File

@ -0,0 +1,3 @@
export const GET: RequestHandler = ({}) => {
}

View File

@ -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")
} }

View File

@ -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

View 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")
});