Route OpenAI image streaming through shared stream handling, split image/realtime/usage helpers for maintainability, and include the related image request and rate limit updates.
Implement the Simple Large-font theme preset and xl font scale options to enhance interface accessibility. Remove status indicator dots from channel badges in logs to keep the table layout visual and clean.
- Remove explicit 'font-mono' and custom size classes from model and token
badges in usage logs.
- Allow model name and token badges to naturally inherit the active theme's
font family (Sans or Serif) and text size from the parent container.
- Restore visual consistency and proportion across all table badge components.
- Re-introduce the custom translucent background color and thin border scheme
for timing and duration badges in common, drawing, and task logs.
- Remove strict max-width constraints on model badges to ensure complete
names (with version suffixes) are always visible and wrap gracefully.
- Adjust spacing on model and token badges (h-6 height, larger gaps, and
proper padding) to prevent crowded elements and restore a balanced,
high-quality look in the log tables.
Adjust PRESET_DEFAULT_FONT so that the shipped 'default' preset falls back to the humanist 'sans' (Public Sans) out-of-the-box instead of forcing the editorial 'serif' (Lora). Keeps the 'anthropic' preset on 'serif' as intended.
- Restore StatusBadge sizes to h-5/text-xs (sm, md) and h-6/text-xs (lg).
- Restore classic textColorMap coloring and status indicator dot.
- Embed channel type icon directly inside StatusBadge as custom children.
- Re-align status badge colors: danger for manual disabled, warning for auto disabled.
Redesigns the hero section into a balanced horizontal dual-column layout:
- Left Column: Features title, clean legal-compliant descriptions, CTA buttons with BookOpen Docs link, and enlarged supported apps buttons (Cherry Studio and CC Switch with lobe icons)
- Right Column: Smoothly integrates the terminal API demo with top horizontal alignment
- i18n: Configures compliance translations for en, zh, fr, ja, ru, and vi locales
Three layered optimizations targeting Gemini-style 5MB base64 payloads where
RSS could balloon to tens of GB under concurrent load:
1. Byte-based param override (relay/common/override.go)
- Switch legacy/operations hot paths from common.Marshal round-trips and
map[string]any conversions to gjson/sjson on []byte directly.
- Avoids cloning 5MB strings during each Set/Delete operation.
2. strings.Builder for Gemini response markdown (relay/channel/gemini/relay-gemini.go)
- Replace string concatenation + strings.Join when assembling
"" content for inline image responses.
- Pre-allocates capacity from inline_data byte sizes.
3. Outbound BodyStorage + streaming Decoder (this commit's core)
- New relay/common/outbound_body.go helper wraps marshaled upstream bodies
in common.BodyStorage, allowing disk-cache mode to offload jsonData to
a temp file while waiting for upstream TTFB. The original []byte can
then be GC'd, removing ~5MB/req of heap residency during the longest
window of a request.
- All 7 relay handlers (gemini/claude/responses/embedding/image/compatible/
rerank) plus chat_completions_via_responses adopt the helper with
defer closer.Close() and explicit jsonData = nil.
- relay/common/relay_info.go: new UpstreamRequestBodySize so
relay/channel/api_request.go can populate req.ContentLength (lost when
body becomes a type-erased io.Reader).
- common/gin.go UnmarshalBodyReusable: when storage is disk-backed and
content-type is JSON, decode via DecodeJson(storage) instead of
storage.Bytes()+Unmarshal, removing one transient 5MB copy per request.
memory mode and form/multipart paths unchanged.
Users reported that the API key edit drawer's "Save changes" button
becomes unresponsive after the drawer has been open / idle for a
while: no loading state, no request, no error. Reopening the drawer
restores it because a fresh DOM is created.
The button lived in `SheetFooter` (a portaled Base UI Sheet) and was
linked to the form via the HTML `form='api-key-form'` attribute. Once
the portal/DOM relationship goes stale, the click no longer triggers
the form's submit event, hence the silent failure.
Defensive fix: drop the cross-DOM `form` linkage and call
`form.handleSubmit(onSubmit)` directly via `onClick`. The native
submit path (Enter key, original `<form onSubmit>`) is preserved.
Closes#4858
The faceted filter popover used a fixed width of 200px, which clipped
long option labels (e.g. user-defined channel group names) and forced
the truncated text to be unreadable without leaving a way to see the
full value.
- Switch PopoverContent from `w-[200px]` to
`min-w-[200px] max-w-[360px]` so short option lists keep their
current footprint while long labels can expand up to 360px before
the existing truncate kicks in.
- Add `title={t(option.label)}` on the truncated label span so users
can still hover to see the full text on extreme cases.
Closes#4905
The new UI's sign-up form sent the invite code under key `aff`, but
the backend `Register` controller binds it to `User.AffCode` whose
JSON tag is `aff_code` (see model/user.go). Result: every invited
sign-up landed with `inviter_id = 0`, breaking the affiliate flow.
Rename only the request payload field so it matches the backend
contract. URL query parameter (`/sign-up?aff=...`), localStorage key
and OAuth state continue to use `aff` and are unchanged.
Closes#4945
- Add singleSelect to status/role filters in API keys, users, and redemptions tables (#4880)
- Fix affiliate link 404 by changing /register to /sign-up (#4893)
- Open FetchModelsDialog in channel creation mode via customFetcher prop (#4817)
- Add TruncatedText component with tooltip for long channel names, token names, and usernames (#4877)
- Elevate forgot-password link z-index to prevent label click interception (#4898)
When proxying through another new-api instance, the upstream
X-Oneapi-Request-Id was overwriting the local one in client responses.
This adds a new `upstream_request_id` field to the logs table, captures
the upstream ID during relay, and filters it from being copied back to
the client. Frontend gains search/filter and detail display support.
* fix: theme-aware payment paths, auto-group validation, route guards, perf group filtering
- Add common.ThemeAwarePath to generate correct redirect URLs based on
active theme (default vs classic), replacing hardcoded /console/* paths
in 7 controllers and service/quota.go (#4765)
- Validate auto-group availability against getUserGroups before defaulting
form values; playground falls back to 'default' group when 'auto' is
unavailable (#4796, #4799)
- Enforce HeaderNavModules settings in rankings route (frontend + backend
API) and SidebarModulesAdmin in playground route to block direct URL
access when features are disabled (#4704, #4512)
- Filter perf_metrics API response to only include currently configured
groups, hiding stale data from deleted groups (#4790)
- Preserve query params (pay=success/fail) in /console/topup → /wallet
frontend redirect
* fix: update hero section text and localization strings for clarity
Add a batched performance summary API for model square cards and show compact latency, throughput, and status metrics without increasing card size. Also fix OTP verification form submission.
Move top_up_link out of the public GetStatus endpoint into the
authenticated GetTopUpInfo endpoint. Update classic frontend to
read topup_link from the topup info response instead of status.
Also add mailto links in SECURITY.md.
close#4582
Uncoordinated bulk vulnerability submissions have caused significant
disruption. Added a prominent notice requiring prior coordination for
bulk reports, with clear consequences: closure without review and
potential blocking of repeated offenders.
* 🎨 feat(web/default): add shadcn-style theme presets, radius prefs, and fix selection badges
Integrate the qn-platform–style OKLCH color system into the default frontend while keeping the existing blue-tinted dark tokens for the default theme. Add [data-theme-preset] palettes for seven named presets plus the default zinc-like scale, define [data-theme-radius] overrides so user radius beats preset --radius, and align the Tailwind @custom-variant dark helper with .dark usage.
Introduce ThemeCustomizationProvider to own preset and radius state, persist choices in cookies (theme-preset, theme-radius), and sync data-theme-preset / data-theme-radius on <html>. Wrap the tree in main.tsx.
Extend ConfigDrawer with theme preset swatches (scoped data-theme-preset) and radius previews wired to context; refactor swatch/card markup so selected CircleCheck badges sit outside clipped rows (remove outer overflow-hidden that hid the centered checkmark).
Add i18n keys for preset names, radius, and accessibility labels across en, zh, fr, ja, ru, vi.
* 🎨 fix(web): align segmented controls with theme radius tokens
- Replace hard-coded inner pill radii (rounded-[5px]) on dashboard chart
toolbars with radius-md so the active state follows --radius when users
change Radius in Theme Settings.
- Use nested radii consistent with TabsList/TabsTrigger: outer
rounded-lg (var(--radius)) and inner rounded-md (calc(var(--radius) - 2px))
so the track and active thumb stay concentric at small scales (e.g.
0.3rem) instead of a squared “focus” block inside a rounded shell.
- Apply the same pattern to pricing SegmentedControl and the segmented
groups in consumption-distribution-chart, model-charts, and user-charts.
Verified: bun run typecheck (web/default)
* ✨ feat(pricing): enrich model details with uptime sparkline and API documentation
Add a compact 30-day uptime sparkline (OpenRouter-style bars + aggregate %) with
per-day tooltips, surface it in a status row under quick stats and in the
per-group performance table, and extend mock data so uptime series are stable
and optionally scoped by group.
Introduce an API tab with Shiki-highlighted code samples (cURL, Python,
TypeScript, JavaScript), endpoint-type switching, authentication guidance, a
supported-parameters table, and mock per-group RPM/TPM/RPD limits. Infer
vendor, tokenizer, license, and data-retention hints for a provider & data
privacy card on the Overview tab (capabilities/modalities stay with model
identity; rate limits stay with the API tab).
Update i18n for all new user-facing strings across en, zh, fr, ja, ru, and vi.
* 🏆 feat(rankings): add comprehensive rankings dashboard
Add a mock-data powered rankings experience with period tabs, model, app, and vendor leaderboards, market share and history charts, movers, new releases, and per-category sections while backend analytics are pending.
Link ranked models to pricing details and ranked vendors to filtered pricing results, and include localized copy for all supported frontend locales.
* fix(theme): correct theme preset selection state
- update Base UI Radio selectors to use data-checked/data-unchecked states.
- fix unchecked theme options still showing selected indicators.
- isolate the default theme preview tokens to prevent preset changes from leaking into it.
* fix(setup): correct usage mode radio state
- use Base UI data-checked/data-unchecked states for RadioGroup styling.
- hide radio indicators when options are unchecked to avoid setup page display issues.
- drive usage mode card and icon selection styles from Base UI state.
* fix(auth): submit sign-in and sign-up forms
* 🎨 refactor: Align default theme with shadcn Base Nova and prune legacy customization
Migrate shadcn UI to Base UI primitives via CLI (`base-nova` / `components.json`)
and reinstall full component registry with `--overwrite`, including Hugeicons-backed
widgets and newly added registry components.
- Remove custom multi-preset/theme-radius system (`ThemeCustomizationProvider`, cookies,
preset UI from config drawer); rely on official semantic CSS tokens + light/dark only.
- Replace `theme.css` with shadcn’s documented neutral `:root`/`.dark` palette and
`@theme inline` mappings (plus skeleton token vars for existing shimmer usage).
- Update global styles for Base UI: collapsible animation uses `--collapsible-panel-height`;
clarify scroll-lock override comment.
Application compatibility:
- Keep minimal shims where app code diverged from official APIs (popover collision props,
combobox legacy `options` callers, Spinner prop typing).
- Switch interactive styling from Radix-era `data-state` / `--radix-*` selectors to Base UI
semantics (`data-open`, `data-popup-open`, `data-panel-open`, `--anchor-width`, etc.)
Tooling / docs / build:
- Rename Rsbuild vendor chunk grouping to `@base-ui` + transitive `@radix-ui`.
- Refresh AGENTS.md / CLAUDE.md / classic→default sync skill for Base UI stack.
- Bump `package.json` / lockfile for shadcn-postinstall deps (Hugeicons, chart stack, themes, etc.)
Verified: `bun run typecheck` passes.
Note: `bun run lint` still reports pre-existing hooks rule violations elsewhere;
not addressed in this change.
* 🎨 chore(web/default): unify table toolbar, relocate usage stats, refine filters
- Refactor DataTableToolbar to a single wrapping flex row with a
right-aligned action cluster (Reset / Search / View / Expand) for a
cleaner Ant Design Pro–style filter bar; remove the dedicated stats row
and the toolbar `stats` prop.
- Move Common Logs summary badges (Usage / RPM / TPM) and the sensitive-
data visibility toggle into the page header via CommonLogsHeaderActions
and SectionPageLayout.Actions so the toolbar stays focused on filters.
- Slim CommonLogsFilterBar props (no stats / preActions eye control).
- Improve CompactDateTimeRangePicker: show minute-precision labels on the
trigger (seconds omitted; aligns with datetime-local inputs); widen the
trigger on sm+ breakpoints so the full range is visible without truncation;
apply the same width in task logs filters.
- Simplify DataTableViewOptions: text-only “View” trigger, no sliders icon.
- Earlier layout tweak: extra top padding on SectionPageLayout scroll
content so control focus rings are not clipped by overflow.
* feat(web/default): Base UI migration and component foundation
Migrate from Radix UI to Base UI, rewrite core UI primitives,
update dependencies (recharts, date-fns, next-themes), add
shadcn agent skill documentation, and refresh AI element components.
This is the foundational work from the v2/localmain lineage that
was not covered by the individual feature commits above.
---------
Co-authored-by: t0ng7u <dev@aiass.cc>
Co-authored-by: QuentinHsu <xuquentinyang@gmail.com>
- Add System Settings entry to Admin sidebar group with Settings icon
- Register /system-settings URL mapping in sidebar config
- Remove 3-click hidden unlock from Frontend Theme setting, making it
directly visible in System Information section
- Port classic ParamOverrideEditorModal to default as standalone dialog (~3200 lines)
with two-panel layout, drag-to-reorder, 23 operation modes, template library,
visual/JSON dual mode, conditions management, and legacy format support
- Redesign channel drawer layout with clear visual hierarchy (CardHeading vs SubHeading)
and bordered sub-modules for Field Passthrough and Upstream Model Detection
- Replace header override JsonEditor with plain textarea matching classic behavior
- Add searchable channel type combobox with scroll fix
- Add 100+ i18n keys across all 6 locales (en, zh, fr, ja, ru, vi)
- Renamed "Models" to "Model Call Analytics" and updated descriptions for clarity.
- Introduced a new state for top user limits in user charts, allowing users to select the number of top users displayed.
- Enhanced user charts to include total call count display and improved data processing for better analytics.
- Added new translations for updated terms in multiple languages to support internationalization.
EPay allows users to switch payment methods (e.g. wxpay→alipay) during
checkout, causing callback rejection. Replace fragile blocklist guard
with a PaymentProvider field on TopUp and SubscriptionOrder that
identifies which gateway created the order.
- quota.go: add missing SettleBilling call in PostWssConsumeQuota
- text_quota.go: gate InjectTieredBillingInfo on tieredBillingApplied bool
instead of tieredResult != nil, so fallback billing still logs metadata
- price.go: remove quotaBeforeGroup == 0 from freeModel condition to avoid
bypassing settlement for output-only expressions
- tiered_settle.go: split cc/cc1h subtraction using UsageSemantic to
distinguish OpenAI vs Claude cache creation token formats
- pricing.go: only set BillingMode when a non-empty expression exists
- useModelPricingEditorState.js: only write billing_mode when
finalBillingExpr is non-empty
- render.jsx: change const destructuring of completionRatio/audioRatio to
use raw names with ?? 0 defaults, preventing "Assignment to constant
variable" errors in renderModelPrice, renderAudioModelPrice, and
renderClaudeModelPrice
- TieredPricingEditor.jsx: add missing MATCH_GTE import, remove misleading
alias help text, preserve conditions for single-tier configs
Remove the intermediate branch that recomputed quota from
EstimatedQuotaBeforeGroup when tieredResult is nil. This discarded the
FinalPreConsumedQuota fallback that TryTieredSettle already selected.
Now the error path simply adds tool surcharges to the passed-in
tieredQuota, preserving the existing fallback semantics.
Also removes unrelated mise.toml and adds a test covering the error
fallback with a pre-consumed quota that differs from the estimate.
Enable Redis requirepass in the compose template and embed the matching
credential in REDIS_CONN_STRING, aligning with the existing PostgreSQL
default password pattern so out-of-the-box deployments are not left with
an unauthenticated Redis instance.
Introduce NODE_NAME environment variable to identify node identity in top-up
audit logs, improving readability over auto-detected container internal IPs
in Docker/K8s deployments. Surface node_name in admin expanded log rows and
add it as a commented example to docker-compose.yml.
Top-up logs written by pre-upgrade instances have no admin_info, which
made the expanded row empty and the row un-expandable. For admins, always
emit an entry: either the audit fields from admin_info when present, or a
warning prompting the operator to upgrade the instance so audit fields
(server IP, callback IP, payment method, system version) are recorded.
Admin username/ID was embedded directly into the log Content for
quota changes and forced 2FA disable, leaking the operator's
identity to the target user via their own usage log page.
Move operator info into Other.admin_info so formatUserLogs strips
it for non-admin viewers, and render it in the expand panel only
for admins as "操作管理员".
Closes#4301
Apply the same LIKE sanitization used for token search to SearchUserTopUps
and SearchAllTopUps (reject %%, cap % count, require >=2 stripped chars,
use ESCAPE '!') and bound COUNT with a 10000-row hard limit to avoid
unbounded full-table scans.
Also restrict user-facing list and search (GetUserTopUps, SearchUserTopUps)
to records within the last 30 days via create_time. Admin endpoints
(GetAllTopUps, SearchAllTopUps) remain unrestricted.
When an admin disables/deletes/promotes/demotes a user via ManageUser,
explicitly evict the user cache and all of the user's token caches from
Redis. This prevents a disabled user from continuing to make successful
API requests until the user cache TTL expires, and ensures subsequent
requests reload fresh status from the database.
Thread caller IP from webhook/admin controllers through model recharge
functions and record a new RecordTopupLog entry with admin_info (server
IP, caller IP, order payment method, callback payment method, system
version). Frontend shows these fields in the expanded log row and the
IP column for admins on top-up logs, while non-admins continue to see
admin_info stripped by formatUserLogs.