Skip to content

Feat/user panel#362

Merged
ding113 merged 21 commits intodevfrom
feat/user-panel
Dec 19, 2025
Merged

Feat/user panel#362
ding113 merged 21 commits intodevfrom
feat/user-panel

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Dec 18, 2025

Summary

Major enhancement to the User Management page with a completely redesigned UI, new user/key editing capabilities, daily reset quota features, and comprehensive internationalization support across 5 languages.

Problem

The existing user management interface had several limitations:

  • Limited editing capabilities for users and their API keys
  • No flexible daily quota reset options (fixed time vs rolling window)
  • Difficult to manage multiple keys per user
  • Missing quick renewal functionality for user expiration
  • No onboarding experience for new users

Related Issues:

Solution

New Database Fields

  • daily_reset_mode enum ('fixed' | 'rolling') - Controls how daily limits reset
  • daily_reset_time varchar(5) - Configurable reset time (e.g., "00:00")
  • allowed_models jsonb - Model restriction list for users

New UI Components

  1. Unified Edit Dialog (unified-edit-dialog.tsx) - Comprehensive user/key editing in a single modal
  2. User Management Table (user-management-table.tsx) - Expandable rows showing user keys
  3. Quick Renewal Dialog (quick-renew-dialog.tsx) - Fast expiration extension with preset options
  4. Limit Rule Picker (limit-rule-picker.tsx) - Dynamic limit configuration UI
  5. Access Restrictions Section (access-restrictions-section.tsx) - Client and model restrictions
  6. Key Stats Dialog (key-stats-dialog.tsx) - Per-key usage statistics
  7. User Onboarding Tour (user-onboarding-tour.tsx) - Guided tour for new users

Key Features

  • Daily Reset Configuration: Choose between fixed time reset or 24h rolling window
  • Quick Renewal: One-click expiration extension (7d, 30d, 90d, 1y options)
  • Expandable Key Rows: View all keys under each user with inline editing
  • Provider Group Selection: Improved UI for assigning provider groups to keys
  • Model Restrictions: Restrict which AI models users can access
  • Comprehensive i18n: Full translation coverage for EN, JA, RU, ZH-CN, ZH-TW

Changes

Core Changes

  • src/drizzle/schema.ts - Added daily_reset_mode, daily_reset_time, allowed_models fields
  • src/actions/users.ts - Extended user CRUD with new fields and permissions
  • src/actions/keys.ts - Enhanced key management actions
  • src/lib/rate-limit/service.ts - Implemented rolling window daily limit logic

UI Components (New)

  • unified-edit-dialog.tsx - Main edit dialog (943 lines)
  • user-management-table.tsx - New table component (427 lines)
  • user-edit-section.tsx - User editing form (446 lines)
  • key-edit-section.tsx - Key editing form (395 lines)
  • key-row-item.tsx - Expandable key row (359 lines)
  • quick-renew-dialog.tsx - Quick renewal UI (270 lines)
  • limit-rule-picker.tsx - Dynamic limit configuration (286 lines)
  • access-restrictions-section.tsx - Client/model restrictions (156 lines)
  • danger-zone.tsx - Enable/disable/delete actions (183 lines)

Supporting Changes

  • src/repository/statistics.ts - Added key-level statistics queries
  • src/repository/user.ts - New user repository methods
  • src/lib/validation/schemas.ts - Added validation for new fields
  • src/components/ui/tag-input.tsx - Improved tag input component
  • messages/*/dashboard.json - ~340 new translation keys per locale

Breaking Changes

Change Impact Migration
New schema fields Database migration required Auto-migration adds columns with defaults
daily_reset_mode default New users default to 'fixed' reset Existing users unaffected

Testing

Automated Tests

  • API action integrity tests updated
  • User/key action tests updated
  • E2E tests for user-key flows updated

Manual Testing

  1. Create a new user via "Create User" button
  2. Verify onboarding tour appears
  3. Edit user and configure daily limit with rolling window
  4. Add multiple keys and verify expandable row display
  5. Use quick renewal to extend user expiration
  6. Configure model/client restrictions and verify enforcement

Checklist

  • Code follows project conventions
  • Self-review completed
  • Tests pass locally
  • Documentation updated (architecture docs updated)
  • All 5 locales have complete translations

Description enhanced by Claude AI

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ding113, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a robust and feature-rich user management panel, empowering administrators with comprehensive control over user accounts and API keys. It introduces flexible daily rate limiting, streamlines provider group assignments through automatic synchronization, and enhances the overall user experience with new, intuitive UI components and extensive internationalization support. The changes span database schema, backend actions, and a significant portion of the frontend dashboard.

Highlights

  • Comprehensive User Management Panel: Introduced a new, unified user management panel that allows administrators to create, edit, and manage users and their associated API keys. This includes detailed settings for limits, access restrictions, and provider groups.
  • Flexible Daily Rate Limiting: Implemented support for two daily rate limit reset modes: 'fixed' (reset at a specific time) and 'rolling' (a 24-hour window). This enhancement provides more granular control over user quotas.
  • Automated Provider Group Synchronization: Refactored the logic for provider group management. User-level provider groups are now automatically calculated as the union of all provider groups assigned to their API keys, simplifying administration and ensuring consistency.
  • Enhanced API Key Management: Added new functionalities for API keys, including the ability to enable/disable individual keys, view full key strings securely, and see detailed model usage statistics per key. Key provider group assignment is now an admin-only operation.
  • Improved User Interface Components: Developed several new UI components such as AccessRestrictionsSection, KeyEditSection, LimitRulePicker, QuickRenewDialog, and UserEditSection to provide a richer and more intuitive user experience for managing user and key properties.
  • Internationalization Updates: Extensive updates to translation files across English, Japanese, Russian, Simplified Chinese, and Traditional Chinese to support all new UI strings for the user management panel.
  • Database Schema Evolution: The users table schema was updated with new columns (daily_reset_mode, daily_reset_time) to support the enhanced daily rate limiting features.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added enhancement New feature or request size/XL Extra Large PR (> 1000 lines) labels Dec 18, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant enhancements to user and key management, focusing on granular control over quotas, access restrictions, and an improved UI. Key changes include adding daily_reset_mode and daily_reset_time columns to the users table for flexible daily quota resets (fixed or rolling window). The system now automatically synchronizes a user's providerGroup based on the union of their keys' providerGroups, removing the previous cascading update logic and restricting non-admin users from directly modifying providerGroup on keys. New UI components have been added for a unified user/key edit dialog, including sections for basic info, expiration, limit rules (5-hour, daily, weekly, monthly, total, concurrent sessions), and access restrictions (allowed clients and models). The UI also features a quick renew dialog, a danger zone for user actions, and an onboarding tour for new users. Backend actions were updated to support these new fields and logic, including new functions to fetch provider group counts and user limit usage for percentage display. Localization files across multiple languages were extensively updated to support all new UI texts and error messages. A review comment highlighted a potential client-side performance issue with filtering logic in UsersPageClient for large datasets, suggesting a backend migration for scalability. Another comment pointed out a hardcoded Chinese fallback error message, recommending an English default message for better localization consistency.

Comment on lines +117 to +184
const { filteredUsers, matchingKeyIds } = useMemo(() => {
const matchingIds = new Set<number>();
const normalizedTerm = searchTerm.trim().toLowerCase();
const hasSearch = normalizedTerm.length > 0;

const filtered: UserDisplay[] = [];

for (const user of users) {
// Collect matching key IDs for this user (before filtering)
const userMatchingKeyIds: number[] = [];

// Search filter: match user-level fields or any key fields
let matchesSearch = !hasSearch;

if (hasSearch) {
// User-level fields: name, note, tags, providerGroup
const userMatches =
user.name.toLowerCase().includes(normalizedTerm) ||
(user.note || "").toLowerCase().includes(normalizedTerm) ||
(user.tags || []).some((tag) => tag.toLowerCase().includes(normalizedTerm)) ||
(user.providerGroup || "").toLowerCase().includes(normalizedTerm);

if (userMatches) {
matchesSearch = true;
} else if (user.keys) {
// Key-level fields: name, maskedKey, fullKey, providerGroup
for (const key of user.keys) {
const keyMatches =
key.name.toLowerCase().includes(normalizedTerm) ||
key.maskedKey.toLowerCase().includes(normalizedTerm) ||
(key.fullKey || "").toLowerCase().includes(normalizedTerm) ||
(key.providerGroup || "").toLowerCase().includes(normalizedTerm);

if (keyMatches) {
matchesSearch = true;
userMatchingKeyIds.push(key.id);
// Don't break - collect all matching keys
}
}
}
}

// Tag filter
const matchesTag = tagFilter === "all" || (user.tags || []).includes(tagFilter);

return matchesSearch && matchesGroup && matchesTag;
});
}, [users, searchTerm, groupFilter, tagFilter]);
// Key group filter (check if any split tag matches the filter)
let matchesKeyGroup = keyGroupFilter === "all";
if (keyGroupFilter !== "all" && user.keys) {
for (const key of user.keys) {
if (splitTags(key.providerGroup).includes(keyGroupFilter)) {
matchesKeyGroup = true;
userMatchingKeyIds.push(key.id);
}
}
}

// Only add to results and matchingIds if user passes ALL filters
if (matchesSearch && matchesTag && matchesKeyGroup) {
filtered.push(user);
// Add matching key IDs only for users that pass the filter
for (const keyId of userMatchingKeyIds) {
matchingIds.add(keyId);
}
}
}

return { filteredUsers: filtered, matchingKeyIds: matchingIds };
}, [users, searchTerm, tagFilter, keyGroupFilter]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The filtering logic inside this useMemo hook iterates through all users and their keys on every change to the search term or filters. While this is acceptable for a small number of users, it could lead to performance degradation on the client-side as the number of users and keys grows significantly. For better scalability, consider moving the filtering and searching logic to the backend, where it can be optimized with database queries.

if (activeKeyCount <= 1) {
return {
ok: false,
error: tError("CANNOT_DISABLE_LAST_KEY") || "无法禁用最后一个可用密钥",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The fallback error message is hardcoded in Chinese. This can lead to inconsistent localization if the translation key CANNOT_DISABLE_LAST_KEY is not found. It's better to rely on next-intl's fallback mechanism by providing an English default message within the tError function call.

Suggested change
error: tError("CANNOT_DISABLE_LAST_KEY") || "无法禁用最后一个可用密钥",
error: tError("CANNOT_DISABLE_LAST_KEY", {}, { defaultMessage: "Cannot disable the last available key" }),

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

This is a large feature PR introducing a comprehensive user panel management system with user/key editing, limit rules, provider groups, and i18n translations. The PR touches 67 files across UI components, backend actions, database schema, and internationalization. Given the scope, the code is generally well-structured, but there are no significant issues that meet the high-confidence threshold requiring immediate attention.

PR Size: XL

  • Lines changed: 7761 (7442 additions, 319 deletions)
  • Files changed: 67

Split Suggestion: For future PRs of this size, consider splitting into:

  1. Database schema changes and migrations
  2. Backend actions (users.ts, keys.ts, providers.ts)
  3. UI components (forms, dialogs, tables)
  4. i18n translations (separate PR per language or as a single i18n PR)

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 0 0 0
Simplification 0 0 0 0

Observations (Not Reported as Issues)

The following were observed but did not meet the 80% confidence threshold for reporting:

  1. syncUserProviderGroupFromKeys function: The new synchronization logic in src/actions/users.ts properly handles the automatic sync of user provider groups from keys. The implementation uses Set for deduplication and sorts for consistency.

  2. Permission checks: The addKey and editKey functions now properly enforce that providerGroup is an admin-only field, with appropriate i18n error messages.

  3. Migration file: The new migration 0036_stale_iron_fist.sql uses IF NOT EXISTS for column additions, which is safe for idempotent migrations.

  4. Error handling: The backend actions properly log errors and return structured ActionResult objects with error codes. The catch blocks in src/actions/keys.ts and src/actions/users.ts properly log and return user-friendly errors.

  5. CLAUDE.md changes: The file was simplified to remove README.md and docs references, which appears intentional for the new feature branch.

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - N/A (test files listed in diff don't exist in current branch, likely new)
  • Code clarity - Good

Notes

  • The PR adds comprehensive i18n support across 5 languages (en, ja, ru, zh-CN, zh-TW)
  • The normalizeProviderGroup helper function is well-designed for consistent group handling
  • The cascade update logic for provider groups on key deletion is properly implemented
  • The toggleKeyEnabled function correctly prevents disabling the last active key

Automated review by Claude AI

ding113 and others added 19 commits December 19, 2025 12:11
… configuration

- Introduced daily quota reset mode and time settings for users, allowing for more flexible quota management.
- Added support for both "fixed" and "rolling" reset modes, with a default reset time of "00:00".
- Updated user-related schemas, actions, and UI components to accommodate the new daily reset features.
- Enhanced user experience by providing clearer management options for daily quotas.

This update improves the granularity of user quota controls and aligns with backend logic for daily limits.
- Add "Create User" button to user management page header
- Implement onboarding tour (4 steps) for first-time user creation
- Enhance UnifiedEditDialog to support create mode with key management:
  - Pre-populate default key for new users
  - Support adding/removing keys dynamically
  - Add rollback mechanism when key creation fails after user creation
- Add createUserOnly server action for controlled user creation
- Fix i18n: replace hardcoded table headers with translation keys
- Fix i18n: replace hardcoded Chinese in unified-edit-dialog with i18n
- Improve empty state UI with create user CTA
- Add comprehensive i18n translations for all 5 languages

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Updated UnifiedEditDialog to improve layout and add user icons for create/edit modes.
- Enhanced KeyEditSection and UserEditSection with better styling and icons for improved user experience.
- Adjusted spacing and structure for better responsiveness and visual clarity.
- Added aria-labels for accessibility improvements.

This update refines the user interface for managing user details and keys, making it more intuitive and visually appealing.
- Revised placeholder text for the provider group field in multiple languages to enhance user understanding.
- Adjusted UI components in the user management section for improved layout and responsiveness.

This update ensures that users have clearer guidance on the provider group functionality across all supported languages.
…atures

- Added support for enabling and disabling users and keys, improving user management flexibility.
- Introduced new dialogs for full key display and model statistics, enhancing the user experience.
- Updated the danger zone section to include enable/disable functionality with confirmation prompts.
- Enhanced key editing section with enable status toggle and improved translations for clarity.
- Improved limit status indicators to show usage percentages, providing better insights into user limits.

This update significantly enhances the user interface and functionality for managing users and their associated keys, making it more intuitive and informative.
- Added new translation keys for copy success and failure messages across multiple languages.
- Introduced key group filtering in the user management UI, allowing users to filter by provider groups.
- Updated user and key management components to improve layout and responsiveness.
- Enhanced user edit and key edit sections with clearer labels and improved styling.

This update significantly improves the user experience by providing better feedback and organization in the user management interface.
Fixed TypeScript error where formatter prop expected `number | undefined`
but was typed as `number`. Used nullish coalescing to safely handle the
undefined case.

CI Run: https://github.com/ding113/claude-code-hub/actions/runs/20311454598

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Prefix unused variables with underscore
- Remove unused imports

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ures

- Added new fields for allowed clients and allowed models in user management, enabling more granular access control.
- Introduced access restrictions section in the user edit dialog for better user experience.
- Updated translations for access restrictions across multiple languages to ensure consistency and clarity.

This update significantly improves the user management interface by providing enhanced control over user access and clearer guidance in multiple languages.
- Updated ALTER TABLE statements to use IF NOT EXISTS for adding new columns in the users table, ensuring no errors occur if the columns already exist.
- Removed redundant translation key for "All" in the dashboard.
- Added detailed descriptions for the balance query page feature, including enabled and disabled states, across multiple languages.
- Introduced new translation keys for key status indicators (enabled/disabled) in various languages.
- Updated user interface components to reflect these changes, improving clarity and user experience.

This update significantly enhances the multilingual support and usability of the dashboard, providing clearer guidance for users managing their keys and settings.
…oggle

- Add QuickRenewDialog component for fast user expiration renewal
  - Quick selection buttons: 7/30/90 days, 1 year
  - Custom date picker with DatePickerField
  - Option to enable disabled users during renewal
  - Returns { ok: boolean } to prevent premature dialog closing on errors

- Move enable/disable toggle from DangerZone to UserEditSection
  - Enable/disable is reversible, not a "dangerous" operation
  - DangerZone now only handles irreversible delete action
  - Added Switch component with AlertDialog confirmation

- Enhance search/filter with multi-tag providerGroup support
  - splitTags() function for comma-separated tag parsing
  - Consistent with backend normalizeProviderGroup pattern

- Update i18n translations for all 5 languages (en, ja, ru, zh-CN, zh-TW)
  - Added quickRenew section with all required keys
  - Added expiresAtHint for table column tooltip
  - Added enableStatus fields for UserEditSection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change default state of expanded users to false in UserManagementTable for better initial visibility.
- Move pagination controls to the bottom of the UserManagementTable for improved layout and user experience.
- Enhance UserEditSection with a toggle for enabling/disabling users, including confirmation dialogs for actions.
- Refactor code for clarity and maintainability, ensuring consistent user interface behavior.

This update improves the usability and organization of user management features, making it easier for administrators to manage user states and navigate the interface.
The function `sumUserCostInTimeRange` was defined twice in statistics.ts
(lines 816 and 874), causing TypeScript compilation to fail with:
- TS2323: Cannot redeclare exported variable 'sumUserCostInTimeRange'
- TS2393: Duplicate function implementation

Both implementations were identical. Removed the second duplicate definition.

CI Run: https://github.com/ding113/claude-code-hub/actions/runs/20344971971

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Updated the `translations` prop in `UnifiedEditDialogInner` to use the `dangerZone` translation keys.
- Enhanced the `DangerZone` component to replace placeholders in confirmation messages with the user's name.
- Modified the `QuickRenewDialog` to calculate the base date for renewal based on the user's current expiration date, ensuring accurate date handling.

These changes improve the user experience by providing clearer messages and more reliable date calculations.
- Introduced a new dialog for viewing key quota usage, enhancing user insights into their usage limits.
- Added a button in the user interface to access the quota usage dialog.
- Updated translations for English, Japanese, Russian, Simplified Chinese, and Traditional Chinese to include new quota-related terms.
- Modified user edit forms to support daily quota settings, improving user management capabilities.

These changes enhance the user experience by providing clearer visibility into quota usage and management options.
@ding113 ding113 merged commit 81dea45 into dev Dec 19, 2025
6 of 7 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Dec 19, 2025
@ding113 ding113 deleted the feat/user-panel branch December 23, 2025 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size/XL Extra Large PR (> 1000 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant