Skip to content

Comments

Feature-user-expiration#273

Merged
ding113 merged 3 commits intoding113:devfrom
NightYuYyy:feature-user-expiration
Dec 5, 2025
Merged

Feature-user-expiration#273
ding113 merged 3 commits intoding113:devfrom
NightYuYyy:feature-user-expiration

Conversation

@NightYuYyy
Copy link
Collaborator

@NightYuYyy NightYuYyy commented Dec 5, 2025

Summary

Add user-level expiration and enable/disable controls, allowing administrators to manage user access with time-based restrictions and manual status toggles.

Problem

Previously, the platform lacked the ability to:

  • Set expiration dates for user accounts (e.g., for trial periods, temporary access)
  • Enable/disable users without deleting them
  • Enforce expiration at the API authentication level

Solution

Implemented a comprehensive user expiration and status management system:

  1. Database Schema: Added is_enabled (boolean) and expires_at (timestamp with timezone) columns to the users table with a composite index for efficient queries
  2. Validation Logic: Created validateExpiresAt() function to ensure expiration dates are in the future and don't exceed 10 years
  3. API Authentication: Added lazy expiration check in ProxyAuthenticator - expired users are automatically disabled on their next request
  4. UI Components: Extended user forms with expiration date picker and enable/disable toggle, plus quick renew actions (30d/90d/1y/custom)

Changes

Database

  • drizzle/0028_abnormal_pretty_boy.sql - Migration adding is_enabled and expires_at columns with index
  • src/drizzle/schema.ts - Schema definition updates

Backend

  • src/actions/users.ts - Added validateExpiresAt(), integrated status fields in add/edit user actions
  • src/app/v1/_lib/proxy/auth-guard.ts - Lazy expiration check with auto-disable on expired users
  • src/repository/user.ts - Added markUserExpired() function
  • src/lib/validation/schemas.ts - Extended Create/Update schemas with isEnabled and expiresAt
  • src/types/user.ts - Updated type definitions

Frontend

  • src/app/[locale]/dashboard/_components/user/*.tsx - Extended user forms and list with status management UI
  • Added renew actions (30d/90d/1y/custom) in user actions dropdown

i18n

  • Added error messages in messages/en/errors.json and messages/zh-CN/errors.json
  • Added UI strings in dashboard translations (en/zh-CN/zh-TW/ja/ru)

Testing

  • Manual testing performed (user creation with expiration, renewal, disable/enable)
  • Verified expired users are blocked at API level
  • Verified lazy disable mechanism works correctly
  • No breaking changes - existing users default to is_enabled=true and expires_at=null

Migration Notes

  • Migration is backwards compatible - new columns have safe defaults
  • Existing users remain enabled with no expiration
  • Index idx_users_enabled_expires_at optimizes status/expiration queries

NightYu and others added 3 commits December 5, 2025 18:22
- Added `is_enabled` and `expires_at` fields to the users table to manage user activation status and expiration dates.
- Updated user-related actions and forms to handle the new fields, including enabling/disabling users and renewing their expiration dates.
- Enhanced user interface components to display user status and expiration information.
- Implemented backend logic to mark users as expired and handle authentication based on user status.

This update improves user management capabilities and enhances the overall user experience in the dashboard.
- Introduced a new function `validateExpiresAt` to validate expiration dates, ensuring they are in the future and do not exceed 10 years.
- Updated user-related actions to incorporate expiration date validation, returning appropriate error messages when validation fails.
- Enhanced user forms to handle expiration dates correctly, converting input dates to the end of the day to avoid premature expiration.
- Added new error messages in both English and Chinese for better user feedback regarding expiration date issues.

This update improves the robustness of user management by enforcing expiration date rules and enhancing user experience with clear error messaging.
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @NightYuYyy, 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 introduces user expiration functionality, allowing administrators to set expiration dates for user accounts. It includes database schema changes, server-side and client-side validation, UI enhancements, and authentication guard updates to enforce expiration policies. The goal is to improve user management and security by automatically disabling expired accounts.

Highlights

  • User Expiration: Introduced expires_at column to the users table, allowing administrators to set expiration dates for user accounts. Expired accounts are automatically disabled.
  • Validation: Implemented server-side validation for expiration dates, ensuring they are in the future and within a 10-year limit. Also, client-side validation was added.
  • UI Enhancements: Updated the user management interface to include controls for setting and renewing expiration dates, as well as enabling/disabling users. Expiration status is displayed in the user list.
  • Authentication Guard: The proxy authentication process now checks for disabled and expired user accounts, preventing access to expired or disabled users.
  • Localization: Added new error messages and UI labels in English, Chinese, Japanese, and Russian to support the new expiration date functionality.
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.

@ding113 ding113 added enhancement New feature or request size/XL Extra Large PR (> 1000 lines) size/L Large PR (< 1000 lines) labels Dec 5, 2025
Copy link
Owner

@ding113 ding113 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 PR implements user expiration functionality, adding is_enabled and expires_at fields to users along with renewal and status management features. The implementation is comprehensive but has some issues that should be addressed.

PR Size: L

  • Lines changed: 2753 (2721 additions + 32 deletions)
  • Files changed: 25

Split suggestion: This is a large PR. Consider splitting into:

  1. Database migration + schema changes
  2. Backend user actions and repository changes
  3. Frontend UI components (forms, lists, dialogs)

Issues Found

Category Critical High Medium Low
Code Quality 0 1 2 0
Security 0 0 0 0
Dependencies 0 0 0 0
Documentation 0 0 0 0

Priority Actions

  1. [High] Hardcoded Chinese strings in key-list-header.tsx (line 118-130): The userStatusInfo useMemo uses hardcoded Chinese strings ("已禁用", "已过期", etc.) for badge text instead of using the i18n translation system. This breaks internationalization - users with non-Chinese locale will still see Chinese text.

    Location: src/app/[locale]/dashboard/_components/user/key-list-header.tsx:118-130

    Fix: Use the translation system:

    const tList = useTranslations("dashboard.userList");
    // ...
    if (!activeUser.isEnabled) {
      status = { code: "disabled", badge: tList("status.disabled"), variant: "secondary" };
    } else if (exp && exp <= now) {
      status = { code: "expired", badge: tList("status.expired"), variant: "destructive" };
    } // etc.
  2. [Medium] Another hardcoded Chinese string (line 143): The expiry label "过期时间:" in the UI is hardcoded instead of using translations.

    Location: src/app/[locale]/dashboard/_components/user/key-list-header.tsx:264

    Fix:

    <span>{tList("expiresAt")}:</span>
  3. [Medium] Missing user type update for expiresAt field: The User interface in src/types/user.ts was not updated to include the new isEnabled and expiresAt fields, though they are used in the codebase via UserDisplay.

    Location: src/types/user.ts

    Fix: Add to User interface:

    export interface User {
      // ... existing fields
      isEnabled: boolean;
      expiresAt: Date | null;
    }

Review Coverage

  • Code quality and correctness
  • Security (OWASP Top 10) - No security issues found
  • PR size assessment - L (Large)
  • Dependency changes - No dependency changes
  • Documentation changes - No documentation changes (i18n strings added properly for most translations)

Automated review by Claude AI

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 a comprehensive user expiration feature, which is a valuable addition for user management. The changes span the database, backend logic, and frontend UI, and are well-implemented. Key additions include a validateExpiresAt function for robust date validation, updates to user-related server actions to handle expiration and status, and a lazy expiration check in the authentication guard for better performance. The UI has also been enhanced to display user status and provide renewal actions. I have one high-severity suggestion to address an internationalization bug and code duplication in the UI.

Comment on lines +113 to +141
// 获取用户状态和过期信息
const userStatusInfo = useMemo(() => {
if (!activeUser) return null;

const now = Date.now();
const exp = activeUser.expiresAt ? new Date(activeUser.expiresAt).getTime() : null;

let status: {
code: string;
badge: string;
variant: "default" | "secondary" | "destructive" | "outline";
};

if (!activeUser.isEnabled) {
status = { code: "disabled", badge: "已禁用", variant: "secondary" };
} else if (exp && exp <= now) {
status = { code: "expired", badge: "已过期", variant: "destructive" };
} else if (exp && exp - now <= 72 * 60 * 60 * 1000) {
status = { code: "expiringSoon", badge: "即将过期", variant: "outline" };
} else {
status = { code: "active", badge: "已启用", variant: "default" };
}

const expiryText = activeUser.expiresAt
? `${formatDateDistance(activeUser.expiresAt, new Date(), locale, { addSuffix: true })} (${formatDate(activeUser.expiresAt, "yyyy-MM-dd", locale)})`
: tUsers("neverExpires");

return { status, expiryText };
}, [activeUser, locale, tUsers]);
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The status badge text is hardcoded in Chinese, which breaks internationalization. It should use the useTranslations hook to get the correct text for the current locale.

Additionally, the logic for determining user status is duplicated in user-list.tsx. It would be beneficial to extract this into a shared utility function to avoid code duplication and ensure consistency.

To fix the immediate issue, you can use the dashboard.userList translations. You'll need to add const tUserList = useTranslations("dashboard.userList"); before this block.

  // 获取用户状态和过期信息
  const userStatusInfo = useMemo(() => {
    if (!activeUser) return null;

    const now = Date.now();
    const exp = activeUser.expiresAt ? new Date(activeUser.expiresAt).getTime() : null;

    let status: {
      code: "active" | "expiringSoon" | "expired" | "disabled";
      variant: "default" | "secondary" | "destructive" | "outline";
    };

    if (!activeUser.isEnabled) {
      status = { code: "disabled", variant: "secondary" };
    } else if (exp && exp <= now) {
      status = { code: "expired", variant: "destructive" };
    } else if (exp && exp - now <= 72 * 60 * 60 * 1000) {
      status = { code: "expiringSoon", variant: "outline" };
    } else {
      status = { code: "active", variant: "default" };
    }

    const expiryText = activeUser.expiresAt
      ? `${formatDateDistance(activeUser.expiresAt, new Date(), locale, { addSuffix: true })} (${formatDate(activeUser.expiresAt, "yyyy-MM-dd", locale)})`
      : tUsers("neverExpires");

    return {
      status: {
        ...status,
        badge: tUserList(`status.${status.code}`),
      },
      expiryText,
    };
  }, [activeUser, locale, tUsers, tUserList]);

@ding113 ding113 merged commit 0d46c48 into ding113:dev Dec 5, 2025
4 of 5 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Dec 5, 2025
@NightYuYyy NightYuYyy deleted the feature-user-expiration branch December 8, 2025 17:05
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/L Large PR (< 1000 lines) size/XL Extra Large PR (> 1000 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants