fix active key,suffix

This commit is contained in:
Sakurasan
2025-04-20 16:22:23 +08:00
parent fe0f2a7e88
commit b83c6d9786
11 changed files with 106 additions and 119 deletions

View File

@@ -100,13 +100,13 @@
class="input input-sm input-bordered w-full" /> class="input input-sm input-bordered w-full" />
</div> </div>
<div class="form-control"> <!-- <div class="form-control">
<label for="deployment_name" class="label"> <label for="deployment_name" class="label">
<span class="label-text">Deployment Name</span> <span class="label-text">Deployment Name</span>
</label> </label>
<input id="deployment_name" type="text" v-model="newApiKey.deployment_name" <input id="deployment_name" type="text" v-model="newApiKey.deployment_name"
placeholder="Deployment Name" class="input input-sm input-bordered w-full" /> placeholder="Deployment Name" class="input input-sm input-bordered w-full" />
</div> </div> -->
<div class="form-control"> <div class="form-control">
<label for="api_secret" class="label"> <label for="api_secret" class="label">
@@ -200,7 +200,7 @@ const newApiKey = ref({
active: true, active: true,
endpoint: '', endpoint: '',
resource_name: '', resource_name: '',
deployment_name: '', // deployment_name: '',
api_secret: '', api_secret: '',
model_prefix: '', model_prefix: '',
model_alias: '', model_alias: '',
@@ -217,7 +217,7 @@ const resetNewApiKey = () => {
active: true, active: true,
endpoint: '', endpoint: '',
resource_name: '', resource_name: '',
deployment_name: '', // deployment_name: '',
api_secret: '', api_secret: '',
model_prefix: '', model_prefix: '',
model_alias: '', model_alias: '',

View File

@@ -79,13 +79,13 @@
class="input input-sm input-bordered w-full" /> class="input input-sm input-bordered w-full" />
</div> </div>
<div class="form-control"> <!-- <div class="form-control">
<label for="deployment_name" class="label"> <label for="deployment_name" class="label">
<span class="label-text">Deployment Name</span> <span class="label-text">Deployment Name</span>
</label> </label>
<input id="deployment_name" type="text" v-model="key.deployment_name" <input id="deployment_name" type="text" v-model="key.deployment_name"
placeholder="Deployment Name" class="input input-sm input-bordered w-full" /> placeholder="Deployment Name" class="input input-sm input-bordered w-full" />
</div> </div> -->
<div class="form-control"> <div class="form-control">
<label for="api_secret" class="label"> <label for="api_secret" class="label">

View File

@@ -126,7 +126,7 @@
<!-- <td class="text-xs dark:text-white">{{ key.apikey }}</td> --> <!-- <td class="text-xs dark:text-white">{{ key.apikey }}</td> -->
<!-- <td class="text-xs dark:text-white">{{ key.endpoint }}</td> --> <!-- <td class="text-xs dark:text-white">{{ key.endpoint }}</td> -->
<td> <td>
<div class="flex gap-1"> <div class="flex gap-2">
<div class="lg:tooltip lg:tooltip-top lg:tooltip-open" data-tip="预览"> <div class="lg:tooltip lg:tooltip-top lg:tooltip-open" data-tip="预览">
<button class="btn btn-ghost btn-xs btn-square " @click="viewKey(key)"> <button class="btn btn-ghost btn-xs btn-square " @click="viewKey(key)">
<EyeIcon class="w-4 h-4 dark:text-white" /> <EyeIcon class="w-4 h-4 dark:text-white" />

View File

@@ -24,17 +24,7 @@
</div> </div>
<!-- Table --> <!-- Table -->
<div class="card card-bordered bg-base-100 shadow-sm mt-6" v-if="user"> <div class="card card-bordered bg-base-100 shadow-sm mt-6">
<div class="card-body">
<h3 class="card-title text-lg">Tokens</h3>
<div v-if="user.tokens">
</div>
<p v-else class="text-center text-base-content/70 py-4">No tokens found</p>
</div>
</div>
<div class="card bg-base-100 shadow-xs overflow-x-auto dark:bg-base-200" v-if="user"> <div class="card bg-base-100 shadow-xs overflow-x-auto dark:bg-base-200" v-if="user">
<table class="table table-sm w-full"> <table class="table table-sm w-full">
@@ -53,8 +43,9 @@
<tr v-for="token in user.tokens" :key="token.id" class="hover"> <tr v-for="token in user.tokens" :key="token.id" class="hover">
<td class="font-mono text-xs px-2 py-3">{{ token.name }}</td> <td class="font-mono text-xs px-2 py-3">{{ token.name }}</td>
<td> <td>
<input type="checkbox" class="toggle toggle-xs" :class="token.active ? 'toggle-success' : 'toggle-error'" <input type="checkbox" class="toggle toggle-xs"
v-model="token.active" @change="updateStatus(token)" /> :class="token.active ? 'toggle-success' : 'toggle-error'" v-model="token.active"
@change="updateStatus(token)" />
</td> </td>
<!-- <td class="font-mono text-xs px-2 py-3">{{ token.key }}</td> --> <!-- <td class="font-mono text-xs px-2 py-3">{{ token.key }}</td> -->
<td class="px-2 py-3">{{ token.expired_at == 0 ? 'Never' : unixToDate(token.expired_at) }}</td> <td class="px-2 py-3">{{ token.expired_at == 0 ? 'Never' : unixToDate(token.expired_at) }}</td>
@@ -72,7 +63,7 @@
<EyeIcon class="w-5 h-5 dark:text-white" /> <EyeIcon class="w-5 h-5 dark:text-white" />
</button> </button>
</div> </div>
<br />
<div class="md:tooltip" data-tip="clean used"> <div class="md:tooltip" data-tip="clean used">
<button class="btn btn-ghost btn-xs btn-square text-sky-300 mt-1" @click="cleanUsedToken(token)" <button class="btn btn-ghost btn-xs btn-square text-sky-300 mt-1" @click="cleanUsedToken(token)"
aria-label="Revoke token"> aria-label="Revoke token">
@@ -80,8 +71,9 @@
</button> </button>
</div> </div>
<button v-if="token.name !== 'default'" class="btn btn-ghost btn-xs btn-square text-error items-center" <button v-if="token.name !== 'default'"
@click="confirmRevokeToken(token)" aria-label="Revoke token"> class="btn btn-ghost btn-xs btn-square text-error items-center" @click="confirmRevokeToken(token)"
aria-label="Revoke token">
<TrashIcon class="w-5 h-5" /> <TrashIcon class="w-5 h-5" />
</button> </button>
</div> </div>
@@ -94,10 +86,7 @@
<form method="dialog"> <form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button> <button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button>
</form> </form>
token <QRCodeCard :value="qrCodeValue" :size="120" />
<QRCodeCard
:value="qrCodeValue"
:size="120" />
</div> </div>
<form method="dialog" class="modal-backdrop"> <form method="dialog" class="modal-backdrop">
<button>关闭</button> <button>关闭</button>
@@ -105,6 +94,12 @@
</dialog> </dialog>
</div> </div>
<p v-else class="text-center text-base-content/70 py-4">No tokens found</p>
</div>
<!-- </div> -->
</div> </div>
</template> </template>
@@ -205,22 +200,6 @@ const viewToken = (token) => {
const qrCodeValue = ref(''); const qrCodeValue = ref('');
// 监听showTokenModel的变化控制模态框的显示和隐藏
// watch(showTokenModel, (newValue) => {
// const dialog = tokenRef.value;
// if (dialog) {
// if (newValue) {
// if (!dialog.hasAttribute('open')) {
// dialog.showModal();
// }
// } else {
// if (dialog.hasAttribute('open')) {
// dialog.close();
// }
// }
// }
// });
// 关闭模态框 // 关闭模态框
const modalRef = ref(null); const modalRef = ref(null);

View File

@@ -125,13 +125,13 @@
<div class="flex gap-1"> <div class="flex gap-1">
<div class="lg:tooltip lg:tooltip-top lg:tooltip-open" data-tip="预览"> <div class="lg:tooltip lg:tooltip-top lg:tooltip-open" data-tip="预览">
<button class="btn btn-ghost btn-xs btn-square " @click="viewUser(user)"> <button class="btn btn-ghost btn-xs btn-square " @click="viewUser(user)">
<EyeIcon class="w-3.5 h-3.5 dark:text-white" /> <EyeIcon class="w-4 h-4 dark:text-white" />
</button> </button>
</div> </div>
<div class="lg:tooltip lg:tooltip-top lg:tooltip-open" data-tip="删除" v-if="user.role < 20"> <div class="lg:tooltip lg:tooltip-top lg:tooltip-open" data-tip="删除" v-if="user.role < 20">
<button class="btn btn-ghost btn-xs btn-square text-error hover:bg-error/30" <button class="btn btn-ghost btn-xs btn-square text-error hover:bg-error/30"
@click="confirmDeleteUser(user)"> @click="confirmDeleteUser(user)">
<TrashIcon class="w-3.5 h-3.5 dark:text-white" /> <TrashIcon class="w-4 h-4 dark:text-white" />
</button> </button>
</div> </div>
</div> </div>

View File

@@ -12,6 +12,7 @@ import (
"net/url" "net/url"
"opencatd-open/internal/dao" "opencatd-open/internal/dao"
"opencatd-open/internal/model" "opencatd-open/internal/model"
"opencatd-open/internal/utils"
"opencatd-open/pkg/config" "opencatd-open/pkg/config"
"os" "os"
"strings" "strings"
@@ -187,7 +188,7 @@ func (p *Proxy) SelectApiKey(model string) error {
if err != nil || len(akpikeys) == 0 { if err != nil || len(akpikeys) == 0 {
if strings.HasPrefix(model, "gpt") || strings.HasPrefix(model, "o1") || strings.HasPrefix(model, "o3") || strings.HasPrefix(model, "o4") { if strings.HasPrefix(model, "gpt") || strings.HasPrefix(model, "o1") || strings.HasPrefix(model, "o3") || strings.HasPrefix(model, "o4") {
keys, err := p.apiKeyDao.FindKeys(map[string]any{"apitype = ?": "openai"}) keys, err := p.apiKeyDao.FindKeys(map[string]any{"active = ?": true, "apitype = ?": "openai"})
if err != nil { if err != nil {
return err return err
} }
@@ -195,7 +196,7 @@ func (p *Proxy) SelectApiKey(model string) error {
} }
if strings.HasPrefix(model, "gemini") { if strings.HasPrefix(model, "gemini") {
keys, err := p.apiKeyDao.FindKeys(map[string]any{"apitype = ?": "gemini"}) keys, err := p.apiKeyDao.FindKeys(map[string]any{"active = ?": true, "apitype = ?": "gemini"})
if err != nil { if err != nil {
return err return err
} }
@@ -203,7 +204,7 @@ func (p *Proxy) SelectApiKey(model string) error {
} }
if strings.HasPrefix(model, "claude") { if strings.HasPrefix(model, "claude") {
keys, err := p.apiKeyDao.FindKeys(map[string]any{"apitype = ?": "claude"}) keys, err := p.apiKeyDao.FindKeys(map[string]any{"active = ?": true, "apitype = ?": "claude"})
if err != nil { if err != nil {
return err return err
} }
@@ -287,6 +288,9 @@ func (p *Proxy) getOpenAISupportModels(apikey model.ApiKey) ([]string, error) {
var supportModels []string var supportModels []string
var req *http.Request var req *http.Request
if *apikey.ApiType == "azure" { if *apikey.ApiType == "azure" {
if strings.HasSuffix(*apikey.Endpoint, "/") {
apikey.Endpoint = utils.ToPtr(strings.TrimSuffix(*apikey.Endpoint, "/"))
}
req, _ = http.NewRequest("GET", *apikey.Endpoint+azureModelsUrl, nil) req, _ = http.NewRequest("GET", *apikey.Endpoint+azureModelsUrl, nil)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("api-key", *apikey.ApiKey) req.Header.Set("api-key", *apikey.ApiKey)

View File

@@ -93,6 +93,7 @@ func (dao *ApiKeyDAO) FindApiKeysBySupportModel(db *gorm.DB, modelName string) (
} }
err := db.Model(&model.ApiKey{}). err := db.Model(&model.ApiKey{}).
Joins("CROSS JOIN JSON_EACH(apikeys.support_models)"). Joins("CROSS JOIN JSON_EACH(apikeys.support_models)").
Where("active = true").
Where("value = ?", modelName). Where("value = ?", modelName).
Find(&apiKeys).Error Find(&apiKeys).Error
return apiKeys, err return apiKeys, err

View File

@@ -10,7 +10,7 @@ type ApiKey_PG struct {
Active *bool `gorm:"column:active;default:true" json:"active,omitempty"` Active *bool `gorm:"column:active;default:true" json:"active,omitempty"`
Endpoint *string `gorm:"column:endpoint" json:"endpoint,omitempty"` Endpoint *string `gorm:"column:endpoint" json:"endpoint,omitempty"`
ResourceNmae *string `gorm:"column:resource_name" json:"resource_name,omitempty"` ResourceNmae *string `gorm:"column:resource_name" json:"resource_name,omitempty"`
DeploymentName *string `gorm:"column:deployment_name" json:"deployment_name,omitempty"` // DeploymentName *string `gorm:"column:deployment_name" json:"deployment_name,omitempty"`
ApiSecret *string `gorm:"column:api_secret" json:"api_secret,omitempty"` ApiSecret *string `gorm:"column:api_secret" json:"api_secret,omitempty"`
ModelPrefix *string `gorm:"column:model_prefix" json:"model_prefix,omitempty"` ModelPrefix *string `gorm:"column:model_prefix" json:"model_prefix,omitempty"`
ModelAlias *string `gorm:"column:model_alias" json:"model_alias,omitempty"` ModelAlias *string `gorm:"column:model_alias" json:"model_alias,omitempty"`
@@ -33,7 +33,7 @@ type ApiKey struct {
Active *bool `gorm:"column:active;default:true" json:"active,omitempty"` Active *bool `gorm:"column:active;default:true" json:"active,omitempty"`
Endpoint *string `gorm:"column:endpoint" json:"endpoint,omitempty"` Endpoint *string `gorm:"column:endpoint" json:"endpoint,omitempty"`
ResourceNmae *string `gorm:"column:resource_name" json:"resource_name,omitempty"` ResourceNmae *string `gorm:"column:resource_name" json:"resource_name,omitempty"`
DeploymentName *string `gorm:"column:deployment_name" json:"deployment_name,omitempty"` // DeploymentName *string `gorm:"column:deployment_name" json:"deployment_name,omitempty"`
AccessKey *string `gorm:"column:access_key" json:"access_key,omitempty"` AccessKey *string `gorm:"column:access_key" json:"access_key,omitempty"`
SecretKey *string `gorm:"column:secret_key" json:"secret_key,omitempty"` SecretKey *string `gorm:"column:secret_key" json:"secret_key,omitempty"`
ModelPrefix *string `gorm:"column:model_prefix" json:"model_prefix,omitempty"` ModelPrefix *string `gorm:"column:model_prefix" json:"model_prefix,omitempty"`

View File

@@ -52,9 +52,6 @@ func (s *ApiKeyServiceImpl) UpdateApiKey(ctx context.Context, apikey *model.ApiK
if apikey.ResourceNmae != nil { if apikey.ResourceNmae != nil {
_key.ResourceNmae = apikey.ResourceNmae _key.ResourceNmae = apikey.ResourceNmae
} }
if apikey.DeploymentName != nil {
_key.DeploymentName = apikey.DeploymentName
}
if apikey.AccessKey != nil { if apikey.AccessKey != nil {
_key.AccessKey = apikey.AccessKey _key.AccessKey = apikey.AccessKey
} }

View File

@@ -47,6 +47,9 @@ func FetchOpenAISupportModels(db *gorm.DB, apikey *model.ApiKey) ([]string, erro
var supportModels []string var supportModels []string
var req *http.Request var req *http.Request
if *apikey.ApiType == "azure" { if *apikey.ApiType == "azure" {
if strings.HasSuffix(*apikey.Endpoint, "/") {
apikey.Endpoint = ToPtr(strings.TrimSuffix(*apikey.Endpoint, "/"))
}
req, _ = http.NewRequest("GET", *apikey.Endpoint+azureModelsUrl, nil) req, _ = http.NewRequest("GET", *apikey.Endpoint+azureModelsUrl, nil)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("api-key", *apikey.ApiKey) req.Header.Set("api-key", *apikey.ApiKey)

View File

@@ -86,6 +86,9 @@ func (o *OpenAICompatible) Chat(ctx context.Context, chatReq llm.ChatRequest) (*
} }
var buildurl string var buildurl string
if *o.ApiKey.Endpoint != "" { if *o.ApiKey.Endpoint != "" {
if strings.HasSuffix(*o.ApiKey.Endpoint, "/") {
o.ApiKey.ApiKey = utils.ToPtr(strings.TrimSuffix(*o.ApiKey.Endpoint, "/"))
}
buildurl = fmt.Sprintf("%s/openai/deployments/%s/chat/completions?api-version=%s", *o.ApiKey.Endpoint, formatModel(chatReq.Model), AzureApiVersion) buildurl = fmt.Sprintf("%s/openai/deployments/%s/chat/completions?api-version=%s", *o.ApiKey.Endpoint, formatModel(chatReq.Model), AzureApiVersion)
} else { } else {
buildurl = fmt.Sprintf("https://%s.openai.azure.com/openai/deployments/%s/chat/completions?api-version=%s", *o.ApiKey.ResourceNmae, formatModel(chatReq.Model), AzureApiVersion) buildurl = fmt.Sprintf("https://%s.openai.azure.com/openai/deployments/%s/chat/completions?api-version=%s", *o.ApiKey.ResourceNmae, formatModel(chatReq.Model), AzureApiVersion)