44 lines · 1.6 KB · TypeScript
RawDownload
import { chmodSync, lstatSync, mkdirSync, readdirSync, unlinkSync } from "node:fs";import { join } from "node:path"; const UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}";const UPLOAD_FILE = new RegExp(`^${UUID}\\.(?:input|wav)$`, "i");const OUTPUT_FILE = new RegExp(`^${UUID}\\.wav$`, "i"); export type ChatterboxStoragePaths = {  readonly speechDirectory: string;  readonly cacheDirectory: string;  readonly voiceDirectory: string;  readonly outputDirectory: string;  readonly uploadDirectory: string;}; export const secureSpeechDirectory = (path: string): void => {  mkdirSync(path, { recursive: true, mode: 0o700 });  if (!lstatSync(path).isDirectory()) throw new Error("Speech storage path is not a directory");  chmodSync(path, 0o700);}; const removeOwnedFiles = (directory: string, pattern: RegExp): void => {  for (const entry of readdirSync(directory, { withFileTypes: true })) {    if (!pattern.test(entry.name) || (!entry.isFile() && !entry.isSymbolicLink())) continue;    unlinkSync(join(directory, entry.name));  }}; export const prepareChatterboxStorage = (paths: ChatterboxStoragePaths): void => {  [    paths.speechDirectory,    paths.cacheDirectory,    paths.voiceDirectory,    paths.outputDirectory,    paths.uploadDirectory,  ].forEach(secureSpeechDirectory);  removeOwnedFiles(paths.uploadDirectory, UPLOAD_FILE);  removeOwnedFiles(paths.outputDirectory, OUTPUT_FILE);}; export const prepareVoicePlaintextStorage = (directory: string): void => {  secureSpeechDirectory(directory);  removeOwnedFiles(directory, OUTPUT_FILE);};