86 lines
2.5 KiB
TypeScript
86 lines
2.5 KiB
TypeScript
import fs from 'fs/promises';
|
|
import path from 'path';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
|
|
const DATA_DIR = path.join(process.cwd(), 'data');
|
|
|
|
// Ensure data directory exists
|
|
const initDataDir = async () => {
|
|
try {
|
|
await fs.access(DATA_DIR);
|
|
} catch {
|
|
await fs.mkdir(DATA_DIR, { recursive: true });
|
|
}
|
|
};
|
|
|
|
initDataDir();
|
|
|
|
export class FileStorageService<T extends { id: string }> {
|
|
private filePath: string;
|
|
|
|
constructor(filename: string) {
|
|
this.filePath = path.join(DATA_DIR, filename);
|
|
}
|
|
|
|
private async readFile(): Promise<T[]> {
|
|
try {
|
|
await initDataDir();
|
|
const data = await fs.readFile(this.filePath, 'utf-8');
|
|
return JSON.parse(data);
|
|
} catch (error: unknown) {
|
|
const err = error as { code?: string };
|
|
if (err.code === 'ENOENT') {
|
|
return []; // File doesn't exist yet, return empty array
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
private async writeFile(data: T[]): Promise<void> {
|
|
await initDataDir();
|
|
await fs.writeFile(this.filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
}
|
|
|
|
async getAll(): Promise<T[]> {
|
|
return this.readFile();
|
|
}
|
|
|
|
async getById(id: string): Promise<T | null> {
|
|
const items = await this.readFile();
|
|
return items.find(item => item.id === id) || null;
|
|
}
|
|
|
|
async create(data: Omit<T, 'id'>): Promise<T> {
|
|
const items = await this.readFile();
|
|
const newItem = { ...data, id: uuidv4() } as T;
|
|
items.push(newItem);
|
|
await this.writeFile(items);
|
|
return newItem;
|
|
}
|
|
|
|
async update(id: string, data: Partial<T>): Promise<T | null> {
|
|
const items = await this.readFile();
|
|
const index = items.findIndex(item => item.id === id);
|
|
if (index === -1) return null;
|
|
|
|
items[index] = { ...items[index], ...data };
|
|
await this.writeFile(items);
|
|
return items[index];
|
|
}
|
|
|
|
async delete(id: string): Promise<boolean> {
|
|
const items = await this.readFile();
|
|
const filtered = items.filter(item => item.id !== id);
|
|
if (filtered.length === items.length) return false;
|
|
|
|
await this.writeFile(filtered);
|
|
return true;
|
|
}
|
|
|
|
// Helper for filtering by foreign keys (e.g., get parts by projectId)
|
|
async getByField(field: keyof T, value: T[keyof T]): Promise<T[]> {
|
|
const items = await this.readFile();
|
|
return items.filter(item => item[field] === value);
|
|
}
|
|
}
|