misc / bugfix: session refresh

This commit is contained in:
2025-08-28 11:15:17 -04:00
parent 315919186b
commit 1d0b310228
7 changed files with 172 additions and 153 deletions

View File

@@ -9,11 +9,6 @@ const YEAR = new Date().getFullYear();
---
<div class="footer">
<small class="block lg:mt-24 mt-16 text-[#1C1C1C] dark:text-[#D4D4D4] footer-text">
<time>© {YEAR}</time>{" "}
{metaData.owner}
</a>
</small>
<RandomMsg client:only="react" />
<div style="margin-top: 15px; bottom: 0%">
<small>Build# {buildNumber}

View File

@@ -22,11 +22,21 @@ export default function LoginPage() {
try {
if (!username) {
setLoading(false);
return toast.error("Username is required");
if (!toast.isActive("login-username-required-toast")) {
return toast.error("Username and password are required",
{
toastId: "login-missing-data-toast",
});
}
}
if (!password) {
setLoading(false);
return toast.error("Password is required");
if (!toast.isActive("login-password-required-toast")) {
return toast.error("Username and password are required",
{
toastId: "login-missing-data-toast",
});
}
}
const formData = new URLSearchParams();
@@ -45,28 +55,52 @@ export default function LoginPage() {
});
if (resp.status === 401) {
toast.error("Invalid username or password");
if (!toast.isActive("login-error-invalid-toast")) {
toast.error("Invalid username or password", {
toastId: "login-error-invalid-toast",
});
}
setLoading(false);
return;
}
if (!resp.ok) {
const data = await resp.json().catch(() => ({}));
toast.error(data.detail ? `Login failed: ${data.detail}` : "Login failed");
if (!toast.isActive("login-error-failed-toast")) {
toast.error(data.detail ? `Login failed: ${data.detail}` : "Login failed",
{
toastId: "login-error-failed-toast",
});
}
setLoading(false);
return;
}
const data = await resp.json();
if (data.access_token) {
toast.success("Login successful!");
if (!toast.isActive("login-success-toast")) {
toast.success("Login successful!",
{
toastId: "login-success-toast",
});
}
window.location.href = "/TRip"; // TODO: fix, hardcoded
} else {
toast.error("Login failed: no access token received");
setLoading(false);
if (!toast.isActive("login-error-no-token-toast")) {
toast.error("Login failed: no access token received",
{
toastId: "login-error-no-token-toast",
});
setLoading(false);
}
}
} catch (error) {
toast.error("Network error during login");
if (!toast.isActive("login-error-network-toast")) {
toast.error("Network error during login",
{
toastId: "login-error-network-toast",
});
}
console.error("Login error:", error);
setLoading(false);
}

View File

