From 49e77fb3dfe0c1d1829afccda3b1317a0d221811 Mon Sep 17 00:00:00 2001
From: RedwindA
Date: Sat, 7 Jun 2025 20:50:36 +0800
Subject: [PATCH 01/10] feat: ali rerank
---
relay/channel/ali/adaptor.go | 6 ++-
relay/channel/ali/constants.go | 1 +
relay/channel/ali/dto.go | 27 +++++++++++
relay/channel/ali/rerank.go | 83 ++++++++++++++++++++++++++++++++++
4 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 relay/channel/ali/rerank.go
diff --git a/relay/channel/ali/adaptor.go b/relay/channel/ali/adaptor.go
index 31e926d6..f30d4dc4 100644
--- a/relay/channel/ali/adaptor.go
+++ b/relay/channel/ali/adaptor.go
@@ -31,6 +31,8 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
switch info.RelayMode {
case constant.RelayModeEmbeddings:
fullRequestURL = fmt.Sprintf("%s/api/v1/services/embeddings/text-embedding/text-embedding", info.BaseUrl)
+ case constant.RelayModeRerank:
+ fullRequestURL = fmt.Sprintf("%s/api/v1/services/rerank/text-rerank/text-rerank", info.BaseUrl)
case constant.RelayModeImagesGenerations:
fullRequestURL = fmt.Sprintf("%s/api/v1/services/aigc/text2image/image-synthesis", info.BaseUrl)
case constant.RelayModeCompletions:
@@ -76,7 +78,7 @@ func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInf
}
func (a *Adaptor) ConvertRerankRequest(c *gin.Context, relayMode int, request dto.RerankRequest) (any, error) {
- return nil, errors.New("not implemented")
+ return ConvertRerankRequest(request), nil
}
func (a *Adaptor) ConvertEmbeddingRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.EmbeddingRequest) (any, error) {
@@ -103,6 +105,8 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycom
err, usage = aliImageHandler(c, resp, info)
case constant.RelayModeEmbeddings:
err, usage = aliEmbeddingHandler(c, resp)
+ case constant.RelayModeRerank:
+ err, usage = RerankHandler(c, resp, info)
default:
if info.IsStream {
err, usage = openai.OaiStreamHandler(c, resp, info)
diff --git a/relay/channel/ali/constants.go b/relay/channel/ali/constants.go
index 46de5e40..df64439b 100644
--- a/relay/channel/ali/constants.go
+++ b/relay/channel/ali/constants.go
@@ -8,6 +8,7 @@ var ModelList = []string{
"qwq-32b",
"qwen3-235b-a22b",
"text-embedding-v1",
+ "gte-rerank-v2",
}
var ChannelName = "ali"
diff --git a/relay/channel/ali/dto.go b/relay/channel/ali/dto.go
index f51286ad..dbd18968 100644
--- a/relay/channel/ali/dto.go
+++ b/relay/channel/ali/dto.go
@@ -1,5 +1,7 @@
package ali
+import "one-api/dto"
+
type AliMessage struct {
Content string `json:"content"`
Role string `json:"role"`
@@ -97,3 +99,28 @@ type AliImageRequest struct {
} `json:"parameters,omitempty"`
ResponseFormat string `json:"response_format,omitempty"`
}
+
+type AliRerankParameters struct {
+ TopN *int `json:"top_n,omitempty"`
+ ReturnDocuments *bool `json:"return_documents,omitempty"`
+}
+
+type AliRerankInput struct {
+ Query string `json:"query"`
+ Documents []any `json:"documents"`
+}
+
+type AliRerankRequest struct {
+ Model string `json:"model"`
+ Input AliRerankInput `json:"input"`
+ Parameters AliRerankParameters `json:"parameters,omitempty"`
+}
+
+type AliRerankResponse struct {
+ Output struct {
+ Results []dto.RerankResponseResult `json:"results"`
+ } `json:"output"`
+ Usage AliUsage `json:"usage"`
+ RequestId string `json:"request_id"`
+ AliError
+}
diff --git a/relay/channel/ali/rerank.go b/relay/channel/ali/rerank.go
new file mode 100644
index 00000000..c9ae066a
--- /dev/null
+++ b/relay/channel/ali/rerank.go
@@ -0,0 +1,83 @@
+package ali
+
+import (
+ "encoding/json"
+ "io"
+ "net/http"
+ "one-api/dto"
+ relaycommon "one-api/relay/common"
+ "one-api/service"
+
+ "github.com/gin-gonic/gin"
+)
+
+func ConvertRerankRequest(request dto.RerankRequest) *AliRerankRequest {
+ returnDocuments := request.ReturnDocuments
+ if returnDocuments == nil {
+ t := true
+ returnDocuments = &t
+ }
+ return &AliRerankRequest{
+ Model: request.Model,
+ Input: AliRerankInput{
+ Query: request.Query,
+ Documents: request.Documents,
+ },
+ Parameters: AliRerankParameters{
+ TopN: &request.TopN,
+ ReturnDocuments: returnDocuments,
+ },
+ }
+}
+
+func RerankHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) {
+ responseBody, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return service.OpenAIErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil
+ }
+ err = resp.Body.Close()
+ if err != nil {
+ return service.OpenAIErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
+ }
+
+ var aliResponse AliRerankResponse
+ err = json.Unmarshal(responseBody, &aliResponse)
+ if err != nil {
+ return service.OpenAIErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil
+ }
+
+ if aliResponse.Code != "" {
+ return &dto.OpenAIErrorWithStatusCode{
+ Error: dto.OpenAIError{
+ Message: aliResponse.Message,
+ Type: aliResponse.Code,
+ Param: aliResponse.RequestId,
+ Code: aliResponse.Code,
+ },
+ StatusCode: resp.StatusCode,
+ }, nil
+ }
+
+ usage := dto.Usage{
+ PromptTokens: aliResponse.Usage.TotalTokens,
+ CompletionTokens: 0,
+ TotalTokens: aliResponse.Usage.TotalTokens,
+ }
+ rerankResponse := dto.RerankResponse{
+ Results: aliResponse.Output.Results,
+ Usage: usage,
+ }
+
+ jsonResponse, err := json.Marshal(rerankResponse)
+ if err != nil {
+ return service.OpenAIErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), nil
+ }
+ c.Writer.Header().Set("Content-Type", "application/json")
+ c.Writer.WriteHeader(resp.StatusCode)
+ _, err = c.Writer.Write(jsonResponse)
+ if err != nil {
+ return service.OpenAIErrorWrapper(err, "write_response_body_failed", http.StatusInternalServerError), nil
+ }
+
+ return nil, &usage
+}
From e0cc13094f90120d858b9946e5823a9702203ce9 Mon Sep 17 00:00:00 2001
From: "Apple\\Apple"
Date: Sat, 7 Jun 2025 22:50:31 +0800
Subject: [PATCH 02/10] =?UTF-8?q?=F0=9F=94=97feat(ui):=20Enhance=20About?=
=?UTF-8?q?=20page=20with=20interactive=20project=20links=20and=20improve?=
=?UTF-8?q?=20external=20link=20handling?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
**Changes:**
- Replace React Router `Link` components with native `` tags for external links in About and Footer components
- Add clickable links for "NewAPI", "QuantumNous", and "One API v0.5.4" in the About page
- Link "NewAPI" to the main project repository (https://github.com/QuantumNous/new-api)
- Link "QuantumNous" to the organization page (https://github.com/QuantumNous)
- Link "One API v0.5.4" to the specific release page (https://github.com/songquanpeng/one-api/releases/tag/v0.5.4)
- Apply consistent styling with primary color theme and hover effects across all links
- Add proper security attributes (`rel="noopener noreferrer"`) to all external links
**i18n Updates:**
- Refactor i18n translation keys to support the new link structure
- Split the original copyright string into smaller, reusable translation keys
- Add new translation keys: `"© {{currentYear}}"` and `"| 基于"`
- Maintain backward compatibility for existing translations
**Benefits:**
- Improved user experience with direct access to relevant project resources
- Better SEO and link accessibility
- Consistent visual styling across all external links
- Enhanced security for external link navigation
- Proper separation of concerns between internal routing and external navigation
---
web/src/components/layout/Footer.js | 14 ++++----
web/src/i18n/locales/en.json | 8 +++--
web/src/pages/About/index.js | 53 ++++++++++++++++++++++++++---
3 files changed, 60 insertions(+), 15 deletions(-)
diff --git a/web/src/components/layout/Footer.js b/web/src/components/layout/Footer.js
index d53ae2ed..b776c216 100644
--- a/web/src/components/layout/Footer.js
+++ b/web/src/components/layout/Footer.js
@@ -81,14 +81,12 @@ const FooterBar = () => {
© {currentYear} {systemName}. {t('版权所有')}
- {isDemoSiteMode && (
-
- )}
+
), [logo, systemName, t, currentYear, isDemoSiteMode]);
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index 29469d14..d9df1088 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -1404,8 +1404,12 @@
"演示站点": "Demo Site",
"页面未找到,请检查您的浏览器地址是否正确": "Page not found, please check if your browser address is correct",
"New API项目仓库地址:": "New API project repository address: ",
- "NewAPI © {{currentYear}} QuantumNous | 基于 One API v0.5.4 © 2023 JustSong。": "NewAPI © {{currentYear}} QuantumNous | Based on One API v0.5.4 © 2023 JustSong.",
- "本项目根据MIT许可证授权,需在遵守Apache-2.0协议的前提下使用。": "This project is licensed under the MIT License and must be used in compliance with the Apache-2.0 License.",
+ "© {{currentYear}}": "© {{currentYear}}",
+ "| 基于": " | Based on ",
+ "© 2023 JustSong。": "© 2023 JustSong.",
+ "本项目根据": "This project is licensed under the ",
+ "授权,需在遵守": " and must be used in compliance with the ",
+ "的前提下使用。": ".",
"管理员暂时未设置任何关于内容": "The administrator has not set any custom About content yet",
"早上好": "Good morning",
"中午好": "Good afternoon",
diff --git a/web/src/pages/About/index.js b/web/src/pages/About/index.js
index 42ac09e0..78de9185 100644
--- a/web/src/pages/About/index.js
+++ b/web/src/pages/About/index.js
@@ -3,7 +3,6 @@ import { API, showError } from '../../helpers';
import { marked } from 'marked';
import { Empty } from '@douyinfe/semi-ui';
import { IllustrationConstruction, IllustrationConstructionDark } from '@douyinfe/semi-illustrations';
-import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
const About = () => {
@@ -42,14 +41,58 @@ const About = () => {
);
From 387721e9078b9cc38c219fdd427965f58475bd23 Mon Sep 17 00:00:00 2001
From: "Apple\\Apple"
Date: Sat, 7 Jun 2025 22:55:12 +0800
Subject: [PATCH 03/10] =?UTF-8?q?=F0=9F=94=97feat(ui):=20Enhance=20About?=
=?UTF-8?q?=20page=20with=20interactive=20project=20links=20and=20improve?=
=?UTF-8?q?=20external=20link=20handling?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web/src/i18n/locales/en.json | 3 ++-
web/src/pages/About/index.js | 13 ++++++++++---
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index d9df1088..82625b67 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -1406,7 +1406,8 @@
"New API项目仓库地址:": "New API project repository address: ",
"© {{currentYear}}": "© {{currentYear}}",
"| 基于": " | Based on ",
- "© 2023 JustSong。": "© 2023 JustSong.",
+ "MIT许可证": "MIT License",
+ "Apache-2.0协议": "Apache-2.0 License",
"本项目根据": "This project is licensed under the ",
"授权,需在遵守": " and must be used in compliance with the ",
"的前提下使用。": ".",
diff --git a/web/src/pages/About/index.js b/web/src/pages/About/index.js
index 78de9185..78a77163 100644
--- a/web/src/pages/About/index.js
+++ b/web/src/pages/About/index.js
@@ -71,7 +71,14 @@ const About = () => {
className="!text-semi-color-primary hover:!text-semi-color-primary-hover transition-colors"
>
One API v0.5.4
- {t('© 2023 JustSong。')}
+ © 2023
+ JustSong
+
{t('本项目根据')}
@@ -81,7 +88,7 @@ const About = () => {
rel="noopener noreferrer"
className="!text-semi-color-primary hover:!text-semi-color-primary-hover transition-colors"
>
- MIT许可证
+ {t('MIT许可证')}
{t('授权,需在遵守')}
{
rel="noopener noreferrer"
className="!text-semi-color-primary hover:!text-semi-color-primary-hover transition-colors"
>
- Apache-2.0协议
+ {t('Apache-2.0协议')}
{t('的前提下使用。')}
From 33014e93992b406c74a900c161dd121fadff512e Mon Sep 17 00:00:00 2001
From: "Apple\\Apple"
Date: Sat, 7 Jun 2025 23:15:25 +0800
Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=94=97feat(ui):=20Standardize=20lin?=
=?UTF-8?q?k=20colors=20and=20update=20documentation=20URL=20in=20EditChan?=
=?UTF-8?q?nel=20component?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
**Changes:**
- Unify link color styling across EditChannel.js by replacing `text-blue-500` with consistent primary color scheme
- Apply `!text-semi-color-primary hover:!text-semi-color-primary-hover transition-colors` to all template fill and documentation links
- Update documentation URL from Calcium-Ion repository to QuantumNous repository
- Add smooth hover transitions and consistent visual feedback for all clickable links
**Affected Elements:**
- Model mapping template fill link
- Deployment region template fill link
- Channel settings template fill link
- Channel settings documentation link
- Status code mapping template fill link
**Benefits:**
- Consistent visual design language across the entire application
- Improved user experience with unified link styling
- Better accessibility with clear hover states and transitions
- Correct documentation references pointing to the current project repository
**Technical Details:**
- Maintains existing functionality while improving visual consistency
- Links now match the color scheme used in About page and Footer components
- Smooth color transitions enhance user interaction feedback
---
web/src/components/layout/Footer.js | 28 ++++++++++++++--------------
web/src/pages/About/index.js | 14 +++++++-------
web/src/pages/Channel/EditChannel.js | 12 ++++++------
3 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/web/src/components/layout/Footer.js b/web/src/components/layout/Footer.js
index b776c216..4f44c1dc 100644
--- a/web/src/components/layout/Footer.js
+++ b/web/src/components/layout/Footer.js
@@ -40,36 +40,36 @@ const FooterBar = () => {
@@ -83,9 +83,9 @@ const FooterBar = () => {
diff --git a/web/src/pages/About/index.js b/web/src/pages/About/index.js
index 78a77163..3259449e 100644
--- a/web/src/pages/About/index.js
+++ b/web/src/pages/About/index.js
@@ -45,7 +45,7 @@ const About = () => {
href='https://github.com/QuantumNous/new-api'
target="_blank"
rel="noopener noreferrer"
- className="!text-semi-color-primary hover:!text-semi-color-primary-hover transition-colors"
+ className="!text-semi-color-primary"
>
https://github.com/QuantumNous/new-api
@@ -54,28 +54,28 @@ const About = () => {
href="https://github.com/QuantumNous/new-api"
target="_blank"
rel="noopener noreferrer"
- className="!text-semi-color-primary hover:!text-semi-color-primary-hover transition-colors"
+ className="!text-semi-color-primary"
>
NewAPI
{t('© {{currentYear}}', { currentYear })}
QuantumNous
{t('| 基于')}
One API v0.5.4
© 2023
JustSong
@@ -86,7 +86,7 @@ const About = () => {
href="https://github.com/songquanpeng/one-api/blob/v0.5.4/LICENSE"
target="_blank"
rel="noopener noreferrer"
- className="!text-semi-color-primary hover:!text-semi-color-primary-hover transition-colors"
+ className="!text-semi-color-primary"
>
{t('MIT许可证')}
@@ -95,7 +95,7 @@ const About = () => {
href="https://github.com/QuantumNous/new-api/blob/main/LICENSE"
target="_blank"
rel="noopener noreferrer"
- className="!text-semi-color-primary hover:!text-semi-color-primary-hover transition-colors"
+ className="!text-semi-color-primary"
>
{t('Apache-2.0协议')}
diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js
index feed8beb..04dc284b 100644
--- a/web/src/pages/Channel/EditChannel.js
+++ b/web/src/pages/Channel/EditChannel.js
@@ -846,7 +846,7 @@ const EditChannel = (props) => {
className="!rounded-lg font-mono"
/>
handleInputChange('model_mapping', JSON.stringify(MODEL_MAPPING_EXAMPLE, null, 2))}
>
{t('填入模板')}
@@ -940,7 +940,7 @@ const EditChannel = (props) => {
className="!rounded-lg font-mono"
/>
handleInputChange('other', JSON.stringify(REGION_EXAMPLE, null, 2))}
>
{t('填入模板')}
@@ -1062,7 +1062,7 @@ const EditChannel = (props) => {
/>
{
handleInputChange(
'setting',
@@ -1073,10 +1073,10 @@ const EditChannel = (props) => {
{t('填入模板')}
{
window.open(
- 'https://github.com/Calcium-Ion/new-api/blob/main/docs/channel/other_setting.md',
+ 'https://github.com/QuantumNous/new-api/blob/main/docs/channel/other_setting.md',
);
}}
>
@@ -1146,7 +1146,7 @@ const EditChannel = (props) => {
className="!rounded-lg font-mono"
/>
{
handleInputChange(
'status_code_mapping',
From 6e7249cf0655c8c869e2c240ca47a1ff26c6f5f4 Mon Sep 17 00:00:00 2001
From: "Apple\\Apple"
Date: Sat, 7 Jun 2025 23:22:25 +0800
Subject: [PATCH 05/10] =?UTF-8?q?=F0=9F=8E=A8feat(ui):=20Improve=20Chat=20?=
=?UTF-8?q?page=20UI=20and=20add=20i18n=20support?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Replace Banner with full-screen Spin component for better loading UX
- Add English translation for "正在跳转..." ("Redirecting...")
- Integrate i18next translation hook in Chat page component
- Remove unused useEffect import for cleaner code
The Chat page now shows a centered full-screen loading spinner instead of
a banner when redirecting, providing a more consistent and professional
user experience. The loading text is now properly internationalized and
will display "Redirecting..." in English and "正在跳转..." in Chinese.
---
web/src/i18n/locales/en.json | 1 +
web/src/pages/Chat/index.js | 23 +++++++++++++++--------
2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index 82625b67..a3a05acd 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -1536,6 +1536,7 @@
"关闭公告": "Close Notice",
"搜索条件": "Search Conditions",
"加载中...": "Loading...",
+ "正在跳转...": "Redirecting...",
"暂无公告": "No Notice",
"操练场": "Playground",
"欢迎使用,请完成以下设置以开始使用系统": "Welcome to use, please complete the following settings to start using the system",
diff --git a/web/src/pages/Chat/index.js b/web/src/pages/Chat/index.js
index bd4e19a1..22c5c1e2 100644
--- a/web/src/pages/Chat/index.js
+++ b/web/src/pages/Chat/index.js
@@ -1,9 +1,11 @@
-import React, { useEffect } from 'react';
+import React from 'react';
import { useTokenKeys } from '../../hooks/useTokenKeys';
-import { Banner, Layout } from '@douyinfe/semi-ui';
+import { Spin } from '@douyinfe/semi-ui';
import { useParams } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
const ChatPage = () => {
+ const { t } = useTranslation();
const { id } = useParams();
const { keys, serverAddress, isLoading } = useTokenKeys(id);
@@ -40,12 +42,17 @@ const ChatPage = () => {
allow='camera;microphone'
/>
) : (
-
-
-
-
-
-
+
+
+
+
+ {t('正在跳转...')}
+
+
);
};
From 7e9bd35ac77cb9fa54af7bbf014d9b07f3621c6c Mon Sep 17 00:00:00 2001
From: "Apple\\Apple"
Date: Sun, 8 Jun 2025 00:07:37 +0800
Subject: [PATCH 06/10] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(auth):=20re?=
=?UTF-8?q?place=20custom=20loading=20UI=20with=20shared=20Loading=20compo?=
=?UTF-8?q?nent=20and=20add=20i18n=20support?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Replace inline loading UI in OAuth2Callback with shared Loading component
- Add internationalization support using useTranslation hook
- Translate all hardcoded Chinese strings to support multiple languages
- Remove unused processing state variable
- Maintain consistent loading experience across the application
- Support dynamic text content for retry attempts with parameter interpolation
---
web/src/components/auth/OAuth2Callback.js | 33 +++++++++--------------
1 file changed, 12 insertions(+), 21 deletions(-)
diff --git a/web/src/components/auth/OAuth2Callback.js b/web/src/components/auth/OAuth2Callback.js
index 83f9db46..6d0bbe70 100644
--- a/web/src/components/auth/OAuth2Callback.js
+++ b/web/src/components/auth/OAuth2Callback.js
@@ -1,15 +1,16 @@
import React, { useContext, useEffect, useState } from 'react';
-import { Spin, Typography, Space } from '@douyinfe/semi-ui';
import { useNavigate, useSearchParams } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
import { API, showError, showSuccess, updateAPI, setUserData } from '../../helpers';
import { UserContext } from '../../context/User';
+import Loading from '../common/Loading';
const OAuth2Callback = (props) => {
+ const { t } = useTranslation();
const [searchParams, setSearchParams] = useSearchParams();
const [userState, userDispatch] = useContext(UserContext);
- const [prompt, setPrompt] = useState('处理中...');
- const [processing, setProcessing] = useState(true);
+ const [prompt, setPrompt] = useState(t('处理中...'));
let navigate = useNavigate();
@@ -20,25 +21,25 @@ const OAuth2Callback = (props) => {
const { success, message, data } = res.data;
if (success) {
if (message === 'bind') {
- showSuccess('绑定成功!');
- navigate('/setting');
+ showSuccess(t('绑定成功!'));
+ navigate('/console/setting');
} else {
userDispatch({ type: 'login', payload: data });
localStorage.setItem('user', JSON.stringify(data));
setUserData(data);
updateAPI();
- showSuccess('登录成功!');
- navigate('/token');
+ showSuccess(t('登录成功!'));
+ navigate('/console/token');
}
} else {
showError(message);
if (count === 0) {
- setPrompt(`操作失败,重定向至登录界面中...`);
- navigate('/setting'); // in case this is failed to bind GitHub
+ setPrompt(t('操作失败,重定向至登录界面中...'));
+ navigate('/console/setting'); // in case this is failed to bind GitHub
return;
}
count++;
- setPrompt(`出现错误,第 ${count} 次重试中...`);
+ setPrompt(t('出现错误,第 ${count} 次重试中...', { count }));
await new Promise((resolve) => setTimeout(resolve, count * 2000));
await sendCode(code, state, count);
}
@@ -50,17 +51,7 @@ const OAuth2Callback = (props) => {
sendCode(code, state, 0).then();
}, []);
- return (
-
- );
+ return ;
};
export default OAuth2Callback;
From cb83a06103b4c57dc81b2f15bfa3c16c7b32f319 Mon Sep 17 00:00:00 2001
From: "Apple\\Apple"
Date: Sun, 8 Jun 2025 00:33:26 +0800
Subject: [PATCH 07/10] =?UTF-8?q?=F0=9F=94=96chore(ui):=20Improve=20Loadin?=
=?UTF-8?q?g=20prompt?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web/src/components/common/Loading.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/src/components/common/Loading.js b/web/src/components/common/Loading.js
index 980e9cb1..a12be053 100644
--- a/web/src/components/common/Loading.js
+++ b/web/src/components/common/Loading.js
@@ -14,7 +14,7 @@ const Loading = ({ prompt: name = '', size = 'large' }) => {
tip={null}
/>
- {name ? t('加载{{name}}中...', { name }) : t('加载中...')}
+ {name ? t('{{name}}', { name }) : t('加载中...')}
From fcc4d0074f3e8981918179383c3c04807f0050c9 Mon Sep 17 00:00:00 2001
From: "Apple\\Apple"
Date: Sun, 8 Jun 2025 01:08:03 +0800
Subject: [PATCH 08/10] =?UTF-8?q?=F0=9F=90=9B=20fix(auth):=20resolve=20pas?=
=?UTF-8?q?sword=20reset=20confirmation=20display=20and=20functionality=20?=
=?UTF-8?q?issues?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Fix input field display issues in password reset confirmation page
* Replace `readOnly` with `disabled={true}` for proper field state
* Improve URL parameter parsing and state management
* Add proper null checks and fallback values
- Enhance user experience and error handling
* Add validation for invalid reset links
* Display appropriate error messages and placeholders
* Add debug logging for troubleshooting
* Improve button states and loading indicators
- Improve password reset form validation
* Add proper email input validation with error messages
* Enhance user feedback for empty email submissions
- Add missing English translations
* Add i18n support for new UI text strings
* Ensure proper internationalization coverage
The password reset confirmation page now correctly displays email addresses
from URL parameters and prevents user input as intended. Error handling
has been improved to provide better user guidance when reset links are
invalid or malformed.
Fixes: Password reset input fields showing empty and allowing user input
when they should display email/password and be read-only.
---
.../components/auth/PasswordResetConfirm.js | 29 ++++++++++++-------
web/src/components/auth/PasswordResetForm.js | 5 +++-
web/src/i18n/locales/en.json | 4 +++
3 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/web/src/components/auth/PasswordResetConfirm.js b/web/src/components/auth/PasswordResetConfirm.js
index dc69ccdc..e56a9b1a 100644
--- a/web/src/components/auth/PasswordResetConfirm.js
+++ b/web/src/components/auth/PasswordResetConfirm.js
@@ -15,12 +15,12 @@ const PasswordResetConfirm = () => {
token: '',
});
const { email, token } = inputs;
+ const isValidResetLink = email && token;
const [loading, setLoading] = useState(false);
const [disableButton, setDisableButton] = useState(false);
const [countdown, setCountdown] = useState(30);
const [newPassword, setNewPassword] = useState('');
-
const [searchParams, setSearchParams] = useSearchParams();
const logo = getLogo();
@@ -30,10 +30,10 @@ const PasswordResetConfirm = () => {
let token = searchParams.get('token');
let email = searchParams.get('email');
setInputs({
- token,
- email,
+ token: token || '',
+ email: email || '',
});
- }, []);
+ }, [searchParams]);
useEffect(() => {
let countdownInterval = null;
@@ -49,7 +49,10 @@ const PasswordResetConfirm = () => {
}, [disableButton, countdown]);
async function handleSubmit(e) {
- if (!email || !token) return;
+ if (!email || !token) {
+ showError(t('无效的重置链接,请重新发起密码重置请求'));
+ return;
+ }
setDisableButton(true);
setLoading(true);
const res = await API.post(`/api/user/reset`, {
@@ -94,6 +97,11 @@ const PasswordResetConfirm = () => {
{t('密码重置确认')}
+ {!isValidResetLink && (
+
+ {t('无效的重置链接,请重新发起密码重置请求')}
+
+ )}
{
name="email"
size="large"
className="!rounded-md"
- value={email}
- readOnly
+ value={email || ''}
+ disabled={true}
prefix={ }
+ placeholder={email ? '' : t('等待获取邮箱信息...')}
/>
{newPassword && (
@@ -114,7 +123,7 @@ const PasswordResetConfirm = () => {
size="large"
className="!rounded-md"
value={newPassword}
- readOnly
+ disabled={true}
prefix={ }
onClick={(e) => {
e.target.select();
@@ -133,9 +142,9 @@ const PasswordResetConfirm = () => {
size="large"
onClick={handleSubmit}
loading={loading}
- disabled={disableButton || newPassword}
+ disabled={disableButton || newPassword || !isValidResetLink}
>
- {newPassword ? t('密码重置完成') : t('提交')}
+ {newPassword ? t('密码重置完成') : t('确认重置密码')}
diff --git a/web/src/components/auth/PasswordResetForm.js b/web/src/components/auth/PasswordResetForm.js
index 6abdfaa5..4ff7882f 100644
--- a/web/src/components/auth/PasswordResetForm.js
+++ b/web/src/components/auth/PasswordResetForm.js
@@ -55,7 +55,10 @@ const PasswordResetForm = () => {
}
async function handleSubmit(e) {
- if (!email) return;
+ if (!email) {
+ showError(t('请输入邮箱地址'));
+ return;
+ }
if (turnstileEnabled && turnstileToken === '') {
showInfo(t('请稍后几秒重试,Turnstile 正在检查用户环境!'));
return;
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index a3a05acd..a7201fe5 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -269,6 +269,10 @@
"密码重置确认": "Password Reset Confirmation",
"邮箱地址": "Email address",
"提交": "Submit",
+ "等待获取邮箱信息...": "Waiting to get email information...",
+ "确认重置密码": "Confirm Password Reset",
+ "无效的重置链接,请重新发起密码重置请求": "Invalid reset link, please initiate a new password reset request",
+ "请输入邮箱地址": "Please enter the email address",
"请稍后几秒重试": "Please retry in a few seconds",
"正在检查用户环境": "Checking user environment",
"重置邮件发送成功": "Reset mail sent successfully",
From d3b93196cfc1cbe06c6b86cb944272976bb32ec7 Mon Sep 17 00:00:00 2001
From: "Apple\\Apple"
Date: Sun, 8 Jun 2025 01:44:38 +0800
Subject: [PATCH 09/10] =?UTF-8?q?=E2=9C=A8chore(PasswordResetConfirm):=20I?=
=?UTF-8?q?mprove=20password=20reset=20confirm=20UI=20and=20fix=20form=20d?=
=?UTF-8?q?ata=20binding?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Replace error message div with Semi UI Banner component for better UX
- Add rounded corners to Banner component with !rounded-lg class
- Fix Form.Input not displaying values by implementing proper formApi usage
- Use getFormApi callback to obtain form API instance
- Replace manual value props with formApi.setValues() for dynamic updates
- Set proper initValues for form initialization
- Remove unused Input import and console.log statements
- Clean up debugging code and optimize form state management
This change enhances the visual consistency with Semi Design system
and resolves the issue where email field was not showing URL parameter values.
---
.../components/auth/PasswordResetConfirm.js | 28 +++++++++++++------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/web/src/components/auth/PasswordResetConfirm.js b/web/src/components/auth/PasswordResetConfirm.js
index e56a9b1a..e799583f 100644
--- a/web/src/components/auth/PasswordResetConfirm.js
+++ b/web/src/components/auth/PasswordResetConfirm.js
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { API, copy, showError, showNotice, getLogo, getSystemName } from '../../helpers';
import { useSearchParams, Link } from 'react-router-dom';
-import { Button, Card, Form, Typography } from '@douyinfe/semi-ui';
+import { Button, Card, Form, Typography, Banner } from '@douyinfe/semi-ui';
import { IconMail, IconLock } from '@douyinfe/semi-icons';
import { useTranslation } from 'react-i18next';
import Background from '/example.png';
@@ -22,6 +22,7 @@ const PasswordResetConfirm = () => {
const [countdown, setCountdown] = useState(30);
const [newPassword, setNewPassword] = useState('');
const [searchParams, setSearchParams] = useSearchParams();
+ const [formApi, setFormApi] = useState(null);
const logo = getLogo();
const systemName = getSystemName();
@@ -33,7 +34,13 @@ const PasswordResetConfirm = () => {
token: token || '',
email: email || '',
});
- }, [searchParams]);
+ if (formApi) {
+ formApi.setValues({
+ email: email || '',
+ newPassword: newPassword || ''
+ });
+ }
+ }, [searchParams, newPassword, formApi]);
useEffect(() => {
let countdownInterval = null;
@@ -98,18 +105,24 @@ const PasswordResetConfirm = () => {
{!isValidResetLink && (
-
- {t('无效的重置链接,请重新发起密码重置请求')}
-
+
)}
-
}
placeholder={email ? '' : t('等待获取邮箱信息...')}
@@ -122,7 +135,6 @@ const PasswordResetConfirm = () => {
name="newPassword"
size="large"
className="!rounded-md"
- value={newPassword}
disabled={true}
prefix={
}
onClick={(e) => {
From 6bf8a720110674aead18d673e2f0fd24db76b3a3 Mon Sep 17 00:00:00 2001
From: "Apple\\Apple"
Date: Sun, 8 Jun 2025 02:23:47 +0800
Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=94=A7=20fix(auth):=20add=20copy=20?=
=?UTF-8?q?button=20to=20disabled=20password=20input=20in=20reset=20confir?=
=?UTF-8?q?mation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Import IconCopy from semi-icons for copy functionality
- Replace onClick handler with suffix copy button to fix disabled input issue
- Use borderless tertiary button as input suffix for better alignment
- Update notification messages formatting (colon spacing)
- Ensure password copying works even when input field is disabled
---
.../components/auth/PasswordResetConfirm.js | 22 +++++++++++++------
web/src/i18n/locales/en.json | 3 ++-
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/web/src/components/auth/PasswordResetConfirm.js b/web/src/components/auth/PasswordResetConfirm.js
index e799583f..025161ac 100644
--- a/web/src/components/auth/PasswordResetConfirm.js
+++ b/web/src/components/auth/PasswordResetConfirm.js
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { API, copy, showError, showNotice, getLogo, getSystemName } from '../../helpers';
import { useSearchParams, Link } from 'react-router-dom';
import { Button, Card, Form, Typography, Banner } from '@douyinfe/semi-ui';
-import { IconMail, IconLock } from '@douyinfe/semi-icons';
+import { IconMail, IconLock, IconCopy } from '@douyinfe/semi-icons';
import { useTranslation } from 'react-i18next';
import Background from '/example.png';
@@ -71,7 +71,7 @@ const PasswordResetConfirm = () => {
let password = res.data.data;
setNewPassword(password);
await copy(password);
- showNotice(`${t('密码已重置并已复制到剪贴板')}: ${password}`);
+ showNotice(`${t('密码已重置并已复制到剪贴板:')} ${password}`);
} else {
showError(message);
}
@@ -137,11 +137,19 @@ const PasswordResetConfirm = () => {
className="!rounded-md"
disabled={true}
prefix={ }
- onClick={(e) => {
- e.target.select();
- navigator.clipboard.writeText(newPassword);
- showNotice(`${t('密码已复制到剪贴板')}: ${newPassword}`);
- }}
+ suffix={
+ }
+ type="tertiary"
+ theme="borderless"
+ onClick={async () => {
+ await copy(newPassword);
+ showNotice(`${t('密码已复制到剪贴板:')} ${newPassword}`);
+ }}
+ >
+ {t('复制')}
+
+ }
/>
)}
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index a7201fe5..520e30c2 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -265,7 +265,8 @@
"设置页脚": "Set Footer",
"新版本": "New Version",
"关闭": "Close",
- "密码已重置并已复制到剪贴板": "Password has been reset and copied to clipboard",
+ "密码已重置并已复制到剪贴板:": "Password has been reset and copied to clipboard: ",
+ "密码已复制到剪贴板:": "Password has been copied to clipboard: ",
"密码重置确认": "Password Reset Confirmation",
"邮箱地址": "Email address",
"提交": "Submit",