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
95cbab6b
authored
Sep 27, 2025
by
huanghejian
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/main' into feature/volcengine-base-url
parents
183ae15a
da88e746
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
330 additions
and
37 deletions
+330
-37
.github/workflows/linux-release.yml
+4
-4
.github/workflows/macos-release.yml
+2
-2
.github/workflows/windows-release.yml
+2
-2
common/sys_log.go
+32
-1
common/utils.go
+72
-0
controller/channel.go
+84
-2
dto/gemini.go
+31
-0
main.go
+7
-2
web/src/components/common/markdown/MarkdownRenderer.jsx
+2
-2
web/src/components/table/channels/modals/EditChannelModal.jsx
+29
-7
web/src/components/table/channels/modals/MultiKeyManageModal.jsx
+42
-1
web/src/helpers/api.js
+8
-7
web/src/helpers/utils.jsx
+11
-7
web/src/i18n/locales/en.json
+4
-0
No files found.
.github/workflows/linux-release.yml
View file @
95cbab6b
...
...
@@ -38,21 +38,21 @@ jobs:
-
name
:
Build Backend (amd64)
run
:
|
go mod download
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o
one
-api
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o
new
-api
-
name
:
Build Backend (arm64)
run
:
|
sudo apt-get update
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y gcc-aarch64-linux-gnu
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o
one
-api-arm64
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o
new
-api-arm64
-
name
:
Release
uses
:
softprops/action-gh-release@v1
if
:
startsWith(github.ref, 'refs/tags/')
with
:
files
:
|
one
-api
one
-api-arm64
new
-api
new
-api-arm64
draft
:
true
generate_release_notes
:
true
env
:
...
...
.github/workflows/macos-release.yml
View file @
95cbab6b
...
...
@@ -39,12 +39,12 @@ jobs:
-
name
:
Build Backend
run
:
|
go mod download
go build -ldflags "-X 'one-api/common.Version=$(git describe --tags)'" -o
one
-api-macos
go build -ldflags "-X 'one-api/common.Version=$(git describe --tags)'" -o
new
-api-macos
-
name
:
Release
uses
:
softprops/action-gh-release@v1
if
:
startsWith(github.ref, 'refs/tags/')
with
:
files
:
one
-api-macos
files
:
new
-api-macos
draft
:
true
generate_release_notes
:
true
env
:
...
...
.github/workflows/windows-release.yml
View file @
95cbab6b
...
...
@@ -41,12 +41,12 @@ jobs:
-
name
:
Build Backend
run
:
|
go mod download
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)'" -o
one
-api.exe
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)'" -o
new
-api.exe
-
name
:
Release
uses
:
softprops/action-gh-release@v1
if
:
startsWith(github.ref, 'refs/tags/')
with
:
files
:
one
-api.exe
files
:
new
-api.exe
draft
:
true
generate_release_notes
:
true
env
:
...
...
common/sys_log.go
View file @
95cbab6b
...
...
@@ -2,9 +2,10 @@ package common
import
(
"fmt"
"github.com/gin-gonic/gin"
"os"
"time"
"github.com/gin-gonic/gin"
)
func
SysLog
(
s
string
)
{
...
...
@@ -22,3 +23,33 @@ func FatalLog(v ...any) {
_
,
_
=
fmt
.
Fprintf
(
gin
.
DefaultErrorWriter
,
"[FATAL] %v | %v
\n
"
,
t
.
Format
(
"2006/01/02 - 15:04:05"
),
v
)
os
.
Exit
(
1
)
}
func
LogStartupSuccess
(
startTime
time
.
Time
,
port
string
)
{
duration
:=
time
.
Since
(
startTime
)
durationMs
:=
duration
.
Milliseconds
()
// Get network IPs
networkIps
:=
GetNetworkIps
()
// Print blank line for spacing
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
"
\n
"
)
// Print the main success message
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
"
\0
33[32m%s %s
\0
33[0m ready in %d ms
\n
"
,
SystemName
,
Version
,
durationMs
)
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
"
\n
"
)
// Skip fancy startup message in container environments
if
!
IsRunningInContainer
()
{
// Print local URL
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
" ➜
\0
33[1mLocal:
\0
33[0m http://localhost:%s/
\n
"
,
port
)
}
// Print network URLs
for
_
,
ip
:=
range
networkIps
{
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
" ➜
\0
33[1mNetwork:
\0
33[0m http://%s:%s/
\n
"
,
ip
,
port
)
}
// Print blank line for spacing
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
"
\n
"
)
}
common/utils.go
View file @
95cbab6b
...
...
@@ -68,6 +68,78 @@ func GetIp() (ip string) {
return
}
func
GetNetworkIps
()
[]
string
{
var
networkIps
[]
string
ips
,
err
:=
net
.
InterfaceAddrs
()
if
err
!=
nil
{
log
.
Println
(
err
)
return
networkIps
}
for
_
,
a
:=
range
ips
{
if
ipNet
,
ok
:=
a
.
(
*
net
.
IPNet
);
ok
&&
!
ipNet
.
IP
.
IsLoopback
()
{
if
ipNet
.
IP
.
To4
()
!=
nil
{
ip
:=
ipNet
.
IP
.
String
()
// Include common private network ranges
if
strings
.
HasPrefix
(
ip
,
"10."
)
||
strings
.
HasPrefix
(
ip
,
"172."
)
||
strings
.
HasPrefix
(
ip
,
"192.168."
)
{
networkIps
=
append
(
networkIps
,
ip
)
}
}
}
}
return
networkIps
}
// IsRunningInContainer detects if the application is running inside a container
func
IsRunningInContainer
()
bool
{
// Method 1: Check for .dockerenv file (Docker containers)
if
_
,
err
:=
os
.
Stat
(
"/.dockerenv"
);
err
==
nil
{
return
true
}
// Method 2: Check cgroup for container indicators
if
data
,
err
:=
os
.
ReadFile
(
"/proc/1/cgroup"
);
err
==
nil
{
content
:=
string
(
data
)
if
strings
.
Contains
(
content
,
"docker"
)
||
strings
.
Contains
(
content
,
"containerd"
)
||
strings
.
Contains
(
content
,
"kubepods"
)
||
strings
.
Contains
(
content
,
"/lxc/"
)
{
return
true
}
}
// Method 3: Check environment variables commonly set by container runtimes
containerEnvVars
:=
[]
string
{
"KUBERNETES_SERVICE_HOST"
,
"DOCKER_CONTAINER"
,
"container"
,
}
for
_
,
envVar
:=
range
containerEnvVars
{
if
os
.
Getenv
(
envVar
)
!=
""
{
return
true
}
}
// Method 4: Check if init process is not the traditional init
if
data
,
err
:=
os
.
ReadFile
(
"/proc/1/comm"
);
err
==
nil
{
comm
:=
strings
.
TrimSpace
(
string
(
data
))
// In containers, process 1 is often not "init" or "systemd"
if
comm
!=
"init"
&&
comm
!=
"systemd"
{
// Additional check: if it's a common container entrypoint
if
strings
.
Contains
(
comm
,
"docker"
)
||
strings
.
Contains
(
comm
,
"containerd"
)
||
strings
.
Contains
(
comm
,
"runc"
)
{
return
true
}
}
}
return
false
}
var
sizeKB
=
1024
var
sizeMB
=
sizeKB
*
1024
var
sizeGB
=
sizeMB
*
1024
...
...
controller/channel.go
View file @
95cbab6b
...
...
@@ -188,6 +188,8 @@ func FetchUpstreamModels(c *gin.Context) {
url
=
fmt
.
Sprintf
(
"%s/v1beta/openai/models"
,
baseURL
)
// Remove key in url since we need to use AuthHeader
case
constant
.
ChannelTypeAli
:
url
=
fmt
.
Sprintf
(
"%s/compatible-mode/v1/models"
,
baseURL
)
case
constant
.
ChannelTypeZhipu_v4
:
url
=
fmt
.
Sprintf
(
"%s/api/paas/v4/models"
,
baseURL
)
default
:
url
=
fmt
.
Sprintf
(
"%s/v1/models"
,
baseURL
)
}
...
...
@@ -1101,8 +1103,8 @@ func CopyChannel(c *gin.Context) {
// MultiKeyManageRequest represents the request for multi-key management operations
type
MultiKeyManageRequest
struct
{
ChannelId
int
`json:"channel_id"`
Action
string
`json:"action"`
// "disable_key", "enable_key", "delete_disabled_keys", "get_key_status"
KeyIndex
*
int
`json:"key_index,omitempty"`
// for disable_key
and enabl
e_key actions
Action
string
`json:"action"`
// "disable_key", "enable_key", "delete_
key", "delete_
disabled_keys", "get_key_status"
KeyIndex
*
int
`json:"key_index,omitempty"`
// for disable_key
, enable_key, and delet
e_key actions
Page
int
`json:"page,omitempty"`
// for get_key_status pagination
PageSize
int
`json:"page_size,omitempty"`
// for get_key_status pagination
Status
*
int
`json:"status,omitempty"`
// for get_key_status filtering: 1=enabled, 2=manual_disabled, 3=auto_disabled, nil=all
...
...
@@ -1430,6 +1432,86 @@ func ManageMultiKeys(c *gin.Context) {
})
return
case
"delete_key"
:
if
request
.
KeyIndex
==
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"未指定要删除的密钥索引"
,
})
return
}
keyIndex
:=
*
request
.
KeyIndex
if
keyIndex
<
0
||
keyIndex
>=
channel
.
ChannelInfo
.
MultiKeySize
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"密钥索引超出范围"
,
})
return
}
keys
:=
channel
.
GetKeys
()
var
remainingKeys
[]
string
var
newStatusList
=
make
(
map
[
int
]
int
)
var
newDisabledTime
=
make
(
map
[
int
]
int64
)
var
newDisabledReason
=
make
(
map
[
int
]
string
)
newIndex
:=
0
for
i
,
key
:=
range
keys
{
// 跳过要删除的密钥
if
i
==
keyIndex
{
continue
}
remainingKeys
=
append
(
remainingKeys
,
key
)
// 保留其他密钥的状态信息,重新索引
if
channel
.
ChannelInfo
.
MultiKeyStatusList
!=
nil
{
if
status
,
exists
:=
channel
.
ChannelInfo
.
MultiKeyStatusList
[
i
];
exists
&&
status
!=
1
{
newStatusList
[
newIndex
]
=
status
}
}
if
channel
.
ChannelInfo
.
MultiKeyDisabledTime
!=
nil
{
if
t
,
exists
:=
channel
.
ChannelInfo
.
MultiKeyDisabledTime
[
i
];
exists
{
newDisabledTime
[
newIndex
]
=
t
}
}
if
channel
.
ChannelInfo
.
MultiKeyDisabledReason
!=
nil
{
if
r
,
exists
:=
channel
.
ChannelInfo
.
MultiKeyDisabledReason
[
i
];
exists
{
newDisabledReason
[
newIndex
]
=
r
}
}
newIndex
++
}
if
len
(
remainingKeys
)
==
0
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"不能删除最后一个密钥"
,
})
return
}
// Update channel with remaining keys
channel
.
Key
=
strings
.
Join
(
remainingKeys
,
"
\n
"
)
channel
.
ChannelInfo
.
MultiKeySize
=
len
(
remainingKeys
)
channel
.
ChannelInfo
.
MultiKeyStatusList
=
newStatusList
channel
.
ChannelInfo
.
MultiKeyDisabledTime
=
newDisabledTime
channel
.
ChannelInfo
.
MultiKeyDisabledReason
=
newDisabledReason
err
=
channel
.
Update
()
if
err
!=
nil
{
common
.
ApiError
(
c
,
err
)
return
}
model
.
InitChannelCache
()
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"message"
:
"密钥已删除"
,
})
return
case
"delete_disabled_keys"
:
keys
:=
channel
.
GetKeys
()
var
remainingKeys
[]
string
...
...
dto/gemini.go
View file @
95cbab6b
...
...
@@ -14,7 +14,30 @@ type GeminiChatRequest struct {
SafetySettings
[]
GeminiChatSafetySettings
`json:"safetySettings,omitempty"`
GenerationConfig
GeminiChatGenerationConfig
`json:"generationConfig,omitempty"`
Tools
json
.
RawMessage
`json:"tools,omitempty"`
ToolConfig
*
ToolConfig
`json:"toolConfig,omitempty"`
SystemInstructions
*
GeminiChatContent
`json:"systemInstruction,omitempty"`
CachedContent
string
`json:"cachedContent,omitempty"`
}
type
ToolConfig
struct
{
FunctionCallingConfig
*
FunctionCallingConfig
`json:"functionCallingConfig,omitempty"`
RetrievalConfig
*
RetrievalConfig
`json:"retrievalConfig,omitempty"`
}
type
FunctionCallingConfig
struct
{
Mode
FunctionCallingConfigMode
`json:"mode,omitempty"`
AllowedFunctionNames
[]
string
`json:"allowedFunctionNames,omitempty"`
}
type
FunctionCallingConfigMode
string
type
RetrievalConfig
struct
{
LatLng
*
LatLng
`json:"latLng,omitempty"`
LanguageCode
string
`json:"languageCode,omitempty"`
}
type
LatLng
struct
{
Latitude
*
float64
`json:"latitude,omitempty"`
Longitude
*
float64
`json:"longitude,omitempty"`
}
func
(
r
*
GeminiChatRequest
)
GetTokenCountMeta
()
*
types
.
TokenCountMeta
{
...
...
@@ -239,12 +262,20 @@ type GeminiChatGenerationConfig struct {
StopSequences
[]
string
`json:"stopSequences,omitempty"`
ResponseMimeType
string
`json:"responseMimeType,omitempty"`
ResponseSchema
any
`json:"responseSchema,omitempty"`
ResponseJsonSchema
json
.
RawMessage
`json:"responseJsonSchema,omitempty"`
PresencePenalty
*
float32
`json:"presencePenalty,omitempty"`
FrequencyPenalty
*
float32
`json:"frequencyPenalty,omitempty"`
ResponseLogprobs
bool
`json:"responseLogprobs,omitempty"`
Logprobs
*
int32
`json:"logprobs,omitempty"`
MediaResolution
MediaResolution
`json:"mediaResolution,omitempty"`
Seed
int64
`json:"seed,omitempty"`
ResponseModalities
[]
string
`json:"responseModalities,omitempty"`
ThinkingConfig
*
GeminiThinkingConfig
`json:"thinkingConfig,omitempty"`
SpeechConfig
json
.
RawMessage
`json:"speechConfig,omitempty"`
// RawMessage to allow flexible speech config
}
type
MediaResolution
string
type
GeminiChatCandidate
struct
{
Content
GeminiChatContent
`json:"content"`
FinishReason
*
string
`json:"finishReason"`
...
...
main.go
View file @
95cbab6b
...
...
@@ -16,6 +16,7 @@ import (
"one-api/setting/ratio_setting"
"os"
"strconv"
"time"
"github.com/bytedance/gopkg/util/gopool"
"github.com/gin-contrib/sessions"
...
...
@@ -33,6 +34,7 @@ var buildFS embed.FS
var
indexPage
[]
byte
func
main
()
{
startTime
:=
time
.
Now
()
err
:=
InitResources
()
if
err
!=
nil
{
...
...
@@ -150,6 +152,10 @@ func main() {
if
port
==
""
{
port
=
strconv
.
Itoa
(
*
common
.
Port
)
}
// Log startup success message
common
.
LogStartupSuccess
(
startTime
,
port
)
err
=
server
.
Run
(
":"
+
port
)
if
err
!=
nil
{
common
.
FatalLog
(
"failed to start HTTP server: "
+
err
.
Error
())
...
...
@@ -204,4 +210,4 @@ func InitResources() error {
return
err
}
return
nil
}
\ No newline at end of file
}
web/src/components/common/markdown/MarkdownRenderer.jsx
View file @
95cbab6b
...
...
@@ -181,8 +181,8 @@ export function PreCode(props) {
e
.
preventDefault
();
e
.
stopPropagation
();
if
(
ref
.
current
)
{
const
code
=
ref
.
current
.
querySelector
(
'code'
)?.
innerTex
t
??
''
;
const
code
Element
=
ref
.
current
.
querySelector
(
'code'
);
const
code
=
codeElement
?.
textConten
t
??
''
;
copy
(
code
).
then
((
success
)
=>
{
if
(
success
)
{
Toast
.
success
(
t
(
'代码已复制到剪贴板'
));
...
...
web/src/components/table/channels/modals/EditChannelModal.jsx
View file @
95cbab6b
...
...
@@ -85,6 +85,26 @@ const REGION_EXAMPLE = {
'claude-3-5-sonnet-20240620'
:
'europe-west1'
,
};
// 支持并且已适配通过接口获取模型列表的渠道类型
const
MODEL_FETCHABLE_TYPES
=
new
Set
([
1
,
4
,
14
,
34
,
17
,
26
,
24
,
47
,
25
,
20
,
23
,
31
,
35
,
40
,
42
,
48
,
]);
function
type2secretPrompt
(
type
)
{
// inputs.type === 15 ? '按照如下格式输入:APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入:APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥')
switch
(
type
)
{
...
...
@@ -1904,13 +1924,15 @@ const EditChannelModal = (props) => {
>
{
t
(
'填入所有模型'
)
}
</
Button
>
<
Button
size=
'small'
type=
'tertiary'
onClick=
{
()
=>
fetchUpstreamModelList
(
'models'
)
}
>
{
t
(
'获取模型列表'
)
}
</
Button
>
{
MODEL_FETCHABLE_TYPES
.
has
(
inputs
.
type
)
&&
(
<
Button
size=
'small'
type=
'tertiary'
onClick=
{
()
=>
fetchUpstreamModelList
(
'models'
)
}
>
{
t
(
'获取模型列表'
)
}
</
Button
>
)
}
<
Button
size=
'small'
type=
'warning'
...
...
web/src/components/table/channels/modals/MultiKeyManageModal.jsx
View file @
95cbab6b
...
...
@@ -247,6 +247,32 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
}
};
// Delete a specific key
const
handleDeleteKey
=
async
(
keyIndex
)
=>
{
const
operationId
=
`delete_
${
keyIndex
}
`
;
setOperationLoading
((
prev
)
=>
({
...
prev
,
[
operationId
]:
true
}));
try
{
const
res
=
await
API
.
post
(
'/api/channel/multi_key/manage'
,
{
channel_id
:
channel
.
id
,
action
:
'delete_key'
,
key_index
:
keyIndex
,
});
if
(
res
.
data
.
success
)
{
showSuccess
(
t
(
'密钥已删除'
));
await
loadKeyStatus
(
currentPage
,
pageSize
);
// Reload current page
onRefresh
&&
onRefresh
();
// Refresh parent component
}
else
{
showError
(
res
.
data
.
message
);
}
}
catch
(
error
)
{
showError
(
t
(
'删除密钥失败'
));
}
finally
{
setOperationLoading
((
prev
)
=>
({
...
prev
,
[
operationId
]:
false
}));
}
};
// Handle page change
const
handlePageChange
=
(
page
)
=>
{
setCurrentPage
(
page
);
...
...
@@ -384,7 +410,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
title
:
t
(
'操作'
),
key
:
'action'
,
fixed
:
'right'
,
width
:
1
0
0
,
width
:
1
5
0
,
render
:
(
_
,
record
)
=>
(
<
Space
>
{
record
.
status
===
1
?
(
...
...
@@ -406,6 +432,21 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
{
t
(
'启用'
)
}
</
Button
>
)
}
<
Popconfirm
title=
{
t
(
'确定要删除此密钥吗?'
)
}
content=
{
t
(
'此操作不可撤销,将永久删除该密钥'
)
}
onConfirm=
{
()
=>
handleDeleteKey
(
record
.
index
)
}
okType=
{
'danger'
}
position=
{
'topRight'
}
>
<
Button
type=
'danger'
size=
'small'
loading=
{
operationLoading
[
`delete_${record.index}`
]
}
>
{
t
(
'删除'
)
}
</
Button
>
</
Popconfirm
>
</
Space
>
),
},
...
...
web/src/helpers/api.js
View file @
95cbab6b
...
...
@@ -118,7 +118,6 @@ export const buildApiPayload = (
model
:
inputs
.
model
,
group
:
inputs
.
group
,
messages
:
processedMessages
,
group
:
inputs
.
group
,
stream
:
inputs
.
stream
,
};
...
...
@@ -132,13 +131,15 @@ export const buildApiPayload = (
seed
:
'seed'
,
};
Object
.
entries
(
parameterMappings
).
forEach
(([
key
,
param
])
=>
{
if
(
parameterEnabled
[
key
]
&&
inputs
[
param
]
!==
undefined
&&
inputs
[
param
]
!==
null
)
{
payload
[
param
]
=
inputs
[
param
];
const
enabled
=
parameterEnabled
[
key
];
const
value
=
inputs
[
param
];
const
hasValue
=
value
!==
undefined
&&
value
!==
null
;
if
(
enabled
&&
hasValue
)
{
payload
[
param
]
=
value
;
}
});
...
...
web/src/helpers/utils.jsx
View file @
95cbab6b
...
...
@@ -75,13 +75,17 @@ export async function copy(text) {
await
navigator
.
clipboard
.
writeText
(
text
);
}
catch
(
e
)
{
try
{
// 构建input 执行 复制命令
var
_input
=
window
.
document
.
createElement
(
'input'
);
_input
.
value
=
text
;
window
.
document
.
body
.
appendChild
(
_input
);
_input
.
select
();
window
.
document
.
execCommand
(
'Copy'
);
window
.
document
.
body
.
removeChild
(
_input
);
// 构建 textarea 执行复制命令,保留多行文本格式
const
textarea
=
window
.
document
.
createElement
(
'textarea'
);
textarea
.
value
=
text
;
textarea
.
setAttribute
(
'readonly'
,
''
);
textarea
.
style
.
position
=
'fixed'
;
textarea
.
style
.
left
=
'-9999px'
;
textarea
.
style
.
top
=
'-9999px'
;
window
.
document
.
body
.
appendChild
(
textarea
);
textarea
.
select
();
window
.
document
.
execCommand
(
'copy'
);
window
.
document
.
body
.
removeChild
(
textarea
);
}
catch
(
e
)
{
okay
=
false
;
console
.
error
(
e
);
...
...
web/src/i18n/locales/en.json
View file @
95cbab6b
...
...
@@ -1889,6 +1889,10 @@
"确定要删除所有已自动禁用的密钥吗?"
:
"Are you sure you want to delete all automatically disabled keys?"
,
"此操作不可撤销,将永久删除已自动禁用的密钥"
:
"This operation cannot be undone, and all automatically disabled keys will be permanently deleted."
,
"删除自动禁用密钥"
:
"Delete auto disabled keys"
,
"确定要删除此密钥吗?"
:
"Are you sure you want to delete this key?"
,
"此操作不可撤销,将永久删除该密钥"
:
"This operation cannot be undone, and the key will be permanently deleted."
,
"密钥已删除"
:
"Key has been deleted"
,
"删除密钥失败"
:
"Failed to delete key"
,
"图标"
:
"Icon"
,
"模型图标"
:
"Model icon"
,
"请输入图标名称"
:
"Please enter the icon name"
,
...
...
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