Commit Graph
2238 Commits
Author SHA1 Message Date
erio f3d2c1931c fix(lint): resolve depguard violation — remove redis import from service layer
Service layer must not import redis directly (depguard rule). Replace
*redis.Client field with WebSearchManagerBuilder callback injected by
the infra layer. Also fix remaining errcheck in manager_test.go.
2026-04-12 00:28:21 +08:00
erio 6df42dd4bd fix(lint): resolve golangci-lint errcheck and gofmt issues
- Add errcheck handling for w.Write, json.Encode in test handlers
- Use defer func() { _ = resp.Body.Close() }() pattern
- Use safe two-return type assertions in websearch_config.go
- Fix gofmt alignment in setting_service.go struct fields
2026-04-12 00:17:52 +08:00
erio 15452fdacf fix(test): add web_search_emulation_enabled to API contract test
The settings API response now includes the new field; update the
expected snapshot in TestAPIContracts to match.
2026-04-12 00:09:28 +08:00
erio c2deb0939d 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-12 00:02:26 +08:00
erio 989c5faad5 Merge remote-tracking branch 'upstream/main' into release/custom-0.1.110
# Conflicts:
#	.github/audit-exceptions.yml
#	backend/cmd/server/VERSION
#	backend/go.sum
#	backend/internal/handler/admin/setting_handler.go
#	backend/internal/handler/dto/settings.go
#	backend/internal/repository/channel_repo.go
#	backend/internal/service/channel_service.go
#	backend/internal/service/setting_service.go
#	backend/internal/service/settings_view.go
#	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:41:54 +08:00
erio 6d7e0642a0 chore: remove unused decodeURLValue and clean up formatting 2026-04-11 16:29:51 +08:00
erio d60015f1d1 revert(payment): remove active upstream sync, keep webhook-only approach
Reverts the sync polling mechanism that queried upstream providers on
each frontend poll. Keep the Stripe expiresAt countdown fix.
2026-04-11 16:10:09 +08:00
erio 576c34bbf7 fix(payment): add active upstream sync to polling and fix Stripe countdown
- Add POST /payment/orders/:id/sync endpoint that queries upstream
  provider on each poll, complementing webhooks for timely payment
  detection when webhooks are delayed or unreachable
- Fix Stripe payment countdown: pass expires_at to PaymentStatusPanel
  instead of empty string (was falling back to hardcoded 30 minutes)
- Idempotent: toPaid uses atomic UPDATE WHERE status=PENDING, so
  concurrent webhook + sync calls won't double-credit
2026-04-11 15:46:28 +08:00
erio b5b5e35757 refactor(payment): code standards fixes and file organization
Cherry-picked from PR branch (feat/payment-system-v2).
- Use payment.Type* constants instead of magic strings in factory
- Add wxpay success response constants
- Add validity unit constants (week/month)
- Add constants for easypay popup mode
- Split payment_order.go into payment_order_lifecycle.go
- Extract shared order utilities to orderUtils.ts
- Improve admin order components to use shared utilities
- Removed duplicate migration (097 already exists)
2026-04-11 13:24:17 +08:00
erio f4e4b1539b feat(payment): add H5/mobile payment support
Cherry-picked from PR branch (feat/payment-system-v2).
- EasyPay: parse payurl2 for H5 mobile links, prefer on mobile
- EasyPay: add device=mobile for popup mode on mobile
- Backend: expand isMobile() to detect iPad/iPod
- Frontend: auto-redirect on mobile instead of popup
- Frontend: fallback to redirect when popup blocked
- Stripe: use mobile_web client for WeChat Pay on mobile
- StripePopup: typed interface, extractApiErrorMessage
2026-04-11 13:22:12 +08:00
IanShaw027 f480e57344 fix: align table defaults and preserve sidebar svg colors 2026-04-10 18:27:53 +08:00
IanShaw027 7dc7ff22d2 fix: preserve messages dispatch config in repository hydration 2026-04-10 18:13:18 +08:00
IanShaw027 67a05dfccd fix: honor table defaults and preserve dispatch mappings 2026-04-10 17:55:37 +08:00
erio 9d4b02688f Revert "fix(payment): restore decodeURLValue in EasyPay webhook verification"
This reverts commit 1430c82078.
2026-04-10 16:43:21 +08:00
erio 1430c82078 fix(payment): restore decodeURLValue in EasyPay webhook verification
The db139191 audit incorrectly removed the second URL decode pass.
EasyPay providers send values that need decoding after url.ParseQuery,
causing signature verification to fail on .147+.
Production .146 worked because it still had decodeURLValue.
2026-04-10 16:40:42 +08:00
erio 8bf91c0915 revert: remove double URL decode, provider-side encoding issue 2026-04-10 14:34:50 +08:00
erio 9b38e44e1d fix(payment): restore double URL decode for EasyPay webhook values
EasyPay callbacks arrive with double-encoded query values (e.g. %25E5 instead
of %E5) due to redirect chains. url.ParseQuery decodes once; decodeURLValue
applies a second safe decode so the sign matches what EasyPay computed.
Also removes temporary debug logging.
2026-04-10 14:27:44 +08:00
erio 72f7677815 debug: log webhook raw body and sign comparison 2026-04-10 14:17:39 +08:00
erio 7ea6cafadb debug: add webhook provider lookup logging 2026-04-10 14:10:37 +08:00
erio d642a16f79 fix(payment): webhook uses order's provider instance for signature verification
When multiple provider instances exist (e.g. 3 EasyPay accounts), the webhook
handler now extracts out_trade_no from the callback, looks up the order, and
uses the order's original provider instance for verification instead of picking
an arbitrary instance from the registry.
2026-04-10 14:03:59 +08:00
erio cd1bbebb77 feat(payment): subscription renewal UI, payment button colors, QR branding, refund day deduction
- Add btn-alipay/btn-wxpay CSS classes; confirm button color follows payment method
- Subscription confirm header uses platform gradient (Anthropic orange, Gemini blue, etc.)
- Subscription confirm page shows plan details (rate, limits, validity)
- SubscriptionPlanCard: show "Renew" button when user has active subscription
- SubscriptionsView: add renewal button on active subscription cards
- QR code display: brand-color border + center logo overlay (Alipay blue, WeChat green)
- StripePaymentView: WeChat QR green border + logo, Alipay spinner brand color
- Backend: fix subscription refund to deduct days (ExtendSubscription -days or Revoke)
- Backend: rollback subscription days on gateway failure
2026-04-10 10:42:06 +08:00
IanShaw027 1312405966 fix(settings): 补齐公开设置中的表格分页字段返回 2026-04-10 09:18:48 +08:00
erio db139191e3 fix(payment): critical audit fixes for security, idempotency and correctness
Backend fixes:
- #1: doSub subscription idempotency via audit log check
- #2: markFailed only when status=RECHARGING (prevents overwriting COMPLETED)
- #3: ExpireTimedOutOrders checks upstream payment before expiring
- #4: Public verify endpoint for payment result page (no auth required)
- #5: EasyPay QueryOrder returns amount, confirmPayment handles zero amount
- #6: WxPay notifyUrl priority: request-first, config-fallback
- #7: EasyPay remove double URL decode in VerifyNotification
- #8: checkPaid/cancelUpstreamPayment use order's provider instance
- #9: Amount NaN/Inf/negative validation in order creation and refund
- #10: Refund amount comparison uses tolerance instead of float64 ==
- #11: Skip balance deduction on retry when previous rollback failed
- #12: checkPaid logs fulfillment errors instead of silently ignoring
- #13: WxPay certSerial added to required config fields

Frontend fixes:
- Payment result page no longer requires authentication
- Public verify API fallback for expired sessions
2026-04-10 02:23:19 +08:00
erio e45c774ab9 fix(payment): refresh provider registry on config changes
- Call RefreshProviders after Create/Update/Delete provider instance
- Call RefreshProviders after saving payment settings
- Auto-save settings when provider dialog saves
- Fixes webhook signature verification using stale pkey
2026-04-10 00:50:12 +08:00
erio 753a87dee3 feat(admin): add user info and keyword search to admin order list
Backend:
- Add Keyword field to OrderListParams
- AdminListOrders supports keyword search on out_trade_no, user_email, user_name
- PaymentOrder already has user_email/user_name/user_notes fields (no join needed)

Frontend:
- Replace inline status badge with OrderStatusBadge component
- Replace user_id column with user_email (shows email, fallback to username, with notes)
- Keyword search matches order number and user info

chore: bump version to 0.1.108.143
2026-04-09 23:41:59 +08:00
erio e9aa1b1330 fix(payment): use order's original provider instance for refund
Previously gwRefund used registry.GetProvider(paymentType) which returns
an arbitrary instance for that type. When multiple instances share the
same payment type (e.g., two EasyPay merchants both supporting alipay),
the refund would be sent to the wrong merchant.

Now getRefundProvider() reads the order's ProviderInstanceID, loads that
instance's config, and creates the correct provider. Falls back to
registry lookup for legacy orders without an instance ID.

chore: bump version to 0.1.108.142
2026-04-09 23:21:38 +08:00
erio 69f885eb5f style: fix gofmt formatting in payment_handler.go 2026-04-09 22:52:11 +08:00
erio 63c4172dd9 fix(ci): upgrade Go to 1.26.2 for security fixes, fix flaky WS test
- Upgrade Go from 1.26.1 to 1.26.2 in go.mod and Dockerfile to fix
  govulncheck findings (GO-2026-4947, GO-2026-4946, GO-2026-4870, etc.)
- Fix flaky PassthroughModeRelaysByCaddyAdapter test: tolerate
  StatusNormalClosure error from server after client closes connection
