Skip to content

feat(web): Introduce Token Sleep Mode and UI enhancements - #2813

Closed
SX2000CN wants to merge 13 commits into
QuantumNous:mainfrom
SX2000CN:beta
Closed

feat(web): Introduce Token Sleep Mode and UI enhancements#2813
SX2000CN wants to merge 13 commits into
QuantumNous:mainfrom
SX2000CN:beta

Conversation

@SX2000CN

@SX2000CN SX2000CN commented Feb 3, 2026

Copy link
Copy Markdown

Summary

This PR introduces the "Token Sleep Mode" (Auto-activation) feature and includes several UI enhancements and fixes.

Key Changes

  • Token Sleep Mode: Added ability to set tokens to "sleep" and auto-activate.
  • Token Management: Updated token expiration shortcuts and sorting logic.
  • UI/UX:
    • Unified expiration and activation settings in EditTokenModal.
    • Fixed spacing between buttons in modal footers.
    • Cleaned up redundant onOk props where custom footers are used.
  • Fixes: Resolved conflicts with recent upstream changes (e.g., CodexOAuthModal).

Testing

  • Verified token creation and editing with sleep mode settings.
  • Checked modal layouts and button spacing.

Summary by CodeRabbit

  • New Features

    • Added token "sleep mode" with auto-activation on first use and duration-based expiration; visual indicator (orange tag) displays sleeping token status.
    • Added comprehensive deployment documentation covering local build, cross-compilation, and operational setup.
    • Extended payment modal to display discount information.
  • UI/UX Improvements

    • Standardized modal footer layouts across the application for consistent user experience.
    • Improved button alignment and spacing in modals and form footers.
  • Chores

    • Updated build system configuration; added Windows build target support.
    • Added Ant Design UI library dependency.

SX2000CN and others added 13 commits January 29, 2026 17:18
- Add duration field to EditTokenModal for auto-activation on first use
- Set default auto-activation duration to 30 days
- Fix layout shift in EditTokenModal when validation errors occur
- Improve button layout in ModelTestModal using Space component
- Fix build errors by adjusting CSS imports and adding antd dependency
- Add gap-2 to NoticeModal and FilterModalFooter flex containers
- Wrap buttons with Space component in TwoFASetting and TwoFactorAuthModal
- Fix ReferenceError in SettingsPaymentGatewayCreem.jsx
- Fix ReferenceError in UpstreamRatioSync.jsx
- Add deployment guide documenting common pitfalls
…nModal

- Replace separate date picker and duration input with unified validity settings
- Add validity type selection (Forever / Custom Duration)
- Add activation mode switch (Immediate / On First Use) for custom durations
- Auto-calculate expiration time or duration based on user selection
- Improve UX by simplifying token validity configuration
- Revert to original DatePicker and shortcut buttons for expiration time
- Add 'Sleep Mode' switch for tokens
- When Sleep Mode is enabled:
  - Token remains valid indefinitely until first use
  - Expiration duration is calculated from the selected expiration date
  - Countdown starts only upon first activation
- Maintain standard expiration behavior when Sleep Mode is disabled
@coderabbitai

coderabbitai Bot commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR refactors 40+ modal components to use custom footers instead of Modal's built-in handlers, introduces token sleep mode with duration-based activation, adds Ant Design dependency, updates the Dockerfile to Windows binary compilation, and includes a deployment guide.

Changes

