Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
7607da23
authored
Jun 08, 2025
by
Calcium-Ion
Committed by
GitHub
Jun 08, 2025
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1177 from QuantumNous/alpha
merge alpha to main
parents
258bad0a
09c6c2c5
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
290 additions
and
86 deletions
+290
-86
relay/channel/ali/adaptor.go
+5
-1
relay/channel/ali/constants.go
+1
-0
relay/channel/ali/dto.go
+27
-0
relay/channel/ali/rerank.go
+83
-0
web/src/components/auth/OAuth2Callback.js
+12
-21
web/src/components/auth/PasswordResetConfirm.js
+49
-20
web/src/components/auth/PasswordResetForm.js
+4
-1
web/src/components/common/Loading.js
+1
-1
web/src/components/layout/Footer.js
+18
-20
web/src/i18n/locales/en.json
+14
-3
web/src/pages/About/index.js
+55
-5
web/src/pages/Channel/EditChannel.js
+6
-6
web/src/pages/Chat/index.js
+15
-8
No files found.
relay/channel/ali/adaptor.go
View file @
7607da23
...
...
@@ -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
)
...
...
relay/channel/ali/constants.go
View file @
7607da23
...
...
@@ -8,6 +8,7 @@ var ModelList = []string{
"qwq-32b"
,
"qwen3-235b-a22b"
,
"text-embedding-v1"
,
"gte-rerank-v2"
,
}
var
ChannelName
=
"ali"
relay/channel/ali/dto.go
View file @
7607da23
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
}
relay/channel/ali/rerank.go
0 → 100644
View file @
7607da23
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
}
web/src/components/auth/OAuth2Callback.js
View file @
7607da23
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
(
<
div
className
=
"flex items-center justify-center min-h-[300px] w-full bg-white rounded-lg shadow p-6"
>
<
Space
vertical
align
=
"center"
>
<
Spin
size
=
"large"
spinning
=
{
processing
}
>
<
div
className
=
"min-h-[200px] min-w-[200px] flex items-center justify-center"
>
<
Typography
.
Text
type
=
"secondary"
>
{
prompt
}
<
/Typography.Text
>
<
/div
>
<
/Spin
>
<
/Space
>
<
/div
>
);
return
<
Loading
prompt
=
{
prompt
}
/>
;
};
export
default
OAuth2Callback
;
web/src/components/auth/PasswordResetConfirm.js
View file @
7607da23
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
{
IconMail
,
IconLock
}
from
'@douyinfe/semi-icons'
;
import
{
Button
,
Card
,
Form
,
Typography
,
Banner
}
from
'@douyinfe/semi-ui'
;
import
{
IconMail
,
IconLock
,
IconCopy
}
from
'@douyinfe/semi-icons'
;
import
{
useTranslation
}
from
'react-i18next'
;
import
Background
from
'/example.png'
;
...
...
@@ -15,13 +15,14 @@ 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
[
formApi
,
setFormApi
]
=
useState
(
null
);
const
logo
=
getLogo
();
const
systemName
=
getSystemName
();
...
...
@@ -30,10 +31,16 @@ const PasswordResetConfirm = () => {
let
token
=
searchParams
.
get
(
'token'
);
let
email
=
searchParams
.
get
(
'email'
);
setInputs
({
token
,
email
,
token
:
token
||
''
,
email
:
email
||
''
,
});
},
[]);
if
(
formApi
)
{
formApi
.
setValues
({
email
:
email
||
''
,
newPassword
:
newPassword
||
''
});
}
},
[
searchParams
,
newPassword
,
formApi
]);
useEffect
(()
=>
{
let
countdownInterval
=
null
;
...
...
@@ -49,7 +56,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`
,
{
...
...
@@ -61,7 +71,7 @@ const PasswordResetConfirm = () => {
let
password
=
res
.
data
.
data
;
setNewPassword
(
password
);
await
copy
(
password
);
showNotice
(
`
${
t
(
'密码已重置并已复制到剪贴板
'
)}
:
${
password
}
`
);
showNotice
(
`
${
t
(
'密码已重置并已复制到剪贴板
:'
)}
${
password
}
`
);
}
else
{
showError
(
message
);
}
...
...
@@ -94,16 +104,28 @@ const PasswordResetConfirm = () => {
<
Title
heading
=
{
3
}
className
=
"text-gray-800 dark:text-gray-200"
>
{
t
(
'密码重置确认'
)}
<
/Title
>
<
/div
>
<
div
className
=
"px-2 py-8"
>
<
Form
className
=
"space-y-3"
>
{
!
isValidResetLink
&&
(
<
Banner
type
=
"danger"
description
=
{
t
(
'无效的重置链接,请重新发起密码重置请求'
)}
className
=
"mb-4 !rounded-lg"
closeIcon
=
{
null
}
/
>
)}
<
Form
getFormApi
=
{(
api
)
=>
setFormApi
(
api
)}
initValues
=
{{
email
:
email
||
''
,
newPassword
:
newPassword
||
''
}}
className
=
"space-y-4"
>
<
Form
.
Input
field
=
"email"
label
=
{
t
(
'邮箱'
)}
name
=
"email"
size
=
"large"
className
=
"!rounded-md"
value
=
{
email
}
readOnly
disabled
=
{
true
}
prefix
=
{
<
IconMail
/>
}
placeholder
=
{
email
?
''
:
t
(
'等待获取邮箱信息...'
)}
/
>
{
newPassword
&&
(
...
...
@@ -113,14 +135,21 @@ const PasswordResetConfirm = () => {
name
=
"newPassword"
size
=
"large"
className
=
"!rounded-md"
value
=
{
newPassword
}
readOnly
disabled
=
{
true
}
prefix
=
{
<
IconLock
/>
}
onClick
=
{(
e
)
=>
{
e
.
target
.
select
();
navigator
.
clipboard
.
writeText
(
newPassword
);
showNotice
(
`
${
t
(
'密码已复制到剪贴板'
)}
:
${
newPassword
}
`
);
}}
suffix
=
{
<
Button
icon
=
{
<
IconCopy
/>
}
type
=
"tertiary"
theme
=
"borderless"
onClick
=
{
async
()
=>
{
await
copy
(
newPassword
);
showNotice
(
`
${
t
(
'密码已复制到剪贴板:'
)}
${
newPassword
}
`
);
}}
>
{
t
(
'复制'
)}
<
/Button
>
}
/
>
)}
...
...
@@ -133,9 +162,9 @@ const PasswordResetConfirm = () => {
size
=
"large"
onClick
=
{
handleSubmit
}
loading
=
{
loading
}
disabled
=
{
disableButton
||
newPassword
}
disabled
=
{
disableButton
||
newPassword
||
!
isValidResetLink
}
>
{
newPassword
?
t
(
'密码重置完成'
)
:
t
(
'
提交
'
)}
{
newPassword
?
t
(
'密码重置完成'
)
:
t
(
'
确认重置密码
'
)}
<
/Button
>
<
/div
>
<
/Form
>
...
...
web/src/components/auth/PasswordResetForm.js
View file @
7607da23
...
...
@@ -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
;
...
...
web/src/components/common/Loading.js
View file @
7607da23
...
...
@@ -14,7 +14,7 @@ const Loading = ({ prompt: name = '', size = 'large' }) => {
tip
=
{
null
}
/
>
<
span
className
=
"whitespace-nowrap mt-2 text-center"
style
=
{{
color
:
'var(--semi-color-primary)'
}}
>
{
name
?
t
(
'
加载{{name}}中...
'
,
{
name
})
:
t
(
'加载中...'
)}
{
name
?
t
(
'
{{name}}
'
,
{
name
})
:
t
(
'加载中...'
)}
<
/span
>
<
/div
>
<
/div
>
...
...
web/src/components/layout/Footer.js
View file @
7607da23
...
...
@@ -40,36 +40,36 @@ const FooterBar = () => {
<
div
className
=
"text-left"
>
<
p
className
=
"!text-semi-color-text-0 font-semibold mb-5"
>
{
t
(
'关于我们'
)}
<
/p
>
<
div
className
=
"flex flex-col gap-4"
>
<
a
href
=
"https://docs.newapi.pro/wiki/project-introduction/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
{
t
(
'关于项目'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/support/community-interaction/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
{
t
(
'联系我们'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/wiki/features-introduction/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
{
t
(
'功能特性'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/wiki/project-introduction/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
{
t
(
'关于项目'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/support/community-interaction/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
{
t
(
'联系我们'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/wiki/features-introduction/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
{
t
(
'功能特性'
)}
<
/a
>
<
/div
>
<
/div
>
<
div
className
=
"text-left"
>
<
p
className
=
"!text-semi-color-text-0 font-semibold mb-5"
>
{
t
(
'文档'
)}
<
/p
>
<
div
className
=
"flex flex-col gap-4"
>
<
a
href
=
"https://docs.newapi.pro/getting-started/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
{
t
(
'快速开始'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/installation/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
{
t
(
'安装指南'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/api/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
{
t
(
'API 文档'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/getting-started/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
{
t
(
'快速开始'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/installation/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
{
t
(
'安装指南'
)}
<
/a
>
<
a
href
=
"https://docs.newapi.pro/api/"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
{
t
(
'API 文档'
)}
<
/a
>
<
/div
>
<
/div
>
<
div
className
=
"text-left"
>
<
p
className
=
"!text-semi-color-text-0 font-semibold mb-5"
>
{
t
(
'相关项目'
)}
<
/p
>
<
div
className
=
"flex flex-col gap-4"
>
<
a
href
=
"https://github.com/songquanpeng/one-api"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
One
API
<
/a
>
<
a
href
=
"https://github.com/novicezk/midjourney-proxy"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
Midjourney
-
Proxy
<
/a
>
<
a
href
=
"https://github.com/Deeptrain-Community/chatnio"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
chatnio
<
/a
>
<
a
href
=
"https://github.com/Calcium-Ion/neko-api-key-tool"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
neko
-
api
-
key
-
tool
<
/a
>
<
a
href
=
"https://github.com/songquanpeng/one-api"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
One
API
<
/a
>
<
a
href
=
"https://github.com/novicezk/midjourney-proxy"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
Midjourney
-
Proxy
<
/a
>
<
a
href
=
"https://github.com/Deeptrain-Community/chatnio"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
chatnio
<
/a
>
<
a
href
=
"https://github.com/Calcium-Ion/neko-api-key-tool"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
neko
-
api
-
key
-
tool
<
/a
>
<
/div
>
<
/div
>
<
div
className
=
"text-left"
>
<
p
className
=
"!text-semi-color-text-0 font-semibold mb-5"
>
{
t
(
'基于New API的项目'
)}
<
/p
>
<
div
className
=
"flex flex-col gap-4"
>
<
a
href
=
"https://github.com/Calcium-Ion/new-api-horizon"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
"
>
new
-
api
-
horizon
<
/a
>
{
/* <a href="https://github.com/VoAPI/VoAPI" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1
hover:!text-semi-color-primary transition-colors
">VoAPI</a> */
}
<
a
href
=
"https://github.com/Calcium-Ion/new-api-horizon"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-text-1"
>
new
-
api
-
horizon
<
/a
>
{
/* <a href="https://github.com/VoAPI/VoAPI" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">VoAPI</a> */
}
<
/div
>
<
/div
>
<
/div
>
...
...
@@ -81,14 +81,12 @@ const FooterBar = () => {
<
Typography
.
Text
className
=
"text-sm !text-semi-color-text-1"
>
©
{
currentYear
}
{
systemName
}.
{
t
(
'版权所有'
)}
<
/Typography.Text
>
<
/div
>
{
isDemoSiteMode
&&
(
<
div
className
=
"text-sm"
>
<
span
className
=
"!text-semi-color-text-1"
>
{
t
(
'设计与开发由'
)}
<
/span
>
<
span
className
=
"!text-semi-color-primary"
>
Douyin
FE
<
/span
>
<
span
className
=
"!text-semi-color-text-1"
>
&
<
/span
>
<
a
href
=
"https://github.com/QuantumNous"
target
=
"_blank"
rel
=
"noreferrer"
className
=
"!text-semi-color-primary hover:!text-semi-color-primary-hover transition-colors"
>
QuantumNous
<
/a
>
<
/div
>
)}
<
div
className
=
"text-sm"
>
<
span
className
=
"!text-semi-color-text-1"
>
{
t
(
'设计与开发由'
)}
<
/span
>
<
a
href
=
"https://github.com/QuantumNous/new-api"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-primary font-medium"
>
New
API
<
/a
>
<
span
className
=
"!text-semi-color-text-1"
>
&
<
/span
>
<
a
href
=
"https://github.com/songquanpeng/one-api"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-primary font-medium"
>
One
API
<
/a
>
<
/div
>
<
/div
>
<
/footer
>
),
[
logo
,
systemName
,
t
,
currentYear
,
isDemoSiteMode
]);
...
...
web/src/i18n/locales/en.json
View file @
7607da23
...
...
@@ -265,10 +265,15 @@
"设置页脚"
:
"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"
,
"等待获取邮箱信息..."
:
"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"
,
...
...
@@ -1404,8 +1409,13 @@
"演示站点"
:
"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 "
,
"MIT许可证"
:
"MIT License"
,
"Apache-2.0协议"
:
"Apache-2.0 License"
,
"本项目根据"
:
"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"
,
...
...
@@ -1531,6 +1541,7 @@
"关闭公告"
:
"Close Notice"
,
"搜索条件"
:
"Search Conditions"
,
"加载中..."
:
"Loading..."
,
"正在跳转..."
:
"Redirecting..."
,
"暂无公告"
:
"No Notice"
,
"操练场"
:
"Playground"
,
"欢迎使用,请完成以下设置以开始使用系统"
:
"Welcome to use, please complete the following settings to start using the system"
,
...
...
web/src/pages/About/index.js
View file @
7607da23
...
...
@@ -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,65 @@ const About = () => {
<
div
style
=
{{
textAlign
:
'center'
}}
>
<
p
>
{
t
(
'可在设置页面设置关于内容,支持 HTML & Markdown'
)}
<
/p
>
{
t
(
'New API项目仓库地址:'
)}
<
Link
to
=
'https://github.com/QuantumNous/new-api'
target
=
"_blank"
>
<
a
href
=
'https://github.com/QuantumNous/new-api'
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-primary"
>
https
:
//github.com/QuantumNous/new-api
<
/
Link
>
<
/
a
>
<
p
>
{
t
(
'NewAPI © {{currentYear}} QuantumNous | 基于 One API v0.5.4 © 2023 JustSong。'
,
{
currentYear
})}
<
a
href
=
"https://github.com/QuantumNous/new-api"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-primary"
>
NewAPI
<
/a> {t
(
'© {{currentYear}}', { currentYear }
)
} <
a
href
=
"https://github.com/QuantumNous"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-primary"
>
QuantumNous
<
/a> {t
(
'| 基于'
)
} <
a
href
=
"https://github.com/songquanpeng/one-api/releases/tag/v0.5.4"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-primary"
>
One
API
v0
.
5.4
<
/a> © 2023 <
a
href
=
"https://github.com/songquanpeng"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-primary"
>
JustSong
<
/a
>
<
/p
>
<
p
>
{
t
(
'本项目根据MIT许可证授权,需在遵守Apache-2.0协议的前提下使用。'
)}
{
t
(
'本项目根据'
)}
<
a
href
=
"https://github.com/songquanpeng/one-api/blob/v0.5.4/LICENSE"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-primary"
>
{
t
(
'MIT许可证'
)}
<
/a
>
{
t
(
'授权,需在遵守'
)}
<
a
href
=
"https://github.com/QuantumNous/new-api/blob/main/LICENSE"
target
=
"_blank"
rel
=
"noopener noreferrer"
className
=
"!text-semi-color-primary"
>
{
t
(
'Apache-2.0协议'
)}
<
/a
>
{
t
(
'的前提下使用。'
)}
<
/p
>
<
/div
>
);
...
...
web/src/pages/Channel/EditChannel.js
View file @
7607da23
...
...
@@ -846,7 +846,7 @@ const EditChannel = (props) => {
className
=
"!rounded-lg font-mono"
/>
<
Text
className
=
"
text-blue-500
cursor-pointer mt-1 block"
className
=
"
!text-semi-color-primary
cursor-pointer mt-1 block"
onClick
=
{()
=>
handleInputChange
(
'model_mapping'
,
JSON
.
stringify
(
MODEL_MAPPING_EXAMPLE
,
null
,
2
))}
>
{
t
(
'填入模板'
)}
...
...
@@ -940,7 +940,7 @@ const EditChannel = (props) => {
className
=
"!rounded-lg font-mono"
/>
<
Text
className
=
"
text-blue-500
cursor-pointer mt-1 block"
className
=
"
!text-semi-color-primary
cursor-pointer mt-1 block"
onClick
=
{()
=>
handleInputChange
(
'other'
,
JSON
.
stringify
(
REGION_EXAMPLE
,
null
,
2
))}
>
{
t
(
'填入模板'
)}
...
...
@@ -1062,7 +1062,7 @@ const EditChannel = (props) => {
/>
<
div
className
=
"flex gap-2 mt-1"
>
<
Text
className
=
"
text-blue-500
cursor-pointer"
className
=
"
!text-semi-color-primary
cursor-pointer"
onClick
=
{()
=>
{
handleInputChange
(
'setting'
,
...
...
@@ -1073,10 +1073,10 @@ const EditChannel = (props) => {
{
t
(
'填入模板'
)}
<
/Text
>
<
Text
className
=
"
text-blue-500
cursor-pointer"
className
=
"
!text-semi-color-primary
cursor-pointer"
onClick
=
{()
=>
{
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"
/>
<
Text
className
=
"
text-blue-500
cursor-pointer mt-1 block"
className
=
"
!text-semi-color-primary
cursor-pointer mt-1 block"
onClick
=
{()
=>
{
handleInputChange
(
'status_code_mapping'
,
...
...
web/src/pages/Chat/index.js
View file @
7607da23
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'
/>
)
:
(
<
div
>
<
Layout
>
<
Layout
.
Header
>
<
Banner
description
=
{
'正在跳转......'
}
type
=
{
'warning'
}
/
>
<
/Layout.Header
>
<
/Layout
>
<
div
className
=
"fixed inset-0 w-screen h-screen flex items-center justify-center bg-white/80 z-[1000]"
>
<
div
className
=
"flex flex-col items-center"
>
<
Spin
size
=
"large"
spinning
=
{
true
}
tip
=
{
null
}
/
>
<
span
className
=
"whitespace-nowrap mt-2 text-center"
style
=
{{
color
:
'var(--semi-color-primary)'
}}
>
{
t
(
'正在跳转...'
)}
<
/span
>
<
/div
>
<
/div
>
);
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment