64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
import { promises as fs } from "node:fs";
|
|
import path from "node:path";
|
|
|
|
import type { AppData } from "@/modules/core/types";
|
|
|
|
const DATA_DIR = process.env.TEATEA_DATA_DIR ?? path.join(process.cwd(), ".data");
|
|
const DATA_FILE = path.join(DATA_DIR, "teatea.json");
|
|
|
|
function createEmptyData(): AppData {
|
|
return {
|
|
accounts: [],
|
|
sessions: [],
|
|
elders: [],
|
|
auditLogs: [],
|
|
};
|
|
}
|
|
|
|
function normalizeData(data: Partial<AppData>): AppData {
|
|
return {
|
|
accounts: Array.isArray(data.accounts) ? data.accounts : [],
|
|
sessions: Array.isArray(data.sessions) ? data.sessions : [],
|
|
elders: Array.isArray(data.elders) ? data.elders : [],
|
|
auditLogs: Array.isArray(data.auditLogs) ? data.auditLogs : [],
|
|
};
|
|
}
|
|
|
|
function isMissingFileError(error: unknown): boolean {
|
|
return (
|
|
typeof error === "object" &&
|
|
error !== null &&
|
|
"code" in error &&
|
|
(error as { code?: unknown }).code === "ENOENT"
|
|
);
|
|
}
|
|
|
|
export async function readData(): Promise<AppData> {
|
|
try {
|
|
const raw = await fs.readFile(DATA_FILE, "utf8");
|
|
const parsed = JSON.parse(raw) as Partial<AppData>;
|
|
return normalizeData(parsed);
|
|
} catch (error) {
|
|
if (isMissingFileError(error)) {
|
|
return createEmptyData();
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export async function writeData(data: AppData): Promise<void> {
|
|
await fs.mkdir(DATA_DIR, { recursive: true });
|
|
const temporaryFile = `${DATA_FILE}.tmp`;
|
|
await fs.writeFile(temporaryFile, JSON.stringify(data, null, 2), "utf8");
|
|
await fs.rename(temporaryFile, DATA_FILE);
|
|
}
|
|
|
|
export async function updateData<T>(mutator: (data: AppData) => T): Promise<T> {
|
|
const data = await readData();
|
|
const result = mutator(data);
|
|
await writeData(data);
|
|
return result;
|
|
}
|
|
|