2026-04-09 22:38:39 +08:00
IanShaw027 269c7a065c fix(test): restore integration expectations 2026-04-09 22:08:42 +08:00
IanShaw027 b6946e78a2 fix(lint): restore repository sort helpers 2026-04-09 21:49:10 +08:00
IanShaw027 2b70d1d332 merge upstream main into fix/bug-cleanup-main 2026-04-09 21:35:48 +08:00
IanShaw027 b37afd68ec fix(lint): format setting service 2026-04-09 21:31:48 +08:00
erio ceee2d0584 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-09 21:29:49 +08:00
erio 44a9d91c8f refactor(payment): rewrite load balancer with complete limit checking
- Batch-query daily usage in one SQL (GroupBy) instead of N queries
- Include PENDING orders in daily usage to prevent over-committing
- Check remaining capacity (used + orderAmount > limit) not just used >= limit
- Check SingleMin/SingleMax per-channel limits
- Pre-attach usage to candidates, reuse across filter and strategy pick
- Fallback to full list with warn log when all instances exhausted

chore: bump version to 0.1.108.139
2026-04-09 21:20:13 +08:00
Wesley Liddick 00c08c574e Merge pull request #1539 from warksu/fix/loadfactor-not-synced-to-scheduler-cache
fix: 同步 LoadFactor 到调度快照缓存
2026-04-09 21:15:40 +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
Wesley Liddick 760cc7d6be Merge pull request #1481 from alfadb/fix/increase-error-log-body-limit
fix(ops): 将错误日志请求体存储限制从 10KB 提升至 256KB
2026-04-09 21:14:13 +08:00
Wesley Liddick 9a72025afb Merge pull request #1523 from octo-patch/fix/issue-1519-home-content-csp-frame-src
fix: include home_content URL in CSP frame-src origins
2026-04-09 21:13:46 +08:00
Wesley Liddick 74302f60ab Merge pull request #1010 from Glorhop/pr/oidc-login
feat(auth): support OIDC login and prefer IdP real email on sign-in
2026-04-09 21:13:22 +08:00
erio e5dd87743f fix(payment): filter instances by remaining capacity and single amount range
Check used + orderAmount > dailyLimit (not just used >= limit) and
also filter by SingleMin/SingleMax per-channel limits.

chore: bump version to 0.1.108.137
2026-04-09 19:33:36 +08:00
IanShaw027 62962c05f1 fix(lint): 修复 CI 中的 ineffassign 和 unused 代码告警,修正 group 排序集成测试兼容性 2026-04-09 19:25:08 +08:00
erio fc99a88fee fix(payment): implement least-amount load balance strategy
Previously SelectInstance always used round-robin regardless of the
configured strategy. Now it reads the strategy from payment config
and selects the instance with the lowest daily transaction amount
when "least-amount" is configured.

chore: bump version to 0.1.108.136
2026-04-09 19:20:20 +08:00
swark2006 118ff85fbf fix: 同步 LoadFactor 到调度快照缓存
LoadFactor 字段在构建调度快照缓存时缺失,导致调度算法
EffectiveLoadFactor() 无法使用配置的负载因子,回退到 Concurrency。
这会影响账号的负载率计算,进而影响调度优先级。

修复:在 buildSchedulerMetadataAccount 中添加 LoadFactor 字段。
2026-04-09 18:50:11 +08:00
IanShaw027 5f8e60a1b7 feat(table): 表格排序与搜索改为后端处理 2026-04-09 18:14:28 +08:00
IanShaw027 66e15a54a4 fix(export): 导出逻辑与当前筛选条件对齐 2026-04-09 18:14:28 +08:00
IanShaw027 ad80606a44 feat(settings): 增加全局表格分页配置,支持自定义 2026-04-09 18:14:28 +08:00
alfadbandClaude Opus 4.6 6401dd7cc7 fix(ops): increase error log request body limit from 10KB to 256KB
10KB is too aggressive for modern LLM API requests where conversation
context routinely exceeds 1MB. This causes error logs to contain only
a minimal placeholder, making it impossible to debug upstream failures.

256KB retains enough context for effective debugging while the existing
multi-pass trimming logic handles larger payloads gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:49:02 +08:00
erio 01864a7a1e fix(payment): ensure providers loaded before webhook, show out_trade_no
- Call EnsureProviders before webhook provider lookup to fix
  "provider not registered" error after restart or config changes
- Display out_trade_no instead of numeric ID in payment result pages
2026-04-09 17:23:23 +08:00
erio 03b97f14ad fix(payment): add return_url verification for popup payment mode
EasyPay popup mode relies on notify_url (webhook) to update order status,
but if the callback is missed, orders stay PENDING forever. This adds:

- POST /api/v1/payment/orders/verify endpoint that actively queries
  the upstream provider to check payment status
- Frontend PaymentResultView calls verify when receiving EasyPay
  return_url params (out_trade_no)
- Fix checkPaid to fall back to OutTradeNo when PaymentTradeNo is empty
  (popup mode doesn't have trade_no until the notify callback)
2026-04-09 14:56:24 +08:00
erio 3844c8688e refactor(payment): remove DB ID from order number, use out_trade_no lookup
Order ID format changed from sub2_5 to sub2_20250409aB3kX9mQ
(prefix + date + 8-char random, no DB ID embedded).

- Add out_trade_no column to payment_orders table
- Store generated external ID on order creation
- Webhook handler looks up order by out_trade_no instead of parsing DB ID
- Fallback to legacy sub2_N format for existing orders
2026-04-09 13:09:27 +08:00