SSL-Sniffer/src/lib/server/utils/filesystem-utils.ts

175 lines
3.3 KiB
TypeScript
Raw Normal View History

import { Stats, type OpenMode } from 'node:fs';
2025-07-02 14:49:23 +00:00
import * as Node from 'node:fs/promises';
import { createHash, type BinaryLike } from 'node:crypto';
2025-07-02 14:49:23 +00:00
export type FileStats = Stats
2025-07-02 14:49:23 +00:00
export class FileHandle {
private path: string
private fd: Node.FileHandle | null
private get file() {
if (!this.fd) {
return this.path
}
return this.fd
}
2025-07-02 14:49:23 +00:00
constructor(
path: string
) {
this.path = path
this.fd = null
2025-07-02 14:49:23 +00:00
}
public async text(encoding?: BufferEncoding) {
2025-07-02 14:49:23 +00:00
if (!encoding) {
encoding = "utf-8"
}
const fileContent = await Node.readFile(this.path, { encoding: encoding })
2025-07-02 14:49:23 +00:00
return fileContent
}
public async write(text: string, append?: boolean) {
if (!append) {
append = true
}
const flag: OpenMode = append ? "a+" : "w"
2025-07-02 14:49:23 +00:00
await Node.writeFile(this.file, text, { flag: flag })
2025-07-02 14:49:23 +00:00
}
public async lastChange() {
const stats = await Node.stat(this.path)
return stats.mtime
}
public async getStats() {
const stats = await Node.stat(this.path)
return stats
}
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
}
public async delete() {
await Node.unlink(this.path)
}
2025-07-02 14:49:23 +00:00
}
/**
* Checks if the file exists
*
* @param path `string` path for the file
* @returns if the file is readable
*/
export async function doesFileExist(path: string): Promise<boolean> {
try {
await Node.access(path)
} catch {
return false
}
return true
}
export async function isDir(path: string): Promise<boolean> {
const stats = await Node.stat(path)
return stats.isDirectory()
2025-07-02 14:49:23 +00:00
}
export async function loadFile(path: string, create?: boolean): Promise<FileHandle> {
const DEFAULT_MODE = 0o600
// Safe to use
// worst case: create = false -> create = false
if (!create) {
create = false
}
let fd: Node.FileHandle | null = null
2025-07-02 14:49:23 +00:00
try {
fd = await Node.open(path, "r+", DEFAULT_MODE)
} catch {
}
// We have a FD, return it
if (fd) {
await fd.close()
return new FileHandle(path)
}
if (!create) {
// UGLY: make more specific
throw new Error("The required file does not exist")
}
// We do want this to throw without catching here
fd = await Node.open(path, "w", DEFAULT_MODE)
await fd.close()
2025-07-02 14:49:23 +00:00
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
}
2025-07-04 03:05:43 +00:00
export async function listFiles(path: string, recursive?: boolean): Promise<string[]> {
if (!recursive) {
recursive = false
}
if (!await isDir(path)) {
// UGLY: be specific
throw new Error("This is not a directory")
}
return await Node.readdir(path, {recursive: recursive})
}