feat(storage): rename snapraid_content to snapraid_conf; clarify conf vs content file; add auto-detect and conf viewer
This commit is contained in:
+5
-1
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user