Skip to content

Commit 4bf6bc7

Browse files
committed
refactor(auth): remove iflow management flows
Remove the deprecated iFlow OAuth and cookie flows from the management center. Keep auth-file labels, provider mappings, and locale strings aligned with the remaining providers.
1 parent 19f72f0 commit 4bf6bc7

13 files changed

Lines changed: 42 additions & 318 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ See `api.md` for the full authentication rules, server-side limits, and edge cas
8181
- OpenAI-compatible providers (multiple API keys, custom headers, model alias import via `/v1/models`, optional browser-side "chat/completions" test).
8282
- Ampcode integration (upstream URL/key, force mappings, model mapping table).
8383
- **Auth Files**: upload/download/delete JSON credentials, filter/search/pagination, runtime-only indicators, view supported models per credential (when the server supports it), manage OAuth excluded models (supports `*` wildcards), configure OAuth model alias mappings.
84-
- **OAuth**: start OAuth/device flows for supported providers, poll status, optionally submit callback `redirect_url`; includes iFlow cookie import.
84+
- **OAuth**: start OAuth/device flows for supported providers, poll status, optionally submit callback `redirect_url`.
8585
- **Quota Management**: manage quota limits and usage for Claude, Antigravity, Codex, Gemini CLI, and other providers.
8686
- **Usage**: requests/tokens charts (hour/day), per-API & per-model breakdown, cached/reasoning token breakdown, RPM/TPM window, optional cost estimation with locally-saved model pricing.
8787
- **Config**: edit `/config.yaml` in-browser with YAML highlighting + search, then save/reload.

README_CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ npm run build
8080
- OpenAI 兼容提供商(多 Key、Header、自助从 `/v1/models` 拉取并导入模型别名、可选浏览器侧 `chat/completions` 测试)。
8181
- Ampcode 集成(上游地址/密钥、强制映射、模型映射表)。
8282
- **认证文件**:上传/下载/删除 JSON 凭据,筛选/搜索/分页,标记 runtime-only;查看单个凭据可用模型(依赖后端支持);管理 OAuth 排除模型(支持 `*` 通配符);配置 OAuth 模型别名映射。
83-
- **OAuth**:对支持的提供商发起 OAuth/设备码流程,轮询状态;可选提交回调 `redirect_url`;包含 iFlow Cookie 导入
83+
- **OAuth**:对支持的提供商发起 OAuth/设备码流程,轮询状态;可选提交回调 `redirect_url`
8484
- **配额管理**:管理 Claude、Antigravity、Codex、Gemini CLI 等提供商的配额上限与使用情况。
8585
- **使用统计**:按小时/天图表、按 API 与按模型统计、缓存/推理 Token 拆分、RPM/TPM 时间窗、可选本地保存的模型价格用于费用估算。
8686
- **配置文件**:浏览器内编辑 `/config.yaml`(YAML 高亮 + 搜索),保存/重载。

