fix(admin): use absolute API URL for cross-origin admin fetches
This commit is contained in:
1 parent
c2f9f9ed62
commit
9a0511b36b
1 file changed
+21
-17
+21
-17
@@ -31,6 +31,7 @@ const adminConfig = {
|
|||||||
const MAX_UPLOAD_BYTES = 2 * 1024 * 1024 * 1024;
|
const MAX_UPLOAD_BYTES = 2 * 1024 * 1024 * 1024;
|
||||||
const UPLOAD_CHUNK_BYTES = 8 * 1024 * 1024;
|
const UPLOAD_CHUNK_BYTES = 8 * 1024 * 1024;
|
||||||
const UPLOAD_RESUME_KEY_PREFIX = "linearvoid-upload-resume:";
|
const UPLOAD_RESUME_KEY_PREFIX = "linearvoid-upload-resume:";
|
||||||
|
const API_URL = import.meta.env.VITE_PUBLIC_APP_URL?.trim() || window.location.origin;
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
mediaUploading: false,
|
mediaUploading: false,
|
||||||
@@ -131,7 +132,7 @@ const pageCache: Record<string, string> = {};
|
|||||||
async function loadPageContent(tabName: string): Promise<string> {
|
async function loadPageContent(tabName: string): Promise<string> {
|
||||||
if (pageCache[tabName]) return pageCache[tabName];
|
if (pageCache[tabName]) return pageCache[tabName];
|
||||||
|
|
||||||
const response = await fetch(`/admin/${tabName}.html`);
|
const response = await fetch(`${API_URL}/admin/${tabName}.html`);
|
||||||
if (!response.ok) throw new Error(`Failed to load page: ${tabName}`);
|
if (!response.ok) throw new Error(`Failed to load page: ${tabName}`);
|
||||||
const html = await response.text();
|
const html = await response.text();
|
||||||
pageCache[tabName] = html;
|
pageCache[tabName] = html;
|
||||||
@@ -175,7 +176,7 @@ async function switchTab(tabName: string) {
|
|||||||
|
|
||||||
export async function refreshOverview() {
|
export async function refreshOverview() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/admin/overview");
|
const response = await fetch(`${API_URL}/api/admin/overview`);
|
||||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
|
|
||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
@@ -202,7 +203,7 @@ export async function refreshOverview() {
|
|||||||
export async function refreshAnalytics() {
|
export async function refreshAnalytics() {
|
||||||
setStatus("analytics-status", "Loading analytics...");
|
setStatus("analytics-status", "Loading analytics...");
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/admin/analytics");
|
const response = await fetch(`${API_URL}/api/admin/analytics`);
|
||||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
|
|
||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
@@ -240,7 +241,7 @@ export async function refreshAnalytics() {
|
|||||||
async function refreshPlaylist() {
|
async function refreshPlaylist() {
|
||||||
setStatus("playlist-status", "Loading playlist...");
|
setStatus("playlist-status", "Loading playlist...");
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/admin/playlist");
|
const response = await fetch(`${API_URL}/api/admin/playlist`);
|
||||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
|
|
||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
@@ -522,7 +523,7 @@ async function submitOverride(e: Event) {
|
|||||||
const endTime = (document.getElementById("override-end") as HTMLInputElement).value;
|
const endTime = (document.getElementById("override-end") as HTMLInputElement).value;
|
||||||
const reason = (document.getElementById("override-reason") as HTMLInputElement).value;
|
const reason = (document.getElementById("override-reason") as HTMLInputElement).value;
|
||||||
try {
|
try {
|
||||||
const resp = await fetch("/api/admin/schedule/overrides", {
|
const resp = await fetch(`${API_URL}/api/admin/schedule/overrides`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -551,7 +552,7 @@ async function submitOverride(e: Event) {
|
|||||||
|
|
||||||
async function deleteOverride(id: number) {
|
async function deleteOverride(id: number) {
|
||||||
try {
|
try {
|
||||||
await fetch(`/api/admin/schedule/overrides?id=${id}`, { method: "DELETE" });
|
await fetch(`${API_URL}/api/admin/schedule/overrides?id=${id}`, { method: "DELETE" });
|
||||||
refreshOverrides();
|
refreshOverrides();
|
||||||
} catch {
|
} catch {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
@@ -749,9 +750,12 @@ async function uploadVideo(file: File) {
|
|||||||
setStatus("media-status", `Uploading ${file.name} (${chunkIndex + 1}/${effectiveTotalChunks} chunks)...`);
|
setStatus("media-status", `Uploading ${file.name} (${chunkIndex + 1}/${effectiveTotalChunks} chunks)...`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const completeResponse = await fetch(`/api/admin/media/uploads/${encodeURIComponent(uploadId)}/complete`, {
|
const completeResponse = await fetch(
|
||||||
method: "POST",
|
`${API_URL}/api/admin/media/uploads/${encodeURIComponent(uploadId)}/complete`,
|
||||||
});
|
{
|
||||||
|
method: "POST",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (!completeResponse.ok) {
|
if (!completeResponse.ok) {
|
||||||
throw new Error(await readErrorMessage(completeResponse));
|
throw new Error(await readErrorMessage(completeResponse));
|
||||||
@@ -791,7 +795,7 @@ async function editMedia(file: MediaEntry) {
|
|||||||
|
|
||||||
setStatus("media-status", `Saving metadata for ${file.name}...`);
|
setStatus("media-status", `Saving metadata for ${file.name}...`);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/admin/media/${file.id}`, {
|
const response = await fetch(`${API_URL}/api/admin/media/${file.id}`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ title, status, tags }),
|
body: JSON.stringify({ title, status, tags }),
|
||||||
@@ -816,7 +820,7 @@ async function normalizeMedia(file: MediaEntry) {
|
|||||||
|
|
||||||
setStatus("media-status", `Normalizing ${file.name}...`);
|
setStatus("media-status", `Normalizing ${file.name}...`);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/admin/media/${file.id}/normalize`, { method: "POST" });
|
const response = await fetch(`${API_URL}/api/admin/media/${file.id}/normalize`, { method: "POST" });
|
||||||
if (!response.ok) throw new Error(await readErrorMessage(response));
|
if (!response.ok) throw new Error(await readErrorMessage(response));
|
||||||
|
|
||||||
setStatus("media-status", `Normalization started for ${file.name}.`);
|
setStatus("media-status", `Normalization started for ${file.name}.`);
|
||||||
@@ -836,7 +840,7 @@ function pollNormalizationStatus(id: number, attempt: number) {
|
|||||||
}
|
}
|
||||||
window.setTimeout(async () => {
|
window.setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/admin/media/${id}`);
|
const response = await fetch(`${API_URL}/api/admin/media/${id}`);
|
||||||
if (!response.ok) return;
|
if (!response.ok) return;
|
||||||
const file = (await response.json()) as MediaEntry;
|
const file = (await response.json()) as MediaEntry;
|
||||||
if (file.status === "normalizing" || file.status === "needs_normalization") {
|
if (file.status === "normalizing" || file.status === "needs_normalization") {
|
||||||
@@ -869,7 +873,7 @@ async function deleteMedia(file: MediaEntry) {
|
|||||||
|
|
||||||
setStatus("media-status", `Deleting ${file.name}...`);
|
setStatus("media-status", `Deleting ${file.name}...`);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/admin/media/${file.id}`, {
|
const response = await fetch(`${API_URL}/api/admin/media/${file.id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
});
|
});
|
||||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
@@ -887,7 +891,7 @@ async function deleteMedia(file: MediaEntry) {
|
|||||||
async function refreshModerationLogs() {
|
async function refreshModerationLogs() {
|
||||||
setStatus("moderation-status", "Loading moderation logs...");
|
setStatus("moderation-status", "Loading moderation logs...");
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/admin/moderation/logs?limit=${adminConfig.moderationLogLimit}`);
|
const response = await fetch(`${API_URL}/api/admin/moderation/logs?limit=${adminConfig.moderationLogLimit}`);
|
||||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
|
|
||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
@@ -988,7 +992,7 @@ async function refreshChatLog() {
|
|||||||
if (chatLogState.username) params.set("username", chatLogState.username);
|
if (chatLogState.username) params.set("username", chatLogState.username);
|
||||||
if (chatLogState.text) params.set("text", chatLogState.text);
|
if (chatLogState.text) params.set("text", chatLogState.text);
|
||||||
|
|
||||||
const response = await fetch(`/api/admin/chat-log?${params}`);
|
const response = await fetch(`${API_URL}/api/admin/chat-log?${params}`);
|
||||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
|
|
||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
@@ -1118,7 +1122,7 @@ async function deleteChatEntry(id: number) {
|
|||||||
|
|
||||||
setStatus("chat-log-status", `Deleting message #${id}...`);
|
setStatus("chat-log-status", `Deleting message #${id}...`);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/admin/chat-log/${id}`, {
|
const response = await fetch(`${API_URL}/api/admin/chat-log/${id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
});
|
});
|
||||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
@@ -1268,7 +1272,7 @@ async function restartService(service: string) {
|
|||||||
const status = document.getElementById("restart-status");
|
const status = document.getElementById("restart-status");
|
||||||
if (status) status.textContent = `Requesting restart of ${service}...`;
|
if (status) status.textContent = `Requesting restart of ${service}...`;
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/api/admin/restart?service=${service}`, { method: "POST" });
|
const resp = await fetch(`${API_URL}/api/admin/restart?service=${service}`, { method: "POST" });
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (status) status.textContent = data.message;
|
if (status) status.textContent = data.message;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in new issue
Block a user