mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
feat: add unbind website for agent (#12500)
This commit is contained in:
@@ -152,6 +152,26 @@ func (b *BaseApi) BindAgentWebsite(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Unbind Agent website
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentIDReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/website/unbind [post]
|
||||
func (b *BaseApi) UnbindAgentWebsite(c *gin.Context) {
|
||||
var req dto.AgentIDReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UnbindWebsite(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent model config
|
||||
// @Accept json
|
||||
|
||||
@@ -46,6 +46,7 @@ type AgentItem struct {
|
||||
AppInstallID uint `json:"appInstallId"`
|
||||
WebsiteID uint `json:"websiteId"`
|
||||
WebsitePrimaryDomain string `json:"websitePrimaryDomain"`
|
||||
WebsiteType string `json:"websiteType"`
|
||||
WebsiteProtocol string `json:"websiteProtocol"`
|
||||
AccountID uint `json:"accountId"`
|
||||
AppVersion string `json:"appVersion"`
|
||||
|
||||
@@ -36,6 +36,7 @@ type IAgentService interface {
|
||||
ResetToken(req dto.AgentTokenResetReq) error
|
||||
UpdateRemark(req dto.AgentRemarkUpdateReq) error
|
||||
BindWebsite(req dto.AgentWebsiteBindReq) error
|
||||
UnbindWebsite(req dto.AgentIDReq) error
|
||||
GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig, error)
|
||||
UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
|
||||
GetHermesChatSessions(req dto.AgentIDReq) ([]dto.AgentHermesChatSessionItem, error)
|
||||
|
||||
@@ -48,6 +48,27 @@ func (a AgentService) BindWebsite(req dto.AgentWebsiteBindReq) error {
|
||||
return ensureOpenclawWebsiteAllowedOrigin(agent, &website)
|
||||
}
|
||||
|
||||
func (a AgentService) UnbindWebsite(req dto.AgentIDReq) error {
|
||||
agent, err := agentRepo.GetFirst(repo.WithByID(req.AgentID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if agent.WebsiteID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
website, err := websiteRepo.GetFirst(repo.WithByID(agent.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if website.Type == constant.Deployment {
|
||||
return buserr.New("ErrAgentWebsiteUnbindUnsupported")
|
||||
}
|
||||
|
||||
agent.WebsiteID = 0
|
||||
return agentRepo.Save(agent)
|
||||
}
|
||||
|
||||
func hydrateAgentWebsiteItems(items []dto.AgentItem) error {
|
||||
explicitWebsiteMap, err := loadAgentWebsiteMapByID(items)
|
||||
if err != nil {
|
||||
@@ -126,9 +147,11 @@ func fillAgentWebsiteItems(items []dto.AgentItem, explicitWebsiteMap map[uint]mo
|
||||
if !ok {
|
||||
items[index].WebsiteID = 0
|
||||
items[index].WebsitePrimaryDomain = ""
|
||||
items[index].WebsiteType = ""
|
||||
items[index].WebsiteProtocol = ""
|
||||
continue
|
||||
}
|
||||
items[index].WebsiteType = website.Type
|
||||
items[index].WebsiteProtocol = website.Protocol
|
||||
websiteDomains := websiteDomainMap[items[index].WebsiteID]
|
||||
if len(websiteDomains) == 0 {
|
||||
|
||||
@@ -64,6 +64,7 @@ ErrAgentLimitReached: 'Community Edition supports up to {{ .max }} AI agents. Up
|
||||
ErrAgentWebsiteBound: 'This agent is already bound to a website'
|
||||
ErrAgentWebsiteTypeUnsupported: 'Only proxy or static websites can be bound'
|
||||
ErrAgentWebsiteInUse: 'This website is already bound to another agent'
|
||||
ErrAgentWebsiteUnbindUnsupported: 'Deployment websites cannot be unbound manually'
|
||||
|
||||
#backup
|
||||
Localhost: 'Local'
|
||||
|
||||
@@ -59,6 +59,7 @@ ErrAgentLimitReached: 'La edición Community admite hasta {{ .max }} agentes de
|
||||
ErrAgentWebsiteBound: 'Este agente ya está vinculado a un sitio web'
|
||||
ErrAgentWebsiteTypeUnsupported: 'Solo se pueden vincular sitios proxy o estáticos'
|
||||
ErrAgentWebsiteInUse: 'Este sitio web ya está vinculado a otro agente'
|
||||
ErrAgentWebsiteUnbindUnsupported: 'Los sitios web de despliegue no se pueden desvincular manualmente'
|
||||
Localhost: 'Máquina local'
|
||||
ErrBackupInUsed: 'Cuenta de respaldo en uso por tarea programada'
|
||||
ErrBackupCheck: 'Conexión de respaldo falló: {{ .err }}'
|
||||
|
||||
@@ -59,6 +59,7 @@ ErrAgentLimitReached: 'Community Edition では AI エージェントを最大 {
|
||||
ErrAgentWebsiteBound: 'このエージェントはすでにサイトに関連付けられています'
|
||||
ErrAgentWebsiteTypeUnsupported: '関連付けできるのはプロキシサイトまたは静的サイトのみです'
|
||||
ErrAgentWebsiteInUse: 'このサイトはすでに別のエージェントに関連付けられています'
|
||||
ErrAgentWebsiteUnbindUnsupported: 'ワンクリックデプロイのサイトは手動で関連解除できません'
|
||||
Localhost: 'ローカルマシン'
|
||||
ErrBackupInUsed: 'バックアップアカウントがスケジュールで使用中'
|
||||
ErrBackupCheck: '接続テストに失敗しました: {{ .err }}'
|
||||
|
||||
@@ -59,6 +59,7 @@ ErrAgentLimitReached: '커뮤니티 에디션에서는 AI 에이전트를 최대
|
||||
ErrAgentWebsiteBound: '이 에이전트는 이미 웹사이트에 연결되어 있습니다'
|
||||
ErrAgentWebsiteTypeUnsupported: '프록시 또는 정적 웹사이트만 연결할 수 있습니다'
|
||||
ErrAgentWebsiteInUse: '이 웹사이트는 이미 다른 에이전트에 연결되어 있습니다'
|
||||
ErrAgentWebsiteUnbindUnsupported: '원클릭 배포 웹사이트는 수동으로 연결 해제할 수 없습니다'
|
||||
Localhost: '로컬 머신'
|
||||
ErrBackupInUsed: '백업 계정이 예약에 사용 중'
|
||||
ErrBackupCheck: '연결 테스트 실패: {{ .err }}'
|
||||
|
||||
@@ -59,6 +59,7 @@ ErrAgentLimitReached: 'Edisi Community menyokong sehingga {{ .max }} ejen AI. Na
|
||||
ErrAgentWebsiteBound: 'Ejen ini sudah dipautkan ke laman web'
|
||||
ErrAgentWebsiteTypeUnsupported: 'Hanya laman web proxy atau statik boleh dipautkan'
|
||||
ErrAgentWebsiteInUse: 'Laman web ini sudah dipautkan ke ejen lain'
|
||||
ErrAgentWebsiteUnbindUnsupported: 'Laman web one-click deployment tidak menyokong nyahikat manual'
|
||||
Localhost: 'Mesin Tempatan'
|
||||
ErrBackupInUsed: 'Akaun sandaran sedang digunakan oleh tugas'
|
||||
ErrBackupCheck: 'Ujian sambungan gagal: {{ .err }}'
|
||||
|
||||
@@ -59,6 +59,7 @@ ErrAgentLimitReached: 'A edição Community suporta até {{ .max }} agentes de I
|
||||
ErrAgentWebsiteBound: 'Este agente já está vinculado a um site'
|
||||
ErrAgentWebsiteTypeUnsupported: 'Somente sites proxy ou estáticos podem ser vinculados'
|
||||
ErrAgentWebsiteInUse: 'Este site já está vinculado a outro agente'
|
||||
ErrAgentWebsiteUnbindUnsupported: 'Sites implantados em um clique não podem ser desvinculados manualmente'
|
||||
Localhost: 'Máquina Local'
|
||||
ErrBackupInUsed: 'Conta de backup em uso por tarefa'
|
||||
ErrBackupCheck: 'Teste de conexão falhou: {{ .err }}'
|
||||
|
||||
@@ -59,6 +59,7 @@ ErrAgentLimitReached: 'Community Edition поддерживает до {{ .max }
|
||||
ErrAgentWebsiteBound: 'Этот агент уже связан с сайтом'
|
||||
ErrAgentWebsiteTypeUnsupported: 'Можно связывать только proxy- или static-сайты'
|
||||
ErrAgentWebsiteInUse: 'Этот сайт уже связан с другим агентом'
|
||||
ErrAgentWebsiteUnbindUnsupported: 'Сайты one-click deployment нельзя отвязать вручную'
|
||||
Localhost: 'Локальная машина'
|
||||
ErrBackupInUsed: 'Аккаунт бэкапа занят задачей'
|
||||
ErrBackupCheck: 'Проверка подключения не удалась: {{ .err }}'
|
||||
|
||||
@@ -59,6 +59,7 @@ ErrAgentLimitReached: 'Community sürümü en fazla {{ .max }} AI ajanını dest
|
||||
ErrAgentWebsiteBound: 'Bu ajan zaten bir web sitesine bağlı'
|
||||
ErrAgentWebsiteTypeUnsupported: 'Yalnızca proxy veya statik web siteleri bağlanabilir'
|
||||
ErrAgentWebsiteInUse: 'Bu web sitesi zaten başka bir ajana bağlı'
|
||||
ErrAgentWebsiteUnbindUnsupported: 'Tek tıkla dağıtılan web sitelerinin bağlantısı manuel olarak kaldırılamaz'
|
||||
Localhost: 'Yerel Makine'
|
||||
ErrBackupInUsed: 'Yedek hesabı görevde kullanılıyor'
|
||||
ErrBackupCheck: 'Bağlantı testi başarısız: {{ .err }}'
|
||||
|
||||
@@ -59,6 +59,7 @@ ErrAgentLimitReached: '社群版最多支援建立 {{ .max }} 個 AI 智能體
|
||||
ErrAgentWebsiteBound: '該智能體已關聯網站'
|
||||
ErrAgentWebsiteTypeUnsupported: '只能關聯反向代理或靜態網站'
|
||||
ErrAgentWebsiteInUse: '該網站已被其他智能體關聯'
|
||||
ErrAgentWebsiteUnbindUnsupported: '一鍵部署網站不支援手動解綁'
|
||||
Localhost: '本機'
|
||||
ErrBackupInUsed: '此備份帳號已在排程任務中使用,無法刪除'
|
||||
ErrBackupCheck: '備份帳號測試連線失敗{{ .err }}'
|
||||
|
||||
@@ -64,6 +64,7 @@ ErrAgentLimitReached: "社区版最多支持创建 {{ .max }} 个智能体,升
|
||||
ErrAgentWebsiteBound: "该智能体已关联网站"
|
||||
ErrAgentWebsiteTypeUnsupported: "只能关联反向代理或静态网站"
|
||||
ErrAgentWebsiteInUse: "该网站已被其他智能体关联"
|
||||
ErrAgentWebsiteUnbindUnsupported: "一键部署网站不支持手动解绑"
|
||||
|
||||
#backup
|
||||
Localhost: '本机'
|
||||
|
||||
@@ -47,6 +47,7 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
aiToolsRouter.POST("/agents/token/reset", baseApi.ResetAgentToken)
|
||||
aiToolsRouter.POST("/agents/remark", baseApi.UpdateAgentRemark)
|
||||
aiToolsRouter.POST("/agents/website/bind", baseApi.BindAgentWebsite)
|
||||
aiToolsRouter.POST("/agents/website/unbind", baseApi.UnbindAgentWebsite)
|
||||
aiToolsRouter.POST("/agents/model/get", baseApi.GetAgentModelConfig)
|
||||
aiToolsRouter.POST("/agents/model/update", baseApi.UpdateAgentModelConfig)
|
||||
aiToolsRouter.POST("/agents/hermes/chat/sessions", baseApi.GetHermesChatSessions)
|
||||
|
||||
@@ -282,6 +282,7 @@ export namespace AI {
|
||||
appInstallId: number;
|
||||
websiteId: number;
|
||||
websitePrimaryDomain: string;
|
||||
websiteType: string;
|
||||
websiteProtocol: string;
|
||||
accountId: number;
|
||||
appVersion: string;
|
||||
|
||||
@@ -122,6 +122,10 @@ export const bindAgentWebsite = (req: AI.AgentWebsiteBindReq) => {
|
||||
return http.post(`/ai/agents/website/bind`, req);
|
||||
};
|
||||
|
||||
export const unbindAgentWebsite = (req: AI.AgentIDReq) => {
|
||||
return http.post(`/ai/agents/website/unbind`, req);
|
||||
};
|
||||
|
||||
export const getAgentModelConfig = (req: AI.AgentIDReq) => {
|
||||
return http.post<AI.AgentModelConfig>(`/ai/agents/model/get`, req);
|
||||
};
|
||||
|
||||
@@ -126,6 +126,14 @@
|
||||
</table>
|
||||
<el-empty v-else :image-size="48" :description="$t('commons.msg.noneData')" />
|
||||
</el-popover>
|
||||
<el-button
|
||||
v-if="canUnbindWebsite(row)"
|
||||
link
|
||||
type="primary"
|
||||
@click="onUnbindWebsite(row)"
|
||||
>
|
||||
{{ $t('commons.button.unbind') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button v-else link type="primary" @click="openBindWebsite(row)">
|
||||
{{ $t('commons.button.bind') }}
|
||||
@@ -204,7 +212,7 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { deleteAgentCheck, pageAgents, resetAgentToken, updateAgentRemark } from '@/api/modules/ai';
|
||||
import { deleteAgentCheck, pageAgents, resetAgentToken, unbindAgentWebsite, updateAgentRemark } from '@/api/modules/ai';
|
||||
import { checkAppInstalled, installedOp, searchApp, searchAppInstalled } from '@/api/modules/app';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import { App } from '@/api/interface/app';
|
||||
@@ -573,6 +581,28 @@ const openBindWebsite = (row: AI.AgentItem) => {
|
||||
bindWebsiteRef.value?.acceptParams(row);
|
||||
};
|
||||
|
||||
const canUnbindWebsite = (row: AI.AgentItem) => {
|
||||
return row.websiteType === 'proxy' || row.websiteType === 'static';
|
||||
};
|
||||
|
||||
const onUnbindWebsite = async (row: AI.AgentItem) => {
|
||||
await ElMessageBox.confirm(
|
||||
i18n.global.t('aiTools.mcp.operatorHelper', [
|
||||
i18n.global.t('menu.website'),
|
||||
i18n.global.t('commons.button.unbind'),
|
||||
]),
|
||||
i18n.global.t('commons.button.unbind'),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'info',
|
||||
},
|
||||
);
|
||||
await unbindAgentWebsite({ agentId: row.id });
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
await search();
|
||||
};
|
||||
|
||||
const loadOpenrestyHttpsPort = async () => {
|
||||
if (openrestyPortLoaded.value) {
|
||||
return;
|
||||
|
||||
@@ -1,18 +1,35 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card class="router_card p-2 sm:p-3 mt-2">
|
||||
<div class="flex w-full justify-start items-center">
|
||||
<el-button type="primary" :plain="index !== '0'" @click="changeTab('0')">
|
||||
{{ $t('xpack.alert.list') }}
|
||||
</el-button>
|
||||
<el-button type="primary" :plain="index !== '1'" @click="changeTab('1')">
|
||||
{{ $t('xpack.alert.logs') }}
|
||||
</el-button>
|
||||
<el-button type="primary" :plain="index !== '2'" @click="changeTab('2')">
|
||||
{{ $t('commons.button.set') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<div class="content-container__search">
|
||||
<el-card>
|
||||
<div>
|
||||
<el-button
|
||||
class="tag-button"
|
||||
:class="index === '0' ? '' : 'no-active'"
|
||||
:type="index === '0' ? 'primary' : ''"
|
||||
@click="changeTab('0')"
|
||||
>
|
||||
{{ $t('xpack.alert.list') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
class="tag-button"
|
||||
:class="index === '1' ? '' : 'no-active'"
|
||||
:type="index === '1' ? 'primary' : ''"
|
||||
@click="changeTab('1')"
|
||||
>
|
||||
{{ $t('xpack.alert.logs') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
class="tag-button"
|
||||
:class="index === '2' ? '' : 'no-active'"
|
||||
:type="index === '2' ? 'primary' : ''"
|
||||
@click="changeTab('2')"
|
||||
>
|
||||
{{ $t('commons.button.set') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<AlertDash v-if="index == '0'" />
|
||||
<AlertLogs v-if="index == '1'" />
|
||||
<AlertSetting v-if="index == '2'" />
|
||||
@@ -37,3 +54,20 @@ onMounted(async () => {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.content-container__search {
|
||||
margin-top: 7px;
|
||||
|
||||
:deep(.el-card) {
|
||||
--el-card-padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-button {
|
||||
&.no-active {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user