mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
fix: update the logic for change password
This commit is contained in:
@@ -31,7 +31,7 @@ export default function ProfilePage() {
|
||||
const [langOpen, setLangOpen] = useState(false);
|
||||
const [settings, setSettings] = useState({ fallbackStrategy: "fill-first" });
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [passwords, setPasswords] = useState({ current: "", new: "", confirm: "" });
|
||||
const [passwords, setPasswords] = useState({ new: "", confirm: "" });
|
||||
const [passStatus, setPassStatus] = useState({ type: "", message: "" });
|
||||
const [passLoading, setPassLoading] = useState(false);
|
||||
const [dbLoading, setDbLoading] = useState(false);
|
||||
@@ -138,7 +138,6 @@ export default function ProfilePage() {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
currentPassword: passwords.current,
|
||||
newPassword: passwords.new,
|
||||
}),
|
||||
});
|
||||
@@ -147,7 +146,7 @@ export default function ProfilePage() {
|
||||
|
||||
if (res.ok) {
|
||||
setPassStatus({ type: "success", message: "Password updated successfully" });
|
||||
setPasswords({ current: "", new: "", confirm: "" });
|
||||
setPasswords({ new: "", confirm: "" });
|
||||
} else {
|
||||
setPassStatus({ type: "error", message: data.error || "Failed to update password" });
|
||||
}
|
||||
@@ -421,25 +420,6 @@ export default function ProfilePage() {
|
||||
<h3 className="text-base sm:text-lg font-semibold">Password</h3>
|
||||
</div>
|
||||
<form onSubmit={handlePasswordChange} className="flex flex-col gap-4">
|
||||
{settings.hasPassword && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-xs sm:text-sm font-medium">Current Password</label>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Enter current password"
|
||||
value={passwords.current}
|
||||
onChange={(e) => setPasswords({ ...passwords, current: e.target.value })}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* {!settings.hasPassword && (
|
||||
<div className="p-3 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||
<p className="text-sm text-blue-600 dark:text-blue-400">
|
||||
Setting password for the first time. Leave current password empty or use default: <code className="bg-blue-500/20 px-1 rounded">123456</code>
|
||||
</p>
|
||||
</div>
|
||||
)} */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-xs sm:text-sm font-medium">New Password</label>
|
||||
|
||||
@@ -131,7 +131,6 @@ export default function ProviderLimits() {
|
||||
const [lastUpdated, setLastUpdated] = useState(null);
|
||||
const [hasHydratedAutoRefresh, setHasHydratedAutoRefresh] = useState(false);
|
||||
const [refreshingAll, setRefreshingAll] = useState(false);
|
||||
const [countdown, setCountdown] = useState(60);
|
||||
const [connectionsLoading, setConnectionsLoading] = useState(true);
|
||||
const [deletingId, setDeletingId] = useState(null);
|
||||
const [togglingId, setTogglingId] = useState(null);
|
||||
@@ -165,7 +164,6 @@ export default function ProviderLimits() {
|
||||
});
|
||||
|
||||
const intervalRef = useRef(null);
|
||||
const countdownRef = useRef(null);
|
||||
const tickCountRef = useRef(0);
|
||||
|
||||
const fetchConnections = useCallback(
|
||||
@@ -461,7 +459,6 @@ export default function ProviderLimits() {
|
||||
if (refreshingAll) return;
|
||||
|
||||
setRefreshingAll(true);
|
||||
setCountdown(60);
|
||||
|
||||
// Throttle Claude: poll its quota every Nth auto-tick (manual force bypasses)
|
||||
const tick = (tickCountRef.current += 1);
|
||||
@@ -571,10 +568,6 @@ export default function ProviderLimits() {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
if (countdownRef.current) {
|
||||
clearInterval(countdownRef.current);
|
||||
countdownRef.current = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -583,17 +576,8 @@ export default function ProviderLimits() {
|
||||
refreshAll();
|
||||
}, REFRESH_INTERVAL_MS);
|
||||
|
||||
// Countdown interval
|
||||
countdownRef.current = setInterval(() => {
|
||||
setCountdown((prev) => {
|
||||
if (prev <= 1) return 60;
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
if (intervalRef.current) clearInterval(intervalRef.current);
|
||||
if (countdownRef.current) clearInterval(countdownRef.current);
|
||||
};
|
||||
}, [autoRefresh, refreshAll, hasHydratedAutoRefresh]);
|
||||
|
||||
@@ -605,16 +589,9 @@ export default function ProviderLimits() {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
if (countdownRef.current) {
|
||||
clearInterval(countdownRef.current);
|
||||
countdownRef.current = null;
|
||||
}
|
||||
} else if (autoRefresh && hasHydratedAutoRefresh) {
|
||||
// Resume auto-refresh when tab becomes visible
|
||||
intervalRef.current = setInterval(() => refreshAll(), REFRESH_INTERVAL_MS);
|
||||
countdownRef.current = setInterval(() => {
|
||||
setCountdown((prev) => (prev <= 1 ? 60 : prev - 1));
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -927,11 +904,6 @@ export default function ProviderLimits() {
|
||||
<span className="hidden text-text-primary sm:inline">
|
||||
Auto-refresh
|
||||
</span>
|
||||
{autoRefresh && (
|
||||
<span className="text-[10px] text-text-muted tabular-nums">
|
||||
({countdown}s)
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ export default function RequestDetailsTab() {
|
||||
className="border-b border-black/5 dark:border-white/5 last:border-b-0 hover:bg-black/[0.02] dark:hover:bg-white/[0.02] transition-colors"
|
||||
>
|
||||
<td className="whitespace-nowrap p-4 text-sm text-text-main">
|
||||
{formatVietnamDateTime(detail.timestamp, { dateStyle: "medium", timeStyle: "medium" }) || "—"}
|
||||
{formatVietnamDateTime(detail.timestamp, { dateStyle: "medium", timeStyle: "short" }) || "—"}
|
||||
</td>
|
||||
<td className="max-w-[260px] truncate p-4 font-mono text-sm text-text-main">
|
||||
{detail.model}
|
||||
@@ -359,7 +359,7 @@ export default function RequestDetailsTab() {
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-text-muted">Timestamp:</span>{" "}
|
||||
<span className="text-text-main">{formatVietnamDateTime(selectedDetail.timestamp, { dateStyle: "medium", timeStyle: "medium" }) || "—"}</span>
|
||||
<span className="text-text-main">{formatVietnamDateTime(selectedDetail.timestamp, { dateStyle: "medium", timeStyle: "short" }) || "—"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-text-muted">Provider:</span>{" "}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { applyOutboundProxyEnv } from "@/lib/network/outboundProxy";
|
||||
import { resetComboRotation } from "open-sse/services/combo.js";
|
||||
import { runQuotaAutoPingTick } from "@/shared/services/quotaAutoPing";
|
||||
import { requireCurrentDashboardUser, requireUsageDashboardUser } from "@/lib/auth/currentUser";
|
||||
import { updateUser, verifyUserPassword } from "@/lib/db";
|
||||
import { updateUser } from "@/lib/db";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const revalidate = 0;
|
||||
@@ -115,7 +115,7 @@ export async function PATCH(request) {
|
||||
// Strip protected secrets before any internal handling sets them
|
||||
for (const key of PROTECTED_SETTING_KEYS) delete body[key];
|
||||
|
||||
// If updating password, hash it
|
||||
// An authenticated dashboard session is sufficient to change its own password.
|
||||
if (body.newPassword) {
|
||||
let user;
|
||||
try {
|
||||
@@ -124,12 +124,6 @@ export async function PATCH(request) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (!body.currentPassword) {
|
||||
return NextResponse.json({ error: "Current password required" }, { status: 400 });
|
||||
}
|
||||
if (!(await verifyUserPassword(user.id, body.currentPassword))) {
|
||||
return NextResponse.json({ error: "Invalid current password" }, { status: 401 });
|
||||
}
|
||||
await updateUser(user.id, { password: body.newPassword });
|
||||
delete body.newPassword;
|
||||
delete body.currentPassword;
|
||||
|
||||
@@ -1002,7 +1002,6 @@ function formatLogDate(date = new Date()) {
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hourCycle: "h23",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function RequestLogger() {
|
||||
<h2 className="text-xl font-semibold">Request Logs</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium text-text-muted flex items-center gap-2 cursor-pointer">
|
||||
<span>Auto Refresh (3s)</span>
|
||||
<span>Auto Refresh</span>
|
||||
<div
|
||||
onClick={() => setAutoRefresh(!autoRefresh)}
|
||||
className={`relative inline-flex h-5 w-9 items-center rounded-full transition-colors focus:outline-none ${autoRefresh ? "bg-primary" : "bg-bg-subtle border border-border"
|
||||
|
||||
@@ -13,18 +13,18 @@ import UsageChart from "@/app/(dashboard)/dashboard/usage/components/UsageChart"
|
||||
|
||||
function timeAgo(timestamp) {
|
||||
const diff = Math.floor((Date.now() - new Date(timestamp)) / 1000);
|
||||
if (diff < 60) return `${diff}s ago`;
|
||||
if (diff < 60) return "Just now";
|
||||
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
|
||||
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
|
||||
return `${Math.floor(diff / 86400)}d ago`;
|
||||
}
|
||||
|
||||
// Auto-update time display every second without re-rendering parent
|
||||
// Keep relative time current without exposing second-level precision.
|
||||
function TimeAgo({ timestamp }) {
|
||||
const [, setTick] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => setTick(t => t + 1), 1000);
|
||||
const timer = setInterval(() => setTick(t => t + 1), 60000);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user