* ci: create docker automation
* ✨ feat: add subscription billing system with admin management and user purchase flow
Implement a new subscription-based billing model alongside existing metered/per-request billing:
Backend:
- Add subscription plan models (SubscriptionPlan, SubscriptionPlanItem, UserSubscription, etc.)
- Implement CRUD APIs for subscription plan management (admin only)
- Add user subscription queries with support for multiple active/expired subscriptions
- Integrate payment gateways (Stripe, Creem, Epay) for subscription purchases
- Implement pre-consume and post-consume billing logic for subscription quota tracking
- Add billing preference settings (subscription_first, wallet_first, etc.)
- Enhance usage logs with subscription deduction details
Frontend - Admin:
- Add subscription management page with table view and drawer-based edit form
- Match UI/UX style with existing admin pages (redemption codes, users)
- Support enabling/disabling plans, configuring payment IDs, and model quotas
- Add user subscription binding modal in user management
Frontend - Wallet:
- Add subscription plans card with current subscription status display
- Show all subscriptions (active and expired) with remaining days/usage percentage
- Display purchasable plans with pricing cards following SaaS best practices
- Extract purchase modal to separate component matching payment confirm modal style
- Add skeleton loading states with active animation
- Implement billing preference selector in card header
- Handle payment gateway availability based on admin configuration
Frontend - Usage Logs:
- Display subscription deduction details in log entries
- Show step-by-step breakdown of subscription usage (pre-consumed, delta, final, remaining)
- Add subscription deduction tag for subscription-covered requests
* ✨ feat(admin): add user subscription management and refine UI/pagination
Add admin APIs to list/create/invalidate/delete user subscriptions
Add model helpers to fetch all user subscriptions (incl. expired) and support cancel/hard-delete
Wire new admin routes for user subscription operations
Replace “Bind subscription plan” entry with a dedicated User Subscriptions SideSheet in Users table
Use CardTable with responsive layout and working client-side pagination inside the SideSheet
Improve subscription purchase modal empty-gateway state with a Banner notice
* ✨ feat(admin): streamline subscription plan benefits editor with bulk actions
Restore the avatar/icon header for the “Model Benefits” section
Replace scattered controls with a compact toolbar-style workflow
Support multi-select add with a default quota for new items
Add row selection with bulk apply-to-selected / apply-to-all quota updates
Enable delete-selected to manage benefits faster and reduce mistakes
* ✨ fix(subscription): finalize payments, log billing, and clean up dead code
Complete subscription orders by creating a matching top-up record and writing billing logs
Add Epay return handler to verify and finalize browser callbacks
Require Stripe/Creem webhook configuration before starting subscription payments
Show subscription purchases in topup history with clearer labels/methods
Remove unused subscription helper, legacy Creem webhook struct, and unused topup fields
Simplify subscription self API payload to active/all lists only
* 🎨 style: format all code with gofmt and lint:fix
Apply consistent code formatting across the entire codebase using
gofmt and lint:fix tools. This ensures adherence to Go community
standards and improves code readability and maintainability.
Changes include:
- Run gofmt on all .go files to standardize formatting
- Apply lint:fix to automatically resolve linting issues
- Fix code style inconsistencies and formatting violations
No functional changes were made in this commit.
* ✨ feat(subscription): add quota reset periods and admin configuration
- Add reset period fields on subscription plans and user items
- Apply automatic quota resets during pre-consume based on plan schedule
- Expose reset-period configuration in the admin plan editor
- Display reset cadence in subscription cards and purchase modal
- Validate custom reset seconds on plan create/update
* ✨ feat(subscription): harden subscription billing with resets, idempotency, and production-grade stability
Add plan-level quota reset periods and display/reset cadence in admin/UI
Enforce natural reset alignment with background reset task and cleanup job
Make subscription pre-consume/refund idempotent with request-scoped records and retries
Use database time for consistent resets across multi-instance deployments
Harden payment callbacks with locking and idempotent order completion
Record subscription purchases in topup history and billing logs
Optimize subscription queries and add critical composite indexes
* ✨ feat(subscription): cache plan lookups and stabilize pre-consume
Introduce hybrid caches for subscription plans, items, and plan info with explicit
invalidation on admin updates. Streamline pre-consume transactions to reduce
redundant queries while preserving idempotency and reset logic.
* 🐛 fix(subscription): avoid pre-consume lookup noise
Use a RowsAffected check for the idempotency lookup so missing records
no longer surface as "record not found" errors while preserving behavior.
* 🔧 ci: Change workflow trigger to sub branch
Update the Docker image workflow to run on pushes to the sub branch instead of main.
* 💸 chore: Align subscription pricing display with global currency settings
Unify subscription price rendering to use the site-wide currency symbol/rate on the wallet and admin views.
Make subscription plan currency read-only in the editor and force USD on create/update to avoid drift.
Use global currency display type when creating Creem checkout payloads.
* 🔧 chore: Unify subscription plan status toggle with PATCH endpoint
Replace separate enable/disable flows with a single PATCH API that updates the enabled flag.
Update frontend hooks and table actions to call the unified endpoint and keep UI behavior consistent.
Introduce a minimal admin controller handler and route for the status update.
* ✨ feat: Add subscription limits and UI tags consistency
Add per-plan purchase limits with backend enforcement and UI disable states.
Expose limit configuration in admin plan editor and show limits in plan tables/cards.
Refine subscription UI tags with unified badge style and streamlined “My Subscriptions” layout.
* 🎨 style: tag color to white
* 🚀 refactor: Simplify subscription quota to total amount model
Remove per-model subscription items and switch to a single total quota per plan and user subscription. Update billing, reset, and logging flows to operate on total quota, and refactor admin/user UI to configure and display total quota consistently.
* 🚀 chore: Remove duplicate subscription usage percentage display
Keep the usage percentage shown only in the total quota line to avoid redundant “已用 0%” text while preserving remaining days in the summary.
* ✨ feat: Add subscription upgrade group with auto downgrade
* ✨ feat: Update subscription purchase modal display
Show total quota as currency with tooltip for raw quota, hide reset cycle when never, and display upgrade group when configured to match card display rules.
* ✨ feat: Extract quota conversion helpers to shared utils
Move quota display/conversion helpers into web/src/helpers/quota.js and update the subscription plan editor to import and use the shared utilities instead of inline functions.
* ✨ chore: Add upgrade group guidance in subscription editor
Add explanatory helper text under the upgrade group field to clarify automatic group upgrades, rollback conditions, and the expected delay before downgrading takes effect.
* 🔧 chore: remove unused Creem settings state
Drop the unused originInputs state and redundant updates to keep the Creem
settings form state minimal and easier to maintain.
* 🚀 chore: Remove useless action
* ✨ Add full i18n coverage for subscription-related UI across locales
* ✨ feat: harden subscription billing and improve UI consistency
Improve subscription payment safety and data integrity by handling user/URL lookup failures, fixing Stripe subscription mode, persisting quota reset fields, and correcting subscription delta accounting and DB timestamp casting. Refine the UI with stricter custom duration validation, accurate currency rounding, conditional Epay labeling, rollback on preference update failure, and shared subscription formatting helpers plus clearer component naming.
* 🔧 fix: make epay webhook and return flow subscription-aware
Ensure Epay webhook acknowledges success only after order completion, returning fail on processing errors to allow retries. Redirect subscription payment returns to the subscription page instead of top-up for correct user flow.
* 🚦 fix: guard epay return success on order completion
Redirect subscription return flow to failure when order completion fails, preventing false success states after payment verification.
* 🔧 fix: normalize epay error handling and webhook retries
Standardize SubscriptionRequestEpay error responses via ApiErrorMsg for a consistent schema.
Return "fail" on non-success trade statuses in the epay webhook to preserve retry behavior.
* 🧾 fix: persist epay orders before purchase
Create the subscription order before initiating epay payment and expire it if the provider call fails, preventing orphaned transactions and improving reconciliation.
* 🔧 fix: harden epay callbacks and billing fallbacks
Use POST and form parsing for epay notify/return routes, persist epay orders before provider calls with expiry on failure, and ensure notify handlers retry correctly.
Restrict subscription-first fallback to insufficient-subscription errors and log refund failures after retries to avoid silent quota drift.
* 🔧 fix: harden billing flow and sidebar settings
Add missing strings import for subscription fallback checks, log failed subscription refunds after retries, and extend sidebar module settings with a subscription management toggle plus translations.
* 🛡️ fix: fail fast on epay form parse errors
Handle ParseForm errors in epay notify/return handlers by returning fail or redirecting to failure, avoiding unsafe fallback to query parameters.
* ✨ fix: refine Japanese subscription status labels
Adjust Japanese UI wording for active-count labels to read more naturally and consistently.
* ✅ fix: standardize epay success response schema
Return subscription epay pay success responses via ApiSuccess to include the consistent success field and align with error schema.
* fix: channel affinity log styles
* fix: Issue with incorrect data storage when switching key sources
* feat: support not retrying after a single rule configuration fails
* fix: render channel affinity tooltip as multiline content
* feat: channel affinity cache hit
* fix: prevent ChannelAffinityUsageCacheModal infinite loading and hide data before fetch
* chore: format backend with gofmt and frontend with prettier/eslint autofix
- Replace blanket console route footer hiding with specific page targeting
- Only hide footer on pages that use CardPro component:
* /console/channel (channels management)
* /console/log (usage logs)
* /console/redemption (redemption codes)
* /console/user (user management)
* /console/token (token management)
* /console/midjourney (midjourney logs)
* /console/task (task logs)
* /console/models (model management)
* /pricing (pricing page)
- Footer now displays on other console pages (dashboard, settings, topup, etc.)
- Improves UI consistency by showing footer where CardPro's internal pagination isn't used
This change ensures footer is only hidden when CardPro component provides its own
pagination/footer functionality, while preserving footer visibility on other pages
that benefit from the global footer navigation.
- Ran: bun run eslint:fix && bun run lint:fix
- Inserted AGPL license header via eslint-plugin-header
- Enforced no-multiple-empty-lines and other lint rules
- Formatted code using Prettier v3 (@so1ve/prettier-config)
- No functional changes; formatting-only baseline across JS/JSX files
- Rename React components/pages/utilities that contain JSX to `.jsx` across `web/src`
- Update import paths and re-exports to match new `.jsx` extensions
- Fix Vite entry by switching `web/index.html` from `/src/index.js` to `/src/index.jsx`
- Verified remaining `.js` files are plain JS (hooks/helpers/constants) and do not require JSX
- No runtime behavior changes; extension and reference alignment only
Context: Resolves the Vite pre-transform error caused by the stale `/src/index.js` entry after migrating to `.jsx`.
- Remove K/M switch from model price column header in pricing table
- Add "Display in K units" option to pricing display settings panel
- Update parameter passing for tokenUnit and setTokenUnit across components:
- PricingDisplaySettings: Add tokenUnit toggle functionality
- PricingSidebar: Pass tokenUnit props to display settings
- PricingFilterModal: Include tokenUnit in mobile filter modal
- Enhance resetPricingFilters utility to reset token unit to default 'M'
- Clean up PricingTableColumns by removing unused setTokenUnit parameter
- Add English translation for "按K显示单位" as "Display in K units"
This change improves UX by consolidating all display-related controls
in the filter settings panel, making the interface more organized and
the token unit setting more discoverable alongside other display options.
Affected components:
- PricingTableColumns.js
- PricingDisplaySettings.jsx
- PricingSidebar.jsx
- PricingFilterModal.jsx
- PricingTable.jsx
- utils.js (resetPricingFilters)
- en.json (translations)
- Increase skeleton card count from 6 to 10 for better visual coverage
- Extend minimum skeleton display duration from 500ms to 1000ms for smoother UX
- Add circle shape to all pricing tags for consistent rounded design
- Apply circle styling to billing type, popularity, endpoint, and context tags
This commit improves the visual consistency and user experience of the pricing
card view by standardizing tag appearance and optimizing skeleton loading timing.
- Add left-right pagination layout for desktop (total info on left, controls on right)
- Keep mobile layout centered with pagination controls only
- Implement proper i18n support for pagination text using react-i18next
- Add pagination translations for Chinese and English
- Standardize t function usage across all table components to use xxxData.t pattern
- Update CardPro footer layout to support justify-between on desktop
- Use CSS variable --semi-color-text-2 for consistent text styling
- Disable built-in Pagination showTotal to avoid duplication
Components updated:
- CardPro: Enhanced footer layout with responsive design
- createCardProPagination: Added i18n support and custom total text
- All table components: Unified t function usage pattern
- i18n files: Added pagination-related translations
The pagination now displays "Showing X to Y of Z items" on desktop
and maintains existing centered layout on mobile devices.
Fix "Rendered fewer hooks than expected" error caused by conditional hook calls
in createCardProPagination function. The issue occurred when paginationArea was
commented out, breaking React's hooks rules.
**Problem:**
- createCardProPagination() internally called useIsMobile() hook
- When paginationArea was disabled, the hook was not called
- This violated React's rule that hooks must be called in the same order on every render
**Solution:**
- Refactor createCardProPagination to accept isMobile as a parameter
- Move useIsMobile() hook calls to component level
- Ensure consistent hook call order regardless of pagination usage
**Changes:**
- Update createCardProPagination function to accept isMobile parameter
- Add useIsMobile hook calls to all table components
- Pass isMobile parameter to createCardProPagination in all usage locations
**Files modified:**
- web/src/helpers/utils.js
- web/src/components/table/channels/index.jsx
- web/src/components/table/redemptions/index.jsx
- web/src/components/table/usage-logs/index.jsx
- web/src/components/table/tokens/index.jsx
- web/src/components/table/users/index.jsx
- web/src/components/table/mj-logs/index.jsx
- web/src/components/table/task-logs/index.jsx
Fixes critical runtime error and ensures stable pagination behavior across all table components.
- Redesign modal layout from single column to responsive two-column grid
- Add new user information fields: display name, user group, invitation code,
invitation count, invitation quota, and remarks
- Implement Badge components with color-coded categories for better visual hierarchy:
* Primary (blue): basic identity info (username, display name)
* Success (green): positive/earning info (balance, invitation quota)
* Warning (orange): usage/consumption info (used quota, request count)
* Tertiary (gray): supplementary info (user group, invitation details, remarks)
- Optimize spacing and typography for better readability:
* Reduce row spacing from 24px to 16px
* Decrease font size from 16px to 14px for values
* Adjust label margins from 4px to 2px
- Implement conditional rendering for optional fields
- Add proper text wrapping for long remarks content
- Reduce overall modal height while maintaining information clarity
This update significantly improves the user experience by presenting
comprehensive user information in a more organized and visually appealing format.
Summary
1. CardTable
• Added collapsible “Details / Collapse” section on mobile cards using Semi-UI Button + Collapsible with chevron icons.
• Integrated i18n (`useTranslation`) for the toggle labels.
• Restored original variable-width skeleton placeholders (50 % / 60 % / 70 % …) for more natural loading states.
2. UsageLogsColumnDefs
• Wrapped each `Tag` inside a native `<span>` when used as Tooltip trigger, removing `findDOMNode` deprecation warnings in React StrictMode.
Impact
• Cleaner, shorter rows on small screens with optional expansion.
• Fully translated UI controls.
• No more console noise in development & CI caused by StrictMode warnings.
Summary
• Swapped out the obsolete `<Spin>` loader for a modern, animated Semi-UI `<Skeleton>` implementation in `UsageLogsActions.jsx`.
Details
1. Added animated Skeleton placeholders mirroring real Tag sizes (108 × 26, 65 × 26, 64 × 26).
2. Introduced `showSkeleton` state with 500 ms minimum display to eliminate flicker.
3. Leveraged existing `showStat` flag to decide when real data is ready.
4. Ensured only the three Tags are under loading state - `CompactModeToggle` renders immediately.
5. Adopted CardTable‐style `Skeleton` pattern (`loading` + `placeholder`) for consistency.
6. Removed all references to the original `Spin` component.
Outcome
A smoother and more consistent loading experience across devices, aligning UI behaviour with the project’s latest Skeleton standards.
1. Add `web/src/components/common/ui/CardTable.js`
• Renders Semi-UI `Table` on desktop; on mobile, transforms each row into a rounded `Card`.
• Supports all standard `Table` props, including `rowSelection`, `scroll`, `pagination`, etc.
• Adds mobile pagination via Semi-UI `Pagination`.
• Implements a 500 ms minimum, active Skeleton loader that mimics real column layout (including operation-button row).
2. Replace legacy `Table` with `CardTable`
• Updated all major data pages: Channels, MJ-Logs, Redemptions, Tokens, Task-Logs, Usage-Logs and Users.
• Removed unused `Table` imports; kept behaviour on desktop unchanged.
3. UI polish
• Right-aligned operation buttons and sensitive fields (e.g., token keys) inside mobile cards.
• Improved Skeleton placeholders to better reflect actual UI hierarchy and preserve the active animation.
These changes dramatically improve the mobile experience while retaining full functionality on larger screens.
Summary
-------
Introduce a reusable compact-mode toggle component and greatly improve the CardPro header for small screens. Removes duplicated code, adds i18n support, and refines overall responsiveness.
Details
-------
🎨 UI / Components
• Create `common/ui/CompactModeToggle.js`
– Provides a single source of truth for switching between “Compact list” and “Adaptive list”
– Automatically hides itself on mobile devices via `useIsMobile()`
• Refactor table modules to use the new component
– `Users`, `Tokens`, `Redemptions`, `Channels`, `TaskLogs`, `MjLogs`, `UsageLogs`
– Deletes legacy in-file toggle buttons & reduces repetition
📱 CardPro improvements
• Hide `actionsArea` and `searchArea` on mobile, showing a single “Show Actions / Hide Actions” toggle button
• Add i18n: texts are now pulled from injected `t()` function (`显示操作项` / `隐藏操作项` etc.)
• Extend PropTypes to accept the `t` prop; supply a safe fallback
• Minor cleanup: remove legacy DOM observers & flag CSS, simplify logic
🔧 Integration
• Pass the `t` translation function to every `CardPro` usage across table pages
• Remove temporary custom class hooks after logic simplification
Benefits
--------
✓ Consistent, DRY compact-mode handling across the entire dashboard
✓ Better mobile experience with decluttered headers
✓ Full translation support for newly added strings
✓ Easier future maintenance (single compact toggle, unified CardPro API)