diff --git a/src/admin/accounts/routes.rs b/src/admin/accounts/routes.rs index 787fafd17..0a48a8f6b 100644 --- a/src/admin/accounts/routes.rs +++ b/src/admin/accounts/routes.rs @@ -16,7 +16,9 @@ use serde_json::Value; use crate::{ admin::auth::session::require_admin_session, - admin::response::{AdminEnvelope, AdminError, AdminPageEnvelope, AdminResponse}, + admin::response::{ + AdminEnvelope, AdminError, AdminResponse, CursorPageMeta, NumberedPageMeta, PageMeta, + }, admin::{ accounts::service::{AdminAccountError, AdminAccountMetadata, AdminAccountMetadataUpdate}, monitoring::{ @@ -28,10 +30,11 @@ use crate::{ }, http::middleware::request_id::RequestId, infra::{ - json::{clamp_limit, clamp_page, Page}, + json::{clamp_limit, clamp_page, total_pages, Page}, time::{china_datetime, china_relative_time, china_rfc3339, china_rfc3339_str}, }, runtime::state::AppState, + upstream::accounts::model::AccountStatus, upstream::accounts::store::StoredAccount, }; @@ -314,6 +317,68 @@ impl AccountListStats { } } +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +struct AdminAccountSummaryData { + total: u64, + active: u64, + high_usage: u64, + attention: u64, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +struct AdminAccountPageData { + items: Vec, + page: PageMeta, + summary: AdminAccountSummaryData, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +struct AdminAccountPageEnvelope { + code: u32, + message: String, + data: AdminAccountPageData, +} + +impl AdminAccountPageEnvelope { + fn cursor(page: Page, limit: u32, summary: AdminAccountSummaryData) -> Self { + Self { + code: 200, + message: "OK".into(), + data: AdminAccountPageData { + items: page.items, + page: PageMeta::Cursor(CursorPageMeta { + limit, + next_cursor: page.next_cursor, + }), + summary, + }, + } + } + + fn numbered( + page: crate::infra::json::NumberedPage, + summary: AdminAccountSummaryData, + ) -> Self { + Self { + code: 200, + message: "OK".into(), + data: AdminAccountPageData { + page: PageMeta::Numbered(NumberedPageMeta { + page: page.page, + page_size: page.page_size, + total: page.total, + total_pages: total_pages(page.total, page.page_size), + }), + items: page.items, + summary, + }, + } + } +} + #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] struct BatchDeleteAccountsData { @@ -430,7 +495,9 @@ pub(crate) async fn accounts( require_admin_session(&state, &headers, &request_id).await?; let limit = clamp_limit(params.page_size.or(params.limit).unwrap_or(50)); let use_numbered_page = params.page.is_some() || params.page_size.is_some(); + let search = params.search.clone(); let stats = account_list_stats(&state).await; + let summary = account_summary_data(&state, &stats, search.as_deref()).await; if use_numbered_page { return match state @@ -452,7 +519,7 @@ pub(crate) async fn accounts( }; Ok(AdminResponse::new( StatusCode::OK, - AdminPageEnvelope::numbered(page, request_id), + AdminAccountPageEnvelope::numbered(page, summary), )) } Err(error) => Err(account_error(error, request_id)), @@ -476,7 +543,7 @@ pub(crate) async fn accounts( }; Ok(AdminResponse::new( StatusCode::OK, - AdminPageEnvelope::ok(page, limit, request_id), + AdminAccountPageEnvelope::cursor(page, limit, summary), )) } Err(error) => Err(account_error(error, request_id)), @@ -874,6 +941,81 @@ async fn account_list_stats(state: &AppState) -> AccountListStats { } } +async fn account_summary_data( + state: &AppState, + stats: &AccountListStats, + search: Option<&str>, +) -> AdminAccountSummaryData { + let accounts = list_all_account_metadata(state, search).await; + let total = accounts.len() as u64; + let active = accounts + .iter() + .filter(|account| account.status == AccountStatus::Active) + .count() as u64; + let high_usage = accounts + .iter() + .filter(|account| { + stats + .quota_by_account + .get(&account.id) + .is_some_and(account_quota_has_high_usage) + }) + .count() as u64; + let attention = accounts + .iter() + .filter(|account| account_summary_needs_attention(account.status)) + .count() as u64; + + AdminAccountSummaryData { + total, + active, + high_usage, + attention, + } +} + +async fn list_all_account_metadata( + state: &AppState, + search: Option<&str>, +) -> Vec { + let mut page = 1; + let mut accounts = Vec::new(); + loop { + let Ok(result) = state + .services + .admin_accounts + .list_page( + page, + ACCOUNT_STATS_PAGE_LIMIT, + search.map(ToString::to_string), + ) + .await + else { + return Vec::new(); + }; + let total = result.total; + accounts.extend(result.items); + if accounts.len() as u64 >= total || total == 0 { + return accounts; + } + page = page.saturating_add(1); + } +} + +fn account_quota_has_high_usage(quota: &AdminAccountQuotaData) -> bool { + quota + .windows + .iter() + .any(|window| window.used_percent.is_some_and(|percent| percent >= 80.0)) +} + +fn account_summary_needs_attention(status: AccountStatus) -> bool { + matches!( + status, + AccountStatus::Expired | AccountStatus::Disabled | AccountStatus::Banned + ) +} + async fn list_all_usage_records(state: &AppState) -> Vec { let mut cursor = None; let mut records = Vec::new(); diff --git a/src/admin/settings/routes.rs b/src/admin/settings/routes.rs index 70ede3e27..b9316822c 100644 --- a/src/admin/settings/routes.rs +++ b/src/admin/settings/routes.rs @@ -1,7 +1,5 @@ //! 管理端设置处理器。 -use std::collections::BTreeMap; - use axum::{ body::Bytes, extract::State, @@ -17,31 +15,17 @@ use crate::{ admin::response::{AdminEnvelope, AdminError, AdminResponse}, config::{ settings::{AdminSettingsPatch, RuntimeSettingsError, SettingsServiceError}, - types::{AppConfig, QuotaWarningThresholds}, + types::AppConfig, }, http::middleware::request_id::RequestId, runtime::state::AppState, }; -const ALLOWED_SETTINGS_KEYS: [&str; 18] = [ +const ALLOWED_SETTINGS_KEYS: [&str; 4] = [ "defaultModel", - "defaultReasoningEffort", - "serviceTier", - "modelAliases", - "refreshEnabled", - "refreshMarginSeconds", - "refreshConcurrency", "maxConcurrentPerAccount", "requestIntervalMs", "rotationStrategy", - "tierPriority", - "quotaRefreshIntervalMinutes", - "quotaWarningThresholds", - "quotaSkipExhausted", - "logsEnabled", - "logsCapacity", - "logsCaptureBody", - "usageHistoryRetentionDays", ]; /// 管理端设置响应。 @@ -49,46 +33,18 @@ const ALLOWED_SETTINGS_KEYS: [&str; 18] = [ #[serde(rename_all = "camelCase")] pub struct AdminSettingsData { pub default_model: String, - pub default_reasoning_effort: Option, - pub service_tier: Option, - pub model_aliases: BTreeMap, - pub refresh_enabled: bool, - pub refresh_margin_seconds: u64, - pub refresh_concurrency: u32, pub max_concurrent_per_account: usize, pub request_interval_ms: u64, pub rotation_strategy: String, - pub tier_priority: Vec, - pub quota_refresh_interval_minutes: u64, - pub quota_warning_thresholds: QuotaWarningThresholds, - pub quota_skip_exhausted: bool, - pub logs_enabled: bool, - pub logs_capacity: u32, - pub logs_capture_body: bool, - pub usage_history_retention_days: Option, } impl AdminSettingsData { fn from_config(config: &AppConfig) -> Self { Self { default_model: config.model.default_model.clone(), - default_reasoning_effort: config.model.default_reasoning_effort.clone(), - service_tier: config.model.service_tier.clone(), - model_aliases: config.model.aliases.clone(), - refresh_enabled: config.auth.refresh_enabled, - refresh_margin_seconds: config.auth.refresh_margin_seconds, - refresh_concurrency: config.auth.refresh_concurrency, max_concurrent_per_account: config.auth.max_concurrent_per_account, request_interval_ms: config.auth.request_interval_ms, rotation_strategy: config.auth.rotation_strategy.clone(), - tier_priority: config.auth.tier_priority.clone(), - quota_refresh_interval_minutes: config.quota.refresh_interval_minutes, - quota_warning_thresholds: config.quota.warning_thresholds.clone(), - quota_skip_exhausted: config.quota.skip_exhausted, - logs_enabled: config.logging.enabled, - logs_capacity: config.logging.capacity, - logs_capture_body: config.logging.capture_body, - usage_history_retention_days: config.usage_stats.history_retention_days, } } } diff --git a/src/config/settings.rs b/src/config/settings.rs index 76d981c7d..4c5d81ff0 100644 --- a/src/config/settings.rs +++ b/src/config/settings.rs @@ -107,40 +107,12 @@ impl Default for AdminQuotaWarningThresholds { pub struct AdminSettingsPatch { /// 默认模型 ID。 pub default_model: Option, - /// 默认 reasoning effort。 - pub default_reasoning_effort: Option, - /// 默认服务层级。 - pub service_tier: Option, - /// 模型别名映射。 - pub model_aliases: Option>, - /// 是否启用访问令牌刷新。 - pub refresh_enabled: Option, - /// 访问令牌过期前多少秒开始刷新。 - pub refresh_margin_seconds: Option, - /// 访问令牌刷新并发数。 - pub refresh_concurrency: Option, /// 单账号最大并发请求数。 pub max_concurrent_per_account: Option, /// 同账号请求间隔毫秒数。 pub request_interval_ms: Option, /// 账号轮换策略。 pub rotation_strategy: Option, - /// 计划类型优先级。 - pub tier_priority: Option>, - /// 配额刷新间隔分钟数。 - pub quota_refresh_interval_minutes: Option, - /// 配额预警阈值。 - pub quota_warning_thresholds: Option, - /// 配额耗尽账号是否跳过调度。 - pub quota_skip_exhausted: Option, - /// 是否启用事件日志。 - pub logs_enabled: Option, - /// 事件日志容量。 - pub logs_capacity: Option, - /// 事件日志是否捕获请求/响应体。 - pub logs_capture_body: Option, - /// 用量历史保留天数。 - pub usage_history_retention_days: Option, } /// 设置领域服务。 @@ -156,27 +128,6 @@ impl SettingsService { if let Some(default_model) = patch.default_model { current.default_model = non_empty_string("defaultModel", default_model)?; } - if let Some(default_reasoning_effort) = patch.default_reasoning_effort { - current.default_reasoning_effort = Some(non_empty_string( - "defaultReasoningEffort", - default_reasoning_effort, - )?); - } - if let Some(service_tier) = patch.service_tier { - current.service_tier = Some(non_empty_string("serviceTier", service_tier)?); - } - if let Some(model_aliases) = patch.model_aliases { - current.model_aliases = validate_aliases(model_aliases)?; - } - if let Some(refresh_enabled) = patch.refresh_enabled { - current.refresh_enabled = refresh_enabled; - } - if let Some(refresh_margin_seconds) = patch.refresh_margin_seconds { - current.refresh_margin_seconds = refresh_margin_seconds; - } - if let Some(refresh_concurrency) = patch.refresh_concurrency { - current.refresh_concurrency = positive_u32("refreshConcurrency", refresh_concurrency)?; - } if let Some(max_concurrent_per_account) = patch.max_concurrent_per_account { current.max_concurrent_per_account = positive_usize("maxConcurrentPerAccount", max_concurrent_per_account)?; @@ -187,37 +138,6 @@ impl SettingsService { if let Some(rotation_strategy) = patch.rotation_strategy { current.rotation_strategy = validate_rotation_strategy(rotation_strategy)?; } - if let Some(tier_priority) = patch.tier_priority { - current.tier_priority = validate_tier_priority(tier_priority)?; - } - if let Some(quota_refresh_interval_minutes) = patch.quota_refresh_interval_minutes { - current.quota_refresh_interval_minutes = positive_u64( - "quotaRefreshIntervalMinutes", - quota_refresh_interval_minutes, - )?; - } - if let Some(quota_warning_thresholds) = patch.quota_warning_thresholds { - validate_thresholds("a_warning_thresholds)?; - current.quota_warning_thresholds = quota_warning_thresholds; - } - if let Some(quota_skip_exhausted) = patch.quota_skip_exhausted { - current.quota_skip_exhausted = quota_skip_exhausted; - } - if let Some(logs_enabled) = patch.logs_enabled { - current.logs_enabled = logs_enabled; - } - if let Some(logs_capacity) = patch.logs_capacity { - current.logs_capacity = positive_u32("logsCapacity", logs_capacity)?; - } - if let Some(logs_capture_body) = patch.logs_capture_body { - current.logs_capture_body = logs_capture_body; - } - if let Some(usage_history_retention_days) = patch.usage_history_retention_days { - current.usage_history_retention_days = Some(positive_u64( - "usageHistoryRetentionDays", - usage_history_retention_days, - )?); - } Ok(()) } } @@ -250,26 +170,6 @@ impl SettingsServiceError { } } -fn validate_aliases( - aliases: BTreeMap, -) -> Result, SettingsServiceError> { - let mut validated = BTreeMap::new(); - for (alias, target) in aliases { - let alias = non_empty_string("modelAliases", alias)?; - let target = non_empty_string("modelAliases", target)?; - validated.insert(alias, target); - } - Ok(validated) -} - -fn validate_tier_priority(tiers: Vec) -> Result, SettingsServiceError> { - let mut validated = Vec::with_capacity(tiers.len()); - for tier in tiers { - validated.push(non_empty_string("tierPriority", tier)?); - } - Ok(validated) -} - fn validate_rotation_strategy(strategy: String) -> Result { let strategy = non_empty_string("rotationStrategy", strategy)?; if ROTATION_STRATEGIES.contains(&strategy.as_str()) { @@ -282,29 +182,6 @@ fn validate_rotation_strategy(strategy: String) -> Result Result<(), SettingsServiceError> { - if thresholds.primary.is_empty() && thresholds.secondary.is_empty() { - return Err(invalid_field( - "quotaWarningThresholds", - "must include at least one threshold", - )); - } - if thresholds - .primary - .iter() - .chain(thresholds.secondary.iter()) - .any(|t| *t > 100) - { - return Err(invalid_field( - "quotaWarningThresholds", - "thresholds must be between 0 and 100", - )); - } - Ok(()) -} - fn non_empty_string(field: &str, value: String) -> Result { let value = value.trim().to_string(); if value.is_empty() { @@ -314,22 +191,6 @@ fn non_empty_string(field: &str, value: String) -> Result Result { - if value == 0 { - Err(invalid_field(field, "must be greater than 0")) - } else { - Ok(value) - } -} - -fn positive_u64(field: &str, value: u64) -> Result { - if value == 0 { - Err(invalid_field(field, "must be greater than 0")) - } else { - Ok(value) - } -} - fn positive_usize(field: &str, value: usize) -> Result { if value == 0 { Err(invalid_field(field, "must be greater than 0")) @@ -411,12 +272,13 @@ pub enum RuntimeSettingsError { } fn admin_settings_from_config(config: &AppConfig) -> AdminSettings { + let defaults = AdminSettings::default(); AdminSettings { default_model: config.model.default_model.clone(), default_reasoning_effort: config.model.default_reasoning_effort.clone(), service_tier: config.model.service_tier.clone(), model_aliases: config.model.aliases.clone(), - refresh_enabled: config.auth.refresh_enabled, + refresh_enabled: defaults.refresh_enabled, refresh_margin_seconds: config.auth.refresh_margin_seconds, refresh_concurrency: config.auth.refresh_concurrency, max_concurrent_per_account: config.auth.max_concurrent_per_account, @@ -428,9 +290,9 @@ fn admin_settings_from_config(config: &AppConfig) -> AdminSettings { primary: config.quota.warning_thresholds.primary.clone(), secondary: config.quota.warning_thresholds.secondary.clone(), }, - quota_skip_exhausted: config.quota.skip_exhausted, - logs_enabled: config.logging.enabled, - logs_capacity: config.logging.capacity, + quota_skip_exhausted: defaults.quota_skip_exhausted, + logs_enabled: defaults.logs_enabled, + logs_capacity: defaults.logs_capacity, logs_capture_body: config.logging.capture_body, usage_history_retention_days: config.usage_stats.history_retention_days, } diff --git a/src/upstream/accounts/pool.rs b/src/upstream/accounts/pool.rs index 1e4fd5195..32410810f 100644 --- a/src/upstream/accounts/pool.rs +++ b/src/upstream/accounts/pool.rs @@ -519,17 +519,15 @@ impl AccountPool { pub fn capacity_summary(&mut self, now: DateTime) -> AccountCapacitySummary { self.cleanup_stale_slots(now); self.refresh_account_statuses(now); + let is_capacity_account = |account: &Account| { + account.status == AccountStatus::Active + && AccountService::quota_available_at(account, now, self.options.skip_quota_limited) + && AccountService::cloudflare_available_at(account, now) + }; let active_accounts = self .accounts .values() - .filter(|account| { - account.status == AccountStatus::Active - && AccountService::quota_available_at( - account, - now, - self.options.skip_quota_limited, - ) - }) + .filter(|account| is_capacity_account(account)) .count(); let total_slots = active_accounts * self.options.max_concurrent_per_account; let used_slots = self @@ -538,7 +536,7 @@ impl AccountPool { .filter(|(account_id, _)| { self.accounts .get(*account_id) - .is_some_and(|account| account.status == AccountStatus::Active) + .is_some_and(|account| is_capacity_account(account)) }) .map(|(_, slots)| slots.len().min(self.options.max_concurrent_per_account)) .sum(); diff --git a/web/package.json b/web/package.json index b0cd859a7..626ac306f 100644 --- a/web/package.json +++ b/web/package.json @@ -14,24 +14,24 @@ "dependencies": { "@fontsource-variable/inter": "^5.2.8", "@fontsource-variable/jetbrains-mono": "^5.2.8", - "@lucide/vue": "^1.18.0", - "axios": "^1.18.0", + "@lucide/vue": "^1.21.0", + "axios": "^1.18.1", "echarts": "^6.1.0", "gsap": "^3.15.0", "pinia": "^3.0.4", "pinia-plugin-persistedstate": "^4.7.1", - "vue": "^3.5.38", + "vue": "^3.5.39", "vue-router": "^5.1.0" }, "devDependencies": { "@tailwindcss/vite": "^4.3.1", - "@types/node": "^25.9.3", + "@types/node": "^26.0.1", "@vitejs/plugin-vue": "^6.0.7", "@vue/tsconfig": "^0.9.1", "prettier": "^3.8.4", "tailwindcss": "^4.3.1", "typescript": "^6.0.3", - "vite": "^8.0.16", + "vite": "^8.1.0", "vue-tsc": "^3.3.5" }, "prettier": { diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index b0bba842c..a91062d55 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -15,11 +15,11 @@ importers: specifier: ^5.2.8 version: 5.2.8 '@lucide/vue': - specifier: ^1.18.0 - version: 1.18.0(vue@3.5.38(typescript@6.0.3)) + specifier: ^1.21.0 + version: 1.21.0(vue@3.5.39(typescript@6.0.3)) axios: - specifier: ^1.18.0 - version: 1.18.0 + specifier: ^1.18.1 + version: 1.18.1 echarts: specifier: ^6.1.0 version: 6.1.0 @@ -28,29 +28,29 @@ importers: version: 3.15.0 pinia: specifier: ^3.0.4 - version: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) + version: 3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)) pinia-plugin-persistedstate: specifier: ^4.7.1 - version: 4.7.1(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))) + version: 4.7.1(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3))) vue: - specifier: ^3.5.38 - version: 3.5.38(typescript@6.0.3) + specifier: ^3.5.39 + version: 3.5.39(typescript@6.0.3) vue-router: specifier: ^5.1.0 - version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + version: 5.1.0(@vue/compiler-sfc@3.5.39)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.3)(vite@8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) devDependencies: '@tailwindcss/vite': specifier: ^4.3.1 - version: 4.3.1(vite@8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.3.1(vite@8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0)) '@types/node': - specifier: ^25.9.3 - version: 25.9.3 + specifier: ^26.0.1 + version: 26.0.1 '@vitejs/plugin-vue': specifier: ^6.0.7 - version: 6.0.7(vite@8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + version: 6.0.7(vite@8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) '@vue/tsconfig': specifier: ^0.9.1 - version: 0.9.1(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) + version: 0.9.1(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)) prettier: specifier: ^3.8.4 version: 3.8.4 @@ -61,32 +61,32 @@ importers: specifier: ^6.0.3 version: 6.0.3 vite: - specifier: ^8.0.16 - version: 8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0) + specifier: ^8.1.0 + version: 8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0) vue-tsc: specifier: ^3.3.5 version: 3.3.5(typescript@6.0.3) packages: - '@babel/generator@8.0.0-rc.6': - resolution: {integrity: sha512-6mIzgVK8DgEzvIapoQwhXTMnnkuE4STQmVv9H03i/tZ2ml8oev3TRvZJgTenK2Bsq0YWNtzOrFdTyNzCMFtjJQ==} + '@babel/generator@8.0.0': + resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-string-parser@7.29.7': resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@8.0.0-rc.6': - resolution: {integrity: sha512-BCkFy+zN6kXQed3YOT7aJl93NfDSzQc3pBfsvTVPs9gU9X3V0aefEF5kwBT0E+mDWH9QgKaZstYUQN9VdQZT4g==} + '@babel/helper-string-parser@8.0.0': + resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@8.0.0-rc.6': - resolution: {integrity: sha512-nVJ+1JcCgntv8d78rRo++o2wuODT0Irknx2BF8Np4Ft2CRgjLqIs4qzSZ8b66yGbBdMWGmZBO9WEZv1hhNiSpg==} + '@babel/helper-validator-identifier@8.0.2': + resolution: {integrity: sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==} engines: {node: ^22.18.0 || >=24.11.0} '@babel/parser@7.29.7': @@ -94,8 +94,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@8.0.0-rc.6': - resolution: {integrity: sha512-rOS8IpdO7mQELkTPlCsTgPejO0bFuZdEDCGQJouYbYf9e1FLTym7Fei2pEjq8q7MWbX0ravcd7QQYKs1TxOuog==} + '@babel/parser@8.0.0': + resolution: {integrity: sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==} engines: {node: ^22.18.0 || >=24.11.0} hasBin: true @@ -103,18 +103,18 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} - '@babel/types@8.0.0-rc.6': - resolution: {integrity: sha512-p7/ABylAYlexb31wtRdIfH9L9A0Z2T/9H6zAqzqndkY2PLkvNNc580wGhp/gGKN4Sp9sQvSkhc6Oga8/O+wTyw==} + '@babel/types@8.0.0': + resolution: {integrity: sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==} engines: {node: ^22.18.0 || >=24.11.0} - '@emnapi/core@1.10.0': - resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} - '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} - '@emnapi/wasi-threads@1.2.1': - resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} '@fontsource-variable/inter@5.2.8': resolution: {integrity: sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==} @@ -138,111 +138,111 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@lucide/vue@1.18.0': - resolution: {integrity: sha512-DmnUpDB85PlMZ+ofjZLcKq3JoJnaD1bk7SIj9xwUvqerfNqA6hCLa0/m3gIybH6rdrErABbqvTD8yYJdNqiZ3Q==} + '@lucide/vue@1.21.0': + resolution: {integrity: sha512-eoFn3tppjKAc12ZqdnRSMFdtwQ1ZMRQFb6SV1Eub6Y8kU28ccnqKeSFdnur9hMg8gIbosU2Y3WFJr/J/xS/IlQ==} peerDependencies: vue: '>=3.0.1' - '@napi-rs/wasm-runtime@1.1.5': - resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@oxc-project/types@0.133.0': - resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + '@oxc-project/types@0.137.0': + resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} - '@rolldown/binding-android-arm64@1.0.3': - resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} + '@rolldown/binding-android-arm64@1.1.3': + resolution: {integrity: sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.3': - resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} + '@rolldown/binding-darwin-arm64@1.1.3': + resolution: {integrity: sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.3': - resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} + '@rolldown/binding-darwin-x64@1.1.3': + resolution: {integrity: sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.3': - resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} + '@rolldown/binding-freebsd-x64@1.1.3': + resolution: {integrity: sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.3': - resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': + resolution: {integrity: sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.3': - resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} + '@rolldown/binding-linux-arm64-gnu@1.1.3': + resolution: {integrity: sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.3': - resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} + '@rolldown/binding-linux-arm64-musl@1.1.3': + resolution: {integrity: sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.3': - resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} + '@rolldown/binding-linux-ppc64-gnu@1.1.3': + resolution: {integrity: sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.3': - resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} + '@rolldown/binding-linux-s390x-gnu@1.1.3': + resolution: {integrity: sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.3': - resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} + '@rolldown/binding-linux-x64-gnu@1.1.3': + resolution: {integrity: sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.3': - resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} + '@rolldown/binding-linux-x64-musl@1.1.3': + resolution: {integrity: sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.3': - resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} + '@rolldown/binding-openharmony-arm64@1.1.3': + resolution: {integrity: sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.3': - resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} + '@rolldown/binding-wasm32-wasi@1.1.3': + resolution: {integrity: sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.3': - resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} + '@rolldown/binding-win32-arm64-msvc@1.1.3': + resolution: {integrity: sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.3': - resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} + '@rolldown/binding-win32-x64-msvc@1.1.3': + resolution: {integrity: sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -344,14 +344,14 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 - '@tybys/wasm-util@0.10.2': - resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} '@types/jsesc@2.5.1': resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} - '@types/node@25.9.3': - resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} + '@types/node@26.0.1': + resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==} '@vitejs/plugin-vue@6.0.7': resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} @@ -378,55 +378,55 @@ packages: vue: optional: true - '@vue/compiler-core@3.5.38': - resolution: {integrity: sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==} + '@vue/compiler-core@3.5.39': + resolution: {integrity: sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==} - '@vue/compiler-dom@3.5.38': - resolution: {integrity: sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==} + '@vue/compiler-dom@3.5.39': + resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==} - '@vue/compiler-sfc@3.5.38': - resolution: {integrity: sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==} + '@vue/compiler-sfc@3.5.39': + resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==} - '@vue/compiler-ssr@3.5.38': - resolution: {integrity: sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==} + '@vue/compiler-ssr@3.5.39': + resolution: {integrity: sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==} - '@vue/devtools-api@7.7.9': - resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} + '@vue/devtools-api@7.7.10': + resolution: {integrity: sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==} - '@vue/devtools-api@8.1.3': - resolution: {integrity: sha512-73NMCvxXh8Hyozc/jiwqTFWVcCMyi11U1zmrq4DoukQJnuo8JHt6FsNu4HdeUDa8SpIp5vb7Q22GWgIq0efsXg==} + '@vue/devtools-api@8.1.4': + resolution: {integrity: sha512-zphdXRe0VxWfUWH2KLcNV4xWUMxjxASyxCjFS/wRHVYfJCMBulq1ce3RHGkYRxghoVBqwCzOw1TWIIfvIWCY1w==} - '@vue/devtools-kit@7.7.9': - resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} + '@vue/devtools-kit@7.7.10': + resolution: {integrity: sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==} - '@vue/devtools-kit@8.1.3': - resolution: {integrity: sha512-cRn7GXiCQkMYU2Z3h3pM4YO/ndbx9FY1yLDAqIqPLcmIq4H6zAOJHein6tvZU3AfPwgrodqLiPBEF+YQaS8AxA==} + '@vue/devtools-kit@8.1.4': + resolution: {integrity: sha512-+GLBwY63hZ46sqTlgXgvd8IcZTpWe0PZzbJgs63ii/8uTYVwg1q9bdIR9xx8ReKcrdWS01RGCbC971jYPvjRCA==} - '@vue/devtools-shared@7.7.9': - resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} + '@vue/devtools-shared@7.7.10': + resolution: {integrity: sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==} - '@vue/devtools-shared@8.1.3': - resolution: {integrity: sha512-CM3uIPL+v+lrJUk33+pxspYo0MhuMWlCvf7zC9fybifvCPyM2jUbYRPwoYEJgYbwRqPikm5HozbUhp60MF2QuA==} + '@vue/devtools-shared@8.1.4': + resolution: {integrity: sha512-Earc/zrg0w0Md4KdrvgRR1vC5F17Zn+VzqTNI7PXYXz0fPJDVWENe7fv6NHi6Ja9Sq6Ce6SYUdc2FlXU0OMkjQ==} '@vue/language-core@3.3.5': resolution: {integrity: sha512-UkKu5nhX89fg4VhlG/FOeI10G3cj/7radKT/cy9BT4Q9qJmJlSTAc/dP63Xqs29aypN4f39xUV6PsLNk/dcD6g==} - '@vue/reactivity@3.5.38': - resolution: {integrity: sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==} + '@vue/reactivity@3.5.39': + resolution: {integrity: sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==} - '@vue/runtime-core@3.5.38': - resolution: {integrity: sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==} + '@vue/runtime-core@3.5.39': + resolution: {integrity: sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==} - '@vue/runtime-dom@3.5.38': - resolution: {integrity: sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==} + '@vue/runtime-dom@3.5.39': + resolution: {integrity: sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==} - '@vue/server-renderer@3.5.38': - resolution: {integrity: sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==} + '@vue/server-renderer@3.5.39': + resolution: {integrity: sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==} peerDependencies: - vue: 3.5.38 + vue: 3.5.39 - '@vue/shared@3.5.38': - resolution: {integrity: sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==} + '@vue/shared@3.5.39': + resolution: {integrity: sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==} '@vue/tsconfig@0.9.1': resolution: {integrity: sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==} @@ -462,8 +462,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.18.0: - resolution: {integrity: sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==} + axios@1.18.1: + resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==} birpc@2.9.0: resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} @@ -547,8 +547,8 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - exsolve@1.0.8: - resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + exsolve@1.1.0: + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -744,8 +744,8 @@ packages: muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -820,8 +820,8 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown@1.0.3: - resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} + rolldown@1.1.3: + resolution: {integrity: sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -865,24 +865,53 @@ packages: ufo@1.6.4: resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} - undici-types@7.24.6: - resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} unplugin-utils@0.3.1: resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==} engines: {node: '>=20.19.0'} - unplugin@3.0.0: - resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==} + unplugin@3.2.0: + resolution: {integrity: sha512-6nGlT7EHsS+tTcTdAkYFqXIUwDrMJyJvHFNYGSr4x2/2ySIcV4f5e1RAJUeDyfOJPR8TF0auE8l+82PLhKjqsA==} engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@farmfe/core': '*' + '@rspack/core': '*' + bun-types-no-globals: '*' + esbuild: '*' + rolldown: '*' + rollup: '*' + unloader: '*' + vite: '*' + webpack: '*' + peerDependenciesMeta: + '@farmfe/core': + optional: true + '@rspack/core': + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true - vite@8.0.16: - resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} + vite@8.1.0: + resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.1.18 + '@vitejs/devtools': ^0.3.0 esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 @@ -946,8 +975,8 @@ packages: peerDependencies: typescript: '>=5.0.0' - vue@3.5.38: - resolution: {integrity: sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==} + vue@3.5.39: + resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -967,10 +996,10 @@ packages: snapshots: - '@babel/generator@8.0.0-rc.6': + '@babel/generator@8.0.0': dependencies: - '@babel/parser': 8.0.0-rc.6 - '@babel/types': 8.0.0-rc.6 + '@babel/parser': 8.0.0 + '@babel/types': 8.0.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 '@types/jsesc': 2.5.1 @@ -978,42 +1007,42 @@ snapshots: '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-string-parser@8.0.0-rc.6': {} + '@babel/helper-string-parser@8.0.0': {} '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-identifier@8.0.0-rc.6': {} + '@babel/helper-validator-identifier@8.0.2': {} '@babel/parser@7.29.7': dependencies: '@babel/types': 7.29.7 - '@babel/parser@8.0.0-rc.6': + '@babel/parser@8.0.0': dependencies: - '@babel/types': 8.0.0-rc.6 + '@babel/types': 8.0.0 '@babel/types@7.29.7': dependencies: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/types@8.0.0-rc.6': + '@babel/types@8.0.0': dependencies: - '@babel/helper-string-parser': 8.0.0-rc.6 - '@babel/helper-validator-identifier': 8.0.0-rc.6 + '@babel/helper-string-parser': 8.0.0 + '@babel/helper-validator-identifier': 8.0.2 - '@emnapi/core@1.10.0': + '@emnapi/core@1.11.1': dependencies: - '@emnapi/wasi-threads': 1.2.1 + '@emnapi/wasi-threads': 1.2.2 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.10.0': + '@emnapi/runtime@1.11.1': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.2.1': + '@emnapi/wasi-threads@1.2.2': dependencies: tslib: 2.8.1 optional: true @@ -1041,66 +1070,66 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@lucide/vue@1.18.0(vue@3.5.38(typescript@6.0.3))': + '@lucide/vue@1.21.0(vue@3.5.39(typescript@6.0.3))': dependencies: - vue: 3.5.38(typescript@6.0.3) + vue: 3.5.39(typescript@6.0.3) - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.2 + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 optional: true - '@oxc-project/types@0.133.0': {} + '@oxc-project/types@0.137.0': {} - '@rolldown/binding-android-arm64@1.0.3': + '@rolldown/binding-android-arm64@1.1.3': optional: true - '@rolldown/binding-darwin-arm64@1.0.3': + '@rolldown/binding-darwin-arm64@1.1.3': optional: true - '@rolldown/binding-darwin-x64@1.0.3': + '@rolldown/binding-darwin-x64@1.1.3': optional: true - '@rolldown/binding-freebsd-x64@1.0.3': + '@rolldown/binding-freebsd-x64@1.1.3': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.3': + '@rolldown/binding-linux-arm64-gnu@1.1.3': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.3': + '@rolldown/binding-linux-arm64-musl@1.1.3': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.3': + '@rolldown/binding-linux-ppc64-gnu@1.1.3': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.3': + '@rolldown/binding-linux-s390x-gnu@1.1.3': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.3': + '@rolldown/binding-linux-x64-gnu@1.1.3': optional: true - '@rolldown/binding-linux-x64-musl@1.0.3': + '@rolldown/binding-linux-x64-musl@1.1.3': optional: true - '@rolldown/binding-openharmony-arm64@1.0.3': + '@rolldown/binding-openharmony-arm64@1.1.3': optional: true - '@rolldown/binding-wasm32-wasi@1.0.3': + '@rolldown/binding-wasm32-wasi@1.1.3': dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.3': + '@rolldown/binding-win32-arm64-msvc@1.1.3': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.3': + '@rolldown/binding-win32-x64-msvc@1.1.3': optional: true '@rolldown/pluginutils@1.0.1': {} @@ -1166,29 +1195,29 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 - '@tailwindcss/vite@4.3.1(vite@8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.1(vite@8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.1 '@tailwindcss/oxide': 4.3.1 tailwindcss: 4.3.1 - vite: 8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0) - '@tybys/wasm-util@0.10.2': + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 optional: true '@types/jsesc@2.5.1': {} - '@types/node@25.9.3': + '@types/node@26.0.1': dependencies: - undici-types: 7.24.6 + undici-types: 8.3.0 - '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': + '@vitejs/plugin-vue@6.0.7(vite@8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0) - vue: 3.5.38(typescript@6.0.3) + vite: 8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0) + vue: 3.5.39(typescript@6.0.3) '@volar/language-core@2.4.28': dependencies: @@ -1202,57 +1231,57 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue-macros/common@3.1.2(vue@3.5.38(typescript@6.0.3))': + '@vue-macros/common@3.1.2(vue@3.5.39(typescript@6.0.3))': dependencies: - '@vue/compiler-sfc': 3.5.38 + '@vue/compiler-sfc': 3.5.39 ast-kit: 2.2.0 local-pkg: 1.2.1 magic-string-ast: 1.0.3 unplugin-utils: 0.3.1 optionalDependencies: - vue: 3.5.38(typescript@6.0.3) + vue: 3.5.39(typescript@6.0.3) - '@vue/compiler-core@3.5.38': + '@vue/compiler-core@3.5.39': dependencies: '@babel/parser': 7.29.7 - '@vue/shared': 3.5.38 + '@vue/shared': 3.5.39 entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.38': + '@vue/compiler-dom@3.5.39': dependencies: - '@vue/compiler-core': 3.5.38 - '@vue/shared': 3.5.38 + '@vue/compiler-core': 3.5.39 + '@vue/shared': 3.5.39 - '@vue/compiler-sfc@3.5.38': + '@vue/compiler-sfc@3.5.39': dependencies: '@babel/parser': 7.29.7 - '@vue/compiler-core': 3.5.38 - '@vue/compiler-dom': 3.5.38 - '@vue/compiler-ssr': 3.5.38 - '@vue/shared': 3.5.38 + '@vue/compiler-core': 3.5.39 + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.15 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.38': + '@vue/compiler-ssr@3.5.39': dependencies: - '@vue/compiler-dom': 3.5.38 - '@vue/shared': 3.5.38 + '@vue/compiler-dom': 3.5.39 + '@vue/shared': 3.5.39 - '@vue/devtools-api@7.7.9': + '@vue/devtools-api@7.7.10': dependencies: - '@vue/devtools-kit': 7.7.9 + '@vue/devtools-kit': 7.7.10 - '@vue/devtools-api@8.1.3': + '@vue/devtools-api@8.1.4': dependencies: - '@vue/devtools-kit': 8.1.3 + '@vue/devtools-kit': 8.1.4 - '@vue/devtools-kit@7.7.9': + '@vue/devtools-kit@7.7.10': dependencies: - '@vue/devtools-shared': 7.7.9 + '@vue/devtools-shared': 7.7.10 birpc: 2.9.0 hookable: 5.5.3 mitt: 3.0.1 @@ -1260,57 +1289,57 @@ snapshots: speakingurl: 14.0.1 superjson: 2.2.6 - '@vue/devtools-kit@8.1.3': + '@vue/devtools-kit@8.1.4': dependencies: - '@vue/devtools-shared': 8.1.3 + '@vue/devtools-shared': 8.1.4 birpc: 2.9.0 hookable: 5.5.3 perfect-debounce: 2.1.0 - '@vue/devtools-shared@7.7.9': + '@vue/devtools-shared@7.7.10': dependencies: rfdc: 1.4.1 - '@vue/devtools-shared@8.1.3': {} + '@vue/devtools-shared@8.1.4': {} '@vue/language-core@3.3.5': dependencies: '@volar/language-core': 2.4.28 - '@vue/compiler-dom': 3.5.38 - '@vue/shared': 3.5.38 + '@vue/compiler-dom': 3.5.39 + '@vue/shared': 3.5.39 alien-signals: 3.2.1 muggle-string: 0.4.1 path-browserify: 1.0.1 picomatch: 4.0.4 - '@vue/reactivity@3.5.38': + '@vue/reactivity@3.5.39': dependencies: - '@vue/shared': 3.5.38 + '@vue/shared': 3.5.39 - '@vue/runtime-core@3.5.38': + '@vue/runtime-core@3.5.39': dependencies: - '@vue/reactivity': 3.5.38 - '@vue/shared': 3.5.38 + '@vue/reactivity': 3.5.39 + '@vue/shared': 3.5.39 - '@vue/runtime-dom@3.5.38': + '@vue/runtime-dom@3.5.39': dependencies: - '@vue/reactivity': 3.5.38 - '@vue/runtime-core': 3.5.38 - '@vue/shared': 3.5.38 + '@vue/reactivity': 3.5.39 + '@vue/runtime-core': 3.5.39 + '@vue/shared': 3.5.39 csstype: 3.2.3 - '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@6.0.3))': + '@vue/server-renderer@3.5.39(vue@3.5.39(typescript@6.0.3))': dependencies: - '@vue/compiler-ssr': 3.5.38 - '@vue/shared': 3.5.38 - vue: 3.5.38(typescript@6.0.3) + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 + vue: 3.5.39(typescript@6.0.3) - '@vue/shared@3.5.38': {} + '@vue/shared@3.5.39': {} - '@vue/tsconfig@0.9.1(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))': + '@vue/tsconfig@0.9.1(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3))': optionalDependencies: typescript: 6.0.3 - vue: 3.5.38(typescript@6.0.3) + vue: 3.5.39(typescript@6.0.3) acorn@8.17.0: {} @@ -1335,7 +1364,7 @@ snapshots: asynckit@0.4.0: {} - axios@1.18.0: + axios@1.18.1: dependencies: follow-redirects: 1.16.0 form-data: 4.0.6 @@ -1415,7 +1444,7 @@ snapshots: estree-walker@2.0.2: {} - exsolve@1.0.8: {} + exsolve@1.1.0: {} fdir@6.5.0(picomatch@4.0.4): optionalDependencies: @@ -1571,7 +1600,7 @@ snapshots: muggle-string@0.4.1: {} - nanoid@3.3.12: {} + nanoid@3.3.15: {} path-browserify@1.0.1: {} @@ -1585,16 +1614,16 @@ snapshots: picomatch@4.0.4: {} - pinia-plugin-persistedstate@4.7.1(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))): + pinia-plugin-persistedstate@4.7.1(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3))): dependencies: defu: 6.1.7 optionalDependencies: - pinia: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) + pinia: 3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)) - pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)): + pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)): dependencies: - '@vue/devtools-api': 7.7.9 - vue: 3.5.38(typescript@6.0.3) + '@vue/devtools-api': 7.7.10 + vue: 3.5.39(typescript@6.0.3) optionalDependencies: typescript: 6.0.3 @@ -1607,12 +1636,12 @@ snapshots: pkg-types@2.3.1: dependencies: confbox: 0.2.4 - exsolve: 1.0.8 + exsolve: 1.1.0 pathe: 2.0.3 postcss@8.5.15: dependencies: - nanoid: 3.3.12 + nanoid: 3.3.15 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -1626,26 +1655,26 @@ snapshots: rfdc@1.4.1: {} - rolldown@1.0.3: + rolldown@1.1.3: dependencies: - '@oxc-project/types': 0.133.0 + '@oxc-project/types': 0.137.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.3 - '@rolldown/binding-darwin-arm64': 1.0.3 - '@rolldown/binding-darwin-x64': 1.0.3 - '@rolldown/binding-freebsd-x64': 1.0.3 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 - '@rolldown/binding-linux-arm64-gnu': 1.0.3 - '@rolldown/binding-linux-arm64-musl': 1.0.3 - '@rolldown/binding-linux-ppc64-gnu': 1.0.3 - '@rolldown/binding-linux-s390x-gnu': 1.0.3 - '@rolldown/binding-linux-x64-gnu': 1.0.3 - '@rolldown/binding-linux-x64-musl': 1.0.3 - '@rolldown/binding-openharmony-arm64': 1.0.3 - '@rolldown/binding-wasm32-wasi': 1.0.3 - '@rolldown/binding-win32-arm64-msvc': 1.0.3 - '@rolldown/binding-win32-x64-msvc': 1.0.3 + '@rolldown/binding-android-arm64': 1.1.3 + '@rolldown/binding-darwin-arm64': 1.1.3 + '@rolldown/binding-darwin-x64': 1.1.3 + '@rolldown/binding-freebsd-x64': 1.1.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.3 + '@rolldown/binding-linux-arm64-gnu': 1.1.3 + '@rolldown/binding-linux-arm64-musl': 1.1.3 + '@rolldown/binding-linux-ppc64-gnu': 1.1.3 + '@rolldown/binding-linux-s390x-gnu': 1.1.3 + '@rolldown/binding-linux-x64-gnu': 1.1.3 + '@rolldown/binding-linux-x64-musl': 1.1.3 + '@rolldown/binding-openharmony-arm64': 1.1.3 + '@rolldown/binding-wasm32-wasi': 1.1.3 + '@rolldown/binding-win32-arm64-msvc': 1.1.3 + '@rolldown/binding-win32-x64-msvc': 1.1.3 scule@1.3.0: {} @@ -1675,39 +1704,42 @@ snapshots: ufo@1.6.4: {} - undici-types@7.24.6: {} + undici-types@8.3.0: {} unplugin-utils@0.3.1: dependencies: pathe: 2.0.3 picomatch: 4.0.4 - unplugin@3.0.0: + unplugin@3.2.0(rolldown@1.1.3)(vite@8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0)): dependencies: '@jridgewell/remapping': 2.3.5 picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 + optionalDependencies: + rolldown: 1.1.3 + vite: 8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0) - vite@8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0): + vite@8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.15 - rolldown: 1.0.3 + rolldown: 1.1.3 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 fsevents: 2.3.3 jiti: 2.7.0 yaml: 2.9.0 vscode-uri@3.1.0: {} - vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)): + vue-router@5.1.0(@vue/compiler-sfc@3.5.39)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.3)(vite@8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)): dependencies: - '@babel/generator': 8.0.0-rc.6 - '@vue-macros/common': 3.1.2(vue@3.5.38(typescript@6.0.3)) - '@vue/devtools-api': 8.1.3 + '@babel/generator': 8.0.0 + '@vue-macros/common': 3.1.2(vue@3.5.39(typescript@6.0.3)) + '@vue/devtools-api': 8.1.4 ast-walker-scope: 0.9.0 chokidar: 5.0.0 json5: 2.2.3 @@ -1719,14 +1751,23 @@ snapshots: picomatch: 4.0.4 scule: 1.3.0 tinyglobby: 0.2.17 - unplugin: 3.0.0 + unplugin: 3.2.0(rolldown@1.1.3)(vite@8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0)) unplugin-utils: 0.3.1 - vue: 3.5.38(typescript@6.0.3) + vue: 3.5.39(typescript@6.0.3) yaml: 2.9.0 optionalDependencies: - '@vue/compiler-sfc': 3.5.38 - pinia: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) - vite: 8.0.16(@types/node@25.9.3)(jiti@2.7.0)(yaml@2.9.0) + '@vue/compiler-sfc': 3.5.39 + pinia: 3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)) + vite: 8.1.0(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - unloader + - webpack vue-tsc@3.3.5(typescript@6.0.3): dependencies: @@ -1734,13 +1775,13 @@ snapshots: '@vue/language-core': 3.3.5 typescript: 6.0.3 - vue@3.5.38(typescript@6.0.3): + vue@3.5.39(typescript@6.0.3): dependencies: - '@vue/compiler-dom': 3.5.38 - '@vue/compiler-sfc': 3.5.38 - '@vue/runtime-dom': 3.5.38 - '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@6.0.3)) - '@vue/shared': 3.5.38 + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-sfc': 3.5.39 + '@vue/runtime-dom': 3.5.39 + '@vue/server-renderer': 3.5.39(vue@3.5.39(typescript@6.0.3)) + '@vue/shared': 3.5.39 optionalDependencies: typescript: 6.0.3 diff --git a/web/src/api/index.ts b/web/src/api/index.ts index 74ba60925..5b5c82b66 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -20,5 +20,4 @@ export * from './modules/dashboard' export * from './modules/settings' // 请求工具 -export * from './request' -export type * from './types' +export { default as request } from './request' diff --git a/web/src/api/modules/accounts.ts b/web/src/api/modules/accounts.ts index 164b8b05a..1e55c38da 100644 --- a/web/src/api/modules/accounts.ts +++ b/web/src/api/modules/accounts.ts @@ -1,206 +1,57 @@ -import { requestJson, requestPageJson } from '../request' -import type { PaginatedResult } from '../types' +import request from '../request' -export interface Account { - id: string - email?: string - accountId?: string - userId?: string - planType?: string - status: 'active' | 'expired' | 'disabled' | 'banned' | 'quota_exhausted' | 'refreshing' - label?: string - accessTokenExpiresAt?: string - accessTokenExpiresAtDisplay?: string - addedAt: string - addedAtDisplay: string - updatedAt: string - updatedAtDisplay: string - quota: AccountQuota - usage: AccountUsage -} - -export interface AccountQuota { - refreshedAtDisplay: string - windows: AccountQuotaWindow[] -} - -export interface AccountQuotaWindow { - key: string - group: 'monthly' | 'shortTerm' | 'other' - windowSeconds?: number | null - labelDisplay: string - usedPercent?: number | null - usedPercentDisplay: string - resetAtDisplay: string - windowUsedDisplay: string -} - -export interface AccountUsage { - requestCount: number - requestCountDisplay: string - emptyResponseCount: number - inputTokens: number - inputTokensDisplay: string - outputTokens: number - outputTokensDisplay: string - cachedTokens: number - cachedTokensDisplay: string - reasoningTokens: number - totalTokens: number - totalTokensDisplay: string - imageInputTokens: number - imageOutputTokens: number - imageTokensDisplay: string - imageRequestCount: number - imageRequestFailedCount: number - createdTokens: number - createdTokensDisplay: string - readTokens: number - readTokensDisplay: string - lastUsedAt?: string - lastUsedAtDisplay: string - models: AccountModelUsage[] -} - -export interface AccountModelUsage { - model: string - requestCount: number - requestCountDisplay: string - successRate: number - successRateDisplay: string - inputTokens: number - inputTokensDisplay: string - outputTokens: number - outputTokensDisplay: string - cachedTokens: number - cachedTokensDisplay: string - totalTokens: number - totalTokensDisplay: string - totalCostUsd: number - totalCostUsdDisplay: string - lastUsedAt?: string - lastUsedAtDisplay: string -} - -export interface AccountsQuery { - page: number - pageSize: number - search?: string -} - -export function getAccounts(): Promise -export function getAccounts(query: AccountsQuery): Promise> -export async function getAccounts(query?: AccountsQuery) { - if (query) { - return requestPageJson('/api/admin/accounts', { - method: 'GET', - params: compactQuery(query), - }) - } - - const result = await requestPageJson('/api/admin/accounts') - return result.items -} - -function compactQuery(query: AccountsQuery) { - return { - page: query.page, - pageSize: query.pageSize, - search: query.search?.trim() || undefined, - } -} - -export interface CreateAccountPayload { - refreshToken: string - label?: string -} - -export function createAccount(payload: CreateAccountPayload) { - return requestJson('/api/admin/accounts', { - method: 'POST', - data: payload, +export function getAccounts(params?: any) { + return request({ + url: '/api/admin/accounts', + method: 'GET', + params, }) } -export function deleteAccount(accountId: string) { - return requestJson<{ deleted: number }>('/api/admin/accounts/delete', { +export function createAccount(data: any) { + return request({ + url: '/api/admin/accounts', method: 'POST', - data: { ids: [accountId] }, + data, }) } -export function refreshAccount(accountId: string) { - return requestJson('/api/admin/accounts/refresh', { +export function deleteAccounts(data: any) { + return request({ + url: '/api/admin/accounts/delete', method: 'POST', - data: { id: accountId }, + data, }) } -export interface UpdateAccountPayload { - label?: string | null - email?: string | null - accountId?: string | null - userId?: string | null - planType?: string | null - status?: Account['status'] -} - -export function updateAccount(accountId: string, payload: UpdateAccountPayload) { - return requestJson('/api/admin/accounts/update', { +export function refreshAccount(data: any) { + return request({ + url: '/api/admin/accounts/refresh', method: 'POST', - data: { id: accountId, ...payload }, + data, }) } -export function updateAccountLabel(accountId: string, label: string | null) { - return updateAccount(accountId, { label }) -} - -export function updateAccountStatus(accountId: string, status: Account['status']) { - return updateAccount(accountId, { status }) -} - -export function batchDeleteAccounts(accountIds: string[]) { - return requestJson<{ deleted: number }>('/api/admin/accounts/delete', { +export function updateAccount(data: any) { + return request({ + url: '/api/admin/accounts/update', method: 'POST', - data: { ids: accountIds }, + data, }) } -export interface QuotaInfo { - requestsCount: number - requestsLimit: number - resetAt: string +export function getAccountQuota(params: any) { + return request({ + url: '/api/admin/accounts/quota', + method: 'GET', + params, + }) } -export function getAccountQuota(accountId: string) { - const params = new URLSearchParams({ id: accountId }) - return requestJson(`/api/admin/accounts/quota?${params}`) -} - -export interface AccountHealthCheckSummary { - total: number - alive: number - dead: number - skipped: number -} - -export interface AccountHealthCheckResult { - id: string - email?: string | null - result: 'alive' | 'dead' | string - error?: string - durationMs?: number -} - -export interface AccountHealthCheckData { - summary: AccountHealthCheckSummary - results: AccountHealthCheckResult[] -} - -export function testAccountConnection(accountId: string) { - return requestJson('/api/admin/accounts/health-check', { +export function testAccountConnection(data: any) { + return request({ + url: '/api/admin/accounts/health-check', method: 'POST', - data: { ids: [accountId] }, + data, }) } diff --git a/web/src/api/modules/api-keys.ts b/web/src/api/modules/api-keys.ts index f24899388..5197f0997 100644 --- a/web/src/api/modules/api-keys.ts +++ b/web/src/api/modules/api-keys.ts @@ -1,66 +1,34 @@ -import { requestJson, requestPageJson } from '../request' +import request from '../request' // ==================== API Keys 管理 ==================== -export interface ClientApiKey { - id: string - name: string - label?: string - prefix: string - enabled: boolean - createdAt: string - createdAtDisplay: string - lastUsedAt?: string - lastUsedAtDisplay: string -} - -export interface CreateApiKeyPayload { - name: string - label?: string -} - -export interface CreateApiKeyResponse { - key: string - id: string - prefix: string -} - -export async function getApiKeys() { - const result = await requestPageJson('/api/admin/keys') - return result.items -} - -export function createApiKey(payload: CreateApiKeyPayload) { - return requestJson('/api/admin/keys', { - method: 'POST', - data: payload, +export function getApiKeys() { + return request({ + url: '/api/admin/keys', + method: 'GET', }) } -export function deleteApiKey(keyId: string) { - return requestJson<{ deleted: number }>('/api/admin/keys/delete', { +export function createApiKey(data: any) { + return request({ + url: '/api/admin/keys', method: 'POST', - data: { ids: [keyId] }, + data, }) } -export function updateApiKeyLabel(keyId: string, label: string | null) { - return requestJson('/api/admin/keys/update', { +export function deleteApiKeys(data: any) { + return request({ + url: '/api/admin/keys/delete', method: 'POST', - data: { id: keyId, label }, + data, }) } -export function updateApiKeyStatus(keyId: string, enabled: boolean) { - return requestJson('/api/admin/keys/update', { +export function updateApiKey(data: any) { + return request({ + url: '/api/admin/keys/update', method: 'POST', - data: { id: keyId, status: enabled ? 'active' : 'disabled' }, - }) -} - -export function batchDeleteApiKeys(keyIds: string[]) { - return requestJson<{ deleted: number }>('/api/admin/keys/delete', { - method: 'POST', - data: { ids: keyIds }, + data, }) } diff --git a/web/src/api/modules/auth.ts b/web/src/api/modules/auth.ts index b70951433..c6599d1d1 100644 --- a/web/src/api/modules/auth.ts +++ b/web/src/api/modules/auth.ts @@ -1,31 +1,23 @@ -import { requestJson } from '../request' +import request from '../request' -// ==================== 认证相关 ==================== - -export interface AuthStatus { - authenticated: boolean -} - -export interface LoginRequest { - username?: string - password: string -} - -export interface LoginResponse { - expiresAt: string -} - -export function login(payload: LoginRequest) { - return requestJson('/api/admin/login', { +export function login(data: any) { + return request({ + url: '/api/admin/login', method: 'POST', - data: payload, + data, }) } export function getAuthStatus() { - return requestJson('/api/admin/auth/status') + return request({ + url: '/api/admin/auth/status', + method: 'GET', + }) } export function logout() { - return requestJson('/api/admin/logout', { method: 'POST' }) + return request({ + url: '/api/admin/logout', + method: 'POST', + }) } diff --git a/web/src/api/modules/dashboard.ts b/web/src/api/modules/dashboard.ts index 43d8b7f41..f1a32ca56 100644 --- a/web/src/api/modules/dashboard.ts +++ b/web/src/api/modules/dashboard.ts @@ -1,144 +1,16 @@ -import { requestJson } from '../request' - -export type DashboardTrendKind = 'usage' | 'latency' | 'errors' - -export interface DashboardSummary { - cards: DashboardCards - trend: DashboardTrend - healthTimeline: DashboardHealthTimeline - accountUsage: DashboardAccountUsage[] - serviceStatuses: DashboardServiceStatus[] - eventLogs: DashboardEventLog[] - poolSummary: DashboardAccountPoolSummary - capacityInfo: DashboardAccountCapacityInfo - rotationStrategy?: string -} - -export interface DashboardHealthTimeline { - title: string - description: string - rangeDisplay: string - reliabilityDisplay: string - oldestLabel: string - newestLabel: string - points: string -} - -export interface DashboardCards { - accounts: DashboardAccountsCard - traffic: DashboardTrafficCard - tokens: DashboardTokenCard - cache: DashboardCacheCard -} - -export interface DashboardAccountsCard { - total: number - enabled: number - abnormal: number -} - -export interface DashboardTrafficCard { - todayRequests: number - yesterdayRequests: number - totalRequests: number - rpm: number - tpm: number -} - -export interface DashboardTokenCard { - todayTokens: number - yesterdayTokens: number - totalTokens: number - todayCostUsd?: number | null - totalCostUsd?: number | null -} - -export interface DashboardCacheCard { - todayHitRate?: number | null - yesterdayHitRate?: number | null - totalHitRate?: number | null - totalCachedTokens: number - firstTokenLatencyMs?: number | null - completionLatencyMs?: number | null -} - -export interface DashboardTrend { - kind: DashboardTrendKind - points: DashboardTrendPoint[] - summary: DashboardTrendSummary[] -} - -export interface DashboardTrendPoint { - time: string - requests: number - inputTokens: number - outputTokens: number - cachedTokens: number - tokens: number - errors: number - latency: number - maxLatency: number - minLatency: number - successRate: number -} - -export interface DashboardTrendSummary { - label: string - value: number - ratio?: number | null -} - -export interface DashboardAccountUsage { - name: string - email: string - plan: string - requests: number - tokens: number - quotaUsedPercent?: number | null - lastUsed: string - status: 'active' | 'expired' | 'disabled' | 'banned' | 'quota_exhausted' | 'refreshing' -} - -export interface DashboardEventLog { - id: string - time: string - level: 'debug' | 'info' | 'warn' | 'error' - requestId?: string - route?: string - model?: string - statusCode?: number - latencyMs?: number -} - -export interface DashboardServiceStatus { - label: string - value: string - detail: string - tone: 'normal' | 'info' | 'success' | 'warning' | 'danger' -} - -export interface DashboardAccountPoolSummary { - total: number - active: number - expired: number - quotaExhausted: number - refreshing: number - disabled: number - banned: number -} - -export interface DashboardAccountCapacityInfo { - maxConcurrentPerAccount: number - totalSlots: number - usedSlots: number - availableSlots: number -} +import request from '../request' export function getDashboardSummary() { - return requestJson('/api/admin/dashboard/summary') + return request({ + url: '/api/admin/dashboard/summary', + method: 'GET', + }) } -export function getDashboardTrend(kind: DashboardTrendKind) { - const params = new URLSearchParams({ kind }) - return requestJson(`/api/admin/dashboard/trend?${params}`) +export function getDashboardTrend(params: any) { + return request({ + url: '/api/admin/dashboard/trend', + method: 'GET', + params, + }) } diff --git a/web/src/api/modules/logs.ts b/web/src/api/modules/logs.ts index 3063b0675..eb4ebd0ef 100644 --- a/web/src/api/modules/logs.ts +++ b/web/src/api/modules/logs.ts @@ -1,79 +1,24 @@ -import { requestJson, requestPageJson } from '../request' -import type { PaginatedResult } from '../types' +import request from '../request' -export interface EventLog { - id: string - requestId?: string - kind: string - level: 'debug' | 'info' | 'warn' | 'error' - accountId?: string - route?: string - model?: string - statusCode?: number - transport?: string - attemptIndex?: number - upstreamStatusCode?: number - failureClass?: string - responseId?: string - upstreamRequestId?: string - latencyMs?: number - message: string - metadata?: any - createdAt: string - createdAtDisplay: string +export function getLogs(params?: any) { + return request({ + url: '/api/admin/logs', + method: 'GET', + params, + }) } -export interface LogsQuery { - page?: number - pageSize?: number - level?: EventLog['level'] - kind?: string - accountId?: string - requestId?: string - route?: string - model?: string - statusCode?: number - transport?: string - search?: string -} - -export type LogsPageQuery = LogsQuery & { - page: number - pageSize: number -} - -export function getLogs(query: LogsPageQuery): Promise> -export function getLogs(query?: LogsQuery): Promise -export async function getLogs(query: LogsQuery = {}) { - const params = new URLSearchParams() - if (query.page) params.set('page', String(query.page)) - if (query.pageSize) params.set('pageSize', String(query.pageSize)) - if (query.level) params.set('level', query.level) - if (query.kind) params.set('kind', query.kind) - if (query.accountId) params.set('accountId', query.accountId) - if (query.requestId) params.set('requestId', query.requestId) - if (query.route) params.set('route', query.route) - if (query.model) params.set('model', query.model) - if (query.statusCode) params.set('statusCode', String(query.statusCode)) - if (query.transport) params.set('transport', query.transport) - if (query.search) params.set('search', query.search) - - const url = `/api/admin/logs${params.toString() ? `?${params}` : ''}` - if (query.page || query.pageSize) { - return requestPageJson(url) - } - - const result = await requestPageJson(url) - return result.items -} - -export function getLogDetail(logId: string) { - const params = new URLSearchParams({ id: logId }) - return requestJson(`/api/admin/logs/detail?${params}`) +export function getLogDetail(params: any) { + return request({ + url: '/api/admin/logs/detail', + method: 'GET', + params, + }) } export function clearLogs() { - return requestJson<{ cleared: number }>('/api/admin/logs/delete', { + return request({ + url: '/api/admin/logs/delete', method: 'POST', }) } diff --git a/web/src/api/modules/settings.ts b/web/src/api/modules/settings.ts index 3a8bc5f63..8c9d3ace8 100644 --- a/web/src/api/modules/settings.ts +++ b/web/src/api/modules/settings.ts @@ -1,41 +1,16 @@ -import { requestJson } from '../request' - -// ==================== 设置管理 ==================== - -export interface QuotaWarningThresholds { - primary: number[] - secondary: number[] -} - -export interface Settings { - defaultModel: string - defaultReasoningEffort?: string | null - serviceTier?: string | null - modelAliases?: Record - refreshEnabled: boolean - refreshMarginSeconds?: number - refreshConcurrency?: number - maxConcurrentPerAccount: number - requestIntervalMs: number - rotationStrategy: string - tierPriority?: string[] - quotaRefreshIntervalMinutes?: number - quotaWarningThresholds: QuotaWarningThresholds - quotaSkipExhausted: boolean - logsEnabled: boolean - logsCapacity: number - logsCaptureBody?: boolean - usageHistoryRetentionDays?: number | null - [key: string]: unknown -} +import request from '../request' export function getSettings() { - return requestJson('/api/admin/settings') + return request({ + url: '/api/admin/settings', + method: 'GET', + }) } -export function updateSettings(settings: Partial) { - return requestJson('/api/admin/settings', { +export function updateSettings(data: any) { + return request({ + url: '/api/admin/settings', method: 'POST', - data: settings, + data, }) } diff --git a/web/src/api/modules/usage.ts b/web/src/api/modules/usage.ts index 76cbcc47c..366be85a5 100644 --- a/web/src/api/modules/usage.ts +++ b/web/src/api/modules/usage.ts @@ -1,71 +1,16 @@ -import { requestJson, requestPageJson } from '../request' -import type { PaginatedResult } from '../types' - -// ==================== 使用统计 ==================== - -export interface UsageSummary { - accountCount: number - requestCount: number - emptyResponseCount: number - inputTokens: number - outputTokens: number - cachedTokens: number - reasoningTokens: number - totalTokens: number - imageInputTokens: number - imageOutputTokens: number - imageRequestCount: number - imageRequestFailedCount: number -} +import request from '../request' export function getUsageSummary() { - return requestJson('/api/admin/usage/summary') + return request({ + url: '/api/admin/usage/summary', + method: 'GET', + }) } -export interface AccountUsageStats { - accountId: string - email?: string - label?: string - planType?: string - requestCount: number - emptyResponseCount: number - inputTokens: number - outputTokens: number - cachedTokens: number - reasoningTokens: number - totalTokens: number - imageInputTokens: number - imageOutputTokens: number - imageRequestCount: number - imageRequestFailedCount: number - lastUsedAt?: string - lastUsedAtDisplay: string -} - -export interface UsageStatsQuery { - page?: number - pageSize?: number -} - -export type UsageStatsPageQuery = UsageStatsQuery & { - page: number - pageSize: number -} - -export function getUsageStats( - query: UsageStatsPageQuery, -): Promise> -export function getUsageStats(query?: UsageStatsQuery): Promise -export async function getUsageStats(query: UsageStatsQuery = {}) { - const params = new URLSearchParams() - if (query.page) params.set('page', String(query.page)) - if (query.pageSize) params.set('pageSize', String(query.pageSize)) - const url = `/api/admin/usage${params.toString() ? `?${params}` : ''}` - - if (query.page || query.pageSize) { - return requestPageJson(url) - } - - const result = await requestPageJson(url) - return result.items +export function getUsageStats(params?: any) { + return request({ + url: '/api/admin/usage', + method: 'GET', + params, + }) } diff --git a/web/src/api/request.ts b/web/src/api/request.ts index 37e07ae0f..d37751b14 100644 --- a/web/src/api/request.ts +++ b/web/src/api/request.ts @@ -6,9 +6,7 @@ import axios, { type InternalAxiosRequestConfig, } from 'axios' -import type { ApiEnvelope, ApiPageEnvelope, PaginatedResult } from './types' - -export class ApiError extends Error { +class ApiError extends Error { constructor( message: string, public readonly status: number, @@ -20,19 +18,15 @@ export class ApiError extends Error { } } -export interface RequestConfig extends AxiosRequestConfig { +interface RequestConfig extends AxiosRequestConfig { skipErrorHandler?: boolean skipAuth?: boolean } const http: AxiosInstance = axios.create({ - baseURL: import.meta.env.VITE_API_BASE_URL || '/', + baseURL: import.meta.env.DEV ? '/dev' : '', timeout: 30000, withCredentials: true, - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, }) http.interceptors.request.use( @@ -56,7 +50,7 @@ http.interceptors.response.use( (response: AxiosResponse) => { return response }, - (error: AxiosError>) => { + (error: AxiosError) => { const { response, config } = error if ((config as RequestConfig)?.skipErrorHandler) { @@ -89,7 +83,7 @@ http.interceptors.response.use( }, ) -function isApiEnvelope(value: unknown): value is ApiEnvelope { +function isApiEnvelope(value: any): value is { data: any } { return ( typeof value === 'object' && value !== null && @@ -99,73 +93,14 @@ function isApiEnvelope(value: unknown): value is ApiEnvelope { ) } -function isApiPageEnvelope(value: unknown): value is ApiPageEnvelope { - return ( - isApiEnvelope(value) && - typeof value.data === 'object' && - value.data !== null && - 'items' in value.data && - 'page' in value.data - ) -} - -export async function requestJson( - url: string, - config?: RequestConfig, -): Promise { - const response = await http.request | T, AxiosResponse | T>, D>({ - url, +export default async function request(config: RequestConfig): Promise { + const response = await http.request>({ ...config, }) - if (isApiEnvelope(response.data)) { + if (isApiEnvelope(response.data)) { return response.data.data } return response.data } - -export async function requestPageJson( - url: string, - config?: RequestConfig, -): Promise> { - const response = await http.request, AxiosResponse>, D>({ - url, - ...config, - }) - - if (!isApiPageEnvelope(response.data)) { - throw new ApiError('分页响应格式无效', response.status) - } - - return { - items: response.data.data.items, - page: response.data.data.page, - } -} - -export function get(url: string, params?: any, config?: RequestConfig): Promise { - return requestJson(url, { - method: 'GET', - params, - ...config, - }) -} - -export function post(url: string, data?: D, config?: RequestConfig): Promise { - return requestJson(url, { - method: 'POST', - data, - ...config, - }) -} - -export { http } - -export default { - http, - request: requestJson, - requestPage: requestPageJson, - get, - post, -} diff --git a/web/src/api/types.ts b/web/src/api/types.ts deleted file mode 100644 index 71b83ba88..000000000 --- a/web/src/api/types.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface ApiEnvelope { - code: number - message: string - data: T -} - -export interface ApiPageMeta { - limit?: number - page?: number - pageSize?: number - total?: number - totalPages?: number -} - -export interface ApiPageData { - items: T[] - page: ApiPageMeta -} - -export type ApiPageEnvelope = ApiEnvelope> - -export interface PaginatedResult { - items: T[] - page: ApiPageMeta -} diff --git a/web/src/components/base/BaseButton.vue b/web/src/components/base/BaseButton.vue index b8409eaed..b11b8b563 100644 --- a/web/src/components/base/BaseButton.vue +++ b/web/src/components/base/BaseButton.vue @@ -1,9 +1,9 @@ diff --git a/web/src/components/base/BaseCard.vue b/web/src/components/base/BaseCard.vue index ebfbc5a06..3e223352a 100644 --- a/web/src/components/base/BaseCard.vue +++ b/web/src/components/base/BaseCard.vue @@ -1,22 +1,70 @@