mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-05-19 12:27:29 +08:00
feat: redirect /login when fetching api gets 401
This commit is contained in:
@@ -1 +1 @@
|
|||||||
20250804190750
|
20250804223856
|
||||||
@@ -390,9 +390,30 @@ if (typeof window !== 'undefined') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---- 工具函数 ----
|
// ---- 工具函数 ----
|
||||||
|
/**
|
||||||
|
* 通用的 fetch 函数,处理 401 状态码自动跳转登录
|
||||||
|
*/
|
||||||
|
async function fetchWithAuth(
|
||||||
|
url: string,
|
||||||
|
options?: RequestInit
|
||||||
|
): Promise<Response> {
|
||||||
|
const res = await fetch(url, options);
|
||||||
|
if (!res.ok) {
|
||||||
|
// 如果是 401 未授权,跳转到登录页面
|
||||||
|
if (res.status === 401) {
|
||||||
|
const currentUrl = window.location.pathname + window.location.search;
|
||||||
|
const loginUrl = new URL('/login', window.location.origin);
|
||||||
|
loginUrl.searchParams.set('redirect', currentUrl);
|
||||||
|
window.location.href = loginUrl.toString();
|
||||||
|
throw new Error('用户未授权,已跳转到登录页面');
|
||||||
|
}
|
||||||
|
throw new Error(`请求 ${url} 失败: ${res.status}`);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchFromApi<T>(path: string): Promise<T> {
|
async function fetchFromApi<T>(path: string): Promise<T> {
|
||||||
const res = await fetch(path);
|
const res = await fetchWithAuth(path);
|
||||||
if (!res.ok) throw new Error(`请求 ${path} 失败: ${res.status}`);
|
|
||||||
return (await res.json()) as T;
|
return (await res.json()) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,17 +514,13 @@ export async function savePlayRecord(
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/playrecords', {
|
await fetchWithAuth('/api/playrecords', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ key, record }),
|
body: JSON.stringify({ key, record }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(`保存播放记录失败: ${res.status}`);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await handleDatabaseOperationFailure('playRecords', err);
|
await handleDatabaseOperationFailure('playRecords', err);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -558,13 +575,9 @@ export async function deletePlayRecord(
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
await fetchWithAuth(`/api/playrecords?key=${encodeURIComponent(key)}`, {
|
||||||
`/api/playrecords?key=${encodeURIComponent(key)}`,
|
method: 'DELETE',
|
||||||
{
|
});
|
||||||
method: 'DELETE',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!res.ok) throw new Error(`删除播放记录失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await handleDatabaseOperationFailure('playRecords', err);
|
await handleDatabaseOperationFailure('playRecords', err);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -684,14 +697,13 @@ export async function addSearchHistory(keyword: string): Promise<void> {
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/searchhistory', {
|
await fetchWithAuth('/api/searchhistory', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ keyword: trimmed }),
|
body: JSON.stringify({ keyword: trimmed }),
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`保存搜索历史失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await handleDatabaseOperationFailure('searchHistory', err);
|
await handleDatabaseOperationFailure('searchHistory', err);
|
||||||
}
|
}
|
||||||
@@ -738,10 +750,9 @@ export async function clearSearchHistory(): Promise<void> {
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/searchhistory`, {
|
await fetchWithAuth(`/api/searchhistory`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`清空搜索历史失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await handleDatabaseOperationFailure('searchHistory', err);
|
await handleDatabaseOperationFailure('searchHistory', err);
|
||||||
}
|
}
|
||||||
@@ -782,13 +793,12 @@ export async function deleteSearchHistory(keyword: string): Promise<void> {
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
await fetchWithAuth(
|
||||||
`/api/searchhistory?keyword=${encodeURIComponent(trimmed)}`,
|
`/api/searchhistory?keyword=${encodeURIComponent(trimmed)}`,
|
||||||
{
|
{
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (!res.ok) throw new Error(`删除搜索历史失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await handleDatabaseOperationFailure('searchHistory', err);
|
await handleDatabaseOperationFailure('searchHistory', err);
|
||||||
}
|
}
|
||||||
@@ -902,14 +912,13 @@ export async function saveFavorite(
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/favorites', {
|
await fetchWithAuth('/api/favorites', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ key, favorite }),
|
body: JSON.stringify({ key, favorite }),
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`保存收藏失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await handleDatabaseOperationFailure('favorites', err);
|
await handleDatabaseOperationFailure('favorites', err);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -964,10 +973,9 @@ export async function deleteFavorite(
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/favorites?key=${encodeURIComponent(key)}`, {
|
await fetchWithAuth(`/api/favorites?key=${encodeURIComponent(key)}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`删除收藏失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await handleDatabaseOperationFailure('favorites', err);
|
await handleDatabaseOperationFailure('favorites', err);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -1069,11 +1077,10 @@ export async function clearAllPlayRecords(): Promise<void> {
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/playrecords`, {
|
await fetchWithAuth(`/api/playrecords`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`清空播放记录失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await handleDatabaseOperationFailure('playRecords', err);
|
await handleDatabaseOperationFailure('playRecords', err);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -1110,11 +1117,10 @@ export async function clearAllFavorites(): Promise<void> {
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/favorites`, {
|
await fetchWithAuth(`/api/favorites`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`清空收藏失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await handleDatabaseOperationFailure('favorites', err);
|
await handleDatabaseOperationFailure('favorites', err);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -1390,14 +1396,13 @@ export async function saveSkipConfig(
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/skipconfigs', {
|
await fetchWithAuth('/api/skipconfigs', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ key, config }),
|
body: JSON.stringify({ key, config }),
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`保存跳过片头片尾配置失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('保存跳过片头片尾配置失败:', err);
|
console.error('保存跳过片头片尾配置失败:', err);
|
||||||
}
|
}
|
||||||
@@ -1513,13 +1518,9 @@ export async function deleteSkipConfig(
|
|||||||
|
|
||||||
// 异步同步到数据库
|
// 异步同步到数据库
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
await fetchWithAuth(`/api/skipconfigs?key=${encodeURIComponent(key)}`, {
|
||||||
`/api/skipconfigs?key=${encodeURIComponent(key)}`,
|
method: 'DELETE',
|
||||||
{
|
});
|
||||||
method: 'DELETE',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!res.ok) throw new Error(`删除跳过片头片尾配置失败: ${res.status}`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('删除跳过片头片尾配置失败:', err);
|
console.error('删除跳过片头片尾配置失败:', err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
const CURRENT_VERSION = '20250804190750';
|
const CURRENT_VERSION = '20250804223856';
|
||||||
|
|
||||||
// 版本检查结果枚举
|
// 版本检查结果枚举
|
||||||
export enum UpdateStatus {
|
export enum UpdateStatus {
|
||||||
|
|||||||
Reference in New Issue
Block a user