Cohort / File(s) Summary
Infrastructure & Deployment
Dockerfile, docs/deployment-guide.md
Dockerfile switches from Bun-based web build to Go-only Windows binary compilation (GOOS=windows, GOARCH=amd64). Comprehensive deployment guide added covering local-build-and-upload workflow, troubleshooting, systemd service setup, and directory structure.
Token Duration Feature
controller/token.go, model/token.go, web/src/components/table/tokens/TokensColumnDefs.jsx, web/src/components/table/tokens/modals/EditTokenModal.jsx
Added Duration and ActivatedTime fields to Token model. Tokens with duration auto-activate on first validation; expiry time is set from ActivatedTime + Duration. EditTokenModal introduces sleep mode UI toggle; when enabled, duration is computed from future timestamp and expired_time is set to -1. Token column displays orange "sleeping" tag when duration > 0 and expired_time === -1.
Dependencies
web/package.json, web/src/index.jsx
Added Ant Design dependency (antd: ^6.2.2). Removed global Semi UI CSS import from index.jsx.
Modal Footer Refactoring (Part 1: Common/Layout)
web/src/components/common/modals/SecureVerificationModal.jsx, web/src/components/common/modals/TwoFactorAuthModal.jsx, web/src/components/layout/NoticeModal.jsx, web/src/components/settings/ChannelSelectorModal.jsx, web/src/components/settings/DashboardSetting.jsx, web/src/components/settings/OtherSetting.jsx, web/src/components/settings/SystemSetting.jsx
Replaced Modal's onOk/okText/cancelText props with custom footers containing Cancel and Confirm/Save buttons. Added Button and Space imports for footer layout with flex justify-end alignment. Behavior unchanged; control flow moved from implicit onOk to explicit footer actions.
Modal Footer Refactoring (Part 2: Personal Settings)
web/src/components/settings/personal/components/TwoFASetting.jsx, web/src/components/settings/personal/modals/AccountDeleteModal.jsx, web/src/components/settings/personal/modals/ChangePasswordModal.jsx, web/src/components/settings/personal/modals/EmailBindModal.jsx
Replaced onOk handlers and empty fragment wrappers with custom footers using Button and Space components. Added danger/warning styling to confirm buttons where appropriate. Modal action flow now explicit via footer buttons.
Modal Footer Refactoring (Part 3: Channels)
web/src/components/table/channels/modals/BatchTagModal.jsx, web/src/components/table/channels/modals/CodexOAuthModal.jsx, web/src/components/table/channels/modals/EditChannelModal.jsx, web/src/components/table/channels/modals/ModelSelectModal.jsx, web/src/components/table/channels/modals/ModelTestModal.jsx, web/src/components/table/channels/modals/OllamaModelModal.jsx, web/src/components/table/channels/modals/SingleModelSelectModal.jsx
Consistent modal footer refactoring with custom footer containing Cancel/Confirm buttons wrapped in flex containers and Space components. ModelTestModal batches buttons into single Space group.
Modal Footer Refactoring (Part 4: Logs & Deployments)
web/src/components/table/mj-logs/modals/ContentModal.jsx, web/src/components/table/model-deployments/modals/ConfirmationDialog.jsx, web/src/components/table/model-deployments/modals/CreateDeploymentModal.jsx, web/src/components/table/model-deployments/modals/ExtendDurationModal.jsx, web/src/components/table/model-deployments/modals/UpdateConfigModal.jsx
Modal footer refactoring replacing onOk handlers with custom footers. ConfirmationDialog and ExtendDurationModal respect disabled states tied to confirmation logic; CreateDeploymentModal wires Confirm button to formApi.submitForm().
Modal Footer Refactoring (Part 5: Models & Pricing)
web/src/components/table/model-pricing/modal/components/FilterModalFooter.jsx, web/src/components/table/models/modals/EditVendorModal.jsx, web/src/components/table/models/modals/MissingModelsModal.jsx, web/src/components/table/models/modals/UpstreamConflictModal.jsx
Modal footer refactoring with custom footers replacing onOk behavior. EditVendorModal wires Confirm to form submission. Added gap-2 spacing to FilterModalFooter. Removed default modal action props.
Modal Footer Refactoring (Part 6: Tokens, Users & Redemptions)
web/src/components/table/redemptions/modals/DeleteRedemptionModal.jsx, web/src/components/table/task-logs/modals/ContentModal.jsx, web/src/components/table/tokens/modals/DeleteTokensModal.jsx, web/src/components/table/users/modals/DeleteUserModal.jsx, web/src/components/table/users/modals/DemoteUserModal.jsx, web/src/components/table/users/modals/EnableDisableUserModal.jsx, web/src/components/table/users/modals/PromoteUserModal.jsx, web/src/components/table/users/modals/ResetPasskeyModal.jsx, web/src/components/table/users/modals/ResetTwoFAModal.jsx
Consistent modal footer refactoring pattern: removed onOk/okText/cancelText/confirmLoading props and replaced with custom footers. Added Button and Space imports. Action buttons now explicitly defined in footer with appropriate styling (warning/danger for destructive actions).
Modal Footer Refactoring (Part 7: Top-up & Chat)
web/src/components/topup/modals/PaymentConfirmModal.jsx, web/src/pages/Setting/Chat/SettingsChats.jsx
PaymentConfirmModal extended with amountNumber and discountRate props for discount calculation and display. Custom footer replaces onOk. SettingsChats.jsx replaces onOk with custom footer footer for Cancel/Confirm actions.
Modal Footer Refactoring (Part 8: Dashboard Settings)
web/src/pages/Setting/Dashboard/SettingsAPIInfo.jsx, web/src/pages/Setting/Dashboard/SettingsAnnouncements.jsx, web/src/pages/Setting/Dashboard/SettingsFAQ.jsx, web/src/pages/Setting/Dashboard/SettingsUptimeKuma.jsx
Multiple modals within each file replaced with custom footers. Modals for add/edit use Save button; delete confirmation uses Confirm Delete. Loading state tied to modalLoading. Removed default onOk/okText/cancelText/confirmLoading props.
Modal Footer Refactoring (Part 9: Operation & Payment)
web/src/pages/Setting/Operation/SettingsChannelAffinity.jsx, web/src/pages/Setting/Operation/SettingsGeneral.jsx, web/src/pages/Setting/Payment/SettingsPaymentGatewayCreem.jsx
Custom footer refactoring pattern applied. SettingsGeneral removes onOk warning close in favor of footer button. SettingsChannelAffinity and PaymentGatewayCreem use Space-wrapped button groups.
Modal Footer Refactoring (Part 10: Ratio Settings)
web/src/pages/Setting/Ratio/ModelRationNotSetEditor.jsx, web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx, web/src/pages/Setting/Ratio/UpstreamRatioSync.jsx
Replaced onOk handlers with custom footers containing Cancel and Confirm buttons. ModelSettingsVisualEditor preserves token-price-based ratio calculations; ConflictConfirmModal in UpstreamRatioSync moves action triggering from built-in footer to explicit button.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

