Commit Graph

475 Commits

Author SHA1 Message Date
erio
6ac8ccde46 fix: merge 30 general improvements from release branch
Bug fixes:
- Detached context for GetAccountConcurrencyBatch (prevent all-zero on request cancel)
- Filter soft-deleted users in GetByGroupID
- Stripe CSP policy (allow Stripe.js in script-src and frame-src)
- WebSearch API key validation on save
- RECHARGING status in payment result success check
- Windows test fixes (logger Sync deadlock, config path escaping)

Feature enhancements:
- Webhook multi-instance dispatch (extractOutTradeNo + GetWebhookProvider)
- EasyPay mobile H5 payment (device param + PayURL2)
- SSE error propagation in WebSearch emulation
- AccountStatsCost DTO field for admin usage logs
- Plans sort by sort_order instead of created_at
- UsageMapHook for streaming response usage data
- apicompat Instructions field passthrough
- EffectiveLoadFactor for ops concurrency/metrics
- Usage billing RETURNING balance for notify system
- BulkUpdate mixed channel warning with details
- println to slog migration in auth cache
- Wire ProviderSet cleanup
- CI cache-dependency-path optimization

Frontend:
- Refund eligibility check per provider (canRequestRefund)
- Plan sort_order editing
- Dead code cleanup (simulate_claude_max, client_affinity)
- GroupsView platform switch guard
- channels features_config API type
- UsageView account_stats_cost export
2026-04-14 17:35:27 +08:00
erio
f1297a3694 feat: add per-provider allow_user_refund control and align wildcard matching
allow_user_refund:
- Add allow_user_refund field to PaymentProviderInstance ent schema
- Migration 103: ALTER TABLE payment_provider_instances ADD COLUMN
- Cascade logic: disabling refund_enabled auto-disables allow_user_refund
- User refund validation: check provider instance allows user refund
- Admin refund validation: check provider instance allows admin refund
- Subscription refund: deduct days on refund, rollback on failure
- New endpoint: GET /payment/orders/refund-eligible-providers
- Frontend: ToggleSwitch in ProviderCard/Dialog, cascade in SettingsView

Wildcard matching:
- Change findPricingForModel from "longest prefix wins" to "config order
  priority (first match wins)", aligning with channel service behavior
2026-04-14 16:26:46 +08:00
erio
7c7292935e feat: websearch quota enhancements and balance notify hint
- QuotaLimit changed to *int64 (null=unlimited, >0=limited)
- Add reset-usage endpoint (POST /admin/settings/web-search-emulation/reset-usage)
- Show quota usage in header always (collapsed and expanded)
- Add reset quota button in expanded provider view
- Quota input: empty=unlimited with ∞ placeholder, must be >0 if set
- Add email verification hint on balance notify card
2026-04-14 09:36:40 +08:00
erio
9e0d12d3b0 fix: show websearch API key visibility/copy buttons for saved providers
The buttons were hidden because v-if only checked provider.api_key,
which is always empty for saved providers (backend sanitizes it).
Now also checks api_key_configured. Copy button is disabled when
no actual key is available (only configured placeholder shown).
2026-04-14 09:35:46 +08:00
erio
0a4ece5f5b fix: audit round-3 — proxy safety, intervals persistence, SMTP timeout, sort fix
- Skip websearch provider when ProxyID is set but proxy not found (prevent
  silent direct connection bypass)
- Fix sortByStableRandomWeight: pair factors with items so sort.Slice swap
  keeps weights aligned
- Allow empty platform in account_stats_pricing_rules (wildcard matching),
  only force anthropic default for main model_pricing
- Add channel_account_stats_pricing_intervals table and repo layer support
  for interval-based pricing in account stats rules
- calculateTokenStatsCost now uses interval pricing when available
- Replace smtp.SendMail/tls.Dial with net.Dialer timeout (10s dial, 20s IO)
  to prevent goroutine leak on SMTP hang
- Fix gofmt formatting issues
- Web Search label: black text with red warning hint
2026-04-14 09:35:20 +08:00
erio
9c09bd19b4 fix: websearch features_config cleanup and pricing rules validation
- Fix web_search_emulation toggle: explicitly write false for disabled
  platforms instead of leaving stale true from cloned features_config
- Extract validatePricingEntries from validateChannelConfig for reuse
- Validate account_stats_pricing_rules[].pricing in both Create and
  Update paths (negative prices, bad intervals, missing per_request price)
