mirror of
https://github.com/hansjone/netx.git
synced 2026-10-10 06:10:47 +08:00
Use custom cli_templates plus community ntc-templates for LLDP and port traffic across ZTE/Huawei/Cisco/H3C/Juniper/Nokia, drop regex parsers, and surface Admin/Phy/Prot on port discover. Also batch-add topology NEs and tighten workbench branding. Co-authored-by: Cursor <cursoragent@cursor.com>
104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
import { useState, type FormEvent } from "react";
|
|
import { useAuth } from "../auth/AuthContext";
|
|
import { useI18n } from "../i18n";
|
|
import { apiPost } from "../services/api";
|
|
|
|
export function ForceChangePasswordPage() {
|
|
const { t } = useI18n();
|
|
const { user, refreshMe, logout } = useAuth();
|
|
const [oldPassword, setOldPassword] = useState("");
|
|
const [newPassword, setNewPassword] = useState("");
|
|
const [confirm, setConfirm] = useState("");
|
|
const [error, setError] = useState("");
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
const onSubmit = async (e: FormEvent) => {
|
|
e.preventDefault();
|
|
setError("");
|
|
if (newPassword.length < 6) {
|
|
setError(t("auth.passwordTooShort"));
|
|
return;
|
|
}
|
|
if (newPassword !== confirm) {
|
|
setError(t("auth.passwordMismatch"));
|
|
return;
|
|
}
|
|
if (newPassword === oldPassword || newPassword === "admin123") {
|
|
setError(t("auth.passwordMustChange"));
|
|
return;
|
|
}
|
|
setBusy(true);
|
|
try {
|
|
await apiPost("/v1/auth/change-password", {
|
|
old_password: oldPassword,
|
|
new_password: newPassword,
|
|
});
|
|
await refreshMe();
|
|
} catch (err) {
|
|
setError(String(err instanceof Error ? err.message : err));
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="login-page">
|
|
<form className="login-card" onSubmit={(e) => void onSubmit(e)}>
|
|
<div className="login-card__brand">NETX</div>
|
|
<h1 className="login-card__title">{t("auth.forceChangeTitle")}</h1>
|
|
<p className="login-card__hint">
|
|
{t("auth.forceChangeHint", { user: user?.username || "admin" })}
|
|
</p>
|
|
<label className="login-card__label">
|
|
{t("auth.oldPassword")}
|
|
<input
|
|
type="password"
|
|
autoComplete="current-password"
|
|
autoFocus
|
|
value={oldPassword}
|
|
onChange={(e) => setOldPassword(e.target.value)}
|
|
disabled={busy}
|
|
required
|
|
/>
|
|
</label>
|
|
<label className="login-card__label">
|
|
{t("auth.newPassword")}
|
|
<input
|
|
type="password"
|
|
autoComplete="new-password"
|
|
value={newPassword}
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
disabled={busy}
|
|
required
|
|
minLength={6}
|
|
/>
|
|
</label>
|
|
<label className="login-card__label">
|
|
{t("auth.confirmPassword")}
|
|
<input
|
|
type="password"
|
|
autoComplete="new-password"
|
|
value={confirm}
|
|
onChange={(e) => setConfirm(e.target.value)}
|
|
disabled={busy}
|
|
required
|
|
minLength={6}
|
|
/>
|
|
</label>
|
|
{error ? <div className="login-card__error">{error}</div> : null}
|
|
<button type="submit" className="login-card__submit" disabled={busy}>
|
|
{busy ? t("auth.savingPassword") : t("auth.savePassword")}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="login-card__submit"
|
|
style={{ background: "#64748b" }}
|
|
disabled={busy}
|
|
onClick={() => void logout()}
|
|
>
|
|
{t("auth.logout")}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|