mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-07 01:45:27 +08:00
Add admin CRUD and connectivity checks for SOCKS5/SOCKS5H proxy nodes. Allow accounts to bind to a proxy, route DeepSeek requests through the selected node, and expose proxy management in the admin UI.
273 lines
9.9 KiB
JavaScript
273 lines
9.9 KiB
JavaScript
import { useState } from 'react'
|
|
|
|
export function useAccountActions({ apiFetch, t, onMessage, onRefresh, config, fetchAccounts, resolveAccountIdentifier }) {
|
|
const [showAddKey, setShowAddKey] = useState(false)
|
|
const [showAddAccount, setShowAddAccount] = useState(false)
|
|
const [newKey, setNewKey] = useState('')
|
|
const [copiedKey, setCopiedKey] = useState(null)
|
|
const [newAccount, setNewAccount] = useState({ email: '', mobile: '', password: '' })
|
|
const [loading, setLoading] = useState(false)
|
|
const [testing, setTesting] = useState({})
|
|
const [testingAll, setTestingAll] = useState(false)
|
|
const [batchProgress, setBatchProgress] = useState({ current: 0, total: 0, results: [] })
|
|
const [sessionCounts, setSessionCounts] = useState({})
|
|
const [deletingSessions, setDeletingSessions] = useState({})
|
|
const [updatingProxy, setUpdatingProxy] = useState({})
|
|
|
|
const addKey = async () => {
|
|
if (!newKey.trim()) return
|
|
setLoading(true)
|
|
try {
|
|
const res = await apiFetch('/admin/keys', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ key: newKey.trim() }),
|
|
})
|
|
if (res.ok) {
|
|
onMessage('success', t('accountManager.addKeySuccess'))
|
|
setNewKey('')
|
|
setShowAddKey(false)
|
|
onRefresh()
|
|
} else {
|
|
const data = await res.json()
|
|
onMessage('error', data.detail || t('messages.failedToAdd'))
|
|
}
|
|
} catch (e) {
|
|
onMessage('error', t('messages.networkError'))
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const deleteKey = async (key) => {
|
|
if (!confirm(t('accountManager.deleteKeyConfirm'))) return
|
|
try {
|
|
const res = await apiFetch(`/admin/keys/${encodeURIComponent(key)}`, { method: 'DELETE' })
|
|
if (res.ok) {
|
|
onMessage('success', t('messages.deleted'))
|
|
onRefresh()
|
|
} else {
|
|
onMessage('error', t('messages.deleteFailed'))
|
|
}
|
|
} catch (e) {
|
|
onMessage('error', t('messages.networkError'))
|
|
}
|
|
}
|
|
|
|
const addAccount = async () => {
|
|
if (!newAccount.password || (!newAccount.email && !newAccount.mobile)) {
|
|
onMessage('error', t('accountManager.requiredFields'))
|
|
return
|
|
}
|
|
setLoading(true)
|
|
try {
|
|
const res = await apiFetch('/admin/accounts', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(newAccount),
|
|
})
|
|
if (res.ok) {
|
|
onMessage('success', t('accountManager.addAccountSuccess'))
|
|
setNewAccount({ email: '', mobile: '', password: '' })
|
|
setShowAddAccount(false)
|
|
fetchAccounts(1)
|
|
onRefresh()
|
|
} else {
|
|
const data = await res.json()
|
|
onMessage('error', data.detail || t('messages.failedToAdd'))
|
|
}
|
|
} catch (e) {
|
|
onMessage('error', t('messages.networkError'))
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const deleteAccount = async (id) => {
|
|
const identifier = String(id || '').trim()
|
|
if (!identifier) {
|
|
onMessage('error', t('accountManager.invalidIdentifier'))
|
|
return
|
|
}
|
|
if (!confirm(t('accountManager.deleteAccountConfirm'))) return
|
|
try {
|
|
const res = await apiFetch(`/admin/accounts/${encodeURIComponent(identifier)}`, { method: 'DELETE' })
|
|
if (res.ok) {
|
|
onMessage('success', t('messages.deleted'))
|
|
fetchAccounts()
|
|
onRefresh()
|
|
} else {
|
|
onMessage('error', t('messages.deleteFailed'))
|
|
}
|
|
} catch (e) {
|
|
onMessage('error', t('messages.networkError'))
|
|
}
|
|
}
|
|
|
|
const testAccount = async (identifier) => {
|
|
const accountID = String(identifier || '').trim()
|
|
if (!accountID) {
|
|
onMessage('error', t('accountManager.invalidIdentifier'))
|
|
return
|
|
}
|
|
setTesting(prev => ({ ...prev, [accountID]: true }))
|
|
try {
|
|
const res = await apiFetch('/admin/accounts/test', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ identifier: accountID }),
|
|
})
|
|
const data = await res.json()
|
|
|
|
// 更新会话数
|
|
if (data.session_count !== undefined) {
|
|
setSessionCounts(prev => ({ ...prev, [accountID]: data.session_count }))
|
|
}
|
|
|
|
const statusMessage = data.success
|
|
? t('apiTester.testSuccess', { account: accountID, time: data.response_time })
|
|
: `${accountID}: ${data.message}`
|
|
onMessage(data.success ? 'success' : 'error', statusMessage)
|
|
fetchAccounts()
|
|
onRefresh()
|
|
} catch (e) {
|
|
onMessage('error', t('accountManager.testFailed', { error: e.message }))
|
|
} finally {
|
|
setTesting(prev => ({ ...prev, [accountID]: false }))
|
|
}
|
|
}
|
|
|
|
const testAllAccounts = async () => {
|
|
if (!confirm(t('accountManager.testAllConfirm'))) return
|
|
const allAccounts = config.accounts || []
|
|
if (allAccounts.length === 0) return
|
|
|
|
setTestingAll(true)
|
|
setBatchProgress({ current: 0, total: allAccounts.length, results: [] })
|
|
|
|
let successCount = 0
|
|
const results = []
|
|
|
|
for (let i = 0; i < allAccounts.length; i++) {
|
|
const acc = allAccounts[i]
|
|
const id = resolveAccountIdentifier(acc)
|
|
if (!id) {
|
|
results.push({ id: '-', success: false, message: t('accountManager.invalidIdentifier') })
|
|
setBatchProgress({ current: i + 1, total: allAccounts.length, results: [...results] })
|
|
continue
|
|
}
|
|
|
|
try {
|
|
const res = await apiFetch('/admin/accounts/test', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ identifier: id }),
|
|
})
|
|
const data = await res.json()
|
|
results.push({ id, success: data.success, message: data.message, time: data.response_time })
|
|
if (data.success) successCount++
|
|
} catch (e) {
|
|
results.push({ id, success: false, message: e.message })
|
|
}
|
|
|
|
setBatchProgress({ current: i + 1, total: allAccounts.length, results: [...results] })
|
|
}
|
|
|
|
onMessage('success', t('accountManager.testAllCompleted', { success: successCount, total: allAccounts.length }))
|
|
fetchAccounts()
|
|
onRefresh()
|
|
setTestingAll(false)
|
|
}
|
|
|
|
const deleteAllSessions = async (identifier) => {
|
|
const accountID = String(identifier || '').trim()
|
|
if (!accountID) {
|
|
onMessage('error', t('accountManager.invalidIdentifier'))
|
|
return
|
|
}
|
|
if (!confirm(t('accountManager.deleteAllSessionsConfirm'))) return
|
|
|
|
setDeletingSessions(prev => ({ ...prev, [accountID]: true }))
|
|
try {
|
|
const res = await apiFetch('/admin/accounts/sessions/delete-all', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ identifier: accountID }),
|
|
})
|
|
const data = await res.json()
|
|
|
|
if (data.success) {
|
|
onMessage('success', t('accountManager.deleteAllSessionsSuccess'))
|
|
// 清除会话数显示
|
|
setSessionCounts(prev => {
|
|
const newCounts = { ...prev }
|
|
delete newCounts[accountID]
|
|
return newCounts
|
|
})
|
|
} else {
|
|
onMessage('error', data.message || t('messages.requestFailed'))
|
|
}
|
|
} catch (e) {
|
|
onMessage('error', t('messages.networkError'))
|
|
} finally {
|
|
setDeletingSessions(prev => ({ ...prev, [accountID]: false }))
|
|
}
|
|
}
|
|
|
|
const updateAccountProxy = async (identifier, proxyID) => {
|
|
const accountID = String(identifier || '').trim()
|
|
if (!accountID) {
|
|
onMessage('error', t('accountManager.invalidIdentifier'))
|
|
return
|
|
}
|
|
setUpdatingProxy(prev => ({ ...prev, [accountID]: true }))
|
|
try {
|
|
const res = await apiFetch(`/admin/accounts/${encodeURIComponent(accountID)}/proxy`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ proxy_id: proxyID || '' }),
|
|
})
|
|
const data = await res.json()
|
|
if (!res.ok) {
|
|
onMessage('error', data.detail || t('messages.requestFailed'))
|
|
return
|
|
}
|
|
onMessage('success', t('accountManager.proxyUpdateSuccess'))
|
|
fetchAccounts()
|
|
onRefresh()
|
|
} catch (_err) {
|
|
onMessage('error', t('messages.networkError'))
|
|
} finally {
|
|
setUpdatingProxy(prev => ({ ...prev, [accountID]: false }))
|
|
}
|
|
}
|
|
|
|
return {
|
|
showAddKey,
|
|
setShowAddKey,
|
|
showAddAccount,
|
|
setShowAddAccount,
|
|
newKey,
|
|
setNewKey,
|
|
copiedKey,
|
|
setCopiedKey,
|
|
newAccount,
|
|
setNewAccount,
|
|
loading,
|
|
testing,
|
|
testingAll,
|
|
batchProgress,
|
|
sessionCounts,
|
|
deletingSessions,
|
|
updatingProxy,
|
|
addKey,
|
|
deleteKey,
|
|
addAccount,
|
|
deleteAccount,
|
|
testAccount,
|
|
testAllAccounts,
|
|
deleteAllSessions,
|
|
updateAccountProxy,
|
|
}
|
|
}
|