From e915a97a70d24f8a337778e55511022837d930ac Mon Sep 17 00:00:00 2001 From: mtvpls Date: Sun, 1 Feb 2026 20:07:41 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=89=8D=E7=AB=AFproxy?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/music/route.ts | 293 +++++++++++++++++++++---------------- src/app/music/page.tsx | 137 ++--------------- 2 files changed, 178 insertions(+), 252 deletions(-) diff --git a/src/app/api/music/route.ts b/src/app/api/music/route.ts index 9e96046..8848470 100644 --- a/src/app/api/music/route.ts +++ b/src/app/api/music/route.ts @@ -50,6 +50,86 @@ async function proxyRequest( } } +// 获取方法配置并执行请求 +async function executeMethod( + baseUrl: string, + platform: string, + func: string, + variables: Record = {} +): Promise { + // 1. 获取方法配置 + const cacheKey = `method-config-${platform}-${func}`; + let config: any; + + const cached = serverCache.methodConfigs.get(cacheKey); + if (cached && Date.now() - cached.timestamp < serverCache.CACHE_DURATION) { + config = cached.data.data; + } else { + const response = await proxyRequest(`${baseUrl}/v1/methods/${platform}/${func}`); + const data = await response.json(); + serverCache.methodConfigs.set(cacheKey, { data, timestamp: Date.now() }); + config = data.data; + } + + if (!config) { + throw new Error('无法获取方法配置'); + } + + // 2. 替换模板变量 + let url = config.url; + const params: Record = {}; + + if (config.params) { + for (const [key, value] of Object.entries(config.params)) { + let processedValue = String(value); + // 替换所有模板变量 + for (const [varName, varValue] of Object.entries(variables)) { + processedValue = processedValue.replace(`{{${varName}}}`, varValue); + } + params[key] = processedValue; + } + } + + // 3. 构建完整 URL + if (config.method === 'GET' && Object.keys(params).length > 0) { + const urlObj = new URL(url); + for (const [key, value] of Object.entries(params)) { + urlObj.searchParams.append(key, value); + } + url = urlObj.toString(); + } + + // 4. 发起请求 + const requestOptions: RequestInit = { + method: config.method || 'GET', + headers: config.headers || {}, + }; + + if (config.method === 'POST' && config.body) { + requestOptions.body = JSON.stringify(config.body); + requestOptions.headers = { + ...requestOptions.headers, + 'Content-Type': 'application/json', + }; + } + + const response = await proxyRequest(url, requestOptions); + let data = await response.json(); + + // 5. 执行 transform 函数(如果有) + if (config.transform) { + try { + // eslint-disable-next-line no-eval + const transformFn = eval(`(${config.transform})`); + data = transformFn(data); + } catch (err) { + console.error('Transform 函数执行失败:', err); + } + } + + return data; +} + // GET 请求处理 export async function GET(request: NextRequest) { try { @@ -74,29 +154,8 @@ export async function GET(request: NextRequest) { // 处理不同的 action switch (action) { - case 'methods': { - // 获取所有平台方法 - const cacheKey = 'methods-all'; - const cached = serverCache.methodConfigs.get(cacheKey); - - if (cached && Date.now() - cached.timestamp < serverCache.CACHE_DURATION) { - console.log('使用缓存: methods'); - return NextResponse.json(cached.data); - } - - const response = await proxyRequest(`${baseUrl}/v1/methods`); - const data = await response.json(); - - serverCache.methodConfigs.set(cacheKey, { - data, - timestamp: Date.now(), - }); - - return NextResponse.json(data); - } - - case 'platform-methods': { - // 获取指定平台的方法 + case 'toplists': { + // 获取排行榜列表 const platform = searchParams.get('platform'); if (!platform) { return NextResponse.json( @@ -105,95 +164,96 @@ export async function GET(request: NextRequest) { ); } - const cacheKey = `platform-methods-${platform}`; - const cached = serverCache.methodConfigs.get(cacheKey); - - if (cached && Date.now() - cached.timestamp < serverCache.CACHE_DURATION) { - console.log(`使用缓存: platform-methods-${platform}`); - return NextResponse.json(cached.data); - } - - const response = await proxyRequest(`${baseUrl}/v1/methods/${platform}`); - const data = await response.json(); - - serverCache.methodConfigs.set(cacheKey, { - data, - timestamp: Date.now(), - }); - - return NextResponse.json(data); - } - - case 'method-config': { - // 获取指定平台指定方法的配置 - const platform = searchParams.get('platform'); - const func = searchParams.get('function'); - if (!platform || !func) { - return NextResponse.json( - { error: '缺少 platform 或 function 参数' }, - { status: 400 } - ); - } - - const cacheKey = `method-config-${platform}-${func}`; - const cached = serverCache.methodConfigs.get(cacheKey); - - if (cached && Date.now() - cached.timestamp < serverCache.CACHE_DURATION) { - console.log(`使用缓存: method-config-${platform}-${func}`); - return NextResponse.json(cached.data); - } - - const response = await proxyRequest( - `${baseUrl}/v1/methods/${platform}/${func}` - ); - const data = await response.json(); - - serverCache.methodConfigs.set(cacheKey, { - data, - timestamp: Date.now(), - }); - - return NextResponse.json(data); - } - - case 'proxy': { - // 代理上游平台请求(用于方法下发后的实际请求) - const targetUrl = searchParams.get('url'); - if (!targetUrl) { - return NextResponse.json( - { error: '缺少 url 参数' }, - { status: 400 } - ); - } - - // 使用完整 URL 作为缓存键 - const cacheKey = `proxy-${targetUrl}`; + const cacheKey = `toplists-${platform}`; const cached = serverCache.proxyRequests.get(cacheKey); if (cached && Date.now() - cached.timestamp < serverCache.CACHE_DURATION) { - console.log(`使用缓存: proxy request`); return NextResponse.json(cached.data); } - // 获取其他查询参数 - const params = new URLSearchParams(); - searchParams.forEach((value, key) => { - if (key !== 'action' && key !== 'url') { - params.append(key, value); - } - }); - - const fullUrl = params.toString() - ? `${targetUrl}?${params.toString()}` - : targetUrl; - - const response = await proxyRequest(fullUrl); - const data = await response.json(); - - serverCache.proxyRequests.set(cacheKey, { - data, - timestamp: Date.now(), + const data = await executeMethod(baseUrl, platform, 'toplists'); + serverCache.proxyRequests.set(cacheKey, { data, timestamp: Date.now() }); + + return NextResponse.json(data); + } + + case 'toplist': { + // 获取排行榜详情 + const platform = searchParams.get('platform'); + const id = searchParams.get('id'); + + if (!platform || !id) { + return NextResponse.json( + { error: '缺少 platform 或 id 参数' }, + { status: 400 } + ); + } + + const cacheKey = `toplist-${platform}-${id}`; + const cached = serverCache.proxyRequests.get(cacheKey); + + if (cached && Date.now() - cached.timestamp < serverCache.CACHE_DURATION) { + return NextResponse.json(cached.data); + } + + const data = await executeMethod(baseUrl, platform, 'toplist', { id }); + serverCache.proxyRequests.set(cacheKey, { data, timestamp: Date.now() }); + + return NextResponse.json(data); + } + + case 'playlist': { + // 获取歌单详情 + const platform = searchParams.get('platform'); + const id = searchParams.get('id'); + + if (!platform || !id) { + return NextResponse.json( + { error: '缺少 platform 或 id 参数' }, + { status: 400 } + ); + } + + const cacheKey = `playlist-${platform}-${id}`; + const cached = serverCache.proxyRequests.get(cacheKey); + + if (cached && Date.now() - cached.timestamp < serverCache.CACHE_DURATION) { + return NextResponse.json(cached.data); + } + + const data = await executeMethod(baseUrl, platform, 'playlist', { id }); + serverCache.proxyRequests.set(cacheKey, { data, timestamp: Date.now() }); + + return NextResponse.json(data); + } + + case 'search': { + // 搜索歌曲 + const platform = searchParams.get('platform'); + const keyword = searchParams.get('keyword'); + const page = searchParams.get('page') || '0'; + const pageSize = searchParams.get('pageSize') || '20'; + + if (!platform || !keyword) { + return NextResponse.json( + { error: '缺少 platform 或 keyword 参数' }, + { status: 400 } + ); + } + + const cacheKey = `search-${platform}-${keyword}-${page}-${pageSize}`; + const cached = serverCache.proxyRequests.get(cacheKey); + + if (cached && Date.now() - cached.timestamp < serverCache.CACHE_DURATION) { + return NextResponse.json(cached.data); + } + + const data = await executeMethod(baseUrl, platform, 'search', { + keyword, + page, + pageSize, }); + serverCache.proxyRequests.set(cacheKey, { data, timestamp: Date.now() }); return NextResponse.json(data); } @@ -301,29 +361,6 @@ export async function POST(request: NextRequest) { } } - case 'proxy-post': { - // 代理 POST 请求到上游平台 - const { url, data: postData, headers } = body; - if (!url) { - return NextResponse.json( - { error: '缺少 url 参数' }, - { status: 400 } - ); - } - - const response = await proxyRequest(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...headers, - }, - body: JSON.stringify(postData), - }); - - const responseData = await response.json(); - return NextResponse.json(responseData); - } - default: return NextResponse.json( { error: '不支持的 action' }, diff --git a/src/app/music/page.tsx b/src/app/music/page.tsx index d22ae3e..2cf397f 100644 --- a/src/app/music/page.tsx +++ b/src/app/music/page.tsx @@ -207,41 +207,10 @@ export default function MusicPage() { setLoading(true); try { const response = await fetch( - `/api/music?action=method-config&platform=${source}&function=toplists` + `/api/music?action=toplists&platform=${source}` ); - const configData = await response.json(); - - if (configData.code === 0 && configData.data) { - const config = configData.data; - const url = new URL(config.url); - - // 添加参数 - if (config.params) { - Object.entries(config.params).forEach(([key, value]) => { - url.searchParams.append(key, String(value)); - }); - } - - // 通过后端代理请求 - const proxyResponse = await fetch( - `/api/music?action=proxy&url=${encodeURIComponent(url.toString())}` - ); - const data = await proxyResponse.json(); - - // 使用 transform 函数处理数据 - if (config.transform) { - try { - const transformFn = eval(`(${config.transform})`); - const transformed = transformFn(data); - setPlaylists(transformed || []); - } catch (err) { - console.error('Transform 函数执行失败:', err); - setPlaylists(data.list || data.data || []); - } - } else { - setPlaylists(data.list || data.data || []); - } - } + const data = await response.json(); + setPlaylists(data || []); } catch (error) { console.error('加载排行榜失败:', error); } finally { @@ -254,47 +223,12 @@ export default function MusicPage() { setLoading(true); try { const response = await fetch( - `/api/music?action=method-config&platform=${currentSource}&function=toplist` + `/api/music?action=toplist&platform=${currentSource}&id=${playlistId}` ); - const configData = await response.json(); - - if (configData.code === 0 && configData.data) { - const config = configData.data; - let url = config.url; - - // 替换模板变量 - if (config.params) { - const params = new URLSearchParams(); - Object.entries(config.params).forEach(([key, value]) => { - const processedValue = String(value).replace('{{id}}', playlistId); - params.append(key, processedValue); - }); - url = `${url}?${params.toString()}`; - } - - // 通过后端代理请求 - const proxyResponse = await fetch( - `/api/music?action=proxy&url=${encodeURIComponent(url)}` - ); - const data = await proxyResponse.json(); - - // 使用 transform 函数处理数据 - if (config.transform) { - try { - const transformFn = eval(`(${config.transform})`); - const transformed = transformFn(data); - setSongs(transformed || []); - } catch (err) { - console.error('Transform 函数执行失败:', err); - setSongs(data.songs || data.data || []); - } - } else { - setSongs(data.songs || data.data || []); - } - - setCurrentPlaylistTitle(playlistName); - setCurrentView('songs'); - } + const data = await response.json(); + setSongs(data || []); + setCurrentPlaylistTitle(playlistName); + setCurrentView('songs'); } catch (error) { console.error('加载歌单失败:', error); } finally { @@ -309,57 +243,12 @@ export default function MusicPage() { setLoading(true); try { const response = await fetch( - `/api/music?action=method-config&platform=${currentSource}&function=search` + `/api/music?action=search&platform=${currentSource}&keyword=${encodeURIComponent(searchKeyword)}&page=1&pageSize=20` ); - const configData = await response.json(); - - if (configData.code === 0 && configData.data) { - const config = configData.data; - let url = config.url; - - // 替换模板变量 - if (config.params) { - const params = new URLSearchParams(); - Object.entries(config.params).forEach(([key, value]) => { - let processedValue = String(value) - .replace('{{keyword}}', searchKeyword) - .replace('{{page}}', '1') - .replace('{{limit}}', '20') - .replace('{{pageSize}}', '20'); - - // 处理复杂表达式 - if (processedValue.includes('{{')) { - processedValue = processedValue.replace(/\{\{.*?\}\}/g, '0'); - } - - params.append(key, processedValue); - }); - url = `${url}?${params.toString()}`; - } - - // 通过后端代理请求 - const proxyResponse = await fetch( - `/api/music?action=proxy&url=${encodeURIComponent(url)}` - ); - const data = await proxyResponse.json(); - - // 使用 transform 函数处理数据 - if (config.transform) { - try { - const transformFn = eval(`(${config.transform})`); - const transformed = transformFn(data); - setSongs(transformed || []); - } catch (err) { - console.error('Transform 函数执行失败:', err); - setSongs(data.songs || data.data || []); - } - } else { - setSongs(data.songs || data.data || []); - } - - setCurrentPlaylistTitle(`搜索: ${searchKeyword}`); - setCurrentView('songs'); - } + const data = await response.json(); + setSongs(data || []); + setCurrentPlaylistTitle(`搜索: ${searchKeyword}`); + setCurrentView('songs'); } catch (error) { console.error('搜索失败:', error); } finally {