Bring official Volcengine channels in line with the China Mobile asset
isolation model: the platform owns the upstream asset group lifecycle
per (user, channel). asset_group.* APIs are forbidden for clients;
CreateAsset is scoped to the user's managed group (client GroupId is
overwritten), ListAssets is scoped with Filter.GroupIds to the managed
group, and Get/Update/Delete verify ownership via GetAsset before
forwarding, returning not-found for foreign assets.
Generalize the shared managed-group plumbing (GetOrCreateUserAssetGroup,
ScopeManagedAssetRequest, RequireManagedAssetOwnership) and add the
DoubaoVideo-specific group creator (Ark CreateAssetGroup returns the id
in Result.Id; GroupType must be omitted). The DoubaoVideo asset adapter
now serializes the request body from req.Body so platform rewrites take
effect (RawBody was bypassing the scoping) and the V4 signature always
covers the actual payload.
Verified end-to-end: group ops rejected, forged GroupId overwritten by
the auto-created managed group, ListAssets returns only the managed
group, foreign assets hidden from get/delete.
Co-Authored-By: ZCode <noreply@anthropic.com>
Register a DoubaoVideo asset adapter that forwards the Ark-compatible
asset API (POST /api/v1/volcengine/asset?Action=xx) to the official
/openApi/portrait endpoint with a Volcengine V4 HMAC-SHA256 signature
(canonical request over content-type/host/x-content-sha256/x-date,
date/cn-beijing/ark/request scope). The AK/SK pair and optional
project code are stored in channel_asset_credentials (pool_id holds
the project code), so asset credentials stay separate from the video
Bearer key; the channel form now accepts them for type 54.
DoubaoVideo joins the seedance asset family so a user's asset upload
channel and video task channel stay the same (asset:// references
pass through to the upstream). Both resolution chains (asset proxy
resolver and video asset family matcher) now require the asset
credential to be configured before a type-54 channel can serve,
bind, or be auto-matched for assets, falling through to compatible
channels otherwise. The /api/v3 native-path whitelist simplifies
back to the family list now that 54 is a member.
Verified end-to-end against the real upstream: CreateAssetGroup,
CreateAsset (real video upload, Processing -> Active moderation),
ListAssets/GetAsset, and a video generation task referencing the
asset via asset://video reference (succeeded, 432900 tokens).
Co-Authored-By: ZCode <noreply@anthropic.com>
The taskdoubao adaptor expected snake_case JSON keys (video_url,
completion_tokens, total_tokens, framespersecond, service_tier,
created_at, updated_at) but Volcengine returns camelCase (videoUrl,
completionTokens, totalTokens, framesPerSecond, serviceTier,
createdAt, updatedAt). As a result the video URL was never parsed:
successful tasks fell back to the platform proxy URL
(BuildProxyURL) as result_url, which broke the video proxy in
containerized deployments (localhost:port not reachable inside the
container) and zeroed the usage tokens used for billing.
Align the responseTask JSON tags with the real Volcengine response
shape and add a regression test with the exact production payload.
Co-Authored-By: ZCode <noreply@anthropic.com>
The native /api/v3/contents/generations/tasks route was restricted to
the seedance asset channel family (58/60/61); official Volcengine Ark
channels (DoubaoVideo, type 54) could only be reached via the
standardized /v1/video/generations path. Allow type 54 on the native
route in both the distributor and the task retry channel selection,
while keeping it out of the seedance asset binding system.
The taskdoubao adaptor now reuses a pre-parsed task request from the
context (native path) instead of re-parsing the body as TaskSubmitReq,
whose prompt validation would reject the Volcengine-native format.
When req.Prompt is empty (native path), content text items from
metadata are preserved instead of being replaced with an empty prompt;
the standardized path behavior (prompt replaces metadata text) is
unchanged and covered by existing tests.
Co-Authored-By: ZCode <noreply@anthropic.com>
Add deploy/build_newapi.sh for tagged Aliyun image builds, remove the
94MB compiled binary new-api.exe~ accidentally committed to the repo,
and add the missing go.sum for third_party/ecloudsdkcore.
Co-Authored-By: ZCode <noreply@anthropic.com>
Register the Seedance mini model in the matrix usage capability table
(both native name and doubao-seedance-2.0 alias) and keep
SupportsAnyMatrixUsageBillingModel in sync so the model bills via
matrix usage on the ChinaMobile Seedance channel.
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 KlingAiping usage-token settlement from upstream final_unit_deduction and duration fallback handling.
Normalize model endpoint config persistence and convert model pricing values between stored USD and display currency in the editor.
Co-Authored-By: Codex <noreply@anthropic.com>