2e1f4d7929
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 lines
1.3 KiB
TypeScript
38 lines
1.3 KiB
TypeScript
import { randomBytes } from "node:crypto";
|
||
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
||
import path from "node:path";
|
||
import { isValidStorageKey, type StorageDriver } from "./types";
|
||
|
||
/** Yerel disk sürücüsü. Yazma atomiktir (geçici dosya + rename); yarım dosya servis edilmez. */
|
||
export function createLocalDriver(baseDir: string): StorageDriver {
|
||
const root = path.resolve(baseDir);
|
||
|
||
function resolve(key: string): string {
|
||
if (!isValidStorageKey(key)) throw new Error(`Geçersiz depolama anahtarı: ${key}`);
|
||
const full = path.resolve(root, key);
|
||
if (!full.startsWith(root + path.sep)) throw new Error("Depolama kökü dışına çıkılamaz.");
|
||
return full;
|
||
}
|
||
|
||
return {
|
||
async put(key, data) {
|
||
const full = resolve(key);
|
||
await mkdir(path.dirname(full), { recursive: true, mode: 0o750 });
|
||
const tmp = `${full}.${randomBytes(6).toString("hex")}.tmp`;
|
||
await writeFile(tmp, data, { mode: 0o640 });
|
||
await rename(tmp, full);
|
||
},
|
||
async get(key) {
|
||
try {
|
||
return await readFile(resolve(key));
|
||
} catch (error) {
|
||
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
||
throw error;
|
||
}
|
||
},
|
||
async delete(key) {
|
||
await rm(resolve(key), { force: true });
|
||
},
|
||
};
|
||
}
|