Summary:
• Imported InputNumber from @douyinfe/semi-ui.
• Swapped plain Input for InputNumber in “Add Quota” modal.
• Added UX tweaks: full-width styling, showClear, step = 500 000.
• Initialized addQuotaLocal to an empty string so the field starts blank.
• Adjusted state handling and kept quota calculation logic unchanged.
This improves numeric input accuracy and overall user experience without breaking existing functionality.
* backend
- constant/endpoint_type.go
• Add EndpointTypeMidjourney, EndpointTypeSuno, EndpointTypeKling, EndpointTypeJimeng.
- common/endpoint_type.go
• Map Midjourney / MidjourneyPlus, SunoAPI, Kling, Jimeng channel types to the new endpoint types.
* frontend
- ModelPricing.js
• Add “Supported Endpoint Type” column.
• Implement renderSupportedEndpoints with `stringToColor` for consistent tag colors.
These changes allow `/api/pricing` and model lists to return accurate
`supported_endpoint_types` covering all non-OpenAI providers and display
them clearly in the UI.
No breaking changes.
Summary:
The redemption list occasionally displayed an invalid range such as “Items -9 - 0” and failed to highlight page 1 after a refresh. This was caused by the table being initialized with `currentPage = 0`.
Changes:
• update `useEffect` to load data starting from page 1 instead of page 0
• refactor `loadRedemptions` to accept `page` (default 1) and sanitize backend‐returned pages (`<= 0` coerced to 1)
• keep other logic unchanged
Impact:
Pagination text and page selection now show correct values on first load or refresh, eliminating negative ranges and ensuring the first page is properly highlighted.
Previously, the table did not enter the loading state after performing actions such as deleting, enabling, or disabling a redemption code. This caused a brief period where the UI appeared unresponsive while awaiting the backend response.
Changes made:
• Added `setLoading(true)` at the beginning of `loadRedemptions` to activate the loading spinner whenever data is (re)fetched.
• Added an explanatory code comment to clarify the intent.
This improves user experience by clearly indicating that the system is processing and prevents confusion during data refresh operations.
• Removed the custom `renderArrow` helper and its `Dropdown`-based arrow navigation, simplifying the component logic.
• Switched the `<Tabs>` component to rely on Semi UI’s built-in behaviour (no more `renderArrow` override).
• Kept `type="card"` and `collapsible` props for consistent visual appearance while still using the default style.
• Eliminated the now-unused `Dropdown` import.
This cleanup reduces bespoke UI code, makes future maintenance easier, and keeps the interface consistent with the rest of the application.
• Added read-only Base URL input that shows `status.server_address` (fallback `window.location.origin`) and copies value on click.
• Embedded `ScrollList` as input `suffix`; auto-cycles common endpoints every 3 s and allows manual selection.
• Introduced `API_ENDPOINTS` array in `web/src/constants/common.constant.js` for centralized endpoint management.
• Implemented custom CSS to hide ScrollList wheel indicators / scrollbars for a cleaner look.
• Created two blurred colour spheres behind the banner (`blur-ball-indigo`, `blur-ball-teal`) with light-/dark-mode opacity tweaks and lower vertical placement.
• Increased letter-spacing for Chinese heading via conditional `tracking-wide` / `md:tracking-wider` classes to improve readability.
• Misc: updated imports, helper functions, and responsive sizes to keep UI consistent across devices.
• Removed the dropdown menu previously used for tag-level operations.
• Added a standalone “Edit” button directly after the “Disable All” button, reducing the number of clicks required to edit a tag group.
• Deleted the now-unused `IconEdit` import and its icon reference.
This streamlines the tag management flow and keeps the UI cleaner and more accessible.
All operation-related UI controls in `ChannelsTable` (buttons, dropdowns,
switches, inputs, tags, etc.) now explicitly use `size="small"`.
Reasons & benefits:
- Creates a more compact and consistent look across the table and modals.
- Improves visual coherence between desktop and mobile views.
- Purely presentational; no functional logic is affected.
No database changes or API interactions are involved.
* ChannelsTable
- Added row-level checkboxes to the model-testing table for multi-selection
- Implemented cross-page “Select All / Deselect All” via rowSelection.onSelectAll
- Introduced allSelectingRef to ignore redundant onChange after onSelectAll
- Added “Copy Selected” button to copy chosen model names (comma-separated) using helpers.copy
- Added “Select Successful” button to auto-tick all models that passed testing
- Moved search bar and new action buttons into the modal title for better UX
- Centralised page size constant MODEL_TABLE_PAGE_SIZE in channel.constants.js
- Fixed pagination slicing and auto-page-switch logic during batch testing
* channel.constants
- Exported MODEL_TABLE_PAGE_SIZE (default 10) for unified pagination control
This commit enables users to conveniently copy or filter successful models, fully supports cross-page bulk operations, and resolves previous selection inconsistencies.
Refs: #1288
Frontend (`ChannelsTable.js`)
1. Initialize `loading` state to `true` so the spinner is visible while the first data request is in-flight.
2. Set `<Table>` prop `loading={loading || searching}` — the spinner now appears for both the initial load and any subsequent search requests.
Result
Users immediately see a loading indicator on page entry and whenever a search is running, improving perceived responsiveness.
1. Backend
• `controller/channel.go`
– Added pagination (`p`, `page_size`) support to `SearchChannels`.
– Added independent `type` filter (keeps `type_counts` unaffected).
– Returned `total`, `type_counts` to match `/api/channel/` response.
2. Frontend
• `ChannelsTable.js`
– `loadChannels` / `searchChannels` now pass `p`, `page_size`, `id_sort`, `type`, `status` correctly.
– Pagination, page-size selector and type tabs work for both normal list and search mode.
– Switch for “ID sort” calls proper API and keeps UI state in sync.
– Removed unnecessary `normalize` helper; `getFormValues` back to concise form.
Result
• Search mode and normal listing now share identical pagination and filtering behavior.
• Type tabs show correct counts even after searching.
• “ID Sort” toggle no longer inverses actual behaviour.
Refactors `ChannelsTable.js` to ensure that the selected group filter is **never lost** when:
1. Cycling between channel-type tabs.
2. Changing the status dropdown (all / enabled / disabled).
Key points:
• `loadChannels` now detects active search filters (keyword / group / model) and transparently delegates to `searchChannels`, guaranteeing all parameters are sent in every request.
• `searchChannels` accepts optional `typeKey` and `statusF` arguments, enabling reuse without code duplication.
• Loading state handling is unified; no extra renders or side effects were introduced, keeping UI performance intact.
• Duplicate logic removed and responsibilities clearly separated for easier future maintenance.
Replaced the verbose placeholder “Search channel ID, name, key and API address ...”
with a concise version “Channel ID, name, key, API address” in
`ChannelsTable.js` and synchronized the corresponding i18n entries.
This improves readability and keeps UI text consistent across languages.
Ensure that the selected "group" filter (and other form search values) persist across
type tab changes, status filter updates, pagination, and page-size changes.
Changes include:
• loadChannels: added `searchParams` argument and now appends keyword, group and model
query strings to API calls.
• refresh / page handlers / type tabs / status Select: now pass current form values
to loadChannels, keeping filters intact.
• searchChannels: maintains active type and status filters when issuing search requests.
• Form.Select (searchGroup): triggers loadChannels when only group filter is active,
preventing parameter loss.
• Minor cleanup and comment adjustments.
WHAT’S NEW
• Backend
– Introduced `parseStatusFilter` helper to normalize `status` query across handlers.
– `GET /api/channel` & `GET /api/channel/search` now accept `status=enabled|disabled` to return only enabled or disabled channels.
– Tag-mode branch respects both `statusFilter` and `typeFilter`; SQL paths trimmed to one query + one lightweight `GROUP BY` for `type_counts`.
• Frontend (`ChannelsTable.js`)
– Added “Status Filter” `<Select>` (All / Enabled / Disabled) with localStorage persistence.
– All data-loading and search requests now always append `type` (when not “all”) and `status` params, so filtering & pagination are handled entirely server-side.
– Removed client-side post-filtering for type, preventing short pages and reducing CPU work.
– Tabs’ type counts stay in sync via backend-provided `type_counts`.
IMPROVEMENTS
• Eliminated duplicated status-parsing logic; single source of truth eases future extension.
• Reduced redundant queries, improved consistency of counts in UI.
• Secured key leakage with `Omit("key")` unchanged; no perf regressions observed.
Closes#1289
Summary
• Added per-table “Compact / Adaptive” view toggle to all major table components (Tokens, Channels, Logs, MjLogs, TaskLogs, Redemptions, Users).
• Persist user preference in a single localStorage entry (`table_compact_modes`) instead of scattered keys.
Details
1. utils.js
• Re-implemented `getTableCompactMode` / `setTableCompactMode` to read & write a shared JSON object.
• Imported storage-key constant from `constants`.
2. hooks/useTableCompactMode.js
• Hook now consumes the unified helpers and listens to `storage` events via the shared key constant.
3. constants
• Added `TABLE_COMPACT_MODES_KEY` to `common.constant.js` and re-exported via `constants/index.js`.
4. Table components
• Integrated `useTableCompactMode('<tableName>')`.
• Dynamically remove `fixed: 'right'` column and horizontal `scroll` when in compact mode.
• UI: toggle button placed at card title’s right; responsive layout on small screens.
5. UI polish
• Replaced all lucide-react `List`/`ListIcon` usages with Semi UI `IconDescend` for consistency.
• Restored correct icons where `Hash` was intended (TaskLogsTable).
Benefits
• Consistent UX for switching list density across the app.
• Cleaner localStorage footprint with easier future maintenance.
This commit enhances the “Copy Selected Tokens to Clipboard” feature in `TokensTable.js` by introducing a user-friendly modal that lets users choose how they want to copy tokens.
Changes made
• Replaced direct copy logic with a `Modal.info` dialog.
• Modal displays the prompt “Please choose your copy mode”.
• Added two buttons in a custom footer:
– **Name + Secret**: copies `tokenName sk-tokenKey`.
– **Secret Only**: copies `sk-tokenKey`.
• Each button triggers the copy operation and closes the dialog.
• Maintains existing validations (e.g., selection check, clipboard feedback).
Benefits
• Gives users clear control over copy format, reducing manual editing.
• Aligns UI with Semi UI’s best practices via custom modal footer.
No backend/API changes are involved; all updates are limited to the front-end UI logic.
Backend
- controller/ratio_sync.go
• Parse /api/pricing response and convert to ratio / price maps.
• Introduce confidence heuristic (model_ratio = 37.5 && completion_ratio = 1) to flag unreliable data.
• Include confidence map when building differences and filter “same”/empty entries.
- dto/ratio_sync.go
• Add `ID` to UpstreamDTO, `upstreams` to UpstreamRequest, and `Confidence` to DifferenceItem.
Frontend
- ChannelSelectorModal.js
• Re-implement with table layout, pagination, search, endpoint-type selector and mobile support.
- UpstreamRatioSync.js
• Send full upstream objects, add ratio-type filter, confidence badges/tooltips, retain endpoints.
• Leverage ChannelSelectorModal’s pagination reset.
- ChannelsTable.js – fix tag color for disabled status.
- en.json – add translations for new UI labels.
Motivation
These changes let users sync model ratios / prices from different upstream endpoints and visually identify potentially unreliable data, improving operational safety and flexibility.
frontend(ChannelsTable):
• Do not render type-filter Tabs when `enableTagMode` is true, preventing UI/logic conflicts in tag aggregation view.
• Adjust API query construction:
– Append `type=` param only when NOT in tag mode and selected tab ≠ 'all'.
– Applies to both `loadChannels` and `searchChannels`.
• Result: UI stays clean in tag view, and backend receives correct parameters across modes.
No other functionality affected.
feat(api):
• Add optional `type` query param to `/api/channel` endpoint for type-specific pagination
• Return `type_counts` map with counts for each channel type
• Implement `GetChannelsByType`, `CountChannelsByType`, `CountChannelsGroupByType` in `model/channel.go`
feat(frontend):
• Introduce type Tabs in `ChannelsTable` to switch between channel types
• Tabs show dynamic counts using backend `type_counts`; “All” is computed from sum
• Persist active type, reload data on tab change (with proper query params)
perf(frontend):
• Use a request counter (`useRef`) to discard stale responses when tabs switch quickly
• Move all `useMemo` hooks to top level to satisfy React Hook rules
• Remove redundant local type counting fallback when backend data present
ui:
• Remove icons from response-time tags for cleaner look
• Use Semi-UI native arrow controls for Tabs; custom arrow code deleted
chore:
• Minor refactor & comments for clarity
• Ensure ESLint Hook rules pass
Result: Channel list now supports fast, accurate type filtering with correct counts, improved concurrency safety, and cleaner UI.
* backend
- model: add `Remark` field (varchar 255, `json:"remark,omitempty"`); AutoMigrate handles schema change automatically
- controller:
* accept `remark` on user create/update endpoints
* hide remark from regular users (`GetSelf`) by zero-ing the field before JSON marshalling
* clarify inline comment explaining the omitempty behaviour
* frontend (React / Semi UI)
- AddUser.js & EditUser.js: add “Remark” input for admins
- UsersTable.js:
* remove standalone “Remark” column
* show remark as a truncated Tag next to username with Tooltip for full text
* import Tooltip component
- i18n: reuse existing translations where applicable
This commit enables administrators to label users with private notes while ensuring those notes are never exposed to the users themselves.
Backend
• Introduced `validateExpiredTime` helper in `controller/redemption.go`; reused in both Add & Update endpoints to enforce “expiry time must not be earlier than now”, eliminating duplicated checks
• Removed `RedemptionCodeStatusExpired` constant and all related references – expiry is now determined exclusively by the `expired_time` field for simpler, safer state management
• Simplified `DeleteInvalidRedemptions`: deletes codes that are `used` / `disabled` or `enabled` but already expired, without relying on extra status codes
• Controller no longer mutates `status` when listing or fetching redemption codes; clients derive expiry status from timestamp
Frontend
• Added reusable `isExpired` helper in `RedemptionsTable.js`; leveraged for:
– status rendering (orange “Expired” tag)
– action-menu enable/disable logic
– row styling
• Removed duplicated inline expiry logic, improving readability and performance
• Adjusted toolbar layout: on small screens the “Clear invalid codes” button now wraps onto its own line, while “Add” & “Copy” remain grouped
Result
The codebase is now more maintainable, secure, and performant with no redundant constants, centralized validation, and cleaner UI behaviour across devices.
- Add informative header section to TokensTable with Key icon and description
- Replace generic eye icon with semantic Ticket icon in RedemptionsTable header
- Import additional UI components (Divider, Typography) for better layout structure
- Improve user experience with contextual information about token and redemption functionality
- Maintain consistent styling and layout between both table components
The changes provide users with clear understanding of each table's purpose:
- Tokens: "令牌用于API访问认证,可以设置额度限制和模型权限" with Key icon
- Redemptions: "兑换码可以批量生成和分发,适合用于推广活动或批量充值" with Ticket icon
- Add IP field to Log model with database index and default empty value
- Implement conditional IP recording based on user setting in RecordConsumeLog and RecordErrorLog
- Add UserSettingRecordIpLog constant and update user settings API to handle record_ip_log field
- Create dedicated "IP记录" tab in personal settings under "其他设置" section
- Add IP column to logs table with help tooltip explaining recording conditions
- Make IP column visible to all users (not admin-only) with proper filtering for consume/error log types
- Restrict display of use_time and retry columns to consume and error log types only
- Update personal settings UI structure: rename "通知设置" to "其他设置" to accommodate new functionality
- Add proper translation support and maintain consistent styling across components
The IP logging feature is disabled by default and only records client IP addresses
for consume (type 2) and error (type 5) logs when explicitly enabled by users
in their personal settings.
Fix the search button loading state to be consistent with other table components.
The search button now properly shows loading animation when the table data is
being fetched.
Changes:
- Update search button loading prop from `loading={searching}` to
`loading={loading || searching}` in TokensTable.js
- This ensures loading state is shown both when searching with keywords
(searching=true) and when loading default data (loading=true)
- Aligns with the behavior of other table components like ChannelsTable,
UsersTable, and RedemptionsTable
Before: Search button only showed loading when searching with keywords
After: Search button shows loading for all table data fetch operations
* Removed `size="middle"` and `centered` props from the column-selector
`Modal` in `ChannelsTable.js` to match the visual style used in
`LogsTable`.
* Re-added `size="middle"` to the main `Table` component to preserve the
original table sizing.
* Ensures consistent UI/UX across both channel and log column settings
modals.
- Remove IconForward import from @douyinfe/semi-icons
- Add Route icon import from lucide-react
- Update model redirection indicator in LogsTable component
The Route icon better represents the concept of model redirection
compared to the generic forward arrow, providing clearer visual
context for users when models are mapped to different upstream models.