2026-02-20 10:31:56 -06:00
|
|
|
import fs from "node:fs";
|
|
|
|
|
import type { PaperclipConfig } from "../config/schema.js";
|
|
|
|
|
import type { CheckResult } from "./index.js";
|
|
|
|
|
import { resolveRuntimeLikePath } from "./path-resolver.js";
|
|
|
|
|
|
|
|
|
|
export function storageCheck(config: PaperclipConfig, configPath?: string): CheckResult {
|
|
|
|
|
if (config.storage.provider === "local_disk") {
|
|
|
|
|
const baseDir = resolveRuntimeLikePath(config.storage.localDisk.baseDir, configPath);
|
|
|
|
|
if (!fs.existsSync(baseDir)) {
|
2026-03-04 09:58:30 -06:00
|
|
|
fs.mkdirSync(baseDir, { recursive: true });
|
2026-02-20 10:31:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
fs.accessSync(baseDir, fs.constants.W_OK);
|
|
|
|
|
return {
|
|
|
|
|
name: "Storage",
|
|
|
|
|
status: "pass",
|
|
|
|
|
message: `Local disk storage is writable: ${baseDir}`,
|
|
|
|
|
};
|
|
|
|
|
} catch {
|
|
|
|
|
return {
|
|
|
|
|
name: "Storage",
|
|
|
|
|
status: "fail",
|
|
|
|
|
message: `Local storage directory is not writable: ${baseDir}`,
|
|
|
|
|
canRepair: false,
|
|
|
|
|
repairHint: "Check file permissions for storage.localDisk.baseDir",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bucket = config.storage.s3.bucket.trim();
|
|
|
|
|
const region = config.storage.s3.region.trim();
|
|
|
|
|
if (!bucket || !region) {
|
|
|
|
|
return {
|
|
|
|
|
name: "Storage",
|
|
|
|
|
status: "fail",
|
|
|
|
|
message: "S3 storage requires non-empty bucket and region",
|
|
|
|
|
canRepair: false,
|
2026-03-03 08:45:26 -06:00
|
|
|
repairHint: "Run `paperclipai configure --section storage`",
|
2026-02-20 10:31:56 -06:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
name: "Storage",
|
|
|
|
|
status: "warn",
|
|
|
|
|
message: `S3 storage configured (bucket=${bucket}, region=${region}). Reachability check is skipped in doctor.`,
|
|
|
|
|
canRepair: false,
|
|
|
|
|
repairHint: "Verify credentials and endpoint in deployment environment",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|