fix: prevent redirect loop by FRONTEND_BASE_URL configuration - #1228
fix: prevent redirect loop by FRONTEND_BASE_URL configuration#1228feitianbubu wants to merge 2950 commits into
Conversation
feat: streaming response for tts
…ON schema, including support for $defs and conditional keywords
- Add CustomRequestEditor component with JSON validation and real-time formatting - Implement bidirectional sync between chat messages and custom request body - Add persistent local storage for chat messages (separate from config) - Remove redundant System Prompt field in custom mode - Refactor configuration storage to separate messages and settings New Features: • Custom request body mode with JSON editor and syntax highlighting • Real-time bidirectional synchronization between chat UI and custom request body • Persistent message storage that survives page refresh • Enhanced configuration export/import including message data • Improved parameter organization with collapsible sections Technical Changes: • Add loadMessages/saveMessages functions in configStorage • Update usePlaygroundState hook to handle message persistence • Refactor SettingsPanel to remove System Prompt in custom mode • Add STORAGE_KEYS constants for better storage key management • Implement debounced auto-save for both config and messages • Add hash-based change detection to prevent unnecessary updates UI/UX Improvements: • Disabled state styling for parameters in custom mode • Warning banners and visual feedback for mode switching • Mobile-responsive design for custom request editor • Consistent styling with existing design system
Fix role toggle functionality where switching message roles (assistant/system) did not update the UI immediately and required page refresh to see changes. Changes: - Add message.role comparison in OptimizedMessageContent memo function - Add message.role comparison in OptimizedMessageActions memo function The issue was caused by React.memo optimization that wasn't tracking role changes, preventing re-renders when only the message role property changed. Now role switches are reflected immediately in both message content display and action button states. Fixes: Role switching requires page refresh to display correctly
Hide y-axis scrollbars to provide a cleaner UI experience while maintaining scroll functionality through mouse wheel and keyboard navigation. Changes include: - Hide scrollbars in CustomRequestEditor TextArea component - Hide scrollbars in chat container and all related chat components - Hide scrollbars in thinking content areas - Add cross-browser compatibility for scrollbar hiding - Maintain scroll functionality while improving visual aesthetics Components affected: - CustomRequestEditor.js: Added custom-request-textarea class - index.css: Updated scrollbar styles for chat, thinking, and editor areas The interface now provides a more streamlined appearance consistent with modern UI design patterns while preserving all interactive capabilities.
- Remove duplicate onRoleToggle prop passing to ChatArea component in Playground/index.js - Move Toast notification outside setMessage callback in useMessageActions hook - Prevent multiple event bindings that caused repeated role switch notifications - Add early return validation for role toggle eligibility This fixes the issue where users would see multiple success toasts when switching between Assistant and System roles in the chat interface. Files changed: - web/src/pages/Playground/index.js - web/src/hooks/useMessageActions.js
…ton with timestamp - Move reset settings button to the same row as the last modified timestamp - Use flexbox layout with justify-between to align timestamp left and reset button right - Keep export and import buttons on the separate row below - Improve space utilization and visual hierarchy in the settings panel This change enhances the user interface by creating a more compact and intuitive layout for the configuration management controls in the playground component.
Remove the 5-image upload restriction in playground and enhance UI consistency Changes: - Remove 5-image limit constraint from ImageUrlInput component - Update hint text to remove "maximum 5 images" references - Add custom scrollbar styling for image list to match site-wide design - Apply consistent thin scrollbar (6px width) with Semi Design color variables - Maintain hover effects and rounded corners for better UX Breaking Changes: None Files modified: - web/src/components/playground/ImageUrlInput.js - web/src/index.css This change allows users to upload unlimited images in playground mode while maintaining visual consistency across the application's scrollable elements.
…te route transition flicker ## Breaking Changes - Remove backward compatibility layer for old action types - StyleContext is no longer exported, use useStyle hook instead ## Major Improvements - **Architecture**: Replace useState with useReducer for complex state management - **Performance**: Add debounced resize handling and batch updates via BATCH_UPDATE action - **DX**: Export useStyle hook and styleActions for type-safe usage - **Memory**: Use useMemo to cache context value and prevent unnecessary re-renders ## Bug Fixes - **UI**: Eliminate padding flicker when navigating to /console/chat* and /console/playground routes - **Logic**: Remove redundant localStorage operations and state synchronization ## Implementation Details - Define ACTION_TYPES and ROUTE_PATTERNS constants for better maintainability - Add comprehensive JSDoc documentation for all functions - Extract custom hooks: useWindowResize, useRouteChange, useMobileSiderAutoHide - Calculate shouldInnerPadding directly in PageLayout based on pathname to prevent async updates - Integrate localStorage saving logic into SET_SIDER_COLLAPSED reducer case - Remove SET_INNER_PADDING action as it's no longer needed ## Updated Components - PageLayout.js: Direct padding calculation based on route - HeaderBar.js: Use new useStyle hook and styleActions - SiderBar.js: Remove redundant localStorage calls - LogsTable.js: Remove unused StyleContext import - Playground/index.js: Migrate to new API ## Performance Impact - Reduced component re-renders through optimized context structure - Eliminated unnecessary effect dependencies and state updates - Improved route transition smoothness with synchronous padding calculation
- Add consistent title section with gradient icon and heading - Include close button in mobile view for better UX consistency - Standardize mobile and desktop ConfigManager styling - Adjust layout structure and padding for visual alignment - Use Settings icon with purple-to-pink gradient to match design system This change ensures both SettingsPanel and DebugPanel have identical header layouts and interaction patterns across all screen sizes.
…nt rendering - Remove duplicate thinking content rendering logic from MessageContent component - Import and utilize ThinkingContent component for consistent thinking display - Clean up unused icon imports (ChevronRight, ChevronUp, Brain) - Consolidate "思考中..." header text logic into single component - Reduce code duplication by ~70 lines while maintaining all functionality - Improve component separation of concerns and maintainability The MessageContent component now delegates thinking content rendering to the dedicated ThinkingContent component, eliminating the previously duplicated UI logic and state management for thinking processes.
…e handling Summary This commit addresses two critical issues affecting the real-time chat experience in the Playground: 1. Optimized re-rendering of reasoning content • Added `reasoningContent` to the comparison function of `OptimizedMessageContent` (`web/src/components/playground/OptimizedComponents.js`). • Ensures the component re-renders while reasoning text streams, resolving the bug where only the first characters (“好,”) were shown until the stream finished. 2. Defensive checks for SSE message updates • Added early-return guards in `streamMessageUpdate` (`web/src/hooks/useApiRequest.js`). • Skips updates when `lastMessage` is undefined or the last message isn’t from the assistant, preventing `TypeError: Cannot read properties of undefined (reading 'status')` during rapid SSE responses. Impact • Real-time reasoning content now appears progressively, enhancing user feedback. • Eliminates runtime crashes caused by undefined message references, improving overall stability.
- Refactor message saving strategy from automatic to manual saving - Save messages only on key operations: send, complete, edit, delete, role toggle, clear - Prevent frequent localStorage writes during streaming responses - Remove excessive console logging - Remove all console.log statements from save/load operations - Clean up debug logs to reduce console noise - Optimize initial state loading with lazy initialization - Replace useRef with useState lazy initialization for config and messages - Ensure loadConfig and loadMessages are called only once on mount - Prevent redundant localStorage reads during re-renders - Update hooks to support new save strategy - Pass saveMessages callback through component hierarchy - Add saveMessagesImmediately to relevant hooks (useApiRequest, useMessageActions, useMessageEdit) - Trigger saves at appropriate lifecycle points This significantly improves performance by reducing localStorage I/O operations from continuous writes during streaming to discrete saves at meaningful points.
- Move useEffect hooks before conditional returns in MessageContent and ThinkingContent - Ensure hooks are called in the same order every render - Fix "Rendered fewer hooks than expected" error when API returns non-200 status - Follow React hooks rules: only call hooks at the top level This prevents the entire page from crashing when API requests fail.
- Modify saveMessagesImmediately to accept messages parameter - Pass updated message list to all save calls instead of relying on closure - Ensure complete message history is saved including the last message - Fix timing issue where old message state was being saved This fixes the issue where the last conversation was not being persisted to localStorage.
…ard UI Summary • Centralized uptime status definition via `uptimeStatusMap`, containing color / label / text for each status. • Generated `uptimeLegendData`, `getUptimeStatusColor`, `getUptimeStatusText` directly from the map, removing multiple switch-case blocks. UI Improvements 1. Added statuses 2 (High Latency) & 3 (Maintenance) with dedicated colors. 2. Relocated status legend to a styled footer wrapped in a borderless sub-Card; header now only shows title + refresh button. 3. Footer (and its negative margin) renders only when `uptimeData` is present, preventing empty legend display. 4. Applied rounded, blurred badge style and always-on shadow to legend container for clearer separation. Maintenance • Simplified code paths, reduced duplication, and improved readability without breaking existing functionality.
fix playground-sse
…siblity-settting-channel feat: add column visibility settings for channels
* Removed `size="middle"` and `centered` props from the column-selector `Modal` in `ChannelsTable.js` to match the visual style used in `LogsTable`. * Re-added `size="middle"` to the main `Table` component to preserve the original table sizing. * Ensures consistent UI/UX across both channel and log column settings modals.
Previously, the uptime status endpoint returned HTTP 400 with “未配置 Uptime Kuma URL/Slug” when either option was not set, resulting in frontend error states. Changes: • Treat absence of `UptimeKumaUrl` or `UptimeKumaSlug` as a valid scenario. • Immediately respond with HTTP 200, `success: true`, and an empty `data` array. • Preserve existing behavior when both options are provided. This prevents unnecessary error notifications on the dashboard when Uptime Kuma integration is not configured and improves overall UX.
…the dashboard to resolve the issue where the last monitoring item is obscured
… tag aggregation SUMMARY • Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination. • Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure. • Implemented counting helpers: – model/token.go CountUserTokens – model/task.go TaskCountAllTasks / TaskCountAllUserTask – model/midjourney.go CountAllTasks / CountAllUserTask – model/channel.go CountAllChannels / CountAllTags • Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns. • Redesigned `ChannelsTable.js`: – `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion. – Fixed runtime white-screen (maximum call-stack) by removing child duplication. – Pagination, search, tag-mode, idSort all hot-reload correctly. • Removed unused `log` import in controller/midjourney.go. BREAKING CHANGES Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
- Replace `showTotal` with `formatPageText` in Dashboard table components - Unify pagination text format to match table components pattern - Update SettingsAnnouncements.js, SettingsAPIInfo.js, and SettingsFAQ.js - Change from "共 X 条记录,显示第 Y-Z 条" to "第 Y - Z 条,共 X 条" format - Ensure consistent user experience across all table components This change improves UI consistency by standardizing the pagination text format across Dashboard and table components.
Fix the search button loading state to be consistent with other table components.
The search button now properly shows loading animation when the table data is
being fetched.
Changes:
- Update search button loading prop from `loading={searching}` to
`loading={loading || searching}` in TokensTable.js
- This ensures loading state is shown both when searching with keywords
(searching=true) and when loading default data (loading=true)
- Aligns with the behavior of other table components like ChannelsTable,
UsersTable, and RedemptionsTable
Before: Search button only showed loading when searching with keywords
After: Search button shows loading for all table data fetch operations
Changes
1. web/src/helpers/token.js
• `fetchTokenKeys` now calls `/api/token/?p=1&size=10` (1-based paging).
• Supports new response shape `{ items, total, page, page_size }`; falls back gracefully if array is returned.
• Filters active tokens from `tokenItems`, not `data` directly.
`useTokenKeys` remains unchanged—its consumer code receives the same list of active keys.
…ed clarity and consistency in function signatures
- Add IP field to Log model with database index and default empty value - Implement conditional IP recording based on user setting in RecordConsumeLog and RecordErrorLog - Add UserSettingRecordIpLog constant and update user settings API to handle record_ip_log field - Create dedicated "IP记录" tab in personal settings under "其他设置" section - Add IP column to logs table with help tooltip explaining recording conditions - Make IP column visible to all users (not admin-only) with proper filtering for consume/error log types - Restrict display of use_time and retry columns to consume and error log types only - Update personal settings UI structure: rename "通知设置" to "其他设置" to accommodate new functionality - Add proper translation support and maintain consistent styling across components The IP logging feature is disabled by default and only records client IP addresses for consume (type 2) and error (type 5) logs when explicitly enabled by users in their personal settings.
…tings save operation - Change success message from "通知设置已更新" to "设置保存成功" - Change error message from "更新通知设置失败" to "设置保存失败" - Makes messages more generic since the function saves multiple types of settings (notification, pricing, IP logging) not just notification settings
WalkthroughA check was added to the Changes
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
router/main.go (1)
28-37: ParsefrontendBaseUrlonce instead of on every request
url.Parse(frontendBaseUrl)runs for every 404 request, which is unnecessary overhead and allocations.
Parse it once after reading the env var and capture the parsed result in the closure.- frontendBaseUrl = strings.TrimSuffix(frontendBaseUrl, "/") - router.NoRoute(func(c *gin.Context) { - if frontendURL, err := url.Parse(frontendBaseUrl); err == nil { + frontendBaseUrl = strings.TrimSuffix(frontendBaseUrl, "/") + frontendURL, _ := url.Parse(frontendBaseUrl) // error already ruled out by env-var validation + router.NoRoute(func(c *gin.Context) { + if frontendURL != nil { ...This avoids repeated parsing and keeps the hot path minimal.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
router/main.go(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
router/main.go (1)
common/logger.go (1)
LogWarn(64-66)
| if frontendURL, err := url.Parse(frontendBaseUrl); err == nil { | ||
| if frontendURL.Host == c.Request.Host { | ||
| common.LogWarn(c, "Misconfiguration detected: FRONTEND_BASE_URL is set to the address of the slave node itself, which would cause a redirect loop. Please set FRONTEND_BASE_URL to the address of the master node.") | ||
| c.JSON(http.StatusInternalServerError, gin.H{ | ||
| "success": false, | ||
| "message": "The service is temporarily unavailable due to a configuration issue. Please contact the administrator.", | ||
| }) | ||
| return | ||
| } |
There was a problem hiding this comment.
Compile-time type mismatch in common.LogWarn call
common.LogWarn expects a context.Context, but you’re passing *gin.Context.
*gin.Context does not implement the full context.Context interface, so this will not compile.
- common.LogWarn(c, "Misconfiguration detected: FRONTEND_BASE_URL is set to the address of the slave node itself, which would cause a redirect loop. Please set FRONTEND_BASE_URL to the address of the master node.")
+ common.LogWarn(c.Request.Context(), "Misconfiguration detected: FRONTEND_BASE_URL is set to the address of the slave node itself, which would cause a redirect loop. Please set FRONTEND_BASE_URL to the address of the master node.")Alternatively, expose a helper that accepts *gin.Context and extracts the underlying request context internally.
🤖 Prompt for AI Agents
In router/main.go around lines 28 to 36, the call to common.LogWarn incorrectly
passes *gin.Context instead of context.Context, causing a compile-time type
mismatch. To fix this, extract the context.Context from the *gin.Context by
using c.Request.Context() and pass that to common.LogWarn. Alternatively, create
a helper function that accepts *gin.Context and internally calls common.LogWarn
with the extracted context.
There was a problem hiding this comment.
gin.Context 实现了 context.Context
Gin 从 v1.8.0 起就在 Context 里实现了
Deadline() (time.Time, bool)
Done() <-chan struct{}
Err() error
Value(key any) any
目前go.mod使用的是gin v1.9.1
There was a problem hiding this comment.
🧩 Analysis chain
@feitianbubu 您说得对!我检查了代码库,确实 Gin v1.9.1 中的 gin.Context 实现了 context.Context 接口。感谢您的纠正。
我之前的分析是错误的,common.LogWarn(c, ...) 这个调用是完全正确的,不会有编译时类型不匹配的问题。
🏁 Script executed:
#!/bin/bash
# Check the Gin version in go.mod
echo "=== Checking Gin version in go.mod ==="
cat go.mod | grep gin
# Search for gin.Context definition and its interface implementations
echo -e "\n=== Searching for gin.Context definition ==="
rg -A 10 -B 5 "type Context struct" --type go
# Look for context.Context interface implementation methods in gin.Context
echo -e "\n=== Checking if gin.Context implements context.Context methods ==="
rg -A 5 "func.*Context.*Deadline|func.*Context.*Done|func.*Context.*Err|func.*Context.*Value" --type go
Length of output: 53031
@feitianbubu 您说得对!从脚本输出可以确认:
- Gin 版本:
go.mod显示确实使用的是github.com/gin-gonic/gin v1.9.1 - 代码验证:整个代码库中有大量地方将
*gin.Context直接传递给期望context.Context的函数,比如common.LogError(c, ...)等
您提到的四个方法 Deadline(), Done(), Err(), Value() 确实在 Gin v1.8.0+ 中已经实现,所以 common.LogWarn(c, ...) 这个调用是完全正确的。
我之前的分析是错误的,感谢您的纠正!
🧬 Code Graph Analysis Results
Relevant snippets for understanding router/main.go and the user comment about gin.Context implementing context.Context:
File: router/main.go (lines 1-44)
package router
import (
"embed"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"net/url"
"one-api/common"
"os"
"strings"
)
func SetRouter(router *gin.Engine, buildFS embed.FS, indexPage []byte) {
SetApiRouter(router)
SetDashboardRouter(router)
SetRelayRouter(router)
frontendBaseUrl := os.Getenv("FRONTEND_BASE_URL")
if common.IsMasterNode && frontendBaseUrl != "" {
frontendBaseUrl = ""
common.SysLog("FRONTEND_BASE_URL is ignored on master node")
}
if frontendBaseUrl == "" {
SetWebRouter(router, buildFS, indexPage)
} else {
frontendBaseUrl = strings.TrimSuffix(frontendBaseUrl, "/")
router.NoRoute(func(c *gin.Context) {
if frontendURL, err := url.Parse(frontendBaseUrl); err == nil {
if frontendURL.Host == c.Request.Host {
common.LogWarn(c, "Misconfiguration detected: FRONTEND_BASE_URL is set to the address of the slave node itself, which would cause a redirect loop. Please set FRONTEND_BASE_URL to the address of the master node.")
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "The service is temporarily unavailable due to a configuration issue. Please contact the administrator.",
})
return
}
}
c.Redirect(http.StatusMovedPermanently, fmt.Sprintf("%s%s", frontendBaseUrl, c.Request.RequestURI))
})
}
}- This is the main router setup function.
- It uses
gin.Contextin theNoRoutehandler. - The user comment mentions that
gin.Contextimplementscontext.Contextinterface since Gin v1.8.0, which is relevant here becausecommon.LogWarnis called withc(a*gin.Context) as the first argument, implying it accepts acontext.Context. - The code also uses environment variable
FRONTEND_BASE_URLand a global flagcommon.IsMasterNode.
File: common/constants.go (line 110)
IsMasterNode bool- Global boolean flag indicating if the current node is the master node.
- Used in
router/main.goto conditionally ignoreFRONTEND_BASE_URL.
File: common/logger.go (lines 50-53 and 64-66)
func SysLog(s string) {
t := time.Now()
_, _ = fmt.Fprintf(gin.DefaultWriter, "[SYS] %v | %s \n", t.Format("2006/01/02 - 15:04:05"), s)
}- Logs system-level messages with timestamp to Gin's default writer.
func LogWarn(ctx context.Context, msg string) {
logHelper(ctx, loggerWarn, msg)
}- Logs warning messages.
- Accepts a
context.Contextas the first parameter. - In
router/main.go,LogWarnis called withc(a*gin.Context), which aligns with the user comment thatgin.Contextimplementscontext.Context.
File: router/relay-router.go (lines 11-91)
- Defines
SetRelayRouter(router *gin.Engine)which is called inSetRouter. - Sets up various route groups with middleware and handlers.
- Shows usage of Gin router groups and middleware, consistent with the style in
main.go.
File: router/api-router.go (lines 11-164)
- Defines
SetApiRouter(router *gin.Engine)which is called inSetRouter. - Sets up many API routes with middleware and handlers.
- Shows typical Gin route group and middleware usage.
Summary of relevance:
- The main file
router/main.gousesgin.Contextin a way that relies on it implementingcontext.Context(e.g., passingctocommon.LogWarn). common/logger.goshowsLogWarnacceptscontext.Context, confirming the interface usage.common/constants.goprovides theIsMasterNodeflag used inmain.go.- The router setup functions in
router/api-router.goandrouter/relay-router.goare relevant for understanding the overall router setup called fromSetRouter. - The user comment about Gin version and
gin.Contextimplementingcontext.Contextis directly related to the usage pattern inrouter/main.go.
If you need further details or specific explanations about any part, please ask.
✏️ Learnings added
Learnt from: feitianbubu
PR: QuantumNous/new-api#1228
File: router/main.go:28-36
Timestamp: 2025-06-15T12:38:11.781Z
Learning: gin.Context implements context.Context interface since Gin v1.8.0, providing the methods Deadline(), Done(), Err(), and Value(). When using Gin v1.8.0 or later, gin.Context can be passed directly to functions expecting context.Context without needing to extract c.Request.Context().
Learnt from: feitianbubu
PR: QuantumNous/new-api#1228
File: router/main.go:28-36
Timestamp: 2025-06-15T12:38:11.781Z
Learning: gin.Context implements context.Context interface since Gin v1.8.0, providing the methods Deadline(), Done(), Err(), and Value(). When using Gin v1.8.0 or later, gin.Context can be passed directly to functions expecting context.Context without needing to extract c.Request.Context().
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
当节点配置为slave(NODE_TYPE=slave), 且FRONTEND_BASE_URL配置为当前请求url的话
请求首页会导致无限循环重定向
增加一个判断提前返回给用户一个友好提示并在后端记录警告日志
Summary by CodeRabbit