misc / bugfix: session refresh
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 >
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user