The changes span 60+ files with heterogeneous modifications: consistent modal footer refactoring patterns (reduces cognitive load), token sleep mode logic with state activation (moderate complexity), Dockerfile infrastructure changes, and dependency updates. While many changes follow a repetitive pattern, the token feature introduces new conditional logic, multiple files are modified for feature integration, and infrastructure changes require separate validation.

Possibly related PRs

  • fix: merge conflict #1955: Modifies web/src/components/table/channels/modals/EditChannelModal.jsx footer/DOM nesting; potential merge conflict with this PR's footer refactoring changes.
  • fix: channel affinity #2799: Updates web/src/pages/Setting/Operation/SettingsChannelAffinity.jsx with skip_retry_on_failure UI logic; overlaps with this PR's Modal footer refactoring in the same file.

Suggested reviewers

  • Calcium-Ion
  • creamlike1024

Poem

🐰 Modal footers dance in custom rows,
Token sleep modes softly glows,
Forty modals now aligned with care,
Windows binaries float through air! ✨

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(web): Introduce Token Sleep Mode and UI enhancements' accurately and concisely summarizes the main changes. It highlights the primary feature (Token Sleep Mode) and acknowledges the broader scope (UI enhancements), aligning well with the changeset which includes token sleep mode implementation, modal footer refactoring, and UI/UX improvements.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
controller/token.go (1)

283-294: ⚠️ Potential issue | 🟠 Major

Keep ExpiredTime consistent when Duration > 0 on update.
AddToken forces ExpiredTime = -1 when Duration > 0, but UpdateToken doesn’t. This can leave a stale ExpiredTime and break sleep-mode semantics after edits.

✅ Suggested fix
   cleanToken.CrossGroupRetry = token.CrossGroupRetry
   cleanToken.Duration = token.Duration
+  if token.Duration > 0 {
+    cleanToken.ExpiredTime = -1
+  }
🤖 Fix all issues with AI agents
In `@controller/token.go`:
- Around line 194-198: Reject negative Duration values at the API boundary by
adding validation in the token handlers (e.g., CreateToken and UpdateToken) to
check token.Duration >= 0 and return a 400 / validation error when it's
negative; do not propagate negative Duration into the model
(cleanToken.Duration) and ensure any logic that sets cleanToken.ExpiredTime
(referencing cleanToken and token.Duration) only runs for valid, non-negative
durations.

In `@Dockerfile`:
- Around line 1-14: The Dockerfile is building a Windows binary (GOOS=windows
producing new-api-galaxy.exe) and lacks a runtime command and reproducible base
image; change GOOS/GOARCH/CGO_ENABLED to produce Linux binaries matching CI
targets (e.g. remove or set GOOS=linux and appropriate GOARCH), build a non-.exe
artifact (e.g. new-api-galaxy or new-api), add a proper ENTRYPOINT or CMD to run
the Gin HTTP server on port 3000, and pin the base image (replace golang:latest
with a specific tag) so CI/CD/docker-compose images are compatible; update or
remove the "伪造的 web/dist" copy step only if it's temporary test data.

In `@docs/deployment-guide.md`:
- Around line 22-24: The Markdown contains several fenced code blocks missing
language identifiers (triggering MD040); update each ``` block that contains
examples like "Load key \"xxx/id_ed25519\": error in libcrypto",
"/www1/new-api/new-api: cannot execute binary file: Exec format error",
"ReferenceError: Space is not defined", "scp: dest open
\"/www1/new-api/new-api\": Failure", and the directory listing to include
appropriate language tags (e.g., ```text or ```bash) so they render with syntax
highlighting and satisfy MD040; search for the bare ``` fences in the document
and add the language specifier to each affected block.

In `@model/token.go`:
- Around line 92-105: The DB update after setting token.ActivatedTime and
token.ExpiredTime (see token.ExpiredTime, token.Duration, token.ActivatedTime
and DB.Model(token).Select("activated_time","expired_time").Updates(token))
currently ignores errors and can lead to in-memory/db divergence and race
conditions; change this to perform an atomic/conditional update (e.g., within a
transaction or using a WHERE activated_time = 0 conditional update and checking
RowsAffected) and handle the returned error: if the update fails or affects 0
rows, revert the in-memory activation (reset ActivatedTime/ExpiredTime) and
avoid firing cacheSetToken in the gopool.Go block, otherwise proceed to call
cacheSetToken; ensure error is logged via common.SysLog when DB update fails.

In `@web/package.json`:
- Line 13: The project lists "antd" in package.json but no stylesheet import or
usage is present; either add the Ant Design reset CSS import or remove the
dependency: if you intend to use antd, add the line import
'antd/dist/reset.css'; to the entry file (web/src/index.jsx) before rendering so
Ant components are styled; if you do not intend to use antd (keeping
`@douyinfe/semi-ui` only), remove "antd" from package.json and run your package
manager to update lockfiles to avoid unused dependency and style conflicts.

