Complete the DoubaoVideo asset library integration on top of the
managed per-user asset groups:
- Asset endpoint resolution is an explicit three-level priority: a
hard-coded override wins, otherwise the per-channel credential
base_url from channel_asset_credentials is used verbatim (it already
contains the full /openApi/portrait path), otherwise the official
default. The channel video base URL no longer participates in asset
routing, so video (official Ark) and asset (gateway) addresses stay
fully independent. The channel form gains an "素材 API 地址" field with
a hint that the official address has no asset API, and the channel
type label becomes "豆包视频(素材网关)" to surface the split. Credential
summaries now cover DoubaoVideo channels and echo the base URL back
for editing.
- After an asset request auto-matches a channel (no existing user
binding), persist it to user_asset_channels so subsequent asset and
video requests stay on the same channel, keeping asset:// references
consistent with the upload channel. This mirrors the video-task
binding backfill; binding failure logs a warning and does not fail
the asset operation.
Co-Authored-By: ZCode <noreply@anthropic.com>
On PostgreSQL the logs table is a RANGE-partitioned table on created_at
owned by pg_partman; GORM AutoMigrate would alter its composite primary
key (id, created_at) and create redundant per-partition indexes,
breaking startup. Extract ensureLogTable() to skip AutoMigrate on
PostgreSQL (existence check only) while keeping the original behavior
on SQLite/MySQL. The dedicated LOG_SQL_DSN PostgreSQL branch follows
the same rule. Ship the matching postgres-partman image (Dockerfile +
01-partition.sql) that creates and maintains the partitioned table via
pg_partman + pg_cron.
Co-Authored-By: ZCode <noreply@anthropic.com>
Add two layers of defense:
1. Webhook availability guard (controller/payment_webhook_availability.go):
- isStripeWebhookEnabled() checks StripeWebhookSecret != "" before processing
- isCreemWebhookEnabled(), isWechatPayWebhookEnabled(), isAlipayWebhookEnabled()
- isEpayWebhookEnabled() with similar checks for all payment webhooks
- Applied to: StripeWebhook, CreemWebhook, WechatPayWebhook, AlipayPayWebhook,
EpayNotify, SubscriptionEpayNotify
2. PaymentProvider field (model/topup.go):
- New PaymentProvider field on TopUp to identify which gateway created the order
- Recharge() checks PaymentProvider == PaymentProviderStripe
- rechargeByQRCodePayment() checks PaymentProvider matches wechat/alipay
- RechargeCreem() checks PaymentProvider == PaymentProviderCreem
- All payment controllers set PaymentProvider when creating orders
Root cause: When StripeWebhookSecret was empty, ComputeSignature used
an empty HMAC key, allowing attackers to forge valid signatures and
complete orders from any payment gateway without actually paying.
Co-Authored-By: Claude <noreply@anthropic.com>
Add candidate user selection and explicit_ids batch creation flow for overseas migration. Also add cancellable batches to release selected-user locks and extend backend, frontend, and e2e coverage for the new flow.
Co-Authored-By: Codex <noreply@anthropic.com>
Add unit tests for group ratio, cross-group retry, and price calculation:
- Group ratio CRUD and special ratio override (12 tests)
- HandleGroupRatio with auto-group and special ratios (5 tests)
- ModelPriceHelper with group ratio in price/ratio modes (3 tests)
- Channel selection priority, groups, weighted LB (6 tests)
- RetryParam and AutoGroup service logic (7 tests)
Also restore the group pricing card in model detail SideSheet.
Replace bytes.Replace with gjson.GetBytes+sjson.SetBytes in passthrough
mode to correctly handle JSON whitespace variations and avoid silently
sending empty body on read errors. Also add log search count limit to
prevent slow COUNT queries on large log tables.
Reject redemption requests from synced users, surface a localized API message, and cover both model- and controller-level paths with tests.
Co-Authored-By: Codex <noreply@anthropic.com>
之前有 meta 记录但 sort_order=999999 的模型排在没 meta 记录的模型前面,
导致部分"未设置排序"的模型仍然挤在前面。现在统一处理:不论是否有
meta 记录,sort_order=999999 的都视为未设置,排在后面。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>