src/components/quota/QuotaCard.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ export function QuotaCard<TState extends QuotaStatusState>({
127127
const key = `auth_files.filter_${type}`;
128128
const translated = t(key);
129129
if (translated !== key) return translated;
130-
if (type.toLowerCase() === 'iflow') return 'iFlow';
131130
return type.charAt(0).toUpperCase() + type.slice(1);
132131
};
133132

src/features/authFiles/constants.ts

Lines changed: 37 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import iconAntigravity from '@/assets/icons/antigravity.svg';
33
import iconClaude from '@/assets/icons/claude.svg';
44
import iconCodex from '@/assets/icons/codex.svg';
55
import iconGemini from '@/assets/icons/gemini.svg';
6-
import iconIflow from '@/assets/icons/iflow.svg';
76
import iconKimiDark from '@/assets/icons/kimi-dark.svg';
87
import iconKimiLight from '@/assets/icons/kimi-light.svg';
98
import iconQwen from '@/assets/icons/qwen.svg';
109
import iconVertex from '@/assets/icons/vertex.svg';
1110
import type { AuthFileItem } from '@/types';
11+
import { formatUnixTimestamp } from '@/utils/format';
1212
import {
1313
normalizeAuthIndex,
1414
normalizeUsageSourceId,
@@ -87,11 +87,6 @@ export const TYPE_COLORS: Record<string, TypeColorSet> = {
8787
light: { bg: '#e0f7fa', text: '#006064' },
8888
dark: { bg: '#004d40', text: '#80deea' },
8989
},
90-
// iFlow logo: 品红紫渐变 #5C5CFF → #AE5CFF,偏品红以区别于 Qwen 的紫罗兰
91-
iflow: {
92-
light: { bg: '#f5e3fc', text: '#9025c8' },
93-
dark: { bg: '#521490', text: '#d49cf5' },
94-
},
9590
// Vertex logo: Google 蓝 #4285F4
9691
vertex: {
9792
light: { bg: '#e4edfd', text: '#2b5fbc' },
@@ -114,7 +109,6 @@ export const AUTH_FILE_ICONS: Record<string, AuthFileIconAsset> = {
114109
codex: iconCodex,
115110
gemini: iconGemini,
116111
'gemini-cli': iconGemini,
117-
iflow: iconIflow,
118112
kimi: { light: iconKimiLight, dark: iconKimiDark },
119113
qwen: iconQwen,
120114
vertex: iconVertex,
@@ -149,7 +143,6 @@ export const getTypeLabel = (t: TFunction, type: string): string => {
149143
const key = `auth_files.filter_${type}`;
150144
const translated = t(key);
151145
if (translated !== key) return translated;
152-
if (type.toLowerCase() === 'iflow') return 'iFlow';
153146
return type.charAt(0).toUpperCase() + type.slice(1);
154147
};
155148

@@ -200,6 +193,36 @@ export const normalizeExcludedModels = (value: unknown): string[] => {
200193
export const parseExcludedModelsText = (value: string): string[] =>
201194
normalizeExcludedModels(value.split(/[\n,]+/));
202195

196+
export const isModelExcluded = (
197+
modelId: string,
198+
provider: string,
199+
excluded: Record<string, string[]>
200+
): boolean => {
201+
const normalizedModelId = modelId.trim().toLowerCase();
202+
if (!normalizedModelId) return false;
203+
204+
const rules = excluded[normalizeProviderKey(provider)] ?? [];
205+
return rules.some((rule) => {
206+
const normalizedRule = String(rule ?? '')
207+
.trim()
208+
.toLowerCase();
209+
if (!normalizedRule) return false;
210+
if (normalizedRule === '*') return true;
211+
if (!normalizedRule.includes('*')) return normalizedRule === normalizedModelId;
212+
213+
const pattern = normalizedRule
214+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
215+
.replace(/\*/g, '.*');
216+
return new RegExp(`^${pattern}$`, 'i').test(normalizedModelId);
217+
});
218+
};
219+
220+
export const formatModified = (file: AuthFileItem): string => {
221+
if (!file.modified) return '-';
222+
const formatted = formatUnixTimestamp(file.modified);
223+
return formatted || '-';
224+
};
225+
203226
export const parseDisableCoolingValue = (value: unknown): boolean | undefined => {
204227
if (typeof value === 'boolean') return value;
205228
if (typeof value === 'number' && Number.isFinite(value)) return value !== 0;
@@ -234,73 +257,17 @@ export function isRuntimeOnlyAuthFile(file: AuthFileItem): boolean {
234257

235258
export function resolveAuthFileStats(file: AuthFileItem, stats: KeyStats): KeyStatBucket {
236259
const defaultStats: KeyStatBucket = { success: 0, failure: 0 };
237-
const rawFileName = file?.name || '';
238260

239-
// 兼容 auth_index 和 authIndex 两种字段名(API 返回的是 auth_index)
240261
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
241-
const authIndexKey = normalizeAuthIndex(rawAuthIndex);
242-
243-
// 尝试根据 authIndex 匹配
244-
if (authIndexKey && stats.byAuthIndex?.[authIndexKey]) {
245-
return stats.byAuthIndex[authIndexKey];
246-
}
247-
248-
// 尝试根据 source (文件名) 匹配
249-
const fileNameId = rawFileName ? normalizeUsageSourceId(rawFileName) : '';
250-
if (fileNameId && stats.bySource?.[fileNameId]) {
251-
const fromName = stats.bySource[fileNameId];
252-
if (fromName.success > 0 || fromName.failure > 0) {
253-
return fromName;
254-
}
262+
const authIndex = normalizeAuthIndex(rawAuthIndex);
263+
if (authIndex) {
264+
return stats.byAuthIndex[authIndex] || defaultStats;
255265
}
256266

257-
// 尝试去掉扩展名后匹配
258-
if (rawFileName) {
259-
const nameWithoutExt = rawFileName.replace(/\.[^/.]+$/, '');
260-
if (nameWithoutExt && nameWithoutExt !== rawFileName) {
261-
const nameWithoutExtId = normalizeUsageSourceId(nameWithoutExt);
262-
const fromNameWithoutExt = nameWithoutExtId ? stats.bySource?.[nameWithoutExtId] : undefined;
263-
if (
264-
fromNameWithoutExt &&
265-
(fromNameWithoutExt.success > 0 || fromNameWithoutExt.failure > 0)
266-
) {
267-
return fromNameWithoutExt;
268-
}
269-
}
267+
const usageSourceId = normalizeUsageSourceId(file['usage_source_id'] ?? file.usageSourceId);
268+
if (usageSourceId) {
269+
return stats.bySource[usageSourceId] || defaultStats;
270270
}
271271

272272
return defaultStats;
273273
}
274-
275-
export const formatModified = (item: AuthFileItem): string => {
276-
const raw = item['modtime'] ?? item.modified;
277-
if (!raw) return '-';
278-
const asNumber = Number(raw);
279-
const date =
280-
Number.isFinite(asNumber) && !Number.isNaN(asNumber)
281-
? new Date(asNumber < 1e12 ? asNumber * 1000 : asNumber)
282-
: new Date(String(raw));
283-
return Number.isNaN(date.getTime()) ? '-' : date.toLocaleString();
284-
};
285-
286-
// 检查模型是否被 OAuth 排除
287-
export const isModelExcluded = (
288-
modelId: string,
289-
providerType: string,
290-
excluded: Record<string, string[]>
291-
): boolean => {
292-
const providerKey = normalizeProviderKey(providerType);
293-
const excludedModels = excluded[providerKey] || excluded[providerType] || [];
294-
return excludedModels.some((pattern) => {
295-
if (pattern.includes('*')) {
296-
// 支持通配符匹配:先转义正则特殊字符,再将 * 视为通配符
297-
const regexSafePattern = pattern
298-
.split('*')
299-
.map((segment) => segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
300-
.join('.*');
301-
const regex = new RegExp(`^${regexSafePattern}$`, 'i');
302-
return regex.test(modelId);
303-
}
304-
return pattern.toLowerCase() === modelId.toLowerCase();
305-
});
306-
};

src/i18n/locales/en.json

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,6 @@
534534
"filter_claude": "Claude",
535535
"filter_codex": "Codex",
536536
"filter_antigravity": "Antigravity",
537-
"filter_iflow": "iFlow",
538537
"filter_vertex": "Vertex",
539538
"filter_empty": "Empty",
540539
"filter_unknown": "Other",
@@ -546,7 +545,6 @@
546545
"type_claude": "Claude",
547546
"type_codex": "Codex",
548547
"type_antigravity": "Antigravity",
549-
"type_iflow": "iFlow",
550548
"type_vertex": "Vertex",
551549
"type_empty": "Empty",
552550
"type_unknown": "Other",
@@ -929,34 +927,6 @@
929927
"oauth_callback_status_success": "Callback URL submitted, waiting for authentication...",
930928
"oauth_callback_status_error": "Callback URL submission failed:",
931929
"missing_state": "Unable to retrieve authentication state parameter",
932-
"iflow_oauth_title": "iFlow OAuth",
933-
"iflow_oauth_button": "Start iFlow Login",
934-
"iflow_oauth_hint": "Login to iFlow service through OAuth flow, automatically obtain and save authentication files.",
935-
"iflow_oauth_url_label": "Authorization URL:",
936-
"iflow_open_link": "Open Link",
937-
"iflow_copy_link": "Copy Link",
938-
"iflow_oauth_status_waiting": "Waiting for authentication...",
939-
"iflow_oauth_status_success": "Authentication successful!",
940-
"iflow_oauth_status_error": "Authentication failed:",
941-
"iflow_oauth_start_error": "Failed to start iFlow OAuth:",
942-
"iflow_oauth_polling_error": "Failed to check authentication status:",
943-
"iflow_cookie_title": "iFlow Cookie Login",
944-
"iflow_cookie_label": "Cookie Value:",
945-
"iflow_cookie_placeholder": "Enter the BXAuth value, starting with BXAuth=",
946-
"iflow_cookie_hint": "Submit an existing cookie to finish login without opening the authorization link; the credential file will be saved automatically.",
947-
"iflow_cookie_key_hint": "Note: Create a key on the platform first.",
948-
"iflow_cookie_button": "Submit Cookie Login",
949-
"iflow_cookie_status_success": "Cookie login succeeded and credentials are saved.",
950-
"iflow_cookie_status_error": "Cookie login failed:",
951-
"iflow_cookie_status_duplicate": "Duplicate config:",
952-
"iflow_cookie_start_error": "Failed to submit cookie login:",
953-
"iflow_cookie_config_duplicate": "A config file already exists (duplicate). Remove the existing file and try again if you want to re-save it.",
954-
"iflow_cookie_required": "Please provide the Cookie value first.",
955-
"iflow_cookie_result_title": "Cookie Login Result",
956-
"iflow_cookie_result_email": "Account",
957-
"iflow_cookie_result_expired": "Expires At",
958-
"iflow_cookie_result_path": "Saved Path",
959-
"iflow_cookie_result_type": "Type",
960930
"remote_access_disabled": "This login method is not available for remote access. Please access from localhost."
961931
},
962932
"usage_stats": {

src/i18n/locales/ru.json

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,6 @@
534534
"filter_claude": "Claude",
535535
"filter_codex": "Codex",
536536
"filter_antigravity": "Antigravity",
537-
"filter_iflow": "iFlow",
538537
"filter_vertex": "Vertex",
539538
"filter_empty": "Пусто",
540539
"filter_unknown": "Другое",
@@ -546,7 +545,6 @@
546545
"type_claude": "Claude",
547546
"type_codex": "Codex",
548547
"type_antigravity": "Antigravity",
549-
"type_iflow": "iFlow",
550548
"type_vertex": "Vertex",
551549
"type_empty": "Пусто",
552550
"type_unknown": "Другое",
@@ -926,34 +924,6 @@
926924
"oauth_callback_status_success": "Callback URL отправлен, ожидаем аутентификацию...",
927925
"oauth_callback_status_error": "Не удалось отправить Callback URL:",
928926
"missing_state": "Не удалось получить параметр состояния аутентификации",
929-
"iflow_oauth_title": "iFlow OAuth",
930-
"iflow_oauth_button": "Начать вход iFlow",
931-
"iflow_oauth_hint": "Выполните вход в сервис iFlow через OAuth и автоматически получите/сохраните файлы авторизации.",
932-
"iflow_oauth_url_label": "URL авторизации:",
933-
"iflow_open_link": "Открыть ссылку",
934-
"iflow_copy_link": "Скопировать ссылку",
935-
"iflow_oauth_status_waiting": "Ожидание аутентификации...",
936-
"iflow_oauth_status_success": "Аутентификация успешна!",
937-
"iflow_oauth_status_error": "Ошибка аутентификации:",
938-
"iflow_oauth_start_error": "Не удалось запустить iFlow OAuth:",
939-
"iflow_oauth_polling_error": "Не удалось проверить статус аутентификации:",
940-
"iflow_cookie_title": "Вход iFlow по cookie",
941-
"iflow_cookie_label": "Значение cookie:",
942-
"iflow_cookie_placeholder": "Введите значение BXAuth, начиная с BXAuth=",
943-
"iflow_cookie_hint": "Отправьте существующий cookie, чтобы завершить вход без открытия ссылки авторизации; файл учётных данных будет сохранён автоматически.",
944-
"iflow_cookie_key_hint": "Примечание: сначала создайте ключ на платформе.",
945-
"iflow_cookie_button": "Отправить вход по cookie",
946-
"iflow_cookie_status_success": "Вход по cookie выполнен, учётные данные сохранены.",
947-
"iflow_cookie_status_error": "Ошибка входа по cookie:",
948-
"iflow_cookie_status_duplicate": "Дублирующая конфигурация:",
949-
"iflow_cookie_start_error": "Не удалось отправить вход по cookie:",
950-
"iflow_cookie_config_duplicate": "Такая конфигурация уже существует. Удалите файл и повторите, если хотите перезаписать.",
951-
"iflow_cookie_required": "Сначала укажите значение cookie.",
952-
"iflow_cookie_result_title": "Результат входа по cookie",
953-
"iflow_cookie_result_email": "Аккаунт",
954-
"iflow_cookie_result_expired": "Истекает",
955-
"iflow_cookie_result_path": "Путь сохранения",
956-
"iflow_cookie_result_type": "Тип",
957927
"remote_access_disabled": "Этот способ входа недоступен при удалённом доступе. Подключитесь с localhost."
958928
},
959929
"usage_stats": {

src/i18n/locales/zh-CN.json

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,6 @@
534534
"filter_claude": "Claude",
535535
"filter_codex": "Codex",
536536
"filter_antigravity": "Antigravity",
537-
"filter_iflow": "iFlow",
538537
"filter_vertex": "Vertex",
539538
"filter_empty": "空文件",
540539
"filter_unknown": "其他",
@@ -546,7 +545,6 @@
546545
"type_claude": "Claude",
547546
"type_codex": "Codex",
548547
"type_antigravity": "Antigravity",
549-
"type_iflow": "iFlow",
550548
"type_vertex": "Vertex",
551549
"type_empty": "空文件",
552550
"type_unknown": "其他",
@@ -929,34 +927,6 @@
929927
"oauth_callback_status_success": "回调 URL 已提交,等待认证中...",
930928
"oauth_callback_status_error": "回调 URL 提交失败:",
931929
"missing_state": "无法获取认证状态参数",
932-
"iflow_oauth_title": "iFlow OAuth",
933-
"iflow_oauth_button": "开始 iFlow 登录",
934-
"iflow_oauth_hint": "通过 OAuth 流程登录 iFlow 服务,自动获取并保存认证文件。",
935-
"iflow_oauth_url_label": "授权链接:",
936-
"iflow_open_link": "打开链接",
937-
"iflow_copy_link": "复制链接",
938-
"iflow_oauth_status_waiting": "等待认证中...",
939-
"iflow_oauth_status_success": "认证成功!",
940-
"iflow_oauth_status_error": "认证失败:",
941-
"iflow_oauth_start_error": "启动 iFlow OAuth 失败:",
942-
"iflow_oauth_polling_error": "检查认证状态失败:",
943-
"iflow_cookie_title": "iFlow Cookie 登录",
944-
"iflow_cookie_label": "Cookie 内容:",
945-
"iflow_cookie_placeholder": "填入BXAuth值 以BXAuth=开头",
946-
"iflow_cookie_hint": "直接提交 Cookie 以完成登录(无需打开授权链接),服务端将自动保存凭据。",
947-
"iflow_cookie_key_hint": "提示:需在平台上先创建 Key。",
948-
"iflow_cookie_button": "提交 Cookie 登录",
949-
"iflow_cookie_status_success": "Cookie 登录成功,凭据已保存。",
950-
"iflow_cookie_status_error": "Cookie 登录失败:",
951-
"iflow_cookie_status_duplicate": "配置文件重复:",
952-
"iflow_cookie_start_error": "提交 Cookie 登录失败:",
953-
"iflow_cookie_config_duplicate": "检测到配置文件已存在(重复),如需重新保存请先删除原文件后重试。",
954-
"iflow_cookie_required": "请先填写 Cookie 内容",
955-
"iflow_cookie_result_title": "Cookie 登录结果",
956-
"iflow_cookie_result_email": "账号",
957-
"iflow_cookie_result_expired": "过期时间",
958-
"iflow_cookie_result_path": "保存路径",
959-
"iflow_cookie_result_type": "类型",
960930
"remote_access_disabled": "远程访问不支持此登录方式,请从本地 (localhost) 访问"
961931
},
962932
"usage_stats": {

src/pages/AuthFilesOAuthExcludedEditPage.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ const OAUTH_PROVIDER_PRESETS = [
2828
'codex',
2929
'qwen',
3030
'kimi',
31-
'iflow',
3231
];
3332

3433
const OAUTH_PROVIDER_EXCLUDES = new Set(['all', 'unknown', 'empty']);
@@ -93,7 +92,6 @@ export function AuthFilesOAuthExcludedEditPage() {
9392
const key = `auth_files.filter_${type}`;
9493
const translated = t(key);
9594
if (translated !== key) return translated;
96-
if (type.toLowerCase() === 'iflow') return 'iFlow';
9795
return type.charAt(0).toUpperCase() + type.slice(1);
9896
},
9997
[t]

src/pages/AuthFilesOAuthModelAliasEditPage.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const OAUTH_PROVIDER_PRESETS = [
3030
'codex',
3131
'qwen',
3232
'kimi',
33-
'iflow',
3433
];
3534

3635
const OAUTH_PROVIDER_EXCLUDES = new Set(['all', 'unknown', 'empty']);
@@ -116,7 +115,6 @@ export function AuthFilesOAuthModelAliasEditPage() {
116115
const key = `auth_files.filter_${type}`;
117116
const translated = t(key);
118117
if (translated !== key) return translated;
119-
if (type.toLowerCase() === 'iflow') return 'iFlow';
120118
return type.charAt(0).toUpperCase() + type.slice(1);
121119
},
122120
[t]

0 commit comments

Comments
 (0)