2026-04-14 09:35:20 +08:00
erio
a9880ee7b9 fix: round-2 audit fixes — security, code quality, and UI improvements
Security (HIGH):
- Normalize all Redis cache keys to lowercase (verifyCode, passwordReset)
- Fix verify code TTL renewal on failed attempts: use remaining TTL via
  ExpiresAt field instead of resetting to full 15-minute window
- Add 3 missing fields to diffSettings audit log (promo_code, invitation_code,
  custom_endpoints)

Code quality (MEDIUM):
- Extract filterVerifiedEmails shared helper (balance_notify_service.go)
- Add Pricing array non-empty validation for channel pricing rules
- Add platform token semantics comment in gateway_service.go
- Complete validatePlanPatch test coverage (+10 test cases)
- Replace string types with QuotaThresholdType/QuotaResetMode across frontend
- Remove duplicate getPlatformTextColor/getRateBadgeClass in ChannelsView
- Return EMAIL_NOT_FOUND error on RemoveNotifyEmail miss

UI improvements:
- Reorder cost tooltip: user billing above separator, account billing below
- Add NaN guard to accountBilled function
- Move timezone selector inline into reset-mode row (no longer standalone)
2026-04-14 09:35:05 +08:00
erio
74f8a30f86 fix: address audit findings for websearch, email verification, and pricing
- Fix websearch provider failover: proxy error from provider-specific proxy
  now continues to next provider instead of aborting the entire loop
- Fix SMTP failure locking users out: send email first, then write cache
  and increment rate counter
- Fix notify email cache key case sensitivity: normalize to lowercase
- Add OriginalPrice validation to validatePlanPatch and validatePlanRequired
- Add empty scope validation for channel pricing rules (group_ids/account_ids)
- Add platform color to account search dropdown in channel pricing rules
2026-04-14 09:33:53 +08:00
erio
1b7c295199 refactor: M5 useQuotaNotifyState composable + H14 Vue file splits
M5: New composable frontend/src/composables/useQuotaNotifyState.ts
  - Replaces 9 individual refs in both Create/Edit modals with reactive state
  - Provides loadFromExtra/writeToExtra/reset helpers
  - Eliminates ~120 lines of duplicated code across the two modals

H14: Vue file length violations fixed
  - AdminPaymentPlansView.vue: 325 → 183 lines (extracted PlanEditDialog.vue)
  - QuotaLimitCard.vue: 327 → 268 lines (extracted QuotaDimensionRow.vue)
  - PlanEditDialog.vue: 181 lines (new, plan create/edit form)
  - QuotaDimensionRow.vue: 108 lines (new, single quota dimension row)
2026-04-14 09:33:39 +08:00
erio
f571d8ffad fix(notify): write back auto-filled recharge URL to form on save 2026-04-14 09:31:08 +08:00
erio
48b6c4811f fix(notify): auto-fill recharge URL with current origin when empty 2026-04-14 09:31:08 +08:00
erio
c1eb79e4ba feat(notify): add platform/ID to quota alert email, add recharge URL to balance alert
- Quota alert email now shows account ID and platform
- Balance low email includes a "Top Up Now" button when recharge URL is configured
- New setting: balance_low_notify_recharge_url in admin settings
2026-04-14 09:30:51 +08:00
erio
b7fb2e4387 fix: audit fixes for websearch, notifications, and channel pricing
P0: fix wildcard matching test assertion (config order, not longest prefix)
P0: add TotalRecharged to auth cache snapshot (v5) for percentage threshold
P1: move pricing rules into per-platform sections in ChannelsView
P1: populate account name cache when editing existing channel rules
P1: sanitize email subject headers to prevent SMTP injection
P1: make Redis INCR+EXPIRE idempotent for rate limiting
P1: deep copy FeaturesConfig in Channel.Clone()
P2: clean up stale email="" placeholder comments
P2: replace log.Printf with slog in email_service.go
2026-04-14 09:26:32 +08:00
erio
a68df457d8 fix: address audit findings across websearch, notify, and channel pricing
Backend fixes:
- Fix balance notify ignoring percentage threshold type (was treating
  percentage value as fixed USD amount)