In `@web/src/components/settings/ChannelSelectorModal.jsx`:
- Around line 257-266: In ChannelSelectorModal update the custom footer so
keyboard Enter confirms instead of cancelling: either add autoFocus={true} to
the primary Button (the one that calls onOk) or implement an Enter-key handler
on the modal/content that listens for keydown Enter and calls the onOk handler;
ensure you keep the existing onCancel/onOk bindings and focus behavior
consistent with Semi UI (attach the handler at the Modal content wrapper or
component mount/unmount lifecycle to avoid leaking listeners).
🧹 Nitpick comments (6)
web/src/components/layout/NoticeModal.jsx (1)

188-188: Minor: No-op className ternary.

This existing code has a conditional that always evaluates to an empty string regardless of item.isUnread. If there's intended styling for unread items, the appropriate class should be added; otherwise, this can be simplified.

-                className={item.isUnread ? '' : ''}
+                className=''

Or remove the attribute entirely if no class is needed.

web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx (1)

525-572: Extract inline logic and reuse existing calculation functions.

The onClick handler contains duplicated calculation logic:

  • Line 539: tokenPrice / 2 duplicates calculateRatioFromTokenPrice (line 276)
  • Lines 550-553: completion ratio calculation duplicates calculateCompletionRatioFromPrices (lines 280-289)

Additionally, if currentModel is falsy, the button silently does nothing - consider showing a validation message or disabling the button.

♻️ Proposed refactor to extract handler logic
+  const handleModalConfirm = () => {
+    if (!currentModel) {
+      showError(t('请先填写模型信息'));
+      return;
+    }
+    
+    const valuesToSave = { ...currentModel };
+    
+    if (
+      pricingMode === 'per-token' &&
+      pricingSubMode === 'token-price' &&
+      currentModel.tokenPrice
+    ) {
+      const tokenPrice = parseFloat(currentModel.tokenPrice);
+      valuesToSave.ratio = calculateRatioFromTokenPrice(tokenPrice).toString();
+      
+      if (currentModel.completionTokenPrice && currentModel.tokenPrice) {
+        const completionPrice = parseFloat(currentModel.completionTokenPrice);
+        const modelPrice = parseFloat(currentModel.tokenPrice);
+        if (modelPrice > 0) {
+          valuesToSave.completionRatio = calculateCompletionRatioFromPrices(
+            modelPrice,
+            completionPrice,
+          ).toString();
+        }
+      }
+    }
+    
+    if (pricingMode === 'per-token') {
+      valuesToSave.price = '';
+    } else {
+      valuesToSave.ratio = '';
+      valuesToSave.completionRatio = '';
+    }
+    
+    addOrUpdateModel(valuesToSave);
+  };

 <Button
   type='primary'
-  onClick={() => {
-    if (currentModel) {
-      // ... long inline logic
-    }
-  }}
+  onClick={handleModalConfirm}
 >
web/src/components/table/users/modals/EnableDisableUserModal.jsx (1)

39-48: Consider adding a loading prop for the Confirm button.

The modal lacks a loading state for the Confirm button. If onConfirm triggers an async operation (e.g., API call to enable/disable the user), users won't see visual feedback.

