File Storage
The storage module (lib/storage.ts) provides a safe file I/O layer for all file operations in Open Genie. All paths are sandboxed within the configured storage root.
Configuration
| Variable | Default | Description |
|---|---|---|
GENIE_STORAGE_PATH | ./data/files | Root directory for all file storage |
Directory Structure
data/files/
├── uploads/ # User-uploaded files
├── camera-captures/ # Camera snapshot images
├── .thumbnails/ # Generated thumbnails (hidden)
└── .cache/ # Temporary cache files (hidden)
API
saveFile(subPath, data)
Write a buffer or readable stream to storage.
import { saveFile } from "@/lib/storage";
await saveFile("uploads/photo.jpg", buffer);
await saveFile("camera-captures/cam1/frame.jpg", readableStream);
getFilePath(subPath)
Resolve a sub-path to an absolute path and verify it exists.
const absPath = await getFilePath("uploads/photo.jpg");
// Returns absolute path or null if not found
deleteFile(subPath)
Remove a file from storage.
await deleteFile("uploads/old-photo.jpg");
listFiles(directory, options?)
List files in a storage directory.
const files = await listFiles("uploads", {
recursive: true,
extensions: [".jpg", ".png"],
});
// Returns array of { name, path, size, mtime }
getStorageStats()
Get aggregate statistics about storage usage.
const stats = await getStorageStats();
// { totalSize, fileCount, byCategory: { uploads: {...}, captures: {...} } }
createFileReadStream(path, options?)
Create a Node.js readable stream for serving files.
const stream = createFileReadStream("uploads/video.mp4", {
start: 0,
end: 1024 * 1024, // First 1MB
});
ensureDirectory(subPath)
Create a directory (and parents) if it doesn't exist.
await ensureDirectory("camera-captures/cam1");
Security
All path operations go through resolveSafe(), which:
- Resolves the path to an absolute path
- Verifies it stays within
GENIE_STORAGE_PATH - Rejects any path traversal attempts (e.g.,
../../etc/passwd)
// These will throw:
await saveFile("../../etc/passwd", data); // Path traversal
await getFilePath("/absolute/path/file.txt"); // Outside storage root