mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-21 11:49:02 +08:00
feat(auth): 添加 SECTL Auth OAuth 登录功能
- 新增 OAuth 登录组件和回调页面 - 集成 Tauri 深度链接插件处理 OAuth 回调 - 添加账户设置页面显示登录信息 - 更新 CI 配置添加 OAuth 环境变量 - 扩展 API 接口支持 OAuth 相关操作 - 添加国际化支持
This commit is contained in:
+122
-5
@@ -12,12 +12,17 @@ import {
|
||||
Modal,
|
||||
Switch,
|
||||
message,
|
||||
Avatar,
|
||||
Typography,
|
||||
} from "antd"
|
||||
import { ThemeQuickSettings } from "./ThemeQuickSettings"
|
||||
import { OAuthLogin } from "./OAuthLogin"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { changeLanguage, getCurrentLanguage, languageOptions, AppLanguage } from "../i18n"
|
||||
import { useResponsive } from "../hooks/useResponsive"
|
||||
|
||||
const { Text, Paragraph } = Typography
|
||||
|
||||
type permissionLevel = "admin" | "points" | "view"
|
||||
type appSettings = {
|
||||
is_wizard_completed: boolean
|
||||
@@ -76,7 +81,13 @@ export const Settings: React.FC<{
|
||||
const [recoveryDialogString, setRecoveryDialogString] = useState("")
|
||||
const [recoveryDialogFilename, setRecoveryDialogFilename] = useState("")
|
||||
|
||||
const [aboutContent, setAboutContent] = useState<{ title: string; description: string; content: string; rawMarkdown: string } | null>(null)
|
||||
const [aboutContent, setAboutContent] = useState<{
|
||||
title: string
|
||||
description: string
|
||||
content: string
|
||||
rawMarkdown: string
|
||||
version?: string
|
||||
} | null>(null)
|
||||
|
||||
const [logsDialogVisible, setLogsDialogVisible] = useState(false)
|
||||
const [logsText, setLogsText] = useState("")
|
||||
@@ -120,6 +131,15 @@ export const Settings: React.FC<{
|
||||
const [pgSwitchLoading, setPgSwitchLoading] = useState(false)
|
||||
const [pgUploadLoading, setPgUploadLoading] = useState(false)
|
||||
|
||||
const [oauthLoginVisible, setOAuthLoginVisible] = useState(false)
|
||||
const [oauthUserInfo, setOAuthUserInfo] = useState<{
|
||||
user_id: string
|
||||
email: string
|
||||
name: string
|
||||
github_username?: string
|
||||
permission: number
|
||||
} | null>(null)
|
||||
|
||||
const permissionTag = useMemo(() => {
|
||||
return (
|
||||
<Tag
|
||||
@@ -652,7 +672,10 @@ export const Settings: React.FC<{
|
||||
onChange={async (v) => {
|
||||
if (!(window as any).api) return
|
||||
const next = String(v) as "t9" | "qwerty26"
|
||||
const res = await (window as any).api.setSetting("search_keyboard_layout", next)
|
||||
const res = await (window as any).api.setSetting(
|
||||
"search_keyboard_layout",
|
||||
next
|
||||
)
|
||||
if (res.success) {
|
||||
setSettings((prev) => ({ ...prev, search_keyboard_layout: next }))
|
||||
messageApi.success(t("settings.general.saved"))
|
||||
@@ -668,7 +691,11 @@ export const Settings: React.FC<{
|
||||
]}
|
||||
/>
|
||||
<div
|
||||
style={{ marginTop: "4px", fontSize: "12px", color: "var(--ss-text-secondary)" }}
|
||||
style={{
|
||||
marginTop: "4px",
|
||||
fontSize: "12px",
|
||||
color: "var(--ss-text-secondary)",
|
||||
}}
|
||||
>
|
||||
{t("settings.searchKeyboard.hint")}
|
||||
</div>
|
||||
@@ -693,7 +720,11 @@ export const Settings: React.FC<{
|
||||
disabled={!canAdmin}
|
||||
/>
|
||||
<div
|
||||
style={{ marginTop: "4px", fontSize: "12px", color: "var(--ss-text-secondary)" }}
|
||||
style={{
|
||||
marginTop: "4px",
|
||||
fontSize: "12px",
|
||||
color: "var(--ss-text-secondary)",
|
||||
}}
|
||||
>
|
||||
{t("settings.searchKeyboard.disableHint")}
|
||||
</div>
|
||||
@@ -734,7 +765,6 @@ export const Settings: React.FC<{
|
||||
{t("settings.zoomHint")}
|
||||
</div>
|
||||
</Form.Item>
|
||||
|
||||
</Form>
|
||||
</Card>
|
||||
),
|
||||
@@ -843,6 +873,84 @@ export const Settings: React.FC<{
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "account",
|
||||
label: t("settings.tabs.account"),
|
||||
children: (
|
||||
<>
|
||||
<Card
|
||||
style={{
|
||||
backgroundColor: "var(--ss-card-bg)",
|
||||
color: "var(--ss-text-main)",
|
||||
marginBottom: "16px",
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 600, marginBottom: "16px" }}>
|
||||
{t("settings.account.title")}
|
||||
</div>
|
||||
|
||||
{oauthUserInfo ? (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "16px" }}>
|
||||
<Avatar size={64} style={{ backgroundColor: "#1890ff" }}>
|
||||
{oauthUserInfo.name.charAt(0).toUpperCase()}
|
||||
</Avatar>
|
||||
<div>
|
||||
<div style={{ fontSize: "18px", fontWeight: 600 }}>{oauthUserInfo.name}</div>
|
||||
<div style={{ fontSize: "14px", color: "var(--ss-text-secondary)" }}>
|
||||
{oauthUserInfo.email}
|
||||
</div>
|
||||
{oauthUserInfo.github_username && (
|
||||
<div style={{ fontSize: "13px", color: "var(--ss-text-secondary)" }}>
|
||||
GitHub: @{oauthUserInfo.github_username}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div>
|
||||
<Text type="secondary">{t("settings.account.userId")}:</Text>
|
||||
<Text code style={{ marginLeft: "8px" }}>
|
||||
{oauthUserInfo.user_id}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text type="secondary">{t("settings.account.permission")}:</Text>
|
||||
<Tag color="blue" style={{ marginLeft: "8px" }}>
|
||||
{oauthUserInfo.permission === 1
|
||||
? t("permissions.admin")
|
||||
: oauthUserInfo.permission === 2
|
||||
? t("permissions.points")
|
||||
: t("permissions.view")}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Button danger onClick={() => setOAuthUserInfo(null)}>
|
||||
{t("settings.account.logout")}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ textAlign: "center", padding: "20px 0" }}>
|
||||
<Paragraph style={{ color: "var(--ss-text-secondary)" }}>
|
||||
{t("settings.account.notLoggedIn")}
|
||||
</Paragraph>
|
||||
<Paragraph style={{ color: "var(--ss-text-secondary)", fontSize: "13px" }}>
|
||||
{t("settings.account.oauthHint")}
|
||||
</Paragraph>
|
||||
<Button type="primary" size="large" onClick={() => setOAuthLoginVisible(true)}>
|
||||
{t("settings.account.loginButton")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "database",
|
||||
label: t("settings.database.title"),
|
||||
@@ -1420,6 +1528,15 @@ export const Settings: React.FC<{
|
||||
>
|
||||
清空后将关闭保护(无密码时默认视为管理权限)。
|
||||
</Modal>
|
||||
|
||||
<OAuthLogin
|
||||
visible={oauthLoginVisible}
|
||||
onClose={() => setOAuthLoginVisible(false)}
|
||||
onSuccess={(userInfo) => {
|
||||
setOAuthUserInfo(userInfo)
|
||||
messageApi.success(t("settings.account.loginSuccess"))
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user