From 7fb11f0ff7776708e98845441eb12aa3eb58e4a6 Mon Sep 17 00:00:00 2001 From: mtvpls Date: Fri, 17 Apr 2026 22:50:58 +0800 Subject: [PATCH] =?UTF-8?q?Suwayomi=E8=AE=A4=E8=AF=81=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/admin/page.tsx | 105 +++++++++++++++-- src/app/api/admin/suwayomi/route.ts | 69 +++++++++++ src/app/api/manga/image/route.ts | 33 +++++- src/lib/admin.types.ts | 4 +- src/lib/config.ts | 17 ++- src/lib/suwayomi.client.ts | 177 ++++++++++++++++++++++++++-- 6 files changed, 378 insertions(+), 27 deletions(-) create mode 100644 src/app/api/admin/suwayomi/route.ts diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 23bc5c7..849ecba 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -10127,7 +10127,9 @@ const SuwayomiConfigComponent = ({ const { isLoading, withLoading } = useLoadingState(); const [enabled, setEnabled] = useState(false); const [serverURL, setServerURL] = useState(''); - const [authToken, setAuthToken] = useState(''); + const [authMode, setAuthMode] = useState<'none' | 'basic_auth' | 'simple_login'>('none'); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); const [defaultLang, setDefaultLang] = useState('zh'); const [sourceIds, setSourceIds] = useState(''); const [maxSources, setMaxSources] = useState(10); @@ -10136,7 +10138,9 @@ const SuwayomiConfigComponent = ({ if (config?.SuwayomiConfig) { setEnabled(config.SuwayomiConfig.Enabled || false); setServerURL(config.SuwayomiConfig.ServerURL || ''); - setAuthToken(config.SuwayomiConfig.AuthToken || ''); + setAuthMode(config.SuwayomiConfig.AuthMode || 'none'); + setUsername(config.SuwayomiConfig.Username || ''); + setPassword(config.SuwayomiConfig.Password || ''); setDefaultLang(config.SuwayomiConfig.DefaultLang || 'zh'); setSourceIds((config.SuwayomiConfig.SourceIds || []).join(',')); setMaxSources(config.SuwayomiConfig.MaxSources || 10); @@ -10146,7 +10150,9 @@ const SuwayomiConfigComponent = ({ const buildConfig = () => ({ Enabled: enabled, ServerURL: serverURL, - AuthToken: authToken, + AuthMode: authMode, + Username: authMode === 'none' ? '' : username, + Password: authMode === 'none' ? '' : password, DefaultLang: defaultLang || 'zh', SourceIds: sourceIds.split(',').map((item) => item.trim()).filter(Boolean), MaxSources: Math.max(1, maxSources || 10), @@ -10180,6 +10186,34 @@ const SuwayomiConfigComponent = ({ }); }; + const handleTest = async () => { + await withLoading('testSuwayomi', async () => { + try { + const response = await fetch('/api/admin/suwayomi', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + ServerURL: serverURL, + AuthMode: authMode, + Username: username, + Password: password, + DefaultLang: defaultLang, + }), + }); + + const data = await response.json(); + if (!response.ok || !data.success) { + throw new Error(data.message || data.error || '测试连接失败'); + } + + showSuccess(data.message || '连接成功', showAlert); + } catch (error) { + showError(error instanceof Error ? error.message : '测试连接失败', showAlert); + throw error; + } + }); + }; + return (
@@ -10188,6 +10222,7 @@ const SuwayomiConfigComponent = ({

• 漫画展馆通过 Suwayomi Server 的 GraphQL 接口搜索、拉取章节与阅读页。

+

• 认证仅支持 basic_auth 与 simple_login;未开启认证时请选择“无认证”。

• 可限制默认语言、可用源白名单,以及单次搜索最多查询的源数量。

• 保存后漫画模块会优先使用这里的配置,环境变量只作为兜底。

@@ -10222,16 +10257,57 @@ const SuwayomiConfigComponent = ({
- - setAuthToken(e.target.value)} - placeholder='可选,若 Suwayomi 开启认证请填写' - 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' - /> + +
+ {[ + { value: 'none', label: '无认证' }, + { value: 'basic_auth', label: 'basic_auth' }, + { value: 'simple_login', label: 'simple_login' }, + ].map((item) => ( + + ))} +
+

+ basic_auth 使用 Basic Authorization 头;simple_login 会向 /login.html 提交表单并复用返回 Cookie。 +

+ {authMode !== 'none' && ( +
+
+ + setUsername(e.target.value)} + placeholder='登录用户名' + 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' + /> +
+
+ + setPassword(e.target.value)} + placeholder='登录密码' + 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' + /> +
+
+ )} +
@@ -10267,6 +10343,13 @@ const SuwayomiConfigComponent = ({
+