mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-14 21:25:09 +08:00
feat: add i18n language toggle and bilingual docs
This commit is contained in:
@@ -1,68 +1,69 @@
|
||||
import { useState } from 'react'
|
||||
import { FileCode, Download, Upload, Copy, Check, AlertTriangle } from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
|
||||
const TEMPLATES = {
|
||||
full: {
|
||||
name: '全量配置模板',
|
||||
desc: '包含密钥、账号及模型映射',
|
||||
config: {
|
||||
keys: ["your-api-key-1", "your-api-key-2"],
|
||||
accounts: [
|
||||
{ email: "user1@example.com", password: "password1", token: "" },
|
||||
{ email: "user2@example.com", password: "password2", token: "" },
|
||||
{ mobile: "+8613800138001", password: "password3", token: "" }
|
||||
],
|
||||
claude_model_mapping: {
|
||||
fast: "deepseek-chat",
|
||||
slow: "deepseek-reasoner"
|
||||
}
|
||||
}
|
||||
},
|
||||
email_only: {
|
||||
name: '仅邮箱账号',
|
||||
desc: '批量导入邮箱格式账号',
|
||||
config: {
|
||||
keys: ["your-api-key"],
|
||||
accounts: [
|
||||
{ email: "account1@example.com", password: "pass1", token: "" },
|
||||
{ email: "account2@example.com", password: "pass2", token: "" },
|
||||
{ email: "account3@example.com", password: "pass3", token: "" }
|
||||
]
|
||||
}
|
||||
},
|
||||
mobile_only: {
|
||||
name: '仅手机号账号',
|
||||
desc: '批量导入手机号格式账号',
|
||||
config: {
|
||||
keys: ["your-api-key"],
|
||||
accounts: [
|
||||
{ mobile: "+8613800000001", password: "pass1", token: "" },
|
||||
{ mobile: "+8613800000002", password: "pass2", token: "" },
|
||||
{ mobile: "+8613800000003", password: "pass3", token: "" }
|
||||
]
|
||||
}
|
||||
},
|
||||
keys_only: {
|
||||
name: '仅 API 密钥',
|
||||
desc: '仅添加 API 访问密钥',
|
||||
config: {
|
||||
keys: ["key-1", "key-2", "key-3"]
|
||||
}
|
||||
}
|
||||
}
|
||||
import { useI18n } from '../i18n'
|
||||
|
||||
export default function BatchImport({ onRefresh, onMessage, authFetch }) {
|
||||
const { t } = useI18n()
|
||||
const [jsonInput, setJsonInput] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [result, setResult] = useState(null)
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const apiFetch = authFetch || fetch
|
||||
const templates = {
|
||||
full: {
|
||||
name: t('batchImport.templates.full.name'),
|
||||
desc: t('batchImport.templates.full.desc'),
|
||||
config: {
|
||||
keys: ["your-api-key-1", "your-api-key-2"],
|
||||
accounts: [
|
||||
{ email: "user1@example.com", password: "password1", token: "" },
|
||||
{ email: "user2@example.com", password: "password2", token: "" },
|
||||
{ mobile: "+8613800138001", password: "password3", token: "" }
|
||||
],
|
||||
claude_model_mapping: {
|
||||
fast: "deepseek-chat",
|
||||
slow: "deepseek-reasoner"
|
||||
}
|
||||
}
|
||||
},
|
||||
email_only: {
|
||||
name: t('batchImport.templates.emailOnly.name'),
|
||||
desc: t('batchImport.templates.emailOnly.desc'),
|
||||
config: {
|
||||
keys: ["your-api-key"],
|
||||
accounts: [
|
||||
{ email: "account1@example.com", password: "pass1", token: "" },
|
||||
{ email: "account2@example.com", password: "pass2", token: "" },
|
||||
{ email: "account3@example.com", password: "pass3", token: "" }
|
||||
]
|
||||
}
|
||||
},
|
||||
mobile_only: {
|
||||
name: t('batchImport.templates.mobileOnly.name'),
|
||||
desc: t('batchImport.templates.mobileOnly.desc'),
|
||||
config: {
|
||||
keys: ["your-api-key"],
|
||||
accounts: [
|
||||
{ mobile: "+8613800000001", password: "pass1", token: "" },
|
||||
{ mobile: "+8613800000002", password: "pass2", token: "" },
|
||||
{ mobile: "+8613800000003", password: "pass3", token: "" }
|
||||
]
|
||||
}
|
||||
},
|
||||
keys_only: {
|
||||
name: t('batchImport.templates.keysOnly.name'),
|
||||
desc: t('batchImport.templates.keysOnly.desc'),
|
||||
config: {
|
||||
keys: ["key-1", "key-2", "key-3"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleImport = async () => {
|
||||
if (!jsonInput.trim()) {
|
||||
onMessage('error', '请输入 JSON 配置内容')
|
||||
onMessage('error', t('batchImport.enterJson'))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ export default function BatchImport({ onRefresh, onMessage, authFetch }) {
|
||||
try {
|
||||
config = JSON.parse(jsonInput)
|
||||
} catch (e) {
|
||||
onMessage('error', '无效的 JSON 格式')
|
||||
onMessage('error', t('messages.invalidJson'))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -85,23 +86,23 @@ export default function BatchImport({ onRefresh, onMessage, authFetch }) {
|
||||
const data = await res.json()
|
||||
if (res.ok) {
|
||||
setResult(data)
|
||||
onMessage('success', `导入成功: ${data.imported_keys} 个密钥, ${data.imported_accounts} 个账号`)
|
||||
onMessage('success', t('batchImport.importSuccess', { keys: data.imported_keys, accounts: data.imported_accounts }))
|
||||
onRefresh()
|
||||
} else {
|
||||
onMessage('error', data.detail || '导入失败')
|
||||
onMessage('error', data.detail || t('messages.importFailed'))
|
||||
}
|
||||
} catch (e) {
|
||||
onMessage('error', '网络错误')
|
||||
onMessage('error', t('messages.networkError'))
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const loadTemplate = (key) => {
|
||||
const tpl = TEMPLATES[key]
|
||||
const tpl = templates[key]
|
||||
if (tpl) {
|
||||
setJsonInput(JSON.stringify(tpl.config, null, 2))
|
||||
onMessage('info', `已加载模板: ${tpl.name}`)
|
||||
onMessage('info', t('batchImport.templateLoaded', { name: tpl.name }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +112,10 @@ export default function BatchImport({ onRefresh, onMessage, authFetch }) {
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setJsonInput(JSON.stringify(JSON.parse(data.json), null, 2))
|
||||
onMessage('success', '当前配置已加载')
|
||||
onMessage('success', t('batchImport.currentConfigLoaded'))
|
||||
}
|
||||
} catch (e) {
|
||||
onMessage('error', '获取配置失败')
|
||||
onMessage('error', t('batchImport.fetchConfigFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,10 +127,10 @@ export default function BatchImport({ onRefresh, onMessage, authFetch }) {
|
||||
await navigator.clipboard.writeText(data.base64)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
onMessage('success', 'Base64 配置已复制到剪贴板')
|
||||
onMessage('success', t('batchImport.copySuccess'))
|
||||
}
|
||||
} catch (e) {
|
||||
onMessage('error', '复制失败')
|
||||
onMessage('error', t('messages.copyFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,10 +141,10 @@ export default function BatchImport({ onRefresh, onMessage, authFetch }) {
|
||||
<div className="bg-card border border-border rounded-xl p-5 shadow-sm">
|
||||
<h3 className="font-semibold flex items-center gap-2 mb-4">
|
||||
<FileCode className="w-4 h-4 text-primary" />
|
||||
快速模板
|
||||
{t('batchImport.quickTemplates')}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
{Object.entries(TEMPLATES).map(([key, tpl]) => (
|
||||
{Object.entries(templates).map(([key, tpl]) => (
|
||||
<button
|
||||
key={key}
|
||||
onClick={() => loadTemplate(key)}
|
||||
@@ -159,20 +160,20 @@ export default function BatchImport({ onRefresh, onMessage, authFetch }) {
|
||||
<div className="bg-linear-to-br from-primary/10 to-transparent border border-primary/20 rounded-xl p-5 shadow-sm">
|
||||
<h3 className="font-semibold flex items-center gap-2 mb-2 text-primary">
|
||||
<Download className="w-4 h-4" />
|
||||
数据导出
|
||||
{t('batchImport.dataExport')}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
获取配置的 Base64 字符串,用于 Vercel 环境变量。
|
||||
{t('batchImport.dataExportDesc')}
|
||||
</p>
|
||||
<button
|
||||
onClick={copyBase64}
|
||||
className="w-full flex items-center justify-center gap-2 py-2.5 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-all font-medium text-sm shadow-sm"
|
||||
>
|
||||
{copied ? <Check className="w-4 h-4" /> : <Copy className="w-4 h-4" />}
|
||||
{copied ? '已复制' : '复制 Base64 配置'}
|
||||
{copied ? t('batchImport.copied') : t('batchImport.copyBase64')}
|
||||
</button>
|
||||
<p className="text-[10px] text-muted-foreground mt-2 text-center">
|
||||
变量名: <code className="bg-background px-1 py-0.5 rounded border border-border">DS2API_CONFIG_JSON</code>
|
||||
{t('batchImport.variableName')}: <code className="bg-background px-1 py-0.5 rounded border border-border">DS2API_CONFIG_JSON</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -182,14 +183,14 @@ export default function BatchImport({ onRefresh, onMessage, authFetch }) {
|
||||
<div className="p-4 border-b border-border flex items-center justify-between bg-muted/20">
|
||||
<h3 className="font-semibold flex items-center gap-2">
|
||||
<Upload className="w-4 h-4 text-primary" />
|
||||
JSON 编辑器
|
||||
{t('batchImport.jsonEditor')}
|
||||
</h3>
|
||||
<div className="flex gap-2">
|
||||
<button onClick={handleExport} className="px-3 py-1.5 bg-secondary text-secondary-foreground rounded-lg hover:bg-secondary/80 transition-colors text-xs font-medium border border-border">
|
||||
加载当前配置
|
||||
{t('batchImport.loadCurrentConfig')}
|
||||
</button>
|
||||
<button onClick={handleImport} disabled={loading} className="px-3 py-1.5 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors text-xs font-medium disabled:opacity-50">
|
||||
{loading ? '正在导入...' : '应用配置'}
|
||||
{loading ? t('batchImport.importing') : t('batchImport.applyConfig')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -217,10 +218,10 @@ export default function BatchImport({ onRefresh, onMessage, authFetch }) {
|
||||
)}
|
||||
<div>
|
||||
<h4 className={clsx("font-medium", result.imported_keys || result.imported_accounts ? "text-emerald-500" : "text-destructive")}>
|
||||
导入操作已完成
|
||||
{t('batchImport.importComplete')}
|
||||
</h4>
|
||||
<p className="text-sm opacity-80 mt-1">
|
||||
成功导入了 {result.imported_keys} 个 API 密钥,并更新了 {result.imported_accounts} 个账号。
|
||||
{t('batchImport.importSummary', { keys: result.imported_keys, accounts: result.imported_accounts })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user