diff --git a/src/app/api/emby/sources/route.ts b/src/app/api/emby/sources/route.ts index fcfd0b5..b66ca85 100644 --- a/src/app/api/emby/sources/route.ts +++ b/src/app/api/emby/sources/route.ts @@ -9,7 +9,10 @@ export const runtime = 'nodejs'; */ export async function GET() { try { + console.log('[Emby Sources API] 开始获取Emby源列表'); const sources = await embyManager.getEnabledSources(); + console.log('[Emby Sources API] 获取到的源数量:', sources.length); + console.log('[Emby Sources API] 源详情:', JSON.stringify(sources, null, 2)); return NextResponse.json({ sources: sources.map(s => ({ diff --git a/src/app/api/movie-requests/[id]/route.ts b/src/app/api/movie-requests/[id]/route.ts index 19d9ad3..cb708c8 100644 --- a/src/app/api/movie-requests/[id]/route.ts +++ b/src/app/api/movie-requests/[id]/route.ts @@ -45,11 +45,18 @@ export async function PATCH( try { const storage = getStorage(); - const userInfo = await storage.getUserInfoV2(authInfo.username); // 检查权限:只有管理员和站长可以操作 - if (userInfo?.role !== 'admin' && userInfo?.role !== 'owner') { - return NextResponse.json({ error: '无权限操作' }, { status: 403 }); + if (storage.getUserInfoV2) { + const userInfo = await storage.getUserInfoV2(authInfo.username); + if (userInfo?.role !== 'admin' && userInfo?.role !== 'owner') { + return NextResponse.json({ error: '无权限操作' }, { status: 403 }); + } + } else { + // 如果不支持 getUserInfoV2,只允许站长操作 + if (authInfo.username !== process.env.USERNAME) { + return NextResponse.json({ error: '无权限操作' }, { status: 403 }); + } } const body = await request.json(); @@ -116,11 +123,18 @@ export async function DELETE( try { const storage = getStorage(); - const userInfo = await storage.getUserInfoV2(authInfo.username); // 检查权限:只有管理员和站长可以删除 - if (userInfo?.role !== 'admin' && userInfo?.role !== 'owner') { - return NextResponse.json({ error: '无权限操作' }, { status: 403 }); + if (storage.getUserInfoV2) { + const userInfo = await storage.getUserInfoV2(authInfo.username); + if (userInfo?.role !== 'admin' && userInfo?.role !== 'owner') { + return NextResponse.json({ error: '无权限操作' }, { status: 403 }); + } + } else { + // 如果不支持 getUserInfoV2,只允许站长操作 + if (authInfo.username !== process.env.USERNAME) { + return NextResponse.json({ error: '无权限操作' }, { status: 403 }); + } } const movieRequest = await storage.getMovieRequest(params.id); diff --git a/src/app/api/movie-requests/route.ts b/src/app/api/movie-requests/route.ts index aaad560..2946636 100644 --- a/src/app/api/movie-requests/route.ts +++ b/src/app/api/movie-requests/route.ts @@ -42,7 +42,7 @@ export async function GET(request: NextRequest) { // 列表页不返回 requestedBy if (!detail) { - requests = requests.map(r => ({ ...r, requestedBy: undefined })); + requests = requests.map(r => ({ ...r, requestedBy: [] })); } // 按求片人数和时间排序 @@ -92,15 +92,17 @@ export async function POST(request: NextRequest) { const cooldownSeconds = config.SiteConfig.MovieRequestCooldown ?? 3600; const rateLimit = cooldownSeconds * 1000; - const userInfo = await storage.getUserInfoV2(authInfo.username); - if (userInfo?.last_movie_request_time) { - const elapsed = Date.now() - userInfo.last_movie_request_time; - if (elapsed < rateLimit) { - const remaining = Math.ceil((rateLimit - elapsed) / 60000); - return NextResponse.json( - { error: `操作太频繁,请${remaining}分钟后再试` }, - { status: 429 } - ); + if (storage.getUserInfoV2) { + const userInfo = await storage.getUserInfoV2(authInfo.username); + if (userInfo?.last_movie_request_time) { + const elapsed = Date.now() - userInfo.last_movie_request_time; + if (elapsed < rateLimit) { + const remaining = Math.ceil((rateLimit - elapsed) / 60000); + return NextResponse.json( + { error: `操作太频繁,请${remaining}分钟后再试` }, + { status: 429 } + ); + } } } @@ -173,15 +175,17 @@ export async function POST(request: NextRequest) { await storage.addUserMovieRequest(authInfo.username, newRequest.id); // 更新频率限制 - 保存到用户信息的 hash 中 - await storage.client.hSet( - `user:${authInfo.username}:info`, - 'last_movie_request_time', - Date.now().toString() - ); + if ('client' in storage && storage.client && typeof (storage.client as any).hSet === 'function') { + await (storage.client as any).hSet( + `user:${authInfo.username}:info`, + 'last_movie_request_time', + Date.now().toString() + ); - // 清除用户信息缓存,确保下次读取到最新数据 - const { userInfoCache } = await import('@/lib/user-cache'); - userInfoCache?.delete(authInfo.username); + // 清除用户信息缓存,确保下次读取到最新数据 + const { userInfoCache } = await import('@/lib/user-cache'); + userInfoCache?.delete(authInfo.username); + } // 给站长发送通知 const ownerUsername = process.env.USERNAME; diff --git a/src/lib/config.ts b/src/lib/config.ts index 0cb0eb1..2856382 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -302,11 +302,18 @@ async function getInitConfig(configFile: string, subConfig: { export async function getConfig(): Promise { // 直接使用内存缓存 if (cachedConfig) { + console.log('[Config] 使用缓存的配置'); + console.log('[Config] 缓存中EmbyConfig存在:', !!cachedConfig.EmbyConfig); + console.log('[Config] 缓存中EmbyConfig.Sources存在:', !!cachedConfig.EmbyConfig?.Sources); + if (cachedConfig.EmbyConfig?.Sources) { + console.log('[Config] 缓存中Sources长度:', cachedConfig.EmbyConfig.Sources.length); + } return cachedConfig; } // 如果正在初始化,等待初始化完成 if (configInitPromise) { + console.log('[Config] 等待配置初始化完成'); return configInitPromise; } diff --git a/src/lib/emby-manager.ts b/src/lib/emby-manager.ts index 548a140..4fc5c41 100644 --- a/src/lib/emby-manager.ts +++ b/src/lib/emby-manager.ts @@ -43,14 +43,24 @@ class EmbyManager { */ private async getSources(): Promise { const config = await getConfig(); + console.log('[EmbyManager] 获取配置完成'); + console.log('[EmbyManager] EmbyConfig存在:', !!config.EmbyConfig); + console.log('[EmbyManager] EmbyConfig.Sources存在:', !!config.EmbyConfig?.Sources); + console.log('[EmbyManager] EmbyConfig.Sources是数组:', Array.isArray(config.EmbyConfig?.Sources)); + if (config.EmbyConfig?.Sources) { + console.log('[EmbyManager] Sources长度:', config.EmbyConfig.Sources.length); + console.log('[EmbyManager] Sources内容:', JSON.stringify(config.EmbyConfig.Sources, null, 2)); + } // 如果是新格式(Sources数组) if (config.EmbyConfig?.Sources && Array.isArray(config.EmbyConfig.Sources)) { + console.log('[EmbyManager] 使用新格式Sources,返回', config.EmbyConfig.Sources.length, '个源'); return config.EmbyConfig.Sources; } // 如果是旧格式(单源配置),转换为数组格式 if (config.EmbyConfig?.ServerURL) { + console.log('[EmbyManager] 使用旧格式配置,转换为数组'); return [{ key: 'default', name: 'Emby', @@ -68,6 +78,7 @@ class EmbyManager { }]; } + console.log('[EmbyManager] 没有找到任何Emby配置,返回空数组'); return []; } @@ -130,8 +141,12 @@ class EmbyManager { * 获取所有启用的Emby源配置 */ async getEnabledSources(): Promise { + console.log('[EmbyManager] getEnabledSources 被调用'); const sources = await this.getSources(); - return sources.filter(s => s.enabled); + console.log('[EmbyManager] 获取到所有源:', sources.length, '个'); + const enabledSources = sources.filter(s => s.enabled); + console.log('[EmbyManager] 过滤后启用的源:', enabledSources.length, '个'); + return enabledSources; } /** diff --git a/src/lib/types.ts b/src/lib/types.ts index 8948020..45d5215 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -127,6 +127,20 @@ export interface IStorage { getUserMovieRequests(userName: string): Promise; addUserMovieRequest(userName: string, requestId: string): Promise; removeUserMovieRequest(userName: string, requestId: string): Promise; + + // 新版用户存储(V2)- 可选方法 + getUserInfoV2?(userName: string): Promise<{ + role: 'owner' | 'admin' | 'user'; + banned: boolean; + tags?: string[]; + oidcSub?: string; + enabledApis?: string[]; + created_at: number; + playrecord_migrated?: boolean; + favorite_migrated?: boolean; + skip_migrated?: boolean; + last_movie_request_time?: number; + } | null>; } // 搜索结果数据结构