调整api路径
This commit is contained in:
+2
-343
@@ -7771,349 +7771,8 @@ const ThemeConfigComponent = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 音乐配置组件
|
// 音乐配置组件(已停用)
|
||||||
export const MusicConfigComponent = ({
|
// const MusicConfigComponent = (...) => { ... }
|
||||||
config,
|
|
||||||
refreshConfig,
|
|
||||||
}: {
|
|
||||||
config: AdminConfig | null;
|
|
||||||
refreshConfig: () => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const { alertModal, showAlert, hideAlert } = useAlertModal();
|
|
||||||
const { isLoading, withLoading } = useLoadingState();
|
|
||||||
|
|
||||||
const [musicSettings, setMusicSettings] = useState({
|
|
||||||
TuneHubEnabled: false,
|
|
||||||
TuneHubBaseUrl: 'https://tunehub.sayqz.com/api',
|
|
||||||
TuneHubApiKey: '',
|
|
||||||
OpenListCacheEnabled: false,
|
|
||||||
OpenListCacheURL: '',
|
|
||||||
OpenListCacheUsername: '',
|
|
||||||
OpenListCachePassword: '',
|
|
||||||
OpenListCachePath: '/music-cache',
|
|
||||||
OpenListCacheProxyEnabled: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 从配置加载音乐设置
|
|
||||||
useEffect(() => {
|
|
||||||
if (config?.MusicConfig) {
|
|
||||||
setMusicSettings({
|
|
||||||
TuneHubEnabled: config.MusicConfig.TuneHubEnabled ?? false,
|
|
||||||
TuneHubBaseUrl: config.MusicConfig.TuneHubBaseUrl ?? 'https://tunehub.sayqz.com/api',
|
|
||||||
TuneHubApiKey: config.MusicConfig.TuneHubApiKey ?? '',
|
|
||||||
OpenListCacheEnabled: config.MusicConfig.OpenListCacheEnabled ?? false,
|
|
||||||
OpenListCacheURL: config.MusicConfig.OpenListCacheURL ?? '',
|
|
||||||
OpenListCacheUsername: config.MusicConfig.OpenListCacheUsername ?? '',
|
|
||||||
OpenListCachePassword: config.MusicConfig.OpenListCachePassword ?? '',
|
|
||||||
OpenListCachePath: config.MusicConfig.OpenListCachePath ?? '/music-cache',
|
|
||||||
OpenListCacheProxyEnabled: config.MusicConfig.OpenListCacheProxyEnabled ?? true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [config]);
|
|
||||||
|
|
||||||
const handleSave = async () => {
|
|
||||||
await withLoading('saveMusicConfig', async () => {
|
|
||||||
try {
|
|
||||||
const resp = await fetch('/api/admin/music', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ ...musicSettings }),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!resp.ok) {
|
|
||||||
const data = await resp.json().catch(() => ({}));
|
|
||||||
throw new Error(data.error || '保存失败');
|
|
||||||
}
|
|
||||||
|
|
||||||
showAlert({ type: 'success', title: '保存成功', message: '音乐配置已更新', timer: 2000 });
|
|
||||||
await refreshConfig();
|
|
||||||
} catch (error: any) {
|
|
||||||
showAlert({ type: 'error', title: '保存失败', message: error.message || '未知错误', showConfirm: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='space-y-6'>
|
|
||||||
{/* TuneHub 音乐配置 */}
|
|
||||||
<div className='space-y-4'>
|
|
||||||
<h3 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
|
|
||||||
TuneHub 音乐配置
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
{/* 开启音乐功能 */}
|
|
||||||
<div>
|
|
||||||
<div className='flex items-center justify-between'>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
|
||||||
开启音乐功能
|
|
||||||
</label>
|
|
||||||
<button
|
|
||||||
type='button'
|
|
||||||
onClick={() =>
|
|
||||||
setMusicSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
TuneHubEnabled: !prev.TuneHubEnabled,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 ${
|
|
||||||
musicSettings.TuneHubEnabled
|
|
||||||
? buttonStyles.toggleOn
|
|
||||||
: buttonStyles.toggleOff
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`inline-block h-4 w-4 transform rounded-full ${
|
|
||||||
buttonStyles.toggleThumb
|
|
||||||
} transition-transform ${
|
|
||||||
musicSettings.TuneHubEnabled
|
|
||||||
? buttonStyles.toggleThumbOn
|
|
||||||
: buttonStyles.toggleThumbOff
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
|
||||||
开启后将在首页显示音乐视听入口,支持网易云、QQ音乐、酷我音乐
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TuneHub Base URL */}
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
|
||||||
TuneHub API 地址
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type='text'
|
|
||||||
placeholder='https://tunehub.sayqz.com/api'
|
|
||||||
value={musicSettings.TuneHubBaseUrl}
|
|
||||||
onChange={(e) =>
|
|
||||||
setMusicSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
TuneHubBaseUrl: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
|
||||||
/>
|
|
||||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
|
||||||
TuneHub API 的基础地址,默认为 https://tunehub.sayqz.com/api。也可以通过环境变量 TUNEHUB_BASE_URL 配置
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TuneHub API Key */}
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
|
||||||
TuneHub API Key
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type='password'
|
|
||||||
placeholder='th_your_api_key_here'
|
|
||||||
value={musicSettings.TuneHubApiKey}
|
|
||||||
onChange={(e) =>
|
|
||||||
setMusicSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
TuneHubApiKey: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
|
||||||
/>
|
|
||||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
|
||||||
用于解析歌曲播放链接的 API Key(消耗积分)。搜索、榜单、歌单等功能不需要 Key。也可以通过环境变量 TUNEHUB_API_KEY 配置
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* OpenList 缓存配置 */}
|
|
||||||
<div className='space-y-4 pt-4 border-t border-gray-200 dark:border-gray-700'>
|
|
||||||
<h3 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
|
|
||||||
OpenList 缓存配置
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
{/* 开启 OpenList 缓存 */}
|
|
||||||
<div>
|
|
||||||
<div className='flex items-center justify-between'>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
|
||||||
开启 OpenList 缓存
|
|
||||||
</label>
|
|
||||||
<button
|
|
||||||
type='button'
|
|
||||||
onClick={() =>
|
|
||||||
setMusicSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
OpenListCacheEnabled: !prev.OpenListCacheEnabled,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 ${
|
|
||||||
musicSettings.OpenListCacheEnabled
|
|
||||||
? buttonStyles.toggleOn
|
|
||||||
: buttonStyles.toggleOff
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`inline-block h-4 w-4 transform rounded-full ${
|
|
||||||
buttonStyles.toggleThumb
|
|
||||||
} transition-transform ${
|
|
||||||
musicSettings.OpenListCacheEnabled
|
|
||||||
? buttonStyles.toggleThumbOn
|
|
||||||
: buttonStyles.toggleThumbOff
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
|
||||||
开启后将音乐解析结果(播放链接、歌词、元信息)和音频文件缓存到 OpenList,减少 API 调用次数并支持离线播放
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* OpenList URL */}
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
|
||||||
OpenList 服务器地址
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type='text'
|
|
||||||
placeholder='https://your-openlist-server.com'
|
|
||||||
value={musicSettings.OpenListCacheURL}
|
|
||||||
onChange={(e) =>
|
|
||||||
setMusicSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
OpenListCacheURL: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
|
||||||
/>
|
|
||||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
|
||||||
OpenList 服务器的完整地址(例如:https://your-openlist-server.com)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* OpenList 用户名 */}
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
|
||||||
OpenList 用户名
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type='text'
|
|
||||||
placeholder='admin'
|
|
||||||
value={musicSettings.OpenListCacheUsername}
|
|
||||||
onChange={(e) =>
|
|
||||||
setMusicSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
OpenListCacheUsername: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* OpenList 密码 */}
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
|
||||||
OpenList 密码
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type='password'
|
|
||||||
placeholder='••••••••'
|
|
||||||
value={musicSettings.OpenListCachePassword}
|
|
||||||
onChange={(e) =>
|
|
||||||
setMusicSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
OpenListCachePassword: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
|
||||||
/>
|
|
||||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
|
||||||
用于登录 OpenList 并获取访问权限
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* OpenList 缓存目录 */}
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
|
||||||
缓存目录路径
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type='text'
|
|
||||||
placeholder='/music-cache'
|
|
||||||
value={musicSettings.OpenListCachePath}
|
|
||||||
onChange={(e) =>
|
|
||||||
setMusicSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
OpenListCachePath: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
|
||||||
/>
|
|
||||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
|
||||||
音乐缓存在 OpenList 中的存储目录(例如:/music-cache)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 缓存代理返回开关 */}
|
|
||||||
<div>
|
|
||||||
<div className='flex items-center justify-between'>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
|
||||||
启用缓存代理返回
|
|
||||||
</label>
|
|
||||||
<button
|
|
||||||
type='button'
|
|
||||||
onClick={() =>
|
|
||||||
setMusicSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
OpenListCacheProxyEnabled: !prev.OpenListCacheProxyEnabled,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 ${
|
|
||||||
musicSettings.OpenListCacheProxyEnabled
|
|
||||||
? buttonStyles.toggleOn
|
|
||||||
: buttonStyles.toggleOff
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`inline-block h-4 w-4 transform rounded-full ${
|
|
||||||
buttonStyles.toggleThumb
|
|
||||||
} transition-transform ${
|
|
||||||
musicSettings.OpenListCacheProxyEnabled
|
|
||||||
? buttonStyles.toggleThumbOn
|
|
||||||
: buttonStyles.toggleThumbOff
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
|
||||||
开启后,如果 OpenList 有缓存,将通过代理方式返回给前端,并设置永久缓存头,提升加载速度
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 操作按钮 */}
|
|
||||||
<div className='flex justify-end'>
|
|
||||||
<button
|
|
||||||
onClick={handleSave}
|
|
||||||
disabled={isLoading('saveMusicConfig')}
|
|
||||||
className={`px-4 py-2 ${
|
|
||||||
isLoading('saveMusicConfig')
|
|
||||||
? buttonStyles.disabled
|
|
||||||
: buttonStyles.success
|
|
||||||
} rounded-lg transition-colors`}
|
|
||||||
>
|
|
||||||
{isLoading('saveMusicConfig') ? '保存中…' : '保存'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 弹窗 */}
|
|
||||||
<AlertModal
|
|
||||||
isOpen={alertModal.isOpen}
|
|
||||||
onClose={hideAlert}
|
|
||||||
type={alertModal.type}
|
|
||||||
title={alertModal.title}
|
|
||||||
message={alertModal.message}
|
|
||||||
timer={alertModal.timer}
|
|
||||||
showConfirm={alertModal.showConfirm}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 新增站点配置组件
|
// 新增站点配置组件
|
||||||
const SiteConfigComponent = ({
|
const SiteConfigComponent = ({
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
assertQuarkCookieHeaderSafe,
|
assertQuarkCookieHeaderSafe,
|
||||||
normalizeQuarkCookie,
|
normalizeQuarkCookie,
|
||||||
validateQuarkCookieReadable,
|
validateQuarkCookieReadable,
|
||||||
} from '@/lib/quark.client';
|
} from '@/lib/netdisk/quark.client';
|
||||||
|
|
||||||
export const runtime = 'nodejs';
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
|
|||||||
|
|
||||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||||
import { getConfig } from '@/lib/config';
|
import { getConfig } from '@/lib/config';
|
||||||
import { createQuarkInstantPlayFolder } from '@/lib/quark.client';
|
import { createQuarkInstantPlayFolder } from '@/lib/netdisk/quark.client';
|
||||||
import { base58Encode } from '@/lib/utils';
|
import { base58Encode } from '@/lib/utils';
|
||||||
|
|
||||||
export const runtime = 'nodejs';
|
export const runtime = 'nodejs';
|
||||||
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
|
|||||||
|
|
||||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||||
import { getConfig } from '@/lib/config';
|
import { getConfig } from '@/lib/config';
|
||||||
import { transferQuarkShare } from '@/lib/quark.client';
|
import { transferQuarkShare } from '@/lib/netdisk/quark.client';
|
||||||
|
|
||||||
export const runtime = 'nodejs';
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ export default function PansouSearch({
|
|||||||
const handleQuarkTransfer = async (link: PansouLink) => {
|
const handleQuarkTransfer = async (link: PansouLink) => {
|
||||||
try {
|
try {
|
||||||
setTransferingUrl(link.url);
|
setTransferingUrl(link.url);
|
||||||
const response = await fetch('/api/quark/transfer', {
|
const response = await fetch('/api/netdisk/quark/transfer', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -152,7 +152,7 @@ export default function PansouSearch({
|
|||||||
const handleQuarkInstantPlay = async (link: PansouLink) => {
|
const handleQuarkInstantPlay = async (link: PansouLink) => {
|
||||||
try {
|
try {
|
||||||
setPlayingUrl(link.url);
|
setPlayingUrl(link.url);
|
||||||
const response = await fetch('/api/quark/instant-play', {
|
const response = await fetch('/api/netdisk/quark/instant-play', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
Reference in New Issue
Block a user