- Remove dead code parseJSONStringArray
- Add ImageOutputTokens to tryModelFilePricing calculation
- Unify zero-value check: cost == 0 → cost <= 0 in calculateTokenStatsCost
- Use MarshalNotifyEmails instead of json.Marshal for consistency
- Rename quotaDim.oldUsed → currentUsed for clarity
- Extract HTML email templates to const variables (function ≤30 lines)

Test fixes:
- Rewrite account_websearch_test.go for GetWebSearchEmulationMode tri-state
- Add 6 tryModelFilePricing test cases

Frontend fixes:
- Replace hardcoded '未命名' with i18n key
- Extract getBillingModeLabel/getBillingModeBadgeClass to shared utils
- Replace inline type with imported NotifyEmailEntry
- Pass platform to AccountStats pricing rules via inferRulePlatform()
- Add billing mode constants (BILLING_MODE_TOKEN/PER_REQUEST/IMAGE)
2026-04-14 09:26:08 +08:00
erio
1262654d97 feat: WebSearch tri-state, account stats pricing fix, quota cache fix, usage tooltip
WebSearch tri-state switch:
- Account-level web_search_emulation changed from bool to tri-state
  string: "default" (follow channel) / "enabled" / "disabled"
- shouldEmulateWebSearch checks channel config when account is "default"
- SQL migration converts old bool values
- Frontend select replaces toggle in Edit/CreateAccountModal

Account stats pricing:
- resolveAccountStatsCost uses upstream model (post-mapping) for matching
- Priority: custom rules → model pricing file (when toggle on) → default
- Custom rules always configurable, independent of toggle
- Account ID field changed to searchable selector filtered by platform
- Description updated to reflect new behavior

Quota notification cache fix:
- CheckAccountQuotaAfterIncrement fetches real-time account from DB
- Reconstructs pre-increment usage for accurate threshold crossing detection
- New AccountQuotaReader interface (minimal: GetByID only)

Usage tooltip:
- Per-request/image billing shows per-request price instead of $0 token price
- Token billing continues to show input/output price per million tokens
2026-04-14 09:26:08 +08:00
erio
915b7a4a56 feat(notify): convert email lists to NotifyEmailEntry struct with toggle support
- Change balance_notify_extra_emails and account_quota_notify_emails
  from []string to []NotifyEmailEntry{email, disabled, verified}
- Add per-email enable/disable toggle for both user and admin notifications
- Add PUT /user/notify-email/toggle API endpoint
- Fix critical bug: API key auth cache snapshot missing balance notify
  fields (Email, Username, BalanceNotifyEnabled, etc.), causing
  notifications to never fire on cached request paths
- Bump cache snapshot version 3→4 to invalidate stale entries
- Add SQL migration 104 to convert old format data
- Backward compatible: parseNotifyEmails auto-detects old/new format
- User balance notify: max 3 emails (primary + 2 extra)
- Admin quota notify: unlimited emails, each with toggle
2026-04-14 09:26:07 +08:00
erio
49281bbe45 fix(websearch): hide show/copy buttons when API key is empty
Only show the inline eye/copy buttons when provider.api_key has a value.
When only api_key_configured is true (saved key, not loaded), buttons are
hidden since there's nothing to show/copy.
2026-04-14 09:25:50 +08:00
erio
80fa484467 refactor(channels): move account stats pricing rules from basic to platform tabs
- Basic settings now only shows the global toggle
- Custom pricing rules appear inside each platform tab when toggle is on
- Group selector in rules scoped to the current platform's groups
- Remove unused allFormGroupIds computed
2026-04-14 09:25:49 +08:00
erio
eba289a7ff feat(notify): add global toggles, percentage threshold, and visibility control
- Add global toggle for account quota notification in admin settings
- Add percentage-based threshold type for per-account quota alerts
- Hide balance notify card on user profile when global toggle is off
- Expose balance_low_notify_enabled and account_quota_notify_enabled in PublicSettings
- Add threshold type (fixed/percentage) to QuotaNotifyToggle with $ / % switcher
2026-04-14 09:25:49 +08:00
erio
889b5b4f3b fix(websearch): improve settings UI and hide config when globally disabled
- API Key show/copy buttons moved inside input field (inline icons)
- Proxy selector and test button on same row to save vertical space
- Test opens a dialog modal instead of inline display
- Hide all websearch config in channels/accounts when global toggle is off
2026-04-14 09:25:36 +08:00
erio
cef22c70ab fix(notify): remove percentage threshold from balance notification
Balance low notification only supports fixed USD amount threshold.
Percentage threshold is a quota concept, not applicable to balance.
Reverted threshold_type from admin settings, user profile, and all
backend/frontend layers. DB fields (balance_notify_threshold_type,
total_recharged) retained for potential future quota use.
2026-04-14 09:25:12 +08:00
erio
9e33d0c4c0 fix: address audit findings for websearch and balance notification
- Fix GetByKeyForAuth not selecting balance notify fields (notifications
  never triggered in gateway path)
