Skip to content

feat: Add OIDC user monthly quota reset feature - #2722

Closed
wanghualoong wants to merge 1 commit into
QuantumNous:mainfrom
wanghualoong:feature/oidc-quota-reset
Closed

feat: Add OIDC user monthly quota reset feature#2722
wanghualoong wants to merge 1 commit into
QuantumNous:mainfrom
wanghualoong:feature/oidc-quota-reset

Conversation

@wanghualoong

@wanghualoong wanghualoong commented Jan 23, 2026

Copy link
Copy Markdown

Summary

  • Add UI controls in Operation Settings for OIDC user monthly quota reset
  • Support enable/disable monthly auto reset
  • Support configurable reset quota amount
  • Add manual reset button with confirmation dialog

Configuration Keys

Key Type Default Description
oidc_quota_reset.enabled boolean false Enable monthly auto reset
oidc_quota_reset.amount number 500000 Quota value to reset

Files Changed

  • web/src/components/settings/OperationSetting.jsx - Add config keys to inputs state
  • web/src/i18n/locales/zh.json - Add Chinese translations
  • web/src/i18n/locales/en.json - Add English translations
  • web/src/pages/Setting/Operation/SettingsCreditLimit.jsx - Add UI components

Backend Service Reference

This feature requires a companion backend service. Below is a reference implementation:

Click to expand: Backend implementation reference

Read config from options table

def get_oidc_quota_reset_config():
    """从 options 表读取 OIDC 额度重置配置"""
    conn = get_pg_connection()
    cur = conn.cursor()
    config = {'enabled': False, 'amount': 500000}

    cur.execute("""
        SELECT key, value FROM options
        WHERE key IN ('oidc_quota_reset.enabled', 'oidc_quota_reset.amount')
    """)

    for row in cur.fetchall():
        if row['key'] == 'oidc_quota_reset.enabled':
            config['enabled'] = row['value'].lower() == 'true'
        elif row['key'] == 'oidc_quota_reset.amount':
            try:
                config['amount'] = int(row['value'])
            except (ValueError, TypeError):
                pass
    cur.close()
    conn.close()
    return config

Reset OIDC users quota

def reset_all_users_quota(quota_amount=None):
    """重置所有 OIDC 用户的配额 (排除 oidc_id 为空或 NULL 的用户)"""
    if quota_amount is None:
        config = get_oidc_quota_reset_config()
        quota_amount = config['amount'] if config['amount'] > 0 else 500000

    conn = get_pg_connection()
    cur = conn.cursor()
    cur.execute("""
        UPDATE users
        SET quota = %s, used_quota = 0
        WHERE oidc_id IS NOT NULL
          AND oidc_id != ''
          AND deleted_at IS NULL
    """, (quota_amount,))

    affected = cur.rowcount
    conn.commit()
    cur.close()
    conn.close()
    return affected, quota_amount

API endpoint for manual reset

@app.route('/api/reset-oidc-quota', methods=['POST'])
def reset_oidc_quota():
    try:
        config = get_oidc_quota_reset_config()
        affected, quota_amount = reset_all_users_quota(config['amount'])
        return jsonify({
            'success': True,
            'affected': affected,
            'quota': quota_amount
        })
    except Exception as e:
        return jsonify({'success': False, 'message': str(e)}), 500

Nginx Proxy Configuration

