From a53b6cd28ff2ce5662ada82379ea44a06b179b81 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Tue, 13 May 2025 21:15:59 +0800 Subject: [PATCH 01/29] =?UTF-8?q?perf:=20=E5=AE=9D=E5=A1=94=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E3=80=811panel=20=E6=94=B9=E6=88=90=E5=AE=8C=E5=85=A8?= =?UTF-8?q?=E5=85=8D=E8=B4=B9=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- step.md | 1 + 1 file changed, 1 insertion(+) diff --git a/step.md b/step.md index 170cf124..bd023e86 100644 --- a/step.md +++ b/step.md @@ -9,6 +9,7 @@ 2. 注册一个域名(支持阿里云万网、腾讯云DnsPod、华为云) 3. 准备好以上DNS解析服务商的AccessKey 和 AccessSecret 4. 证书要部署的目标(可选,单纯当成证书申请工具用也不错) + ## 自动化流水线创建 From 08e779f9f1265ce0a9171b0cb783b1bd5bbfb65e Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Tue, 13 May 2025 23:06:54 +0800 Subject: [PATCH 02/29] docs: ipv6 --- docs/guide/qa/index.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/guide/qa/index.md b/docs/guide/qa/index.md index eae66615..7c5050dc 100644 --- a/docs/guide/qa/index.md +++ b/docs/guide/qa/index.md @@ -16,4 +16,22 @@ services: # # ↓↓↓↓ ------- # 如果你服务器部署在国外,可以用这个替换上面阿里云的公共dns # - 8.8.8.8 # 谷歌公共dns # - 8.8.4.4 -``` \ No newline at end of file +``` + + +## 2. 连接IPv6超时 +docker-compose 需要放开IPv6网络的配置 +```yaml +services: + certd: + networks: + - ip6net +# ↓↓↓↓ -------------------------------------------------------------- 启用ipv6网络,还需要把上面networks的注释放开 +networks: + ip6net: + enable_ipv6: true + ipam: + config: + - subnet: 2001:db8::/64 + +``` From e332ce28f838a2af06c720f139f7bb615d716c26 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 14 May 2025 01:06:30 +0800 Subject: [PATCH 03/29] chore: baotawaf access --- docs/guide/development/demo/access.md | 88 +++++++++++++++++++ .../plugins/common/remote-input.vue | 2 +- 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 docs/guide/development/demo/access.md diff --git a/docs/guide/development/demo/access.md b/docs/guide/development/demo/access.md new file mode 100644 index 00000000..550d9184 --- /dev/null +++ b/docs/guide/development/demo/access.md @@ -0,0 +1,88 @@ + +# 授权插件Demo + +```ts +import { AccessInput, BaseAccess, IsAccess } from '@certd/pipeline'; +import { isDev } from '../../utils/env.js'; + +/** + * 这个注解将注册一个授权配置 + * 在certd的后台管理系统中,用户可以选择添加此类型的授权 + */ +@IsAccess({ + name: 'demo', + title: '授权插件示例', + icon: 'clarity:plugin-line', + desc: '', +}) +export class DemoAccess extends BaseAccess { + /** + * 授权属性配置 + */ + @AccessInput({ + title: '密钥Id', + component: { + placeholder: 'demoKeyId', + }, + required: true, + }) + demoKeyId = ''; + + /** + * 授权属性配置 + */ + @AccessInput({ + //标题 + title: '密钥串', + component: { + //input组件的placeholder + placeholder: 'demoKeySecret', + }, + //是否必填 + required: true, + //改属性是否需要加密 + encrypt: true, + }) + //属性名称 + demoKeySecret = ''; +} +new DemoAccess(); +``` + + +# 阿里云授权 +```ts + +import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline"; + +@IsAccess({ + name: "aliyun", + title: "阿里云授权", + desc: "", + icon: "ant-design:aliyun-outlined", + order: 0, +}) +export class AliyunAccess extends BaseAccess { + @AccessInput({ + title: "accessKeyId", + component: { + placeholder: "accessKeyId", + }, + helper: "登录阿里云控制台->AccessKey管理页面获取。", + required: true, + }) + accessKeyId = ""; + @AccessInput({ + title: "accessKeySecret", + component: { + placeholder: "accessKeySecret", + }, + required: true, + encrypt: true, + helper: "注意:证书申请需要dns解析权限;其他阿里云插件,需要对应的权限,比如证书上传需要证书管理权限;嫌麻烦就用主账号的全量权限的accessKey", + }) + accessKeySecret = ""; +} + +new AliyunAccess(); +``` \ No newline at end of file diff --git a/packages/ui/certd-client/src/components/plugins/common/remote-input.vue b/packages/ui/certd-client/src/components/plugins/common/remote-input.vue index 9fd0f3fb..6a1e1c48 100644 --- a/packages/ui/certd-client/src/components/plugins/common/remote-input.vue +++ b/packages/ui/certd-client/src/components/plugins/common/remote-input.vue @@ -18,7 +18,7 @@ const props = defineProps<{ modelValue: string; title: string; action: string; - form: any; + form?: any; button?: any; }>(); From 0b9a02afde56bb43c9c524be7e35c83905cfb948 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 14 May 2025 08:56:48 +0800 Subject: [PATCH 04/29] docs: docs --- packages/ui/certd-client/index.html | 11 ++ packages/ui/certd-client/public/iframe.js | 151 ++++++++++++++++++ .../plugin/upload-to-host/index.ts | 5 +- 3 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 packages/ui/certd-client/public/iframe.js diff --git a/packages/ui/certd-client/index.html b/packages/ui/certd-client/index.html index 1740a304..f6398595 100644 --- a/packages/ui/certd-client/index.html +++ b/packages/ui/certd-client/index.html @@ -7,6 +7,17 @@ Loading +
diff --git a/packages/ui/certd-client/public/iframe.js b/packages/ui/certd-client/public/iframe.js new file mode 100644 index 00000000..22bfaf74 --- /dev/null +++ b/packages/ui/certd-client/public/iframe.js @@ -0,0 +1,151 @@ +function embedChatbot() { + const chatBtnId = "fastgpt-chatbot-button"; + const chatWindowId = "fastgpt-chatbot-window"; + const script = document.getElementById("chatbot-iframe"); + const botSrc = script?.getAttribute("data-bot-src"); + const defaultOpen = script?.getAttribute("data-default-open") === "true"; + const canDrag = script?.getAttribute("data-drag") === "true"; + const MessageIcon = + script?.getAttribute("data-open-icon") || + `data:image/svg+xml;base64,PHN2ZyB0PSIxNjkwNTMyNzg1NjY0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQxMzIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48cGF0aCBkPSJNNTEyIDMyQzI0Ny4wNCAzMiAzMiAyMjQgMzIgNDY0QTQxMC4yNCA0MTAuMjQgMCAwIDAgMTcyLjQ4IDc2OEwxNjAgOTY1LjEyYTI1LjI4IDI1LjI4IDAgMCAwIDM5LjA0IDIyLjRsMTY4LTExMkE1MjguNjQgNTI4LjY0IDAgMCAwIDUxMiA4OTZjMjY0Ljk2IDAgNDgwLTE5MiA0ODAtNDMyUzc3Ni45NiAzMiA1MTIgMzJ6IG0yNDQuOCA0MTZsLTM2MS42IDMwMS43NmExMi40OCAxMi40OCAwIDAgMS0xOS44NC0xMi40OGw1OS4yLTIzMy45MmgtMTYwYTEyLjQ4IDEyLjQ4IDAgMCAxLTcuMzYtMjMuMzZsMzYxLjYtMzAxLjc2YTEyLjQ4IDEyLjQ4IDAgMCAxIDE5Ljg0IDEyLjQ4bC01OS4yIDIzMy45MmgxNjBhMTIuNDggMTIuNDggMCAwIDEgOCAyMi4wOHoiIGZpbGw9IiM0ZTgzZmQiIHAtaWQ9IjQxMzMiPjwvcGF0aD48L3N2Zz4=`; + const CloseIcon = + script?.getAttribute("data-close-icon") || + "data:image/svg+xml;base64,PHN2ZyB0PSIxNjkwNTM1NDQxNTI2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjYzNjciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48cGF0aCBkPSJNNTEyIDEwMjRBNTEyIDUxMiAwIDEgMSA1MTIgMGE1MTIgNTEyIDAgMCAxIDAgMTAyNHpNMzA1Ljk1NjU3MSAzNzAuMzk1NDI5TDQ0Ny40ODggNTEyIDMwNS45NTY1NzEgNjUzLjYwNDU3MWE0NS41NjggNDUuNTY4IDAgMSAwIDY0LjQzODg1OCA2NC40Mzg4NThMNTEyIDU3Ni41MTJsMTQxLjYwNDU3MSAxNDEuNTMxNDI5YTQ1LjU2OCA0NS41NjggMCAwIDAgNjQuNDM4ODU4LTY0LjQzODg1OEw1NzYuNTEyIDUxMmwxNDEuNTMxNDI5LTE0MS42MDQ1NzFhNDUuNTY4IDQ1LjU2OCAwIDEgMC02NC40Mzg4NTgtNjQuNDM4ODU4TDUxMiA0NDcuNDg4IDM3MC4zOTU0MjkgMzA1Ljk1NjU3MWE0NS41NjggNDUuNTY4IDAgMCAwLTY0LjQzODg1OCA2NC40Mzg4NTh6IiBmaWxsPSIjNGU4M2ZkIiBwLWlkPSI2MzY4Ij48L3BhdGg+PC9zdmc+"; + + if (!botSrc) { + console.error(`Can't find appid`); + return; + } + if (document.getElementById(chatBtnId)) { + return; + } + + const ChatBtn = document.createElement("div"); + ChatBtn.id = chatBtnId; + ChatBtn.style.cssText = "position: fixed; bottom: 30px; right: 60px; width: 40px; height: 40px; cursor: pointer; z-index: 2147483647; transition: 0;"; + + const ChatBtnDiv = document.createElement("img"); + ChatBtnDiv.src = defaultOpen ? CloseIcon : MessageIcon; + ChatBtnDiv.setAttribute("width", "100%"); + ChatBtnDiv.setAttribute("height", "100%"); + ChatBtnDiv.draggable = false; + + const parent = document.createElement("div"); + const iframe = document.createElement("iframe"); + iframe.allow = "*"; + iframe.referrerPolicy = "no-referrer"; + iframe.title = "FastGPT Chat Window"; + parent.id = chatWindowId; + iframe.src = botSrc; + parent.style.cssText = + "border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; width: 375px; height: 667px; max-width: 90vw; max-height: 85vh; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;"; + parent.style.visibility = defaultOpen ? "unset" : "hidden"; + iframe.style.cssText = "border: none;width:100%;height:100%"; + + document.body.appendChild(parent); + parent.appendChild(iframe); + + const tips = document.createElement("div"); + tips.innerText = "内容由AI生成,仅供参考"; + tips.style.cssText = "padding:5px;font-size:10px;display:flex; justify-content:center;border-top: 1px solid #ddd;"; + parent.appendChild(tips); + + const observer = new MutationObserver(mutations => { + mutations.forEach(mutation => { + if (mutation.type === "attributes" && mutation.attributeName === "data-bot-src") { + const newBotSrc = script.getAttribute("data-bot-src"); + if (newBotSrc) { + iframe.src = newBotSrc; + } + } + }); + }); + observer.observe(script, { + attributes: true, + attributeFilter: ["data-bot-src"], + }); + + let chatBtnDragged = false; + let chatBtnDown = false; + let chatBtnMouseX; + let chatBtnMouseY; + + const updateChatWindowPosition = () => { + const chatWindow = document.getElementById(chatWindowId); + const btn = ChatBtn.getBoundingClientRect(); + const [vw, vh, ww, wh] = [window.innerWidth, window.innerHeight, chatWindow.offsetWidth, chatWindow.offsetHeight]; + + let right = 0; + if (btn.left >= ww) { + right = vw - btn.left + 10; // 左侧有空间则放在左侧,间距 10 + } else if (vw - btn.right >= ww) { + right = vw - btn.right - ww - 10; // 右侧有空间则放在右侧 + } + + let bottom = Math.max(30, vh - btn.bottom); // 聊天窗口底部和按钮对齐,最少 30 + if (btn.top < wh) { + bottom = Math.min(bottom, vh - wh - 30); // 确保聊天窗口不超出顶部 + } + + chatWindow.style.right = `${right}px`; + chatWindow.style.bottom = `${bottom}px`; + }; + + ChatBtn.addEventListener("click", function () { + if (chatBtnDragged) { + chatBtnDragged = false; + return; + } + const chatWindow = document.getElementById(chatWindowId); + + if (!chatWindow) return; + const visibilityVal = chatWindow.style.visibility; + if (visibilityVal === "hidden") { + chatWindow.style.visibility = "unset"; + ChatBtnDiv.src = CloseIcon; + } else { + chatWindow.style.visibility = "hidden"; + ChatBtnDiv.src = MessageIcon; + } + }); + + ChatBtn.addEventListener("mousedown", e => { + e.stopPropagation(); + chatBtnMouseX = e.clientX; + chatBtnMouseY = e.clientY; + chatBtnDown = true; + + ChatBtn.initialRight = parseInt(ChatBtn.style.right) || 60; + ChatBtn.initialBottom = parseInt(ChatBtn.style.bottom) || 30; + }); + + window.addEventListener("mousemove", e => { + e.stopPropagation(); + if (!canDrag || !chatBtnDown) return; + + chatBtnDragged = true; + + const deltaX = e.clientX - chatBtnMouseX; + const deltaY = e.clientY - chatBtnMouseY; + + let newRight = ChatBtn.initialRight - deltaX; + let newBottom = ChatBtn.initialBottom - deltaY; + + newRight = Math.max(20, Math.min(window.innerWidth - 60, newRight)); + newBottom = Math.max(30, Math.min(window.innerHeight - 70, newBottom)); + + ChatBtn.style.right = `${newRight}px`; + ChatBtn.style.bottom = `${newBottom}px`; + + updateChatWindowPosition(); + }); + + window.addEventListener("mouseup", e => { + chatBtnDown = false; + }); + + ChatBtn.appendChild(ChatBtnDiv); + document.body.appendChild(ChatBtn); + updateChatWindowPosition(); +} +window.addEventListener("load", embedChatbot); diff --git a/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts b/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts index 4def0157..9a0c9f93 100644 --- a/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts @@ -64,7 +64,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { crtPath!: string; @TaskInput({ title: '私钥保存路径', - helper: '需要有写入权限,路径要包含私钥文件名,例如:/tmp/cert.key', + helper: '原本的私钥保存路径,需要有写入权限,路径要包含私钥文件名,例如:/tmp/cert.key', component: { placeholder: '/root/deploy/nginx/cert.key', }, @@ -212,8 +212,9 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { name: 'a-textarea', vModel: 'value', rows: 6, + placeholder: 'systemctl restart nginx ', }, - helper: '上传后执行脚本命令,不填则不执行\n注意:如果目标主机是windows,且终端是cmd,系统会自动将多行命令通过“&&”连接成一行', + helper: '上传后执行脚本命令,让证书生效(比如重启nginx),不填则不执行\n注意:如果目标主机是windows,且终端是cmd,系统会自动将多行命令通过“&&”连接成一行', required: false, }) script!: string; From aa96859798166426e485947a6590464de189de05 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 14 May 2025 15:03:47 +0800 Subject: [PATCH 05/29] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81AI=E5=88=86?= =?UTF-8?q?=E6=9E=90=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide/qa/index.md | 4 + packages/ui/certd-client/index.html | 11 -- packages/ui/certd-client/public/iframe.js | 151 ------------------ packages/ui/certd-client/src/App.vue | 12 +- .../certd-client/src/components/ai/index.vue | 10 +- .../pipeline/components/change-group.vue | 20 +-- .../pipeline/component/task-view/index.vue | 25 +++ .../src/views/sys/console/index.vue | 22 ++- 8 files changed, 67 insertions(+), 188 deletions(-) delete mode 100644 packages/ui/certd-client/public/iframe.js diff --git a/docs/guide/qa/index.md b/docs/guide/qa/index.md index 7c5050dc..b2b882d7 100644 --- a/docs/guide/qa/index.md +++ b/docs/guide/qa/index.md @@ -35,3 +35,7 @@ networks: - subnet: 2001:db8::/64 ``` + +## 3. SSL_CERT_NOT_MATCH_DOMAIN_ERROR +部署证书任务报类似 `SSL_CERT_NOT_MATCH_DOMAIN_ERROR`错误 +这是由于当前流水线的证书域名与要部署的目标站点的域名不匹配导致的,在申请证书任务中,增加目标站点域名,重新运行流水线即可 \ No newline at end of file diff --git a/packages/ui/certd-client/index.html b/packages/ui/certd-client/index.html index f6398595..1740a304 100644 --- a/packages/ui/certd-client/index.html +++ b/packages/ui/certd-client/index.html @@ -7,17 +7,6 @@ Loading -
diff --git a/packages/ui/certd-client/public/iframe.js b/packages/ui/certd-client/public/iframe.js deleted file mode 100644 index 22bfaf74..00000000 --- a/packages/ui/certd-client/public/iframe.js +++ /dev/null @@ -1,151 +0,0 @@ -function embedChatbot() { - const chatBtnId = "fastgpt-chatbot-button"; - const chatWindowId = "fastgpt-chatbot-window"; - const script = document.getElementById("chatbot-iframe"); - const botSrc = script?.getAttribute("data-bot-src"); - const defaultOpen = script?.getAttribute("data-default-open") === "true"; - const canDrag = script?.getAttribute("data-drag") === "true"; - const MessageIcon = - script?.getAttribute("data-open-icon") || - `data:image/svg+xml;base64,PHN2ZyB0PSIxNjkwNTMyNzg1NjY0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQxMzIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48cGF0aCBkPSJNNTEyIDMyQzI0Ny4wNCAzMiAzMiAyMjQgMzIgNDY0QTQxMC4yNCA0MTAuMjQgMCAwIDAgMTcyLjQ4IDc2OEwxNjAgOTY1LjEyYTI1LjI4IDI1LjI4IDAgMCAwIDM5LjA0IDIyLjRsMTY4LTExMkE1MjguNjQgNTI4LjY0IDAgMCAwIDUxMiA4OTZjMjY0Ljk2IDAgNDgwLTE5MiA0ODAtNDMyUzc3Ni45NiAzMiA1MTIgMzJ6IG0yNDQuOCA0MTZsLTM2MS42IDMwMS43NmExMi40OCAxMi40OCAwIDAgMS0xOS44NC0xMi40OGw1OS4yLTIzMy45MmgtMTYwYTEyLjQ4IDEyLjQ4IDAgMCAxLTcuMzYtMjMuMzZsMzYxLjYtMzAxLjc2YTEyLjQ4IDEyLjQ4IDAgMCAxIDE5Ljg0IDEyLjQ4bC01OS4yIDIzMy45MmgxNjBhMTIuNDggMTIuNDggMCAwIDEgOCAyMi4wOHoiIGZpbGw9IiM0ZTgzZmQiIHAtaWQ9IjQxMzMiPjwvcGF0aD48L3N2Zz4=`; - const CloseIcon = - script?.getAttribute("data-close-icon") || - "data:image/svg+xml;base64,PHN2ZyB0PSIxNjkwNTM1NDQxNTI2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjYzNjciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48cGF0aCBkPSJNNTEyIDEwMjRBNTEyIDUxMiAwIDEgMSA1MTIgMGE1MTIgNTEyIDAgMCAxIDAgMTAyNHpNMzA1Ljk1NjU3MSAzNzAuMzk1NDI5TDQ0Ny40ODggNTEyIDMwNS45NTY1NzEgNjUzLjYwNDU3MWE0NS41NjggNDUuNTY4IDAgMSAwIDY0LjQzODg1OCA2NC40Mzg4NThMNTEyIDU3Ni41MTJsMTQxLjYwNDU3MSAxNDEuNTMxNDI5YTQ1LjU2OCA0NS41NjggMCAwIDAgNjQuNDM4ODU4LTY0LjQzODg1OEw1NzYuNTEyIDUxMmwxNDEuNTMxNDI5LTE0MS42MDQ1NzFhNDUuNTY4IDQ1LjU2OCAwIDEgMC02NC40Mzg4NTgtNjQuNDM4ODU4TDUxMiA0NDcuNDg4IDM3MC4zOTU0MjkgMzA1Ljk1NjU3MWE0NS41NjggNDUuNTY4IDAgMCAwLTY0LjQzODg1OCA2NC40Mzg4NTh6IiBmaWxsPSIjNGU4M2ZkIiBwLWlkPSI2MzY4Ij48L3BhdGg+PC9zdmc+"; - - if (!botSrc) { - console.error(`Can't find appid`); - return; - } - if (document.getElementById(chatBtnId)) { - return; - } - - const ChatBtn = document.createElement("div"); - ChatBtn.id = chatBtnId; - ChatBtn.style.cssText = "position: fixed; bottom: 30px; right: 60px; width: 40px; height: 40px; cursor: pointer; z-index: 2147483647; transition: 0;"; - - const ChatBtnDiv = document.createElement("img"); - ChatBtnDiv.src = defaultOpen ? CloseIcon : MessageIcon; - ChatBtnDiv.setAttribute("width", "100%"); - ChatBtnDiv.setAttribute("height", "100%"); - ChatBtnDiv.draggable = false; - - const parent = document.createElement("div"); - const iframe = document.createElement("iframe"); - iframe.allow = "*"; - iframe.referrerPolicy = "no-referrer"; - iframe.title = "FastGPT Chat Window"; - parent.id = chatWindowId; - iframe.src = botSrc; - parent.style.cssText = - "border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; width: 375px; height: 667px; max-width: 90vw; max-height: 85vh; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;"; - parent.style.visibility = defaultOpen ? "unset" : "hidden"; - iframe.style.cssText = "border: none;width:100%;height:100%"; - - document.body.appendChild(parent); - parent.appendChild(iframe); - - const tips = document.createElement("div"); - tips.innerText = "内容由AI生成,仅供参考"; - tips.style.cssText = "padding:5px;font-size:10px;display:flex; justify-content:center;border-top: 1px solid #ddd;"; - parent.appendChild(tips); - - const observer = new MutationObserver(mutations => { - mutations.forEach(mutation => { - if (mutation.type === "attributes" && mutation.attributeName === "data-bot-src") { - const newBotSrc = script.getAttribute("data-bot-src"); - if (newBotSrc) { - iframe.src = newBotSrc; - } - } - }); - }); - observer.observe(script, { - attributes: true, - attributeFilter: ["data-bot-src"], - }); - - let chatBtnDragged = false; - let chatBtnDown = false; - let chatBtnMouseX; - let chatBtnMouseY; - - const updateChatWindowPosition = () => { - const chatWindow = document.getElementById(chatWindowId); - const btn = ChatBtn.getBoundingClientRect(); - const [vw, vh, ww, wh] = [window.innerWidth, window.innerHeight, chatWindow.offsetWidth, chatWindow.offsetHeight]; - - let right = 0; - if (btn.left >= ww) { - right = vw - btn.left + 10; // 左侧有空间则放在左侧,间距 10 - } else if (vw - btn.right >= ww) { - right = vw - btn.right - ww - 10; // 右侧有空间则放在右侧 - } - - let bottom = Math.max(30, vh - btn.bottom); // 聊天窗口底部和按钮对齐,最少 30 - if (btn.top < wh) { - bottom = Math.min(bottom, vh - wh - 30); // 确保聊天窗口不超出顶部 - } - - chatWindow.style.right = `${right}px`; - chatWindow.style.bottom = `${bottom}px`; - }; - - ChatBtn.addEventListener("click", function () { - if (chatBtnDragged) { - chatBtnDragged = false; - return; - } - const chatWindow = document.getElementById(chatWindowId); - - if (!chatWindow) return; - const visibilityVal = chatWindow.style.visibility; - if (visibilityVal === "hidden") { - chatWindow.style.visibility = "unset"; - ChatBtnDiv.src = CloseIcon; - } else { - chatWindow.style.visibility = "hidden"; - ChatBtnDiv.src = MessageIcon; - } - }); - - ChatBtn.addEventListener("mousedown", e => { - e.stopPropagation(); - chatBtnMouseX = e.clientX; - chatBtnMouseY = e.clientY; - chatBtnDown = true; - - ChatBtn.initialRight = parseInt(ChatBtn.style.right) || 60; - ChatBtn.initialBottom = parseInt(ChatBtn.style.bottom) || 30; - }); - - window.addEventListener("mousemove", e => { - e.stopPropagation(); - if (!canDrag || !chatBtnDown) return; - - chatBtnDragged = true; - - const deltaX = e.clientX - chatBtnMouseX; - const deltaY = e.clientY - chatBtnMouseY; - - let newRight = ChatBtn.initialRight - deltaX; - let newBottom = ChatBtn.initialBottom - deltaY; - - newRight = Math.max(20, Math.min(window.innerWidth - 60, newRight)); - newBottom = Math.max(30, Math.min(window.innerHeight - 70, newBottom)); - - ChatBtn.style.right = `${newRight}px`; - ChatBtn.style.bottom = `${newBottom}px`; - - updateChatWindowPosition(); - }); - - window.addEventListener("mouseup", e => { - chatBtnDown = false; - }); - - ChatBtn.appendChild(ChatBtnDiv); - document.body.appendChild(ChatBtn); - updateChatWindowPosition(); -} -window.addEventListener("load", embedChatbot); diff --git a/packages/ui/certd-client/src/App.vue b/packages/ui/certd-client/src/App.vue index d8f9367f..3be8db2d 100644 --- a/packages/ui/certd-client/src/App.vue +++ b/packages/ui/certd-client/src/App.vue @@ -74,8 +74,12 @@ const tokenTheme = computed(() => { // settingStore.init(); const chatBox = ref(); -onMounted(async () => { - // await util.sleep(5000); - // await chatBox.value.openChat({ q: "hello" }); -}); +// onMounted(async () => { +// await util.sleep(2000); +// await chatBox.value.openChat({ q: "hello" }); +// }); +const openChat = (q: string) => { + chatBox.value.openChat({ q }); +}; +provide("fn:ai.open", openChat); diff --git a/packages/ui/certd-client/src/components/ai/index.vue b/packages/ui/certd-client/src/components/ai/index.vue index dc9020de..8bd9c85d 100644 --- a/packages/ui/certd-client/src/components/ai/index.vue +++ b/packages/ui/certd-client/src/components/ai/index.vue @@ -24,7 +24,7 @@
-
+
@@ -123,7 +123,7 @@ onMounted(() => { }); async function openChat(req: { q: string }) { - showGuide.value = true; + chatVisible.value = true; const iframeId = "maxkb-chat"; @@ -227,10 +227,10 @@ defineExpose({ #maxkb .maxkb-tips .maxkb-button button::after { border: none; } -#maxkb .maxkb-tips . { +#maxkb .maxkb-tips { position: absolute; right: 20px; - top: 20px; + //top: 20px; cursor: pointer; } #maxkb-chat-container { @@ -248,7 +248,7 @@ defineExpose({ #maxkb .maxkb-chat-button { position: fixed; - right: 0px; + right: 10px; bottom: 30px; cursor: pointer; z-index: 10000; diff --git a/packages/ui/certd-client/src/views/certd/pipeline/components/change-group.vue b/packages/ui/certd-client/src/views/certd/pipeline/components/change-group.vue index d5ad12ea..01c2489e 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/components/change-group.vue +++ b/packages/ui/certd-client/src/views/certd/pipeline/components/change-group.vue @@ -21,7 +21,7 @@ async function batchUpdateGroupRequest(groupId: number) { const pipelineGroupDictRef = dict({ url: "/pi/pipeline/group/all", value: "id", - label: "name" + label: "name", }); const { openCrudFormDialog } = useFormWrapper(); @@ -33,9 +33,9 @@ async function openGroupSelectDialog() { type: "dict-select", dict: pipelineGroupDictRef, form: { - rules: [{ required: true, message: "请选择分组" }] - } - } + rules: [{ required: true, message: "请选择分组" }], + }, + }, }, form: { mode: "edit", @@ -44,18 +44,18 @@ async function openGroupSelectDialog() { await batchUpdateGroupRequest(form.groupId); }, col: { - span: 22 + span: 22, }, labelCol: { style: { - width: "100px" - } + width: "100px", + }, }, wrapper: { title: "批量修改分组", - width: 600 - } - } + width: 600, + }, + }, } as any; await openCrudFormDialog({ crudOptions }); } diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-view/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-view/index.vue index 6a58039a..ebe89e43 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-view/index.vue +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-view/index.vue @@ -22,6 +22,11 @@
+ @@ -37,11 +42,15 @@ export default { props: {}, emits: ["run"], setup(props: any, ctx: any) { + const openAiChat: any = inject("fn:ai.open", (q: string) => {}); const taskModal = ref({ open: false, onOk() { taskViewClose(); }, + onAiChat() { + onAiChat(); + }, cancelText: "关闭", }); const { isMobile } = usePreferences(); @@ -52,6 +61,22 @@ export default { return "left"; }); + function onAiChat() { + debugger; + const logs = currentHistory.value?.logs[activeKey.value]; + let logText = ""; + for (let log of logs) { + logText += log + "\n"; + } + const maxLength = 5000; + if (logText.length > maxLength) { + logText = logText.substring(logText.length - maxLength); + } + if (openAiChat) { + openAiChat(logText); + } + } + const detail = ref({ nodes: [] }); const activeKey = ref(); const currentHistory: Ref | undefined = inject("currentHistory"); diff --git a/packages/ui/certd-client/src/views/sys/console/index.vue b/packages/ui/certd-client/src/views/sys/console/index.vue index 4651b012..b3afb449 100644 --- a/packages/ui/certd-client/src/views/sys/console/index.vue +++ b/packages/ui/certd-client/src/views/sys/console/index.vue @@ -9,7 +9,10 @@ @@ -21,7 +24,10 @@ @@ -42,21 +48,23 @@