feat(storage): rename snapraid_content to snapraid_conf; clarify conf vs content file; add auto-detect and conf viewer

This commit is contained in:
2026-07-07 01:17:55 -04:00
parent 047f0f5c09
commit 599af5a828
11 changed files with 143 additions and 42 deletions
+5 -1
View File
@@ -170,7 +170,7 @@ export interface StorageConfig {
mover_inplace: boolean;
mover_rsync_options: string;
mover_warning_threshold: number;
snapraid_content: string;
snapraid_conf: string;
snapraid_data_dirs: string;
snapraid_parity_dir: string;
snapraid_scrub_plan: number;
@@ -322,6 +322,10 @@ export const api = {
request<StorageJob>("POST", "/storage/jobs", { kind, args }),
cancelStorageJob: (id: number) => request<{ ok: boolean }>("POST", `/storage/jobs/${id}/cancel`),
storageJobStreamUrl: (id: number) => `/api/storage/jobs/${id}/stream`,
getStorageConfFile: () => fetch(`/api/storage/conf-file`).then(r => {
if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
return r.text();
}),
};
export function formatBytes(bytes: number): string {
+71 -7
View File
@@ -41,7 +41,10 @@ export default function Storage() {
const [scrubPlanInput, setScrubPlanInput] = useState("8");
const [dirtyConfig, setDirtyConfig] = useState(false);
const [pendingConfig, setPendingConfig] = useState<Partial<StorageConfig>>({});
const outputRef = useRef<HTMLPreElement>(null);
const [confModalOpen, setConfModalOpen] = useState(false);
const [confModalContent, setConfModalContent] = useState("");
const [confModalError, setConfModalError] = useState("");
const outputRef = useRef<HTMLPreElement | null>(null);
const esRef = useRef<EventSource | null>(null);
useEffect(() => {
@@ -116,6 +119,18 @@ export default function Storage() {
}
}
async function openConfModal() {
setConfModalOpen(true);
setConfModalContent("");
setConfModalError("");
try {
const content = await api.getStorageConfFile();
setConfModalContent(content);
} catch (e: unknown) {
setConfModalError(e instanceof Error ? e.message : String(e));
}
}
async function startJob(kind: JobKind) {
try {
const job = await api.startStorageJob(kind);
@@ -311,12 +326,35 @@ export default function Storage() {
<fieldset className="space-y-3">
<legend className="text-sm font-medium text-slate-300">SnapRAID</legend>
<Field
label="Content file"
value={mergedConfig.snapraid_content ?? ""}
onChange={(v) => handleConfigChange("snapraid_content", v)}
placeholder="/mnt/pool/snapraid.content"
/>
<div>
<Field
label="Archivo de configuración"
value={mergedConfig.snapraid_conf ?? ""}
onChange={(v) => handleConfigChange("snapraid_conf", v)}
placeholder="/etc/snapraid.conf"
/>
<div className="mt-1 flex items-center gap-2">
<button
type="button"
className="text-xs text-slate-500 underline hover:text-slate-300 disabled:opacity-40"
onClick={openConfModal}
disabled={!mergedConfig.snapraid_conf}
>
Ver contenido
</button>
{!mergedConfig.snapraid_conf && (
<span className="text-xs text-amber-400">
No configurado. Busca en{" "}
<code className="text-amber-300">/etc/snapraid.conf</code> o{" "}
<code className="text-amber-300">/usr/local/etc/snapraid.conf</code>
</span>
)}
</div>
<p className="mt-1 text-xs text-slate-500">
Ruta al archivo <code className="text-slate-400">snapraid.conf</code>. Este es el archivo de configuración de texto,{" "}
<span className="text-amber-400">no el archivo binario .content</span>.
</p>
</div>
<Field
label="Directorios de datos (CSV)"
value={mergedConfig.snapraid_data_dirs ?? ""}
@@ -503,6 +541,32 @@ export default function Storage() {
</div>
)}
</div>
{/* Conf file viewer modal */}
{confModalOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60">
<div className="w-full max-w-2xl rounded-lg border border-slate-600 bg-slate-800 p-4 shadow-xl">
<div className="mb-3 flex items-center justify-between">
<h3 className="text-sm font-medium text-slate-200">
{mergedConfig.snapraid_conf ?? "snapraid.conf"}
</h3>
<button
className="text-slate-400 hover:text-white"
onClick={() => setConfModalOpen(false)}
>
</button>
</div>
{confModalError ? (
<p className="text-sm text-red-400">{confModalError}</p>
) : (
<pre className="max-h-96 overflow-auto rounded bg-slate-900 p-3 text-xs text-slate-300 font-mono">
{confModalContent || "Cargando..."}
</pre>
)}
</div>
</div>
)}
</div>
);
}