location /api/sync/ {
    proxy_pass http://localhost:3001/api/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

Test Plan

  • Verify "OIDC 用户月度额度重置" section appears in Settings → Operation Settings → Credit Limit
  • Test enable/disable toggle saves correctly
  • Test quota amount input saves correctly
  • Test manual reset button calls the API endpoint

🤖 Generated with Claude Code

- Add UI controls in Operation Settings for OIDC quota reset
- Support enable/disable monthly auto reset
- Support configurable reset quota amount
- Add manual reset button with confirmation
- Add Chinese and English translations

Configuration keys:
- oidc_quota_reset.enabled: Enable/disable monthly auto reset
- oidc_quota_reset.amount: Quota value to reset

Note: This feature requires sync_service.py updates (not in this repo)
and nginx proxy configuration for /api/sync/ endpoint.

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

coderabbitai Bot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This change introduces OIDC user quota reset functionality, enabling administrators to automatically reset quotas monthly or manually trigger quota resets for all OIDC users. It adds configuration fields, a new API endpoint integration, UI components with confirmation dialogs, and translations in English and Chinese.

Changes

Cohort / File(s) Summary
Configuration Settings
web/src/components/settings/OperationSetting.jsx
Adds two new quota reset configuration fields to initial inputs: oidc_quota_reset.enabled and oidc_quota_reset.amount (default 500000).
Translations
web/src/i18n/locales/en.json, web/src/i18n/locales/zh.json
Adds 11 new quota reset-related translation keys for both English and Chinese. Updates 3 existing translations: "OIDC" references to "Duke NetID", "LLM API Gateway" to "Gateway", and "The Unified" to "DKU AI Service".
Quota Reset UI Implementation
web/src/pages/Setting/Operation/SettingsCreditLimit.jsx
Implements quota reset feature with toggle switch for monthly auto-reset, input field for reset amount, save button, and manual reset button wrapped in confirmation dialog. Adds handleManualReset() method that POSTs to /api/sync/reset-oidc-quota endpoint with success/error feedback.

Sequence Diagram

sequenceDiagram
    actor Admin
    participant UI as SettingsCreditLimit<br/>Component
    participant API as Frontend API<br/>Client
    participant Backend as Backend<br/>Service

    Admin->>UI: Click "Reset All OIDC<br/>Users Quota Now"
    UI->>UI: Show Popconfirm<br/>Dialog
    Admin->>UI: Confirm Reset
    UI->>UI: Set resetting = true
    UI->>API: POST /api/sync/<br/>reset-oidc-quota
    API->>Backend: Forward Reset<br/>Request
    Backend->>Backend: Reset quotas for<br/>all OIDC users
    Backend-->>API: Return {status,<br/>count, quota}
    API-->>UI: Response with<br/>reset details
    UI->>UI: Show Success<br/>Message
    UI->>UI: Set resetting = false
    Admin->>Admin: View confirmation<br/>with user count
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through quota lands,
With OIDC reset in nimble hands,
Monthly resets, smooth and neat,
Duke NetID users start their beat! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Add OIDC user monthly quota reset feature' accurately and directly summarizes the main change—introducing a new OIDC user monthly quota reset feature with associated UI controls, configuration keys, and translations.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@web/src/pages/Setting/Operation/SettingsCreditLimit.jsx`:
- Around line 84-107: In handleManualReset replace the raw fetch call with the
shared API helper (use API.post('/api/sync/reset-oidc-quota')) so the request
gets the standard headers (including New-API-User) and consistent behavior; keep
the same success/error handling (parse response, call showSuccess/showError,
setResetting(true/false) in try/finally) and ensure you await API.post and read
the returned data shape (data.success, data.affected, data.quota, data.message)
just like the current fetch-based logic.

Comment on lines +84 to +107
async function handleManualReset() {
setResetting(true);
try {
const response = await fetch(`${SYNC_SERVICE_URL}/reset-oidc-quota`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
const data = await response.json();
if (data.success) {
showSuccess(t('重置成功,已重置 {{count}} 个用户的额度为 {{quota}}', {
count: data.affected,
quota: data.quota,
}));
} else {
showError(data.message || t('重置失败'));
}
} catch (error) {
showError(t('重置失败:') + error.message);
} finally {
setResetting(false);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for API helper definitions and auth/interceptor patterns
rg -n "API\s*=\s*|export\s+const\s+API|axios\.create" web/src --type js --type jsx

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Search for Authorization headers and auth interceptors
rg -n "Authorization|token|interceptor|Bearer" web/src --type js --type jsx | head -30

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Find where SYNC_SERVICE_URL is defined and used
rg -n "SYNC_SERVICE_URL" web/src --type js --type jsx

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Check other API call patterns in the codebase
rg -n "fetch\(" web/src --type js --type jsx -A 2 | head -40

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Look for API helper usage patterns (API.post, API.get, etc.)
rg -n "API\.(post|get|put|delete|request)" web/src --type js --type jsx | head -30

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Search for API helper definitions (broader search)
rg -n "API\s*=\s*|export\s+const\s+API|axios\.create" web/src

Repository: QuantumNous/new-api

Length of output: 519


🏁 Script executed:

# Search for Authorization and interceptor patterns
rg -n "Authorization|interceptor|Bearer" web/src

Repository: QuantumNous/new-api

Length of output: 4987


🏁 Script executed:

# Find SYNC_SERVICE_URL definition
rg -n "SYNC_SERVICE_URL" web/src

Repository: QuantumNous/new-api

Length of output: 295


🏁 Script executed:

# Look at the actual file in question
cat -n web/src/pages/Setting/Operation/SettingsCreditLimit.jsx | head -120

Repository: QuantumNous/new-api

Length of output: 4539


🏁 Script executed:

# Find API-related imports and usage patterns
rg -n "import.*API|from.*api" web/src | head -20

Repository: QuantumNous/new-api

Length of output: 2351


🏁 Script executed:

# Examine the API helper setup and interceptors
cat -n web/src/helpers/api.js | head -100

Repository: QuantumNous/new-api

Length of output: 3373


🏁 Script executed:

# Check auth helper setup
cat -n web/src/helpers/auth.jsx | head -50

Repository: QuantumNous/new-api

Length of output: 1866


🏁 Script executed:

# Check if there's a request interceptor that adds auth headers
rg -n "interceptors\.request|setAuthHeader" web/src --context 5

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Look at how other components use API for POST requests
rg -n "API\.post" web/src -A 3 | head -40

Repository: QuantumNous/new-api

Length of output: 2630


🏁 Script executed:

# Check if auth headers are being injected via request interceptor
cat -n web/src/helpers/api.js | tail -50

Repository: QuantumNous/new-api

Length of output: 1704


🏁 Script executed:

# Check helpers/index.js to see what's exported
cat -n web/src/helpers/index.js

Repository: QuantumNous/new-api

Length of output: 1322


🏁 Script executed:

# Look for any middleware or setup that adds auth to requests
rg -n "authHeader|Authorization" web/src/helpers --context 3

Repository: QuantumNous/new-api

Length of output: 728


🏁 Script executed:

# Check if there's a request interceptor we may have missed
rg -n "interceptors\.request" web/src/helpers/api.js -A 10

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Verify if authHeader is actually used anywhere as a request interceptor
rg -n "authHeader\(" web/src --context 2

Repository: QuantumNous/new-api

Length of output: 854


🏁 Script executed:

# Check the complete setup of the API instance one more time - look for where auth is configured
sed -n '29,93p' web/src/helpers/api.js

Repository: QuantumNous/new-api

Length of output: 1641


Use API.post() instead of fetch() for consistency and to include necessary request headers.

The current fetch() call bypasses the API helper entirely, missing the New-API-User header that is automatically injected by the API instance. Use API.post('/api/sync/reset-oidc-quota') to align with the existing API pattern (as used on line 59 of this file with API.put()) and ensure proper request headers are included.

🤖 Prompt for AI Agents
In `@web/src/pages/Setting/Operation/SettingsCreditLimit.jsx` around lines 84 -
107, In handleManualReset replace the raw fetch call with the shared API helper
(use API.post('/api/sync/reset-oidc-quota')) so the request gets the standard
headers (including New-API-User) and consistent behavior; keep the same
success/error handling (parse response, call showSuccess/showError,
setResetting(true/false) in try/finally) and ensure you await API.post and read
the returned data shape (data.success, data.affected, data.quota, data.message)
just like the current fetch-based logic.

@wanghualoong

Copy link
Copy Markdown
Author

Summary

  • Add UI controls in Operation Settings for OIDC user monthly quota reset
  • Support enable/disable monthly auto reset
  • Support configurable reset quota amount
  • Add manual reset button with confirmation dialog

Configuration Keys

Key Type Default Description
oidc_quota_reset.enabled boolean false Enable monthly auto reset
oidc_quota_reset.amount number 500000 Quota value to reset

Files Changed

  • web/src/components/settings/OperationSetting.jsx - Add config keys to inputs state
  • web/src/i18n/locales/zh.json - Add Chinese translations
  • web/src/i18n/locales/en.json - Add English translations
  • web/src/pages/Setting/Operation/SettingsCreditLimit.jsx - Add UI components

Backend Service Reference

This feature requires a companion backend service. Below is a reference implementation:

Click to expand: Backend implementation reference

Read config from options table

def get_oidc_quota_reset_config():
    """从 options 表读取 OIDC 额度重置配置"""
    conn = get_pg_connection()
    cur = conn.cursor()
    config = {'enabled': False, 'amount': 500000}

    cur.execute("""
        SELECT key, value FROM options
        WHERE key IN ('oidc_quota_reset.enabled', 'oidc_quota_reset.amount')
    """)

    for row in cur.fetchall():
        if row['key'] == 'oidc_quota_reset.enabled':
            config['enabled'] = row['value'].lower() == 'true'
        elif row['key'] == 'oidc_quota_reset.amount':
            try:
                config['amount'] = int(row['value'])
            except (ValueError, TypeError):
                pass
    cur.close()
    conn.close()
    return config

Reset OIDC users quota

def reset_all_users_quota(quota_amount=None):
    """重置所有 OIDC 用户的配额 (排除 oidc_id 为空或 NULL 的用户)"""
    if quota_amount is None:
        config = get_oidc_quota_reset_config()
        quota_amount = config['amount'] if config['amount'] > 0 else 500000

    conn = get_pg_connection()
    cur = conn.cursor()
    cur.execute("""
        UPDATE users
        SET quota = %s, used_quota = 0
        WHERE oidc_id IS NOT NULL
          AND oidc_id != ''
          AND deleted_at IS NULL
    """, (quota_amount,))

    affected = cur.rowcount
    conn.commit()
    cur.close()
    conn.close()
    return affected, quota_amount

API endpoint for manual reset

@app.route('/api/reset-oidc-quota', methods=['POST'])
def reset_oidc_quota():
    try:
        config = get_oidc_quota_reset_config()
        affected, quota_amount = reset_all_users_quota(config['amount'])
        return jsonify({
            'success': True,
            'affected': affected,
            'quota': quota_amount
        })
    except Exception as e:
        return jsonify({'success': False, 'message': str(e)}), 500

Nginx Proxy Configuration

location /api/sync/ {
    proxy_pass http://localhost:3001/api/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

Test Plan

  • Verify "OIDC 用户月度额度重置" section appears in Settings → Operation Settings → Credit Limit
  • Test enable/disable toggle saves correctly
  • Test quota amount input saves correctly
  • Test manual reset button calls the API endpoint

🤖 Generated with Claude Code

@seefs001

Copy link
Copy Markdown
Collaborator

看起来不是一个通用功能

@seefs001

Copy link
Copy Markdown
Collaborator

仔细一看怎么还是一个外挂的服务呢,提交的内容就加几个配置项。。。这种自用脚本还是自己用吧

@seefs001 seefs001 closed this Jan 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants