dev #3

Merged
codey merged 3 commits from dev into master 2025-12-24 07:50:59 -05:00
8 changed files with 52 additions and 35 deletions
Showing only changes of commit bb71f662ed - Show all commits

View File

@@ -29,10 +29,19 @@
}
.mobile-menu-dropdown.open {
max-height: 500px;
max-height: none;
overflow: visible;
padding-bottom: 0.75rem;
opacity: 1;
}
.mobile-menu-dropdown a {
font-size: 0.95rem;
line-height: 1.25rem;
padding: 0.6rem 0.75rem;
border-radius: 12px;
}
@media (min-width: 768px) {
.mobile-menu-dropdown {
display: none;

View File

@@ -17,9 +17,9 @@ interface RequestJob {
tracks: number;
quality: string;
status: string;
progress: number;
progress: number | string | null;
type?: string;
tarball_path?: string;
tarball?: string;
created_at?: string;
updated_at?: string;
[key: string]: unknown;
@@ -40,9 +40,13 @@ export default function RequestManagement() {
const pollingDetailRef = useRef<ReturnType<typeof setInterval> | null>(null);
const resolveTarballPath = (job: RequestJob) => job.tarball;
const tarballUrl = (absPath: string | undefined, quality: string) => {
if (!absPath) return null;
const filename = absPath.split("/").pop(); // get "SOMETHING.tar.gz"
// If the backend already stores a fully qualified URL, return as-is
if (/^https?:\/\//i.test(absPath)) return absPath;
return `${TAR_BASE_URL}/${quality}/${filename}`;
};
@@ -172,24 +176,21 @@ export default function RequestManagement() {
const formatProgress = (p: unknown) => {
if (p === null || p === undefined || p === "") return "—";
const num = Number(p);
if (Number.isNaN(num)) return "—";
const pct = num > 1 ? Math.round(num) : num;
const pct = computePct(p);
return `${pct}%`;
};
const computePct = (p: unknown) => {
if (p === null || p === undefined || p === "") return 0;
const num = Number(p);
if (Number.isNaN(num)) return 0;
return Math.min(100, Math.max(0, num > 1 ? Math.round(num) : Math.round(num * 100)));
if (!Number.isFinite(num)) return 0;
const normalized = num > 1 ? num : num * 100;
return Math.min(100, Math.max(0, Math.round(normalized)));
};
const progressBarTemplate = (rowData: RequestJob) => {
const p = rowData.progress;
if (p === null || p === undefined || p === 0) return "—";
const num = Number(p);
if (Number.isNaN(num)) return "—";
if (p === null || p === undefined || p === "") return "—";
const pct = computePct(p);
const getProgressColor = () => {
@@ -341,7 +342,7 @@ export default function RequestManagement() {
</span>
}
body={(row: RequestJob) => {
const url = tarballUrl(row.tarball_path, row.quality || "FLAC");
const url = tarballUrl(resolveTarballPath(row as RequestJob), row.quality || "FLAC");
if (!url) return "—";
const encodedURL = encodeURI(url);
@@ -409,18 +410,25 @@ export default function RequestManagement() {
<strong>Progress:</strong>
<div className="rm-progress-container mt-2">
<div className="rm-progress-track rm-progress-track-lg">
{(() => {
const pctDialog = computePct(selectedRequest.progress);
const status = selectedRequest.status;
const fillColor = status === "Failed" ? "bg-red-500" : status === "Finished" ? "bg-green-500" : "bg-blue-500";
return (
<div
className={`rm-progress-fill ${selectedRequest.status === "Failed" ? "bg-red-500" : selectedRequest.status === "Finished" ? "bg-green-500" : "bg-blue-500"}`}
className={`rm-progress-fill ${fillColor}`}
style={{
['--rm-progress' as string]: (computePct(selectedRequest.progress) / 100).toString(),
borderTopRightRadius: computePct(selectedRequest.progress) >= 100 ? '999px' : 0,
borderBottomRightRadius: computePct(selectedRequest.progress) >= 100 ? '999px' : 0
['--rm-progress' as string]: (pctDialog / 100).toString(),
borderTopRightRadius: pctDialog >= 100 ? '999px' : 0,
borderBottomRightRadius: pctDialog >= 100 ? '999px' : 0
}}
data-pct={computePct(selectedRequest.progress)}
aria-valuenow={Math.min(100, Math.max(0, Number(selectedRequest.progress) > 1 ? Math.round(selectedRequest.progress) : selectedRequest.progress * 100))}
data-pct={pctDialog}
aria-valuenow={pctDialog}
aria-valuemin={0}
aria-valuemax={100}
/>
);
})()}
</div>
<span className="rm-progress-text">{formatProgress(selectedRequest.progress)}</span>
</div>
@@ -437,17 +445,17 @@ export default function RequestManagement() {
{/* --- Tarball Card --- */}
{
selectedRequest.tarball_path && (
selectedRequest.tarball && (
<div className="p-3 bg-gray-100 dark:bg-neutral-800 rounded-md">
<p>
<strong>Tarball:</strong>{" "}
<a
href={encodeURI(tarballUrl(selectedRequest.tarball_path, selectedRequest.quality) || "")}
href={encodeURI(tarballUrl(resolveTarballPath(selectedRequest), selectedRequest.quality) || "")}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
>
{tarballUrl(selectedRequest.tarball_path, selectedRequest.quality)?.split("/").pop()}
{tarballUrl(resolveTarballPath(selectedRequest), selectedRequest.quality)?.split("/").pop()}
</a>
</p>
</div>

View File

@@ -27,9 +27,9 @@ interface ChannelRow {
export async function GET({ request }: APIContext): Promise<Response> {
// Rate limit check
const rateCheck = checkRateLimit(request, {
limit: 20,
limit: 60,
windowMs: 1000,
burstLimit: 100,
burstLimit: 240,
burstWindowMs: 10_000,
});

View File

@@ -143,9 +143,9 @@ async function getChannelVisibleMembers(channelId: string, guildId: string): Pro
export async function GET({ request }) {
// Rate limit check
const rateCheck = checkRateLimit(request, {
limit: 20,
limit: 80,
windowMs: 1000,
burstLimit: 100,
burstLimit: 320,
burstWindowMs: 10_000,
});

View File

@@ -99,9 +99,9 @@ function getSafeImageUrl(originalUrl: string | null, baseUrl: string): string |
export async function GET({ request }: APIContext) {
// Rate limit check
const rateCheck = checkRateLimit(request, {
limit: 30,
limit: 100,
windowMs: 1000,
burstLimit: 150,
burstLimit: 400,
burstWindowMs: 10_000,
});

View File

@@ -7,7 +7,7 @@ import Root from "@/components/AppLayout.jsx";
const user = Astro.locals.user as any;
---
<Base>
<Base title="Lighting">
<section class="page-section">
<Root child="Lighting" user?={user} client:only="react" />
</section>

View File

@@ -9,7 +9,7 @@ const accessDenied = Astro.locals.accessDenied || false;
const requiredRoles = Astro.locals.requiredRoles || [];
---
<Base>
<Base title="Login">
<section class="page-section">
<Root child="LoginPage" loggedIn={isLoggedIn} accessDenied={accessDenied} requiredRoles={requiredRoles} client:only="react" />
</section>

View File

@@ -4,7 +4,7 @@ import Root from "../components/AppLayout.jsx";
import "@styles/MemeGrid.css";
---
<Base hideFooter>
<Base hideFooter title="Memes">
<section class="page-section">
<Root child="Memes" client:only="react" />
</section>