Clear Web UI session on 401 and keep API/Ingress errors as JSON.
Include the missing resource id in not-found messages, expire auth to Guest after 401, and document Ingress annotations so nginx does not rewrite 404/405 bodies into branded HTML.
This commit is contained in:
+15
-3
@@ -76,7 +76,11 @@ async def require_node(request: Request, node: str) -> None:
|
||||
node,
|
||||
)
|
||||
if not exists:
|
||||
raise ApiError(404, "node does not exist")
|
||||
raise ApiError(
|
||||
404,
|
||||
f"No such node ('{node}')",
|
||||
errors={"node": f"No such node ('{node}')"},
|
||||
)
|
||||
|
||||
|
||||
async def cluster_metadata(request: Request) -> dict[str, Any]:
|
||||
@@ -105,7 +109,11 @@ async def node_metadata(request: Request, node: str) -> dict[str, Any]:
|
||||
node,
|
||||
)
|
||||
if row is None:
|
||||
raise ApiError(404, "node does not exist")
|
||||
raise ApiError(
|
||||
404,
|
||||
f"No such node ('{node}')",
|
||||
errors={"node": f"No such node ('{node}')"},
|
||||
)
|
||||
return state(row["metadata"])
|
||||
|
||||
|
||||
@@ -116,7 +124,11 @@ async def save_node_metadata(request: Request, node: str, metadata: dict[str, An
|
||||
json.dumps(metadata, sort_keys=True),
|
||||
)
|
||||
if status != "UPDATE 1":
|
||||
raise ApiError(404, "node does not exist")
|
||||
raise ApiError(
|
||||
404,
|
||||
f"No such node ('{node}')",
|
||||
errors={"node": f"No such node ('{node}')"},
|
||||
)
|
||||
|
||||
|
||||
def storage_payload(row: Any) -> dict[str, Any]:
|
||||
|
||||
@@ -125,7 +125,11 @@ async def _show_server(
|
||||
ctx.project_id,
|
||||
)
|
||||
if row is None:
|
||||
raise OpenStackError("computeFault", "Instance could not be found", status_code=404)
|
||||
raise OpenStackError(
|
||||
"computeFault",
|
||||
f"Instance '{resource_id}' could not be found",
|
||||
status_code=404,
|
||||
)
|
||||
return {"server": _server_dict(row)}
|
||||
|
||||
|
||||
@@ -164,7 +168,11 @@ async def _update_server(
|
||||
ctx.project_id,
|
||||
)
|
||||
if row is None:
|
||||
raise OpenStackError("computeFault", "Instance could not be found", status_code=404)
|
||||
raise OpenStackError(
|
||||
"computeFault",
|
||||
f"Instance '{resource_id}' could not be found",
|
||||
status_code=404,
|
||||
)
|
||||
return {"server": _server_dict(row)}
|
||||
|
||||
|
||||
@@ -299,7 +307,11 @@ async def delete_server(
|
||||
ctx.project_id,
|
||||
)
|
||||
if result.endswith("0"):
|
||||
raise OpenStackError("computeFault", "Instance could not be found", status_code=404)
|
||||
raise OpenStackError(
|
||||
"computeFault",
|
||||
f"Instance '{server_id}' could not be found",
|
||||
status_code=404,
|
||||
)
|
||||
return Response(status_code=204)
|
||||
|
||||
|
||||
@@ -316,7 +328,11 @@ async def server_action(
|
||||
ctx.project_id,
|
||||
)
|
||||
if row is None:
|
||||
raise OpenStackError("computeFault", "Instance could not be found", status_code=404)
|
||||
raise OpenStackError(
|
||||
"computeFault",
|
||||
f"Instance '{server_id}' could not be found",
|
||||
status_code=404,
|
||||
)
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
action = await request.json()
|
||||
|
||||
@@ -438,7 +438,7 @@ async def _handle_show(
|
||||
item_id,
|
||||
)
|
||||
if row is None:
|
||||
raise OpenStackError("NotFound", f"{op.resource_type} {item_id} not found", status_code=404)
|
||||
raise OpenStackError("NotFound", f"{op.resource_type} '{item_id}' not found", status_code=404)
|
||||
return JSONResponse(_fixture_or_item(op, _row_item(row)), status_code=op.status_code)
|
||||
|
||||
|
||||
|
||||
+32
-15
@@ -4982,12 +4982,7 @@
|
||||
if (!res.ok) throw new Error("session expired");
|
||||
setAuth(true);
|
||||
} catch {
|
||||
state.ticket = null;
|
||||
state.csrf = null;
|
||||
state.username = null;
|
||||
state.project = null;
|
||||
localStorage.removeItem(LS_AUTH);
|
||||
setAuth(false);
|
||||
clearSession({ closePanel: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6342,6 +6337,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
function clearSession(options = {}) {
|
||||
const { toastMessage = null, toastKind = "info", closePanel = true } = options;
|
||||
state.ticket = null;
|
||||
state.csrf = null;
|
||||
state.username = null;
|
||||
state.project = null;
|
||||
try {
|
||||
localStorage.removeItem(LS_AUTH);
|
||||
sessionStorage.removeItem(LS_AUTH);
|
||||
} catch {
|
||||
/* ignore storage failures */
|
||||
}
|
||||
setAuth(false);
|
||||
persistAuth();
|
||||
if (closePanel) closeAuthPanel();
|
||||
resetClusterStats("—");
|
||||
if (toastMessage) toast(toastMessage, toastKind);
|
||||
}
|
||||
|
||||
function logout() {
|
||||
const token = state.ticket;
|
||||
if (token) {
|
||||
@@ -6350,15 +6364,15 @@
|
||||
headers: { "X-Auth-Token": token, "X-Subject-Token": token },
|
||||
}).catch(() => {});
|
||||
}
|
||||
state.ticket = null;
|
||||
state.csrf = null;
|
||||
state.username = null;
|
||||
state.project = null;
|
||||
setAuth(false);
|
||||
persistAuth();
|
||||
closeAuthPanel();
|
||||
resetClusterStats("—");
|
||||
toast("Signed out", "info");
|
||||
clearSession({ toastMessage: "Signed out", toastKind: "info" });
|
||||
}
|
||||
|
||||
function expireSession() {
|
||||
clearSession({
|
||||
toastMessage: "Session expired — sign in again",
|
||||
toastKind: "warn",
|
||||
closePanel: false,
|
||||
});
|
||||
}
|
||||
|
||||
function updateStatusBadge(status) {
|
||||
@@ -6472,6 +6486,9 @@
|
||||
const text = await res.text();
|
||||
let parsed;
|
||||
try { parsed = JSON.parse(text); } catch { parsed = text; }
|
||||
if (res.status === 401 && (state.ticket || state.username)) {
|
||||
expireSession();
|
||||
}
|
||||
return { status: res.status, body: parsed, durationMs: Math.round(performance.now() - started) };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user