@@ -10,7 +10,7 @@ import { confirmDialog, ConfirmDialog } from "primereact/confirmdialog";
import BreadcrumbNav from "./BreadcrumbNav";
import { API_URL } from "@/config";
const STATUS_OPTIONS = ["queued", "started", "compressing", "finished", "failed"];
const STATUS_OPTIONS = ["Queued", "Started", "Compressing", "Finished", "Failed"];
const TAR_BASE_URL = "https://codey.lol/m/m2"; // configurable prefix
export default function RequestManagement() {
@@ -83,7 +83,7 @@ export default function RequestManagement() {
}
}, [isDialogVisible, selectedRequest?.id]);
useEffect(() => {
const hasActive = requests.some((j) => ["queued", "started", "compressing"].includes(j.status));
const hasActive = requests.some((j) => ["Queued", "Started", "Compressing"].includes(j.status));
if (hasActive && !pollingRef.current) pollingRef.current = setInterval(fetchJobs, 1500);
else if (!hasActive && pollingRef.current) {
clearInterval(pollingRef.current);
@@ -104,20 +104,20 @@ export default function RequestManagement() {
const getStatusColorClass = (status) => {
switch (status) {
case "queued": return "bg-yellow-500 text-black";
case "started": return "bg-blue-500 text-white";
case "compressing": return "bg-orange-500 text-white";
case "finished": return "bg-green-500 text-white";
case "failed": return "bg-red-500 text-white";
default: return "bg-gray-500 text-white";
case "Queued": return "bg-yellow-700 text-white";
case "Started": return "bg-blue-700 text-white";
case "Compressing": return "bg-orange-700 text-white";
case "Finished": return "bg-green-700 text-white";
case "Failed": return "bg-red-700 text-white";
default: return "bg-gray-700 text-white";
}
};
const getQualityColorClass = (quality) => {
switch (quality) {
case "FLAC": return "bg-green-500 text-white";
case "Lossy": return "bg-yellow-500 text-white";
default: return "bg-gray-500 text-white";
case "FLAC": return "bg-green-700 text-white";
case "Lossy": return "bg-yellow-700 text-white";
default: return "bg-gray-700 text-white";
}
};
@@ -306,12 +306,11 @@ export default function RequestManagement() {
removableSort
sortMode="multiple"
emptyMessage="No requests found."
responsiveLayout="scroll"
onRowClick={handleRowClick}
>
<Column field="id" header="ID" sortable style={{ width: "8rem" }} body={(row) => textWithEllipsis(row.id, "6rem")} />
<Column field="id" header="ID" style={{ width: "8rem" }} body={(row) => textWithEllipsis(row.id, "6rem")} />
<Column field="target" header="Target" sortable style={{ width: "12rem" }} body={(row) => textWithEllipsis(row.target, "10rem")} />
<Column field="tracks" header="# Tracks" sortable style={{ width: "8rem" }} body={(row) => row.tracks} />
<Column field="tracks" header="# Tracks" style={{ width: "8rem" }} body={(row) => row.tracks} />
<Column field="status" header="Status" body={statusBodyTemplate} style={{ width: "10rem", textAlign: "center" }} sortable />
<Column field="progress" header="Progress" body={(row) => formatProgress(row.progress)} style={{ width: "8rem", textAlign: "center" }} sortable />
<Column
@@ -358,64 +357,71 @@ export default function RequestManagement() {
>
{selectedRequest ? (
<div className="space-y-4 text-sm">
{selectedRequest.id && (
<p><strong>ID:</strong> {selectedRequest.id}</p>
)}
{selectedRequest.target && (
<p><strong>Target:</strong> {selectedRequest.target}</p>
)}
{selectedRequest.tracks && (
<p><strong># Tracks:</strong> {selectedRequest.tracks}</p>
)}
{selectedRequest.status && (<p>
<strong>Status:</strong>{" "}
<span
className={`px-2 py-0.5 rounded-full text-xs font-bold ${getStatusColorClass(selectedRequest.status)}`}
>
{selectedRequest.status}
</span>
</p>)}
{selectedRequest.progress !== undefined && selectedRequest.progress !== null && (
<p><strong>Progress:</strong> {formatProgress(selectedRequest.progress)}</p>
)}
{/* --- Metadata Card --- */}
<div className="p-3 bg-gray-100 dark:bg-neutral-800 rounded-md grid grid-cols-2 gap-4">
{selectedRequest.id && <p className="col-span-2 break-all"><strong>ID:</strong> {selectedRequest.id}</p>}
{selectedRequest.target && <p><strong>Target:</strong> {selectedRequest.target}</p>}
{selectedRequest.tracks && <p><strong># Tracks:</strong> {selectedRequest.tracks}</p>}
{selectedRequest.quality && (
<p>
<strong>Quality:</strong>{" "}
<span className={`px-2 py-0.5 rounded-full text-xs font-bold ${getQualityColorClass(selectedRequest.quality)}`}>
{selectedRequest.quality}
</span>
</p>
)}
</div>
{selectedRequest.enqueued_at && (
<p><strong>Enqueued:</strong> {new Date(selectedRequest.enqueued_at).toLocaleString()}</p>
)}
{selectedRequest.started_at && (
<p><strong>Started:</strong> {new Date(selectedRequest.started_at).toLocaleString()}</p>
)}
{selectedRequest.ended_at && (
<p><strong>Ended:</strong> {new Date(selectedRequest.ended_at).toLocaleString()}</p>
)}
{/* --- Status / Progress Card --- */}
<div className="p-3 bg-gray-100 dark:bg-neutral-800 rounded-md grid grid-cols-2 gap-4">
{selectedRequest.status && (
<p>
<strong>Status:</strong>{" "}
<span className={`px-2 py-0.5 rounded-full text-xs font-bold ${getStatusColorClass(selectedRequest.status)}`}>
{selectedRequest.status}
</span>
</p>
)}
{selectedRequest.progress !== undefined && selectedRequest.progress !== null && (
<p><strong>Progress:</strong> {formatProgress(selectedRequest.progress)}</p>
)}
</div>
{selectedRequest.tarball && (
<p>
<strong>Tarball:</strong>{" "}
<a
href={tarballUrl(selectedRequest.tarball)}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
>
{tarballUrl(selectedRequest.tarball).split("/").pop()}
</a>
</p>
)}
{selectedRequest.quality && (
<p><strong>Quality:</strong>{" "}
<span
className={`px-2 py-0.5 rounded-full text-xs font-bold ${getQualityColorClass(selectedRequest.quality)}`}
>{selectedRequest.quality}</span></p>
)}
</div>
{/* --- Timestamps Card --- */}
<div className="p-3 bg-gray-100 dark:bg-neutral-800 rounded-md grid grid-cols-1 gap-2">
{selectedRequest.enqueued_at && <p><strong>Enqueued:</strong> {new Date(selectedRequest.enqueued_at).toLocaleString()}</p>}
{selectedRequest.started_at && <p><strong>Started:</strong> {new Date(selectedRequest.started_at).toLocaleString()}</p>}
{selectedRequest.ended_at && <p><strong>Ended:</strong> {new Date(selectedRequest.ended_at).toLocaleString()}</p>}
</div>
{/* --- Tarball Card --- */}
{
selectedRequest.tarball && (
<div className="p-3 bg-gray-100 dark:bg-neutral-800 rounded-md">
<p>
<strong>Tarball:</strong>{" "}
<a
href={tarballUrl(selectedRequest.tarball, selectedRequest.quality)}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
>
{tarballUrl(selectedRequest.tarball, selectedRequest.quality).split("/").pop()}
</a>
</p>
</div>
)
}
</div >
) : (
<p>Loading...</p>
)
}
</Dialog >
</div >
);
}