feat(oauth): implement custom OAuth provider management #1106

- Add support for custom OAuth providers, including creation, retrieval, updating, and deletion.
- Introduce new model and controller for managing custom OAuth providers.
- Enhance existing OAuth logic to accommodate custom providers.
- Update API routes for custom OAuth provider management.
- Include i18n support for custom OAuth-related messages.
This commit is contained in:
CaIon
2026-02-05 21:18:43 +08:00
parent 632baadb57
commit af54ea85d2
20 changed files with 2066 additions and 11 deletions

View File

@@ -34,6 +34,7 @@ import {
onDiscordOAuthClicked,
onOIDCClicked,
onLinuxDOOAuthClicked,
onCustomOAuthClicked,
prepareCredentialRequestOptions,
buildAssertionResult,
isPasskeySupported,
@@ -109,6 +110,7 @@ const LoginForm = () => {
const [githubButtonDisabled, setGithubButtonDisabled] = useState(false);
const githubTimeoutRef = useRef(null);
const githubButtonText = t(githubButtonTextKeyByState[githubButtonState]);
const [customOAuthLoading, setCustomOAuthLoading] = useState({});
const logo = getLogo();
const systemName = getSystemName();
@@ -373,6 +375,23 @@ const LoginForm = () => {
}
};
// 包装的自定义OAuth登录点击处理
const handleCustomOAuthClick = (provider) => {
if ((hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms) {
showInfo(t('请先阅读并同意用户协议和隐私政策'));
return;
}
setCustomOAuthLoading((prev) => ({ ...prev, [provider.slug]: true }));
try {
onCustomOAuthClicked(provider, { shouldLogout: true });
} finally {
// 由于重定向,这里不会执行到,但为了完整性添加
setTimeout(() => {
setCustomOAuthLoading((prev) => ({ ...prev, [provider.slug]: false }));
}, 3000);
}
};
// 包装的邮箱登录选项点击处理
const handleEmailLoginClick = () => {
setEmailLoginLoading(true);
@@ -572,6 +591,23 @@ const LoginForm = () => {
</Button>
)}
{status.custom_oauth_providers &&
status.custom_oauth_providers.map((provider) => (
<Button
key={provider.slug}
theme='outline'
className='w-full h-12 flex items-center justify-center !rounded-full border border-gray-200 hover:bg-gray-50 transition-colors'
type='tertiary'
icon={<IconLock size='large' />}
onClick={() => handleCustomOAuthClick(provider)}
loading={customOAuthLoading[provider.slug]}
>
<span className='ml-3'>
{t('使用 {{name}} 继续', { name: provider.name })}
</span>
</Button>
))}
{status.telegram_oauth && (
<div className='flex justify-center my-2'>
<TelegramLoginButton