diff --git a/src/app/api/admin/openlist/route.ts b/src/app/api/admin/openlist/route.ts
index 2311eb7..a8393fb 100644
--- a/src/app/api/admin/openlist/route.ts
+++ b/src/app/api/admin/openlist/route.ts
@@ -26,7 +26,7 @@ export async function POST(request: NextRequest) {
try {
const body = await request.json();
- const { action, URL, Token, Username, Password, RootPath } = body;
+ const { action, URL, Username, Password, RootPath } = body;
const authInfo = getAuthInfoFromCookie(request);
if (!authInfo || !authInfo.username) {
@@ -49,40 +49,30 @@ export async function POST(request: NextRequest) {
if (action === 'save') {
// 保存配置
- if (!URL) {
- return NextResponse.json({ error: '缺少URL参数' }, { status: 400 });
- }
-
- let finalToken = Token;
-
- // 如果没有Token但有账号密码,尝试登录获取Token
- if (!finalToken && Username && Password) {
- try {
- console.log('[OpenList Config] 使用账号密码登录获取Token');
- finalToken = await OpenListClient.login(URL, Username, Password);
- console.log('[OpenList Config] 登录成功,获取到Token');
- } catch (error) {
- console.error('[OpenList Config] 登录失败:', error);
- return NextResponse.json(
- { error: '使用账号密码登录失败: ' + (error as Error).message },
- { status: 400 }
- );
- }
- }
-
- // 检查是否有Token
- if (!finalToken) {
+ if (!URL || !Username || !Password) {
return NextResponse.json(
- { error: '请提供Token或账号密码' },
+ { error: '请提供 URL、账号和密码' },
+ { status: 400 }
+ );
+ }
+
+ // 验证账号密码是否正确
+ try {
+ console.log('[OpenList Config] 验证账号密码');
+ await OpenListClient.login(URL, Username, Password);
+ console.log('[OpenList Config] 账号密码验证成功');
+ } catch (error) {
+ console.error('[OpenList Config] 账号密码验证失败:', error);
+ return NextResponse.json(
+ { error: '账号密码验证失败: ' + (error as Error).message },
{ status: 400 }
);
}
adminConfig.OpenListConfig = {
URL,
- Token: finalToken,
- Username: Username || undefined,
- Password: Password || undefined,
+ Username,
+ Password,
RootPath: RootPath || '/',
LastRefreshTime: adminConfig.OpenListConfig?.LastRefreshTime,
ResourceCount: adminConfig.OpenListConfig?.ResourceCount,
diff --git a/src/app/api/cms-proxy/route.ts b/src/app/api/cms-proxy/route.ts
index a11baf2..0008414 100644
--- a/src/app/api/cms-proxy/route.ts
+++ b/src/app/api/cms-proxy/route.ts
@@ -264,7 +264,7 @@ async function handleOpenListProxy(request: NextRequest) {
const config = await getConfig();
const openListConfig = config.OpenListConfig;
- if (!openListConfig || !openListConfig.URL || !openListConfig.Token) {
+ if (!openListConfig || !openListConfig.URL || !openListConfig.Username || !openListConfig.Password) {
return NextResponse.json(
{ code: 0, msg: 'OpenList 未配置', list: [] },
{ status: 200 }
@@ -272,7 +272,11 @@ async function handleOpenListProxy(request: NextRequest) {
}
const rootPath = openListConfig.RootPath || '/';
- const client = new OpenListClient(openListConfig.URL, openListConfig.Token);
+ const client = new OpenListClient(
+ openListConfig.URL,
+ openListConfig.Username,
+ openListConfig.Password
+ );
// 读取 metainfo (从数据库或缓存)
let metaInfo: MetaInfo | null = getCachedMetaInfo(rootPath);
diff --git a/src/app/api/detail/route.ts b/src/app/api/detail/route.ts
index 66717fd..580cd4a 100644
--- a/src/app/api/detail/route.ts
+++ b/src/app/api/detail/route.ts
@@ -26,7 +26,7 @@ export async function GET(request: NextRequest) {
const config = await getConfig();
const openListConfig = config.OpenListConfig;
- if (!openListConfig || !openListConfig.URL || !openListConfig.Token) {
+ if (!openListConfig || !openListConfig.URL || !openListConfig.Username || !openListConfig.Password) {
throw new Error('OpenList 未配置');
}
diff --git a/src/app/api/openlist/correct/route.ts b/src/app/api/openlist/correct/route.ts
index 8a27ea5..e956b2a 100644
--- a/src/app/api/openlist/correct/route.ts
+++ b/src/app/api/openlist/correct/route.ts
@@ -39,7 +39,7 @@ export async function POST(request: NextRequest) {
const config = await getConfig();
const openListConfig = config.OpenListConfig;
- if (!openListConfig || !openListConfig.URL || !openListConfig.Token) {
+ if (!openListConfig || !openListConfig.URL || !openListConfig.Username || !openListConfig.Password) {
return NextResponse.json(
{ error: 'OpenList 未配置' },
{ status: 400 }
@@ -49,7 +49,6 @@ export async function POST(request: NextRequest) {
const rootPath = openListConfig.RootPath || '/';
const client = new OpenListClient(
openListConfig.URL,
- openListConfig.Token,
openListConfig.Username,
openListConfig.Password
);
diff --git a/src/app/api/openlist/detail/route.ts b/src/app/api/openlist/detail/route.ts
index 1d40be3..300afea 100644
--- a/src/app/api/openlist/detail/route.ts
+++ b/src/app/api/openlist/detail/route.ts
@@ -35,7 +35,7 @@ export async function GET(request: NextRequest) {
const config = await getConfig();
const openListConfig = config.OpenListConfig;
- if (!openListConfig || !openListConfig.URL || !openListConfig.Token) {
+ if (!openListConfig || !openListConfig.URL || !openListConfig.Username || !openListConfig.Password) {
return NextResponse.json({ error: 'OpenList 未配置' }, { status: 400 });
}
@@ -43,7 +43,6 @@ export async function GET(request: NextRequest) {
const folderPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}${folderName}`;
const client = new OpenListClient(
openListConfig.URL,
- openListConfig.Token,
openListConfig.Username,
openListConfig.Password
);
diff --git a/src/app/api/openlist/list/route.ts b/src/app/api/openlist/list/route.ts
index e5e2db9..4c18a84 100644
--- a/src/app/api/openlist/list/route.ts
+++ b/src/app/api/openlist/list/route.ts
@@ -34,7 +34,7 @@ export async function GET(request: NextRequest) {
const config = await getConfig();
const openListConfig = config.OpenListConfig;
- if (!openListConfig || !openListConfig.URL || !openListConfig.Token) {
+ if (!openListConfig || !openListConfig.URL || !openListConfig.Username || !openListConfig.Password) {
return NextResponse.json(
{ error: 'OpenList 未配置', list: [], total: 0 },
{ status: 200 }
@@ -44,7 +44,6 @@ export async function GET(request: NextRequest) {
const rootPath = openListConfig.RootPath || '/';
const client = new OpenListClient(
openListConfig.URL,
- openListConfig.Token,
openListConfig.Username,
openListConfig.Password
);
diff --git a/src/app/api/openlist/play/route.ts b/src/app/api/openlist/play/route.ts
index 9e5fa9f..731db4f 100644
--- a/src/app/api/openlist/play/route.ts
+++ b/src/app/api/openlist/play/route.ts
@@ -31,7 +31,7 @@ export async function GET(request: NextRequest) {
const config = await getConfig();
const openListConfig = config.OpenListConfig;
- if (!openListConfig || !openListConfig.URL || !openListConfig.Token) {
+ if (!openListConfig || !openListConfig.URL || !openListConfig.Username || !openListConfig.Password) {
return NextResponse.json({ error: 'OpenList 未配置' }, { status: 400 });
}
@@ -41,7 +41,6 @@ export async function GET(request: NextRequest) {
const client = new OpenListClient(
openListConfig.URL,
- openListConfig.Token,
openListConfig.Username,
openListConfig.Password
);
diff --git a/src/app/api/openlist/refresh-video/route.ts b/src/app/api/openlist/refresh-video/route.ts
index a33ef6c..50527e3 100644
--- a/src/app/api/openlist/refresh-video/route.ts
+++ b/src/app/api/openlist/refresh-video/route.ts
@@ -30,7 +30,7 @@ export async function POST(request: NextRequest) {
const config = await getConfig();
const openListConfig = config.OpenListConfig;
- if (!openListConfig || !openListConfig.URL || !openListConfig.Token) {
+ if (!openListConfig || !openListConfig.URL || !openListConfig.Username || !openListConfig.Password) {
return NextResponse.json({ error: 'OpenList 未配置' }, { status: 400 });
}
@@ -38,7 +38,6 @@ export async function POST(request: NextRequest) {
const folderPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}${folder}`;
const client = new OpenListClient(
openListConfig.URL,
- openListConfig.Token,
openListConfig.Username,
openListConfig.Password
);
diff --git a/src/app/api/openlist/refresh/route.ts b/src/app/api/openlist/refresh/route.ts
index 409a070..dccd53b 100644
--- a/src/app/api/openlist/refresh/route.ts
+++ b/src/app/api/openlist/refresh/route.ts
@@ -39,7 +39,7 @@ export async function POST(request: NextRequest) {
const config = await getConfig();
const openListConfig = config.OpenListConfig;
- if (!openListConfig || !openListConfig.URL || !openListConfig.Token) {
+ if (!openListConfig || !openListConfig.URL || !openListConfig.Username || !openListConfig.Password) {
return NextResponse.json(
{ error: 'OpenList 未配置' },
{ status: 400 }
@@ -66,7 +66,6 @@ export async function POST(request: NextRequest) {
performScan(
taskId,
openListConfig.URL,
- openListConfig.Token,
openListConfig.RootPath || '/',
tmdbApiKey,
tmdbProxy,
@@ -97,20 +96,18 @@ export async function POST(request: NextRequest) {
async function performScan(
taskId: string,
url: string,
- token: string,
rootPath: string,
tmdbApiKey: string,
tmdbProxy?: string,
username?: string,
password?: string
): Promise {
- const client = new OpenListClient(url, token, username, password);
+ const client = new OpenListClient(url, username!, password!);
console.log('[OpenList Refresh] 开始扫描:', {
taskId,
rootPath,
url,
- hasToken: !!token,
});
// 立即更新进度,确保任务可被查询
diff --git a/src/app/api/search/ws/route.ts b/src/app/api/search/ws/route.ts
index 0747b17..c896d41 100644
--- a/src/app/api/search/ws/route.ts
+++ b/src/app/api/search/ws/route.ts
@@ -34,7 +34,7 @@ export async function GET(request: NextRequest) {
const apiSites = await getAvailableApiSites(authInfo.username);
// 检查是否配置了 OpenList
- const hasOpenList = !!(config.OpenListConfig?.URL && config.OpenListConfig?.Token);
+ const hasOpenList = !!(config.OpenListConfig?.URL && config.OpenListConfig?.Username && config.OpenListConfig?.Password);
// 共享状态
let streamClosed = false;
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 0918ead..7fc07cc 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -90,7 +90,9 @@ export default async function RootLayout({
tmdbApiKey = config.SiteConfig.TMDBApiKey || '';
// 检查是否配置了 OpenList
openListEnabled = !!(
- config.OpenListConfig?.URL && config.OpenListConfig?.Token
+ config.OpenListConfig?.URL &&
+ config.OpenListConfig?.Username &&
+ config.OpenListConfig?.Password
);
}
diff --git a/src/lib/admin.types.ts b/src/lib/admin.types.ts
index 8990ec1..528178b 100644
--- a/src/lib/admin.types.ts
+++ b/src/lib/admin.types.ts
@@ -93,9 +93,8 @@ export interface AdminConfig {
};
OpenListConfig?: {
URL: string; // OpenList 服务器地址
- Token: string; // 认证 Token
- Username?: string; // 账号(可选,用于登录获取Token)
- Password?: string; // 密码(可选,用于登录获取Token)
+ Username: string; // 账号(用于登录获取Token)
+ Password: string; // 密码(用于登录获取Token)
RootPath: string; // 根目录路径,默认 "/"
LastRefreshTime?: number; // 上次刷新时间戳
ResourceCount?: number; // 资源数量
diff --git a/src/lib/openlist.client.ts b/src/lib/openlist.client.ts
index 743c750..fd79721 100644
--- a/src/lib/openlist.client.ts
+++ b/src/lib/openlist.client.ts
@@ -1,5 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
+// Token 内存缓存
+const tokenCache = new Map();
+
export interface OpenListFile {
name: string;
size: number;
@@ -30,11 +33,12 @@ export interface OpenListGetResponse {
}
export class OpenListClient {
+ private token: string = '';
+
constructor(
private baseURL: string,
- private token: string,
- private username?: string,
- private password?: string
+ private username: string,
+ private password: string
) {}
/**
@@ -69,60 +73,81 @@ export class OpenListClient {
}
/**
- * 刷新Token(如果配置了账号密码)
+ * 获取缓存的 Token 或重新登录
*/
- private async refreshToken(): Promise {
- if (!this.username || !this.password) {
- return false;
+ private async getToken(): Promise {
+ const cacheKey = `${this.baseURL}:${this.username}`;
+ const cached = tokenCache.get(cacheKey);
+
+ // 如果有缓存且未过期,直接返回
+ if (cached && cached.expiresAt > Date.now()) {
+ this.token = cached.token;
+ return this.token;
}
- try {
- console.log('[OpenListClient] Token可能失效,尝试使用账号密码重新登录');
- this.token = await OpenListClient.login(
- this.baseURL,
- this.username,
- this.password
- );
- console.log('[OpenListClient] Token刷新成功');
- return true;
- } catch (error) {
- console.error('[OpenListClient] Token刷新失败:', error);
- return false;
- }
+ // 否则重新登录
+ console.log('[OpenListClient] Token 不存在或已过期,重新登录');
+ this.token = await OpenListClient.login(
+ this.baseURL,
+ this.username,
+ this.password
+ );
+
+ // 缓存 Token,设置 1 小时过期
+ tokenCache.set(cacheKey, {
+ token: this.token,
+ expiresAt: Date.now() + 60 * 60 * 1000,
+ });
+
+ console.log('[OpenListClient] 登录成功,Token 已缓存');
+ return this.token;
}
/**
- * 执行请求,如果401则尝试刷新Token后重试
+ * 清除 Token 缓存(当 Token 失效时调用)
+ */
+ private clearTokenCache(): void {
+ const cacheKey = `${this.baseURL}:${this.username}`;
+ tokenCache.delete(cacheKey);
+ console.log('[OpenListClient] Token 缓存已清除');
+ }
+
+ /**
+ * 执行请求,如果401则清除缓存并重新登录后重试
*/
private async fetchWithRetry(
url: string,
options: RequestInit,
retried = false
): Promise {
- const response = await fetch(url, options);
+ // 获取 Token
+ const token = await this.getToken();
- // 如果是401且未重试过且有账号密码,尝试刷新Token后重试
- if (response.status === 401 && !retried && this.username && this.password) {
- const refreshed = await this.refreshToken();
- if (refreshed) {
- // 更新请求头中的Token
- const newOptions = {
- ...options,
- headers: {
- ...options.headers,
- Authorization: this.token,
- },
- };
- return this.fetchWithRetry(url, newOptions, true);
- }
+ // 更新请求头中的 Token
+ const requestOptions = {
+ ...options,
+ headers: {
+ ...options.headers,
+ Authorization: token,
+ },
+ };
+
+ const response = await fetch(url, requestOptions);
+
+ // 如果是401且未重试过,清除缓存并重新登录后重试
+ if (response.status === 401 && !retried) {
+ console.log('[OpenListClient] 收到 401,清除 Token 缓存并重试');
+ this.clearTokenCache();
+ return this.fetchWithRetry(url, options, true);
}
return response;
}
- private getHeaders() {
+ private async getHeaders() {
+ const token = await this.getToken();
return {
- Authorization: this.token, // 不带 bearer
+ Authorization: token, // 不带 bearer
'Content-Type': 'application/json',
};
}
@@ -135,7 +160,7 @@ export class OpenListClient {
): Promise {
const response = await this.fetchWithRetry(`${this.baseURL}/api/fs/list`, {
method: 'POST',
- headers: this.getHeaders(),
+ headers: await this.getHeaders(),
body: JSON.stringify({
path,
password: '',
@@ -156,7 +181,7 @@ export class OpenListClient {
async getFile(path: string): Promise {
const response = await this.fetchWithRetry(`${this.baseURL}/api/fs/get`, {
method: 'POST',
- headers: this.getHeaders(),
+ headers: await this.getHeaders(),
body: JSON.stringify({
path,
password: '',
@@ -172,10 +197,11 @@ export class OpenListClient {
// 上传文件
async uploadFile(path: string, content: string): Promise {
+ const token = await this.getToken();
const response = await this.fetchWithRetry(`${this.baseURL}/api/fs/put`, {
method: 'PUT',
headers: {
- Authorization: this.token,
+ Authorization: token,
'Content-Type': 'text/plain; charset=utf-8',
'File-Path': encodeURIComponent(path),
'As-Task': 'false',
@@ -198,7 +224,7 @@ export class OpenListClient {
try {
const response = await this.fetchWithRetry(`${this.baseURL}/api/fs/list`, {
method: 'POST',
- headers: this.getHeaders(),
+ headers: await this.getHeaders(),
body: JSON.stringify({
path,
password: '',
@@ -223,7 +249,7 @@ export class OpenListClient {
const response = await this.fetchWithRetry(`${this.baseURL}/api/fs/remove`, {
method: 'POST',
- headers: this.getHeaders(),
+ headers: await this.getHeaders(),
body: JSON.stringify({
names: [fileName],
dir: dir,