- Fix provider-level ProxyURL never resolved: inject ProxyRepository into
  SettingService, resolve proxy URLs when building Manager
- Fix admin manual balance adjustment not updating total_recharged
- Add threshold_type input validation (reject invalid values)
- Fix user threshold_type inheritance: custom threshold defaults to "fixed"
  instead of inheriting global type (prevents $5 being treated as 5%)
- Add try-catch for clipboard.writeText (fails on non-HTTPS)
- Add SetTotalRecharged to user Update for admin balance operations
2026-04-14 09:24:58 +08:00
erio
f694afbbf4 feat(notify): add percentage threshold type for balance low notification
- Add threshold_type field (fixed/percentage) to system and user settings
- Add total_recharged field to users table, auto-incremented on balance credit
- Percentage mode: effective threshold = total_recharged × percentage / 100
- User-level threshold_type inherits from system default when not set
- Update admin settings UI with radio selector (fixed amount / percentage)
- Migration: 102_add_balance_notify_threshold_type.sql
2026-04-14 09:24:17 +08:00
erio
d0674e0ff9 feat(websearch): settings UI overhaul and quota improvements
- Remove Priority field, auto load-balance by quota remaining
- Replace QuotaRefreshInterval (daily/weekly/monthly) with SubscribedAt
  (subscription date, monthly lazy refresh via Redis TTL)
- Add collapsible provider cards, API key show/copy, usage progress bar
- Add test endpoint (POST /web-search-emulation/test) bypassing quota
- Wire WebSearchManagerBuilder on startup (was never called before)
- Fix nextMonthlyReset day-of-month overflow (Jan 31 → Feb 28)
- Fix non-deterministic sort in selectByQuotaWeight
- Map ProxyID in builder for provider-level proxy tracking
- Fix frontend timezone drift in subscribed_at date picker
- Fix provider deletion index shift for expandedProviders state
2026-04-14 09:23:40 +08:00
erio
b32d1a2c9f feat(notify): add balance low & account quota notification system
- User balance low notification: email alert when balance drops below
  configurable threshold (user email + verified extra emails)
- Account quota notification: broadcast email to admin-configured
  recipients when daily/weekly/total quota usage exceeds alert threshold
- Admin settings: global enable/disable, default threshold, quota
  notification email list (Email Settings tab)
- User profile: enable/disable, custom threshold, add/remove extra
  notification emails with verification code flow
- Account quota: per-dimension alert toggle and threshold in quota
  control card
- Trigger logic: first-crossing only (old >= threshold && new < threshold
  for balance; old < threshold && new >= threshold for quota), naturally
  prevents duplicate notifications without Redis dedup
2026-04-14 09:23:02 +08:00
erio
7535e312e0 feat(channels): add custom account stats pricing rules
Allow channels to configure independent model pricing for account
statistics cost calculation, decoupled from user billing.

Backend:
- Migration 101: channels.apply_pricing_to_account_stats toggle,
  channel_account_stats_pricing_rules/model_pricing tables,
  usage_logs.account_stats_cost column
- resolveAccountStatsCost: match rules by group/account, then channel
  pricing, fallback to original formula when unconfigured
- Integrate into both GatewayService.recordUsageCore and
  OpenAIGatewayService.RecordUsage
- Update 8 account stats SQL queries to use
  COALESCE(account_stats_cost, total_cost) * account_rate_multiplier
- 23 unit tests for matching, pricing lookup, and cost calculation

Frontend:
- Channel edit dialog: toggle + custom rules UI with group/account
  multi-select and pricing entry cards
- API types and i18n (zh/en)
2026-04-14 09:22:12 +08:00
erio
1b53ffcac7 feat(gateway): add web search emulation for Anthropic API Key accounts
Inject web search capability for Claude Console (API Key) accounts that
don't natively support Anthropic's web_search tool. When a pure
web_search request is detected, the gateway calls Brave Search or Tavily
API directly and constructs an Anthropic-protocol-compliant SSE/JSON
response without forwarding to upstream.