✨ Proposed enhancement to add loading support
 const EnableDisableUserModal = ({
   visible,
   onCancel,
   onConfirm,
   user,
   action,
   t,
+  loading = false,
 }) => {
-            <Button type='warning' theme='solid' onClick={onConfirm}>
+            <Button type='warning' theme='solid' onClick={onConfirm} loading={loading}>
               {t('确定')}
             </Button>
docs/deployment-guide.md (1)

173-176: Consider using a dedicated service user instead of root.

Running the service as User=root (line 175) is a security risk. If the application is compromised, attackers gain root access.

🔒 Suggested improvement for service user
 [Service]
 Type=simple
-User=root
+User=newapi
 WorkingDirectory=/www1/new-api

Add user creation to the deployment steps:

# Create dedicated service user
sudo useradd -r -s /bin/false newapi
sudo chown -R newapi:newapi /www1/new-api
web/src/components/table/tokens/modals/DeleteTokensModal.jsx (1)

36-45: Consider adding loading state to the confirm button for consistency.

Other modals in this PR (e.g., SettingsAnnouncements, SettingsFAQ) show a loading indicator on the confirm button during async operations. If onConfirm is async, consider accepting and using a confirmLoading prop here as well.

Optional: Add loading state
 const DeleteTokensModal = ({
   visible,
   onCancel,
   onConfirm,
   selectedKeys,
   t,
+  confirmLoading,
 }) => {
   ...
-            <Button type='warning' theme='solid' onClick={onConfirm}>
+            <Button type='warning' theme='solid' onClick={onConfirm} loading={confirmLoading}>
               {t('确定')}
             </Button>
web/src/components/table/tokens/modals/EditTokenModal.jsx (1)

44-45: Unused imports detected.

Radio and InputGroup are imported but not used in the component. Consider removing them to keep the import list clean.

Remove unused imports
-  Radio,
-  InputGroup,

Comment thread controller/token.go
Comment on lines +194 to 198
Duration: token.Duration,
}
if token.Duration > 0 {
cleanToken.ExpiredTime = -1
}

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 | 🟡 Minor

Validate Duration to avoid invalid negative values.
Right now any negative duration will flow into the model; consider rejecting it at the API boundary for both create and update.

🔧 Suggested guard
 if len(token.Name) > 50 {
   c.JSON(http.StatusOK, gin.H{
     "success": false,
     "message": "令牌名称过长",
   })
   return
 }
+if token.Duration < 0 {
+  c.JSON(http.StatusOK, gin.H{
+    "success": false,
+    "message": "Duration 不能为负数",
+  })
+  return
+}
🤖 Prompt for AI Agents
In `@controller/token.go` around lines 194 - 198, Reject negative Duration values
at the API boundary by adding validation in the token handlers (e.g.,
CreateToken and UpdateToken) to check token.Duration >= 0 and return a 400 /
validation error when it's negative; do not propagate negative Duration into the
model (cleanToken.Duration) and ensure any logic that sets
cleanToken.ExpiredTime (referencing cleanToken and token.Duration) only runs for
valid, non-negative durations.

Comment thread Dockerfile
Comment on lines +1 to +14
FROM golang:latest

WORKDIR /build
COPY web/package.json .
COPY web/bun.lock .
RUN bun install
COPY ./web .
COPY ./VERSION .
RUN DISABLE_ESLINT_PLUGIN='true' VITE_REACT_APP_VERSION=$(cat VERSION) bun run build
WORKDIR /app

FROM golang:alpine AS builder2
ENV GO111MODULE=on CGO_ENABLED=0

ARG TARGETOS
ARG TARGETARCH
ENV GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64}
ENV GOEXPERIMENT=greenteagc

WORKDIR /build

ADD go.mod go.sum ./
RUN go mod download
ENV GOOS=windows
ENV GOARCH=amd64
ENV CGO_ENABLED=0

# 复制源码(包含我们伪造的 web/dist)
COPY . .
COPY --from=builder /build/dist ./web/dist
RUN go build -ldflags "-s -w -X 'github.com/QuantumNous/new-api/common.Version=$(cat VERSION)'" -o new-api

FROM debian:bookworm-slim

RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates tzdata libasan8 wget \
&& rm -rf /var/lib/apt/lists/* \
&& update-ca-certificates

COPY --from=builder2 /build/new-api /
EXPOSE 3000
WORKDIR /data
ENTRYPOINT ["/new-api"]
# 下载依赖并编译
RUN go mod download
RUN go build -ldflags "-s -w" -o new-api-galaxy.exe

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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# First, explore the repo structure
echo "=== Repository structure ==="
ls -la
echo ""

# Look for CI workflows that might reference this Dockerfile
echo "=== CI Workflows ==="
find . -type d -name '.github' -o -name '.gitlab-ci' -o -name '.circleci' | head -20
if [ -d ".github/workflows" ]; then
  echo "GitHub workflows found:"
  ls -la .github/workflows/
  echo ""
fi

# Look for deployment or documentation files
echo "=== Documentation files ==="
fd -e md -e rst | grep -i -E "(deploy|readme|dockerfile|build|docker)" | head -20
echo ""

# Check git history for recent Dockerfile changes
echo "=== Recent Dockerfile history ==="
git log --oneline -n 10 -- Dockerfile 2>/dev/null || echo "Git history not available"
echo ""

# Check for any other Dockerfile variations
echo "=== All Dockerfile variations ==="
fd -type f -name 'Dockerfile*'

Repository: QuantumNous/new-api

Length of output: 3423


🏁 Script executed:

#!/bin/bash

# Check the CI workflows to see if they use this Dockerfile
echo "=== docker-image-alpha.yml ==="
cat .github/workflows/docker-image-alpha.yml
echo ""

echo "=== docker-image-arm64.yml ==="
cat .github/workflows/docker-image-arm64.yml
echo ""

# Check git diff for Dockerfile changes
echo "=== Git diff for Dockerfile (last 5 commits) ==="
git diff HEAD~1 -- Dockerfile 2>/dev/null || echo "Cannot get diff"
echo ""

# Check the git log with patches
echo "=== Git log for Dockerfile ==="
git log --oneline --all -- Dockerfile 2>/dev/null | head -20

# Look at the previous version of Dockerfile
echo ""
echo "=== Previous Dockerfile (HEAD~1) ==="
git show HEAD~1:Dockerfile 2>/dev/null || echo "Cannot retrieve previous version"

Repository: QuantumNous/new-api

Length of output: 10206


🏁 Script executed:

#!/bin/bash

# Read the current Dockerfile
echo "=== Current Dockerfile content ==="
cat -n Dockerfile
echo ""

# Check git history more carefully - find when Dockerfile last changed meaningfully
echo "=== Last few actual Dockerfile changes ==="
git log -p --follow -- Dockerfile 2>/dev/null | head -200
echo ""

# Look at electron directory since it might be related to Windows builds
echo "=== Electron directory structure ==="
ls -la electron/
echo ""

# Check if there's a build-windows script or similar
echo "=== Windows-related files ==="
find . -type f -name "*windows*" -o -name "*exe*" 2>/dev/null | grep -v node_modules | grep -v .git

Repository: QuantumNous/new-api

Length of output: 1514


🏁 Script executed:

#!/bin/bash

# Check what deployment-guide.md says about Docker
echo "=== deployment-guide.md Docker section ==="
grep -A 20 -i "docker" docs/deployment-guide.md | head -40
echo ""

# Check docker-compose.yml to understand expected setup
echo "=== docker-compose.yml ==="
cat docker-compose.yml | head -50
echo ""

# Check if there's a makefile or build script for Windows
echo "=== makefile content ==="
cat makefile
echo ""

# Look at main.go to understand if this is desktop app or server
echo "=== main.go first 50 lines ==="
head -50 main.go

Repository: QuantumNous/new-api

Length of output: 4514


Critical: Dockerfile produces Windows binary, breaking Docker deployments and CI/CD.

This Dockerfile compiles a Windows executable (new-api-galaxy.exe) but the CI/CD workflows (docker-image-alpha.yml, docker-image-arm64.yml) are configured to build for Linux platforms (linux/amd64, linux/arm64) and push to Docker Hub. The docker-compose.yml depends on the Linux image calciumion/new-api:latest. This is a fundamental incompatibility that will break:

  • CI/CD pipelines that build and push to Docker registries
  • Docker Compose deployments using the published images
  • Any container-based deployment

Issues identified:

  1. GOOS=windows produces .exe binary incompatible with linux/amd64 and linux/arm64 targets
  2. No ENTRYPOINT/CMD - the image only compiles, doesn't define how to run
  3. "伪造的 web/dist" comment (fake web/dist) indicates this is temporary/test code
  4. golang:latest - unpinned tag reduces reproducibility

The application is an HTTP server (Gin framework, port 3000 per main.go), not a Windows desktop app. Restore the Linux-compatible Dockerfile or clarify if this is a separate Windows build artifact (not for Docker deployment).

🤖 Prompt for AI Agents
In `@Dockerfile` around lines 1 - 14, The Dockerfile is building a Windows binary
(GOOS=windows producing new-api-galaxy.exe) and lacks a runtime command and
reproducible base image; change GOOS/GOARCH/CGO_ENABLED to produce Linux
binaries matching CI targets (e.g. remove or set GOOS=linux and appropriate
GOARCH), build a non-.exe artifact (e.g. new-api-galaxy or new-api), add a
proper ENTRYPOINT or CMD to run the Gin HTTP server on port 3000, and pin the
base image (replace golang:latest with a specific tag) so CI/CD/docker-compose
images are compatible; update or remove the "伪造的 web/dist" copy step only if
it's temporary test data.

Comment thread docs/deployment-guide.md
Comment on lines +22 to +24
```
Load key "xxx/id_ed25519": error in libcrypto
```

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 | 🟡 Minor

Add language identifiers to fenced code blocks.

Several code blocks are missing language specifiers, which triggers MD040 warnings and affects syntax highlighting.

📝 Suggested fixes for code block languages
 **错误信息:**
-```
+```text
 Load key "xxx/id_ed25519": error in libcrypto

```diff
 **错误信息:**
-```
+```text
 /www1/new-api/new-api: cannot execute binary file: Exec format error

```diff
 **错误信息:**
-```
+```text
 ReferenceError: Space is not defined

```diff
 **错误信息:**
-```
+```text
 scp: dest open "/www1/new-api/new-api": Failure

```diff
 ## 目录结构
 
-```
+```text
 /www1/new-api/
 ├── new-api          # 可执行文件

Also applies to: 41-43, 65-67, 93-95, 213-218

🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 22-22: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@docs/deployment-guide.md` around lines 22 - 24, The Markdown contains several
fenced code blocks missing language identifiers (triggering MD040); update each
``` block that contains examples like "Load key \"xxx/id_ed25519\": error in
libcrypto", "/www1/new-api/new-api: cannot execute binary file: Exec format
error", "ReferenceError: Space is not defined", "scp: dest open
\"/www1/new-api/new-api\": Failure", and the directory listing to include
appropriate language tags (e.g., ```text or ```bash) so they render with syntax
highlighting and satisfy MD040; search for the bare ``` fences in the document
and add the language specifier to each affected block.

Comment thread model/token.go
Comment on lines +92 to +105
if token.ExpiredTime == -1 && token.Duration > 0 && token.ActivatedTime == 0 {
token.ActivatedTime = common.GetTimestamp()
token.ExpiredTime = token.ActivatedTime + token.Duration
// Update DB
DB.Model(token).Select("activated_time", "expired_time").Updates(token)
// Update Redis if enabled
if common.RedisEnabled {
gopool.Go(func() {
if err := cacheSetToken(*token); err != nil {
common.SysLog("failed to update token cache: " + err.Error())
}
})
}
}

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

Missing error handling for DB update during token activation.

The database update on line 96 does not check the returned error. If the update fails, the token's in-memory state will be inconsistent with the database, and the function will continue as if activation succeeded.

Additionally, there's a potential race condition: if two concurrent requests validate the same sleeping token simultaneously, both may attempt activation before either persists. Consider using a transaction with row-level locking or an atomic conditional update (e.g., WHERE activated_time = 0).

Proposed fix for error handling and atomicity
 		if token.ExpiredTime == -1 && token.Duration > 0 && token.ActivatedTime == 0 {
-			token.ActivatedTime = common.GetTimestamp()
-			token.ExpiredTime = token.ActivatedTime + token.Duration
-			// Update DB
-			DB.Model(token).Select("activated_time", "expired_time").Updates(token)
+			now := common.GetTimestamp()
+			// Atomic conditional update to prevent race conditions
+			result := DB.Model(token).
+				Where("activated_time = ?", 0).
+				Select("activated_time", "expired_time").
+				Updates(map[string]interface{}{
+					"activated_time": now,
+					"expired_time":   now + token.Duration,
+				})
+			if result.Error != nil {
+				common.SysLog("failed to activate token: " + result.Error.Error())
+				return token, errors.New("令牌激活失败")
+			}
+			if result.RowsAffected > 0 {
+				token.ActivatedTime = now
+				token.ExpiredTime = now + token.Duration
+			} else {
+				// Another request already activated; reload token
+				token, err = GetTokenByKey(key, true)
+				if err != nil {
+					return nil, err
+				}
+			}
 			// Update Redis if enabled
🤖 Prompt for AI Agents
In `@model/token.go` around lines 92 - 105, The DB update after setting
token.ActivatedTime and token.ExpiredTime (see token.ExpiredTime,
token.Duration, token.ActivatedTime and
DB.Model(token).Select("activated_time","expired_time").Updates(token))
currently ignores errors and can lead to in-memory/db divergence and race
conditions; change this to perform an atomic/conditional update (e.g., within a
transaction or using a WHERE activated_time = 0 conditional update and checking
RowsAffected) and handle the returned error: if the update fails or affects 0
rows, revert the in-memory activation (reset ActivatedTime/ExpiredTime) and
avoid firing cacheSetToken in the gopool.Go block, otherwise proceed to call
cacheSetToken; ensure error is logged via common.SysLog when DB update fails.

Comment thread web/package.json
"@visactor/react-vchart": "~1.8.8",
"@visactor/vchart": "~1.8.8",
"@visactor/vchart-semi-theme": "~1.8.8",
"antd": "^6.2.2",

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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat web/package.json

Repository: QuantumNous/new-api

Length of output: 2745


🌐 Web query:

Ant Design 6.2.2 peer dependencies React 18 Vite compatibility

💡 Result:

  • Peer dependencies (antd 6.2.2): react >= 18.0.0 and react-dom >= 18.0.0. No other peer deps are required by antd itself. [1]
  • React 18: Fully supported (v6 raised the minimum supported React version to 18). [2]
  • Vite compatibility: antd 6.2.2 ships ESM (module: es/index.js) and marks *.css as side effects, which works cleanly with Vite’s dependency pre-bundling and tree-shaking. [1]
    • For styles, you can import the provided CSS entry (commonly antd/dist/reset.css; it exists in 6.2.2). [3]

Sources: [1] [2] [3]


🏁 Script executed:

# Search for existing style/CSS imports in the codebase
rg -i "antd|styles|css|import.*from.*antd" web/src --type js --type jsx --type ts --type tsx -A 2 -B 2 | head -50

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Check if there's a main entry point or setup file
fd -e main.js -e main.jsx -e main.ts -e main.tsx -e app.js -e app.jsx -e app.ts -e app.tsx web/src

Repository: QuantumNous/new-api

Length of output: 396


🏁 Script executed:

# Find main entry point in web/src
fd . web/src --max-depth 1 -e js -e jsx -e ts -e tsx

Repository: QuantumNous/new-api

Length of output: 96


🏁 Script executed:

# Search for any existing antd or CSS imports
rg "import.*antd|import.*\.css|from.*antd" web/src --max-count 20

Repository: QuantumNous/new-api

Length of output: 437


🏁 Script executed:

# Check vite.config file
fd vite.config web

Repository: QuantumNous/new-api

Length of output: 81


🏁 Script executed:

cat -n web/src/index.jsx | head -30

Repository: QuantumNous/new-api

Length of output: 1475


🏁 Script executed:

cat -n web/src/App.jsx | head -50

Repository: QuantumNous/new-api

Length of output: 2610


🏁 Script executed:

cat -n web/vite.config.js

Repository: QuantumNous/new-api

Length of output: 3587


🏁 Script executed:

# Check if any antd components are actually imported/used
rg "from 'antd'|from \"antd\"|Button|Modal|Form|Input|Select" web/src --max-count 15

Repository: QuantumNous/new-api

Length of output: 50376


🏁 Script executed:

rg "from\s+['\"]antd['\"]" web/src

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Check if antd is imported/used anywhere at all
rg "import.*from.*['\"]antd['\"]" web/src

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Verify the full index.jsx to see all imports
wc -l web/src/index.jsx

Repository: QuantumNous/new-api

Length of output: 83


🏁 Script executed:

cat -n web/src/index.jsx | tail -20

Repository: QuantumNous/new-api

Length of output: 664


Add required Ant Design CSS import and clarify intent to use it.

Ant Design v6.2.2 is compatible with React 18.2.0 and Vite 5.2.0, but the integration is incomplete: no CSS is imported in the entry point, so any antd components will render unstyled. Additionally, antd is not imported anywhere in the codebase, yet the project already uses @douyinfe/semi-ui as its complete UI library.

Either add import 'antd/dist/reset.css'; to web/src/index.jsx if antd will be used, or remove the dependency if semi-ui is the intended choice. Using both libraries together will cause style conflicts, increased bundle size, and maintenance burden.

🤖 Prompt for AI Agents
In `@web/package.json` at line 13, The project lists "antd" in package.json but no
stylesheet import or usage is present; either add the Ant Design reset CSS
import or remove the dependency: if you intend to use antd, add the line import
'antd/dist/reset.css'; to the entry file (web/src/index.jsx) before rendering so
Ant components are styled; if you do not intend to use antd (keeping
`@douyinfe/semi-ui` only), remove "antd" from package.json and run your package
manager to update lockfiles to avoid unused dependency and style conflicts.

Comment on lines +257 to +266
footer={
<div className='flex justify-end'>
<Space>
<Button onClick={onCancel}>{t('取消')}</Button>
<Button type='primary' onClick={onOk}>
{t('确定')}
</Button>
</Space>
</div>
}

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

🌐 Web query:

Semi UI Modal custom footer Enter key keyboard confirmation behavior

💡 Result:

In Semi UI Modal, Enter triggers the currently focused element. By default, when the modal opens, focus is on the Cancel button, but you can change that via okButtonProps / cancelButtonProps autoFocus. (semi.design)

If you are using the built-in footer (recommended)

Make OK focused on open, so Enter = OK:

<Modal
  visible={visible}
  onOk={handleOk}
  onCancel={handleCancel}
  okButtonProps={{ autoFocus: true }}
  cancelButtonProps={{ autoFocus: false }}
/>

(Semi documents that initial focus defaults to Cancel and can be controlled by autoFocus in those props.) (semi.design)

If you set a custom footer

When you override footer, you’re responsible for keyboard-confirm behavior. Semi explicitly supports custom footer rendering. (semi.design)
Typical approach: (1) autoFocus your primary button, and/or (2) add an Enter key handler.

const onKeyDown = (e: React.KeyboardEvent) => {
  if (e.key === 'Enter' && !e.shiftKey) {
    // avoid interfering with multiline inputs if you have them
    const tag = (e.target as HTMLElement).tagName;
    if (tag === 'TEXTAREA') return;

    e.preventDefault();
    handleOk();
  }
};

<Modal
  visible={visible}
  onCancel={handleCancel}
  footer={
    <Button type="primary" onClick={handleOk} autoFocus>
      Confirm
    </Button>
  }
>
  <div onKeyDown={onKeyDown}>
    {/* modal content */}
  </div>
