fix(nfs): reconcile clients[i].advanced type mismatch (object vs string)
backend: db.NFSClient.Advanced is db.NFSAdvanced (struct → JSON object) handler: nfsClientRequest.Advanced was string → now db.NFSAdvanced frontend: api.ts NFSClient.advanced string → NFSAdvanced (object) frontend: Nfs.tsx updated to work with object advanced per host Without this fix, editing an existing NFS export with per-host options (loaded from DB via migration 0005) would 400 on PUT because the backend expected a string but received a JSON object.
This commit is contained in:
+10
-1
@@ -15,7 +15,16 @@ export interface NFSClient {
|
||||
async: boolean;
|
||||
root_squash: boolean;
|
||||
subtree_check: boolean;
|
||||
advanced: string;
|
||||
advanced: NFSAdvanced;
|
||||
}
|
||||
|
||||
export interface NFSAdvanced {
|
||||
all_squash: boolean;
|
||||
secure: boolean;
|
||||
wdelay: boolean;
|
||||
hide: boolean;
|
||||
crossmnt: boolean;
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
export interface NFSExport {
|
||||
|
||||
+23
-24
@@ -30,14 +30,18 @@ function serializeAdvanced(m: Record<string, boolean>): string {
|
||||
return JSON.stringify(m);
|
||||
}
|
||||
|
||||
function clientDefaults(exp: Partial<NFSExport>): Partial<NFSClient> {
|
||||
function emptyAdvanced() {
|
||||
return { all_squash: false, secure: false, wdelay: false, hide: false, crossmnt: false };
|
||||
}
|
||||
|
||||
function clientDefaults(exp: Partial<NFSExport>): NFSClient {
|
||||
return {
|
||||
host: "",
|
||||
read_only: exp.read_only ?? false,
|
||||
async: exp.async ?? false,
|
||||
root_squash: exp.root_squash ?? true,
|
||||
subtree_check: exp.subtree_check ?? false,
|
||||
advanced: exp.advanced ?? "{}",
|
||||
advanced: emptyAdvanced(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,7 +76,10 @@ export default function Nfs() {
|
||||
setAdvanced(parseAdvanced(x.advanced ?? "{}"));
|
||||
setShowAdvanced(false);
|
||||
setEditing(x);
|
||||
setHostDrafts(x.clients.map(c => ({ ...c })));
|
||||
setHostDrafts(x.clients.map(c => ({
|
||||
...c,
|
||||
advanced: c.advanced ?? emptyAdvanced(),
|
||||
})));
|
||||
}
|
||||
|
||||
function openNew() {
|
||||
@@ -89,10 +96,7 @@ export default function Nfs() {
|
||||
|
||||
const payload = {
|
||||
...editing,
|
||||
clients: hostDrafts.map(h => ({
|
||||
...h,
|
||||
advanced: h.advanced !== undefined ? h.advanced : serializeAdvanced(advanced),
|
||||
})),
|
||||
clients: hostDrafts.map(h => ({ ...h })),
|
||||
advanced: serializeAdvanced(advanced),
|
||||
};
|
||||
|
||||
@@ -138,9 +142,7 @@ export default function Nfs() {
|
||||
function toggleHostAdvanced(idx: number, key: string) {
|
||||
setHostDrafts(prev => prev.map((h, i) => {
|
||||
if (i !== idx) return h;
|
||||
const adv = parseAdvanced(h.advanced || "{}");
|
||||
adv[key] = !adv[key];
|
||||
return { ...h, advanced: serializeAdvanced(adv) };
|
||||
return { ...h, advanced: { ...h.advanced, [key]: !h.advanced[key] } };
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -262,20 +264,17 @@ export default function Nfs() {
|
||||
{showAdvanced ? "▾" : "▸"} Avanzado
|
||||
</summary>
|
||||
<div className="mt-1 grid grid-cols-2 gap-y-1">
|
||||
{ADVANCED_KEYS.map(({ key, label }) => {
|
||||
const adv = parseAdvanced(c.advanced || "{}");
|
||||
return (
|
||||
<label key={key} className="flex items-center gap-1.5 text-xs text-slate-300">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="checkbox"
|
||||
checked={!!adv[key]}
|
||||
onChange={() => toggleHostAdvanced(idx, key)}
|
||||
/>
|
||||
{label}
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
{ADVANCED_KEYS.map(({ key, label }) => (
|
||||
<label key={key} className="flex items-center gap-1.5 text-xs text-slate-300">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="checkbox"
|
||||
checked={!!c.advanced?.[key]}
|
||||
onChange={() => toggleHostAdvanced(idx, key)}
|
||||
/>
|
||||
{label}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user