Backend:
- New `pkg/websearch/` SDK: Brave and Tavily provider implementations
  with io.LimitReader, proxy support, and Redis-based quota tracking
  (Lua atomic INCR + TTL, DECR rollback on failure)
- Global config via `settings.web_search_emulation_config` (JSON) with
  in-process cache + singleflight, input validation, API key merge on
  save, and sanitized API responses
- Channel-level toggle via `channels.features_config` JSONB column
  (DB migration 101)
- Account-level toggle via `accounts.extra.web_search_emulation`
- Request interception in `Forward()` with SSE streaming response
  construction using json.Marshal (no manual string concatenation)
- Manager hot-reload: `RebuildWebSearchManager()` called on config save
  and startup via `SetWebSearchRedisClient()`
- 70 unit tests covering providers, manager, config validation,
  sanitization, tool detection, query extraction, and response building

Frontend:
- Settings → Gateway tab: Web Search Emulation config card with global
  toggle, provider list (add/remove, API key, priority, quota, proxy)
- Channels → Anthropic tab: web search emulation toggle with global
  state linkage (disabled when global off)
- Account Create/Edit modals: web search emulation toggle for API Key
  type with Toggle component
- Full i18n coverage (zh + en)
2026-04-14 09:20:39 +08:00
erio
56e4a9a914 fix: audit fixes - magic strings to constants, frontend any/catch, LB tests
Backend:
- Define OrderTypeBalance/Subscription, EntityStatusActive, DeductionType*,
  NotificationStatus* constants in payment/types.go
- Replace all magic strings in payment_order, payment_fulfillment, payment_refund
- Add local constants in easypay.go (tradeStatusSuccess, signTypeMD5)
- Add 27 unit tests for load balancer (filterByLimits, pickLeastAmount,
  getInstanceChannelLimits, startOfDay)

Frontend:
- Remove all `any` types in SettingsView.vue (18 catch blocks + 1 payload)
- Fix bare catch blocks in PaymentResultView, PaymentView
- Add `unknown` type annotation to all catch blocks

chore: bump version to 0.1.108.140
2026-04-14 09:18:58 +08:00
erio
5bae3b0577 fix(payment): audit fixes for alipay/wxpay/stripe payment providers
Backend:
- Extract YuanToFen/FenToYuan to payment/amount.go using shopspring/decimal
- Require alipay publicKey in config validation
- Fix wxpay webhook response to return JSON per V3 spec
- Remove wxpay certSerial fallback to publicKeyId
- Define magic strings as named constants in wxpay/alipay providers
- Add slog warning for wxpay H5→Native payment downgrade
- Make EncryptionKey validation return error on invalid (non-empty) key
- Make decryptConfig propagate errors instead of returning nil
- Add idempotency check in doBalance to prevent stuck FAILED retries

Frontend:
- Fix dashboard currency symbol from $ to ¥
- Fix AdminPaymentPlansView any type to proper SubscriptionPlan type
- Make quick amount buttons follow selected payment method limits
- Center help image with larger height and text below
2026-04-14 09:17:06 +08:00
erio
3de7713017 fix(channel): splice替换model_pricing条目 + 增强调试日志 2026-04-14 09:08:58 +08:00
erio
1def627443 feat(settings): add EasyPay provider recommendation link below enabled providers 2026-04-11 21:13:46 +08:00
erio
64cee0b633 feat(settings): add payment config guide link in payment settings header 2026-04-11 21:13:28 +08:00
erio
fa833f7684 Merge remote-tracking branch 'upstream/main' into feat/payment-system-v2
# Conflicts:
#	frontend/src/api/admin/settings.ts
#	frontend/src/stores/app.ts
#	frontend/src/types/index.ts
#	frontend/src/views/admin/SettingsView.vue
2026-04-11 18:25:06 +08:00
erio
e3a000e0d4 refactor(payment): code standards fixes and regression repairs
Backend:
- Split payment_order.go (546→314 lines) into payment_order_lifecycle.go
- Replace magic strings with constants in factory, easypay, webhook handler
- Add rate limit/validity unit constants in payment_order_lifecycle, payment_service
- Fix critical regression: add PaymentEnabled to GetPublicSettings response
- Add missing migration 099_fix_migrated_purchase_menu_label_icon.sql

Frontend:
- Fix StripePopupView.vue: replace `as any` with typed interface, use extractApiErrorMessage
- Fix AdminOrderTable.vue: replace hardcoded column labels with i18n t() calls
- Fix SubscriptionsView.vue: replace hardcoded Today/Tomorrow with i18n
- Extract duplicate statusBadgeClass/canRefund/formatOrderDateTime to orderUtils.ts
- Add missing i18n keys: common.today, common.tomorrow, payment.orders.orderType/actions
- Remove dead PurchaseSubscriptionView.vue (replaced by PaymentView)
2026-04-11 13:16:35 +08:00
erio
27cd2f8e96 fix(payment): remove purchase_subscription fields replaced by payment system
The built-in payment system replaces the old external purchase subscription
iframe approach. Remove purchase_subscription_enabled/url from admin settings
interface and form defaults, as the Payment tab now handles this functionality.
Kept in stores/app.ts fallback to match backend DTO response structure.
2026-04-11 13:16:35 +08:00
erio
e1547d7835 fix(payment): resolve PR audit issues
- Add payment navigation to AppSidebar (user orders + admin payment menu group with collapse)
- Add 5 missing nav i18n keys (myOrders, orderManagement, paymentDashboard, paymentConfig, paymentPlans)
- Renumber payment migrations 090-100 → 092-102 to avoid conflict with upstream 090/091
- Remove non-payment sora_client_enabled change, restore upstream purchase_subscription fields
- Remove extra 'data' from SettingsTab type union
2026-04-11 13:16:35 +08:00
erio
63d1860dc0 feat(payment): add complete payment system with multi-provider support
Add a full payment and subscription system supporting EasyPay (Alipay/WeChat),
Stripe, and direct Alipay/WeChat Pay providers with multi-instance load balancing.
2026-04-11 13:16:35 +08:00
IanShaw027
f480e57344 fix: align table defaults and preserve sidebar svg colors 2026-04-10 18:27:53 +08:00
IanShaw027
2b70d1d332 merge upstream main into fix/bug-cleanup-main 2026-04-09 21:35:48 +08:00
Wesley Liddick
bbc79796dc Merge pull request #1529 from IanShaw027/feat/group-messages-dispatch-redo
feat: 为openai分组增加messages调度模型映射并支持instructions模板注入
2026-04-09 21:14:38 +08:00
IanShaw027
5f8e60a1b7 feat(table): 表格排序与搜索改为后端处理 2026-04-09 18:14:28 +08:00
IanShaw027
ad80606a44 feat(settings): 增加全局表格分页配置,支持自定义 2026-04-09 18:14:28 +08:00
IanShaw027
de9b9c9dfb feat(admin): 增加分组 messages 调度映射配置界面 2026-04-09 12:30:25 +08:00
IanShaw027
d765359f4b test(admin): 增加messages调度表单状态转换测试 2026-04-09 12:30:06 +08:00
ruiqurm
02a66a01c3 feat: support OIDC login. 2026-04-09 02:20:51 +00:00
shaw
e51c9e50b5 feat: sync billing header cc_version with User-Agent and add opt-in CCH signing
- Sync cc_version in x-anthropic-billing-header with the fingerprint
  User-Agent version, preserving the message-derived suffix
- Implement xxHash64-based CCH signing to replace the cch=00000
  placeholder with a computed hash
- Add admin toggle (enable_cch_signing) under gateway forwarding settings,
  disabled by default
2026-04-08 16:11:19 +08:00
shaw
7c60ee3c85 feat: Beta策略支持按模型区分处理(模型白名单) 2026-04-07 20:33:09 +08:00
shaw
a07296770c fix: remove remaining Sora references from frontend
The previous Sora removal missed several frontend references, causing
TypeScript build errors for sora_client_enabled and a missing SoraView.vue
import. Clean up all remaining Sora code from types, router, sidebar,
settings, store, and accounts API.
2026-04-05 21:26:47 +08:00
erio
08e69af572 fix: remove empty DataManagement tab from settings (was Sora S3 storage) 2026-04-05 17:22:22 +08:00
erio
294b4bcbac fix: remove Sora S3 profile code from DataManagementView, keep backup functionality 2026-04-05 17:22:22 +08:00