mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 00:35:32 +08:00
feat(deeplink): 深链支持用量查询配置 (#400)
## 新增功能
- 深链导入支持用量查询配置参数:
- `usageEnabled`: 是否启用用量查询
- `usageScript`: Base64 编码的用量查询脚本
- `usageApiKey`: 用量查询专用 API Key
- `usageBaseUrl`: 用量查询专用 Base URL
- `usageAccessToken`: 访问令牌(NewAPI 模板)
- `usageUserId`: 用户 ID(NewAPI 模板)
- `usageAutoInterval`: 自动查询间隔(分钟)
## 修改文件
- **mod.rs**: DeepLinkImportRequest 结构体添加用量查询字段
- **parser.rs**: 解析 URL 中的用量查询参数
- **provider.rs**: 构建 ProviderMeta 包含 UsageScript 配置
- **deeplink.ts**: 添加 TypeScript 类型定义
- **DeepLinkImportDialog.tsx**: 确认对话框显示用量查询配置
## Bug 修复
- **formatters.ts**: 修复 formatJSON() 格式化时删除 "env" 键的问题
## 深链格式示例
```
ccswitch://v1/import?resource=provider&app=claude&name=xxx&usageEnabled=true&usageScript={base64}&usageAutoInterval=30
```
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,7 @@ export function DeepLinkImportDialog() {
|
||||
|
||||
// 容错判断:MCP 导入结果可能缺少 type 字段
|
||||
const isMcpImportResult = (
|
||||
value: unknown,
|
||||
value: unknown
|
||||
): value is {
|
||||
importedCount: number;
|
||||
importedIds: string[];
|
||||
@@ -59,7 +59,7 @@ export function DeepLinkImportDialog() {
|
||||
if (event.payload.config || event.payload.configUrl) {
|
||||
try {
|
||||
const mergedRequest = await deeplinkApi.mergeDeeplinkConfig(
|
||||
event.payload,
|
||||
event.payload
|
||||
);
|
||||
console.log("Config merged successfully:", mergedRequest);
|
||||
setRequest(mergedRequest);
|
||||
@@ -77,7 +77,7 @@ export function DeepLinkImportDialog() {
|
||||
}
|
||||
|
||||
setIsOpen(true);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Listen for deep link error events
|
||||
@@ -148,7 +148,7 @@ export function DeepLinkImportDialog() {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("prompt-imported", {
|
||||
detail: { app: request.app },
|
||||
}),
|
||||
})
|
||||
);
|
||||
toast.success(t("deeplink.promptImportSuccess"), {
|
||||
description: t("deeplink.promptImportSuccessDescription", {
|
||||
@@ -279,7 +279,7 @@ export function DeepLinkImportDialog() {
|
||||
const maskValue = (key: string, value: string): string => {
|
||||
const sensitiveKeys = ["TOKEN", "KEY", "SECRET", "PASSWORD"];
|
||||
const isSensitive = sensitiveKeys.some((k) =>
|
||||
key.toUpperCase().includes(k),
|
||||
key.toUpperCase().includes(k)
|
||||
);
|
||||
if (isSensitive && value.length > 8) {
|
||||
return `${value.substring(0, 8)}${"*".repeat(12)}`;
|
||||
@@ -521,7 +521,7 @@ export function DeepLinkImportDialog() {
|
||||
{maskValue(key, String(value))}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -548,7 +548,7 @@ export function DeepLinkImportDialog() {
|
||||
{maskValue(key, String(value))}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -584,7 +584,7 @@ export function DeepLinkImportDialog() {
|
||||
{maskValue(key, String(value))}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -605,6 +605,86 @@ export function DeepLinkImportDialog() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Usage Script Configuration (v3.9+) */}
|
||||
{request.usageScript && (
|
||||
<div className="space-y-3 pt-2 border-t border-border-default">
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
{t("deeplink.usageScript", {
|
||||
defaultValue: "用量查询",
|
||||
})}
|
||||
</div>
|
||||
<div className="col-span-2 text-sm">
|
||||
<span
|
||||
className={`inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium ${
|
||||
request.usageEnabled !== false
|
||||
? "bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300"
|
||||
: "bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{request.usageEnabled !== false
|
||||
? t("deeplink.usageScriptEnabled", {
|
||||
defaultValue: "已启用",
|
||||
})
|
||||
: t("deeplink.usageScriptDisabled", {
|
||||
defaultValue: "未启用",
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Usage API Key (if different from provider) */}
|
||||
{request.usageApiKey &&
|
||||
request.usageApiKey !== request.apiKey && (
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
{t("deeplink.usageApiKey", {
|
||||
defaultValue: "用量 API Key",
|
||||
})}
|
||||
</div>
|
||||
<div className="col-span-2 text-sm font-mono text-muted-foreground">
|
||||
{request.usageApiKey.length > 4
|
||||
? `${request.usageApiKey.substring(0, 4)}${"*".repeat(12)}`
|
||||
: "****"}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Usage Base URL (if different from provider) */}
|
||||
{request.usageBaseUrl &&
|
||||
request.usageBaseUrl !== request.endpoint && (
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
{t("deeplink.usageBaseUrl", {
|
||||
defaultValue: "用量查询地址",
|
||||
})}
|
||||
</div>
|
||||
<div className="col-span-2 text-sm break-all">
|
||||
{request.usageBaseUrl}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Auto Query Interval */}
|
||||
{request.usageAutoInterval &&
|
||||
request.usageAutoInterval > 0 && (
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<div className="font-medium text-sm text-muted-foreground">
|
||||
{t("deeplink.usageAutoInterval", {
|
||||
defaultValue: "自动查询",
|
||||
})}
|
||||
</div>
|
||||
<div className="col-span-2 text-sm">
|
||||
{t("deeplink.usageAutoIntervalValue", {
|
||||
defaultValue: "每 {{minutes}} 分钟",
|
||||
minutes: request.usageAutoInterval,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Warning */}
|
||||
<div className="rounded-lg bg-yellow-50 dark:bg-yellow-900/20 p-3 text-sm text-yellow-800 dark:text-yellow-200">
|
||||
{t("deeplink.warning")}
|
||||
|
||||
Reference in New Issue
Block a user