</Modal>

If you share your exact footer/content structure (e.g., forms, inputs, textarea), I can tailor the “when to trigger Enter” rules.

Citations:


🏁 Script executed:

# First, let's find and examine the ChannelSelectorModal.jsx file
find . -name "ChannelSelectorModal.jsx" -type f

Repository: QuantumNous/new-api

Length of output: 117


🏁 Script executed:

# Read the ChannelSelectorModal.jsx file to see the full context
cat -n ./web/src/components/settings/ChannelSelectorModal.jsx | head -300

Repository: QuantumNous/new-api

Length of output: 10513


🏁 Script executed:

# Continue reading the rest of the file
cat -n ./web/src/components/settings/ChannelSelectorModal.jsx | tail -n +300

Repository: QuantumNous/new-api

Length of output: 196


Add autoFocus={true} to the OK button or implement Enter-key handling.

The current custom footer lacks keyboard confirmation: the OK button doesn't have autoFocus={true}, and there's no Enter-key handler. Per Semi UI documentation, with a custom footer you're responsible for keyboard behavior. Without an explicit autoFocus or handler, pressing Enter will trigger the Cancel button instead of confirming. Either set autoFocus={true} on the primary Button or add an Enter-key handler to the Modal's content.

🤖 Prompt for AI Agents
In `@web/src/components/settings/ChannelSelectorModal.jsx` around lines 257 - 266,
In ChannelSelectorModal update the custom footer so keyboard Enter confirms
instead of cancelling: either add autoFocus={true} to the primary Button (the
one that calls onOk) or implement an Enter-key handler on the modal/content that
listens for keydown Enter and calls the onOk handler; ensure you keep the
existing onCancel/onOk bindings and focus behavior consistent with Semi UI
(attach the handler at the Modal content wrapper or component mount/unmount
lifecycle to avoid leaking listeners).

@seefs001 seefs001 closed this Feb 3, 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