Skip to content

Commit 9ae1bd3

Browse files
committed
Implement gallery upload feature with image and video management in Admin section
1 parent c5cfb3c commit 9ae1bd3

3 files changed

Lines changed: 370 additions & 83 deletions

File tree

src/lib/api.ts

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -531,21 +531,74 @@ export function backgroundImageUrl(filename: string): string {
531531

532532
// Tournament images
533533
export async function uploadTournamentImage(file: File, tournamentId?: string | number): Promise<unknown> {
534-
// Backend expects: POST /api/v1/upload/tounament/image?tounament_id={id}
535-
// We'll be generous and also include a form field for broader compatibility.
534+
// Try multiple endpoint/param combinations to maximize compatibility
536535
const fd = new FormData();
536+
// Match backend exactly: single field named "file"
537537
fd.set("file", file);
538-
if (tournamentId !== undefined) {
539-
// Some backends read form-data, others read query param (with the misspelled key)
540-
fd.set("tournament_id", String(tournamentId));
541-
}
542538
const token = getAuthToken();
543-
const url = tournamentId !== undefined
544-
? `${buildUrl(API_PATHS.uploadTournamentImage)}?tounament_id=${encodeURIComponent(String(tournamentId))}`
545-
: buildUrl(API_PATHS.uploadTournamentImage);
546-
const res = await fetch(url, { method: "POST", headers: token ? { Authorization: `Bearer ${token}` } : undefined, body: fd });
547-
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
548-
return await res.json();
539+
// Prepare multiple auth header variants to accommodate different backend schemes
540+
const authHeaderVariants: Array<Record<string, string> | undefined> = (() => {
541+
if (!token) return [undefined];
542+
return [
543+
{ Authorization: `Bearer ${token}` },
544+
{ Authorization: `JWT ${token}` },
545+
{ Authorization: `Token ${token}` },
546+
{ Authorization: token }, // raw token as Authorization value
547+
{ "X-Auth-Token": token },
548+
{ "X-API-KEY": token },
549+
];
550+
})();
551+
const idQ = tournamentId !== undefined ? String(tournamentId) : undefined;
552+
const candidates: string[] = [];
553+
// Provided paths (with misspelling)
554+
if (idQ) candidates.push(`${buildUrl(API_PATHS.uploadTournamentImage)}?tounament_id=${encodeURIComponent(idQ)}`);
555+
candidates.push(buildUrl(API_PATHS.uploadTournamentImage));
556+
// Alternate: correct spelling "tournament"
557+
const altBase = buildUrl("/api/v1/upload/tournament/image");
558+
if (idQ) {
559+
candidates.push(`${altBase}?tournament_id=${encodeURIComponent(idQ)}`);
560+
candidates.push(`${altBase}?tounament_id=${encodeURIComponent(idQ)}`);
561+
}
562+
candidates.push(altBase);
563+
564+
let lastErr: unknown = null;
565+
const attempts: Array<{ url: string; status?: number; note?: string; auth?: string }> = [];
566+
for (const url of candidates) {
567+
// Try each auth scheme for this URL
568+
for (const hdr of authHeaderVariants) {
569+
try {
570+
const headers: Record<string, string> | undefined = hdr ? { ...hdr, accept: "application/json" } : { accept: "application/json" };
571+
const res = await fetch(url, { method: "POST", headers, body: fd });
572+
if (res.ok) {
573+
try { return await res.json(); } catch { return null; }
574+
}
575+
// For 404/405/415, attempt next combination
576+
if ([404, 405, 415].includes(res.status)) {
577+
lastErr = new Error(`${res.status} ${res.statusText}`);
578+
attempts.push({ url, status: res.status, auth: hdr ? Object.keys(hdr)[0] : "none" });
579+
// break auth loop if clearly not found; try next URL
580+
break;
581+
}
582+
const text = await res.text();
583+
const msg = text || `${res.status} ${res.statusText}`;
584+
attempts.push({ url, status: res.status, note: msg, auth: hdr ? Object.keys(hdr)[0] : "none" });
585+
// If unauthorized, keep trying other auth variants for the same URL
586+
if (res.status === 401 || res.status === 403) {
587+
continue; // try next auth header variant for this URL
588+
}
589+
throw new Error(`Upload failed @ ${url}${msg}`);
590+
} catch (err) {
591+
lastErr = err as unknown;
592+
// Continue to next auth header or URL
593+
}
594+
}
595+
}
596+
if (lastErr) {
597+
const summary = attempts.map(a => `${a.url} [auth:${a.auth ?? 'none'}] ${a.status ?? ''} ${a.note ?? ''}`.trim()).join(" | ");
598+
const err = lastErr instanceof Error ? new Error(`${lastErr.message}${summary ? ` | Tried: ${summary}` : ''}`) : new Error(summary || 'Unknown upload error');
599+
throw err;
600+
}
601+
throw new Error("Upload failed: no valid endpoint");
549602
}
550603
export async function listTournamentImageFiles(tournamentId?: string): Promise<unknown> {
551604
const path = tournamentId

0 commit comments

Comments
 (0)