mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-12 16:00:33 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
582cf1f533 | ||
|
|
b8a4e8e9e0 | ||
|
|
4a462aecc7 | ||
|
|
74f221b895 | ||
|
|
cbcb628bff | ||
|
|
f602645e31 | ||
|
|
ec9e609f10 | ||
|
|
1c1f3c7761 | ||
|
|
fae49284b7 | ||
|
|
072a608a63 | ||
|
|
1e6fd9c3a0 | ||
|
|
23200cbe85 | ||
|
|
08604e8c02 | ||
|
|
62a472d2d1 | ||
|
|
2c84665e8b | ||
|
|
616bd6830c | ||
|
|
e55ea96f00 | ||
|
|
aa676e139c | ||
|
|
7708fea58d | ||
|
|
34bdff01ad | ||
|
|
5a49579ddf | ||
|
|
b98a2b06c9 | ||
|
|
9073b16baa | ||
|
|
2ec4db8d7e | ||
|
|
fe9628f91c | ||
|
|
18a469e4be | ||
|
|
8c691cc074 | ||
|
|
2cd31a47f8 | ||
|
|
d97aa9c2a0 | ||
|
|
f31f0a89ff | ||
|
|
0a1621166e | ||
|
|
8623aa5318 | ||
|
|
ec50599526 | ||
|
|
458a30c520 | ||
|
|
9a660db4c3 | ||
|
|
f4fc71d32b | ||
|
|
e45a8185e3 | ||
|
|
7be9189b3c | ||
|
|
bdf13c01f6 | ||
|
|
902bc90aef | ||
|
|
27ba50d8be | ||
|
|
fb483ecb90 | ||
|
|
2de7079dd2 | ||
|
|
2e4ac604a8 | ||
|
|
46af80142e | ||
|
|
677b47dcdb | ||
|
|
56cccdf7c0 | ||
|
|
3e0d0bb809 | ||
|
|
86aa6c09c6 | ||
|
|
f67e1d897c | ||
|
|
9e518daa62 | ||
|
|
2e5455e655 | ||
|
|
868f1db4c5 | ||
|
|
efd84b1e1b | ||
|
|
5591fe50f0 | ||
|
|
5e0a598799 | ||
|
|
45d7b30abc | ||
|
|
46af88e1d0 | ||
|
|
7f331f5503 | ||
|
|
7b74e09d30 | ||
|
|
01e4455110 | ||
|
|
922a2d21c8 | ||
|
|
439c4794b7 | ||
|
|
5ac7f16b67 | ||
|
|
bfb79066b4 | ||
|
|
19602b42fb | ||
|
|
f987af264f | ||
|
|
a6c76dffc3 | ||
|
|
a2eed66bc4 | ||
|
|
a49b9b132d | ||
|
|
e8505a249a | ||
|
|
dbff9c02e9 | ||
|
|
45c5c5b40f | ||
|
|
aea71664ee | ||
|
|
fc65fb9323 | ||
|
|
f476823531 | ||
|
|
8222579d99 | ||
|
|
209c126445 | ||
|
|
c846ca3e71 | ||
|
|
e777fa143e | ||
|
|
e66000ffd2 | ||
|
|
cfdf448765 | ||
|
|
c61139c5cc | ||
|
|
571a4068c4 | ||
|
|
9ce63adb4f | ||
|
|
ef51a2f9b2 | ||
|
|
fa3e137db8 | ||
|
|
af6a708049 | ||
|
|
5d6ccf5717 | ||
|
|
59c870aca4 | ||
|
|
94cb014598 | ||
|
|
ed30567a22 | ||
|
|
791350c299 | ||
|
|
6547de1758 | ||
|
|
2151daab1f | ||
|
|
b791def0df | ||
|
|
c036d5859f | ||
|
|
6495869664 | ||
|
|
a2e6e7e879 | ||
|
|
338301907a | ||
|
|
681141f971 | ||
|
|
506ff1d46e | ||
|
|
c3b7deedb4 | ||
|
|
51252a15db | ||
|
|
375824f77a | ||
|
|
8918d10253 | ||
|
|
fe343a64ed | ||
|
|
205f12e14a | ||
|
|
aca94e470a | ||
|
|
a69f7b1144 | ||
|
|
e55abfb174 | ||
|
|
4a734b5bb6 | ||
|
|
758eb9dfda | ||
|
|
04e2770763 | ||
|
|
7391b4bcd0 | ||
|
|
d9d4d55eca | ||
|
|
9a64f8de98 | ||
|
|
9f9c9a2688 | ||
|
|
f80f4d1c19 | ||
|
|
145b39c4c0 | ||
|
|
44e264fe65 | ||
|
|
15fef4d84e | ||
|
|
b8bd1490ec | ||
|
|
ccd8923fdd | ||
|
|
e49fa620c8 | ||
|
|
654691543c | ||
|
|
929cd38184 | ||
|
|
9748a0794b | ||
|
|
a3f8c30508 | ||
|
|
602c0e8a3b | ||
|
|
7b7f840c2e | ||
|
|
ba9a8592ee | ||
|
|
389fcbf10c | ||
|
|
9cca14c382 | ||
|
|
243a2fe6f3 | ||
|
|
3d5069fd40 | ||
|
|
2b78bae451 | ||
|
|
8693cc17df | ||
|
|
9a2b7a7775 | ||
|
|
024a4a9ef3 | ||
|
|
f5b47cf74d | ||
|
|
4956b96193 | ||
|
|
a332dfd3b3 | ||
|
|
68f8d8d221 | ||
|
|
4f78060d35 | ||
|
|
adb97730c1 | ||
|
|
f1a0453615 | ||
|
|
e64cc875fe | ||
|
|
b1c028b786 | ||
|
|
d0e3914a29 | ||
|
|
fe7d1108cd | ||
|
|
462a3bbc83 | ||
|
|
b9dff99d63 | ||
|
|
612e67e4cc | ||
|
|
4f0838c98c | ||
|
|
fe70a77264 | ||
|
|
8ed33fd06a | ||
|
|
f5662769b3 | ||
|
|
61a2e20798 | ||
|
|
7d20118f38 | ||
|
|
7307b9d6fb | ||
|
|
c2971812a5 | ||
|
|
176a4bd34c | ||
|
|
4a01726058 | ||
|
|
18a372e027 | ||
|
|
483a2a867b | ||
|
|
1a0de94489 | ||
|
|
979c1588e1 | ||
|
|
e39f6b1003 | ||
|
|
4443ed1b7f | ||
|
|
81f11f13fe | ||
|
|
4fa14f055d | ||
|
|
0fe9033d23 | ||
|
|
e5ff53b1af | ||
|
|
d005e61c72 | ||
|
|
9328a79b37 | ||
|
|
56c7d5e3bd | ||
|
|
0cefe8f6ad | ||
|
|
3759fd2dad | ||
|
|
b35c771d16 | ||
|
|
3ce70cee4d | ||
|
|
8dd27a3e7b | ||
|
|
c91769e364 | ||
|
|
ea7856d591 | ||
|
|
023040a814 | ||
|
|
13fdb6caa9 | ||
|
|
81e1a41130 | ||
|
|
f3933155d6 | ||
|
|
4e692b8667 | ||
|
|
cf5f1cdb4e | ||
|
|
46986fba24 | ||
|
|
4627e7c0af | ||
|
|
38174a0b9f | ||
|
|
c0489245d9 | ||
|
|
bec23f793a | ||
|
|
e7578a9fc5 | ||
|
|
9d3313fe11 | ||
|
|
f41e5e3da5 | ||
|
|
540be68adf | ||
|
|
956bf0992a | ||
|
|
d0faee4eeb | ||
|
|
4517ae2f81 | ||
|
|
c77260193f | ||
|
|
d57c998047 | ||
|
|
3a5d5bc6d7 | ||
|
|
00afa748e0 | ||
|
|
8c9c7b9b1f | ||
|
|
1e2dc42554 | ||
|
|
cd621b9dba | ||
|
|
d10afe7a5a | ||
|
|
03fc9302d6 | ||
|
|
d522d837ff | ||
|
|
aa10760658 | ||
|
|
db6e8841ec | ||
|
|
75258bb465 | ||
|
|
7c7a59cf21 | ||
|
|
6f38c4eb71 | ||
|
|
7abedeae22 | ||
|
|
afacefaccb | ||
|
|
e5b2b5d5dc | ||
|
|
9508238805 | ||
|
|
2c21bd7286 | ||
|
|
811ddcc26c | ||
|
|
6cd0373678 | ||
|
|
11771b2b53 | ||
|
|
ff5c6e878e | ||
|
|
1896aed973 | ||
|
|
6dc5deb6d3 | ||
|
|
d7242d526c | ||
|
|
22c3fc8c40 | ||
|
|
8d44105f88 | ||
|
|
8fb8d97dcf | ||
|
|
6941014153 | ||
|
|
bfd610d255 | ||
|
|
5c61217c78 | ||
|
|
eb527857f3 | ||
|
|
d55982bde0 | ||
|
|
84a27de792 | ||
|
|
111bec547c | ||
|
|
db4f699b85 | ||
|
|
12f2d95265 | ||
|
|
a917ca00a1 | ||
|
|
69906046e8 | ||
|
|
d9cedeca09 | ||
|
|
0e28f27819 | ||
|
|
33fc7f14df | ||
|
|
7159aca226 | ||
|
|
2c5728e27e | ||
|
|
defb40702c | ||
|
|
b476df3b61 | ||
|
|
4bbd9b6f06 | ||
|
|
325b9c4d88 | ||
|
|
76d965b3b7 | ||
|
|
4b1b27abd8 | ||
|
|
f26b4f290d | ||
|
|
5d2221203a | ||
|
|
0aff0d529e | ||
|
|
20f09b3a0c | ||
|
|
c392b72470 | ||
|
|
50a70ef1e5 | ||
|
|
75266ef659 | ||
|
|
b3c593b852 | ||
|
|
73530bb8b9 | ||
|
|
1b6e70964b | ||
|
|
61d42b05e9 | ||
|
|
79703e3c71 | ||
|
|
aee4e4e282 | ||
|
|
744c7559f3 | ||
|
|
d94c3ec9cc | ||
|
|
f2625eab16 | ||
|
|
c6d498a9be | ||
|
|
b06bc0a455 | ||
|
|
68411bddd9 | ||
|
|
a2ac2e0b22 | ||
|
|
a5a5d9f1ac | ||
|
|
82876eac8d | ||
|
|
19c9c0f4e5 | ||
|
|
70f044dc50 | ||
|
|
1c8418026c | ||
|
|
17f8471c14 | ||
|
|
4deb2ea484 | ||
|
|
d05317dbf5 | ||
|
|
1db6ccd72b | ||
|
|
bc00760404 | ||
|
|
e57dc1240b | ||
|
|
4c396d68ca | ||
|
|
ab0fe4286a | ||
|
|
5ab758d626 | ||
|
|
3a0a6537f0 | ||
|
|
4eda92f25c | ||
|
|
94f8c5d2f1 | ||
|
|
92598effc1 | ||
|
|
748cecaee8 | ||
|
|
b783034d0a | ||
|
|
94a4e39793 | ||
|
|
1864dee591 | ||
|
|
8c4ea85d02 | ||
|
|
a714d5a84e | ||
|
|
4f97a7890f | ||
|
|
f15f91e5fa | ||
|
|
02bd500917 | ||
|
|
810c6fd232 | ||
|
|
d50a8983bc | ||
|
|
89647304bb | ||
|
|
9ebad13bf8 | ||
|
|
cc0ac8cc43 | ||
|
|
9f5a1d842a | ||
|
|
beb33957be | ||
|
|
ffcbbb705b | ||
|
|
058bc71630 | ||
|
|
0a38979bd9 | ||
|
|
5fa4a15914 | ||
|
|
81c551a11b | ||
|
|
5681449353 | ||
|
|
88849e6355 | ||
|
|
d6bbcdc32f | ||
|
|
abd1581c1d | ||
|
|
cd30ec7632 | ||
|
|
646826769a | ||
|
|
074c0abb3b | ||
|
|
3a00e893bc | ||
|
|
2f37677650 | ||
|
|
12ae89923b | ||
|
|
031a203a74 | ||
|
|
646b99974a | ||
|
|
254ed954e9 | ||
|
|
a0050dca09 | ||
|
|
cd80b4e6c3 | ||
|
|
df5d3d782c | ||
|
|
3c5b3c3467 | ||
|
|
2bdba42d87 | ||
|
|
0b146f7ff3 | ||
|
|
c28a4f6d69 | ||
|
|
f93a34acc4 | ||
|
|
4e78841bf6 | ||
|
|
7f5bb58c72 | ||
|
|
6a28359ac2 | ||
|
|
59655695b5 | ||
|
|
786c5c1d7c | ||
|
|
08388124b3 | ||
|
|
34ccc68b3a | ||
|
|
97ef386f17 | ||
|
|
394339d7dc | ||
|
|
8362578355 | ||
|
|
6dc218cf80 | ||
|
|
89888cef73 | ||
|
|
4d06f444ac |
@@ -0,0 +1,14 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directories:
|
||||||
|
- "/agent"
|
||||||
|
- "/core"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/frontend"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
@@ -6,7 +6,7 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
generic_handler:
|
generic_handler:
|
||||||
name: Add Labels to PR
|
name: Add Labels to PR
|
||||||
if: github.repository == '1Panel-dev/1Panel'
|
if: github.repository == '1Panel-dev/1Panel' && github.event.pull_request.user.login != 'dependabot[bot]'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
name: Build And Publish (OSS + R2)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
create-release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '24.11.1'
|
|
||||||
|
|
||||||
- name: Build Web
|
|
||||||
run: |
|
|
||||||
cd frontend && npm install && npm run build:pro
|
|
||||||
env:
|
|
||||||
NODE_OPTIONS: --max-old-space-size=8192
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '1.25'
|
|
||||||
cache-dependency-path: |
|
|
||||||
core/go.sum
|
|
||||||
agent/go.sum
|
|
||||||
|
|
||||||
- name: Build Release
|
|
||||||
uses: goreleaser/goreleaser-action@v6
|
|
||||||
with:
|
|
||||||
distribution: goreleaser
|
|
||||||
version: '~> v2'
|
|
||||||
args: release --skip=publish --clean
|
|
||||||
|
|
||||||
- name: Upload Assets
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
with:
|
|
||||||
draft: true
|
|
||||||
files: |
|
|
||||||
dist/*.tar.gz
|
|
||||||
dist/checksums.txt
|
|
||||||
|
|
||||||
- name: Setup OSSUTIL
|
|
||||||
uses: yizhoumo/setup-ossutil@v2
|
|
||||||
with:
|
|
||||||
endpoint: ${{ secrets.OSS_ENDPOINT }}
|
|
||||||
access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}
|
|
||||||
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
|
||||||
ossutil-version: '1.7.18'
|
|
||||||
|
|
||||||
- name: Upload Assets to OSS
|
|
||||||
run: |
|
|
||||||
ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/v2/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force
|
|
||||||
|
|
||||||
- name: Setup Rclone
|
|
||||||
uses: AnimMouse/setup-rclone@v1
|
|
||||||
with:
|
|
||||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
|
||||||
|
|
||||||
- name: Upload to Cloudflare R2
|
|
||||||
run: |
|
|
||||||
rclone copy dist/ cloudflare_r2:package/v2/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --progress
|
|
||||||
+10
-7
@@ -9,7 +9,6 @@ build/1panel-core
|
|||||||
|
|
||||||
# Mac
|
# Mac
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*/.DS_Store
|
|
||||||
|
|
||||||
# VS Code
|
# VS Code
|
||||||
.vscode
|
.vscode
|
||||||
@@ -41,20 +40,23 @@ core/cmd/server/web/index.html
|
|||||||
frontend/auto-imports.d.ts
|
frontend/auto-imports.d.ts
|
||||||
frontend/components.d.ts
|
frontend/components.d.ts
|
||||||
frontend/src/xpack
|
frontend/src/xpack
|
||||||
|
frontend/src/enterprise
|
||||||
agent/xpack
|
agent/xpack
|
||||||
|
agent/enterprise
|
||||||
agent/router/entry_xpack.go
|
agent/router/entry_xpack.go
|
||||||
|
agent/router/entry_enterprise.go
|
||||||
agent/server/init_xpack.go
|
agent/server/init_xpack.go
|
||||||
|
agent/server/init_enterprise.go
|
||||||
agent/utils/xpack/xpack.go
|
agent/utils/xpack/xpack.go
|
||||||
agent/utils/xpack/xpack_xpack.go
|
agent/utils/xpack/enterprise.go
|
||||||
core/xpack
|
core/xpack
|
||||||
|
core/enterprise
|
||||||
core/router/entry_xpack.go
|
core/router/entry_xpack.go
|
||||||
|
core/router/entry_enterprise.go
|
||||||
core/server/init_xpack.go
|
core/server/init_xpack.go
|
||||||
|
core/server/init_enterprise.go
|
||||||
core/utils/xpack/xpack.go
|
core/utils/xpack/xpack.go
|
||||||
core/utils/xpack/xpack_xpack.go
|
core/utils/xpack/enterprise.go
|
||||||
core/xpack-ee
|
|
||||||
core/router/entry_xpackee.go
|
|
||||||
core/server/init_xpackee.go
|
|
||||||
core/utils/xpack/xpack_xpackee.go
|
|
||||||
|
|
||||||
.history/
|
.history/
|
||||||
dist/
|
dist/
|
||||||
@@ -76,3 +78,4 @@ AGENTS.md
|
|||||||
opencode.json
|
opencode.json
|
||||||
superpowers
|
superpowers
|
||||||
.worktrees/
|
.worktrees/
|
||||||
|
.codex/
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
|
||||||
# vim: set ts=2 sw=2 tw=0 fo=jcroql
|
|
||||||
version: 2
|
|
||||||
|
|
||||||
before:
|
|
||||||
hooks:
|
|
||||||
# - export NODE_OPTIONS="--max-old-space-size=8192"
|
|
||||||
# - make build_web
|
|
||||||
- chmod +x ./ci/script.sh
|
|
||||||
- ./ci/script.sh
|
|
||||||
- sed -i 's@ORIGINAL_VERSION=.*@ORIGINAL_VERSION=v{{ .Version }}@g' 1pctl
|
|
||||||
|
|
||||||
builds:
|
|
||||||
- id: agent
|
|
||||||
dir: agent
|
|
||||||
main: cmd/server/main.go
|
|
||||||
binary: 1panel-agent
|
|
||||||
flags:
|
|
||||||
- -tags=xpack
|
|
||||||
- -trimpath
|
|
||||||
ldflags:
|
|
||||||
- -w -s
|
|
||||||
env:
|
|
||||||
- CGO_ENABLED=0
|
|
||||||
goos:
|
|
||||||
- linux
|
|
||||||
goarm:
|
|
||||||
- 7
|
|
||||||
goarch:
|
|
||||||
- amd64
|
|
||||||
- arm64
|
|
||||||
- arm
|
|
||||||
- ppc64le
|
|
||||||
- s390x
|
|
||||||
- riscv64
|
|
||||||
|
|
||||||
- id: core
|
|
||||||
dir: core
|
|
||||||
main: cmd/server/main.go
|
|
||||||
binary: 1panel-core
|
|
||||||
flags:
|
|
||||||
- -tags=xpack
|
|
||||||
- -trimpath
|
|
||||||
ldflags:
|
|
||||||
- -w -s
|
|
||||||
env:
|
|
||||||
- CGO_ENABLED=0
|
|
||||||
goos:
|
|
||||||
- linux
|
|
||||||
goarm:
|
|
||||||
- 7
|
|
||||||
goarch:
|
|
||||||
- amd64
|
|
||||||
- arm64
|
|
||||||
- arm
|
|
||||||
- ppc64le
|
|
||||||
- s390x
|
|
||||||
- riscv64
|
|
||||||
|
|
||||||
archives:
|
|
||||||
- formats: [ 'tar.gz' ]
|
|
||||||
ids: [core, agent]
|
|
||||||
name_template: "1panel-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{- if .Arm }}v{{ .Arm }}{{ end }}"
|
|
||||||
wrap_in_directory: true
|
|
||||||
files:
|
|
||||||
- 1pctl
|
|
||||||
- install.sh
|
|
||||||
- 1panel-core.service
|
|
||||||
- 1panel-agent.service
|
|
||||||
- initscript/*
|
|
||||||
- lang/*
|
|
||||||
- GeoIP.mmdb
|
|
||||||
|
|
||||||
checksum:
|
|
||||||
name_template: 'checksums.txt'
|
|
||||||
|
|
||||||
changelog:
|
|
||||||
sort: asc
|
|
||||||
filters:
|
|
||||||
exclude:
|
|
||||||
- "^docs:"
|
|
||||||
- "^test:"
|
|
||||||
+4
-2
@@ -10,10 +10,12 @@ PR are always welcome, even if they only contain small fixes like typos or a few
|
|||||||
|
|
||||||
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot of features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
|
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot of features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
|
||||||
|
|
||||||
This [development guideline](https://docs.1panel.pro/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.
|
|
||||||
|
|
||||||
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
|
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
|
||||||
|
|
||||||
|
## Add a new translation
|
||||||
|
|
||||||
|
If you'd like to help translate 1Panel into a new language, please read the [Translation Contribution Guide](docs/TRANSLATION.md). It lists every file you need to create or modify, along with a reference PR you can use as a concrete example.
|
||||||
|
|
||||||
## Report issues
|
## Report issues
|
||||||
It is a great way to contribute by reporting an issue. Well-written and complete bug reports are always welcome! Please open an issue and follow the template to fill in required information.
|
It is a great way to contribute by reporting an issue. Well-written and complete bug reports are always welcome! Please open an issue and follow the template to fill in required information.
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ reviewers:
|
|||||||
- wanghe-fit2cloud
|
- wanghe-fit2cloud
|
||||||
- zhengkunwang223
|
- zhengkunwang223
|
||||||
- ssongliu
|
- ssongliu
|
||||||
|
- lan-yonghui
|
||||||
|
|
||||||
approvers:
|
approvers:
|
||||||
- wanghe-fit2cloud
|
- wanghe-fit2cloud
|
||||||
- zhengkunwang223
|
- zhengkunwang223
|
||||||
- ssongliu
|
- ssongliu
|
||||||
|
- lan-yonghui
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<a href="/docs/README.pt-br.md"><img alt="Português (Brasil)" src="https://img.shields.io/badge/Português (Brasil)-d9d9d9"></a>
|
<a href="/docs/README.pt-br.md"><img alt="Português (Brasil)" src="https://img.shields.io/badge/Português (Brasil)-d9d9d9"></a>
|
||||||
<a href="/docs/README.ar.md"><img alt="العربية" src="https://img.shields.io/badge/العربية-d9d9d9"></a>
|
<a href="/docs/README.ar.md"><img alt="العربية" src="https://img.shields.io/badge/العربية-d9d9d9"></a>
|
||||||
<a href="/docs/README.de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-d9d9d9"></a>
|
<a href="/docs/README.de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-d9d9d9"></a>
|
||||||
<a href="/docs/README.es.md"><img alt="Español" src="https://img.shields.io/badge/Español-d9d9d9"></a>
|
<a href="/docs/README.es-es.md"><img alt="Español" src="https://img.shields.io/badge/Español-d9d9d9"></a>
|
||||||
<a href="/docs/README.fr.md"><img alt="français" src="https://img.shields.io/badge/français-d9d9d9"></a>
|
<a href="/docs/README.fr.md"><img alt="français" src="https://img.shields.io/badge/français-d9d9d9"></a>
|
||||||
<a href="/docs/README.ko.md"><img alt="한국어" src="https://img.shields.io/badge/한국어-d9d9d9"></a>
|
<a href="/docs/README.ko.md"><img alt="한국어" src="https://img.shields.io/badge/한국어-d9d9d9"></a>
|
||||||
<a href="/docs/README.id.md"><img alt="Bahasa Indonesia" src="https://img.shields.io/badge/Bahasa Indonesia-d9d9d9"></a>
|
<a href="/docs/README.id.md"><img alt="Bahasa Indonesia" src="https://img.shields.io/badge/Bahasa Indonesia-d9d9d9"></a>
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
<a href="/docs/README.tr.md"><img alt="Türkçe" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a>
|
<a href="/docs/README.tr.md"><img alt="Türkçe" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a>
|
||||||
<a href="/docs/README.ru.md"><img alt="Русский" src="https://img.shields.io/badge/Русский-d9d9d9"></a>
|
<a href="/docs/README.ru.md"><img alt="Русский" src="https://img.shields.io/badge/Русский-d9d9d9"></a>
|
||||||
<a href="/docs/README.ms.md"><img alt="Bahasa Melayu" src="https://img.shields.io/badge/Bahasa Melayu-d9d9d9"></a>
|
<a href="/docs/README.ms.md"><img alt="Bahasa Melayu" src="https://img.shields.io/badge/Bahasa Melayu-d9d9d9"></a>
|
||||||
|
<a href="/docs/README.fa.md"><img alt="Persian" src="https://img.shields.io/badge/%D9%81%D8%A7%D8%B1%D8%B3%DB%8C-d9d9d9"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+267
-20
@@ -27,6 +27,90 @@ func (b *BaseApi) CreateAgent(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, res)
|
helper.SuccessWithData(c, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Batch install Agent
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentBatchInstallReq true "request"
|
||||||
|
// @Success 200 {object} dto.AgentItem
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/batch/install [post]
|
||||||
|
func (b *BaseApi) BatchInstallAgent(c *gin.Context) {
|
||||||
|
var req dto.AgentBatchInstallReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := agentService.BatchInstall(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Batch upgrade Agent
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentBatchUpgradeReq true "request"
|
||||||
|
// @Success 200 {array} dto.AgentBatchUpgradeResult
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/batch/upgrade [post]
|
||||||
|
func (b *BaseApi) BatchUpgradeAgent(c *gin.Context) {
|
||||||
|
var req dto.AgentBatchUpgradeReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := agentService.BatchUpgrade(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Batch install Agent Skill
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentBatchSkillInstallReq true "request"
|
||||||
|
// @Success 200 {array} dto.AgentBatchSkillInstallResult
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/batch/skill/install [post]
|
||||||
|
func (b *BaseApi) BatchInstallAgentSkill(c *gin.Context) {
|
||||||
|
var req dto.AgentBatchSkillInstallReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := agentService.BatchInstallSkill(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Batch operate Agent
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentBatchOperateReq true "request"
|
||||||
|
// @Success 200 {array} dto.AgentBatchOperateResult
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/batch/operate [post]
|
||||||
|
func (b *BaseApi) BatchOperateAgent(c *gin.Context) {
|
||||||
|
var req dto.AgentBatchOperateReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := agentService.BatchOperate(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Page Agents
|
// @Summary Page Agents
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -152,6 +236,26 @@ func (b *BaseApi) BindAgentWebsite(c *gin.Context) {
|
|||||||
helper.Success(c)
|
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
|
// @Tags AI
|
||||||
// @Summary Get Agent model config
|
// @Summary Get Agent model config
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -215,11 +319,72 @@ func (b *BaseApi) GetAgentOverview(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Get Providers
|
// @Summary Get Hermes chat sessions
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentIDReq true "request"
|
||||||
|
// @Success 200 {array} dto.AgentHermesChatSessionItem
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/hermes/chat/sessions [post]
|
||||||
|
func (b *BaseApi) GetHermesChatSessions(c *gin.Context) {
|
||||||
|
var req dto.AgentIDReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := agentService.GetHermesChatSessions(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Rename Hermes chat session
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentHermesChatSessionRenameReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/hermes/chat/sessions/rename [post]
|
||||||
|
func (b *BaseApi) RenameHermesChatSession(c *gin.Context) {
|
||||||
|
var req dto.AgentHermesChatSessionRenameReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := agentService.RenameHermesChatSession(req); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Delete Hermes chat session
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentHermesChatSessionDeleteReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/hermes/chat/sessions/delete [post]
|
||||||
|
func (b *BaseApi) DeleteHermesChatSession(c *gin.Context) {
|
||||||
|
var req dto.AgentHermesChatSessionDeleteReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := agentService.DeleteHermesChatSession(req); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Get model account providers
|
||||||
// @Success 200 {array} dto.ProviderInfo
|
// @Success 200 {array} dto.ProviderInfo
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/providers [get]
|
// @Router /ai/accounts/providers [get]
|
||||||
func (b *BaseApi) GetAgentProviders(c *gin.Context) {
|
func (b *BaseApi) GetAgentProviders(c *gin.Context) {
|
||||||
list, err := agentService.GetProviders()
|
list, err := agentService.GetProviders()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -230,13 +395,13 @@ func (b *BaseApi) GetAgentProviders(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Create Agent account
|
// @Summary Create model account
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.AgentAccountCreateReq true "request"
|
// @Param request body dto.AgentAccountCreateReq true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/accounts [post]
|
// @Router /ai/accounts [post]
|
||||||
func (b *BaseApi) CreateAgentAccount(c *gin.Context) {
|
func (b *BaseApi) CreateAgentAccount(c *gin.Context) {
|
||||||
var req dto.AgentAccountCreateReq
|
var req dto.AgentAccountCreateReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -250,13 +415,13 @@ func (b *BaseApi) CreateAgentAccount(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Update Agent account
|
// @Summary Update model account
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.AgentAccountUpdateReq true "request"
|
// @Param request body dto.AgentAccountUpdateReq true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/accounts/update [post]
|
// @Router /ai/accounts/update [post]
|
||||||
func (b *BaseApi) UpdateAgentAccount(c *gin.Context) {
|
func (b *BaseApi) UpdateAgentAccount(c *gin.Context) {
|
||||||
var req dto.AgentAccountUpdateReq
|
var req dto.AgentAccountUpdateReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -270,13 +435,13 @@ func (b *BaseApi) UpdateAgentAccount(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Page Agent accounts
|
// @Summary Page model accounts
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.AgentAccountSearch true "request"
|
// @Param request body dto.AgentAccountSearch true "request"
|
||||||
// @Success 200 {object} dto.PageResult
|
// @Success 200 {object} dto.PageResult
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/accounts/search [post]
|
// @Router /ai/accounts/search [post]
|
||||||
func (b *BaseApi) PageAgentAccounts(c *gin.Context) {
|
func (b *BaseApi) PageAgentAccounts(c *gin.Context) {
|
||||||
var req dto.AgentAccountSearch
|
var req dto.AgentAccountSearch
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -294,13 +459,34 @@ func (b *BaseApi) PageAgentAccounts(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary List Agent account models
|
// @Summary Count model accounts by provider
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentAccountProviderCountReq true "request"
|
||||||
|
// @Success 200 {object} map[string]int64
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/accounts/counts [post]
|
||||||
|
func (b *BaseApi) CountAgentAccountsByProviders(c *gin.Context) {
|
||||||
|
var req dto.AgentAccountProviderCountReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
counts, err := agentService.CountAccountsByProviders(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, counts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary List model account models
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.AgentAccountModelReq true "request"
|
// @Param request body dto.AgentAccountModelReq true "request"
|
||||||
// @Success 200 {array} dto.AgentAccountModel
|
// @Success 200 {array} dto.AgentAccountModel
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/accounts/models [post]
|
// @Router /ai/accounts/models [post]
|
||||||
func (b *BaseApi) GetAgentAccountModels(c *gin.Context) {
|
func (b *BaseApi) GetAgentAccountModels(c *gin.Context) {
|
||||||
var req dto.AgentAccountModelReq
|
var req dto.AgentAccountModelReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -315,13 +501,13 @@ func (b *BaseApi) GetAgentAccountModels(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Create Agent account model
|
// @Summary Create model account model
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.AgentAccountModelCreateReq true "request"
|
// @Param request body dto.AgentAccountModelCreateReq true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/accounts/models/create [post]
|
// @Router /ai/accounts/models/create [post]
|
||||||
func (b *BaseApi) CreateAgentAccountModel(c *gin.Context) {
|
func (b *BaseApi) CreateAgentAccountModel(c *gin.Context) {
|
||||||
var req dto.AgentAccountModelCreateReq
|
var req dto.AgentAccountModelCreateReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -335,13 +521,13 @@ func (b *BaseApi) CreateAgentAccountModel(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Update Agent account model
|
// @Summary Update model account model
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.AgentAccountModelUpdateReq true "request"
|
// @Param request body dto.AgentAccountModelUpdateReq true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/accounts/models/update [post]
|
// @Router /ai/accounts/models/update [post]
|
||||||
func (b *BaseApi) UpdateAgentAccountModel(c *gin.Context) {
|
func (b *BaseApi) UpdateAgentAccountModel(c *gin.Context) {
|
||||||
var req dto.AgentAccountModelUpdateReq
|
var req dto.AgentAccountModelUpdateReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -355,13 +541,13 @@ func (b *BaseApi) UpdateAgentAccountModel(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Delete Agent account model
|
// @Summary Delete model account model
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.AgentAccountModelDeleteReq true "request"
|
// @Param request body dto.AgentAccountModelDeleteReq true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/accounts/models/delete [post]
|
// @Router /ai/accounts/models/delete [post]
|
||||||
func (b *BaseApi) DeleteAgentAccountModel(c *gin.Context) {
|
func (b *BaseApi) DeleteAgentAccountModel(c *gin.Context) {
|
||||||
var req dto.AgentAccountModelDeleteReq
|
var req dto.AgentAccountModelDeleteReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -375,13 +561,13 @@ func (b *BaseApi) DeleteAgentAccountModel(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Verify Agent account
|
// @Summary Verify model account
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.AgentAccountVerifyReq true "request"
|
// @Param request body dto.AgentAccountVerifyReq true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/accounts/verify [post]
|
// @Router /ai/accounts/verify [post]
|
||||||
func (b *BaseApi) VerifyAgentAccount(c *gin.Context) {
|
func (b *BaseApi) VerifyAgentAccount(c *gin.Context) {
|
||||||
var req dto.AgentAccountVerifyReq
|
var req dto.AgentAccountVerifyReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -395,13 +581,13 @@ func (b *BaseApi) VerifyAgentAccount(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Delete Agent account
|
// @Summary Delete model account
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.AgentAccountDeleteReq true "request"
|
// @Param request body dto.AgentAccountDeleteReq true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /ai/agents/accounts/delete [post]
|
// @Router /ai/accounts/delete [post]
|
||||||
func (b *BaseApi) DeleteAgentAccount(c *gin.Context) {
|
func (b *BaseApi) DeleteAgentAccount(c *gin.Context) {
|
||||||
var req dto.AgentAccountDeleteReq
|
var req dto.AgentAccountDeleteReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -783,6 +969,27 @@ func (b *BaseApi) UpdateAgentDingTalkConfig(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Get Agent Weixin channel config
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentIDReq true "request"
|
||||||
|
// @Success 200 {object} dto.AgentWeixinConfig
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/channel/weixin/get [post]
|
||||||
|
func (b *BaseApi) GetAgentWeixinConfig(c *gin.Context) {
|
||||||
|
var req dto.AgentIDReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := agentService.GetWeixinConfig(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Get Agent QQ Bot channel config
|
// @Summary Get Agent QQ Bot channel config
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -824,6 +1031,26 @@ func (b *BaseApi) UpdateAgentQQBotConfig(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Delete Agent channel config
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentChannelDeleteReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/channel/delete [post]
|
||||||
|
func (b *BaseApi) DeleteAgentChannelConfig(c *gin.Context) {
|
||||||
|
var req dto.AgentChannelDeleteReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := agentService.DeleteChannelConfig(req); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Install Agent plugin
|
// @Summary Install Agent plugin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -1110,6 +1337,26 @@ func (b *BaseApi) InstallAgentSkill(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Uninstall Agent skill
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentSkillUninstallReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/agents/skills/uninstall [post]
|
||||||
|
func (b *BaseApi) UninstallAgentSkill(c *gin.Context) {
|
||||||
|
var req dto.AgentSkillUninstallReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := agentService.UninstallSkill(req); err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Login Agent Weixin channel
|
// @Summary Login Agent Weixin channel
|
||||||
// @Accept json
|
// @Accept json
|
||||||
|
|||||||
+9
-42
@@ -3,9 +3,6 @@ package v2
|
|||||||
import (
|
import (
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -163,37 +160,6 @@ func (b *BaseApi) DeleteOllamaModel(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
|
||||||
// @Summary Load gpu / xpu info
|
|
||||||
// @Accept json
|
|
||||||
// @Success 200
|
|
||||||
// @Security ApiKeyAuth
|
|
||||||
// @Security Timestamp
|
|
||||||
// @Router /ai/gpu/load [get]
|
|
||||||
func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
|
|
||||||
ok, client := gpu.New()
|
|
||||||
if ok {
|
|
||||||
info, err := client.LoadGpuInfo()
|
|
||||||
if err != nil {
|
|
||||||
helper.BadRequest(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
helper.SuccessWithData(c, info)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
xpuOK, xpuClient := xpu.New()
|
|
||||||
if xpuOK {
|
|
||||||
info, err := xpuClient.LoadGpuInfo()
|
|
||||||
if err != nil {
|
|
||||||
helper.BadRequest(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
helper.SuccessWithData(c, info)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
helper.SuccessWithData(c, &common.GpuInfo{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Bind domain
|
// @Summary Bind domain
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -235,14 +201,15 @@ func (b *BaseApi) GetBindDomain(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, res)
|
helper.SuccessWithData(c, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tags AI
|
// @Tags AI
|
||||||
// Summary Update bind domain
|
// @Summary Update bind domain
|
||||||
// Accept json
|
// @Accept json
|
||||||
// Param request body dto.OllamaBindDomain true "request"
|
// @Param request body dto.OllamaBindDomain true "request"
|
||||||
// Success 200
|
// @Success 200
|
||||||
// Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// Security Timestamp
|
// @Security Timestamp
|
||||||
// Router /ai/domain/update [post]
|
// @Router /ai/domain/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["domain"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 Ollama 绑定域名 [domain]","formatEN":"update Ollama bind domain [domain]"}
|
||||||
func (b *BaseApi) UpdateBindDomain(c *gin.Context) {
|
func (b *BaseApi) UpdateBindDomain(c *gin.Context) {
|
||||||
var req dto.OllamaBindDomain
|
var req dto.OllamaBindDomain
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
+159
-4
@@ -2,11 +2,24 @@ package v2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultAuditUser = "system"
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Page alert
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AlertSearch true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/search [post]
|
||||||
func (b *BaseApi) PageAlert(c *gin.Context) {
|
func (b *BaseApi) PageAlert(c *gin.Context) {
|
||||||
var req dto.AlertSearch
|
var req dto.AlertSearch
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -32,12 +45,21 @@ func (b *BaseApi) GetAlerts(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, alerts)
|
helper.SuccessWithData(c, alerts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Create alert
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AlertCreate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["title"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建告警任务 [title]","formatEN":"create alert [title]"}
|
||||||
func (b *BaseApi) CreateAlert(c *gin.Context) {
|
func (b *BaseApi) CreateAlert(c *gin.Context) {
|
||||||
var req dto.AlertCreate
|
var req dto.AlertCreate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := alertService.CreateAlert(req)
|
err := alertService.CreateAlert(req, loadAuditUser(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
@@ -45,6 +67,15 @@ func (b *BaseApi) CreateAlert(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Delete alert
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.DeleteRequest true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/del [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除告警任务 [id]","formatEN":"delete alert [id]"}
|
||||||
func (b *BaseApi) DeleteAlert(c *gin.Context) {
|
func (b *BaseApi) DeleteAlert(c *gin.Context) {
|
||||||
var req dto.DeleteRequest
|
var req dto.DeleteRequest
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -58,12 +89,21 @@ func (b *BaseApi) DeleteAlert(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Update alert
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AlertUpdate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["title"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新告警任务 [title]","formatEN":"update alert [title]"}
|
||||||
func (b *BaseApi) UpdateAlert(c *gin.Context) {
|
func (b *BaseApi) UpdateAlert(c *gin.Context) {
|
||||||
var req dto.AlertUpdate
|
var req dto.AlertUpdate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := alertService.UpdateAlert(req); err != nil {
|
if err := alertService.UpdateAlert(req, loadAuditUser(c)); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -84,6 +124,15 @@ func (b *BaseApi) GetAlert(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, alert)
|
helper.SuccessWithData(c, alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Update alert status
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AlertUpdateStatus true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/status [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新告警任务 [id] 状态 [status]","formatEN":"update alert [id] status [status]"}
|
||||||
func (b *BaseApi) UpdateAlertStatus(c *gin.Context) {
|
func (b *BaseApi) UpdateAlertStatus(c *gin.Context) {
|
||||||
var req dto.AlertUpdateStatus
|
var req dto.AlertUpdateStatus
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -97,6 +146,12 @@ func (b *BaseApi) UpdateAlertStatus(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Get disks
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/disks/list [get]
|
||||||
func (b *BaseApi) GetDisks(c *gin.Context) {
|
func (b *BaseApi) GetDisks(c *gin.Context) {
|
||||||
alerts, err := alertService.GetDisks()
|
alerts, err := alertService.GetDisks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -106,6 +161,14 @@ func (b *BaseApi) GetDisks(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, alerts)
|
helper.SuccessWithData(c, alerts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Page alert logs
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AlertLogSearch true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/logs/search [post]
|
||||||
func (b *BaseApi) PageAlertLogs(c *gin.Context) {
|
func (b *BaseApi) PageAlertLogs(c *gin.Context) {
|
||||||
var req dto.AlertLogSearch
|
var req dto.AlertLogSearch
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -122,6 +185,13 @@ func (b *BaseApi) PageAlertLogs(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Clean alert logs
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/logs/clean [post]
|
||||||
|
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空告警日志","formatEN":"clean alert logs"}
|
||||||
func (b *BaseApi) CleanAlertLogs(c *gin.Context) {
|
func (b *BaseApi) CleanAlertLogs(c *gin.Context) {
|
||||||
if err := alertService.CleanAlertLogs(); err != nil {
|
if err := alertService.CleanAlertLogs(); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
@@ -130,6 +200,12 @@ func (b *BaseApi) CleanAlertLogs(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Get clams
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/clams/list [get]
|
||||||
func (b *BaseApi) GetClams(c *gin.Context) {
|
func (b *BaseApi) GetClams(c *gin.Context) {
|
||||||
clams, err := alertService.GetClams()
|
clams, err := alertService.GetClams()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -139,6 +215,14 @@ func (b *BaseApi) GetClams(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, clams)
|
helper.SuccessWithData(c, clams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Get cron jobs
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.CronJobReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/cronjob/list [post]
|
||||||
func (b *BaseApi) GetCronJobs(c *gin.Context) {
|
func (b *BaseApi) GetCronJobs(c *gin.Context) {
|
||||||
var req dto.CronJobReq
|
var req dto.CronJobReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -152,8 +236,18 @@ func (b *BaseApi) GetCronJobs(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, cronJobs)
|
helper.SuccessWithData(c, cronJobs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Get alert config
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/config/info [post]
|
||||||
func (b *BaseApi) GetAlertConfig(c *gin.Context) {
|
func (b *BaseApi) GetAlertConfig(c *gin.Context) {
|
||||||
config, err := alertService.GetAlertConfig()
|
var req dto.AlertConfigQuery
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config, err := alertService.GetAlertConfig(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
@@ -161,18 +255,71 @@ func (b *BaseApi) GetAlertConfig(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, config)
|
helper.SuccessWithData(c, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Page alert config
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AlertConfigPageReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/config/search [post]
|
||||||
|
func (b *BaseApi) PageAlertConfig(c *gin.Context) {
|
||||||
|
var req dto.AlertConfigPageReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total, configs, err := alertService.PageAlertConfig(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{
|
||||||
|
Total: total,
|
||||||
|
Items: configs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Update alert config
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AlertConfigUpdate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/config/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id","displayName"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新告警配置 [id][displayName]","formatEN":"update alert config [id][displayName]"}
|
||||||
func (b *BaseApi) UpdateAlertConfig(c *gin.Context) {
|
func (b *BaseApi) UpdateAlertConfig(c *gin.Context) {
|
||||||
var req dto.AlertConfigUpdate
|
var req dto.AlertConfigUpdate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := alertService.UpdateAlertConfig(req); err != nil {
|
if err := alertService.UpdateAlertConfig(req, loadAuditUser(c)); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadAuditUser(c *gin.Context) string {
|
||||||
|
userName := strings.TrimSpace(c.GetHeader("X-Panel-User"))
|
||||||
|
if userName == "" {
|
||||||
|
return defaultAuditUser
|
||||||
|
}
|
||||||
|
if decoded, err := url.QueryUnescape(userName); err == nil {
|
||||||
|
return decoded
|
||||||
|
}
|
||||||
|
return userName
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Delete alert config
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.DeleteRequest true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/config/del [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除告警配置 [id]","formatEN":"delete alert config [id]"}
|
||||||
func (b *BaseApi) DeleteAlertConfig(c *gin.Context) {
|
func (b *BaseApi) DeleteAlertConfig(c *gin.Context) {
|
||||||
var req dto.DeleteRequest
|
var req dto.DeleteRequest
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -186,6 +333,14 @@ func (b *BaseApi) DeleteAlertConfig(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Alert
|
||||||
|
// @Summary Test alert config
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AlertConfigTest true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /alert/config/test [post]
|
||||||
func (b *BaseApi) TestAlertConfig(c *gin.Context) {
|
func (b *BaseApi) TestAlertConfig(c *gin.Context) {
|
||||||
var req dto.AlertConfigTest
|
var req dto.AlertConfigTest
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
+10
-4
@@ -107,7 +107,7 @@ func (b *BaseApi) GetApp(c *gin.Context) {
|
|||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param appId path integer true "app id"
|
// @Param appId path integer true "app id"
|
||||||
// @Param version path string true "app 版本"
|
// @Param version path string true "app 版本"
|
||||||
// @Param version path string true "app 类型"
|
// @Param type path string true "app 类型"
|
||||||
// @Success 200 {object} response.AppDetailDTO
|
// @Success 200 {object} response.AppDetailDTO
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
@@ -131,7 +131,7 @@ func (b *BaseApi) GetAppDetail(c *gin.Context) {
|
|||||||
// @Tags App
|
// @Tags App
|
||||||
// @Summary Get app detail by id
|
// @Summary Get app detail by id
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param appId path integer true "id"
|
// @Param id path integer true "id"
|
||||||
// @Success 200 {object} response.AppDetailDTO
|
// @Success 200 {object} response.AppDetailDTO
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
@@ -172,6 +172,12 @@ func (b *BaseApi) InstallApp(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, install)
|
helper.SuccessWithData(c, install)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags App
|
||||||
|
// @Summary Get app tags
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /apps/tags [get]
|
||||||
func (b *BaseApi) GetAppTags(c *gin.Context) {
|
func (b *BaseApi) GetAppTags(c *gin.Context) {
|
||||||
tags, err := appService.GetAppTags(c)
|
tags, err := appService.GetAppTags(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -199,7 +205,7 @@ func (b *BaseApi) GetAppListUpdate(c *gin.Context) {
|
|||||||
// @Tags App
|
// @Tags App
|
||||||
// @Summary Get app icon by app_id
|
// @Summary Get app icon by app_id
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param appId path integer true "app id"
|
// @Param key path string true "app key"
|
||||||
// @Success 200 {file} file "app icon"
|
// @Success 200 {file} file "app icon"
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
@@ -237,7 +243,7 @@ func (b *BaseApi) GetAppIcon(c *gin.Context) {
|
|||||||
// @Tags App
|
// @Tags App
|
||||||
// @Summary Search app detail by appkey and version
|
// @Summary Search app detail by appkey and version
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param appId path integer true "app key"
|
// @Param appKey path string true "app key"
|
||||||
// @Param version path string true "app version"
|
// @Param version path string true "app version"
|
||||||
// @Success 200 {object} response.AppDetailSimpleDTO
|
// @Success 200 {object} response.AppDetailSimpleDTO
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
|
|||||||
@@ -203,7 +203,6 @@ func (b *BaseApi) GetServices(c *gin.Context) {
|
|||||||
// @Tags App
|
// @Tags App
|
||||||
// @Summary Search app update version by install id
|
// @Summary Search app update version by install id
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param appInstallId path integer true "request"
|
|
||||||
// @Success 200 {array} dto.AppVersion
|
// @Success 200 {array} dto.AppVersion
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
@@ -267,7 +266,7 @@ func (b *BaseApi) GetDefaultConfig(c *gin.Context) {
|
|||||||
// @Tags App
|
// @Tags App
|
||||||
// @Summary Search params by appInstallId
|
// @Summary Search params by appInstallId
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param appInstallId path string true "request"
|
// @Param appInstallId path integer true "request"
|
||||||
// @Success 200 {object} response.AppConfig
|
// @Success 200 {object} response.AppConfig
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
@@ -350,6 +349,15 @@ func (b *BaseApi) GetAppInstallInfo(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, info)
|
helper.SuccessWithData(c, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags App
|
||||||
|
// @Summary Update app install sort
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.AppInstallSort true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /apps/installed/sort/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新已安装应用排序","formatEN":"update installed app sort"}
|
||||||
func (b *BaseApi) UpdateAppInstallSort(c *gin.Context) {
|
func (b *BaseApi) UpdateAppInstallSort(c *gin.Context) {
|
||||||
var req request.AppInstallSort
|
var req request.AppInstallSort
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// @Tags Backup Account
|
||||||
|
// @Summary Check backup used
|
||||||
|
// @Param name path string true "name"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /backups/check/{name} [get]
|
||||||
func (b *BaseApi) CheckBackupUsed(c *gin.Context) {
|
func (b *BaseApi) CheckBackupUsed(c *gin.Context) {
|
||||||
name, err := helper.GetStrParamByKey(c, "name")
|
name, err := helper.GetStrParamByKey(c, "name")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -32,7 +39,7 @@ func (b *BaseApi) CheckBackupUsed(c *gin.Context) {
|
|||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /backups/check [post]
|
// @Router /backups/conn/check [post]
|
||||||
func (b *BaseApi) CheckBackup(c *gin.Context) {
|
func (b *BaseApi) CheckBackup(c *gin.Context) {
|
||||||
var req dto.BackupOperate
|
var req dto.BackupOperate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -136,7 +143,7 @@ func (b *BaseApi) DeleteBackup(c *gin.Context) {
|
|||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /backups/update [post]
|
// @Router /backups/update [post]
|
||||||
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新备份账号 [types]","formatEN":"update backup account [types]"}
|
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新备份账号 [type]","formatEN":"update backup account [type]"}
|
||||||
func (b *BaseApi) UpdateBackup(c *gin.Context) {
|
func (b *BaseApi) UpdateBackup(c *gin.Context) {
|
||||||
var req dto.BackupOperate
|
var req dto.BackupOperate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -406,15 +413,25 @@ func (b *BaseApi) Backup(c *gin.Context) {
|
|||||||
|
|
||||||
switch req.Type {
|
switch req.Type {
|
||||||
case "app":
|
case "app":
|
||||||
if _, err := backupService.AppBackup(req); err != nil {
|
record, err := backupService.AppBackup(req)
|
||||||
|
if err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if req.IsImmediate {
|
||||||
|
helper.SuccessWithData(c, record)
|
||||||
|
return
|
||||||
|
}
|
||||||
case "mysql", "mariadb", constant.AppMysqlCluster:
|
case "mysql", "mariadb", constant.AppMysqlCluster:
|
||||||
if err := backupService.MysqlBackup(req); err != nil {
|
if err := backupService.MysqlBackup(req); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case "mongodb":
|
||||||
|
if err := backupService.MongodbBackup(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||||
if err := backupService.PostgresqlBackup(req); err != nil {
|
if err := backupService.PostgresqlBackup(req); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
@@ -475,6 +492,11 @@ func (b *BaseApi) Recover(c *gin.Context) {
|
|||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case "mongodb":
|
||||||
|
if err := backupService.MongodbRecover(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||||
if err := backupService.PostgresqlRecover(req); err != nil {
|
if err := backupService.PostgresqlRecover(req); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
@@ -530,6 +552,11 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
|
|||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case "mongodb":
|
||||||
|
if err := backupService.MongodbRecoverByUpload(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||||
if err := backupService.PostgresqlRecoverByUpload(req); err != nil {
|
if err := backupService.PostgresqlRecoverByUpload(req); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (b *BaseApi) CreateClam(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := clamService.Create(req); err != nil {
|
if err := clamService.Create(req, loadAuditUser(c)); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ func (b *BaseApi) UpdateClam(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := clamService.Update(req); err != nil {
|
if err := clamService.Update(req, loadAuditUser(c)); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -123,7 +123,7 @@ func (b *BaseApi) LoadClamBaseInfo(c *gin.Context) {
|
|||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /toolbox/clam/operate [post]
|
// @Router /toolbox/clam/operate [post]
|
||||||
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] Clam","formatEN":"[operation] FTP"}
|
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] Clam","formatEN":"[operation] Clam"}
|
||||||
func (b *BaseApi) OperateClam(c *gin.Context) {
|
func (b *BaseApi) OperateClam(c *gin.Context) {
|
||||||
var req dto.Operate
|
var req dto.Operate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
@@ -394,6 +395,7 @@ func (b *BaseApi) ContainerInfo(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, data)
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Container
|
||||||
// @Summary Load container limits
|
// @Summary Load container limits
|
||||||
// @Success 200 {object} dto.ResourceLimit
|
// @Success 200 {object} dto.ResourceLimit
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
@@ -408,6 +410,7 @@ func (b *BaseApi) LoadResourceLimit(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, data)
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Container
|
||||||
// @Summary Load container stats
|
// @Summary Load container stats
|
||||||
// @Success 200 {array} dto.ContainerListStats
|
// @Success 200 {array} dto.ContainerListStats
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
@@ -422,6 +425,7 @@ func (b *BaseApi) ContainerListStats(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, data)
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Container
|
||||||
// @Summary Load container stats size
|
// @Summary Load container stats size
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.OperationWithName true "request"
|
// @Param request body dto.OperationWithName true "request"
|
||||||
@@ -662,6 +666,14 @@ func (b *BaseApi) Inspect(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, result)
|
helper.SuccessWithData(c, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Container
|
||||||
|
// @Summary Download container logs
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.ContainerLog true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /containers/download/log [post]
|
||||||
func (b *BaseApi) DownloadContainerLogs(c *gin.Context) {
|
func (b *BaseApi) DownloadContainerLogs(c *gin.Context) {
|
||||||
var req dto.ContainerLog
|
var req dto.ContainerLog
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -901,6 +913,10 @@ func (b *BaseApi) LoadComposeEnv(c *gin.Context) {
|
|||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /containers/search/log [get]
|
// @Router /containers/search/log [get]
|
||||||
func (b *BaseApi) ContainerStreamLogs(c *gin.Context) {
|
func (b *BaseApi) ContainerStreamLogs(c *gin.Context) {
|
||||||
|
if !strings.Contains(strings.ToLower(c.GetHeader("Accept")), "text/event-stream") {
|
||||||
|
helper.Success(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
c.Header("Content-Type", "text/event-stream")
|
c.Header("Content-Type", "text/event-stream")
|
||||||
c.Header("Cache-Control", "no-cache")
|
c.Header("Cache-Control", "no-cache")
|
||||||
c.Header("Connection", "keep-alive")
|
c.Header("Connection", "keep-alive")
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func (b *BaseApi) CreateCronjob(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cronjobService.Create(req); err != nil {
|
if err := cronjobService.Create(req, loadAuditUser(c)); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ func (b *BaseApi) ImportCronjob(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cronjobService.Import(req.Cronjobs); err != nil {
|
if err := cronjobService.Import(req.Cronjobs, loadAuditUser(c)); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -285,7 +285,7 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cronjobService.Update(req.ID, req); err != nil {
|
if err := cronjobService.Update(req.ID, req, loadAuditUser(c)); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ func (b *BaseApi) SearchDatabase(c *gin.Context) {
|
|||||||
// @Success 200 {array} dto.DatabaseOption
|
// @Success 200 {array} dto.DatabaseOption
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
|
// @Param type path string true "type"
|
||||||
// @Router /databases/db/list/:type [get]
|
// @Router /databases/db/list/:type [get]
|
||||||
func (b *BaseApi) ListDatabase(c *gin.Context) {
|
func (b *BaseApi) ListDatabase(c *gin.Context) {
|
||||||
dbType, err := helper.GetStrParamByKey(c, "type")
|
dbType, err := helper.GetStrParamByKey(c, "type")
|
||||||
@@ -116,6 +117,7 @@ func (b *BaseApi) ListDatabase(c *gin.Context) {
|
|||||||
// @Success 200 {array} dto.DatabaseItem
|
// @Success 200 {array} dto.DatabaseItem
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
|
// @Param type path string true "type"
|
||||||
// @Router /databases/db/item/:type [get]
|
// @Router /databases/db/item/:type [get]
|
||||||
func (b *BaseApi) LoadDatabaseItems(c *gin.Context) {
|
func (b *BaseApi) LoadDatabaseItems(c *gin.Context) {
|
||||||
dbType, err := helper.GetStrParamByKey(c, "type")
|
dbType, err := helper.GetStrParamByKey(c, "type")
|
||||||
@@ -137,6 +139,7 @@ func (b *BaseApi) LoadDatabaseItems(c *gin.Context) {
|
|||||||
// @Success 200 {object} dto.DatabaseInfo
|
// @Success 200 {object} dto.DatabaseInfo
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
|
// @Param name path string true "name"
|
||||||
// @Router /databases/db/:name [get]
|
// @Router /databases/db/:name [get]
|
||||||
func (b *BaseApi) GetDatabase(c *gin.Context) {
|
func (b *BaseApi) GetDatabase(c *gin.Context) {
|
||||||
name, err := helper.GetStrParamByKey(c, "name")
|
name, err := helper.GetStrParamByKey(c, "name")
|
||||||
|
|||||||
@@ -0,0 +1,294 @@
|
|||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Create mongodb database
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MongodbDBCreate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 mongodb 数据库 [name]","formatEN":"create mongodb database [name]"}
|
||||||
|
func (b *BaseApi) CreateMongodb(c *gin.Context) {
|
||||||
|
var req dto.MongodbDBCreate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.Password) != 0 {
|
||||||
|
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Password = string(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := mongodbService.Create(context.Background(), req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Page mongodb databases
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MongodbDBSearch true "request"
|
||||||
|
// @Success 200 {object} dto.PageResult
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/search [post]
|
||||||
|
func (b *BaseApi) SearchMongodb(c *gin.Context) {
|
||||||
|
var req dto.MongodbDBSearch
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total, list, err := mongodbService.SearchWithPage(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{
|
||||||
|
Items: list,
|
||||||
|
Total: total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Update mongodb database description
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.UpdateDescription true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/description [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mongodbs","output_column":"name","output_value":"name"}],"formatZH":"mongodb 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the mongodb database [name] is modified => [description]"}
|
||||||
|
func (b *BaseApi) UpdateMongodbDescription(c *gin.Context) {
|
||||||
|
var req dto.UpdateDescription
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mongodbService.UpdateDescription(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Load mongodb database from remote
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MongodbLoadDB true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/load [post]
|
||||||
|
func (b *BaseApi) LoadMongodbFromRemote(c *gin.Context) {
|
||||||
|
var req dto.MongodbLoadDB
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mongodbService.LoadFromRemote(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Bind mongodb database user info
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MongodbBind true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/bind [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database", "name", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 mongodb 数据库 [database] [name] 用户 [username]","formatEN":"bind mongodb database [database] [name] user [username]"}
|
||||||
|
func (b *BaseApi) BindMongodbUser(c *gin.Context) {
|
||||||
|
var req dto.MongodbBind
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.Password) != 0 {
|
||||||
|
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Password = string(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mongodbService.BindUser(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Change mongodb database password
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MongodbPassword true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/password [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database", "name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 mongodb 数据库 [database] [name] 密码","formatEN":"update mongodb database [database] [name] password"}
|
||||||
|
func (b *BaseApi) ChangeMongodbPassword(c *gin.Context) {
|
||||||
|
var req dto.MongodbPassword
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.Password) != 0 {
|
||||||
|
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Password = string(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mongodbService.ChangePassword(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Change mongodb root password
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.ChangeDBInfo true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/root/password [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 mongodb 数据库 [database] root 密码","formatEN":"update mongodb database [database] root password"}
|
||||||
|
func (b *BaseApi) ChangeMongodbRootPassword(c *gin.Context) {
|
||||||
|
var req dto.ChangeDBInfo
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.Value) != 0 {
|
||||||
|
value, err := base64.StdEncoding.DecodeString(req.Value)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Value = string(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mongodbService.ChangeRootPassword(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Load mongodb privileges
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MongodbPrivilegesLoad true "request"
|
||||||
|
// @Success 200 {string} string
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/privileges [post]
|
||||||
|
func (b *BaseApi) LoadMongodbPrivileges(c *gin.Context) {
|
||||||
|
var req dto.MongodbPrivilegesLoad
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
permission, err := mongodbService.LoadPrivileges(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, permission)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Change mongodb privileges
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MongodbPrivileges true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/privileges/change [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 mongodb 数据库 [database] 用户 [username] 权限","formatEN":"update mongodb database [database] user [username] privileges"}
|
||||||
|
func (b *BaseApi) ChangeMongodbPrivileges(c *gin.Context) {
|
||||||
|
var req dto.MongodbPrivileges
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mongodbService.ChangePrivileges(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Check before delete mongodb database
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MongodbDBDeleteCheck true "request"
|
||||||
|
// @Success 200 {array} string
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/del/check [post]
|
||||||
|
func (b *BaseApi) DeleteCheckMongodb(c *gin.Context) {
|
||||||
|
var req dto.MongodbDBDeleteCheck
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apps, err := mongodbService.DeleteCheck(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, apps)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mongodb
|
||||||
|
// @Summary Delete mongodb database
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MongodbDBDelete true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/mongodb/del [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mongodbs","output_column":"name","output_value":"name"}],"formatZH":"删除 mongodb 数据库 [name]","formatEN":"delete mongodb database [name]"}
|
||||||
|
func (b *BaseApi) DeleteMongodb(c *gin.Context) {
|
||||||
|
var req dto.MongodbDBDelete
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, ctx := helper.GetTxAndContext()
|
||||||
|
if err := mongodbService.Delete(ctx, req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
tx.Rollback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
@@ -92,7 +92,7 @@ func (b *BaseApi) UpdatePostgresqlDescription(c *gin.Context) {
|
|||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /databases/pg/privileges [post]
|
// @Router /databases/pg/privileges [post]
|
||||||
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新数据库 [database] 用户 [username] 权限","formatEN":"Update [user] privileges of database [database]"}
|
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新数据库 [database] 用户 [username] 权限","formatEN":"Update [username] privileges of database [database]"}
|
||||||
func (b *BaseApi) ChangePostgresqlPrivileges(c *gin.Context) {
|
func (b *BaseApi) ChangePostgresqlPrivileges(c *gin.Context) {
|
||||||
var req dto.PostgresqlPrivileges
|
var req dto.PostgresqlPrivileges
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -170,6 +170,7 @@ func (b *BaseApi) SearchPostgresql(c *gin.Context) {
|
|||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
|
// @Param database path string true "database"
|
||||||
// @Router /databases/pg/:database/load [post]
|
// @Router /databases/pg/:database/load [post]
|
||||||
func (b *BaseApi) LoadPostgresqlDBFromRemote(c *gin.Context) {
|
func (b *BaseApi) LoadPostgresqlDBFromRemote(c *gin.Context) {
|
||||||
database, err := helper.GetStrParamByKey(c, "database")
|
database, err := helper.GetStrParamByKey(c, "database")
|
||||||
|
|||||||
@@ -74,6 +74,12 @@ func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, data)
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Database Redis
|
||||||
|
// @Summary Check has cli
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/redis/check [get]
|
||||||
func (b *BaseApi) CheckHasCli(c *gin.Context) {
|
func (b *BaseApi) CheckHasCli(c *gin.Context) {
|
||||||
helper.SuccessWithData(c, redisService.CheckHasCli())
|
helper.SuccessWithData(c, redisService.CheckHasCli())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,18 +31,20 @@ var (
|
|||||||
dbCommonService = service.NewIDBCommonService()
|
dbCommonService = service.NewIDBCommonService()
|
||||||
mysqlService = service.NewIMysqlService()
|
mysqlService = service.NewIMysqlService()
|
||||||
postgresqlService = service.NewIPostgresqlService()
|
postgresqlService = service.NewIPostgresqlService()
|
||||||
|
mongodbService = service.NewIMongodbService()
|
||||||
databaseService = service.NewIDatabaseService()
|
databaseService = service.NewIDatabaseService()
|
||||||
redisService = service.NewIRedisService()
|
redisService = service.NewIRedisService()
|
||||||
|
|
||||||
cronjobService = service.NewICronjobService()
|
cronjobService = service.NewICronjobService()
|
||||||
|
|
||||||
fileService = service.NewIFileService()
|
fileService = service.NewIFileService()
|
||||||
fileShareService = service.NewIFileShareService()
|
fileHistoryService = service.NewIFileHistoryService()
|
||||||
sshService = service.NewISSHService()
|
fileShareService = service.NewIFileShareService()
|
||||||
firewallService = service.NewIFirewallService()
|
sshService = service.NewISSHService()
|
||||||
iptablesService = service.NewIIptablesService()
|
firewallService = service.NewIFirewallService()
|
||||||
monitorService = service.NewIMonitorService()
|
iptablesService = service.NewIIptablesService()
|
||||||
systemService = service.NewISystemService()
|
monitorService = service.NewIMonitorService()
|
||||||
|
systemService = service.NewISystemService()
|
||||||
|
|
||||||
deviceService = service.NewIDeviceService()
|
deviceService = service.NewIDeviceService()
|
||||||
fail2banService = service.NewIFail2BanService()
|
fail2banService = service.NewIFail2BanService()
|
||||||
|
|||||||
+88
-18
@@ -193,7 +193,7 @@ func (b *BaseApi) BatchDeleteFile(c *gin.Context) {
|
|||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /files/mode [post]
|
// @Router /files/mode [post]
|
||||||
// @x-panel-log {"bodyKeys":["path","mode"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改权限 [paths] => [mode]","formatEN":"Change mode [paths] => [mode]"}
|
// @x-panel-log {"bodyKeys":["path","mode"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改权限 [path] => [mode]","formatEN":"Change mode [path] => [mode]"}
|
||||||
func (b *BaseApi) ChangeFileMode(c *gin.Context) {
|
func (b *BaseApi) ChangeFileMode(c *gin.Context) {
|
||||||
var req request.FileCreate
|
var req request.FileCreate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -215,7 +215,7 @@ func (b *BaseApi) ChangeFileMode(c *gin.Context) {
|
|||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /files/owner [post]
|
// @Router /files/owner [post]
|
||||||
// @x-panel-log {"bodyKeys":["path","user","group"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改用户/组 [paths] => [user]/[group]","formatEN":"Change owner [paths] => [user]/[group]"}
|
// @x-panel-log {"bodyKeys":["path","user","group"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改用户/组 [path] => [user]/[group]","formatEN":"Change owner [path] => [user]/[group]"}
|
||||||
func (b *BaseApi) ChangeFileOwner(c *gin.Context) {
|
func (b *BaseApi) ChangeFileOwner(c *gin.Context) {
|
||||||
var req request.FileRoleUpdate
|
var req request.FileRoleUpdate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -250,6 +250,26 @@ func (b *BaseApi) CompressFile(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Stop compress task
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.FileCompressStopReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /files/compress/stop [post]
|
||||||
|
func (b *BaseApi) StopCompressFile(c *gin.Context) {
|
||||||
|
var req request.FileCompressStopReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := fileService.StopCompress(req.TaskID); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags File
|
// @Tags File
|
||||||
// @Summary Decompress file
|
// @Summary Decompress file
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -272,6 +292,26 @@ func (b *BaseApi) DeCompressFile(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Stop decompress task
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.FileDeCompressStopReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /files/decompress/stop [post]
|
||||||
|
func (b *BaseApi) StopDeCompressFile(c *gin.Context) {
|
||||||
|
var req request.FileDeCompressStopReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := fileService.StopDeCompress(req.TaskID); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags File
|
// @Tags File
|
||||||
// @Summary Load file content
|
// @Summary Load file content
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -371,7 +411,7 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
|||||||
helper.BadRequest(c, errors.New("error paths in request"))
|
helper.BadRequest(c, errors.New("error paths in request"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dir := path.Dir(paths[0])
|
dir := path.Clean(paths[0])
|
||||||
|
|
||||||
_, err = os.Stat(dir)
|
_, err = os.Stat(dir)
|
||||||
if err != nil && os.IsNotExist(err) {
|
if err != nil && os.IsNotExist(err) {
|
||||||
@@ -412,8 +452,14 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
_ = os.Chown(dstDir, uid, gid)
|
_ = os.Chown(dstDir, uid, gid)
|
||||||
}
|
}
|
||||||
|
dstDirMode := mode
|
||||||
|
dstFileMode := mode.Perm()
|
||||||
|
if dstDirInfo, err := os.Stat(dstDir); err == nil {
|
||||||
|
dstDirMode = dstDirInfo.Mode()
|
||||||
|
dstFileMode = dstDirInfo.Mode().Perm()
|
||||||
|
}
|
||||||
tmpFilename := dstFilename + ".tmp"
|
tmpFilename := dstFilename + ".tmp"
|
||||||
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
|
if err := c.SaveUploadedFile(file, tmpFilename, dstDirMode); err != nil {
|
||||||
_ = os.Remove(tmpFilename)
|
_ = os.Remove(tmpFilename)
|
||||||
e := fmt.Errorf("upload [%s] file failed, err: %v", file.Filename, err)
|
e := fmt.Errorf("upload [%s] file failed, err: %v", file.Filename, err)
|
||||||
failures[file.Filename] = e
|
failures[file.Filename] = e
|
||||||
@@ -436,7 +482,7 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
|||||||
if statErr == nil {
|
if statErr == nil {
|
||||||
_ = os.Chmod(dstFilename, dstInfo.Mode())
|
_ = os.Chmod(dstFilename, dstInfo.Mode())
|
||||||
} else {
|
} else {
|
||||||
_ = os.Chmod(dstFilename, mode)
|
_ = os.Chmod(dstFilename, dstFileMode)
|
||||||
}
|
}
|
||||||
if uid != -1 && gid != -1 {
|
if uid != -1 && gid != -1 {
|
||||||
_ = os.Chown(dstFilename, uid, gid)
|
_ = os.Chown(dstFilename, uid, gid)
|
||||||
@@ -594,6 +640,10 @@ func (b *BaseApi) MoveFile(c *gin.Context) {
|
|||||||
// @Router /files/download [get]
|
// @Router /files/download [get]
|
||||||
func (b *BaseApi) Download(c *gin.Context) {
|
func (b *BaseApi) Download(c *gin.Context) {
|
||||||
filePath := c.Query("path")
|
filePath := c.Query("path")
|
||||||
|
if files.ShouldDenySensitiveFileRead(filePath) {
|
||||||
|
helper.InternalServer(c, buserr.New("ErrSensitiveFileRead"))
|
||||||
|
return
|
||||||
|
}
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
@@ -629,6 +679,10 @@ func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
|
|||||||
helper.ErrorWithDetail(c, http.StatusInternalServerError, "ErrPathNotFound", nil)
|
helper.ErrorWithDetail(c, http.StatusInternalServerError, "ErrPathNotFound", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if files.ShouldDenySensitiveFileRead(req.Path) {
|
||||||
|
helper.InternalServer(c, buserr.New("ErrSensitiveFileRead"))
|
||||||
|
return
|
||||||
|
}
|
||||||
filePath := req.Path
|
filePath := req.Path
|
||||||
fstFile, err := fileOp.OpenFile(filePath)
|
fstFile, err := fileOp.OpenFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -747,12 +801,13 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if dstDirInfo, err := os.Stat(dstDir); err == nil {
|
||||||
|
mode = dstDirInfo.Mode().Perm()
|
||||||
|
}
|
||||||
dstFileName := filepath.Join(dstDir, fileName)
|
dstFileName := filepath.Join(dstDir, fileName)
|
||||||
dstInfo, statErr := os.Stat(dstFileName)
|
dstInfo, statErr := os.Stat(dstFileName)
|
||||||
if statErr == nil {
|
if statErr == nil {
|
||||||
mode = dstInfo.Mode()
|
mode = dstInfo.Mode()
|
||||||
} else {
|
|
||||||
mode = 0644
|
|
||||||
}
|
}
|
||||||
if overwrite {
|
if overwrite {
|
||||||
_ = os.Remove(dstFileName)
|
_ = os.Remove(dstFileName)
|
||||||
@@ -774,6 +829,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int,
|
|||||||
}
|
}
|
||||||
_ = os.Remove(chunkPath)
|
_ = os.Remove(chunkPath)
|
||||||
}
|
}
|
||||||
|
_ = os.Chmod(dstFileName, mode)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -878,7 +934,17 @@ var wsUpgrade = websocket.Upgrader{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Wget process
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /files/wget/process [get]
|
||||||
func (b *BaseApi) WgetProcess(c *gin.Context) {
|
func (b *BaseApi) WgetProcess(c *gin.Context) {
|
||||||
|
if !websocket.IsWebSocketUpgrade(c.Request) {
|
||||||
|
helper.Success(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
ws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)
|
ws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -888,6 +954,12 @@ func (b *BaseApi) WgetProcess(c *gin.Context) {
|
|||||||
go wsClient.Write()
|
go wsClient.Write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Process keys
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /files/wget/process/keys [get]
|
||||||
func (b *BaseApi) ProcessKeys(c *gin.Context) {
|
func (b *BaseApi) ProcessKeys(c *gin.Context) {
|
||||||
res := &response.FileProcessKeys{}
|
res := &response.FileProcessKeys{}
|
||||||
keys := global.CACHE.PrefixScanKey("file-wget-")
|
keys := global.CACHE.PrefixScanKey("file-wget-")
|
||||||
@@ -907,16 +979,20 @@ func (b *BaseApi) ProcessKeys(c *gin.Context) {
|
|||||||
|
|
||||||
// @Tags File
|
// @Tags File
|
||||||
// @Summary Read file by Line
|
// @Summary Read file by Line
|
||||||
|
// @Param type path string true "type"
|
||||||
// @Param request body request.FileReadByLineReq true "request"
|
// @Param request body request.FileReadByLineReq true "request"
|
||||||
// @Success 200 {object} response.FileLineContent
|
// @Success 200 {object} response.FileLineContent
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /files/read [post]
|
// @Router /files/read/{type} [post]
|
||||||
func (b *BaseApi) ReadFileByLine(c *gin.Context) {
|
func (b *BaseApi) ReadFileByLine(c *gin.Context) {
|
||||||
var req request.FileReadByLineReq
|
var req request.FileReadByLineReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if readType := strings.TrimSpace(c.Param("type")); readType != "" {
|
||||||
|
req.Type = readType
|
||||||
|
}
|
||||||
res, err := fileService.ReadLogByLine(req)
|
res, err := fileService.ReadLogByLine(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
@@ -949,16 +1025,6 @@ func (b *BaseApi) BatchChangeModeAndOwner(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) GetPathByType(c *gin.Context) {
|
|
||||||
pathType, ok := c.Params.Get("type")
|
|
||||||
if !ok {
|
|
||||||
helper.BadRequest(c, errors.New("error pathType id in path"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resPath := fileService.GetPathByType(pathType)
|
|
||||||
helper.SuccessWithData(c, resPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Tags File
|
// @Tags File
|
||||||
// @Summary system mount
|
// @Summary system mount
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -1290,6 +1356,10 @@ func (b *BaseApi) DownloadFileShare(c *gin.Context) {
|
|||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if files.ShouldDenySensitiveFileRead(filePath) {
|
||||||
|
helper.InternalServer(c, buserr.New("ErrSensitiveFileRead"))
|
||||||
|
return
|
||||||
|
}
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Load file history list
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.FileHistorySearchReq true "request"
|
||||||
|
// @Success 200 {object} dto.PageResult
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /files/history/search [post]
|
||||||
|
func (b *BaseApi) SearchFileHistory(c *gin.Context) {
|
||||||
|
var req request.FileHistorySearchReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total, items, err := fileHistoryService.Search(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{Items: items, Total: total})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Load file history content
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.FileHistoryContentReq true "request"
|
||||||
|
// @Success 200 {object} response.FileHistoryInfo
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /files/history/content [post]
|
||||||
|
func (b *BaseApi) GetFileHistoryContent(c *gin.Context) {
|
||||||
|
var req request.FileHistoryContentReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
item, err := fileHistoryService.GetContent(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Delete file history record
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.FileHistoryDeleteReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /files/history/del [post]
|
||||||
|
func (b *BaseApi) DeleteFileHistory(c *gin.Context) {
|
||||||
|
var req request.FileHistoryDeleteReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := fileHistoryService.Delete(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Restore file history record
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.FileHistoryRestoreReq true "request"
|
||||||
|
// @Success 200 {object} response.FileInfo
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /files/history/restore [post]
|
||||||
|
func (b *BaseApi) RestoreFileHistory(c *gin.Context) {
|
||||||
|
var req request.FileHistoryRestoreReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info, err := fileHistoryService.Restore(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, info)
|
||||||
|
}
|
||||||
@@ -236,7 +236,7 @@ func (b *BaseApi) UpdateAddrRule(c *gin.Context) {
|
|||||||
// @Success 200 {object} dto.PageResult
|
// @Success 200 {object} dto.PageResult
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /hosts/firewall/filter/search [post]
|
// @Router /hosts/firewall/filter/rule/search [post]
|
||||||
func (b *BaseApi) SearchFilterRules(c *gin.Context) {
|
func (b *BaseApi) SearchFilterRules(c *gin.Context) {
|
||||||
var req dto.SearchPageWithType
|
var req dto.SearchPageWithType
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Load gpu / xpu info
|
||||||
|
// @Accept json
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/gpu/load [get]
|
||||||
|
func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
|
||||||
|
ok, client := gpu.New()
|
||||||
|
if ok {
|
||||||
|
info, err := client.LoadGpuInfo()
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, info)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xpuOK, xpuClient := xpu.New()
|
||||||
|
if xpuOK {
|
||||||
|
info, err := xpuClient.LoadGpuInfo()
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, info)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, &common.GpuInfo{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @Summary Get CPU options
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/gpu/options [get]
|
||||||
|
func (b *BaseApi) GetCPUOptions(c *gin.Context) {
|
||||||
|
helper.SuccessWithData(c, monitorService.LoadGPUOptions())
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Monitor
|
||||||
|
// @Summary Load monitor data
|
||||||
|
// @Param request body dto.MonitorGPUSearch true "request"
|
||||||
|
// @Success 200 {object} dto.MonitorGPUData
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/gpu/search [post]
|
||||||
|
func (b *BaseApi) LoadGPUMonitor(c *gin.Context) {
|
||||||
|
var req dto.MonitorGPUSearch
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := monitorService.LoadGPUMonitorData(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
@@ -5,6 +5,12 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// @Tags Health
|
||||||
|
// @Summary Check health
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /health/check [get]
|
||||||
func (b *BaseApi) CheckHealth(c *gin.Context) {
|
func (b *BaseApi) CheckHealth(c *gin.Context) {
|
||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,15 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// @Tags Host
|
||||||
|
// @Summary Create host
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.HostOperate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name","addr"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建主机 [name][addr]","formatEN":"create host [name][addr]"}
|
||||||
func (b *BaseApi) CreateHost(c *gin.Context) {
|
func (b *BaseApi) CreateHost(c *gin.Context) {
|
||||||
var req dto.HostOperate
|
var req dto.HostOperate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -21,6 +30,14 @@ func (b *BaseApi) CreateHost(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, host)
|
helper.SuccessWithData(c, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Host
|
||||||
|
// @Summary Test by info
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.HostConnTest true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/test/byinfo [post]
|
||||||
func (b *BaseApi) TestByInfo(c *gin.Context) {
|
func (b *BaseApi) TestByInfo(c *gin.Context) {
|
||||||
var req dto.HostConnTest
|
var req dto.HostConnTest
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -30,6 +47,14 @@ func (b *BaseApi) TestByInfo(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, hostService.TestByInfo(req))
|
helper.SuccessWithData(c, hostService.TestByInfo(req))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Host
|
||||||
|
// @Summary Test by ID
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OperateByID true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/test/byid [post]
|
||||||
func (b *BaseApi) TestByID(c *gin.Context) {
|
func (b *BaseApi) TestByID(c *gin.Context) {
|
||||||
var req dto.OperateByID
|
var req dto.OperateByID
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -39,6 +64,14 @@ func (b *BaseApi) TestByID(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, hostService.TestLocalConn(req.ID))
|
helper.SuccessWithData(c, hostService.TestLocalConn(req.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Host
|
||||||
|
// @Summary Host tree
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.SearchForTree true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/tree [post]
|
||||||
func (b *BaseApi) HostTree(c *gin.Context) {
|
func (b *BaseApi) HostTree(c *gin.Context) {
|
||||||
var req dto.SearchForTree
|
var req dto.SearchForTree
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -53,6 +86,14 @@ func (b *BaseApi) HostTree(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, data)
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Host
|
||||||
|
// @Summary Search host
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.SearchPageWithGroup true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/search [post]
|
||||||
func (b *BaseApi) SearchHost(c *gin.Context) {
|
func (b *BaseApi) SearchHost(c *gin.Context) {
|
||||||
var req dto.SearchPageWithGroup
|
var req dto.SearchPageWithGroup
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -68,6 +109,15 @@ func (b *BaseApi) SearchHost(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, dto.PageResult{Items: list, Total: total})
|
helper.SuccessWithData(c, dto.PageResult{Items: list, Total: total})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Host
|
||||||
|
// @Summary Delete host
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OperateByIDs true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/del [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"hosts","output_column":"name","output_value":"names"}],"formatZH":"删除主机 [names]","formatEN":"delete host [names]"}
|
||||||
func (b *BaseApi) DeleteHost(c *gin.Context) {
|
func (b *BaseApi) DeleteHost(c *gin.Context) {
|
||||||
var req dto.OperateByIDs
|
var req dto.OperateByIDs
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -81,6 +131,15 @@ func (b *BaseApi) DeleteHost(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Host
|
||||||
|
// @Summary Update host
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.HostOperate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name","addr"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新主机 [name][addr]","formatEN":"update host [name][addr]"}
|
||||||
func (b *BaseApi) UpdateHost(c *gin.Context) {
|
func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||||
var req dto.HostOperate
|
var req dto.HostOperate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -140,6 +199,15 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, hostItem)
|
helper.SuccessWithData(c, hostItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Host
|
||||||
|
// @Summary Update host group
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.ChangeGroup true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/update/group [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id","groupID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"hosts","output_column":"name","output_value":"name"}],"formatZH":"更新主机 [name] 分组","formatEN":"update host [name] group"}
|
||||||
func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
|
func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
|
||||||
var req dto.ChangeGroup
|
var req dto.ChangeGroup
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -153,6 +221,14 @@ func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Host
|
||||||
|
// @Summary Get host by ID
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OperateByID true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/info [post]
|
||||||
func (b *BaseApi) GetHostByID(c *gin.Context) {
|
func (b *BaseApi) GetHostByID(c *gin.Context) {
|
||||||
var req dto.OperateByID
|
var req dto.OperateByID
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import (
|
|||||||
// @Tags Host tool
|
// @Tags Host tool
|
||||||
// @Summary Get tool status
|
// @Summary Get tool status
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.HostToolReq true "request"
|
// @Param request body request.HostToolTypeReq true "request"
|
||||||
// @Success 200 {object} response.HostToolRes
|
// @Success 200 {object} response.HostToolRes
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /hosts/tool [post]
|
// @Router /hosts/tool/status [post]
|
||||||
func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
||||||
var req request.HostToolReq
|
var req request.HostToolTypeReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -53,14 +53,14 @@ func (b *BaseApi) InitToolConfig(c *gin.Context) {
|
|||||||
// @Tags Host tool
|
// @Tags Host tool
|
||||||
// @Summary Operate tool
|
// @Summary Operate tool
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.HostToolReq true "request"
|
// @Param request body request.HostToolOperateReq true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /hosts/tool/operate [post]
|
// @Router /hosts/tool/operate [post]
|
||||||
// @x-panel-log {"bodyKeys":["operate","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] [type] ","formatEN":"[operate] [type]"}
|
// @x-panel-log {"bodyKeys":["operate","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] [type] ","formatEN":"[operate] [type]"}
|
||||||
func (b *BaseApi) OperateTool(c *gin.Context) {
|
func (b *BaseApi) OperateTool(c *gin.Context) {
|
||||||
var req request.HostToolReq
|
var req request.HostToolOperateReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -75,19 +75,18 @@ func (b *BaseApi) OperateTool(c *gin.Context) {
|
|||||||
// @Tags Host tool
|
// @Tags Host tool
|
||||||
// @Summary Get tool config
|
// @Summary Get tool config
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.HostToolConfig true "request"
|
// @Param request body request.HostToolTypeReq true "request"
|
||||||
// @Success 200 {object} response.HostToolConfig
|
// @Success 200 {object} response.HostToolConfig
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /hosts/tool/config [post]
|
// @Router /hosts/tool/config/get [post]
|
||||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机工具配置文件 ","formatEN":"[operate] tool config"}
|
func (b *BaseApi) GetToolConfig(c *gin.Context) {
|
||||||
func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
var req request.HostToolTypeReq
|
||||||
var req request.HostToolConfig
|
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := hostToolService.OperateToolConfig(req)
|
config, err := hostToolService.GetToolConfig(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
@@ -95,6 +94,28 @@ func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, config)
|
helper.SuccessWithData(c, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Host tool
|
||||||
|
// @Summary Update tool config
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.HostToolConfigUpdate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/tool/config/set [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 [type] 主机工具配置文件 ","formatEN":"update [type] tool config"}
|
||||||
|
func (b *BaseApi) UpdateToolConfig(c *gin.Context) {
|
||||||
|
var req request.HostToolConfigUpdate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hostToolService.UpdateToolConfig(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags Host tool
|
// @Tags Host tool
|
||||||
// @Summary Create Supervisor process
|
// @Summary Create Supervisor process
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -137,18 +158,44 @@ func (b *BaseApi) GetProcess(c *gin.Context) {
|
|||||||
// @Tags Host tool
|
// @Tags Host tool
|
||||||
// @Summary Get Supervisor process config file
|
// @Summary Get Supervisor process config file
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.SupervisorProcessFileReq true "request"
|
// @Param request body request.HostSupervisorProcessFileGetReq true "request"
|
||||||
// @Success 200 {string} content
|
// @Success 200 {string} content
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /hosts/tool/supervisor/process/file [post]
|
// @Router /hosts/tool/supervisor/process/file/get [post]
|
||||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] Supervisor 进程文件 ","formatEN":"[operate] Supervisor Process Config file"}
|
|
||||||
func (b *BaseApi) GetProcessFile(c *gin.Context) {
|
func (b *BaseApi) GetProcessFile(c *gin.Context) {
|
||||||
var req request.SupervisorProcessFileReq
|
var req request.HostSupervisorProcessFileGetReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
content, err := hostToolService.OperateSupervisorProcessFile(req)
|
content, err := hostToolService.GetSupervisorProcessFile(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Host tool
|
||||||
|
// @Summary Operate Supervisor process config file
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.HostSupervisorProcessFileOperateReq true "request"
|
||||||
|
// @Success 200 {string} content
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/tool/supervisor/process/file [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] Supervisor 进程文件 ","formatEN":"[operate] Supervisor Process Config file"}
|
||||||
|
func (b *BaseApi) OperateProcessFile(c *gin.Context) {
|
||||||
|
var req request.HostSupervisorProcessFileOperateReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
content, err := hostToolService.OperateSupervisorProcessFile(request.SupervisorProcessFileReq{
|
||||||
|
Name: req.Name,
|
||||||
|
Operate: req.Operate,
|
||||||
|
Content: req.Content,
|
||||||
|
File: req.File,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -23,6 +23,27 @@ func (b *BaseApi) PageMcpServers(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, list)
|
helper.SuccessWithData(c, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags McpServer
|
||||||
|
// @Summary Load mcp server detail
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.McpServerDetail true "request"
|
||||||
|
// @Success 200 {object} response.McpServerDTO
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/mcp/server/detail [post]
|
||||||
|
func (b *BaseApi) LoadMcpServerDetail(c *gin.Context) {
|
||||||
|
var req request.McpServerDetail
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := mcpServerService.Detail(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags McpServer
|
// @Tags McpServer
|
||||||
// @Summary Create mcp server
|
// @Summary Create mcp server
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -107,6 +128,48 @@ func (b *BaseApi) OperateMcpServer(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags McpServer
|
||||||
|
// @Summary Sync mcp server status
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.McpServerStatusSync true "request"
|
||||||
|
// @Success 200 {array} response.McpServerStatusDTO
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/mcp/server/status/sync [post]
|
||||||
|
func (b *BaseApi) SyncMcpServerStatus(c *gin.Context) {
|
||||||
|
var req request.McpServerStatusSync
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := mcpServerService.SyncStatus(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags McpServer
|
||||||
|
// @Summary Test mcp server connection
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.McpServerConnectionTest true "request"
|
||||||
|
// @Success 200 {object} response.McpServerConnectionTestRes
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/mcp/server/connection/test [post]
|
||||||
|
func (b *BaseApi) TestMcpServerConnection(c *gin.Context) {
|
||||||
|
var req request.McpServerConnectionTest
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := mcpServerService.TestConnection(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags McpServer
|
// @Tags McpServer
|
||||||
// @Summary Bind Domain for mcp server
|
// @Summary Bind Domain for mcp server
|
||||||
// @Accept json
|
// @Accept json
|
||||||
|
|||||||
+13
-26
@@ -31,27 +31,6 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
|||||||
helper.SuccessWithDataGzipped(c, data)
|
helper.SuccessWithDataGzipped(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags Monitor
|
|
||||||
// @Summary Load monitor data
|
|
||||||
// @Param request body dto.MonitorGPUSearch true "request"
|
|
||||||
// @Success 200 {object} dto.MonitorGPUData
|
|
||||||
// @Security ApiKeyAuth
|
|
||||||
// @Security Timestamp
|
|
||||||
// @Router /hosts/monitor/gpu/search [post]
|
|
||||||
func (b *BaseApi) LoadGPUMonitor(c *gin.Context) {
|
|
||||||
var req dto.MonitorGPUSearch
|
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := monitorService.LoadGPUMonitorData(req)
|
|
||||||
if err != nil {
|
|
||||||
helper.InternalServer(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
helper.SuccessWithData(c, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Tags Monitor
|
// @Tags Monitor
|
||||||
// @Summary Clean monitor data
|
// @Summary Clean monitor data
|
||||||
// @Success 200
|
// @Success 200
|
||||||
@@ -91,7 +70,7 @@ func (b *BaseApi) LoadMonitorSetting(c *gin.Context) {
|
|||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /hosts/monitor/setting/update [post]
|
// @Router /hosts/monitor/setting/update [post]
|
||||||
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改默认监控网卡 [name]-[value]","formatEN":"update default monitor [name]-[value]"}
|
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改默认监控网卡 [key]-[value]","formatEN":"update default monitor [key]-[value]"}
|
||||||
func (b *BaseApi) UpdateMonitorSetting(c *gin.Context) {
|
func (b *BaseApi) UpdateMonitorSetting(c *gin.Context) {
|
||||||
var req dto.MonitorSettingUpdate
|
var req dto.MonitorSettingUpdate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -105,6 +84,12 @@ func (b *BaseApi) UpdateMonitorSetting(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Monitor
|
||||||
|
// @Summary Get network options
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/monitor/netoptions [get]
|
||||||
func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
|
func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
|
||||||
netStat, _ := net.IOCounters(true)
|
netStat, _ := net.IOCounters(true)
|
||||||
var options []string
|
var options []string
|
||||||
@@ -116,6 +101,12 @@ func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, options)
|
helper.SuccessWithData(c, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Monitor
|
||||||
|
// @Summary Get IO options
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/monitor/iooptions [get]
|
||||||
func (b *BaseApi) GetIOOptions(c *gin.Context) {
|
func (b *BaseApi) GetIOOptions(c *gin.Context) {
|
||||||
diskStat, _ := disk.IOCounters()
|
diskStat, _ := disk.IOCounters()
|
||||||
var options []string
|
var options []string
|
||||||
@@ -126,7 +117,3 @@ func (b *BaseApi) GetIOOptions(c *gin.Context) {
|
|||||||
sort.Strings(options)
|
sort.Strings(options)
|
||||||
helper.SuccessWithData(c, options)
|
helper.SuccessWithData(c, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) GetCPUOptions(c *gin.Context) {
|
|
||||||
helper.SuccessWithData(c, monitorService.LoadGPUOptions())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,9 +5,20 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||||
websocket2 "github.com/1Panel-dev/1Panel/agent/utils/websocket"
|
websocket2 "github.com/1Panel-dev/1Panel/agent/utils/websocket"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// @Tags Process
|
||||||
|
// @Summary Process ws
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /process/ws [get]
|
||||||
func (b *BaseApi) ProcessWs(c *gin.Context) {
|
func (b *BaseApi) ProcessWs(c *gin.Context) {
|
||||||
|
if !websocket.IsWebSocketUpgrade(c.Request) {
|
||||||
|
helper.Success(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
ws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)
|
ws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func (b *BaseApi) CreateRuntime(c *gin.Context) {
|
|||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /runtimes/del [post]
|
// @Router /runtimes/del [post]
|
||||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除运行环境 [name]","formatEN":"Delete runtime [name]"}
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除运行环境 [id]","formatEN":"Delete runtime [id]"}
|
||||||
func (b *BaseApi) DeleteRuntime(c *gin.Context) {
|
func (b *BaseApi) DeleteRuntime(c *gin.Context) {
|
||||||
var req request.RuntimeDelete
|
var req request.RuntimeDelete
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -81,6 +81,7 @@ func (b *BaseApi) DeleteRuntime(c *gin.Context) {
|
|||||||
// @Success 200 {array} dto.AppResource
|
// @Success 200 {array} dto.AppResource
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
|
// @Param id path integer true "id"
|
||||||
// @Router /runtimes/installed/delete/check/:id [get]
|
// @Router /runtimes/installed/delete/check/:id [get]
|
||||||
func (b *BaseApi) DeleteRuntimeCheck(c *gin.Context) {
|
func (b *BaseApi) DeleteRuntimeCheck(c *gin.Context) {
|
||||||
runTimeId, err := helper.GetIntParamByKey(c, "id")
|
runTimeId, err := helper.GetIntParamByKey(c, "id")
|
||||||
@@ -120,7 +121,7 @@ func (b *BaseApi) UpdateRuntime(c *gin.Context) {
|
|||||||
// @Tags Runtime
|
// @Tags Runtime
|
||||||
// @Summary Get runtime
|
// @Summary Get runtime
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param id path string true "request"
|
// @Param id path integer true "request"
|
||||||
// @Success 200 {object} response.RuntimeDTO
|
// @Success 200 {object} response.RuntimeDTO
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
@@ -168,7 +169,7 @@ func (b *BaseApi) GetNodePackageRunScript(c *gin.Context) {
|
|||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /runtimes/operate [post]
|
// @Router /runtimes/operate [post]
|
||||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"操作运行环境 [name]","formatEN":"Operate runtime [name]"}
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"操作运行环境 [id]","formatEN":"Operate runtime [id]"}
|
||||||
func (b *BaseApi) OperateRuntime(c *gin.Context) {
|
func (b *BaseApi) OperateRuntime(c *gin.Context) {
|
||||||
var req request.RuntimeOperate
|
var req request.RuntimeOperate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -242,7 +243,7 @@ func (b *BaseApi) SyncStatus(c *gin.Context) {
|
|||||||
// @Tags Runtime
|
// @Tags Runtime
|
||||||
// @Summary Get php runtime extension
|
// @Summary Get php runtime extension
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param id path string true "request"
|
// @Param id path integer true "request"
|
||||||
// @Success 200 {object} response.PHPExtensionRes
|
// @Success 200 {object} response.PHPExtensionRes
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
|
|||||||
+87
-30
@@ -1,15 +1,12 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// @Tags System Setting
|
// @Tags System Setting
|
||||||
@@ -27,6 +24,12 @@ func (b *BaseApi) GetSettingInfo(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, setting)
|
helper.SuccessWithData(c, setting)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Get terminal AI setting info
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /settings/terminal/ai/search [post]
|
||||||
func (b *BaseApi) GetTerminalAISettingInfo(c *gin.Context) {
|
func (b *BaseApi) GetTerminalAISettingInfo(c *gin.Context) {
|
||||||
setting, err := settingService.GetTerminalAIInfo()
|
setting, err := settingService.GetTerminalAIInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -49,14 +52,14 @@ func (b *BaseApi) GetSystemAvailable(c *gin.Context) {
|
|||||||
// @Tags System Setting
|
// @Tags System Setting
|
||||||
// @Summary Update system setting
|
// @Summary Update system setting
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.SettingUpdate true "request"
|
// @Param request body dto.AgentSettingUpdate true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /settings/update [post]
|
// @Router /settings/update [post]
|
||||||
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改系统配置 [key] => [value]","formatEN":"update system setting [key] => [value]"}
|
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改系统配置 [key] => [value]","formatEN":"update system setting [key] => [value]"}
|
||||||
func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
||||||
var req dto.SettingUpdate
|
var req dto.AgentSettingUpdate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -68,6 +71,15 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Update terminal AI setting
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.TerminalAIInfo true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /settings/terminal/ai/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["aiStatus","aiAccountId"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新终端 AI 设置 [aiStatus][aiAccountId]","formatEN":"update terminal AI setting [aiStatus][aiAccountId]"}
|
||||||
func (b *BaseApi) UpdateTerminalAISetting(c *gin.Context) {
|
func (b *BaseApi) UpdateTerminalAISetting(c *gin.Context) {
|
||||||
var req dto.TerminalAIInfo
|
var req dto.TerminalAIInfo
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -81,6 +93,12 @@ func (b *BaseApi) UpdateTerminalAISetting(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Get file manage AI setting info
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /settings/files/ai/search [post]
|
||||||
func (b *BaseApi) GetFileManageAISettingInfo(c *gin.Context) {
|
func (b *BaseApi) GetFileManageAISettingInfo(c *gin.Context) {
|
||||||
setting, err := settingService.GetFileManageAIInfo()
|
setting, err := settingService.GetFileManageAIInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -90,6 +108,15 @@ func (b *BaseApi) GetFileManageAISettingInfo(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, setting)
|
helper.SuccessWithData(c, setting)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Update file manage AI setting
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.FileManageAIInfo true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /settings/files/ai/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["aiStatus","aiAccountId"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新文件管理 AI 设置 [aiStatus][aiAccountId]","formatEN":"update file manage AI setting [aiStatus][aiAccountId]"}
|
||||||
func (b *BaseApi) UpdateFileManageAISetting(c *gin.Context) {
|
func (b *BaseApi) UpdateFileManageAISetting(c *gin.Context) {
|
||||||
var req dto.FileManageAIInfo
|
var req dto.FileManageAIInfo
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -102,6 +129,51 @@ func (b *BaseApi) UpdateFileManageAISetting(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Load file history setting info
|
||||||
|
// @Success 200 {object} response.FileHistorySettingInfo
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /settings/file-history/search [post]
|
||||||
|
func (b *BaseApi) GetFileHistorySettingInfo(c *gin.Context) {
|
||||||
|
setting, err := settingService.GetFileHistorySettingInfo()
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Update file history setting
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.FileHistorySettingUpdate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /settings/file-history/update [post]
|
||||||
|
func (b *BaseApi) UpdateFileHistorySetting(c *gin.Context) {
|
||||||
|
var req request.FileHistorySettingUpdate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := settingService.UpdateFileHistorySetting(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Load website dir
|
||||||
|
// @Success 200 {string} path
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /settings/website/dir [get]
|
||||||
|
func (b *BaseApi) LoadWebsiteDir(c *gin.Context) {
|
||||||
|
helper.SuccessWithData(c, settingService.GetWebsiteDir())
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags System Setting
|
// @Tags System Setting
|
||||||
// @Summary Load local backup dir
|
// @Summary Load local backup dir
|
||||||
// @Success 200 {string} path
|
// @Success 200 {string} path
|
||||||
@@ -122,6 +194,12 @@ func (b *BaseApi) LoadLocalConn(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, settingService.GetLocalConn())
|
helper.SuccessWithData(c, settingService.GetLocalConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Check local conn
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /settings/ssh/check [post]
|
||||||
func (b *BaseApi) CheckLocalConn(c *gin.Context) {
|
func (b *BaseApi) CheckLocalConn(c *gin.Context) {
|
||||||
client, err := loadLocalConn()
|
client, err := loadLocalConn()
|
||||||
if err == nil && client != nil {
|
if err == nil && client != nil {
|
||||||
@@ -137,7 +215,7 @@ func (b *BaseApi) CheckLocalConn(c *gin.Context) {
|
|||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /settings/ssh/conn/default [post]
|
// @Router /settings/ssh/default [post]
|
||||||
// @x-panel-log {"bodyKeys":["defaultConn"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"本地终端默认连接 [defaultConn]","formatEN":"update system default conn [defaultConn]"}
|
// @x-panel-log {"bodyKeys":["defaultConn"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"本地终端默认连接 [defaultConn]","formatEN":"update system default conn [defaultConn]"}
|
||||||
func (b *BaseApi) SetDefaultIsConn(c *gin.Context) {
|
func (b *BaseApi) SetDefaultIsConn(c *gin.Context) {
|
||||||
var req dto.SSHDefaultConn
|
var req dto.SSHDefaultConn
|
||||||
@@ -187,12 +265,8 @@ func (b *BaseApi) SaveLocalConn(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadLocalConn() (*ssh.SSHClient, error) {
|
func loadLocalConn() (*ssh.SSHClient, error) {
|
||||||
connInfoInDB := settingService.GetSettingByKey("LocalSSHConn")
|
connInDB, err := settingService.GetLocalConnForSSH()
|
||||||
if len(connInfoInDB) == 0 {
|
if err != nil {
|
||||||
return nil, errors.New("no such ssh conn info in db!")
|
|
||||||
}
|
|
||||||
var connInDB model.LocalConnInfo
|
|
||||||
if err := json.Unmarshal([]byte(connInfoInDB), &connInDB); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sshInfo := ssh.ConnInfo{
|
sshInfo := ssh.ConnInfo{
|
||||||
@@ -207,23 +281,6 @@ func loadLocalConn() (*ssh.SSHClient, error) {
|
|||||||
return ssh.NewClient(sshInfo)
|
return ssh.NewClient(sshInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags System Setting
|
|
||||||
// @Summary Load system setting by key
|
|
||||||
// @Param key path string true "key"
|
|
||||||
// @Success 200 {object} dto.SettingInfo
|
|
||||||
// @Security ApiKeyAuth
|
|
||||||
// @Security Timestamp
|
|
||||||
// @Router /settings/get/{key} [get]
|
|
||||||
func (b *BaseApi) GetSettingByKey(c *gin.Context) {
|
|
||||||
key := c.Param("key")
|
|
||||||
if len(key) == 0 {
|
|
||||||
helper.BadRequest(c, errors.New("key is empty"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value := settingService.GetSettingByKey(key)
|
|
||||||
helper.SuccessWithData(c, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Tags System Setting
|
// @Tags System Setting
|
||||||
// @Summary Save common description
|
// @Summary Save common description
|
||||||
// @Accept json
|
// @Accept json
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package v2
|
|||||||
import (
|
import (
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,6 +32,31 @@ func (b *BaseApi) PageTasks(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags TaskLog
|
||||||
|
// @Summary Read task log by Line
|
||||||
|
// @Param request body request.TaskLogReadReq true "request"
|
||||||
|
// @Success 200 {object} response.FileLineContent
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /logs/tasks/read [post]
|
||||||
|
func (b *BaseApi) ReadTaskLogByLine(c *gin.Context) {
|
||||||
|
var req request.TaskLogReadReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var res *response.FileLineContent
|
||||||
|
res, err := taskService.ReadByLine(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.TotalLines > 100 {
|
||||||
|
helper.SuccessWithDataGzipped(c, res)
|
||||||
|
} else {
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags TaskLog
|
// @Tags TaskLog
|
||||||
// @Summary Get the number of executing tasks
|
// @Summary Get the number of executing tasks
|
||||||
// @Success 200 {integer} int64
|
// @Success 200 {integer} int64
|
||||||
|
|||||||
@@ -6,6 +6,14 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// @Tags TensorRT LLM
|
||||||
|
// @Summary Page TensorRT LLMs
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.TensorRTLLMSearch true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/tensorrt/search [post]
|
||||||
func (b *BaseApi) PageTensorRTLLMs(c *gin.Context) {
|
func (b *BaseApi) PageTensorRTLLMs(c *gin.Context) {
|
||||||
var req request.TensorRTLLMSearch
|
var req request.TensorRTLLMSearch
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -15,6 +23,15 @@ func (b *BaseApi) PageTensorRTLLMs(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, list)
|
helper.SuccessWithData(c, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags TensorRT LLM
|
||||||
|
// @Summary Create TensorRT LLM
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.TensorRTLLMCreate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/tensorrt/create [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 TensorRT LLM [name]","formatEN":"create TensorRT LLM [name]"}
|
||||||
func (b *BaseApi) CreateTensorRTLLM(c *gin.Context) {
|
func (b *BaseApi) CreateTensorRTLLM(c *gin.Context) {
|
||||||
var req request.TensorRTLLMCreate
|
var req request.TensorRTLLMCreate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -28,6 +45,15 @@ func (b *BaseApi) CreateTensorRTLLM(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags TensorRT LLM
|
||||||
|
// @Summary Update TensorRT LLM
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.TensorRTLLMUpdate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/tensorrt/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id","name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 TensorRT LLM [id][name]","formatEN":"update TensorRT LLM [id][name]"}
|
||||||
func (b *BaseApi) UpdateTensorRTLLM(c *gin.Context) {
|
func (b *BaseApi) UpdateTensorRTLLM(c *gin.Context) {
|
||||||
var req request.TensorRTLLMUpdate
|
var req request.TensorRTLLMUpdate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -41,6 +67,15 @@ func (b *BaseApi) UpdateTensorRTLLM(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags TensorRT LLM
|
||||||
|
// @Summary Delete TensorRT LLM
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.TensorRTLLMDelete true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/tensorrt/delete [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除 TensorRT LLM [id]","formatEN":"delete TensorRT LLM [id]"}
|
||||||
func (b *BaseApi) DeleteTensorRTLLM(c *gin.Context) {
|
func (b *BaseApi) DeleteTensorRTLLM(c *gin.Context) {
|
||||||
var req request.TensorRTLLMDelete
|
var req request.TensorRTLLMDelete
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -54,6 +89,15 @@ func (b *BaseApi) DeleteTensorRTLLM(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags TensorRT LLM
|
||||||
|
// @Summary Operate TensorRT LLM
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.TensorRTLLMOperate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/tensorrt/operate [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id","operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"操作 TensorRT LLM [id][operate]","formatEN":"operate TensorRT LLM [id][operate]"}
|
||||||
func (b *BaseApi) OperateTensorRTLLM(c *gin.Context) {
|
func (b *BaseApi) OperateTensorRTLLM(c *gin.Context) {
|
||||||
var req request.TensorRTLLMOperate
|
var req request.TensorRTLLMOperate
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
+155
-106
@@ -8,7 +8,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/service"
|
"github.com/1Panel-dev/1Panel/agent/app/service"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
@@ -19,117 +21,52 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *BaseApi) WsSSH(c *gin.Context) {
|
// @Tags Terminal
|
||||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
// @Summary Ws local terminal
|
||||||
if err != nil {
|
// @Param command query string false "command"
|
||||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
// @Success 200
|
||||||
return
|
// @Security ApiKeyAuth
|
||||||
}
|
// @Security Timestamp
|
||||||
defer wsConn.Close()
|
// @Router /hosts/terminal/local [get]
|
||||||
|
func (b *BaseApi) WsLocalTerminal(c *gin.Context) {
|
||||||
if global.CONF.Base.IsDemo {
|
b.runSSHSession(c, loadLocalConn, c.DefaultQuery("command", ""))
|
||||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hostID, _ := strconv.Atoi(c.DefaultQuery("id", "0"))
|
|
||||||
var client *ssh.SSHClient
|
|
||||||
if hostID > 0 {
|
|
||||||
host, err := service.GetHostInfo(uint(hostID))
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "load host info by id failed")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
connInfo := ssh.ConnInfo{
|
|
||||||
Addr: host.Addr,
|
|
||||||
Port: int(host.Port),
|
|
||||||
User: host.User,
|
|
||||||
AuthMode: host.AuthMode,
|
|
||||||
Password: host.Password,
|
|
||||||
PrivateKey: []byte(host.PrivateKey),
|
|
||||||
}
|
|
||||||
if len(host.PassPhrase) != 0 {
|
|
||||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
|
||||||
}
|
|
||||||
client, err = ssh.NewClient(connInfo)
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
client, err = loadLocalConn()
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer client.Close()
|
|
||||||
command := c.DefaultQuery("command", "")
|
|
||||||
sws, err := terminal.NewLogicSshWsSession(cols, rows, client.Client, wsConn, command)
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer sws.Close()
|
|
||||||
|
|
||||||
quitChan := make(chan bool, 3)
|
|
||||||
sws.Start(quitChan)
|
|
||||||
go sws.Wait(quitChan)
|
|
||||||
|
|
||||||
<-quitChan
|
|
||||||
|
|
||||||
dt := time.Now().Add(time.Second)
|
|
||||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
// @Tags Terminal
|
||||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
// @Summary Ws host SSH
|
||||||
if err != nil {
|
// @Param id query integer false "id"
|
||||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
// @Param command query string false "command"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/terminal/ssh [get]
|
||||||
|
func (b *BaseApi) WsHostSSH(c *gin.Context) {
|
||||||
|
b.runSSHSession(c, func() (*ssh.SSHClient, error) {
|
||||||
|
hostID, _ := strconv.Atoi(c.DefaultQuery("id", "0"))
|
||||||
|
if hostID <= 0 {
|
||||||
|
return nil, errors.New("missing host id")
|
||||||
|
}
|
||||||
|
host, err := service.GetHostInfo(uint(hostID))
|
||||||
|
return newHostSSHClient(host, err)
|
||||||
|
}, c.DefaultQuery("command", ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Terminal
|
||||||
|
// @Summary Ws container terminal
|
||||||
|
// @Param cols query integer false "cols"
|
||||||
|
// @Param rows query integer false "rows"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/terminal/container [get]
|
||||||
|
func (b *BaseApi) WsContainerTerminal(c *gin.Context) {
|
||||||
|
wsConn, cols, rows, ok := prepareTerminalSession(c)
|
||||||
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer wsConn.Close()
|
defer wsConn.Close()
|
||||||
|
|
||||||
if global.CONF.Base.IsDemo {
|
slave, err := loadContainerTerminalCommand(c)
|
||||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
source := c.Query("source")
|
|
||||||
var initCmd []string
|
|
||||||
switch source {
|
|
||||||
case "redis", "redis-cluster":
|
|
||||||
initCmd, err = loadRedisInitCmd(c, source)
|
|
||||||
case "ollama":
|
|
||||||
initCmd, err = loadOllamaInitCmd(c)
|
|
||||||
case "container":
|
|
||||||
initCmd, err = loadContainerInitCmd(c)
|
|
||||||
case "database":
|
|
||||||
initCmd, err = loadDatabaseInitCmd(c)
|
|
||||||
default:
|
|
||||||
if wshandleError(wsConn, fmt.Errorf("not support such source %s", source)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
slave, err := terminal.NewCommand("docker", initCmd...)
|
|
||||||
if wshandleError(wsConn, err) {
|
if wshandleError(wsConn, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -147,10 +84,112 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
|||||||
<-quitChan
|
<-quitChan
|
||||||
|
|
||||||
global.LOG.Info("websocket finished")
|
global.LOG.Info("websocket finished")
|
||||||
|
closeTerminalConn(wsConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareTerminalSession(c *gin.Context) (*websocket.Conn, int, int, bool) {
|
||||||
|
if !websocket.IsWebSocketUpgrade(c.Request) {
|
||||||
|
helper.Success(c)
|
||||||
|
return nil, 0, 0, false
|
||||||
|
}
|
||||||
|
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||||
|
return nil, 0, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if global.CONF.Base.IsDemo {
|
||||||
|
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||||
|
return nil, 0, 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||||
|
return nil, 0, 0, false
|
||||||
|
}
|
||||||
|
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||||
|
return nil, 0, 0, false
|
||||||
|
}
|
||||||
|
return wsConn, cols, rows, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) runSSHSession(c *gin.Context, connect func() (*ssh.SSHClient, error), command string) {
|
||||||
|
wsConn, cols, rows, ok := prepareTerminalSession(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer wsConn.Close()
|
||||||
|
|
||||||
|
client, clientErr := connect()
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(clientErr, "failed to set up the connection. Please check the host information")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sws, err := terminal.NewLogicSshWsSession(cols, rows, client.Client, wsConn, command)
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer sws.Close()
|
||||||
|
|
||||||
|
quitChan := make(chan bool, 3)
|
||||||
|
sws.Start(quitChan)
|
||||||
|
go sws.Wait(quitChan)
|
||||||
|
|
||||||
|
<-quitChan
|
||||||
|
|
||||||
|
closeTerminalConn(wsConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeTerminalConn(wsConn *websocket.Conn) {
|
||||||
dt := time.Now().Add(time.Second)
|
dt := time.Now().Add(time.Second)
|
||||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newHostSSHClient(host *model.Host, err error) (*ssh.SSHClient, error) {
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithMessage(err, "load host info by id failed")
|
||||||
|
}
|
||||||
|
connInfo := ssh.ConnInfo{
|
||||||
|
Addr: host.Addr,
|
||||||
|
Port: int(host.Port),
|
||||||
|
User: host.User,
|
||||||
|
AuthMode: host.AuthMode,
|
||||||
|
Password: host.Password,
|
||||||
|
PrivateKey: []byte(host.PrivateKey),
|
||||||
|
}
|
||||||
|
if len(host.PassPhrase) != 0 {
|
||||||
|
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||||
|
}
|
||||||
|
return ssh.NewClient(connInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadContainerTerminalCommand(c *gin.Context) (*terminal.LocalCommand, error) {
|
||||||
|
source := c.Query("source")
|
||||||
|
var (
|
||||||
|
initCmd []string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
switch source {
|
||||||
|
case "redis", "redis-cluster":
|
||||||
|
initCmd, err = loadRedisInitCmd(c, source)
|
||||||
|
case "ollama":
|
||||||
|
initCmd, err = loadOllamaInitCmd(c)
|
||||||
|
case "container":
|
||||||
|
initCmd, err = loadContainerInitCmd(c)
|
||||||
|
case "database":
|
||||||
|
initCmd, err = loadDatabaseInitCmd(c)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("not support such source %s", source)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return terminal.NewCommand("docker", initCmd...)
|
||||||
|
}
|
||||||
|
|
||||||
func loadRedisInitCmd(c *gin.Context, redisType string) ([]string, error) {
|
func loadRedisInitCmd(c *gin.Context, redisType string) ([]string, error) {
|
||||||
name := c.Query("name")
|
name := c.Query("name")
|
||||||
from := c.Query("from")
|
from := c.Query("from")
|
||||||
@@ -213,19 +252,29 @@ func loadContainerInitCmd(c *gin.Context) ([]string, error) {
|
|||||||
func loadDatabaseInitCmd(c *gin.Context) ([]string, error) {
|
func loadDatabaseInitCmd(c *gin.Context) ([]string, error) {
|
||||||
database := c.Query("database")
|
database := c.Query("database")
|
||||||
databaseType := c.Query("databaseType")
|
databaseType := c.Query("databaseType")
|
||||||
if len(database) == 0 || len(databaseType) == 0 {
|
if len(databaseType) == 0 {
|
||||||
return nil, fmt.Errorf("error param of database: %s or database type: %s", database, databaseType)
|
return nil, fmt.Errorf("error param of database: %s or database type: %s", database, databaseType)
|
||||||
}
|
}
|
||||||
databaseConn, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Type: databaseType, Name: database})
|
databaseConn, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Type: databaseType, Name: database})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("no such database in db, err: %v", err)
|
return nil, fmt.Errorf("no such database in db, err: %v", err)
|
||||||
}
|
}
|
||||||
|
if len(databaseConn.ContainerName) == 0 {
|
||||||
|
return nil, fmt.Errorf("no such database container for database: %s or database type: %s", database, databaseType)
|
||||||
|
}
|
||||||
commands := []string{"exec", "-it", databaseConn.ContainerName}
|
commands := []string{"exec", "-it", databaseConn.ContainerName}
|
||||||
switch databaseType {
|
switch databaseType {
|
||||||
case "mysql", "mysql-cluster":
|
case "mysql", "mysql-cluster":
|
||||||
commands = append(commands, []string{"mysql", "-uroot", "-p" + databaseConn.Password}...)
|
commands = append(commands, []string{"mysql", "-uroot", "-p" + databaseConn.Password}...)
|
||||||
case "mariadb":
|
case "mariadb":
|
||||||
commands = append(commands, []string{"mariadb", "-uroot", "-p" + databaseConn.Password}...)
|
commands = append(commands, []string{"mariadb", "-uroot", "-p" + databaseConn.Password}...)
|
||||||
|
case "mongodb":
|
||||||
|
commands = append(commands, []string{
|
||||||
|
"mongosh",
|
||||||
|
"--username", databaseConn.Username,
|
||||||
|
"--password", databaseConn.Password,
|
||||||
|
"--authenticationDatabase", "admin",
|
||||||
|
}...)
|
||||||
case "postgresql", "postgresql-cluster":
|
case "postgresql", "postgresql-cluster":
|
||||||
commands = []string{"exec", "-e", fmt.Sprintf("PGPASSWORD=%s", databaseConn.Password), "-it", databaseConn.ContainerName, "psql", "-t", "-U", databaseConn.Username}
|
commands = []string{"exec", "-e", fmt.Sprintf("PGPASSWORD=%s", databaseConn.Password), "-it", databaseConn.ContainerName, "psql", "-t", "-U", databaseConn.Username}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ func (b *BaseApi) GetWebsite(c *gin.Context) {
|
|||||||
// @Success 200 {object} response.FileInfo
|
// @Success 200 {object} response.FileInfo
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
|
// @Param type path string true "type"
|
||||||
// @Router /websites/:id/config/:type [get]
|
// @Router /websites/:id/config/:type [get]
|
||||||
func (b *BaseApi) GetWebsiteNginx(c *gin.Context) {
|
func (b *BaseApi) GetWebsiteNginx(c *gin.Context) {
|
||||||
id, err := helper.GetParamID(c)
|
id, err := helper.GetParamID(c)
|
||||||
@@ -269,6 +270,7 @@ func (b *BaseApi) GetHTTPSConfig(c *gin.Context) {
|
|||||||
// @Success 200 {object} response.WebsiteHTTPS
|
// @Success 200 {object} response.WebsiteHTTPS
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
|
// @Param id path string true "id"
|
||||||
// @Router /websites/:id/https [post]
|
// @Router /websites/:id/https [post]
|
||||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] https 配置","formatEN":"Update website https [domain] conf"}
|
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] https 配置","formatEN":"Update website https [domain] conf"}
|
||||||
func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {
|
func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {
|
||||||
@@ -330,25 +332,45 @@ func (b *BaseApi) UpdateWebsiteNginxConfig(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags Website
|
// @Tags Website
|
||||||
// @Summary Operate website log
|
// @Summary Get website log
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.WebsiteLogReq true "request"
|
// @Param request body request.WebsiteLogSearchReq true "request"
|
||||||
// @Success 200 {object} response.WebsiteLog
|
// @Success 200 {object} response.WebsiteLog
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /websites/log [post]
|
// @Router /websites/log/search [post]
|
||||||
|
func (b *BaseApi) GetWebsiteLog(c *gin.Context) {
|
||||||
|
var req request.WebsiteLogSearchReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := websiteService.GetWebsiteLog(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Website
|
||||||
|
// @Summary Operate website log
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.WebsiteLogReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /websites/log/operate [post]
|
||||||
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain][operate] 日志","formatEN":"[domain][operate] logs"}
|
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain][operate] 日志","formatEN":"[domain][operate] logs"}
|
||||||
func (b *BaseApi) OpWebsiteLog(c *gin.Context) {
|
func (b *BaseApi) OpWebsiteLog(c *gin.Context) {
|
||||||
var req request.WebsiteLogReq
|
var req request.WebsiteLogReq
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res, err := websiteService.OpWebsiteLog(req)
|
if err := websiteService.OpWebsiteLog(req); err != nil {
|
||||||
if err != nil {
|
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
helper.SuccessWithData(c, res)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags Website
|
// @Tags Website
|
||||||
@@ -799,6 +821,7 @@ func (b *BaseApi) GetDirConfig(c *gin.Context) {
|
|||||||
// @Success 200 {object} response.WebsiteHtmlRes
|
// @Success 200 {object} response.WebsiteHtmlRes
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
|
// @Param type path string true "type"
|
||||||
// @Router /websites/default/html/:type [get]
|
// @Router /websites/default/html/:type [get]
|
||||||
func (b *BaseApi) GetDefaultHtml(c *gin.Context) {
|
func (b *BaseApi) GetDefaultHtml(c *gin.Context) {
|
||||||
resourceType, err := helper.GetStrParamByKey(c, "type")
|
resourceType, err := helper.GetStrParamByKey(c, "type")
|
||||||
@@ -838,11 +861,11 @@ func (b *BaseApi) UpdateDefaultHtml(c *gin.Context) {
|
|||||||
// @Tags Website
|
// @Tags Website
|
||||||
// @Summary Get website upstreams
|
// @Summary Get website upstreams
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.WebsiteCommonReq true "request"
|
// @Param id path integer true "id"
|
||||||
// @Success 200 {array} dto.NginxUpstream
|
// @Success 200 {array} dto.NginxUpstream
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /websites/lbs [get]
|
// @Router /websites/{id}/lbs [get]
|
||||||
func (b *BaseApi) GetLoadBalances(c *gin.Context) {
|
func (b *BaseApi) GetLoadBalances(c *gin.Context) {
|
||||||
id, err := helper.GetParamID(c)
|
id, err := helper.GetParamID(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -937,6 +960,15 @@ func (b *BaseApi) UpdateLoadBalanceFile(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Website
|
||||||
|
// @Summary Change website group
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.UpdateGroup true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /websites/group/change [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["group","newGroup"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"网站分组 [group] 迁移到 [newGroup]","formatEN":"change website group [group] to [newGroup]"}
|
||||||
func (b *BaseApi) ChangeWebsiteGroup(c *gin.Context) {
|
func (b *BaseApi) ChangeWebsiteGroup(c *gin.Context) {
|
||||||
var req dto.UpdateGroup
|
var req dto.UpdateGroup
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -969,6 +1001,7 @@ func (b *BaseApi) UpdateProxyCache(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Website
|
||||||
// @Summary Get website proxy cache config
|
// @Summary Get website proxy cache config
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param id path int true "id"
|
// @Param id path int true "id"
|
||||||
@@ -1295,7 +1328,7 @@ func (b *BaseApi) UpdateStreamConfig(c *gin.Context) {
|
|||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /websites/batch/https [post]
|
// @Router /websites/batch/ssl [post]
|
||||||
func (b *BaseApi) BatchSetHttps(c *gin.Context) {
|
func (b *BaseApi) BatchSetHttps(c *gin.Context) {
|
||||||
var req request.BatchWebsiteHttps
|
var req request.BatchWebsiteHttps
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
@@ -210,6 +210,27 @@ func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {
|
|||||||
helper.Success(c)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Website SSL
|
||||||
|
// @Summary Push ssl to nodes
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.WebsiteSSLPush true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /websites/ssl/push [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"推送证书到节点 [domain]","formatEN":"Push ssl to nodes [domain]"}
|
||||||
|
func (b *BaseApi) PushWebsiteSSLToNode(c *gin.Context) {
|
||||||
|
var req request.WebsiteSSLPush
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := websiteSSLService.PushToNode(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags Website SSL
|
// @Tags Website SSL
|
||||||
// @Summary Upload ssl
|
// @Summary Upload ssl
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -248,6 +269,8 @@ func (b *BaseApi) UploadSSLFile(c *gin.Context) {
|
|||||||
var req request.WebsiteSSLFileUpload
|
var req request.WebsiteSSLFileUpload
|
||||||
|
|
||||||
req.Description = c.PostForm("description")
|
req.Description = c.PostForm("description")
|
||||||
|
req.Nodes = c.PostForm("nodes")
|
||||||
|
req.PushNode, _ = strconv.ParseBool(c.PostForm("pushNode"))
|
||||||
sslID := c.PostForm("sslID")
|
sslID := c.PostForm("sslID")
|
||||||
if sslID != "" {
|
if sslID != "" {
|
||||||
req.SSLID, _ = strconv.ParseUint(sslID, 10, 64)
|
req.SSLID, _ = strconv.ParseUint(sslID, 10, 64)
|
||||||
@@ -283,6 +306,8 @@ func (b *BaseApi) UploadSSLFile(c *gin.Context) {
|
|||||||
Certificate: string(certificateContent),
|
Certificate: string(certificateContent),
|
||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
SSLID: uint(req.SSLID),
|
SSLID: uint(req.SSLID),
|
||||||
|
PushNode: req.PushNode,
|
||||||
|
Nodes: req.Nodes,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := websiteSSLService.Upload(uploadReq); err != nil {
|
if err := websiteSSLService.Upload(uploadReq); err != nil {
|
||||||
@@ -333,6 +358,15 @@ func (b *BaseApi) DownloadWebsiteSSL(c *gin.Context) {
|
|||||||
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
|
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Website SSL
|
||||||
|
// @Summary Import master SSL
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body model.WebsiteSSL true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /websites/ssl/import [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["primaryDomain"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"导入主节点证书 [primaryDomain]","formatEN":"import master SSL [primaryDomain]"}
|
||||||
func (b *BaseApi) ImportMasterSSL(c *gin.Context) {
|
func (b *BaseApi) ImportMasterSSL(c *gin.Context) {
|
||||||
var req model.WebsiteSSL
|
var req model.WebsiteSSL
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
+209
-61
@@ -3,28 +3,110 @@ package dto
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type AgentCreateReq struct {
|
type AgentCreateReq struct {
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
AppVersion string `json:"appVersion" validate:"required"`
|
AppVersion string `json:"appVersion" validate:"required"`
|
||||||
WebUIPort int `json:"webUIPort" validate:"required"`
|
WebUIPort int `json:"webUIPort" validate:"required,min=1,max=65535"`
|
||||||
BridgePort int `json:"bridgePort"`
|
BridgePort int `json:"bridgePort"`
|
||||||
AllowedOrigins []string `json:"allowedOrigins"`
|
AllowedOrigins []string `json:"allowedOrigins"`
|
||||||
AgentType string `json:"agentType" validate:"required,oneof=openclaw copaw"`
|
AgentType string `json:"agentType" validate:"required,oneof=openclaw copaw hermes-agent"`
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
AccountID uint `json:"accountId"`
|
AccountID uint `json:"accountId"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
TaskID string `json:"taskID"`
|
DashboardUsername string `json:"dashboardUsername"`
|
||||||
Advanced bool `json:"advanced"`
|
DashboardPassword string `json:"dashboardPassword"`
|
||||||
ContainerName string `json:"containerName"`
|
TaskID string `json:"taskID"`
|
||||||
AllowPort bool `json:"allowPort"`
|
Advanced bool `json:"advanced"`
|
||||||
SpecifyIP string `json:"specifyIP"`
|
ContainerName string `json:"containerName"`
|
||||||
RestartPolicy string `json:"restartPolicy"`
|
AllowPort bool `json:"allowPort"`
|
||||||
CpuQuota float64 `json:"cpuQuota"`
|
SpecifyIP string `json:"specifyIP"`
|
||||||
MemoryLimit float64 `json:"memoryLimit"`
|
RestartPolicy string `json:"restartPolicy"`
|
||||||
MemoryUnit string `json:"memoryUnit"`
|
CpuQuota float64 `json:"cpuQuota"`
|
||||||
PullImage bool `json:"pullImage"`
|
MemoryLimit float64 `json:"memoryLimit"`
|
||||||
EditCompose bool `json:"editCompose"`
|
MemoryUnit string `json:"memoryUnit"`
|
||||||
DockerCompose string `json:"dockerCompose"`
|
PullImage bool `json:"pullImage"`
|
||||||
|
EditCompose bool `json:"editCompose"`
|
||||||
|
DockerCompose string `json:"dockerCompose"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentBatchInstallReq struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
AppVersion string `json:"appVersion" validate:"required"`
|
||||||
|
WebUIPort int `json:"webUIPort" validate:"required,min=1,max=65535"`
|
||||||
|
BridgePort int `json:"bridgePort"`
|
||||||
|
AllowedOrigins []string `json:"allowedOrigins"`
|
||||||
|
AgentType string `json:"agentType" validate:"required,oneof=openclaw copaw hermes-agent"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
AccountID uint `json:"accountId"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
DashboardUsername string `json:"dashboardUsername"`
|
||||||
|
DashboardPassword string `json:"dashboardPassword"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
|
Advanced bool `json:"advanced"`
|
||||||
|
ContainerName string `json:"containerName"`
|
||||||
|
AllowPort bool `json:"allowPort"`
|
||||||
|
SpecifyIP string `json:"specifyIP"`
|
||||||
|
RestartPolicy string `json:"restartPolicy"`
|
||||||
|
CpuQuota float64 `json:"cpuQuota"`
|
||||||
|
MemoryLimit float64 `json:"memoryLimit"`
|
||||||
|
MemoryUnit string `json:"memoryUnit"`
|
||||||
|
PullImage bool `json:"pullImage"`
|
||||||
|
EditCompose bool `json:"editCompose"`
|
||||||
|
DockerCompose string `json:"dockerCompose"`
|
||||||
|
MasterAccountID uint `json:"masterAccountId"`
|
||||||
|
FallbackAccessHost string `json:"fallbackAccessHost"`
|
||||||
|
AccountSnapshot AgentAccountInfo `json:"accountSnapshot"`
|
||||||
|
AccountModels []AgentAccountModel `json:"accountModels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentBatchUpgradeReq struct {
|
||||||
|
AgentType string `json:"agentType" validate:"required,oneof=openclaw copaw hermes-agent"`
|
||||||
|
TargetVersion string `json:"targetVersion" validate:"required"`
|
||||||
|
Backup bool `json:"backup"`
|
||||||
|
PullImage bool `json:"pullImage"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentBatchUpgradeResult struct {
|
||||||
|
AgentID uint `json:"agentID"`
|
||||||
|
AgentName string `json:"agentName"`
|
||||||
|
AppInstallID uint `json:"appInstallID"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Skipped bool `json:"skipped"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentBatchSkillInstallReq struct {
|
||||||
|
AgentType string `json:"agentType" validate:"required,oneof=openclaw hermes-agent"`
|
||||||
|
SkillName string `json:"skillName" validate:"required"`
|
||||||
|
PackagePath string `json:"packagePath" validate:"required"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentBatchSkillInstallResult struct {
|
||||||
|
AgentID uint `json:"agentID"`
|
||||||
|
AgentName string `json:"agentName"`
|
||||||
|
AppInstallID uint `json:"appInstallID"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Skipped bool `json:"skipped"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentBatchOperateReq struct {
|
||||||
|
AgentType string `json:"agentType" validate:"required,oneof=openclaw copaw hermes-agent"`
|
||||||
|
Operate string `json:"operate" validate:"required,oneof=start stop restart delete"`
|
||||||
|
ForceDelete bool `json:"forceDelete"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentBatchOperateResult struct {
|
||||||
|
AgentID uint `json:"agentID"`
|
||||||
|
AgentName string `json:"agentName"`
|
||||||
|
AppInstallID uint `json:"appInstallID"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Skipped bool `json:"skipped"`
|
||||||
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentItem struct {
|
type AgentItem struct {
|
||||||
@@ -41,11 +123,14 @@ type AgentItem struct {
|
|||||||
BaseURL string `json:"baseUrl"`
|
BaseURL string `json:"baseUrl"`
|
||||||
APIKey string `json:"apiKey"`
|
APIKey string `json:"apiKey"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
|
DashboardUsername string `json:"dashboardUsername"`
|
||||||
|
DashboardPassword string `json:"dashboardPassword"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
AppInstallID uint `json:"appInstallId"`
|
AppInstallID uint `json:"appInstallId"`
|
||||||
WebsiteID uint `json:"websiteId"`
|
WebsiteID uint `json:"websiteId"`
|
||||||
WebsitePrimaryDomain string `json:"websitePrimaryDomain"`
|
WebsitePrimaryDomain string `json:"websitePrimaryDomain"`
|
||||||
|
WebsiteType string `json:"websiteType"`
|
||||||
WebsiteProtocol string `json:"websiteProtocol"`
|
WebsiteProtocol string `json:"websiteProtocol"`
|
||||||
AccountID uint `json:"accountId"`
|
AccountID uint `json:"accountId"`
|
||||||
AppVersion string `json:"appVersion"`
|
AppVersion string `json:"appVersion"`
|
||||||
@@ -91,6 +176,15 @@ type AgentModelConfig struct {
|
|||||||
Fallbacks []string `json:"fallbacks"`
|
Fallbacks []string `json:"fallbacks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentHermesChatSessionItem struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
MessageCount int64 `json:"messageCount"`
|
||||||
|
StartedAt string `json:"startedAt"`
|
||||||
|
LastActive string `json:"lastActive"`
|
||||||
|
}
|
||||||
|
|
||||||
type AgentOverviewReq struct {
|
type AgentOverviewReq struct {
|
||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
}
|
}
|
||||||
@@ -99,6 +193,17 @@ type AgentIDReq struct {
|
|||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentHermesChatSessionRenameReq struct {
|
||||||
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
|
ID string `json:"id" validate:"required"`
|
||||||
|
Title string `json:"title" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentHermesChatSessionDeleteReq struct {
|
||||||
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
|
ID string `json:"id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type AgentOverview struct {
|
type AgentOverview struct {
|
||||||
Snapshot AgentOverviewSnapshot `json:"snapshot"`
|
Snapshot AgentOverviewSnapshot `json:"snapshot"`
|
||||||
}
|
}
|
||||||
@@ -253,19 +358,24 @@ type AgentAccountSearch struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentAccountProviderCountReq struct {
|
||||||
|
Providers []string `json:"providers"`
|
||||||
|
}
|
||||||
|
|
||||||
type AgentAccountInfo struct {
|
type AgentAccountInfo struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Provider string `json:"provider"`
|
MasterAccountID uint `json:"masterAccountId"`
|
||||||
ProviderName string `json:"providerName"`
|
Provider string `json:"provider"`
|
||||||
Name string `json:"name"`
|
ProviderName string `json:"providerName"`
|
||||||
APIKey string `json:"apiKey"`
|
Name string `json:"name"`
|
||||||
RememberAPIKey bool `json:"rememberApiKey"`
|
APIKey string `json:"apiKey"`
|
||||||
BaseURL string `json:"baseUrl"`
|
RememberAPIKey bool `json:"rememberApiKey"`
|
||||||
Models []AgentAccountModel `json:"models"`
|
BaseURL string `json:"baseUrl"`
|
||||||
APIType string `json:"apiType"`
|
Models []AgentAccountModel `json:"models"`
|
||||||
Verified bool `json:"verified"`
|
APIType string `json:"apiType"`
|
||||||
Remark string `json:"remark"`
|
Verified bool `json:"verified"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
Remark string `json:"remark"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProviderModelInfo struct {
|
type ProviderModelInfo struct {
|
||||||
@@ -298,14 +408,11 @@ type AgentFeishuConfigUpdateReq struct {
|
|||||||
RequireMention string `json:"requireMention" validate:"required,oneof=true false open"`
|
RequireMention string `json:"requireMention" validate:"required,oneof=true false open"`
|
||||||
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
||||||
GroupAllowFrom []string `json:"groupAllowFrom"`
|
GroupAllowFrom []string `json:"groupAllowFrom"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
ConnectionMode string `json:"connectionMode"`
|
||||||
Bots []AgentFeishuBot `json:"bots" validate:"required,min=1"`
|
Bots []AgentFeishuBot `json:"bots" validate:"required,min=1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentFeishuPairingApproveReq struct {
|
|
||||||
AgentID uint `json:"agentId" validate:"required"`
|
|
||||||
PairingCode string `json:"pairingCode" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AgentFeishuConfig struct {
|
type AgentFeishuConfig struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
ThreadSession bool `json:"threadSession"`
|
ThreadSession bool `json:"threadSession"`
|
||||||
@@ -314,6 +421,8 @@ type AgentFeishuConfig struct {
|
|||||||
RequireMention string `json:"requireMention"`
|
RequireMention string `json:"requireMention"`
|
||||||
GroupPolicy string `json:"groupPolicy"`
|
GroupPolicy string `json:"groupPolicy"`
|
||||||
GroupAllowFrom []string `json:"groupAllowFrom"`
|
GroupAllowFrom []string `json:"groupAllowFrom"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
ConnectionMode string `json:"connectionMode"`
|
||||||
Bots []AgentFeishuBot `json:"bots"`
|
Bots []AgentFeishuBot `json:"bots"`
|
||||||
Installed bool `json:"installed"`
|
Installed bool `json:"installed"`
|
||||||
}
|
}
|
||||||
@@ -327,6 +436,7 @@ type AgentTelegramConfigUpdateReq struct {
|
|||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing open allowlist disabled"`
|
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing open allowlist disabled"`
|
||||||
AllowFrom []string `json:"allowFrom"`
|
AllowFrom []string `json:"allowFrom"`
|
||||||
|
RequireMention bool `json:"requireMention"`
|
||||||
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
||||||
GroupAllowFrom []string `json:"groupAllowFrom"`
|
GroupAllowFrom []string `json:"groupAllowFrom"`
|
||||||
Proxy string `json:"proxy"`
|
Proxy string `json:"proxy"`
|
||||||
@@ -339,6 +449,7 @@ type AgentTelegramConfig struct {
|
|||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
DmPolicy string `json:"dmPolicy"`
|
DmPolicy string `json:"dmPolicy"`
|
||||||
AllowFrom []string `json:"allowFrom"`
|
AllowFrom []string `json:"allowFrom"`
|
||||||
|
RequireMention bool `json:"requireMention"`
|
||||||
GroupPolicy string `json:"groupPolicy"`
|
GroupPolicy string `json:"groupPolicy"`
|
||||||
GroupAllowFrom []string `json:"groupAllowFrom"`
|
GroupAllowFrom []string `json:"groupAllowFrom"`
|
||||||
Proxy string `json:"proxy"`
|
Proxy string `json:"proxy"`
|
||||||
@@ -349,11 +460,16 @@ type AgentTelegramConfig struct {
|
|||||||
|
|
||||||
type AgentChannelPairingApproveReq struct {
|
type AgentChannelPairingApproveReq struct {
|
||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
Type string `json:"type" validate:"required,oneof=feishu telegram discord wecom"`
|
Type string `json:"type" validate:"required,oneof=feishu telegram discord wecom qqbot dingtalk"`
|
||||||
PairingCode string `json:"pairingCode" validate:"required"`
|
PairingCode string `json:"pairingCode" validate:"required"`
|
||||||
AccountID string `json:"accountId"`
|
AccountID string `json:"accountId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentChannelDeleteReq struct {
|
||||||
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
|
Type string `json:"type" validate:"required,oneof=feishu telegram discord wecom qqbot dingtalk weixin"`
|
||||||
|
}
|
||||||
|
|
||||||
type AgentWecomConfigUpdateReq struct {
|
type AgentWecomConfigUpdateReq struct {
|
||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
@@ -379,7 +495,7 @@ type AgentWecomConfig struct {
|
|||||||
type AgentDingTalkConfigUpdateReq struct {
|
type AgentDingTalkConfigUpdateReq struct {
|
||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
DmPolicy string `json:"dmPolicy" validate:"required,oneof=allowlist open disabled"`
|
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing allowlist open disabled"`
|
||||||
AllowFrom []string `json:"allowFrom"`
|
AllowFrom []string `json:"allowFrom"`
|
||||||
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
||||||
GroupAllowFrom []string `json:"groupAllowFrom"`
|
GroupAllowFrom []string `json:"groupAllowFrom"`
|
||||||
@@ -411,16 +527,28 @@ type AgentWeixinLoginReq struct {
|
|||||||
TaskID string `json:"taskID" validate:"required"`
|
TaskID string `json:"taskID" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentWeixinConfig struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
type AgentQQBotConfigUpdateReq struct {
|
type AgentQQBotConfigUpdateReq struct {
|
||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
Bots []AgentQQBotBot `json:"bots" validate:"required,min=1"`
|
DmPolicy string `json:"dmPolicy"`
|
||||||
|
AllowFrom []string `json:"allowFrom"`
|
||||||
|
GroupPolicy string `json:"groupPolicy"`
|
||||||
|
GroupAllowFrom []string `json:"groupAllowFrom"`
|
||||||
|
Bots []AgentQQBotBot `json:"bots" validate:"required,min=1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentQQBotConfig struct {
|
type AgentQQBotConfig struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
Bots []AgentQQBotBot `json:"bots"`
|
DmPolicy string `json:"dmPolicy"`
|
||||||
Installed bool `json:"installed"`
|
AllowFrom []string `json:"allowFrom"`
|
||||||
|
GroupPolicy string `json:"groupPolicy"`
|
||||||
|
GroupAllowFrom []string `json:"groupAllowFrom"`
|
||||||
|
Bots []AgentQQBotBot `json:"bots"`
|
||||||
|
Installed bool `json:"installed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentPluginInstallReq struct {
|
type AgentPluginInstallReq struct {
|
||||||
@@ -458,6 +586,8 @@ type AgentDiscordConfigUpdateReq struct {
|
|||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
DmPolicy string `json:"dmPolicy" validate:"required"`
|
DmPolicy string `json:"dmPolicy" validate:"required"`
|
||||||
|
AllowFrom []string `json:"allowFrom"`
|
||||||
|
RequireMention bool `json:"requireMention"`
|
||||||
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
||||||
Proxy string `json:"proxy"`
|
Proxy string `json:"proxy"`
|
||||||
DefaultAccount string `json:"defaultAccount" validate:"required"`
|
DefaultAccount string `json:"defaultAccount" validate:"required"`
|
||||||
@@ -467,6 +597,8 @@ type AgentDiscordConfigUpdateReq struct {
|
|||||||
type AgentDiscordConfig struct {
|
type AgentDiscordConfig struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
DmPolicy string `json:"dmPolicy"`
|
DmPolicy string `json:"dmPolicy"`
|
||||||
|
AllowFrom []string `json:"allowFrom"`
|
||||||
|
RequireMention bool `json:"requireMention"`
|
||||||
GroupPolicy string `json:"groupPolicy"`
|
GroupPolicy string `json:"groupPolicy"`
|
||||||
Proxy string `json:"proxy"`
|
Proxy string `json:"proxy"`
|
||||||
DefaultAccount string `json:"defaultAccount"`
|
DefaultAccount string `json:"defaultAccount"`
|
||||||
@@ -529,16 +661,20 @@ type AgentSecurityConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AgentOtherConfigUpdateReq struct {
|
type AgentOtherConfigUpdateReq struct {
|
||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
UserTimezone string `json:"userTimezone" validate:"required"`
|
UserTimezone string `json:"userTimezone" validate:"required"`
|
||||||
BrowserEnabled bool `json:"browserEnabled"`
|
BrowserEnabled bool `json:"browserEnabled"`
|
||||||
NPMRegistry string `json:"npmRegistry" validate:"required"`
|
NPMRegistry string `json:"npmRegistry" validate:"required"`
|
||||||
|
DashboardUsername string `json:"dashboardUsername"`
|
||||||
|
DashboardPassword string `json:"dashboardPassword"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentOtherConfig struct {
|
type AgentOtherConfig struct {
|
||||||
UserTimezone string `json:"userTimezone"`
|
UserTimezone string `json:"userTimezone"`
|
||||||
BrowserEnabled bool `json:"browserEnabled"`
|
BrowserEnabled bool `json:"browserEnabled"`
|
||||||
NPMRegistry string `json:"npmRegistry"`
|
NPMRegistry string `json:"npmRegistry"`
|
||||||
|
DashboardUsername string `json:"dashboardUsername"`
|
||||||
|
DashboardPassword string `json:"dashboardPassword"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentConfigFileReq struct {
|
type AgentConfigFileReq struct {
|
||||||
@@ -556,25 +692,32 @@ type AgentConfigFile struct {
|
|||||||
|
|
||||||
type AgentSkillSearchReq struct {
|
type AgentSkillSearchReq struct {
|
||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
Source string `json:"source" validate:"required,oneof=clawhub-global clawhub-cn skillhub"`
|
Source string `json:"source" validate:"required,oneof=clawhub-global clawhub-cn skillhub official skills-sh local-hub"`
|
||||||
Keyword string `json:"keyword" validate:"required"`
|
Keyword string `json:"keyword" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentSkillItem struct {
|
type AgentSkillItem struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Source string `json:"source"`
|
Category string `json:"category"`
|
||||||
Bundled bool `json:"bundled"`
|
Tags []string `json:"tags"`
|
||||||
Disabled bool `json:"disabled"`
|
Source string `json:"source"`
|
||||||
|
Trust string `json:"trust"`
|
||||||
|
Identifier string `json:"identifier"`
|
||||||
|
Bundled bool `json:"bundled"`
|
||||||
|
Disabled bool `json:"disabled"`
|
||||||
|
Uninstallable bool `json:"uninstallable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentSkillSearchItem struct {
|
type AgentSkillSearchItem struct {
|
||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
|
Identifier string `json:"identifier"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Summary string `json:"summary"`
|
Summary string `json:"summary"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
|
Trust string `json:"trust"`
|
||||||
Score string `json:"score"`
|
Score string `json:"score"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +729,12 @@ type AgentSkillUpdateReq struct {
|
|||||||
|
|
||||||
type AgentSkillInstallReq struct {
|
type AgentSkillInstallReq struct {
|
||||||
AgentID uint `json:"agentId" validate:"required"`
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
Source string `json:"source" validate:"required,oneof=clawhub-global clawhub-cn skillhub"`
|
Source string `json:"source" validate:"required,oneof=clawhub-global clawhub-cn skillhub official skills-sh local-hub"`
|
||||||
Slug string `json:"slug" validate:"required"`
|
Slug string `json:"slug" validate:"required"`
|
||||||
TaskID string `json:"taskID" validate:"required"`
|
TaskID string `json:"taskID" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentSkillUninstallReq struct {
|
||||||
|
AgentID uint `json:"agentId" validate:"required"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
}
|
||||||
|
|||||||
+19
-6
@@ -2,8 +2,9 @@ package dto
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateOrUpdateAlert struct {
|
type CreateOrUpdateAlert struct {
|
||||||
@@ -35,6 +36,15 @@ type AlertSearch struct {
|
|||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AlertConfigQuery struct {
|
||||||
|
ExcludeTypes []string `json:"excludeTypes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertConfigPageReq struct {
|
||||||
|
PageInfo
|
||||||
|
ExcludeTypes []string `json:"excludeTypes"`
|
||||||
|
}
|
||||||
|
|
||||||
type AlertDTO struct {
|
type AlertDTO struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
@@ -46,6 +56,8 @@ type AlertDTO struct {
|
|||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
SendCount uint `json:"sendCount"`
|
SendCount uint `json:"sendCount"`
|
||||||
AdvancedParams string `json:"advancedParams"`
|
AdvancedParams string `json:"advancedParams"`
|
||||||
|
CreateUser string `json:"createUser"`
|
||||||
|
UpdateUser string `json:"updateUser"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
}
|
}
|
||||||
@@ -281,11 +293,12 @@ type OfflineQueryRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AlertConfigUpdate struct {
|
type AlertConfigUpdate struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Config string `json:"config"`
|
Config string `json:"config"`
|
||||||
|
DisplayName string `json:"displayName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlertConfigTest struct {
|
type AlertConfigTest struct {
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ type Locale struct {
|
|||||||
Ko string `json:"ko"`
|
Ko string `json:"ko"`
|
||||||
Tr string `json:"tr"`
|
Tr string `json:"tr"`
|
||||||
Es string `json:"es-es" yaml:"es-es"`
|
Es string `json:"es-es" yaml:"es-es"`
|
||||||
|
Fa string `json:"fa"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppForm struct {
|
type AppForm struct {
|
||||||
|
|||||||
+19
-17
@@ -65,27 +65,29 @@ type UploadForRecover struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CommonBackup struct {
|
type CommonBackup struct {
|
||||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster container compose"`
|
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mongodb mysql-cluster postgresql-cluster redis-cluster container compose"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
DetailName string `json:"detailName"`
|
DetailName string `json:"detailName"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
StopBefore bool `json:"stopBefore"`
|
IsImmediate bool `json:"isImmediate"`
|
||||||
TaskID string `json:"taskID"`
|
StopBefore bool `json:"stopBefore"`
|
||||||
FileName string `json:"fileName"`
|
TaskID string `json:"taskID"`
|
||||||
Args []string `json:"args"`
|
FileName string `json:"fileName"`
|
||||||
|
Args []string `json:"args"`
|
||||||
|
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
type CommonRecover struct {
|
type CommonRecover struct {
|
||||||
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
||||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster container compose"`
|
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mongodb mysql-cluster postgresql-cluster redis-cluster container compose"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
DetailName string `json:"detailName"`
|
DetailName string `json:"detailName"`
|
||||||
File string `json:"file"`
|
File string `json:"file"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
TaskID string `json:"taskID"`
|
DropAllCollections bool `json:"dropAllCollections"`
|
||||||
BackupRecordID uint `json:"backupRecordID"`
|
TaskID string `json:"taskID"`
|
||||||
Timeout int `json:"timeout"`
|
BackupRecordID uint `json:"backupRecordID"`
|
||||||
|
Timeout int `json:"timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecordSearch struct {
|
type RecordSearch struct {
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ type ComposeCreate struct {
|
|||||||
type ComposeOperation struct {
|
type ComposeOperation struct {
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Operation string `json:"operation" validate:"required,oneof=up start restart stop down delete"`
|
Operation string `json:"operation" validate:"required,oneof=up start restart stop down delete rebuild"`
|
||||||
WithFile bool `json:"withFile"`
|
WithFile bool `json:"withFile"`
|
||||||
Force bool `json:"force"`
|
Force bool `json:"force"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,8 +79,9 @@ type NodeCurrent struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DashboardCurrent struct {
|
type DashboardCurrent struct {
|
||||||
Uptime uint64 `json:"uptime"`
|
Uptime uint64 `json:"uptime"`
|
||||||
TimeSinceUptime string `json:"timeSinceUptime"`
|
TimeSinceUptime string `json:"timeSinceUptime"`
|
||||||
|
RunningTime RunningTime `json:"runningTime"`
|
||||||
|
|
||||||
Procs uint64 `json:"procs"`
|
Procs uint64 `json:"procs"`
|
||||||
|
|
||||||
@@ -128,6 +129,13 @@ type DashboardCurrent struct {
|
|||||||
ShotTime time.Time `json:"shotTime"`
|
ShotTime time.Time `json:"shotTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RunningTime struct {
|
||||||
|
Days uint64 `json:"days"`
|
||||||
|
Hours uint64 `json:"hours"`
|
||||||
|
Minutes uint64 `json:"minutes"`
|
||||||
|
Seconds uint64 `json:"seconds"`
|
||||||
|
}
|
||||||
|
|
||||||
type AppLauncherSync struct {
|
type AppLauncherSync struct {
|
||||||
Keys []string `json:"keys"`
|
Keys []string `json:"keys"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ type DBConfUpdateByFile struct {
|
|||||||
type ChangeDBInfo struct {
|
type ChangeDBInfo struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
From string `json:"from" validate:"required,oneof=local remote"`
|
From string `json:"from" validate:"required,oneof=local remote"`
|
||||||
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster redis-cluster"`
|
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mongodb mysql-cluster postgresql-cluster redis-cluster"`
|
||||||
Database string `json:"database" validate:"required"`
|
Database string `json:"database" validate:"required"`
|
||||||
Value string `json:"value" validate:"required"`
|
Value string `json:"value" validate:"required"`
|
||||||
}
|
}
|
||||||
@@ -137,6 +137,83 @@ type MysqlStatus struct {
|
|||||||
Position string `json:"Position"`
|
Position string `json:"Position"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mongodb
|
||||||
|
type MongodbDBSearch struct {
|
||||||
|
PageInfo
|
||||||
|
Info string `json:"info"`
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
OrderBy string `json:"orderBy" validate:"required,oneof=name createdAt"`
|
||||||
|
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongodbDBInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
From string `json:"from"`
|
||||||
|
MongodbName string `json:"mongodbName"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
IsDelete bool `json:"isDelete"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongodbDBCreate struct {
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
From string `json:"from" validate:"required,oneof=local remote"`
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Password string `json:"password" validate:"required"`
|
||||||
|
Permission string `json:"permission" validate:"required,oneof=dbOwner read readWrite userAdmin"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongodbLoadDB struct {
|
||||||
|
From string `json:"from" validate:"required,oneof=local remote"`
|
||||||
|
Type string `json:"type" validate:"required,oneof=mongodb"`
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongodbDBDeleteCheck struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
Type string `json:"type" validate:"required,oneof=mongodb"`
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongodbDBDelete struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
Type string `json:"type" validate:"required,oneof=mongodb"`
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
ForceDelete bool `json:"forceDelete"`
|
||||||
|
DeleteBackup bool `json:"deleteBackup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongodbBind struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Password string `json:"password" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongodbPassword struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Password string `json:"password" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongodbPrivileges struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Permission string `json:"permission" validate:"required,oneof=dbOwner read readWrite userAdmin"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongodbPrivilegesLoad struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type MysqlVariables struct {
|
type MysqlVariables struct {
|
||||||
BinlogCacheSize string `json:"binlog_cache_size"`
|
BinlogCacheSize string `json:"binlog_cache_size"`
|
||||||
InnodbBufferPoolSize string `json:"innodb_buffer_pool_size"`
|
InnodbBufferPoolSize string `json:"innodb_buffer_pool_size"`
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
type SearchTaskLogReq struct {
|
type SearchTaskLogReq struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
PageInfo
|
PageInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ type AppInstallUpgrade struct {
|
|||||||
DetailID uint `json:"detailId"`
|
DetailID uint `json:"detailId"`
|
||||||
Backup bool `json:"backup"`
|
Backup bool `json:"backup"`
|
||||||
PullImage bool `json:"pullImage"`
|
PullImage bool `json:"pullImage"`
|
||||||
|
DeleteImage bool `json:"deleteImage"`
|
||||||
DockerCompose string `json:"dockerCompose"`
|
DockerCompose string `json:"dockerCompose"`
|
||||||
TaskID string `json:"taskID"`
|
TaskID string `json:"taskID"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,11 @@ type FileCompress struct {
|
|||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Replace bool `json:"replace"`
|
Replace bool `json:"replace"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileCompressStopReq struct {
|
||||||
|
TaskID string `json:"taskID" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileDeCompress struct {
|
type FileDeCompress struct {
|
||||||
@@ -85,6 +90,11 @@ type FileDeCompress struct {
|
|||||||
Type string `json:"type" validate:"required"`
|
Type string `json:"type" validate:"required"`
|
||||||
Path string `json:"path" validate:"required"`
|
Path string `json:"path" validate:"required"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileDeCompressStopReq struct {
|
||||||
|
TaskID string `json:"taskID" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileEdit struct {
|
type FileEdit struct {
|
||||||
@@ -111,6 +121,7 @@ type FileWget struct {
|
|||||||
Path string `json:"path" validate:"required"`
|
Path string `json:"path" validate:"required"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
IgnoreCertificate bool `json:"ignoreCertificate"`
|
IgnoreCertificate bool `json:"ignoreCertificate"`
|
||||||
|
UseProxy bool `json:"useProxy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileMove struct {
|
type FileMove struct {
|
||||||
@@ -152,13 +163,20 @@ type FileRoleUpdate struct {
|
|||||||
type FileReadByLineReq struct {
|
type FileReadByLineReq struct {
|
||||||
Page int `json:"page" validate:"required"`
|
Page int `json:"page" validate:"required"`
|
||||||
PageSize int `json:"pageSize" validate:"required"`
|
PageSize int `json:"pageSize" validate:"required"`
|
||||||
Type string `json:"type" validate:"required"`
|
Type string `json:"type"`
|
||||||
ID uint `json:"ID"`
|
ID uint `json:"ID"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Latest bool `json:"latest"`
|
Latest bool `json:"latest"`
|
||||||
TaskReq
|
TaskReq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TaskLogReadReq struct {
|
||||||
|
Page int `json:"page" validate:"required,min=1"`
|
||||||
|
PageSize int `json:"pageSize" validate:"required,min=1"`
|
||||||
|
Latest bool `json:"latest"`
|
||||||
|
TaskReq
|
||||||
|
}
|
||||||
|
|
||||||
type TaskReq struct {
|
type TaskReq struct {
|
||||||
TaskID string `json:"taskID"`
|
TaskID string `json:"taskID"`
|
||||||
TaskType string `json:"taskType"`
|
TaskType string `json:"taskType"`
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileHistorySearchReq struct {
|
||||||
|
dto.PageInfo
|
||||||
|
Path string `json:"path"`
|
||||||
|
Scope string `json:"scope" validate:"required,oneof=current all"`
|
||||||
|
Operation string `json:"operation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileHistoryContentReq struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileHistoryIDs []uint
|
||||||
|
|
||||||
|
func (ids *FileHistoryIDs) UnmarshalJSON(data []byte) error {
|
||||||
|
type alias []uint
|
||||||
|
var direct alias
|
||||||
|
if err := json.Unmarshal(data, &direct); err == nil {
|
||||||
|
*ids = FileHistoryIDs(direct)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrapped struct {
|
||||||
|
IDs []uint `json:"ids"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &wrapped); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*ids = FileHistoryIDs(wrapped.IDs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileHistoryDeleteReq struct {
|
||||||
|
IDs FileHistoryIDs `json:"ids" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileHistoryRestoreReq struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileHistorySettingUpdate struct {
|
||||||
|
Enable string `json:"enable" validate:"required,oneof=Enable Disable"`
|
||||||
|
MaxPerPath int `json:"maxPerPath" validate:"min=0,max=1000"`
|
||||||
|
DiskQuotaMB int `json:"diskQuotaMB" validate:"min=0,max=1048576"`
|
||||||
|
}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
package request
|
package request
|
||||||
|
|
||||||
type HostToolReq struct {
|
type HostToolTypeReq struct {
|
||||||
|
Type string `json:"type" validate:"required,oneof=supervisord"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HostToolOperateReq struct {
|
||||||
Type string `json:"type" validate:"required,oneof=supervisord"`
|
Type string `json:"type" validate:"required,oneof=supervisord"`
|
||||||
Operate string `json:"operate" validate:"oneof=status restart start stop"`
|
Operate string `json:"operate" validate:"required,oneof=restart start stop"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type HostToolCreate struct {
|
type HostToolCreate struct {
|
||||||
@@ -15,13 +19,8 @@ type SupervisorConfig struct {
|
|||||||
ServiceName string `json:"serviceName"`
|
ServiceName string `json:"serviceName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type HostToolLogReq struct {
|
type HostToolConfigUpdate struct {
|
||||||
Type string `json:"type" validate:"required,oneof=supervisord"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HostToolConfig struct {
|
|
||||||
Type string `json:"type" validate:"required,oneof=supervisord"`
|
Type string `json:"type" validate:"required,oneof=supervisord"`
|
||||||
Operate string `json:"operate" validate:"oneof=get set"`
|
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,3 +42,15 @@ type SupervisorProcessFileReq struct {
|
|||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
File string `json:"file" validate:"required,oneof=out.log err.log config"`
|
File string `json:"file" validate:"required,oneof=out.log err.log config"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HostSupervisorProcessFileGetReq struct {
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
File string `json:"file" validate:"required,oneof=out.log err.log config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HostSupervisorProcessFileOperateReq struct {
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Operate string `json:"operate" validate:"required,oneof=clear update"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
File string `json:"file" validate:"required,oneof=out.log err.log config"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ type McpServerCreate struct {
|
|||||||
StreamableHttpPath string `json:"streamableHttpPath"`
|
StreamableHttpPath string `json:"streamableHttpPath"`
|
||||||
OutputTransport string `json:"outputTransport" validate:"required"`
|
OutputTransport string `json:"outputTransport" validate:"required"`
|
||||||
Type string `json:"type" validate:"required"`
|
Type string `json:"type" validate:"required"`
|
||||||
|
GatewayImage string `json:"gatewayImage"`
|
||||||
|
ProtocolVersion string `json:"protocolVersion"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type McpServerUpdate struct {
|
type McpServerUpdate struct {
|
||||||
@@ -32,11 +35,23 @@ type McpServerDelete struct {
|
|||||||
ID uint `json:"id" validate:"required"`
|
ID uint `json:"id" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type McpServerDetail struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type McpServerStatusSync struct {
|
||||||
|
IDs []uint `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
type McpServerOperate struct {
|
type McpServerOperate struct {
|
||||||
ID uint `json:"id" validate:"required"`
|
ID uint `json:"id" validate:"required"`
|
||||||
Operate string `json:"operate" validate:"required"`
|
Operate string `json:"operate" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type McpServerConnectionTest struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type McpBindDomain struct {
|
type McpBindDomain struct {
|
||||||
Domain string `json:"domain" validate:"required"`
|
Domain string `json:"domain" validate:"required"`
|
||||||
SSLID uint `json:"sslID"`
|
SSLID uint `json:"sslID"`
|
||||||
|
|||||||
@@ -21,13 +21,14 @@ type RuntimeCreate struct {
|
|||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
CodeDir string `json:"codeDir"`
|
CodeDir string `json:"codeDir"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
|
|
||||||
Params map[string]interface{} `json:"params"`
|
Params map[string]interface{} `json:"params"`
|
||||||
NodeConfig
|
NodeConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeConfig struct {
|
type NodeConfig struct {
|
||||||
Install bool `json:"install"`
|
Install *bool `json:"install"`
|
||||||
Clean bool `json:"clean"`
|
Clean bool `json:"clean"`
|
||||||
ExposedPorts []ExposedPort `json:"exposedPorts"`
|
ExposedPorts []ExposedPort `json:"exposedPorts"`
|
||||||
Environments []Environment `json:"environments"`
|
Environments []Environment `json:"environments"`
|
||||||
@@ -42,12 +43,14 @@ type Environment struct {
|
|||||||
type Volume struct {
|
type Volume struct {
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
Target string `json:"target"`
|
Target string `json:"target"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExposedPort struct {
|
type ExposedPort struct {
|
||||||
HostPort int `json:"hostPort"`
|
HostPort int `json:"hostPort"`
|
||||||
ContainerPort int `json:"containerPort"`
|
ContainerPort int `json:"containerPort"`
|
||||||
HostIP string `json:"hostIP"`
|
HostIP string `json:"hostIP"`
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtraHost struct {
|
type ExtraHost struct {
|
||||||
@@ -56,8 +59,10 @@ type ExtraHost struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeDelete struct {
|
type RuntimeDelete struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
ForceDelete bool `json:"forceDelete"`
|
ForceDelete bool `json:"forceDelete"`
|
||||||
|
DeleteImage bool `json:"deleteImage"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeUpdate struct {
|
type RuntimeUpdate struct {
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
package request
|
|
||||||
@@ -223,9 +223,14 @@ type WebsiteNginxUpdate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteLogReq struct {
|
type WebsiteLogReq struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
Operate string `json:"operate" validate:"required,oneof=enable disable delete"`
|
||||||
|
LogType string `json:"logType" validate:"required,oneof=access.log error.log"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WebsiteLogSearchReq struct {
|
||||||
ID uint `json:"id" validate:"required"`
|
ID uint `json:"id" validate:"required"`
|
||||||
Operate string `json:"operate" validate:"required"`
|
LogType string `json:"logType" validate:"required,oneof=access.log error.log"`
|
||||||
LogType string `json:"logType" validate:"required"`
|
|
||||||
Page int `json:"page"`
|
Page int `json:"page"`
|
||||||
PageSize int `json:"pageSize"`
|
PageSize int `json:"pageSize"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ type WebsiteSSLSearch struct {
|
|||||||
dto.PageInfo
|
dto.PageInfo
|
||||||
AcmeAccountID string `json:"acmeAccountID"`
|
AcmeAccountID string `json:"acmeAccountID"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
OrderBy string `json:"orderBy" validate:"omitempty,oneof=created_at expire_date"`
|
OrderBy string `json:"orderBy" validate:"omitempty,oneof=created_at updated_at expire_date"`
|
||||||
Order string `json:"order" validate:"omitempty,oneof=null ascending descending"`
|
Order string `json:"order" validate:"omitempty,oneof=null ascending descending"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ type WebsiteSSLObtain struct {
|
|||||||
type WebsiteAcmeAccountCreate struct {
|
type WebsiteAcmeAccountCreate struct {
|
||||||
Email string `json:"email" validate:"required"`
|
Email string `json:"email" validate:"required"`
|
||||||
Type string `json:"type" validate:"required,oneof=letsencrypt zerossl buypass google custom"`
|
Type string `json:"type" validate:"required,oneof=letsencrypt zerossl buypass google custom"`
|
||||||
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
|
KeyType string `json:"keyType" validate:"required,oneof=EC256 EC384 RSA2048 RSA3072 RSA4096 RSA8192"`
|
||||||
EabKid string `json:"eabKid"`
|
EabKid string `json:"eabKid"`
|
||||||
EabHmacKey string `json:"eabHmacKey"`
|
EabHmacKey string `json:"eabHmacKey"`
|
||||||
UseProxy bool `json:"useProxy"`
|
UseProxy bool `json:"useProxy"`
|
||||||
@@ -126,6 +126,16 @@ type WebsiteSSLUpload struct {
|
|||||||
Type string `json:"type" validate:"required,oneof=paste local"`
|
Type string `json:"type" validate:"required,oneof=paste local"`
|
||||||
SSLID uint `json:"sslID"`
|
SSLID uint `json:"sslID"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
PushNode bool `json:"pushNode"`
|
||||||
|
Nodes string `json:"nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WebsiteSSLPush struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
PushNode bool `json:"pushNode"`
|
||||||
|
Nodes string `json:"nodes"`
|
||||||
|
TaskID string `json:"taskID" validate:"required"`
|
||||||
|
Sync bool `json:"sync"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteCASearch struct {
|
type WebsiteCASearch struct {
|
||||||
@@ -138,7 +148,7 @@ type WebsiteCACreate struct {
|
|||||||
Organization string `json:"organization" validate:"required"`
|
Organization string `json:"organization" validate:"required"`
|
||||||
OrganizationUint string `json:"organizationUint"`
|
OrganizationUint string `json:"organizationUint"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
|
KeyType string `json:"keyType" validate:"required,oneof=EC256 EC384 RSA2048 RSA3072 RSA4096 RSA8192"`
|
||||||
Province string `json:"province" `
|
Province string `json:"province" `
|
||||||
City string `json:"city"`
|
City string `json:"city"`
|
||||||
}
|
}
|
||||||
@@ -146,7 +156,7 @@ type WebsiteCACreate struct {
|
|||||||
type WebsiteCAObtain struct {
|
type WebsiteCAObtain struct {
|
||||||
ID uint `json:"id" validate:"required"`
|
ID uint `json:"id" validate:"required"`
|
||||||
Domains string `json:"domains" validate:"required"`
|
Domains string `json:"domains" validate:"required"`
|
||||||
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
|
KeyType string `json:"keyType" validate:"required,oneof=EC256 EC384 RSA2048 RSA3072 RSA4096 RSA8192"`
|
||||||
Time int `json:"time" validate:"required"`
|
Time int `json:"time" validate:"required"`
|
||||||
Unit string `json:"unit" validate:"required"`
|
Unit string `json:"unit" validate:"required"`
|
||||||
PushDir bool `json:"pushDir"`
|
PushDir bool `json:"pushDir"`
|
||||||
@@ -157,6 +167,8 @@ type WebsiteCAObtain struct {
|
|||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
ExecShell bool `json:"execShell"`
|
ExecShell bool `json:"execShell"`
|
||||||
Shell string `json:"shell"`
|
Shell string `json:"shell"`
|
||||||
|
PushNode bool `json:"pushNode"`
|
||||||
|
Nodes string `json:"nodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteCARenew struct {
|
type WebsiteCARenew struct {
|
||||||
@@ -167,4 +179,6 @@ type WebsiteSSLFileUpload struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
SSLID uint64 `json:"sslID"`
|
SSLID uint64 `json:"sslID"`
|
||||||
|
PushNode bool `json:"pushNode"`
|
||||||
|
Nodes string `json:"nodes"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,34 +104,36 @@ type AppDetail struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AppInstallDTO struct {
|
type AppInstallDTO struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
AppID uint `json:"appID"`
|
AppID uint `json:"appID"`
|
||||||
AppDetailID uint `json:"appDetailID"`
|
AppDetailID uint `json:"appDetailID"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
HttpPort int `json:"httpPort"`
|
HttpPort int `json:"httpPort"`
|
||||||
HttpsPort int `json:"httpsPort"`
|
HttpsPort int `json:"httpsPort"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
CanUpdate bool `json:"canUpdate"`
|
CanUpdate bool `json:"canUpdate"`
|
||||||
Icon string `json:"icon"`
|
Icon string `json:"icon"`
|
||||||
AppName string `json:"appName"`
|
AppName string `json:"appName"`
|
||||||
Ready int `json:"ready"`
|
Ready int `json:"ready"`
|
||||||
Total int `json:"total"`
|
Total int `json:"total"`
|
||||||
AppKey string `json:"appKey"`
|
AppKey string `json:"appKey"`
|
||||||
AppType string `json:"appType"`
|
AppType string `json:"appType"`
|
||||||
AppStatus string `json:"appStatus"`
|
AppStatus string `json:"appStatus"`
|
||||||
DockerCompose string `json:"dockerCompose"`
|
DockerCompose string `json:"dockerCompose"`
|
||||||
WebUI string `json:"webUI"`
|
WebUI string `json:"webUI"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
Favorite bool `json:"favorite"`
|
Favorite bool `json:"favorite"`
|
||||||
SortOrder int `json:"sortOrder"`
|
SortOrder int `json:"sortOrder"`
|
||||||
App AppDetail `json:"app"`
|
App AppDetail `json:"app"`
|
||||||
Container string `json:"container"`
|
Container string `json:"container"`
|
||||||
IsEdit bool `json:"isEdit"`
|
IsEdit bool `json:"isEdit"`
|
||||||
LinkDB bool `json:"linkDB"`
|
LinkDB bool `json:"linkDB"`
|
||||||
ServiceName string `json:"serviceName"`
|
ResourceKeys []string `json:"resourceKeys"`
|
||||||
|
ServiceName string `json:"serviceName"`
|
||||||
|
Env map[string]interface{} `json:"env"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppInstallInfo struct {
|
type AppInstallInfo struct {
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type FileHistoryInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
FileID string `json:"fileId"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
CurrentPath string `json:"currentPath"`
|
||||||
|
PreviousID uint `json:"previousId"`
|
||||||
|
SourcePath string `json:"sourcePath"`
|
||||||
|
TargetPath string `json:"targetPath"`
|
||||||
|
FileName string `json:"fileName"`
|
||||||
|
Extension string `json:"extension"`
|
||||||
|
FileMode string `json:"fileMode"`
|
||||||
|
Operation string `json:"operation"`
|
||||||
|
Deleted bool `json:"deleted"`
|
||||||
|
ContentSize int64 `json:"contentSize"`
|
||||||
|
ContentSHA string `json:"contentSHA"`
|
||||||
|
StoragePath string `json:"storagePath,omitempty"`
|
||||||
|
Content string `json:"content,omitempty"`
|
||||||
|
CurrentContent string `json:"currentContent,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileHistorySettingInfo struct {
|
||||||
|
Enable string `json:"enable"`
|
||||||
|
MaxPerPath int `json:"maxPerPath"`
|
||||||
|
DiskQuotaMB int `json:"diskQuotaMB"`
|
||||||
|
}
|
||||||
@@ -16,6 +16,12 @@ type McpServerDTO struct {
|
|||||||
Volumes []request.Volume `json:"volumes"`
|
Volumes []request.Volume `json:"volumes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type McpServerStatusDTO struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
type McpBindDomainRes struct {
|
type McpBindDomainRes struct {
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
SSLID uint `json:"sslID"`
|
SSLID uint `json:"sslID"`
|
||||||
@@ -24,3 +30,11 @@ type McpBindDomainRes struct {
|
|||||||
WebsiteID uint `json:"websiteID"`
|
WebsiteID uint `json:"websiteID"`
|
||||||
ConnUrl string `json:"connUrl"`
|
ConnUrl string `json:"connUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type McpServerConnectionTestRes struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
OutputTransport string `json:"outputTransport"`
|
||||||
|
ProtocolVersion string `json:"protocolVersion,omitempty"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ type SettingInfo struct {
|
|||||||
AppStoreLastModified string `json:"appStoreLastModified"`
|
AppStoreLastModified string `json:"appStoreLastModified"`
|
||||||
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
|
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
|
||||||
|
|
||||||
FileRecycleBin string `json:"fileRecycleBin"`
|
FileRecycleBin string `json:"fileRecycleBin"`
|
||||||
|
LocalSSHConnShow string `json:"localSSHConnShow"`
|
||||||
|
|
||||||
|
FirewallPortWhiteList string `json:"firewallPortWhiteList"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SettingUpdate struct {
|
type SettingUpdate struct {
|
||||||
@@ -31,6 +34,11 @@ type SettingUpdate struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentSettingUpdate struct {
|
||||||
|
Key string `json:"key" validate:"required,oneof=SystemIP DockerSockPath FileRecycleBin FirewallPortWhiteList"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
type SyncTime struct {
|
type SyncTime struct {
|
||||||
NtpSite string `json:"ntpSite" validate:"required"`
|
NtpSite string `json:"ntpSite" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,8 +57,10 @@ type SSHConfUpdate struct {
|
|||||||
}
|
}
|
||||||
type SearchSSHLog struct {
|
type SearchSSHLog struct {
|
||||||
PageInfo
|
PageInfo
|
||||||
Info string `json:"info"`
|
Info string `json:"info"`
|
||||||
Status string `json:"Status" validate:"required,oneof=Success Failed All"`
|
Status string `json:"Status" validate:"required,oneof=Success Failed All"`
|
||||||
|
StartTime time.Time `json:"startTime"`
|
||||||
|
EndTime time.Time `json:"endTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SSHHistory struct {
|
type SSHHistory struct {
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ package model
|
|||||||
|
|
||||||
type AgentAccount struct {
|
type AgentAccount struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
APIKey string `json:"apiKey"`
|
APIKey string `json:"apiKey"`
|
||||||
BaseURL string `json:"baseUrl"`
|
BaseURL string `json:"baseUrl"`
|
||||||
APIType string `json:"apiType"`
|
APIType string `json:"apiType"`
|
||||||
RememberAPIKey bool `json:"rememberApiKey"`
|
RememberAPIKey bool `json:"rememberApiKey"`
|
||||||
Verified bool `json:"verified"`
|
Verified bool `json:"verified"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
|
MasterAccountID uint `json:"masterAccountId" gorm:"index"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (AgentAccount) TableName() string {
|
func (AgentAccount) TableName() string {
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ type Alert struct {
|
|||||||
Count uint `gorm:"type:integer;not null" json:"count"`
|
Count uint `gorm:"type:integer;not null" json:"count"`
|
||||||
Project string `gorm:"type:varchar(64)" json:"project"`
|
Project string `gorm:"type:varchar(64)" json:"project"`
|
||||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||||
Method string `gorm:"type:varchar(64);not null" json:"method"`
|
Method string `gorm:"type:text;not null" json:"method"`
|
||||||
SendCount uint `gorm:"type:integer" json:"sendCount"`
|
SendCount uint `gorm:"type:integer" json:"sendCount"`
|
||||||
AdvancedParams string `gorm:"type:longText" json:"advancedParams"`
|
AdvancedParams string `gorm:"type:longText" json:"advancedParams"`
|
||||||
|
CreateUser string `gorm:"type:varchar(256)" json:"createUser"`
|
||||||
|
UpdateUser string `gorm:"type:varchar(256)" json:"updateUser"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlertTask struct {
|
type AlertTask struct {
|
||||||
@@ -19,7 +21,7 @@ type AlertTask struct {
|
|||||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||||
Quota string `gorm:"type:varchar(64)" json:"quota"`
|
Quota string `gorm:"type:varchar(64)" json:"quota"`
|
||||||
QuotaType string `gorm:"type:varchar(64)" json:"quotaType"`
|
QuotaType string `gorm:"type:varchar(64)" json:"quotaType"`
|
||||||
Method string `gorm:"type:varchar(64);not null;default:'sms'" json:"method"`
|
Method string `gorm:"type:varchar(128);not null;default:'sms'" json:"method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlertLog struct {
|
type AlertLog struct {
|
||||||
@@ -34,15 +36,17 @@ type AlertLog struct {
|
|||||||
Message string `gorm:"type:varchar(256);" json:"message"`
|
Message string `gorm:"type:varchar(256);" json:"message"`
|
||||||
RecordId uint `gorm:"type:integer;" json:"recordId"`
|
RecordId uint `gorm:"type:integer;" json:"recordId"`
|
||||||
LicenseId string `gorm:"type:varchar(256);not null;" json:"licenseId" `
|
LicenseId string `gorm:"type:varchar(256);not null;" json:"licenseId" `
|
||||||
Method string `gorm:"type:varchar(64);not null;default:'sms'" json:"method"`
|
Method string `gorm:"type:varchar(128);not null;default:'sms'" json:"method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlertConfig struct {
|
type AlertConfig struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||||
Title string `gorm:"type:varchar(64);not null" json:"title"`
|
Title string `gorm:"type:varchar(64);not null" json:"title"`
|
||||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||||
Config string `gorm:"type:varchar(256);not null" json:"config"`
|
Config string `gorm:"type:varchar(256);not null" json:"config"`
|
||||||
|
CreateUser string `gorm:"type:varchar(256)" json:"createUser"`
|
||||||
|
UpdateUser string `gorm:"type:varchar(256)" json:"updateUser"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginLog struct {
|
type LoginLog struct {
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type DatabaseMongodb struct {
|
||||||
|
BaseModel
|
||||||
|
Name string `json:"name" gorm:"not null"`
|
||||||
|
From string `json:"from" gorm:"not null;default:local"`
|
||||||
|
MongodbName string `json:"mongodbName" gorm:"not null"`
|
||||||
|
Username string `json:"username" gorm:"not null"`
|
||||||
|
Password string `json:"password" gorm:"not null"`
|
||||||
|
IsDelete bool `json:"isDelete"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type FileHistory struct {
|
||||||
|
BaseModel
|
||||||
|
FileID string `json:"fileId" gorm:"not null;index:idx_file_history_file_id_created_at,priority:1;index:idx_file_history_file_id_deleted_created_at,priority:1"`
|
||||||
|
Path string `json:"path" gorm:"not null;index:idx_file_history_path_created_at,priority:1;index:idx_file_history_path_hash_created_at,priority:1;index:idx_file_history_related_path,priority:1"`
|
||||||
|
PathHash string `json:"pathHash" gorm:"not null;index:idx_file_history_path_created_at,priority:2;index:idx_file_history_path_hash_created_at,priority:2"`
|
||||||
|
PreviousID uint `json:"previousId" gorm:"index"`
|
||||||
|
SourcePath string `json:"sourcePath" gorm:"index:idx_file_history_related_path,priority:2"`
|
||||||
|
TargetPath string `json:"targetPath" gorm:"index:idx_file_history_related_path,priority:3"`
|
||||||
|
FileName string `json:"fileName" gorm:"not null"`
|
||||||
|
Extension string `json:"extension"`
|
||||||
|
FileMode string `json:"fileMode"`
|
||||||
|
Operation string `json:"operation" gorm:"not null;index:idx_file_history_operation_created_at,priority:1"`
|
||||||
|
Deleted bool `json:"deleted" gorm:"not null;default:false;index:idx_file_history_deleted_created_at,priority:1;index:idx_file_history_file_id_deleted_created_at,priority:2"`
|
||||||
|
ContentSize int64 `json:"contentSize" gorm:"not null"`
|
||||||
|
ContentSHA string `json:"contentSHA" gorm:"not null;index"`
|
||||||
|
StoragePath string `json:"storagePath" gorm:"not null;uniqueIndex"`
|
||||||
|
}
|
||||||
@@ -18,4 +18,6 @@ type McpServer struct {
|
|||||||
StreamableHttpPath string `json:"streamableHttpPath"`
|
StreamableHttpPath string `json:"streamableHttpPath"`
|
||||||
OutputTransport string `json:"outputTransport"`
|
OutputTransport string `json:"outputTransport"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
GatewayImage string `json:"gatewayImage"`
|
||||||
|
ProtocolVersion string `json:"protocolVersion"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ type WebsiteAcmeAccount struct {
|
|||||||
Type string `gorm:"not null;default:letsencrypt" json:"type"`
|
Type string `gorm:"not null;default:letsencrypt" json:"type"`
|
||||||
EabKid string `json:"eabKid"`
|
EabKid string `json:"eabKid"`
|
||||||
EabHmacKey string `json:"eabHmacKey"`
|
EabHmacKey string `json:"eabHmacKey"`
|
||||||
KeyType string `gorm:"not null;default:2048" json:"keyType"`
|
KeyType string `gorm:"not null;default:RSA2048" json:"keyType"`
|
||||||
UseProxy bool `gorm:"default:false" json:"useProxy"`
|
UseProxy bool `gorm:"default:false" json:"useProxy"`
|
||||||
CaDirURL string `json:"caDirURL"`
|
CaDirURL string `json:"caDirURL"`
|
||||||
UseEAB bool `json:"useEAB"`
|
UseEAB bool `json:"useEAB"`
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ type WebsiteCA struct {
|
|||||||
CSR string `gorm:"not null;" json:"csr"`
|
CSR string `gorm:"not null;" json:"csr"`
|
||||||
Name string `gorm:"not null;" json:"name"`
|
Name string `gorm:"not null;" json:"name"`
|
||||||
PrivateKey string `gorm:"not null" json:"privateKey"`
|
PrivateKey string `gorm:"not null" json:"privateKey"`
|
||||||
KeyType string `gorm:"not null;default:2048" json:"keyType"`
|
KeyType string `gorm:"not null;default:RSA2048" json:"keyType"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ var catalog = map[string]Meta{
|
|||||||
Input: []string{"text"},
|
Input: []string{"text"},
|
||||||
},
|
},
|
||||||
Models: []Model{
|
Models: []Model{
|
||||||
{ID: "deepseek/deepseek-chat", Name: "DeepSeek Chat"},
|
{ID: "deepseek/deepseek-v4-flash", Name: "deepseek-v4-flash"},
|
||||||
{ID: "deepseek/deepseek-reasoner", Name: "DeepSeek Reasoner", MaxTokens: 65536, ContextWindow: 131072, Reasoning: true},
|
{ID: "deepseek/deepseek-v4-pro", Name: "deepseek-v4-pro"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"bailian-coding-plan": {
|
"bailian-coding-plan": {
|
||||||
@@ -163,10 +163,9 @@ var catalog = map[string]Meta{
|
|||||||
Input: []string{"text"},
|
Input: []string{"text"},
|
||||||
},
|
},
|
||||||
Models: []Model{
|
Models: []Model{
|
||||||
|
{ID: "minimax/MiniMax-M3", Name: "MiniMax M3", ContextWindow: 1000000, MaxTokens: 128000, Reasoning: true, Input: []string{"text", "image"}},
|
||||||
{ID: "minimax/MiniMax-M2.7", Name: "MiniMax M2.7"},
|
{ID: "minimax/MiniMax-M2.7", Name: "MiniMax M2.7"},
|
||||||
{ID: "minimax/MiniMax-M2.7-highspeed", Name: "MiniMax M2.7 highspeed"},
|
{ID: "minimax/MiniMax-M2.7-highspeed", Name: "MiniMax M2.7 highspeed"},
|
||||||
{ID: "minimax/MiniMax-M2.5", Name: "MiniMax M2.5", Reasoning: true},
|
|
||||||
{ID: "minimax/MiniMax-M2.5-highspeed", Name: "MiniMax M2.5 highspeed"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"xiaomi": {
|
"xiaomi": {
|
||||||
|
|||||||
+17
-107
@@ -5,11 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OpenClawPatch struct {
|
|
||||||
PrimaryModel string
|
|
||||||
Models map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OpenClawProviderPatch struct {
|
type OpenClawProviderPatch struct {
|
||||||
PrimaryModel string
|
PrimaryModel string
|
||||||
ProviderKey string
|
ProviderKey string
|
||||||
@@ -20,24 +15,7 @@ type OpenClawProviderPatch struct {
|
|||||||
AuthHeader bool
|
AuthHeader bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildOpenClawPatch(provider, modelName, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) (*OpenClawPatch, error) {
|
|
||||||
providerPatch, err := BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, maxTokens, contextWindow = ResolveRuntimeParams(provider, apiType, maxTokens, contextWindow)
|
|
||||||
return newOpenClawPatch(
|
|
||||||
providerPatch,
|
|
||||||
resolveOpenClawModelName(provider, providerPatch.ModelID),
|
|
||||||
resolveOpenClawModelInput(provider, providerPatch.ModelID),
|
|
||||||
reasoning,
|
|
||||||
contextWindow,
|
|
||||||
maxTokens,
|
|
||||||
), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey string) (*OpenClawProviderPatch, error) {
|
func BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey string) (*OpenClawProviderPatch, error) {
|
||||||
modelName = strings.TrimSpace(modelName)
|
|
||||||
if modelName == "" {
|
if modelName == "" {
|
||||||
return nil, fmt.Errorf("model is required")
|
return nil, fmt.Errorf("model is required")
|
||||||
}
|
}
|
||||||
@@ -45,29 +23,29 @@ func BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey st
|
|||||||
modelID := resolveOpenClawModelID(provider, modelName)
|
modelID := resolveOpenClawModelID(provider, modelName)
|
||||||
switch provider {
|
switch provider {
|
||||||
case "deepseek":
|
case "deepseek":
|
||||||
return newOpenClawProviderPatch(modelName, "deepseek", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
return newOpenClawProviderPatch(modelName, "deepseek", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||||
case "gemini":
|
case "gemini":
|
||||||
return newOpenClawProviderPatch("google/"+modelID, "google", modelID, strings.TrimSpace(apiKey), baseURL, resolvedAPIType, false), nil
|
return newOpenClawProviderPatch("google/"+modelID, "google", modelID, apiKey, baseURL, resolvedAPIType, false), nil
|
||||||
case "moonshot", "kimi":
|
case "moonshot", "kimi":
|
||||||
return buildMoonshotProviderPatch(provider, modelName, modelID, baseURL, apiKey), nil
|
return buildMoonshotProviderPatch(provider, modelName, modelID, baseURL, apiKey), nil
|
||||||
case "bailian-coding-plan":
|
case "bailian-coding-plan":
|
||||||
return newOpenClawProviderPatch("bailian-coding-plan/"+modelID, "bailian-coding-plan", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
return newOpenClawProviderPatch("bailian-coding-plan/"+modelID, "bailian-coding-plan", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||||
case "ark-coding-plan":
|
case "ark-coding-plan":
|
||||||
return newOpenClawProviderPatch("ark-coding-plan/"+modelID, "ark-coding-plan", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
return newOpenClawProviderPatch("ark-coding-plan/"+modelID, "ark-coding-plan", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||||
case "minimax":
|
case "minimax":
|
||||||
return newOpenClawProviderPatch("minimax/"+modelID, "minimax", modelID, strings.TrimSpace(apiKey), baseURL, "anthropic-messages", true), nil
|
return newOpenClawProviderPatch("minimax/"+modelID, "minimax", modelID, apiKey, baseURL, "anthropic-messages", true), nil
|
||||||
case "xiaomi":
|
case "xiaomi":
|
||||||
return newOpenClawProviderPatch("xiaomi/"+modelID, "xiaomi", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
return newOpenClawProviderPatch("xiaomi/"+modelID, "xiaomi", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||||
case "custom", "vllm":
|
case "custom", "vllm":
|
||||||
return newOpenClawProviderPatch(provider+"/"+modelID, provider, modelID, strings.TrimSpace(apiKey), strings.TrimSpace(baseURL), resolvedAPIType, false), nil
|
return newOpenClawProviderPatch(provider+"/"+modelID, provider, modelID, apiKey, baseURL, resolvedAPIType, false), nil
|
||||||
case "ollama":
|
case "ollama":
|
||||||
return newOpenClawProviderPatch(modelName, "ollama", modelID, "ollama", strings.TrimSpace(baseURL), resolvedAPIType, false), nil
|
return newOpenClawProviderPatch(modelName, "ollama", modelID, "ollama", baseURL, resolvedAPIType, false), nil
|
||||||
case "kimi-coding":
|
case "kimi-coding":
|
||||||
return newOpenClawProviderPatch(modelName, "kimi-coding", modelID, strings.TrimSpace(apiKey), baseURL, "anthropic-messages", false), nil
|
return newOpenClawProviderPatch(modelName, "kimi-coding", modelID, apiKey, baseURL, "anthropic-messages", false), nil
|
||||||
case "zai":
|
case "zai":
|
||||||
return newOpenClawProviderPatch("zai/"+modelID, "zai", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
return newOpenClawProviderPatch("zai/"+modelID, "zai", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||||
default:
|
default:
|
||||||
return newOpenClawProviderPatch(modelName, provider, modelID, strings.TrimSpace(apiKey), baseURL, resolvedAPIType, false), nil
|
return newOpenClawProviderPatch(modelName, provider, modelID, apiKey, baseURL, resolvedAPIType, false), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +56,7 @@ func buildMoonshotProviderPatch(provider, modelName, modelID, baseURL, apiKey st
|
|||||||
providerKey = "moonshot"
|
providerKey = "moonshot"
|
||||||
primaryModel = "moonshot/" + modelID
|
primaryModel = "moonshot/" + modelID
|
||||||
}
|
}
|
||||||
return newOpenClawProviderPatch(primaryModel, providerKey, modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false)
|
return newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseURL, "openai-completions", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseURL, apiType string, authHeader bool) *OpenClawProviderPatch {
|
func newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseURL, apiType string, authHeader bool) *OpenClawProviderPatch {
|
||||||
@@ -93,84 +71,16 @@ func newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseUR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOpenClawPatch(providerPatch *OpenClawProviderPatch, modelName string, input []string, reasoning bool, contextWindow, maxTokens int) *OpenClawPatch {
|
|
||||||
model := map[string]interface{}{
|
|
||||||
"id": providerPatch.ModelID,
|
|
||||||
"name": modelName,
|
|
||||||
"reasoning": reasoning,
|
|
||||||
"input": input,
|
|
||||||
"contextWindow": contextWindow,
|
|
||||||
"maxTokens": maxTokens,
|
|
||||||
"cost": map[string]interface{}{},
|
|
||||||
}
|
|
||||||
providerConfig := map[string]interface{}{
|
|
||||||
"apiKey": providerPatch.APIKey,
|
|
||||||
"baseUrl": providerPatch.BaseURL,
|
|
||||||
"api": providerPatch.APIType,
|
|
||||||
"models": []map[string]interface{}{model},
|
|
||||||
}
|
|
||||||
if providerPatch.AuthHeader {
|
|
||||||
providerConfig["authHeader"] = true
|
|
||||||
}
|
|
||||||
return &OpenClawPatch{
|
|
||||||
PrimaryModel: providerPatch.PrimaryModel,
|
|
||||||
Models: map[string]interface{}{
|
|
||||||
"mode": "merge",
|
|
||||||
"providers": map[string]interface{}{
|
|
||||||
providerPatch.ProviderKey: providerConfig,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveOpenClawModelID(provider, modelName string) string {
|
func resolveOpenClawModelID(provider, modelName string) string {
|
||||||
if provider == "custom" || provider == "vllm" {
|
if provider == "custom" || provider == "vllm" {
|
||||||
return normalizeCustomModel(modelName)
|
target := strings.TrimLeft(modelName, "/")
|
||||||
|
if parts := strings.SplitN(target, "/", 2); len(parts) == 2 && parts[0] == "custom" {
|
||||||
|
return strings.TrimLeft(parts[1], "/")
|
||||||
|
}
|
||||||
|
return target
|
||||||
}
|
}
|
||||||
if parts := strings.SplitN(modelName, "/", 2); len(parts) == 2 {
|
if parts := strings.SplitN(modelName, "/", 2); len(parts) == 2 {
|
||||||
return parts[1]
|
return parts[1]
|
||||||
}
|
}
|
||||||
return modelName
|
return modelName
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveOpenClawModelName(provider, modelID string) string {
|
|
||||||
if item, ok := resolveOpenClawCatalogModel(provider, modelID); ok {
|
|
||||||
return item.Name
|
|
||||||
}
|
|
||||||
if provider == "kimi-coding" {
|
|
||||||
return "Kimi for Coding"
|
|
||||||
}
|
|
||||||
return modelID
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveOpenClawModelInput(provider, modelID string) []string {
|
|
||||||
if item, ok := resolveOpenClawCatalogModel(provider, modelID); ok && len(item.Input) > 0 {
|
|
||||||
return item.Input
|
|
||||||
}
|
|
||||||
if provider == "kimi-coding" {
|
|
||||||
return []string{"text", "image"}
|
|
||||||
}
|
|
||||||
return []string{"text"}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveOpenClawCatalogModel(provider, modelID string) (Model, bool) {
|
|
||||||
candidates := []string{provider + "/" + modelID}
|
|
||||||
if provider == "gemini" {
|
|
||||||
candidates = append(candidates, "google/"+modelID)
|
|
||||||
}
|
|
||||||
for _, candidate := range candidates {
|
|
||||||
if item, ok := FindModel(provider, candidate); ok {
|
|
||||||
return item, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Model{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeCustomModel(modelName string) string {
|
|
||||||
trim := strings.TrimSpace(modelName)
|
|
||||||
trim = strings.TrimLeft(trim, "/")
|
|
||||||
if parts := strings.SplitN(trim, "/", 2); len(parts) == 2 && strings.EqualFold(parts[0], "custom") {
|
|
||||||
return strings.TrimLeft(strings.TrimSpace(parts[1]), "/")
|
|
||||||
}
|
|
||||||
return trim
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ func BuildVerifyRequest(provider, baseURL, apiKey string) VerifyRequest {
|
|||||||
request.URL = base + "/v1/messages"
|
request.URL = base + "/v1/messages"
|
||||||
}
|
}
|
||||||
request.Body = mustJSON(map[string]interface{}{
|
request.Body = mustJSON(map[string]interface{}{
|
||||||
"model": "MiniMax-M2.5",
|
"model": "MiniMax-M3",
|
||||||
"max_tokens": 1,
|
"max_tokens": 1,
|
||||||
"messages": []map[string]interface{}{{
|
"messages": []map[string]interface{}{{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
import "github.com/1Panel-dev/1Panel/agent/app/model"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
type AgentAccountRepo struct{}
|
type AgentAccountRepo struct{}
|
||||||
|
|
||||||
type IAgentAccountRepo interface {
|
type IAgentAccountRepo interface {
|
||||||
Page(page, size int, opts ...DBOption) (int64, []model.AgentAccount, error)
|
Page(page, size int, opts ...DBOption) (int64, []model.AgentAccount, error)
|
||||||
GetFirst(opts ...DBOption) (*model.AgentAccount, error)
|
GetFirst(opts ...DBOption) (*model.AgentAccount, error)
|
||||||
|
WithByMasterAccountID(masterID uint) DBOption
|
||||||
Create(account *model.AgentAccount) error
|
Create(account *model.AgentAccount) error
|
||||||
Save(account *model.AgentAccount) error
|
Save(account *model.AgentAccount) error
|
||||||
DeleteByID(id uint) error
|
DeleteByID(id uint) error
|
||||||
List(opts ...DBOption) ([]model.AgentAccount, error)
|
List(opts ...DBOption) ([]model.AgentAccount, error)
|
||||||
|
CountByProviders(providers []string) (map[string]int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIAgentAccountRepo() IAgentAccountRepo {
|
func NewIAgentAccountRepo() IAgentAccountRepo {
|
||||||
@@ -34,6 +41,12 @@ func (a AgentAccountRepo) GetFirst(opts ...DBOption) (*model.AgentAccount, error
|
|||||||
return &account, nil
|
return &account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AgentAccountRepo) WithByMasterAccountID(masterID uint) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
return g.Where("master_account_id = ?", masterID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a AgentAccountRepo) Create(account *model.AgentAccount) error {
|
func (a AgentAccountRepo) Create(account *model.AgentAccount) error {
|
||||||
return getDb().Create(account).Error
|
return getDb().Create(account).Error
|
||||||
}
|
}
|
||||||
@@ -53,3 +66,49 @@ func (a AgentAccountRepo) List(opts ...DBOption) ([]model.AgentAccount, error) {
|
|||||||
}
|
}
|
||||||
return accounts, nil
|
return accounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AgentAccountRepo) CountByProviders(providers []string) (map[string]int64, error) {
|
||||||
|
normalizedProviders := normalizeProviders(providers)
|
||||||
|
counts := make(map[string]int64, len(normalizedProviders))
|
||||||
|
for _, provider := range normalizedProviders {
|
||||||
|
counts[provider] = 0
|
||||||
|
}
|
||||||
|
if len(normalizedProviders) == 0 {
|
||||||
|
return counts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type providerCount struct {
|
||||||
|
Provider string
|
||||||
|
Count int64
|
||||||
|
}
|
||||||
|
var rows []providerCount
|
||||||
|
if err := getDb().
|
||||||
|
Model(&model.AgentAccount{}).
|
||||||
|
Select("provider, COUNT(*) as count").
|
||||||
|
Where("provider IN ?", normalizedProviders).
|
||||||
|
Group("provider").
|
||||||
|
Scan(&rows).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
counts[row.Provider] = row.Count
|
||||||
|
}
|
||||||
|
return counts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeProviders(providers []string) []string {
|
||||||
|
seen := make(map[string]struct{}, len(providers))
|
||||||
|
result := make([]string, 0, len(providers))
|
||||||
|
for _, provider := range providers {
|
||||||
|
provider = strings.TrimSpace(provider)
|
||||||
|
if provider == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[provider]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[provider] = struct{}{}
|
||||||
|
result = append(result, provider)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
+235
-14
@@ -1,11 +1,15 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
"google.golang.org/genproto/googleapis/type/date"
|
"google.golang.org/genproto/googleapis/type/date"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,7 +24,8 @@ type IAlertRepo interface {
|
|||||||
WithByCreateAt(date *date.Date) DBOption
|
WithByCreateAt(date *date.Date) DBOption
|
||||||
WithByLicenseId(licenseId string) DBOption
|
WithByLicenseId(licenseId string) DBOption
|
||||||
WithByRecordId(recordId uint) DBOption
|
WithByRecordId(recordId uint) DBOption
|
||||||
WithByMethod(method string) DBOption
|
WithByAlertMethodContainsConfigID(id uint) DBOption
|
||||||
|
WithByMethodConfigIDs(ids []uint) DBOption
|
||||||
|
|
||||||
Create(alert *model.Alert) error
|
Create(alert *model.Alert) error
|
||||||
Get(opts ...DBOption) (model.Alert, error)
|
Get(opts ...DBOption) (model.Alert, error)
|
||||||
@@ -47,11 +52,15 @@ type IAlertRepo interface {
|
|||||||
GetLicensePushCount(method string) (uint, error)
|
GetLicensePushCount(method string) (uint, error)
|
||||||
|
|
||||||
GetConfig(opts ...DBOption) (model.AlertConfig, error)
|
GetConfig(opts ...DBOption) (model.AlertConfig, error)
|
||||||
|
GetConfigById(id uint) (model.AlertConfig, error)
|
||||||
AlertConfigList(opts ...DBOption) ([]model.AlertConfig, error)
|
AlertConfigList(opts ...DBOption) ([]model.AlertConfig, error)
|
||||||
UpdateAlertConfig(maps map[string]interface{}, opts ...DBOption) error
|
UpdateAlertConfig(maps map[string]interface{}, opts ...DBOption) error
|
||||||
CreateAlertConfig(config *model.AlertConfig) error
|
CreateAlertConfig(config *model.AlertConfig) error
|
||||||
DeleteAlertConfig(opts ...DBOption) error
|
DeleteAlertConfig(opts ...DBOption) error
|
||||||
|
|
||||||
|
WithByTypeNotIn(types []string) DBOption
|
||||||
|
PageAlertConfig(page, size int, opts ...DBOption) (int64, []model.AlertConfig, error)
|
||||||
|
|
||||||
SyncAll(data []model.AlertConfig) error
|
SyncAll(data []model.AlertConfig) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,9 +110,20 @@ func (a *AlertRepo) WithByRecordId(recordId uint) DBOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AlertRepo) WithByMethod(method string) DBOption {
|
func (a *AlertRepo) WithByAlertMethodContainsConfigID(id uint) DBOption {
|
||||||
|
method := strconv.Itoa(int(id))
|
||||||
return func(g *gorm.DB) *gorm.DB {
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
return g.Where("method = ?", method)
|
return g.Where("(method = ? OR method LIKE ? OR method LIKE ? OR method LIKE ?)", method, method+",%", "%,"+method, "%,"+method+",%")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AlertRepo) WithByMethodConfigIDs(ids []uint) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
methods := make([]string, 0, len(ids))
|
||||||
|
for _, id := range ids {
|
||||||
|
methods = append(methods, strconv.Itoa(int(id)))
|
||||||
|
}
|
||||||
|
return g.Where("method IN ?", methods)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,32 +326,233 @@ func (a *AlertRepo) GetConfig(opts ...DBOption) (model.AlertConfig, error) {
|
|||||||
return alertConfig, err
|
return alertConfig, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AlertRepo) GetConfigById(id uint) (model.AlertConfig, error) {
|
||||||
|
var config model.AlertConfig
|
||||||
|
err := global.AlertDB.First(&config, id).Error
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AlertRepo) WithByTypeNotIn(types []string) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
return g.Where("`type` NOT IN (?)", types)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AlertRepo) PageAlertConfig(page, size int, opts ...DBOption) (int64, []model.AlertConfig, error) {
|
||||||
|
var configs []model.AlertConfig
|
||||||
|
db := global.AlertDB.Model(&model.AlertConfig{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Limit(size).Offset(size * (page - 1)).Find(&configs).Error
|
||||||
|
return count, configs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var singletonTypes = map[string]bool{
|
||||||
|
constant.CommonConfig: true,
|
||||||
|
}
|
||||||
|
|
||||||
func (a *AlertRepo) SyncAll(data []model.AlertConfig) error {
|
func (a *AlertRepo) SyncAll(data []model.AlertConfig) error {
|
||||||
tx := global.AlertDB.Begin()
|
tx := global.AlertDB.Begin()
|
||||||
|
if tx.Error != nil {
|
||||||
|
return tx.Error
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
var oldConfigs []model.AlertConfig
|
var oldConfigs []model.AlertConfig
|
||||||
_ = tx.Find(&oldConfigs).Error
|
if err := tx.Find(&oldConfigs).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
usedConfigIDs, err := loadUsedAlertConfigIDs(tx)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
oldConfigMap := make(map[string]uint)
|
oldConfigMap := make(map[string]uint)
|
||||||
|
oldConfigByType := make(map[string][]model.AlertConfig)
|
||||||
|
oldConfigByKey := make(map[string][]model.AlertConfig)
|
||||||
|
consumedConfigIDs := make(map[uint]struct{})
|
||||||
for _, item := range oldConfigs {
|
for _, item := range oldConfigs {
|
||||||
oldConfigMap[item.Type] = item.ID
|
if singletonTypes[item.Type] {
|
||||||
|
oldConfigMap[item.Type] = item.ID
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
oldConfigByType[item.Type] = append(oldConfigByType[item.Type], item)
|
||||||
|
oldConfigByKey[alertConfigSyncKey(item)] = append(oldConfigByKey[alertConfigSyncKey(item)], item)
|
||||||
}
|
}
|
||||||
for _, item := range data {
|
for _, item := range data {
|
||||||
if val, ok := oldConfigMap[item.Type]; ok {
|
if singletonTypes[item.Type] {
|
||||||
item.ID = val
|
if val, ok := oldConfigMap[item.Type]; ok {
|
||||||
delete(oldConfigMap, item.Type)
|
item.ID = val
|
||||||
} else {
|
delete(oldConfigMap, item.Type)
|
||||||
item.ID = 0
|
consumedConfigIDs[item.ID] = struct{}{}
|
||||||
|
} else {
|
||||||
|
item.ID = 0
|
||||||
|
}
|
||||||
|
if item.ID == 0 {
|
||||||
|
if err := tx.Create(&item).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if err := tx.Save(&item).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if err := tx.Model(model.AlertConfig{}).Where("id = ?", item.ID).Save(&item).Error; err != nil {
|
|
||||||
|
key := alertConfigSyncKey(item)
|
||||||
|
if matched, ok := popAlertConfigByKey(oldConfigByKey, key); ok {
|
||||||
|
item.ID = matched.ID
|
||||||
|
consumedConfigIDs[item.ID] = struct{}{}
|
||||||
|
if err := tx.Save(&item).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
deleteAlertConfigByID(oldConfigByType, matched.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched, ok := popUnusedAlertConfigByType(oldConfigByType, usedConfigIDs, item.Type); ok {
|
||||||
|
item.ID = matched.ID
|
||||||
|
consumedConfigIDs[item.ID] = struct{}{}
|
||||||
|
if err := tx.Save(&item).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
item.ID = 0
|
||||||
|
if err := tx.Create(&item).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, val := range oldConfigMap {
|
for _, item := range oldConfigs {
|
||||||
if err := tx.Where("id = ?", val).Delete(&model.AlertConfig{}).Error; err != nil {
|
if _, used := usedConfigIDs[item.ID]; used {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, kept := consumedConfigIDs[item.ID]; kept {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := tx.Where("id = ?", item.ID).Delete(&model.AlertConfig{}).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.Commit()
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadUsedAlertConfigIDs(tx *gorm.DB) (map[uint]struct{}, error) {
|
||||||
|
var alerts []model.Alert
|
||||||
|
if err := tx.Select("method").Find(&alerts).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
usedIDs := make(map[uint]struct{})
|
||||||
|
for _, alert := range alerts {
|
||||||
|
for _, item := range strings.Split(alert.Method, ",") {
|
||||||
|
item = strings.TrimSpace(item)
|
||||||
|
if item == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
id, err := strconv.ParseUint(item, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
usedIDs[uint(id)] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return usedIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func alertConfigSyncKey(item model.AlertConfig) string {
|
||||||
|
return item.Type + "::" + normalizeAlertConfigJSON(item.Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeAlertConfigJSON(config string) string {
|
||||||
|
trimmed := strings.TrimSpace(config)
|
||||||
|
if trimmed == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var data any
|
||||||
|
if err := json.Unmarshal([]byte(trimmed), &data); err != nil {
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func popAlertConfigByKey(configMap map[string][]model.AlertConfig, key string) (model.AlertConfig, bool) {
|
||||||
|
items := configMap[key]
|
||||||
|
if len(items) == 0 {
|
||||||
|
return model.AlertConfig{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
item := items[0]
|
||||||
|
if len(items) == 1 {
|
||||||
|
delete(configMap, key)
|
||||||
|
} else {
|
||||||
|
configMap[key] = items[1:]
|
||||||
|
}
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func popUnusedAlertConfigByType(configMap map[string][]model.AlertConfig, usedConfigIDs map[uint]struct{}, configType string) (model.AlertConfig, bool) {
|
||||||
|
items := configMap[configType]
|
||||||
|
if len(items) == 0 {
|
||||||
|
return model.AlertConfig{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, item := range items {
|
||||||
|
if _, used := usedConfigIDs[item.ID]; used {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if idx == 0 {
|
||||||
|
if len(items) == 1 {
|
||||||
|
delete(configMap, configType)
|
||||||
|
} else {
|
||||||
|
configMap[configType] = items[1:]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configMap[configType] = append(items[:idx], items[idx+1:]...)
|
||||||
|
}
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
return model.AlertConfig{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteAlertConfigByID(configMap map[string][]model.AlertConfig, id uint) {
|
||||||
|
for key, items := range configMap {
|
||||||
|
for idx, item := range items {
|
||||||
|
if item.ID != id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(items) == 1 {
|
||||||
|
delete(configMap, key)
|
||||||
|
} else {
|
||||||
|
configMap[key] = append(items[:idx], items[idx+1:]...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MongodbRepo struct{}
|
||||||
|
|
||||||
|
type IMongodbRepo interface {
|
||||||
|
Get(opts ...DBOption) (model.DatabaseMongodb, error)
|
||||||
|
WithByMongodbName(mongodbName string) DBOption
|
||||||
|
List(opts ...DBOption) ([]model.DatabaseMongodb, error)
|
||||||
|
Page(limit, offset int, opts ...DBOption) (int64, []model.DatabaseMongodb, error)
|
||||||
|
Create(ctx context.Context, mongodb *model.DatabaseMongodb) error
|
||||||
|
Delete(ctx context.Context, opts ...DBOption) error
|
||||||
|
Update(id uint, vars map[string]interface{}) error
|
||||||
|
DeleteLocal(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIMongodbRepo() IMongodbRepo {
|
||||||
|
return &MongodbRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MongodbRepo) Get(opts ...DBOption) (model.DatabaseMongodb, error) {
|
||||||
|
var mongodb model.DatabaseMongodb
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
if err := db.First(&mongodb).Error; err != nil {
|
||||||
|
return mongodb, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pass, err := encrypt.StringDecrypt(mongodb.Password)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("decrypt mongodb db %s password failed, err: %v", mongodb.Name, err)
|
||||||
|
}
|
||||||
|
mongodb.Password = pass
|
||||||
|
return mongodb, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MongodbRepo) List(opts ...DBOption) ([]model.DatabaseMongodb, error) {
|
||||||
|
var mongodbs []model.DatabaseMongodb
|
||||||
|
db := global.DB.Model(&model.DatabaseMongodb{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
if err := db.Find(&mongodbs).Error; err != nil {
|
||||||
|
return mongodbs, err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(mongodbs); i++ {
|
||||||
|
pass, err := encrypt.StringDecrypt(mongodbs[i].Password)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("decrypt mongodb db %s password failed, err: %v", mongodbs[i].Name, err)
|
||||||
|
}
|
||||||
|
mongodbs[i].Password = pass
|
||||||
|
}
|
||||||
|
return mongodbs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MongodbRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabaseMongodb, error) {
|
||||||
|
var mongodbs []model.DatabaseMongodb
|
||||||
|
db := global.DB.Model(&model.DatabaseMongodb{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
if err := db.Limit(size).Offset(size * (page - 1)).Find(&mongodbs).Error; err != nil {
|
||||||
|
return count, mongodbs, err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(mongodbs); i++ {
|
||||||
|
pass, err := encrypt.StringDecrypt(mongodbs[i].Password)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("decrypt mongodb db %s password failed, err: %v", mongodbs[i].Name, err)
|
||||||
|
}
|
||||||
|
mongodbs[i].Password = pass
|
||||||
|
}
|
||||||
|
return count, mongodbs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MongodbRepo) Create(ctx context.Context, mongodb *model.DatabaseMongodb) error {
|
||||||
|
pass, err := encrypt.StringEncrypt(mongodb.Password)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("encrypt mongodb db %s password failed, err: %v", mongodb.Name, err)
|
||||||
|
}
|
||||||
|
mongodb.Password = pass
|
||||||
|
return getTx(ctx).Create(mongodb).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MongodbRepo) Delete(ctx context.Context, opts ...DBOption) error {
|
||||||
|
return getTx(ctx, opts...).Delete(&model.DatabaseMongodb{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MongodbRepo) DeleteLocal(ctx context.Context) error {
|
||||||
|
return getTx(ctx).Where("`from` = ?", "local").Delete(&model.DatabaseMongodb{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MongodbRepo) Update(id uint, vars map[string]interface{}) error {
|
||||||
|
return global.DB.Model(&model.DatabaseMongodb{}).Where("id = ?", id).Updates(vars).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MongodbRepo) WithByMongodbName(mongodbName string) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
return g.Where("mongodb_name = ?", mongodbName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileHistoryRepo struct{}
|
||||||
|
|
||||||
|
type IFileHistoryRepo interface {
|
||||||
|
Get(opts ...DBOption) (model.FileHistory, error)
|
||||||
|
Page(limit, offset int, opts ...DBOption) (int64, []model.FileHistory, error)
|
||||||
|
Create(history *model.FileHistory) error
|
||||||
|
Delete(opts ...DBOption) error
|
||||||
|
DeleteByIDs(ids []uint) error
|
||||||
|
WithByID(id uint) DBOption
|
||||||
|
WithByFileID(fileID string) DBOption
|
||||||
|
WithByPath(path string) DBOption
|
||||||
|
WithByPathHash(pathHash string) DBOption
|
||||||
|
WithByPathLike(path string) DBOption
|
||||||
|
WithByOperation(operation string) DBOption
|
||||||
|
WithNotOperation(operation string) DBOption
|
||||||
|
WithByRelatedPath(path string) DBOption
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIFileHistoryRepo() IFileHistoryRepo {
|
||||||
|
return &FileHistoryRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) Get(opts ...DBOption) (model.FileHistory, error) {
|
||||||
|
var item model.FileHistory
|
||||||
|
db := global.DB.Model(&model.FileHistory{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return item, db.First(&item).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) Page(limit, offset int, opts ...DBOption) (int64, []model.FileHistory, error) {
|
||||||
|
var total int64
|
||||||
|
var items []model.FileHistory
|
||||||
|
db := global.DB.Model(&model.FileHistory{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
if err := db.Count(&total).Error; err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
if err := db.Order("created_at desc").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
return total, items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) Create(history *model.FileHistory) error {
|
||||||
|
return global.DB.Create(history).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) Delete(opts ...DBOption) error {
|
||||||
|
db := global.DB.Model(&model.FileHistory{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return db.Delete(&model.FileHistory{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) DeleteByIDs(ids []uint) error {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return global.DB.Where("id in ?", ids).Delete(&model.FileHistory{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) WithByID(id uint) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("id = ?", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) WithByFileID(fileID string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("file_id = ?", fileID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) WithByPath(path string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("path = ?", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) WithByPathHash(pathHash string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("path_hash = ?", pathHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) WithByPathLike(path string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("path like ?", "%"+path+"%")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) WithByOperation(operation string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("operation = ?", operation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) WithNotOperation(operation string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("operation <> ?", operation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FileHistoryRepo) WithByRelatedPath(path string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("path = ? OR source_path = ? OR target_path = ?", path, path, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -165,6 +165,9 @@ func (h *HostRepo) SaveFirewallRecord(firewall *model.Firewall) error {
|
|||||||
firewall.Strategy,
|
firewall.Strategy,
|
||||||
).First(&data).Error
|
).First(&data).Error
|
||||||
}
|
}
|
||||||
|
if data.ID != 0 {
|
||||||
|
firewall.ID = data.ID
|
||||||
|
}
|
||||||
return global.DB.Save(firewall).Error
|
return global.DB.Save(firewall).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type IWebsiteRepo interface {
|
|||||||
WithGroupID(groupId uint) DBOption
|
WithGroupID(groupId uint) DBOption
|
||||||
WithDefaultServer() DBOption
|
WithDefaultServer() DBOption
|
||||||
WithDomainLike(domain string) DBOption
|
WithDomainLike(domain string) DBOption
|
||||||
|
WithSearchKeyword(keyword string, ids []uint) DBOption
|
||||||
WithRuntimeID(runtimeID uint) DBOption
|
WithRuntimeID(runtimeID uint) DBOption
|
||||||
WithParentID(websiteID uint) DBOption
|
WithParentID(websiteID uint) DBOption
|
||||||
WithType(websiteType string) DBOption
|
WithType(websiteType string) DBOption
|
||||||
@@ -76,6 +77,19 @@ func (w *WebsiteRepo) WithDomainLike(domain string) DBOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WebsiteRepo) WithSearchKeyword(keyword string, ids []uint) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
if keyword == "" {
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
keyword = "%" + keyword + "%"
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return db.Where("(primary_domain like ? OR alias like ?)", keyword, keyword)
|
||||||
|
}
|
||||||
|
return db.Where("(primary_domain like ? OR alias like ? OR id in (?))", keyword, keyword, ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WebsiteRepo) WithAlias(alias string) DBOption {
|
func (w *WebsiteRepo) WithAlias(alias string) DBOption {
|
||||||
return func(db *gorm.DB) *gorm.DB {
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
return db.Where("alias = ?", alias)
|
return db.Where("alias = ?", alias)
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package repo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ type ISSLRepo interface {
|
|||||||
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
|
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
|
||||||
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
|
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
|
||||||
List(opts ...DBOption) ([]model.WebsiteSSL, error)
|
List(opts ...DBOption) ([]model.WebsiteSSL, error)
|
||||||
|
TryMarkApplying(id uint) (bool, error)
|
||||||
Create(ctx context.Context, ssl *model.WebsiteSSL) error
|
Create(ctx context.Context, ssl *model.WebsiteSSL) error
|
||||||
Save(ssl *model.WebsiteSSL) error
|
Save(ssl *model.WebsiteSSL) error
|
||||||
DeleteBy(opts ...DBOption) error
|
DeleteBy(opts ...DBOption) error
|
||||||
@@ -76,12 +79,12 @@ func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.W
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
|
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
|
||||||
var website *model.WebsiteSSL
|
var website model.WebsiteSSL
|
||||||
db := getDb(opts...).Model(&model.WebsiteSSL{})
|
db := getDb(opts...).Model(&model.WebsiteSSL{})
|
||||||
if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil {
|
if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil {
|
||||||
return website, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return website, nil
|
return &website, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {
|
func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {
|
||||||
@@ -93,14 +96,25 @@ func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {
|
|||||||
return websites, nil
|
return websites, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w WebsiteSSLRepo) TryMarkApplying(id uint) (bool, error) {
|
||||||
|
db := getDb().Model(&model.WebsiteSSL{}).
|
||||||
|
Where("id = ? AND status <> ?", id, constant.SSLApply).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"status": constant.SSLApply,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
})
|
||||||
|
if db.Error != nil {
|
||||||
|
return false, db.Error
|
||||||
|
}
|
||||||
|
return db.RowsAffected > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error {
|
func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error {
|
||||||
return getTx(ctx).Create(ssl).Error
|
return getTx(ctx).Create(ssl).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
|
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
|
||||||
return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{
|
return getDb().Save(ssl).Error
|
||||||
ID: ssl.ID,
|
|
||||||
}}).Save(&ssl).Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error {
|
func (w WebsiteSSLRepo) SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error {
|
||||||
|
|||||||
+646
-61
@@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@@ -16,6 +17,7 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
providercatalog "github.com/1Panel-dev/1Panel/agent/app/provider"
|
providercatalog "github.com/1Panel-dev/1Panel/agent/app/provider"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
@@ -24,19 +26,28 @@ import (
|
|||||||
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
|
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IAgentService interface {
|
type IAgentService interface {
|
||||||
Create(req dto.AgentCreateReq) (*dto.AgentItem, error)
|
Create(req dto.AgentCreateReq) (*dto.AgentItem, error)
|
||||||
|
BatchInstall(req dto.AgentBatchInstallReq) (*dto.AgentItem, error)
|
||||||
|
BatchUpgrade(req dto.AgentBatchUpgradeReq) ([]dto.AgentBatchUpgradeResult, error)
|
||||||
|
BatchInstallSkill(req dto.AgentBatchSkillInstallReq) ([]dto.AgentBatchSkillInstallResult, error)
|
||||||
|
BatchOperate(req dto.AgentBatchOperateReq) ([]dto.AgentBatchOperateResult, error)
|
||||||
Page(req dto.SearchWithPage) (int64, []dto.AgentItem, error)
|
Page(req dto.SearchWithPage) (int64, []dto.AgentItem, error)
|
||||||
DeleteCheck(req dto.AgentIDReq) ([]dto.AppResource, error)
|
DeleteCheck(req dto.AgentIDReq) ([]dto.AppResource, error)
|
||||||
Delete(req dto.AgentDeleteReq) error
|
Delete(req dto.AgentDeleteReq) error
|
||||||
ResetToken(req dto.AgentTokenResetReq) error
|
ResetToken(req dto.AgentTokenResetReq) error
|
||||||
UpdateRemark(req dto.AgentRemarkUpdateReq) error
|
UpdateRemark(req dto.AgentRemarkUpdateReq) error
|
||||||
BindWebsite(req dto.AgentWebsiteBindReq) error
|
BindWebsite(req dto.AgentWebsiteBindReq) error
|
||||||
|
UnbindWebsite(req dto.AgentIDReq) error
|
||||||
GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig, error)
|
GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig, error)
|
||||||
UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
|
UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
|
||||||
|
GetHermesChatSessions(req dto.AgentIDReq) ([]dto.AgentHermesChatSessionItem, error)
|
||||||
|
RenameHermesChatSession(req dto.AgentHermesChatSessionRenameReq) error
|
||||||
|
DeleteHermesChatSession(req dto.AgentHermesChatSessionDeleteReq) error
|
||||||
GetOverview(req dto.AgentOverviewReq) (*dto.AgentOverview, error)
|
GetOverview(req dto.AgentOverviewReq) (*dto.AgentOverview, error)
|
||||||
GetProviders() ([]dto.ProviderInfo, error)
|
GetProviders() ([]dto.ProviderInfo, error)
|
||||||
GetSecurityConfig(req dto.AgentIDReq) (*dto.AgentSecurityConfig, error)
|
GetSecurityConfig(req dto.AgentIDReq) (*dto.AgentSecurityConfig, error)
|
||||||
@@ -49,6 +60,7 @@ type IAgentService interface {
|
|||||||
SearchSkills(req dto.AgentSkillSearchReq) ([]dto.AgentSkillSearchItem, error)
|
SearchSkills(req dto.AgentSkillSearchReq) ([]dto.AgentSkillSearchItem, error)
|
||||||
UpdateSkill(req dto.AgentSkillUpdateReq) error
|
UpdateSkill(req dto.AgentSkillUpdateReq) error
|
||||||
InstallSkill(req dto.AgentSkillInstallReq) error
|
InstallSkill(req dto.AgentSkillInstallReq) error
|
||||||
|
UninstallSkill(req dto.AgentSkillUninstallReq) error
|
||||||
|
|
||||||
CreateRole(req dto.AgentRoleCreateReq) (*dto.AgentRoleCreateResp, error)
|
CreateRole(req dto.AgentRoleCreateReq) (*dto.AgentRoleCreateResp, error)
|
||||||
DeleteRole(req dto.AgentRoleDeleteReq) error
|
DeleteRole(req dto.AgentRoleDeleteReq) error
|
||||||
@@ -63,6 +75,7 @@ type IAgentService interface {
|
|||||||
UpdateAccount(req dto.AgentAccountUpdateReq) error
|
UpdateAccount(req dto.AgentAccountUpdateReq) error
|
||||||
SyncAgentsByAccount(account *model.AgentAccount) error
|
SyncAgentsByAccount(account *model.AgentAccount) error
|
||||||
PageAccounts(req dto.AgentAccountSearch) (int64, []dto.AgentAccountInfo, error)
|
PageAccounts(req dto.AgentAccountSearch) (int64, []dto.AgentAccountInfo, error)
|
||||||
|
CountAccountsByProviders(req dto.AgentAccountProviderCountReq) (map[string]int64, error)
|
||||||
GetAccountModels(req dto.AgentAccountModelReq) ([]dto.AgentAccountModel, error)
|
GetAccountModels(req dto.AgentAccountModelReq) ([]dto.AgentAccountModel, error)
|
||||||
CreateAccountModel(req dto.AgentAccountModelCreateReq) error
|
CreateAccountModel(req dto.AgentAccountModelCreateReq) error
|
||||||
UpdateAccountModel(req dto.AgentAccountModelUpdateReq) error
|
UpdateAccountModel(req dto.AgentAccountModelUpdateReq) error
|
||||||
@@ -80,9 +93,11 @@ type IAgentService interface {
|
|||||||
UpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error
|
UpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error
|
||||||
GetDingTalkConfig(req dto.AgentIDReq) (*dto.AgentDingTalkConfig, error)
|
GetDingTalkConfig(req dto.AgentIDReq) (*dto.AgentDingTalkConfig, error)
|
||||||
UpdateDingTalkConfig(req dto.AgentDingTalkConfigUpdateReq) error
|
UpdateDingTalkConfig(req dto.AgentDingTalkConfigUpdateReq) error
|
||||||
|
GetWeixinConfig(req dto.AgentIDReq) (*dto.AgentWeixinConfig, error)
|
||||||
LoginWeixinChannel(req dto.AgentWeixinLoginReq) error
|
LoginWeixinChannel(req dto.AgentWeixinLoginReq) error
|
||||||
GetQQBotConfig(req dto.AgentIDReq) (*dto.AgentQQBotConfig, error)
|
GetQQBotConfig(req dto.AgentIDReq) (*dto.AgentQQBotConfig, error)
|
||||||
UpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error
|
UpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error
|
||||||
|
DeleteChannelConfig(req dto.AgentChannelDeleteReq) error
|
||||||
InstallPlugin(req dto.AgentPluginInstallReq) error
|
InstallPlugin(req dto.AgentPluginInstallReq) error
|
||||||
UpgradePlugin(req dto.AgentPluginUpgradeReq) error
|
UpgradePlugin(req dto.AgentPluginUpgradeReq) error
|
||||||
UninstallPlugin(req dto.AgentPluginUninstallReq) error
|
UninstallPlugin(req dto.AgentPluginUninstallReq) error
|
||||||
@@ -90,8 +105,18 @@ type IAgentService interface {
|
|||||||
ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error
|
ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type batchUpgradePlan struct {
|
||||||
|
ctx batchAgentInstallContext
|
||||||
|
req request.AppInstallUpgrade
|
||||||
|
}
|
||||||
|
|
||||||
|
type batchAgentInstallContext struct {
|
||||||
|
agent model.Agent
|
||||||
|
install model.AppInstall
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultBrowserExecutablePath = "/home/node/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome"
|
defaultBrowserExecutablePath = "/home/node/.cache/ms-playwright/openclaw-browser"
|
||||||
defaultBrowserProfile = "openclaw"
|
defaultBrowserProfile = "openclaw"
|
||||||
defaultUserTimezone = "Asia/Shanghai"
|
defaultUserTimezone = "Asia/Shanghai"
|
||||||
defaultToolsProfile = "full"
|
defaultToolsProfile = "full"
|
||||||
@@ -119,7 +144,7 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
if installs, _ := appInstallRepo.ListBy(context.Background(), repo.WithByLowerName(req.Name)); len(installs) > 0 {
|
if installs, _ := appInstallRepo.ListBy(context.Background(), repo.WithByLowerName(req.Name)); len(installs) > 0 {
|
||||||
return nil, buserr.New("ErrNameIsExist")
|
return nil, buserr.New("ErrNameIsExist")
|
||||||
}
|
}
|
||||||
if !xpack.IsXpack() {
|
if !xpack.MultiNodeProvider.IsXpack() {
|
||||||
count, _, err := agentRepo.Page(1, 1)
|
count, _, err := agentRepo.Page(1, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -151,16 +176,9 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
var allowedOrigins []string
|
var allowedOrigins []string
|
||||||
var account *model.AgentAccount
|
var account *model.AgentAccount
|
||||||
var installHooks *appInstallHooks
|
var installHooks *appInstallHooks
|
||||||
|
var hermesAuth hermesDashboardAuth
|
||||||
|
|
||||||
if agentType == constant.AppOpenclaw {
|
if agentType == constant.AppOpenclaw || agentType == constant.AppHermesAgent {
|
||||||
var err error
|
|
||||||
allowedOrigins, err = normalizeAllowedOrigins(req.AllowedOrigins)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(allowedOrigins) == 0 {
|
|
||||||
return nil, fmt.Errorf("allowed origins is required")
|
|
||||||
}
|
|
||||||
if req.AccountID == 0 {
|
if req.AccountID == 0 {
|
||||||
return nil, buserr.New("ErrAgentAccountRequired")
|
return nil, buserr.New("ErrAgentAccountRequired")
|
||||||
}
|
}
|
||||||
@@ -184,6 +202,17 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
runtimeModel = resolvedRuntime.PrimaryModel
|
runtimeModel = resolvedRuntime.PrimaryModel
|
||||||
apiKey = account.APIKey
|
apiKey = account.APIKey
|
||||||
accountID = account.ID
|
accountID = account.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if agentType == constant.AppOpenclaw {
|
||||||
|
var err error
|
||||||
|
allowedOrigins, err = normalizeAllowedOrigins(req.AllowedOrigins)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(allowedOrigins) == 0 {
|
||||||
|
return nil, fmt.Errorf("allowed origins is required")
|
||||||
|
}
|
||||||
token = strings.TrimSpace(req.Token)
|
token = strings.TrimSpace(req.Token)
|
||||||
if token == "" {
|
if token == "" {
|
||||||
token = generateToken()
|
token = generateToken()
|
||||||
@@ -193,6 +222,16 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
return prepareOpenclawInstallFiles(appInstall, account, storedModel, token, allowedOrigins)
|
return prepareOpenclawInstallFiles(appInstall, account, storedModel, token, allowedOrigins)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
} else if agentType == constant.AppHermesAgent {
|
||||||
|
hermesAuth = normalizeHermesDashboardAuth(req.DashboardUsername, req.DashboardPassword)
|
||||||
|
installHooks = &appInstallHooks{
|
||||||
|
AfterCopyData: func(appInstall *model.AppInstall) error {
|
||||||
|
if err := prepareHermesInstallFiles(appInstall, account, storedModel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeHermesDashboardAuthEnv(path.Join(appInstall.GetPath(), ".env"), hermesAuth, false)
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params := map[string]interface{}{
|
params := map[string]interface{}{
|
||||||
@@ -200,12 +239,8 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
constant.MemoryLimit: "0",
|
constant.MemoryLimit: "0",
|
||||||
constant.HostIP: "",
|
constant.HostIP: "",
|
||||||
}
|
}
|
||||||
|
setAgentWebUIParams(params, agentType, detail.Version, req.WebUIPort)
|
||||||
if agentType == constant.AppOpenclaw {
|
if agentType == constant.AppOpenclaw {
|
||||||
if isOpenclawHTTPSWindowVersion(detail.Version) {
|
|
||||||
params["PANEL_APP_PORT_HTTPS"] = req.WebUIPort
|
|
||||||
} else {
|
|
||||||
params["PANEL_APP_PORT_HTTP"] = req.WebUIPort
|
|
||||||
}
|
|
||||||
if allowedOrigin := firstAllowedOrigin(allowedOrigins); allowedOrigin != "" {
|
if allowedOrigin := firstAllowedOrigin(allowedOrigins); allowedOrigin != "" {
|
||||||
params["ALLOWED_ORIGIN"] = allowedOrigin
|
params["ALLOWED_ORIGIN"] = allowedOrigin
|
||||||
}
|
}
|
||||||
@@ -217,8 +252,10 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
params["BASE_URL"] = baseURL
|
params["BASE_URL"] = baseURL
|
||||||
params["API_KEY"] = apiKey
|
params["API_KEY"] = apiKey
|
||||||
params["OPENCLAW_GATEWAY_TOKEN"] = token
|
params["OPENCLAW_GATEWAY_TOKEN"] = token
|
||||||
} else {
|
}
|
||||||
params["PANEL_APP_PORT_HTTP"] = req.WebUIPort
|
if agentType == constant.AppHermesAgent {
|
||||||
|
params[hermesDashboardUsernameEnvKey] = hermesAuth.Username
|
||||||
|
params[hermesDashboardPasswordEnvKey] = hermesAuth.Password
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.EditCompose && strings.TrimSpace(req.DockerCompose) == "" {
|
if req.EditCompose && strings.TrimSpace(req.DockerCompose) == "" {
|
||||||
@@ -250,6 +287,9 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
if agentType == constant.AppOpenclaw {
|
if agentType == constant.AppOpenclaw {
|
||||||
configPath = path.Join(appInstall.GetPath(), "data", "conf", "openclaw.json")
|
configPath = path.Join(appInstall.GetPath(), "data", "conf", "openclaw.json")
|
||||||
}
|
}
|
||||||
|
if agentType == constant.AppHermesAgent {
|
||||||
|
configPath = path.Join(appInstall.GetPath(), "data", "config.yaml")
|
||||||
|
}
|
||||||
agent := &model.Agent{
|
agent := &model.Agent{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Remark: req.Remark,
|
Remark: req.Remark,
|
||||||
@@ -276,6 +316,441 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
return &item, nil
|
return &item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AgentService) BatchInstall(req dto.AgentBatchInstallReq) (*dto.AgentItem, error) {
|
||||||
|
accountID, err := a.ensureBatchInstallAccount(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
createReq := buildCreateReqFromBatchInstallReq(req)
|
||||||
|
createReq.AccountID = accountID
|
||||||
|
if strings.TrimSpace(createReq.Name) == "" {
|
||||||
|
name, err := buildBatchInstallAgentName(createReq.AgentType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
createReq.Name = name
|
||||||
|
}
|
||||||
|
if createReq.AgentType == constant.AppOpenclaw && len(createReq.AllowedOrigins) == 0 {
|
||||||
|
allowedOrigin, err := buildBatchInstallOpenclawAllowedOrigin(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
createReq.AllowedOrigins = []string{allowedOrigin}
|
||||||
|
}
|
||||||
|
return a.Create(createReq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AgentService) BatchUpgrade(req dto.AgentBatchUpgradeReq) ([]dto.AgentBatchUpgradeResult, error) {
|
||||||
|
plans, results, err := buildBatchUpgradePlans(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, plan := range plans {
|
||||||
|
result := dto.AgentBatchUpgradeResult{
|
||||||
|
AgentID: plan.ctx.agent.ID,
|
||||||
|
AgentName: plan.ctx.agent.Name,
|
||||||
|
AppInstallID: plan.ctx.agent.AppInstallID,
|
||||||
|
}
|
||||||
|
if err := upgradeInstall(plan.req); err != nil {
|
||||||
|
result.Message = err.Error()
|
||||||
|
} else {
|
||||||
|
result.Success = true
|
||||||
|
}
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AgentService) BatchInstallSkill(req dto.AgentBatchSkillInstallReq) ([]dto.AgentBatchSkillInstallResult, error) {
|
||||||
|
if req.AgentType != constant.AppOpenclaw && req.AgentType != constant.AppHermesAgent {
|
||||||
|
return nil, fmt.Errorf("%s does not support skill batch install", req.AgentType)
|
||||||
|
}
|
||||||
|
packagePath, err := validateLocalSkillPackagePath(req.PackagePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if format, err := localSkillPackageFormat(packagePath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if format != "zip" {
|
||||||
|
return nil, fmt.Errorf("only .zip skill packages can be installed")
|
||||||
|
}
|
||||||
|
skillName := sanitizeLocalSkillDirName(req.SkillName, packagePath, req.SkillName)
|
||||||
|
agents, err := listBatchAgents(req.AgentType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results := make([]dto.AgentBatchSkillInstallResult, 0, len(agents))
|
||||||
|
for _, agent := range agents {
|
||||||
|
result := dto.AgentBatchSkillInstallResult{
|
||||||
|
AgentID: agent.ID,
|
||||||
|
AgentName: agent.Name,
|
||||||
|
AppInstallID: agent.AppInstallID,
|
||||||
|
}
|
||||||
|
ctx, message := loadBatchAgentInstall(agent, req.AgentType)
|
||||||
|
if message != "" {
|
||||||
|
result.Message = message
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
install := ctx.install
|
||||||
|
result.AppInstallID = install.ID
|
||||||
|
if install.Status == constant.StatusInstalling || install.Status == constant.StatusUpgrading {
|
||||||
|
result.Message = fmt.Sprintf("agent status is %s", install.Status)
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||||
|
result.Message = err.Error()
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
installTask, err := task.NewTaskWithOps(skillName, task.TaskInstall, task.TaskScopeAI, buildBatchSkillInstallTaskID(req.TaskID, agent.ID), agent.ID)
|
||||||
|
if err != nil {
|
||||||
|
result.Message = err.Error()
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentAgent := ctx.agent
|
||||||
|
currentInstall := ctx.install
|
||||||
|
installTask.AddSubTask("Install local skill", func(t *task.Task) error {
|
||||||
|
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(20*time.Minute))
|
||||||
|
return installLocalSkillPackage(mgr, currentInstall.ContainerName, currentAgent.AgentType, currentAgent.ConfigPath, packagePath, skillName)
|
||||||
|
}, nil)
|
||||||
|
go func() {
|
||||||
|
if err := installTask.Execute(); err != nil {
|
||||||
|
global.LOG.Errorf("batch install local skill failed: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
result.Success = true
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AgentService) BatchOperate(req dto.AgentBatchOperateReq) ([]dto.AgentBatchOperateResult, error) {
|
||||||
|
operate := constant.AppOperate(strings.TrimSpace(req.Operate))
|
||||||
|
if operate != constant.Start && operate != constant.Stop && operate != constant.Restart && operate != constant.Delete {
|
||||||
|
return nil, fmt.Errorf("operate %s is not supported", req.Operate)
|
||||||
|
}
|
||||||
|
agents, err := listBatchAgents(req.AgentType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results := make([]dto.AgentBatchOperateResult, 0, len(agents))
|
||||||
|
for _, agent := range agents {
|
||||||
|
result := dto.AgentBatchOperateResult{
|
||||||
|
AgentID: agent.ID,
|
||||||
|
AgentName: agent.Name,
|
||||||
|
AppInstallID: agent.AppInstallID,
|
||||||
|
}
|
||||||
|
if operate == constant.Delete {
|
||||||
|
if err := a.Delete(dto.AgentDeleteReq{
|
||||||
|
ID: agent.ID,
|
||||||
|
TaskID: buildBatchOperateTaskID(req.TaskID, agent.ID),
|
||||||
|
ForceDelete: req.ForceDelete,
|
||||||
|
}); err != nil {
|
||||||
|
result.Message = err.Error()
|
||||||
|
} else {
|
||||||
|
result.Success = true
|
||||||
|
}
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ctx, message := loadBatchAgentInstall(agent, req.AgentType)
|
||||||
|
if message != "" {
|
||||||
|
result.Message = message
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
install := ctx.install
|
||||||
|
result.AppInstallID = install.ID
|
||||||
|
if message := batchOperateSkipMessage(operate, install.Status); message != "" {
|
||||||
|
result.Success = true
|
||||||
|
result.Skipped = true
|
||||||
|
result.Message = message
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := NewIAppInstalledService().Operate(request.AppInstalledOperate{
|
||||||
|
InstallId: install.ID,
|
||||||
|
Operate: operate,
|
||||||
|
TaskID: buildBatchOperateTaskID(req.TaskID, agent.ID),
|
||||||
|
}); err != nil {
|
||||||
|
result.Message = err.Error()
|
||||||
|
} else {
|
||||||
|
result.Success = true
|
||||||
|
}
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listBatchAgents(agentType string) ([]model.Agent, error) {
|
||||||
|
return agentRepo.List(func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("agent_type = ?", agentType).Order("id ASC")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadBatchAgentInstall(agent model.Agent, agentType string) (batchAgentInstallContext, string) {
|
||||||
|
ctx := batchAgentInstallContext{agent: agent}
|
||||||
|
if agent.AppInstallID == 0 {
|
||||||
|
return ctx, "agent app install id is empty"
|
||||||
|
}
|
||||||
|
install, err := appInstallRepo.GetFirst(repo.WithByID(agent.AppInstallID))
|
||||||
|
if err != nil {
|
||||||
|
return ctx, err.Error()
|
||||||
|
}
|
||||||
|
ctx.install = install
|
||||||
|
if install.App.Key != agentType {
|
||||||
|
return ctx, fmt.Sprintf("app key %s does not match agent type %s", install.App.Key, agentType)
|
||||||
|
}
|
||||||
|
return ctx, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBatchUpgradePlans(req dto.AgentBatchUpgradeReq) ([]batchUpgradePlan, []dto.AgentBatchUpgradeResult, error) {
|
||||||
|
agents, err := listBatchAgents(req.AgentType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
plans := make([]batchUpgradePlan, 0, len(agents))
|
||||||
|
results := make([]dto.AgentBatchUpgradeResult, 0)
|
||||||
|
for _, agent := range agents {
|
||||||
|
result := dto.AgentBatchUpgradeResult{
|
||||||
|
AgentID: agent.ID,
|
||||||
|
AgentName: agent.Name,
|
||||||
|
AppInstallID: agent.AppInstallID,
|
||||||
|
}
|
||||||
|
ctx, message := loadBatchAgentInstall(agent, req.AgentType)
|
||||||
|
if message != "" {
|
||||||
|
result.Message = message
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
install := ctx.install
|
||||||
|
result.AppInstallID = install.ID
|
||||||
|
if install.Status == constant.StatusInstalling || install.Status == constant.StatusUpgrading {
|
||||||
|
result.Message = fmt.Sprintf("agent status is %s", install.Status)
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if install.Version == req.TargetVersion {
|
||||||
|
result.Success = true
|
||||||
|
result.Skipped = true
|
||||||
|
result.Message = "already target version"
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
detail, err := appDetailRepo.GetFirst(appDetailRepo.WithAppId(install.AppId), appDetailRepo.WithVersion(req.TargetVersion))
|
||||||
|
if err != nil {
|
||||||
|
result.Message = err.Error()
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if detail.ID == 0 {
|
||||||
|
result.Message = fmt.Sprintf("target version %s not found", req.TargetVersion)
|
||||||
|
results = append(results, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
plans = append(plans, batchUpgradePlan{
|
||||||
|
ctx: ctx,
|
||||||
|
req: request.AppInstallUpgrade{
|
||||||
|
InstallID: install.ID,
|
||||||
|
DetailID: detail.ID,
|
||||||
|
Backup: req.Backup,
|
||||||
|
PullImage: req.PullImage,
|
||||||
|
TaskID: buildBatchUpgradeTaskID(req.TaskID, install.ID),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return plans, results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBatchUpgradeTaskID(taskID string, appInstallID uint) string {
|
||||||
|
taskID = strings.TrimSpace(taskID)
|
||||||
|
if taskID == "" {
|
||||||
|
taskID = fmt.Sprintf("batch-upgrade-%d-%d", appInstallID, time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s-%d", taskID, appInstallID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBatchSkillInstallTaskID(taskID string, agentID uint) string {
|
||||||
|
taskID = strings.TrimSpace(taskID)
|
||||||
|
if taskID == "" {
|
||||||
|
taskID = fmt.Sprintf("batch-skill-install-%d-%d", agentID, time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s-%d", taskID, agentID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBatchOperateTaskID(taskID string, agentID uint) string {
|
||||||
|
taskID = strings.TrimSpace(taskID)
|
||||||
|
if taskID == "" {
|
||||||
|
taskID = fmt.Sprintf("batch-operate-%d-%d", agentID, time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s-%d", taskID, agentID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func batchOperateSkipMessage(operate constant.AppOperate, status string) string {
|
||||||
|
switch status {
|
||||||
|
case constant.StatusInstalling, constant.StatusUpgrading, constant.StatusUninstalling, constant.StatusRebuilding:
|
||||||
|
return fmt.Sprintf("agent status is %s", status)
|
||||||
|
}
|
||||||
|
switch operate {
|
||||||
|
case constant.Start:
|
||||||
|
if status == constant.StatusRunning || status == constant.StatusStarting || status == constant.StatusRestarting {
|
||||||
|
return fmt.Sprintf("agent status is %s", status)
|
||||||
|
}
|
||||||
|
case constant.Stop:
|
||||||
|
if status != constant.StatusRunning {
|
||||||
|
return fmt.Sprintf("agent status is %s", status)
|
||||||
|
}
|
||||||
|
case constant.Restart:
|
||||||
|
if status == constant.StatusStarting {
|
||||||
|
return fmt.Sprintf("agent status is %s", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildCreateReqFromBatchInstallReq(req dto.AgentBatchInstallReq) dto.AgentCreateReq {
|
||||||
|
return dto.AgentCreateReq{
|
||||||
|
Name: req.Name,
|
||||||
|
Remark: req.Remark,
|
||||||
|
AppVersion: req.AppVersion,
|
||||||
|
WebUIPort: req.WebUIPort,
|
||||||
|
BridgePort: req.BridgePort,
|
||||||
|
AllowedOrigins: req.AllowedOrigins,
|
||||||
|
AgentType: req.AgentType,
|
||||||
|
Model: req.Model,
|
||||||
|
AccountID: req.AccountID,
|
||||||
|
Token: req.Token,
|
||||||
|
DashboardUsername: req.DashboardUsername,
|
||||||
|
DashboardPassword: req.DashboardPassword,
|
||||||
|
TaskID: req.TaskID,
|
||||||
|
Advanced: req.Advanced,
|
||||||
|
ContainerName: req.ContainerName,
|
||||||
|
AllowPort: req.AllowPort,
|
||||||
|
SpecifyIP: req.SpecifyIP,
|
||||||
|
RestartPolicy: req.RestartPolicy,
|
||||||
|
CpuQuota: req.CpuQuota,
|
||||||
|
MemoryLimit: req.MemoryLimit,
|
||||||
|
MemoryUnit: req.MemoryUnit,
|
||||||
|
PullImage: req.PullImage,
|
||||||
|
EditCompose: req.EditCompose,
|
||||||
|
DockerCompose: req.DockerCompose,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBatchInstallAgentName(agentType string) (string, error) {
|
||||||
|
prefix := strings.ToLower(strings.TrimSpace(agentType))
|
||||||
|
if prefix == "" {
|
||||||
|
prefix = "agent"
|
||||||
|
}
|
||||||
|
for i := 1; i <= 10000; i++ {
|
||||||
|
name := fmt.Sprintf("%s-%d", prefix, i)
|
||||||
|
exists, err := agentNameExists(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no available agent name")
|
||||||
|
}
|
||||||
|
|
||||||
|
func agentNameExists(name string) (bool, error) {
|
||||||
|
if exist, err := agentRepo.GetFirst(repo.WithByLowerName(name)); err == nil && exist != nil && exist.ID > 0 {
|
||||||
|
return true, nil
|
||||||
|
} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
installs, err := appInstallRepo.ListBy(context.Background(), repo.WithByLowerName(name))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return len(installs) > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBatchInstallOpenclawAllowedOrigin(req dto.AgentBatchInstallReq) (string, error) {
|
||||||
|
host := ""
|
||||||
|
if value, err := settingRepo.GetValueByKey("SystemIP"); err == nil {
|
||||||
|
host = strings.TrimSpace(value)
|
||||||
|
}
|
||||||
|
if host == "" {
|
||||||
|
host = strings.TrimSpace(req.FallbackAccessHost)
|
||||||
|
}
|
||||||
|
if host == "" {
|
||||||
|
host = "127.0.0.1"
|
||||||
|
}
|
||||||
|
return buildOpenclawAllowedOrigin(openclawAllowedOriginScheme(req.AppVersion), host, req.WebUIPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AgentService) ensureBatchInstallAccount(req dto.AgentBatchInstallReq) (uint, error) {
|
||||||
|
if !requiresAgentAccount(req.AgentType) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
if req.MasterAccountID == 0 {
|
||||||
|
return 0, buserr.New("ErrInvalidParams")
|
||||||
|
}
|
||||||
|
snapshot := req.AccountSnapshot
|
||||||
|
account, err := agentAccountRepo.GetFirst(agentAccountRepo.WithByMasterAccountID(req.MasterAccountID))
|
||||||
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if account == nil && global.IsMaster {
|
||||||
|
account, err = agentAccountRepo.GetFirst(repo.WithByID(req.MasterAccountID))
|
||||||
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if account == nil {
|
||||||
|
account = &model.AgentAccount{
|
||||||
|
MasterAccountID: req.MasterAccountID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
account.MasterAccountID = req.MasterAccountID
|
||||||
|
account.Provider = snapshot.Provider
|
||||||
|
account.Name = snapshot.Name
|
||||||
|
account.APIKey = snapshot.APIKey
|
||||||
|
account.RememberAPIKey = snapshot.RememberAPIKey
|
||||||
|
account.BaseURL = snapshot.BaseURL
|
||||||
|
account.APIType = snapshot.APIType
|
||||||
|
account.Remark = snapshot.Remark
|
||||||
|
account.Verified = true
|
||||||
|
|
||||||
|
initialModels, err := buildInitialAgentAccountModels(account, req.AccountModels)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := global.DB.Transaction(func(tx *gorm.DB) error {
|
||||||
|
if account.ID == 0 {
|
||||||
|
if err := tx.Create(account).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := tx.Save(account).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return replacePersistedAgentAccountModelsWithTx(tx, account.ID, initialModels)
|
||||||
|
}); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return account.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func requiresAgentAccount(agentType string) bool {
|
||||||
|
return agentType != constant.AppCopaw
|
||||||
|
}
|
||||||
|
|
||||||
|
func setAgentWebUIParams(params map[string]interface{}, agentType, appVersion string, webUIPort int) {
|
||||||
|
if agentType == constant.AppOpenclaw && isOpenclawHTTPSWindowVersion(appVersion) {
|
||||||
|
params["PANEL_APP_PORT_HTTPS"] = webUIPort
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params["PANEL_APP_PORT_HTTP"] = webUIPort
|
||||||
|
}
|
||||||
|
|
||||||
func (a AgentService) Page(req dto.SearchWithPage) (int64, []dto.AgentItem, error) {
|
func (a AgentService) Page(req dto.SearchWithPage) (int64, []dto.AgentItem, error) {
|
||||||
var opts []repo.DBOption
|
var opts []repo.DBOption
|
||||||
if strings.TrimSpace(req.Info) != "" {
|
if strings.TrimSpace(req.Info) != "" {
|
||||||
@@ -420,6 +895,33 @@ func (a AgentService) UpdateRemark(req dto.AgentRemarkUpdateReq) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig, error) {
|
func (a AgentService) GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig, error) {
|
||||||
|
agent, err := agentRepo.GetFirst(repo.WithByID(req.AgentID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
cfg, err := readHermesConfig(agent.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
account, err := agentAccountRepo.GetFirst(repo.WithByID(agent.AccountID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
accountModels, err := loadAgentAccountModels(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
model, err := resolveHermesConfiguredModelID(account, accountModels, cfg.Model.Default)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dto.AgentModelConfig{
|
||||||
|
AccountID: agent.AccountID,
|
||||||
|
Model: model,
|
||||||
|
Fallbacks: []string{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
agent, _, conf, err := a.loadOpenclawAgentConfig(req.AgentID)
|
agent, _, conf, err := a.loadOpenclawAgentConfig(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -444,7 +946,7 @@ func (a AgentService) GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error {
|
func (a AgentService) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error {
|
||||||
agent, err := loadOpenclawAgentByID(req.AgentID)
|
agent, err := agentRepo.GetFirst(repo.WithByID(req.AgentID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -459,8 +961,21 @@ func (a AgentService) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
|
|||||||
modelName := resolvedRuntime.StoredModel
|
modelName := resolvedRuntime.StoredModel
|
||||||
apiType, maxTokens, contextWindow := resolvedRuntime.APIType, resolvedRuntime.MaxTokens, resolvedRuntime.ContextWindow
|
apiType, maxTokens, contextWindow := resolvedRuntime.APIType, resolvedRuntime.MaxTokens, resolvedRuntime.ContextWindow
|
||||||
confDir := path.Dir(agent.ConfigPath)
|
confDir := path.Dir(agent.ConfigPath)
|
||||||
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, req.Fallbacks); err != nil {
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
return err
|
cfg, err := readHermesConfig(agent.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := writeHermesConfig(confDir, account, modelName, cfg.Timezone); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if agent.AgentType != constant.AppOpenclaw {
|
||||||
|
return fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
|
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, req.Fallbacks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
agent.Provider = account.Provider
|
agent.Provider = account.Provider
|
||||||
agent.Model = modelName
|
agent.Model = modelName
|
||||||
@@ -590,18 +1105,19 @@ func (a AgentService) PageAccounts(req dto.AgentAccountSearch) (int64, []dto.Age
|
|||||||
apiKey = item.APIKey
|
apiKey = item.APIKey
|
||||||
}
|
}
|
||||||
items = append(items, dto.AgentAccountInfo{
|
items = append(items, dto.AgentAccountInfo{
|
||||||
ID: item.ID,
|
ID: item.ID,
|
||||||
Provider: item.Provider,
|
MasterAccountID: item.MasterAccountID,
|
||||||
ProviderName: providercatalog.DisplayName(item.Provider),
|
Provider: item.Provider,
|
||||||
Name: item.Name,
|
ProviderName: providercatalog.DisplayName(item.Provider),
|
||||||
APIKey: apiKey,
|
Name: item.Name,
|
||||||
RememberAPIKey: item.RememberAPIKey,
|
APIKey: apiKey,
|
||||||
BaseURL: item.BaseURL,
|
RememberAPIKey: item.RememberAPIKey,
|
||||||
Models: nil,
|
BaseURL: item.BaseURL,
|
||||||
APIType: item.APIType,
|
Models: nil,
|
||||||
Verified: item.Verified,
|
APIType: item.APIType,
|
||||||
Remark: item.Remark,
|
Verified: item.Verified,
|
||||||
CreatedAt: item.CreatedAt,
|
Remark: item.Remark,
|
||||||
|
CreatedAt: item.CreatedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for i := range items {
|
for i := range items {
|
||||||
@@ -614,6 +1130,10 @@ func (a AgentService) PageAccounts(req dto.AgentAccountSearch) (int64, []dto.Age
|
|||||||
return count, items, nil
|
return count, items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AgentService) CountAccountsByProviders(req dto.AgentAccountProviderCountReq) (map[string]int64, error) {
|
||||||
|
return agentAccountRepo.CountByProviders(req.Providers)
|
||||||
|
}
|
||||||
|
|
||||||
func (a AgentService) GetAccountModels(req dto.AgentAccountModelReq) ([]dto.AgentAccountModel, error) {
|
func (a AgentService) GetAccountModels(req dto.AgentAccountModelReq) ([]dto.AgentAccountModel, error) {
|
||||||
account, err := agentAccountRepo.GetFirst(repo.WithByID(req.AccountID))
|
account, err := agentAccountRepo.GetFirst(repo.WithByID(req.AccountID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -813,6 +1333,20 @@ func (a AgentService) GetOtherConfig(req dto.AgentIDReq) (*dto.AgentOtherConfig,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
cfg, err := readHermesConfig(agent.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
auth := readHermesDashboardAuthFromInstall(install)
|
||||||
|
return &dto.AgentOtherConfig{
|
||||||
|
UserTimezone: cfg.Timezone,
|
||||||
|
BrowserEnabled: true,
|
||||||
|
NPMRegistry: "https://registry.npmjs.org/",
|
||||||
|
DashboardUsername: auth.Username,
|
||||||
|
DashboardPassword: auth.Password,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -830,6 +1364,28 @@ func (a AgentService) UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
account, err := agentAccountRepo.GetFirst(repo.WithByID(agent.AccountID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
previousAuth := readHermesDashboardAuthFromInstall(install)
|
||||||
|
nextAuth := normalizeHermesDashboardAuth(req.DashboardUsername, req.DashboardPassword)
|
||||||
|
if err := writeHermesConfig(path.Dir(agent.ConfigPath), account, agent.Model, strings.TrimSpace(req.UserTimezone)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := writeHermesDashboardAuthEnv(path.Join(install.GetPath(), ".env"), nextAuth, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
operate := constant.Restart
|
||||||
|
if previousAuth.Username != nextAuth.Username || previousAuth.Password != nextAuth.Password {
|
||||||
|
operate = constant.Rebuild
|
||||||
|
}
|
||||||
|
return NewIAppInstalledService().Operate(request.AppInstalledOperate{
|
||||||
|
InstallId: install.ID,
|
||||||
|
Operate: operate,
|
||||||
|
})
|
||||||
|
}
|
||||||
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -844,11 +1400,14 @@ func (a AgentService) UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error
|
|||||||
if err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {
|
if err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return setOpenclawNPMRegistry(install.ContainerName, req.NPMRegistry)
|
if err := setOpenclawNPMRegistry(install.ContainerName, req.NPMRegistry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) GetConfigFile(req dto.AgentConfigFileReq) (*dto.AgentConfigFile, error) {
|
func (a AgentService) GetConfigFile(req dto.AgentConfigFileReq) (*dto.AgentConfigFile, error) {
|
||||||
agent, _, err := a.loadOpenclawAgentAndInstall(req.AgentID)
|
agent, _, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -860,12 +1419,11 @@ func (a AgentService) GetConfigFile(req dto.AgentConfigFileReq) (*dto.AgentConfi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) UpdateConfigFile(req dto.AgentConfigFileUpdateReq) error {
|
func (a AgentService) UpdateConfigFile(req dto.AgentConfigFileUpdateReq) error {
|
||||||
agent, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var payload interface{}
|
if err := validateAgentConfigFileContent(agent.AgentType, req.Content); err != nil {
|
||||||
if err := json.Unmarshal([]byte(req.Content), &payload); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
info, err := os.Stat(agent.ConfigPath)
|
info, err := os.Stat(agent.ConfigPath)
|
||||||
@@ -875,14 +1433,34 @@ func (a AgentService) UpdateConfigFile(req dto.AgentConfigFileUpdateReq) error {
|
|||||||
if err := os.WriteFile(agent.ConfigPath, []byte(req.Content), info.Mode()); err != nil {
|
if err := os.WriteFile(agent.ConfigPath, []byte(req.Content), info.Mode()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
cfg, err := readHermesConfig(agent.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cfg.Model.Default != "" {
|
||||||
|
agent.Model = cfg.Model.Default
|
||||||
|
if err := agentRepo.Save(agent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return NewIAppInstalledService().Operate(request.AppInstalledOperate{
|
return NewIAppInstalledService().Operate(request.AppInstalledOperate{
|
||||||
InstallId: install.ID,
|
InstallId: install.ID,
|
||||||
Operate: constant.Restart,
|
Operate: constant.Restart,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateAgentConfigFileContent(agentType, content string) error {
|
||||||
|
var payload interface{}
|
||||||
|
if agentType == constant.AppHermesAgent {
|
||||||
|
return yaml.Unmarshal([]byte(content), &payload)
|
||||||
|
}
|
||||||
|
return json.Unmarshal([]byte(content), &payload)
|
||||||
|
}
|
||||||
|
|
||||||
func getOpenclawNPMRegistry(containerName string) (string, error) {
|
func getOpenclawNPMRegistry(containerName string) (string, error) {
|
||||||
registry, err := runDockerExecWithStdout(20*time.Second, containerName, "npm", "get", "registry")
|
registry, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "npm", "get", "registry")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -894,7 +1472,7 @@ func getOpenclawNPMRegistry(containerName string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setOpenclawNPMRegistry(containerName, registry string) error {
|
func setOpenclawNPMRegistry(containerName, registry string) error {
|
||||||
return cmd.RunDefaultBashCf("docker exec %s npm set registry %q", containerName, registry)
|
return cmd.NewCommandMgr().Run("docker", "exec", containerName, "npm", "set", "registry", registry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) loadAgentAndInstall(agentID uint) (*model.Agent, *model.AppInstall, error) {
|
func (a AgentService) loadAgentAndInstall(agentID uint) (*model.Agent, *model.AppInstall, error) {
|
||||||
@@ -917,8 +1495,8 @@ func (a AgentService) loadOpenclawAgentAndInstall(agentID uint) (*model.Agent, *
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if agent.AgentType == constant.AppCopaw {
|
if agent.AgentType != constant.AppOpenclaw {
|
||||||
return nil, nil, fmt.Errorf("copaw does not support")
|
return nil, nil, fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
}
|
}
|
||||||
return agent, install, nil
|
return agent, install, nil
|
||||||
}
|
}
|
||||||
@@ -940,8 +1518,8 @@ func (a AgentService) loadOpenclawAgentConfig(agentID uint) (*model.Agent, *mode
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
if agent.AgentType == constant.AppCopaw {
|
if agent.AgentType != constant.AppOpenclaw {
|
||||||
return nil, nil, nil, fmt.Errorf("copaw does not support")
|
return nil, nil, nil, fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
}
|
}
|
||||||
return agent, install, conf, nil
|
return agent, install, conf, nil
|
||||||
}
|
}
|
||||||
@@ -970,30 +1548,37 @@ func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, agent := range agents {
|
for _, agent := range agents {
|
||||||
confDir := path.Dir(agent.ConfigPath)
|
selectedAccountModel, err := requireAgentAccountModelForProvider(account.Provider, accountModels, agent.Model)
|
||||||
modelName := strings.TrimSpace(agent.Model)
|
|
||||||
var selectedAccountModel dto.AgentAccountModel
|
|
||||||
if modelName != "" {
|
|
||||||
selectedAccountModel, err = requireAgentAccountModelForProvider(account.Provider, accountModels, modelName)
|
|
||||||
if err != nil {
|
|
||||||
return buserr.WithName("ErrAgentModelInUse", agent.Name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
selectedAccountModel = accountModels[0]
|
|
||||||
}
|
|
||||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return buserr.WithName("ErrAgentModelInUse", agent.Name)
|
||||||
}
|
}
|
||||||
fallbacks := extractOpenclawFallbackModelIDs(conf, account, accountModels, selectedAccountModel.ID)
|
|
||||||
resolvedRuntime, err := buildOpenclawAccountModelRuntime(account, selectedAccountModel)
|
resolvedRuntime, err := buildOpenclawAccountModelRuntime(account, selectedAccountModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
modelName = resolvedRuntime.StoredModel
|
modelName := resolvedRuntime.StoredModel
|
||||||
apiType, maxTokens, contextWindow := resolvedRuntime.APIType, resolvedRuntime.MaxTokens, resolvedRuntime.ContextWindow
|
apiType, maxTokens, contextWindow := resolvedRuntime.APIType, resolvedRuntime.MaxTokens, resolvedRuntime.ContextWindow
|
||||||
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, fallbacks); err != nil {
|
confDir := path.Dir(agent.ConfigPath)
|
||||||
return err
|
switch agent.AgentType {
|
||||||
|
case constant.AppOpenclaw:
|
||||||
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fallbacks := extractOpenclawFallbackModelIDs(conf, account, accountModels, selectedAccountModel.ID)
|
||||||
|
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, fallbacks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case constant.AppHermesAgent:
|
||||||
|
cfg, err := readHermesConfig(agent.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := writeHermesConfig(confDir, account, modelName, cfg.Timezone); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
agent.BaseURL = account.BaseURL
|
agent.BaseURL = account.BaseURL
|
||||||
agent.APIKey = account.APIKey
|
agent.APIKey = account.APIKey
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (a AgentService) CreateRole(req dto.AgentRoleCreateReq) (*dto.AgentRoleCrea
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{"exec", install.ContainerName, "openclaw", "agents", "add", req.Name}
|
args := []string{"openclaw", "agents", "add", req.Name}
|
||||||
workspace := "/home/node/.openclaw/workspace-agent_" + req.Name
|
workspace := "/home/node/.openclaw/workspace-agent_" + req.Name
|
||||||
agentDir := "/home/node/.openclaw/agents/" + req.Name
|
agentDir := "/home/node/.openclaw/agents/" + req.Name
|
||||||
args = append(args, "--workspace", workspace)
|
args = append(args, "--workspace", workspace)
|
||||||
@@ -43,8 +43,7 @@ func (a AgentService) CreateRole(req dto.AgentRoleCreateReq) (*dto.AgentRoleCrea
|
|||||||
args = append(args, "--agent-dir", agentDir)
|
args = append(args, "--agent-dir", agentDir)
|
||||||
args = append(args, "--non-interactive", "--json")
|
args = append(args, "--non-interactive", "--json")
|
||||||
|
|
||||||
mgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Minute))
|
output, err := cmd.RunDockerExecWithStdout(5*time.Minute, install.ContainerName, args...)
|
||||||
output, err := mgr.RunWithStdout("docker", args...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -125,10 +124,8 @@ func (a AgentService) DeleteRole(req dto.AgentRoleDeleteReq) error {
|
|||||||
return buserr.New("ErrRecordNotFound")
|
return buserr.New("ErrRecordNotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{"exec", install.ContainerName, "openclaw", "agents", "delete", req.ID, "--force"}
|
args := []string{"openclaw", "agents", "delete", req.ID, "--force"}
|
||||||
|
if _, err = cmd.RunDockerExecWithStdout(5*time.Minute, install.ContainerName, args...); err != nil {
|
||||||
mgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Minute))
|
|
||||||
if _, err = mgr.RunWithStdout("docker", args...); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if target.Workspace != "" {
|
if target.Workspace != "" {
|
||||||
@@ -171,8 +168,6 @@ func (a AgentService) operateRoleBinding(req dto.AgentRoleBindReq, action string
|
|||||||
return buserr.New("ErrInvalidParams")
|
return buserr.New("ErrInvalidParams")
|
||||||
}
|
}
|
||||||
args := []string{
|
args := []string{
|
||||||
"exec",
|
|
||||||
install.ContainerName,
|
|
||||||
"openclaw",
|
"openclaw",
|
||||||
"agents",
|
"agents",
|
||||||
action,
|
action,
|
||||||
@@ -182,8 +177,7 @@ func (a AgentService) operateRoleBinding(req dto.AgentRoleBindReq, action string
|
|||||||
binding,
|
binding,
|
||||||
}
|
}
|
||||||
args = append(args, "--json")
|
args = append(args, "--json")
|
||||||
mgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Minute))
|
_, err = cmd.RunDockerExecWithStdout(5*time.Minute, install.ContainerName, args...)
|
||||||
_, err = mgr.RunWithStdout("docker", args...)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,17 +13,34 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||||
)
|
)
|
||||||
|
|
||||||
type openclawPluginPackage struct {
|
type openclawPluginPackage struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateHermesChannelConfig(agent *model.Agent, install *model.AppInstall, write func(confDir string) error) error {
|
||||||
|
if err := write(path.Dir(agent.ConfigPath)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := compose.Restart(install.GetComposePath())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (a AgentService) GetFeishuConfig(req dto.AgentFeishuConfigReq) (*dto.AgentFeishuConfig, error) {
|
func (a AgentService) GetFeishuConfig(req dto.AgentFeishuConfigReq) (*dto.AgentFeishuConfig, error) {
|
||||||
_, install, conf, err := a.loadAgentConfig(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return readHermesFeishuChannelConfig(path.Dir(agent.ConfigPath))
|
||||||
|
}
|
||||||
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -34,6 +51,26 @@ func (a AgentService) GetFeishuConfig(req dto.AgentFeishuConfigReq) (*dto.AgentF
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) error {
|
func (a AgentService) UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return updateHermesChannelConfig(agent, install, func(confDir string) error {
|
||||||
|
return writeHermesFeishuChannelConfig(confDir, dto.AgentFeishuConfig{
|
||||||
|
Enabled: req.Enabled,
|
||||||
|
ThreadSession: req.ThreadSession,
|
||||||
|
ReplyMode: req.ReplyMode,
|
||||||
|
Streaming: req.Streaming,
|
||||||
|
RequireMention: req.RequireMention,
|
||||||
|
GroupPolicy: req.GroupPolicy,
|
||||||
|
GroupAllowFrom: req.GroupAllowFrom,
|
||||||
|
Domain: "feishu",
|
||||||
|
ConnectionMode: "websocket",
|
||||||
|
Bots: req.Bots,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||||
config := dto.AgentFeishuConfig{
|
config := dto.AgentFeishuConfig{
|
||||||
Enabled: req.Enabled,
|
Enabled: req.Enabled,
|
||||||
@@ -43,6 +80,8 @@ func (a AgentService) UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) err
|
|||||||
RequireMention: req.RequireMention,
|
RequireMention: req.RequireMention,
|
||||||
GroupPolicy: req.GroupPolicy,
|
GroupPolicy: req.GroupPolicy,
|
||||||
GroupAllowFrom: req.GroupAllowFrom,
|
GroupAllowFrom: req.GroupAllowFrom,
|
||||||
|
Domain: req.Domain,
|
||||||
|
ConnectionMode: req.ConnectionMode,
|
||||||
Bots: req.Bots,
|
Bots: req.Bots,
|
||||||
}
|
}
|
||||||
setFeishuConfig(conf, config)
|
setFeishuConfig(conf, config)
|
||||||
@@ -52,7 +91,14 @@ func (a AgentService) UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) GetTelegramConfig(req dto.AgentTelegramConfigReq) (*dto.AgentTelegramConfig, error) {
|
func (a AgentService) GetTelegramConfig(req dto.AgentTelegramConfigReq) (*dto.AgentTelegramConfig, error) {
|
||||||
_, _, conf, err := a.loadAgentConfig(req.AgentID)
|
agent, _, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return readHermesTelegramChannelConfig(path.Dir(agent.ConfigPath))
|
||||||
|
}
|
||||||
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -61,11 +107,32 @@ func (a AgentService) GetTelegramConfig(req dto.AgentTelegramConfigReq) (*dto.Ag
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) UpdateTelegramConfig(req dto.AgentTelegramConfigUpdateReq) error {
|
func (a AgentService) UpdateTelegramConfig(req dto.AgentTelegramConfigUpdateReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return updateHermesChannelConfig(agent, install, func(confDir string) error {
|
||||||
|
return writeHermesTelegramChannelConfig(confDir, dto.AgentTelegramConfig{
|
||||||
|
Enabled: req.Enabled,
|
||||||
|
DmPolicy: req.DmPolicy,
|
||||||
|
AllowFrom: req.AllowFrom,
|
||||||
|
RequireMention: req.RequireMention,
|
||||||
|
GroupPolicy: req.GroupPolicy,
|
||||||
|
GroupAllowFrom: req.GroupAllowFrom,
|
||||||
|
Proxy: req.Proxy,
|
||||||
|
Streaming: req.Streaming,
|
||||||
|
DefaultAccount: req.DefaultAccount,
|
||||||
|
Bots: req.Bots,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||||
setTelegramConfig(conf, dto.AgentTelegramConfig{
|
setTelegramConfig(conf, dto.AgentTelegramConfig{
|
||||||
Enabled: req.Enabled,
|
Enabled: req.Enabled,
|
||||||
DmPolicy: req.DmPolicy,
|
DmPolicy: req.DmPolicy,
|
||||||
AllowFrom: req.AllowFrom,
|
AllowFrom: req.AllowFrom,
|
||||||
|
RequireMention: req.RequireMention,
|
||||||
GroupPolicy: req.GroupPolicy,
|
GroupPolicy: req.GroupPolicy,
|
||||||
GroupAllowFrom: req.GroupAllowFrom,
|
GroupAllowFrom: req.GroupAllowFrom,
|
||||||
Proxy: req.Proxy,
|
Proxy: req.Proxy,
|
||||||
@@ -78,7 +145,14 @@ func (a AgentService) UpdateTelegramConfig(req dto.AgentTelegramConfigUpdateReq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) GetDiscordConfig(req dto.AgentIDReq) (*dto.AgentDiscordConfig, error) {
|
func (a AgentService) GetDiscordConfig(req dto.AgentIDReq) (*dto.AgentDiscordConfig, error) {
|
||||||
_, _, conf, err := a.loadAgentConfig(req.AgentID)
|
agent, _, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return readHermesDiscordChannelConfig(path.Dir(agent.ConfigPath))
|
||||||
|
}
|
||||||
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -87,10 +161,30 @@ func (a AgentService) GetDiscordConfig(req dto.AgentIDReq) (*dto.AgentDiscordCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) error {
|
func (a AgentService) UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return updateHermesChannelConfig(agent, install, func(confDir string) error {
|
||||||
|
return writeHermesDiscordChannelConfig(confDir, dto.AgentDiscordConfig{
|
||||||
|
Enabled: req.Enabled,
|
||||||
|
DmPolicy: req.DmPolicy,
|
||||||
|
AllowFrom: req.AllowFrom,
|
||||||
|
RequireMention: req.RequireMention,
|
||||||
|
GroupPolicy: req.GroupPolicy,
|
||||||
|
Proxy: req.Proxy,
|
||||||
|
DefaultAccount: req.DefaultAccount,
|
||||||
|
Bots: req.Bots,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||||
setDiscordConfig(conf, dto.AgentDiscordConfig{
|
setDiscordConfig(conf, dto.AgentDiscordConfig{
|
||||||
Enabled: req.Enabled,
|
Enabled: req.Enabled,
|
||||||
DmPolicy: req.DmPolicy,
|
DmPolicy: req.DmPolicy,
|
||||||
|
AllowFrom: req.AllowFrom,
|
||||||
|
RequireMention: req.RequireMention,
|
||||||
GroupPolicy: req.GroupPolicy,
|
GroupPolicy: req.GroupPolicy,
|
||||||
Proxy: req.Proxy,
|
Proxy: req.Proxy,
|
||||||
DefaultAccount: req.DefaultAccount,
|
DefaultAccount: req.DefaultAccount,
|
||||||
@@ -101,7 +195,14 @@ func (a AgentService) UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) GetQQBotConfig(req dto.AgentIDReq) (*dto.AgentQQBotConfig, error) {
|
func (a AgentService) GetQQBotConfig(req dto.AgentIDReq) (*dto.AgentQQBotConfig, error) {
|
||||||
_, install, conf, err := a.loadAgentConfig(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return readHermesQQBotChannelConfig(path.Dir(agent.ConfigPath))
|
||||||
|
}
|
||||||
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -112,6 +213,22 @@ func (a AgentService) GetQQBotConfig(req dto.AgentIDReq) (*dto.AgentQQBotConfig,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) UpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error {
|
func (a AgentService) UpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return updateHermesChannelConfig(agent, install, func(confDir string) error {
|
||||||
|
return writeHermesQQBotChannelConfig(confDir, dto.AgentQQBotConfig{
|
||||||
|
Enabled: req.Enabled,
|
||||||
|
DmPolicy: req.DmPolicy,
|
||||||
|
AllowFrom: req.AllowFrom,
|
||||||
|
GroupPolicy: req.GroupPolicy,
|
||||||
|
GroupAllowFrom: req.GroupAllowFrom,
|
||||||
|
Bots: req.Bots,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||||
setQQBotConfig(conf, dto.AgentQQBotConfig{
|
setQQBotConfig(conf, dto.AgentQQBotConfig{
|
||||||
Enabled: req.Enabled,
|
Enabled: req.Enabled,
|
||||||
@@ -122,7 +239,14 @@ func (a AgentService) UpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) GetWecomConfig(req dto.AgentIDReq) (*dto.AgentWecomConfig, error) {
|
func (a AgentService) GetWecomConfig(req dto.AgentIDReq) (*dto.AgentWecomConfig, error) {
|
||||||
_, install, conf, err := a.loadAgentConfig(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return readHermesWecomChannelConfig(path.Dir(agent.ConfigPath))
|
||||||
|
}
|
||||||
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -133,6 +257,23 @@ func (a AgentService) GetWecomConfig(req dto.AgentIDReq) (*dto.AgentWecomConfig,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) UpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error {
|
func (a AgentService) UpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return updateHermesChannelConfig(agent, install, func(confDir string) error {
|
||||||
|
return writeHermesWecomChannelConfig(confDir, dto.AgentWecomConfig{
|
||||||
|
Enabled: req.Enabled,
|
||||||
|
DmPolicy: req.DmPolicy,
|
||||||
|
AllowFrom: req.AllowFrom,
|
||||||
|
GroupPolicy: req.GroupPolicy,
|
||||||
|
GroupAllowFrom: req.GroupAllowFrom,
|
||||||
|
BotID: req.BotID,
|
||||||
|
Secret: req.Secret,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||||
setWecomConfig(conf, dto.AgentWecomConfig{
|
setWecomConfig(conf, dto.AgentWecomConfig{
|
||||||
Enabled: req.Enabled,
|
Enabled: req.Enabled,
|
||||||
@@ -148,7 +289,14 @@ func (a AgentService) UpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) GetDingTalkConfig(req dto.AgentIDReq) (*dto.AgentDingTalkConfig, error) {
|
func (a AgentService) GetDingTalkConfig(req dto.AgentIDReq) (*dto.AgentDingTalkConfig, error) {
|
||||||
_, install, conf, err := a.loadAgentConfig(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return readHermesDingTalkChannelConfig(path.Dir(agent.ConfigPath))
|
||||||
|
}
|
||||||
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -158,7 +306,34 @@ func (a AgentService) GetDingTalkConfig(req dto.AgentIDReq) (*dto.AgentDingTalkC
|
|||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AgentService) GetWeixinConfig(req dto.AgentIDReq) (*dto.AgentWeixinConfig, error) {
|
||||||
|
agent, _, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return readHermesWeixinChannelConfig(path.Dir(agent.ConfigPath))
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
|
|
||||||
func (a AgentService) UpdateDingTalkConfig(req dto.AgentDingTalkConfigUpdateReq) error {
|
func (a AgentService) UpdateDingTalkConfig(req dto.AgentDingTalkConfigUpdateReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return updateHermesChannelConfig(agent, install, func(confDir string) error {
|
||||||
|
return writeHermesDingTalkChannelConfig(confDir, dto.AgentDingTalkConfig{
|
||||||
|
Enabled: req.Enabled,
|
||||||
|
DmPolicy: req.DmPolicy,
|
||||||
|
AllowFrom: req.AllowFrom,
|
||||||
|
GroupPolicy: req.GroupPolicy,
|
||||||
|
GroupAllowFrom: req.GroupAllowFrom,
|
||||||
|
Bots: req.Bots,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||||
setDingTalkConfig(conf, dto.AgentDingTalkConfig{
|
setDingTalkConfig(conf, dto.AgentDingTalkConfig{
|
||||||
Enabled: req.Enabled,
|
Enabled: req.Enabled,
|
||||||
@@ -197,14 +372,14 @@ func (a AgentService) InstallPlugin(req dto.AgentPluginInstallReq) error {
|
|||||||
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
|
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
|
||||||
if req.Type == "qqbot" {
|
if req.Type == "qqbot" {
|
||||||
legacyPluginPath := path.Join(openclawPluginBaseDir, "qqbot")
|
legacyPluginPath := path.Join(openclawPluginBaseDir, "qqbot")
|
||||||
if err := mgr.RunBashCf("docker exec %s test -d %s", install.ContainerName, legacyPluginPath); err == nil {
|
if err := mgr.Run("docker", "exec", install.ContainerName, "test", "-d", legacyPluginPath); err == nil {
|
||||||
if _, err := mgr.RunWithStdout("docker", "exec", "-i", install.ContainerName, "sh", "-c", buildOpenclawPluginUninstallScript("qqbot")); err != nil {
|
if err := uninstallOpenclawPlugin(mgr, install.ContainerName, "qqbot"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err := mgr.RunWithStdout("docker", "exec", install.ContainerName, "sh", "-c", buildOpenclawPluginInstallScript(spec, pluginID)); err != nil {
|
if err := installOpenclawPlugin(mgr, install.ContainerName, spec, pluginID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
@@ -240,11 +415,11 @@ func (a AgentService) UpgradePlugin(req dto.AgentPluginUpgradeReq) error {
|
|||||||
}
|
}
|
||||||
upgradeTask.AddSubTask("Upgrade OpenClaw plugin", func(t *task.Task) error {
|
upgradeTask.AddSubTask("Upgrade OpenClaw plugin", func(t *task.Task) error {
|
||||||
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
|
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
|
||||||
if _, err := mgr.RunWithStdout("docker", "exec", "-i", install.ContainerName, "sh", "-c", buildOpenclawPluginUninstallScript(pluginID)); err != nil {
|
if err := uninstallOpenclawPlugin(mgr, install.ContainerName, pluginID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
if _, err := mgr.RunWithStdout("docker", "exec", install.ContainerName, "sh", "-c", buildOpenclawPluginInstallScript(spec, pluginID)); err != nil {
|
if err := installOpenclawPlugin(mgr, install.ContainerName, spec, pluginID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
@@ -280,7 +455,7 @@ func (a AgentService) UninstallPlugin(req dto.AgentPluginUninstallReq) error {
|
|||||||
}
|
}
|
||||||
uninstallTask.AddSubTask("Uninstall OpenClaw plugin", func(t *task.Task) error {
|
uninstallTask.AddSubTask("Uninstall OpenClaw plugin", func(t *task.Task) error {
|
||||||
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
|
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
|
||||||
if _, err := mgr.RunWithStdout("docker", "exec", "-i", install.ContainerName, "sh", "-c", buildOpenclawPluginUninstallScript(pluginID)); err != nil {
|
if err := uninstallOpenclawPlugin(mgr, install.ContainerName, pluginID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||||
@@ -299,7 +474,7 @@ func (a AgentService) UninstallPlugin(req dto.AgentPluginUninstallReq) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) LoginWeixinChannel(req dto.AgentWeixinLoginReq) error {
|
func (a AgentService) LoginWeixinChannel(req dto.AgentWeixinLoginReq) error {
|
||||||
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -307,10 +482,22 @@ func (a AgentService) LoginWeixinChannel(req dto.AgentWeixinLoginReq) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
loginTask.AddSubTask("Login OpenClaw Weixin channel", func(t *task.Task) error {
|
loginTask.AddSubTask("Login Weixin channel", func(t *task.Task) error {
|
||||||
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(30*time.Minute))
|
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(30*time.Minute))
|
||||||
return mgr.RunBashCf("docker exec %s openclaw channels login --channel openclaw-weixin", install.ContainerName)
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return mgr.Run("docker", buildHermesWeixinLoginArgs(install.ContainerName)...)
|
||||||
|
}
|
||||||
|
return mgr.Run("docker", "exec", install.ContainerName, "openclaw", "channels", "login", "--channel", "openclaw-weixin")
|
||||||
}, nil)
|
}, nil)
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
loginTask.AddSubTask("Restart Hermes-Agent container", func(t *task.Task) error {
|
||||||
|
output, err := compose.Restart(install.GetComposePath())
|
||||||
|
if output != "" {
|
||||||
|
t.Log(output)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}, nil)
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
if err := loginTask.Execute(); err != nil {
|
if err := loginTask.Execute(); err != nil {
|
||||||
global.LOG.Errorf("login openclaw weixin channel failed: %v", err)
|
global.LOG.Errorf("login openclaw weixin channel failed: %v", err)
|
||||||
@@ -319,6 +506,36 @@ func (a AgentService) LoginWeixinChannel(req dto.AgentWeixinLoginReq) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AgentService) DeleteChannelConfig(req dto.AgentChannelDeleteReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType != constant.AppHermesAgent {
|
||||||
|
return fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
|
return updateHermesChannelConfig(agent, install, func(confDir string) error {
|
||||||
|
switch req.Type {
|
||||||
|
case "telegram":
|
||||||
|
return deleteHermesTelegramChannelConfig(confDir)
|
||||||
|
case "discord":
|
||||||
|
return deleteHermesDiscordChannelConfig(confDir)
|
||||||
|
case "qqbot":
|
||||||
|
return deleteHermesQQBotChannelConfig(confDir)
|
||||||
|
case "wecom":
|
||||||
|
return deleteHermesWecomChannelConfig(confDir)
|
||||||
|
case "dingtalk":
|
||||||
|
return deleteHermesDingTalkChannelConfig(confDir)
|
||||||
|
case "feishu":
|
||||||
|
return deleteHermesFeishuChannelConfig(confDir)
|
||||||
|
case "weixin":
|
||||||
|
return deleteHermesWeixinChannelConfig(confDir)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported channel type: %s", req.Type)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (a AgentService) CheckPlugin(req dto.AgentPluginCheckReq) (*dto.AgentPluginStatus, error) {
|
func (a AgentService) CheckPlugin(req dto.AgentPluginCheckReq) (*dto.AgentPluginStatus, error) {
|
||||||
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -354,22 +571,39 @@ func (a AgentService) CheckPlugin(req dto.AgentPluginCheckReq) (*dto.AgentPlugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error {
|
func (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error {
|
||||||
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
mgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
|
||||||
|
output, err := mgr.RunWithStdout(
|
||||||
|
"docker",
|
||||||
|
buildHermesDockerExecArgs(install.ContainerName, "pairing", "approve", req.Type, req.PairingCode)...,
|
||||||
|
)
|
||||||
|
return validateHermesPairingApproveResult(output, err)
|
||||||
|
}
|
||||||
if req.AccountID != "" {
|
if req.AccountID != "" {
|
||||||
return cmd.RunDefaultBashCf(
|
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run(
|
||||||
"docker exec %s openclaw pairing approve %s %q --account %q",
|
"docker",
|
||||||
|
"exec",
|
||||||
install.ContainerName,
|
install.ContainerName,
|
||||||
|
"openclaw",
|
||||||
|
"pairing",
|
||||||
|
"approve",
|
||||||
req.Type,
|
req.Type,
|
||||||
req.PairingCode,
|
req.PairingCode,
|
||||||
|
"--account",
|
||||||
req.AccountID,
|
req.AccountID,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return cmd.RunDefaultBashCf(
|
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run(
|
||||||
"docker exec %s openclaw pairing approve %s %q",
|
"docker",
|
||||||
|
"exec",
|
||||||
install.ContainerName,
|
install.ContainerName,
|
||||||
|
"openclaw",
|
||||||
|
"pairing",
|
||||||
|
"approve",
|
||||||
req.Type,
|
req.Type,
|
||||||
req.PairingCode,
|
req.PairingCode,
|
||||||
)
|
)
|
||||||
@@ -384,7 +618,7 @@ func extractFeishuConfig(conf map[string]interface{}) dto.AgentFeishuConfig {
|
|||||||
RequireMention: "true",
|
RequireMention: "true",
|
||||||
GroupPolicy: "open",
|
GroupPolicy: "open",
|
||||||
GroupAllowFrom: []string{},
|
GroupAllowFrom: []string{},
|
||||||
Bots: []dto.AgentFeishuBot{defaultFeishuBot()},
|
Bots: []dto.AgentFeishuBot{},
|
||||||
}
|
}
|
||||||
feishu := getChannelConfig(conf, "feishu")
|
feishu := getChannelConfig(conf, "feishu")
|
||||||
if len(feishu) == 0 {
|
if len(feishu) == 0 {
|
||||||
@@ -434,7 +668,10 @@ func extractFeishuConfig(conf map[string]interface{}) dto.AgentFeishuConfig {
|
|||||||
baseEnabled := defaultBot.Enabled
|
baseEnabled := defaultBot.Enabled
|
||||||
baseDmPolicy := defaultBot.DmPolicy
|
baseDmPolicy := defaultBot.DmPolicy
|
||||||
baseAllowFrom := append([]string(nil), defaultBot.AllowFrom...)
|
baseAllowFrom := append([]string(nil), defaultBot.AllowFrom...)
|
||||||
bots := []dto.AgentFeishuBot{defaultBot}
|
bots := make([]dto.AgentFeishuBot, 0, len(accounts)+1)
|
||||||
|
if defaultBot.AppID != "" || defaultBot.AppSecret != "" {
|
||||||
|
bots = append(bots, defaultBot)
|
||||||
|
}
|
||||||
for _, accountID := range sortedChildKeys(accounts) {
|
for _, accountID := range sortedChildKeys(accounts) {
|
||||||
if accountID == "default" {
|
if accountID == "default" {
|
||||||
continue
|
continue
|
||||||
@@ -552,6 +789,7 @@ func extractTelegramConfig(conf map[string]interface{}) dto.AgentTelegramConfig
|
|||||||
Enabled: true,
|
Enabled: true,
|
||||||
DmPolicy: "pairing",
|
DmPolicy: "pairing",
|
||||||
AllowFrom: []string{},
|
AllowFrom: []string{},
|
||||||
|
RequireMention: false,
|
||||||
GroupPolicy: "open",
|
GroupPolicy: "open",
|
||||||
GroupAllowFrom: []string{},
|
GroupAllowFrom: []string{},
|
||||||
Streaming: "partial",
|
Streaming: "partial",
|
||||||
@@ -570,11 +808,10 @@ func extractTelegramConfig(conf map[string]interface{}) dto.AgentTelegramConfig
|
|||||||
if groupPolicy := extractStringValue(telegram["groupPolicy"]); groupPolicy != "" {
|
if groupPolicy := extractStringValue(telegram["groupPolicy"]); groupPolicy != "" {
|
||||||
result.GroupPolicy = groupPolicy
|
result.GroupPolicy = groupPolicy
|
||||||
}
|
}
|
||||||
|
result.RequireMention = result.GroupPolicy == "allowlist"
|
||||||
result.GroupAllowFrom = extractStringList(telegram["groupAllowFrom"])
|
result.GroupAllowFrom = extractStringList(telegram["groupAllowFrom"])
|
||||||
result.Proxy = extractStringValue(telegram["proxy"])
|
result.Proxy = extractStringValue(telegram["proxy"])
|
||||||
if streaming := extractStringValue(telegram["streaming"]); streaming != "" {
|
result.Streaming = normalizeTelegramStreamingMode(telegram["streaming"], result.Streaming)
|
||||||
result.Streaming = streaming
|
|
||||||
}
|
|
||||||
accounts := childMap(telegram, "accounts")
|
accounts := childMap(telegram, "accounts")
|
||||||
if len(accounts) == 0 {
|
if len(accounts) == 0 {
|
||||||
botToken := extractStringValue(telegram["botToken"])
|
botToken := extractStringValue(telegram["botToken"])
|
||||||
@@ -600,7 +837,7 @@ func extractTelegramConfig(conf map[string]interface{}) dto.AgentTelegramConfig
|
|||||||
BotToken: extractStringValue(account["botToken"]),
|
BotToken: extractStringValue(account["botToken"]),
|
||||||
DmPolicy: extractStringValue(account["dmPolicy"]),
|
DmPolicy: extractStringValue(account["dmPolicy"]),
|
||||||
GroupPolicy: extractStringValue(account["groupPolicy"]),
|
GroupPolicy: extractStringValue(account["groupPolicy"]),
|
||||||
Streaming: extractStringValue(account["streaming"]),
|
Streaming: normalizeTelegramStreamingMode(account["streaming"], result.Streaming),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
result.DefaultAccount = normalizeDefaultAccount(extractStringValue(telegram["defaultAccount"]), getTelegramBotAccountIDs(bots))
|
result.DefaultAccount = normalizeDefaultAccount(extractStringValue(telegram["defaultAccount"]), getTelegramBotAccountIDs(bots))
|
||||||
@@ -618,6 +855,11 @@ func setTelegramConfig(conf map[string]interface{}, config dto.AgentTelegramConf
|
|||||||
telegram["dmPolicy"] = config.DmPolicy
|
telegram["dmPolicy"] = config.DmPolicy
|
||||||
telegram["groupPolicy"] = config.GroupPolicy
|
telegram["groupPolicy"] = config.GroupPolicy
|
||||||
telegram["defaultAccount"] = defaultAccount
|
telegram["defaultAccount"] = defaultAccount
|
||||||
|
if config.RequireMention {
|
||||||
|
telegram["groupPolicy"] = "allowlist"
|
||||||
|
} else if config.GroupPolicy == "allowlist" {
|
||||||
|
telegram["groupPolicy"] = "allowlist"
|
||||||
|
}
|
||||||
if config.DmPolicy == "open" {
|
if config.DmPolicy == "open" {
|
||||||
telegram["allowFrom"] = []string{"*"}
|
telegram["allowFrom"] = []string{"*"}
|
||||||
} else if config.DmPolicy == "allowlist" {
|
} else if config.DmPolicy == "allowlist" {
|
||||||
@@ -635,7 +877,7 @@ func setTelegramConfig(conf map[string]interface{}, config dto.AgentTelegramConf
|
|||||||
} else {
|
} else {
|
||||||
delete(telegram, "proxy")
|
delete(telegram, "proxy")
|
||||||
}
|
}
|
||||||
telegram["streaming"] = config.Streaming
|
telegram["streaming"] = buildTelegramStreamingConfig(config.Streaming)
|
||||||
accounts := make(map[string]interface{}, len(config.Bots))
|
accounts := make(map[string]interface{}, len(config.Bots))
|
||||||
for _, bot := range config.Bots {
|
for _, bot := range config.Bots {
|
||||||
account := map[string]interface{}{
|
account := map[string]interface{}{
|
||||||
@@ -644,7 +886,7 @@ func setTelegramConfig(conf map[string]interface{}, config dto.AgentTelegramConf
|
|||||||
"botToken": bot.BotToken,
|
"botToken": bot.BotToken,
|
||||||
"dmPolicy": bot.DmPolicy,
|
"dmPolicy": bot.DmPolicy,
|
||||||
"groupPolicy": bot.GroupPolicy,
|
"groupPolicy": bot.GroupPolicy,
|
||||||
"streaming": bot.Streaming,
|
"streaming": buildTelegramStreamingConfig(bot.Streaming),
|
||||||
}
|
}
|
||||||
if bot.DmPolicy == "open" {
|
if bot.DmPolicy == "open" {
|
||||||
account["allowFrom"] = []string{"*"}
|
account["allowFrom"] = []string{"*"}
|
||||||
@@ -655,8 +897,31 @@ func setTelegramConfig(conf map[string]interface{}, config dto.AgentTelegramConf
|
|||||||
delete(telegram, "botToken")
|
delete(telegram, "botToken")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeTelegramStreamingMode(value interface{}, defaultMode string) string {
|
||||||
|
mode := defaultMode
|
||||||
|
switch typed := value.(type) {
|
||||||
|
case string:
|
||||||
|
mode = typed
|
||||||
|
case map[string]interface{}:
|
||||||
|
mode = extractStringValue(typed["mode"])
|
||||||
|
}
|
||||||
|
mode = strings.ToLower(strings.TrimSpace(mode))
|
||||||
|
switch mode {
|
||||||
|
case "off", "partial", "block", "progress":
|
||||||
|
return mode
|
||||||
|
default:
|
||||||
|
return "partial"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildTelegramStreamingConfig(mode string) map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"mode": normalizeTelegramStreamingMode(mode, "partial"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func extractDiscordConfig(conf map[string]interface{}) dto.AgentDiscordConfig {
|
func extractDiscordConfig(conf map[string]interface{}) dto.AgentDiscordConfig {
|
||||||
result := dto.AgentDiscordConfig{Enabled: true, DmPolicy: "pairing", GroupPolicy: "open"}
|
result := dto.AgentDiscordConfig{Enabled: true, DmPolicy: "pairing", AllowFrom: []string{}, RequireMention: false, GroupPolicy: "open"}
|
||||||
discord := getChannelConfig(conf, "discord")
|
discord := getChannelConfig(conf, "discord")
|
||||||
if len(discord) == 0 {
|
if len(discord) == 0 {
|
||||||
return result
|
return result
|
||||||
@@ -674,6 +939,8 @@ func extractDiscordConfig(conf map[string]interface{}) dto.AgentDiscordConfig {
|
|||||||
if groupPolicy := extractStringValue(discord["groupPolicy"]); groupPolicy != "" {
|
if groupPolicy := extractStringValue(discord["groupPolicy"]); groupPolicy != "" {
|
||||||
result.GroupPolicy = groupPolicy
|
result.GroupPolicy = groupPolicy
|
||||||
}
|
}
|
||||||
|
result.RequireMention = result.GroupPolicy == "allowlist"
|
||||||
|
result.AllowFrom = extractStringList(discord["allowFrom"])
|
||||||
result.Proxy = extractStringValue(discord["proxy"])
|
result.Proxy = extractStringValue(discord["proxy"])
|
||||||
accounts := childMap(discord, "accounts")
|
accounts := childMap(discord, "accounts")
|
||||||
if len(accounts) == 0 {
|
if len(accounts) == 0 {
|
||||||
@@ -712,8 +979,15 @@ func setDiscordConfig(conf map[string]interface{}, config dto.AgentDiscordConfig
|
|||||||
discord["dmPolicy"] = config.DmPolicy
|
discord["dmPolicy"] = config.DmPolicy
|
||||||
discord["groupPolicy"] = config.GroupPolicy
|
discord["groupPolicy"] = config.GroupPolicy
|
||||||
discord["defaultAccount"] = defaultAccount
|
discord["defaultAccount"] = defaultAccount
|
||||||
|
if config.RequireMention {
|
||||||
|
discord["groupPolicy"] = "allowlist"
|
||||||
|
} else if config.GroupPolicy == "allowlist" {
|
||||||
|
discord["groupPolicy"] = "allowlist"
|
||||||
|
}
|
||||||
if config.DmPolicy == "open" {
|
if config.DmPolicy == "open" {
|
||||||
discord["allowFrom"] = []string{"*"}
|
discord["allowFrom"] = []string{"*"}
|
||||||
|
} else if config.DmPolicy == "allowlist" {
|
||||||
|
discord["allowFrom"] = append([]string(nil), config.AllowFrom...)
|
||||||
} else {
|
} else {
|
||||||
delete(discord, "allowFrom")
|
delete(discord, "allowFrom")
|
||||||
}
|
}
|
||||||
@@ -740,31 +1014,34 @@ func extractQQBotConfig(conf map[string]interface{}) dto.AgentQQBotConfig {
|
|||||||
result := dto.AgentQQBotConfig{Enabled: true}
|
result := dto.AgentQQBotConfig{Enabled: true}
|
||||||
qqbot := getChannelConfig(conf, "qqbot")
|
qqbot := getChannelConfig(conf, "qqbot")
|
||||||
if len(qqbot) == 0 {
|
if len(qqbot) == 0 {
|
||||||
result.Bots = []dto.AgentQQBotBot{defaultQQBot()}
|
result.Bots = []dto.AgentQQBotBot{}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if enabled, ok := qqbot["enabled"].(bool); ok {
|
if enabled, ok := qqbot["enabled"].(bool); ok {
|
||||||
result.Enabled = enabled
|
result.Enabled = enabled
|
||||||
}
|
}
|
||||||
bots := []dto.AgentQQBotBot{
|
bots := make([]dto.AgentQQBotBot, 0, len(childMap(qqbot, "accounts"))+1)
|
||||||
{
|
defaultBot := dto.AgentQQBotBot{
|
||||||
AgentChannelBotBase: dto.AgentChannelBotBase{
|
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||||
AccountID: "default",
|
AccountID: "default",
|
||||||
Name: extractStringValue(qqbot["name"]),
|
Name: extractStringValue(qqbot["name"]),
|
||||||
Enabled: extractBoolValue(qqbot["enabled"], true),
|
Enabled: extractBoolValue(qqbot["enabled"], true),
|
||||||
IsDefault: true,
|
IsDefault: true,
|
||||||
},
|
|
||||||
AppID: extractStringValue(qqbot["appId"]),
|
|
||||||
ClientSecret: extractStringValue(qqbot["clientSecret"]),
|
|
||||||
AllowFrom: extractStringList(qqbot["allowFrom"]),
|
|
||||||
SystemPrompt: extractStringValue(qqbot["systemPrompt"]),
|
|
||||||
},
|
},
|
||||||
|
AppID: extractStringValue(qqbot["appId"]),
|
||||||
|
ClientSecret: extractStringValue(qqbot["clientSecret"]),
|
||||||
|
AllowFrom: extractStringList(qqbot["allowFrom"]),
|
||||||
|
SystemPrompt: extractStringValue(qqbot["systemPrompt"]),
|
||||||
}
|
}
|
||||||
if bots[0].Name == "" {
|
if defaultBot.Name == "" {
|
||||||
bots[0].Name = "Default"
|
defaultBot.Name = "Default"
|
||||||
}
|
}
|
||||||
for _, accountID := range sortedChildKeys(childMap(qqbot, "accounts")) {
|
if defaultBot.AppID != "" || defaultBot.ClientSecret != "" {
|
||||||
account := childMap(childMap(qqbot, "accounts"), accountID)
|
bots = append(bots, defaultBot)
|
||||||
|
}
|
||||||
|
accounts := childMap(qqbot, "accounts")
|
||||||
|
for _, accountID := range sortedChildKeys(accounts) {
|
||||||
|
account := childMap(accounts, accountID)
|
||||||
bots = append(bots, dto.AgentQQBotBot{
|
bots = append(bots, dto.AgentQQBotBot{
|
||||||
AgentChannelBotBase: dto.AgentChannelBotBase{
|
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
@@ -1042,20 +1319,40 @@ func appendPluginAllow(conf map[string]interface{}, pluginID string) {
|
|||||||
plugins["allow"] = append(allow, pluginID)
|
plugins["allow"] = append(allow, pluginID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildOpenclawPluginInstallScript(spec, pluginID string) string {
|
func installOpenclawPlugin(mgr *cmd.CommandHelper, containerName, spec, pluginID string) error {
|
||||||
return fmt.Sprintf(
|
workdir := path.Join(openclawPluginPackageTmpDir, pluginID)
|
||||||
"set -e; workdir=%s/%s; rm -rf \"$workdir\"; mkdir -p \"$workdir\"; cd \"$workdir\"; npm pack --silent %q >/dev/null 2>&1; pkg=$(find \"$workdir\" -maxdepth 1 -type f -name '*.tgz' | head -n 1); printf '%%s\\n' \"$pkg\"; openclaw plugins install \"$pkg\" --dangerously-force-unsafe-install; rm -rf \"$workdir\"",
|
defer func() {
|
||||||
openclawPluginPackageTmpDir,
|
_ = mgr.Run("docker", "exec", containerName, "rm", "-rf", workdir)
|
||||||
pluginID,
|
}()
|
||||||
spec,
|
if err := mgr.Run("docker", "exec", containerName, "rm", "-rf", workdir); err != nil {
|
||||||
)
|
return err
|
||||||
|
}
|
||||||
|
if err := mgr.Run("docker", "exec", containerName, "mkdir", "-p", workdir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := mgr.Run("docker", "exec", "-w", workdir, containerName, "npm", "pack", "--silent", spec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkgPath, err := cmd.RunDockerExecWithStdout(10*time.Minute, containerName, "find", workdir, "-maxdepth", "1", "-type", "f", "-name", "*.tgz", "-print", "-quit")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pkgPath = strings.TrimSpace(pkgPath)
|
||||||
|
if pkgPath == "" {
|
||||||
|
return fmt.Errorf("openclaw plugin package not found")
|
||||||
|
}
|
||||||
|
return mgr.Run("docker", "exec", containerName, "openclaw", "plugins", "install", pkgPath, "--dangerously-force-unsafe-install")
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildOpenclawPluginUninstallScript(pluginID string) string {
|
func uninstallOpenclawPlugin(mgr *cmd.CommandHelper, containerName, pluginID string) error {
|
||||||
return fmt.Sprintf(
|
_, err := mgr.RunPipe(
|
||||||
"set +e; printf 'yes\\n' | openclaw plugins uninstall %s; code=$?; if [ \"$code\" -eq 137 ]; then exit 0; fi; exit \"$code\"",
|
cmd.PipeCommand{Name: "printf", Args: []string{"yes\n"}},
|
||||||
pluginID,
|
cmd.PipeCommand{Name: "docker", Args: []string{"exec", "-i", containerName, "openclaw", "plugins", "uninstall", pluginID}},
|
||||||
)
|
)
|
||||||
|
if err != nil && strings.Contains(err.Error(), "exit status 137") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolvePluginMeta(pluginType string) (string, string, error) {
|
func resolvePluginMeta(pluginType string) (string, string, error) {
|
||||||
@@ -1110,7 +1407,7 @@ func loadOpenclawPluginLatestVersion(containerName, pluginType string) (string,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
output, err := runDockerExecWithStdout(20*time.Second, containerName, "npm", "view", spec, "version", "--json")
|
output, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "npm", "view", spec, "version", "--json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,742 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
providercatalog "github.com/1Panel-dev/1Panel/agent/app/provider"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
const hermesWorkspaceDir = "/opt/data/workspace"
|
||||||
|
const hermesExecutablePath = "/opt/hermes/.venv/bin/hermes"
|
||||||
|
const hermesDashboardUsernameEnvKey = "HERMES_DASHBOARD_USERNAME"
|
||||||
|
const hermesDashboardPasswordEnvKey = "HERMES_DASHBOARD_PASSWORD"
|
||||||
|
|
||||||
|
type hermesDashboardAuth struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type hermesConfig struct {
|
||||||
|
Model hermesModelConfig `yaml:"model"`
|
||||||
|
Terminal hermesTerminalConfig `yaml:"terminal"`
|
||||||
|
Timezone string `yaml:"timezone,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type hermesModelConfig struct {
|
||||||
|
Default string `yaml:"default"`
|
||||||
|
Provider string `yaml:"provider"`
|
||||||
|
BaseURL string `yaml:"base_url,omitempty"`
|
||||||
|
APIKey string `yaml:"api_key,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type hermesTerminalConfig struct {
|
||||||
|
Backend string `yaml:"backend"`
|
||||||
|
Cwd string `yaml:"cwd"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type hermesEnvEntry struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildHermesDockerExecCommandArgs(containerName, command string, commandArgs ...string) []string {
|
||||||
|
args := []string{"exec", "-u", "hermes", containerName, command}
|
||||||
|
return append(args, commandArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildHermesDockerExecArgs(containerName string, hermesArgs ...string) []string {
|
||||||
|
return buildHermesDockerExecCommandArgs(containerName, "hermes", hermesArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesConfig(confDir string, account *model.AgentAccount, modelName string, timezone string) error {
|
||||||
|
if strings.TrimSpace(confDir) == "" {
|
||||||
|
return fmt.Errorf("config dir is required")
|
||||||
|
}
|
||||||
|
if account == nil {
|
||||||
|
return fmt.Errorf("account is required")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(modelName) == "" {
|
||||||
|
return fmt.Errorf("model is required")
|
||||||
|
}
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
if !fileOp.Stat(confDir) {
|
||||||
|
if err := fileOp.CreateDir(confDir, constant.DirPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := resolveHermesProvider(account.Provider)
|
||||||
|
configPath := path.Join(confDir, "config.yaml")
|
||||||
|
cfg, err := readHermesConfigMap(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
model := map[string]interface{}{
|
||||||
|
"default": resolveHermesModel(account.Provider, provider, modelName),
|
||||||
|
"provider": provider,
|
||||||
|
"base_url": account.BaseURL,
|
||||||
|
}
|
||||||
|
if provider == "custom" && account.APIKey != "" {
|
||||||
|
model["api_key"] = "${CUSTOM_API_KEY}"
|
||||||
|
}
|
||||||
|
cfg["model"] = model
|
||||||
|
cfg["terminal"] = map[string]interface{}{
|
||||||
|
"backend": "local",
|
||||||
|
"cwd": hermesWorkspaceDir,
|
||||||
|
}
|
||||||
|
if timezone != "" {
|
||||||
|
cfg["timezone"] = timezone
|
||||||
|
} else {
|
||||||
|
delete(cfg, "timezone")
|
||||||
|
}
|
||||||
|
if err := writeHermesConfigMap(configPath, cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeHermesModelEnv(path.Join(confDir, ".env"), account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareHermesInstallFiles(appInstall *model.AppInstall, account *model.AgentAccount, modelName string) error {
|
||||||
|
if appInstall == nil {
|
||||||
|
return fmt.Errorf("app install is required")
|
||||||
|
}
|
||||||
|
dataDir := path.Join(appInstall.GetPath(), "data")
|
||||||
|
if err := writeHermesConfig(dataDir, account, modelName, normalizeHermesTimezone(common.LoadTimeZoneByCmd())); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return files.NewFileOp().ChownR(dataDir, "1000", "1000", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeHermesDashboardAuth(username, password string) hermesDashboardAuth {
|
||||||
|
auth := hermesDashboardAuth{
|
||||||
|
Username: strings.TrimSpace(username),
|
||||||
|
Password: strings.TrimSpace(password),
|
||||||
|
}
|
||||||
|
if auth.Username == "" {
|
||||||
|
auth.Username = "admin"
|
||||||
|
}
|
||||||
|
if auth.Password == "" {
|
||||||
|
auth.Password = common.RandStr(8)
|
||||||
|
}
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesDashboardAuthEnv(envPath string, auth hermesDashboardAuth, overwrite bool) error {
|
||||||
|
return upsertAgentEnv(envPath, map[string]string{
|
||||||
|
hermesDashboardUsernameEnvKey: auth.Username,
|
||||||
|
hermesDashboardPasswordEnvKey: auth.Password,
|
||||||
|
}, []string{
|
||||||
|
hermesDashboardUsernameEnvKey,
|
||||||
|
hermesDashboardPasswordEnvKey,
|
||||||
|
}, overwrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesDashboardAuthEnv(envPath string) (hermesDashboardAuth, error) {
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return hermesDashboardAuth{}, err
|
||||||
|
}
|
||||||
|
return hermesDashboardAuth{
|
||||||
|
Username: strings.TrimSpace(envMap[hermesDashboardUsernameEnvKey]),
|
||||||
|
Password: strings.TrimSpace(envMap[hermesDashboardPasswordEnvKey]),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesDashboardAuthFromInstall(appInstall *model.AppInstall) hermesDashboardAuth {
|
||||||
|
if appInstall == nil || appInstall.ID == 0 {
|
||||||
|
return hermesDashboardAuth{}
|
||||||
|
}
|
||||||
|
auth, err := readHermesDashboardAuthEnv(path.Join(appInstall.GetPath(), ".env"))
|
||||||
|
if err != nil {
|
||||||
|
return hermesDashboardAuth{}
|
||||||
|
}
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesConfig(configPath string) (*hermesConfig, error) {
|
||||||
|
content, err := files.NewFileOp().GetContent(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var cfg hermesConfig
|
||||||
|
if err := yaml.Unmarshal(content, &cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesConfigMap(configPath string) (map[string]interface{}, error) {
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
if !fileOp.Stat(configPath) {
|
||||||
|
return map[string]interface{}{}, nil
|
||||||
|
}
|
||||||
|
content, err := fileOp.GetContent(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg := map[string]interface{}{}
|
||||||
|
if strings.TrimSpace(string(content)) == "" {
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
if err := yaml.Unmarshal(content, &cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesConfigMap(configPath string, cfg map[string]interface{}) error {
|
||||||
|
payload, err := yaml.Marshal(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return files.NewFileOp().SaveFile(configPath, string(payload), 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesTelegramChannelConfig(confDir string) (*dto.AgentTelegramConfig, error) {
|
||||||
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg, err := readHermesConfigMap(path.Join(confDir, "config.yaml"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allowFrom := splitHermesEnvList(envMap["TELEGRAM_ALLOWED_USERS"])
|
||||||
|
requireMention := extractBoolValue(childMap(cfg, "telegram")["require_mention"], false)
|
||||||
|
dmPolicy := ""
|
||||||
|
if extractHermesEnvBool(envMap, "TELEGRAM_ALLOW_ALL_USERS", false) {
|
||||||
|
dmPolicy = "open"
|
||||||
|
} else if len(allowFrom) > 0 {
|
||||||
|
dmPolicy = "allowlist"
|
||||||
|
} else if envMap["TELEGRAM_BOT_TOKEN"] != "" {
|
||||||
|
dmPolicy = "pairing"
|
||||||
|
}
|
||||||
|
groupPolicy := ""
|
||||||
|
if requireMention {
|
||||||
|
groupPolicy = "allowlist"
|
||||||
|
} else if envMap["TELEGRAM_BOT_TOKEN"] != "" {
|
||||||
|
groupPolicy = "open"
|
||||||
|
}
|
||||||
|
token := envMap["TELEGRAM_BOT_TOKEN"]
|
||||||
|
result := &dto.AgentTelegramConfig{
|
||||||
|
Enabled: token != "",
|
||||||
|
DmPolicy: dmPolicy,
|
||||||
|
AllowFrom: allowFrom,
|
||||||
|
RequireMention: requireMention,
|
||||||
|
GroupPolicy: groupPolicy,
|
||||||
|
GroupAllowFrom: []string{},
|
||||||
|
Streaming: "",
|
||||||
|
DefaultAccount: "",
|
||||||
|
}
|
||||||
|
if token != "" {
|
||||||
|
result.Bots = []dto.AgentTelegramBot{
|
||||||
|
{
|
||||||
|
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||||
|
AccountID: "default",
|
||||||
|
Name: "Default",
|
||||||
|
Enabled: true,
|
||||||
|
IsDefault: true,
|
||||||
|
},
|
||||||
|
BotToken: token,
|
||||||
|
DmPolicy: dmPolicy,
|
||||||
|
GroupPolicy: groupPolicy,
|
||||||
|
Streaming: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesTelegramChannelConfig(confDir string, config dto.AgentTelegramConfig) error {
|
||||||
|
envPath := path.Join(confDir, ".env")
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
token := firstHermesTelegramBotToken(config.Bots, config.DefaultAccount)
|
||||||
|
if token != "" {
|
||||||
|
envMap["TELEGRAM_BOT_TOKEN"] = token
|
||||||
|
} else {
|
||||||
|
delete(envMap, "TELEGRAM_BOT_TOKEN")
|
||||||
|
}
|
||||||
|
delete(envMap, "TELEGRAM_ALLOWED_USERS")
|
||||||
|
delete(envMap, "TELEGRAM_ALLOW_ALL_USERS")
|
||||||
|
switch config.DmPolicy {
|
||||||
|
case "open":
|
||||||
|
envMap["TELEGRAM_ALLOW_ALL_USERS"] = "true"
|
||||||
|
case "allowlist":
|
||||||
|
if allow := joinHermesEnvList(config.AllowFrom); allow != "" {
|
||||||
|
envMap["TELEGRAM_ALLOWED_USERS"] = allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
|
"TELEGRAM_BOT_TOKEN",
|
||||||
|
"TELEGRAM_ALLOWED_USERS",
|
||||||
|
"TELEGRAM_ALLOW_ALL_USERS",
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := path.Join(confDir, "config.yaml")
|
||||||
|
cfg, err := readHermesConfigMap(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
telegram := ensureChildMap(cfg, "telegram")
|
||||||
|
telegram["require_mention"] = config.RequireMention
|
||||||
|
return writeHermesConfigMap(configPath, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesDiscordChannelConfig(confDir string) (*dto.AgentDiscordConfig, error) {
|
||||||
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg, err := readHermesConfigMap(path.Join(confDir, "config.yaml"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allowFrom := splitHermesEnvList(envMap["DISCORD_ALLOWED_USERS"])
|
||||||
|
requireMention := extractBoolValue(childMap(cfg, "discord")["require_mention"], false)
|
||||||
|
dmPolicy := ""
|
||||||
|
if extractHermesEnvBool(envMap, "DISCORD_ALLOW_ALL_USERS", false) {
|
||||||
|
dmPolicy = "open"
|
||||||
|
} else if len(allowFrom) > 0 {
|
||||||
|
dmPolicy = "allowlist"
|
||||||
|
} else if envMap["DISCORD_BOT_TOKEN"] != "" {
|
||||||
|
dmPolicy = "pairing"
|
||||||
|
}
|
||||||
|
groupPolicy := ""
|
||||||
|
if requireMention {
|
||||||
|
groupPolicy = "allowlist"
|
||||||
|
} else if envMap["DISCORD_BOT_TOKEN"] != "" {
|
||||||
|
groupPolicy = "open"
|
||||||
|
}
|
||||||
|
token := envMap["DISCORD_BOT_TOKEN"]
|
||||||
|
result := &dto.AgentDiscordConfig{
|
||||||
|
Enabled: token != "",
|
||||||
|
DmPolicy: dmPolicy,
|
||||||
|
AllowFrom: allowFrom,
|
||||||
|
RequireMention: requireMention,
|
||||||
|
GroupPolicy: groupPolicy,
|
||||||
|
DefaultAccount: "",
|
||||||
|
}
|
||||||
|
if token != "" {
|
||||||
|
result.Bots = []dto.AgentDiscordBot{
|
||||||
|
{
|
||||||
|
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||||
|
AccountID: "default",
|
||||||
|
Name: "Default",
|
||||||
|
Enabled: true,
|
||||||
|
IsDefault: true,
|
||||||
|
},
|
||||||
|
Token: token,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesDiscordChannelConfig(confDir string, config dto.AgentDiscordConfig) error {
|
||||||
|
envPath := path.Join(confDir, ".env")
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
token := firstHermesDiscordBotToken(config.Bots, config.DefaultAccount)
|
||||||
|
if token != "" {
|
||||||
|
envMap["DISCORD_BOT_TOKEN"] = token
|
||||||
|
} else {
|
||||||
|
delete(envMap, "DISCORD_BOT_TOKEN")
|
||||||
|
}
|
||||||
|
delete(envMap, "DISCORD_ALLOWED_USERS")
|
||||||
|
delete(envMap, "DISCORD_ALLOW_ALL_USERS")
|
||||||
|
switch config.DmPolicy {
|
||||||
|
case "open":
|
||||||
|
envMap["DISCORD_ALLOW_ALL_USERS"] = "true"
|
||||||
|
case "allowlist":
|
||||||
|
if allow := joinHermesEnvList(config.AllowFrom); allow != "" {
|
||||||
|
envMap["DISCORD_ALLOWED_USERS"] = allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
|
"DISCORD_BOT_TOKEN",
|
||||||
|
"DISCORD_ALLOWED_USERS",
|
||||||
|
"DISCORD_ALLOW_ALL_USERS",
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := path.Join(confDir, "config.yaml")
|
||||||
|
cfg, err := readHermesConfigMap(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
discord := ensureChildMap(cfg, "discord")
|
||||||
|
discord["require_mention"] = config.RequireMention
|
||||||
|
return writeHermesConfigMap(configPath, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHermesEnvKeys(confDir string, keys ...string) error {
|
||||||
|
envPath := path.Join(confDir, ".env")
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
delete(envMap, key)
|
||||||
|
}
|
||||||
|
return writeAgentEnvMap(envPath, envMap, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHermesConfigSections(confDir string, topLevelKeys []string, platformKeys []string) error {
|
||||||
|
configPath := path.Join(confDir, "config.yaml")
|
||||||
|
cfg, err := readHermesConfigMap(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, key := range topLevelKeys {
|
||||||
|
delete(cfg, key)
|
||||||
|
}
|
||||||
|
if len(platformKeys) > 0 {
|
||||||
|
if platforms, ok := cfg["platforms"].(map[string]interface{}); ok {
|
||||||
|
for _, key := range platformKeys {
|
||||||
|
delete(platforms, key)
|
||||||
|
}
|
||||||
|
if len(platforms) == 0 {
|
||||||
|
delete(cfg, "platforms")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writeHermesConfigMap(configPath, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHermesTelegramChannelConfig(confDir string) error {
|
||||||
|
if err := deleteHermesEnvKeys(confDir,
|
||||||
|
"TELEGRAM_BOT_TOKEN",
|
||||||
|
"TELEGRAM_ALLOWED_USERS",
|
||||||
|
"TELEGRAM_ALLOW_ALL_USERS",
|
||||||
|
"TELEGRAM_HOME_CHANNEL",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return deleteHermesConfigSections(confDir, []string{"telegram"}, []string{"telegram"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHermesDiscordChannelConfig(confDir string) error {
|
||||||
|
if err := deleteHermesEnvKeys(confDir,
|
||||||
|
"DISCORD_BOT_TOKEN",
|
||||||
|
"DISCORD_ALLOWED_USERS",
|
||||||
|
"DISCORD_ALLOW_ALL_USERS",
|
||||||
|
"DISCORD_HOME_CHANNEL",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return deleteHermesConfigSections(confDir, []string{"discord"}, []string{"discord"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeHermesTimezone(timezone string) string {
|
||||||
|
timezone = strings.TrimSpace(timezone)
|
||||||
|
if timezone == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if _, err := time.LoadLocation(timezone); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return timezone
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveHermesProvider(provider string) string {
|
||||||
|
switch provider {
|
||||||
|
case "":
|
||||||
|
return "custom"
|
||||||
|
case "openrouter", "anthropic", "gemini", "zai", "kimi-coding", "xiaomi":
|
||||||
|
return provider
|
||||||
|
case "minimax":
|
||||||
|
return "minimax-cn"
|
||||||
|
default:
|
||||||
|
return "custom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveHermesModel(sourceProvider, targetProvider, modelName string) string {
|
||||||
|
target := strings.TrimSpace(modelName)
|
||||||
|
if target == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if targetProvider != "custom" {
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
if sourceProvider == "custom" || sourceProvider == "vllm" {
|
||||||
|
return normalizeCustomModel(target)
|
||||||
|
}
|
||||||
|
if strings.Contains(target, "/") {
|
||||||
|
parts := strings.SplitN(target, "/", 2)
|
||||||
|
model := strings.TrimSpace(parts[1])
|
||||||
|
if model != "" {
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveHermesConfiguredModelID(account *model.AgentAccount, accountModels []dto.AgentAccountModel, configuredModel string) (string, error) {
|
||||||
|
if account == nil {
|
||||||
|
return "", buserr.New("ErrAgentModelNotInAccount")
|
||||||
|
}
|
||||||
|
configuredModel = strings.TrimSpace(configuredModel)
|
||||||
|
if configuredModel == "" {
|
||||||
|
return "", buserr.New("ErrAgentModelNotInAccount")
|
||||||
|
}
|
||||||
|
provider := resolveHermesProvider(account.Provider)
|
||||||
|
for _, item := range accountModels {
|
||||||
|
if resolveHermesModel(account.Provider, provider, item.ID) == configuredModel {
|
||||||
|
return item.ID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", buserr.New("ErrAgentModelNotInAccount")
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveHermesEnvEntries(account *model.AgentAccount) []hermesEnvEntry {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if resolveHermesProvider(account.Provider) == "custom" {
|
||||||
|
if account.APIKey == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []hermesEnvEntry{
|
||||||
|
{Key: "CUSTOM_API_KEY", Value: account.APIKey},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apiKey := account.APIKey
|
||||||
|
baseURL := account.BaseURL
|
||||||
|
entries := make([]hermesEnvEntry, 0, 4)
|
||||||
|
|
||||||
|
appendEntry := func(key, value string) {
|
||||||
|
if key == "" || value == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
entries = append(entries, hermesEnvEntry{Key: key, Value: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
switch account.Provider {
|
||||||
|
case "openrouter":
|
||||||
|
appendEntry("OPENROUTER_API_KEY", apiKey)
|
||||||
|
appendEntry("OPENROUTER_BASE_URL", baseURL)
|
||||||
|
case "anthropic":
|
||||||
|
appendEntry("ANTHROPIC_API_KEY", apiKey)
|
||||||
|
case "gemini":
|
||||||
|
appendEntry("GOOGLE_API_KEY", apiKey)
|
||||||
|
appendEntry("GEMINI_API_KEY", apiKey)
|
||||||
|
appendEntry("GEMINI_BASE_URL", baseURL)
|
||||||
|
case "zai":
|
||||||
|
appendEntry("GLM_API_KEY", apiKey)
|
||||||
|
appendEntry("ZAI_API_KEY", apiKey)
|
||||||
|
appendEntry("Z_AI_API_KEY", apiKey)
|
||||||
|
appendEntry("GLM_BASE_URL", baseURL)
|
||||||
|
case "kimi-coding", "moonshot":
|
||||||
|
appendEntry("KIMI_API_KEY", apiKey)
|
||||||
|
appendEntry("KIMI_BASE_URL", baseURL)
|
||||||
|
case "kimi":
|
||||||
|
appendEntry("KIMI_CN_API_KEY", apiKey)
|
||||||
|
appendEntry("KIMI_BASE_URL", baseURL)
|
||||||
|
case "minimax":
|
||||||
|
appendEntry("MINIMAX_CN_API_KEY", apiKey)
|
||||||
|
appendEntry("MINIMAX_CN_BASE_URL", baseURL)
|
||||||
|
case "xiaomi":
|
||||||
|
appendEntry("XIAOMI_API_KEY", apiKey)
|
||||||
|
appendEntry("XIAOMI_BASE_URL", baseURL)
|
||||||
|
case "deepseek":
|
||||||
|
appendEntry("DEEPSEEK_API_KEY", apiKey)
|
||||||
|
appendEntry("DEEPSEEK_BASE_URL", baseURL)
|
||||||
|
case "bailian-coding-plan":
|
||||||
|
appendEntry("DASHSCOPE_API_KEY", apiKey)
|
||||||
|
appendEntry("DASHSCOPE_BASE_URL", baseURL)
|
||||||
|
case "openai":
|
||||||
|
appendEntry("OPENAI_API_KEY", apiKey)
|
||||||
|
appendEntry("OPENAI_BASE_URL", baseURL)
|
||||||
|
case "custom", "vllm":
|
||||||
|
if account.APIType == "openai-completions" || account.APIType == "openai-responses" {
|
||||||
|
appendEntry("OPENAI_API_KEY", apiKey)
|
||||||
|
appendEntry("OPENAI_BASE_URL", baseURL)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if envKey := providercatalog.EnvKey(account.Provider); envKey != "" {
|
||||||
|
appendEntry(envKey, apiKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesModelEnv(envPath string, account *model.AgentAccount) error {
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, key := range hermesManagedModelEnvKeys() {
|
||||||
|
delete(envMap, key)
|
||||||
|
}
|
||||||
|
entries := resolveHermesEnvEntries(account)
|
||||||
|
order := make([]string, 0, len(entries))
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.Key == "" || entry.Value == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
envMap[entry.Key] = entry.Value
|
||||||
|
order = append(order, entry.Key)
|
||||||
|
}
|
||||||
|
return writeAgentEnvMap(envPath, envMap, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hermesManagedModelEnvKeys() []string {
|
||||||
|
keys := []string{
|
||||||
|
"OPENROUTER_API_KEY",
|
||||||
|
"OPENROUTER_BASE_URL",
|
||||||
|
"ANTHROPIC_API_KEY",
|
||||||
|
"GOOGLE_API_KEY",
|
||||||
|
"GEMINI_API_KEY",
|
||||||
|
"GEMINI_BASE_URL",
|
||||||
|
"GLM_API_KEY",
|
||||||
|
"ZAI_API_KEY",
|
||||||
|
"Z_AI_API_KEY",
|
||||||
|
"GLM_BASE_URL",
|
||||||
|
"KIMI_API_KEY",
|
||||||
|
"KIMI_CN_API_KEY",
|
||||||
|
"KIMI_BASE_URL",
|
||||||
|
"MINIMAX_CN_API_KEY",
|
||||||
|
"MINIMAX_CN_BASE_URL",
|
||||||
|
"XIAOMI_API_KEY",
|
||||||
|
"XIAOMI_BASE_URL",
|
||||||
|
"DEEPSEEK_API_KEY",
|
||||||
|
"DEEPSEEK_BASE_URL",
|
||||||
|
"DASHSCOPE_API_KEY",
|
||||||
|
"DASHSCOPE_BASE_URL",
|
||||||
|
"OPENAI_API_KEY",
|
||||||
|
"OPENAI_BASE_URL",
|
||||||
|
}
|
||||||
|
seen := make(map[string]struct{}, len(keys))
|
||||||
|
result := make([]string, 0, len(keys))
|
||||||
|
appendKey := func(key string) {
|
||||||
|
if key == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := seen[key]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seen[key] = struct{}{}
|
||||||
|
result = append(result, key)
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
appendKey(key)
|
||||||
|
}
|
||||||
|
for _, meta := range providercatalog.All() {
|
||||||
|
appendKey(meta.EnvKey)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitHermesEnvList(value string) []string {
|
||||||
|
if value == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
parts := strings.Split(value, ",")
|
||||||
|
result := make([]string, 0, len(parts))
|
||||||
|
seen := make(map[string]struct{}, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
item := strings.TrimSpace(part)
|
||||||
|
if item == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[item]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[item] = struct{}{}
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinHermesEnvList(values []string) string {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
result := make([]string, 0, len(values))
|
||||||
|
seen := make(map[string]struct{}, len(values))
|
||||||
|
for _, value := range values {
|
||||||
|
item := strings.TrimSpace(value)
|
||||||
|
if item == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[item]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[item] = struct{}{}
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
return strings.Join(result, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstHermesTelegramBotToken(bots []dto.AgentTelegramBot, defaultAccount string) string {
|
||||||
|
for _, bot := range bots {
|
||||||
|
if bot.AccountID == defaultAccount || bot.IsDefault {
|
||||||
|
return bot.BotToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bots) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return bots[0].BotToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstHermesDiscordBotToken(bots []dto.AgentDiscordBot, defaultAccount string) string {
|
||||||
|
for _, bot := range bots {
|
||||||
|
if bot.AccountID == defaultAccount || bot.IsDefault {
|
||||||
|
return bot.Token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bots) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return bots[0].Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractHermesEnvBool(envMap map[string]string, key string, defaultValue bool) bool {
|
||||||
|
value, ok := envMap[key]
|
||||||
|
if !ok || value == "" {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return strings.EqualFold(value, "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateHermesPairingApproveResult(output string, err error) error {
|
||||||
|
if strings.Contains(output, "Approved!") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if strings.Contains(output, "not found or expired for platform") {
|
||||||
|
return buserr.New("ErrHermesPairingCodeUnavailable")
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if strings.TrimSpace(output) == "" {
|
||||||
|
return fmt.Errorf("unexpected hermes pairing approve result")
|
||||||
|
}
|
||||||
|
return errors.New(strings.TrimSpace(output))
|
||||||
|
}
|
||||||
|
if strings.Contains(err.Error(), "not found or expired for platform") {
|
||||||
|
return buserr.New("ErrHermesPairingCodeUnavailable")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,591 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const hermesWeixinLoginScript = `import asyncio
|
||||||
|
from gateway.platforms.weixin import qr_login
|
||||||
|
from hermes_cli.config import save_env_value
|
||||||
|
|
||||||
|
creds = asyncio.run(qr_login('/opt/data'))
|
||||||
|
print('RESULT =', creds)
|
||||||
|
|
||||||
|
if not creds:
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
save_env_value('WEIXIN_ACCOUNT_ID', creds.get('account_id', ''))
|
||||||
|
save_env_value('WEIXIN_TOKEN', creds.get('token', ''))
|
||||||
|
if creds.get('base_url'):
|
||||||
|
save_env_value('WEIXIN_BASE_URL', creds['base_url'])
|
||||||
|
save_env_value('WEIXIN_CDN_BASE_URL', 'https://novac2c.cdn.weixin.qq.com/c2c')
|
||||||
|
save_env_value('WEIXIN_DM_POLICY', 'open')
|
||||||
|
save_env_value('WEIXIN_ALLOW_ALL_USERS', 'true')
|
||||||
|
save_env_value('WEIXIN_ALLOWED_USERS', '')
|
||||||
|
save_env_value('WEIXIN_GROUP_POLICY', 'disabled')
|
||||||
|
save_env_value('WEIXIN_GROUP_ALLOWED_USERS', '')
|
||||||
|
save_env_value('WEIXIN_HOME_CHANNEL', creds.get('user_id', ''))
|
||||||
|
|
||||||
|
print('WEIXIN ENV WRITTEN')
|
||||||
|
print('Restart the Hermes-Agent container to apply the new Weixin settings.')
|
||||||
|
`
|
||||||
|
|
||||||
|
func readHermesQQBotChannelConfig(confDir string) (*dto.AgentQQBotConfig, error) {
|
||||||
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg, err := readHermesConfigMap(path.Join(confDir, "config.yaml"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
platform := childMap(childMap(cfg, "platforms"), "qq")
|
||||||
|
extra := childMap(platform, "extra")
|
||||||
|
appID := envMap["QQ_APP_ID"]
|
||||||
|
clientSecret := envMap["QQ_CLIENT_SECRET"]
|
||||||
|
dmPolicy := extractStringValue(extra["dm_policy"])
|
||||||
|
allowFrom := splitHermesEnvList(envMap["QQ_ALLOWED_USERS"])
|
||||||
|
groupPolicy := extractStringValue(extra["group_policy"])
|
||||||
|
groupAllowFrom := splitHermesEnvList(envMap["QQ_GROUP_ALLOWED_USERS"])
|
||||||
|
|
||||||
|
result := &dto.AgentQQBotConfig{
|
||||||
|
Enabled: extractBoolValue(platform["enabled"], false) && appID != "" && clientSecret != "",
|
||||||
|
DmPolicy: dmPolicy,
|
||||||
|
AllowFrom: allowFrom,
|
||||||
|
GroupPolicy: groupPolicy,
|
||||||
|
GroupAllowFrom: groupAllowFrom,
|
||||||
|
}
|
||||||
|
if appID != "" || clientSecret != "" {
|
||||||
|
result.Bots = []dto.AgentQQBotBot{
|
||||||
|
{
|
||||||
|
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||||
|
AccountID: "default",
|
||||||
|
Name: "Default",
|
||||||
|
Enabled: true,
|
||||||
|
IsDefault: true,
|
||||||
|
},
|
||||||
|
AppID: appID,
|
||||||
|
ClientSecret: clientSecret,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesQQBotChannelConfig(confDir string, config dto.AgentQQBotConfig) error {
|
||||||
|
envPath := path.Join(confDir, ".env")
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultBot := getDefaultQQBot(config.Bots)
|
||||||
|
envMap["QQ_APP_ID"] = defaultBot.AppID
|
||||||
|
envMap["QQ_CLIENT_SECRET"] = defaultBot.ClientSecret
|
||||||
|
delete(envMap, "QQ_ALLOWED_USERS")
|
||||||
|
delete(envMap, "QQ_GROUP_ALLOWED_USERS")
|
||||||
|
delete(envMap, "QQ_ALLOW_ALL_USERS")
|
||||||
|
delete(envMap, "QQ_MARKDOWN_SUPPORT")
|
||||||
|
if config.DmPolicy == "open" {
|
||||||
|
envMap["QQ_ALLOW_ALL_USERS"] = "true"
|
||||||
|
} else if config.DmPolicy == "allowlist" {
|
||||||
|
if allow := joinHermesEnvList(config.AllowFrom); allow != "" {
|
||||||
|
envMap["QQ_ALLOWED_USERS"] = allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.GroupPolicy == "allowlist" {
|
||||||
|
if allow := joinHermesEnvList(config.GroupAllowFrom); allow != "" {
|
||||||
|
envMap["QQ_GROUP_ALLOWED_USERS"] = allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
|
"QQ_APP_ID",
|
||||||
|
"QQ_CLIENT_SECRET",
|
||||||
|
"QQ_ALLOW_ALL_USERS",
|
||||||
|
"QQ_ALLOWED_USERS",
|
||||||
|
"QQ_GROUP_ALLOWED_USERS",
|
||||||
|
"QQ_HOME_CHANNEL",
|
||||||
|
"QQ_HOME_CHANNEL_NAME",
|
||||||
|
"QQ_STT_API_KEY",
|
||||||
|
"QQ_STT_BASE_URL",
|
||||||
|
"QQ_STT_MODEL",
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := path.Join(confDir, "config.yaml")
|
||||||
|
cfg, err := readHermesConfigMap(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
platform := ensureChildMap(ensureChildMap(cfg, "platforms"), "qq")
|
||||||
|
platform["enabled"] = config.Enabled && defaultBot.AppID != "" && defaultBot.ClientSecret != ""
|
||||||
|
extra := ensureChildMap(platform, "extra")
|
||||||
|
extra["markdown_support"] = true
|
||||||
|
extra["dm_policy"] = config.DmPolicy
|
||||||
|
extra["group_policy"] = config.GroupPolicy
|
||||||
|
delete(extra, "app_id")
|
||||||
|
delete(extra, "client_secret")
|
||||||
|
if config.DmPolicy == "allowlist" {
|
||||||
|
extra["allow_from"] = append([]string(nil), config.AllowFrom...)
|
||||||
|
} else {
|
||||||
|
delete(extra, "allow_from")
|
||||||
|
}
|
||||||
|
if config.GroupPolicy == "allowlist" {
|
||||||
|
extra["group_allow_from"] = append([]string(nil), config.GroupAllowFrom...)
|
||||||
|
} else {
|
||||||
|
delete(extra, "group_allow_from")
|
||||||
|
}
|
||||||
|
return writeHermesConfigMap(configPath, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHermesQQBotChannelConfig(confDir string) error {
|
||||||
|
if err := deleteHermesEnvKeys(confDir,
|
||||||
|
"QQ_APP_ID",
|
||||||
|
"QQ_CLIENT_SECRET",
|
||||||
|
"QQ_ALLOW_ALL_USERS",
|
||||||
|
"QQ_ALLOWED_USERS",
|
||||||
|
"QQ_GROUP_ALLOWED_USERS",
|
||||||
|
"QQ_HOME_CHANNEL",
|
||||||
|
"QQ_HOME_CHANNEL_NAME",
|
||||||
|
"QQ_STT_API_KEY",
|
||||||
|
"QQ_STT_BASE_URL",
|
||||||
|
"QQ_STT_MODEL",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return deleteHermesConfigSections(confDir, nil, []string{"qq"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesWecomChannelConfig(confDir string) (*dto.AgentWecomConfig, error) {
|
||||||
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg, err := readHermesConfigMap(path.Join(confDir, "config.yaml"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
platform := childMap(childMap(cfg, "platforms"), "wecom")
|
||||||
|
allowFrom := splitHermesEnvList(envMap["WECOM_ALLOWED_USERS"])
|
||||||
|
groupAllowFrom := splitHermesEnvList(envMap["WECOM_GROUP_ALLOWED_USERS"])
|
||||||
|
dmPolicy := envMap["WECOM_DM_POLICY"]
|
||||||
|
groupPolicy := envMap["WECOM_GROUP_POLICY"]
|
||||||
|
|
||||||
|
botID := envMap["WECOM_BOT_ID"]
|
||||||
|
secret := envMap["WECOM_SECRET"]
|
||||||
|
|
||||||
|
return &dto.AgentWecomConfig{
|
||||||
|
Enabled: extractBoolValue(platform["enabled"], false) && botID != "" && secret != "",
|
||||||
|
DmPolicy: dmPolicy,
|
||||||
|
AllowFrom: allowFrom,
|
||||||
|
GroupPolicy: groupPolicy,
|
||||||
|
GroupAllowFrom: groupAllowFrom,
|
||||||
|
BotID: botID,
|
||||||
|
Secret: secret,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesWecomChannelConfig(confDir string, config dto.AgentWecomConfig) error {
|
||||||
|
envPath := path.Join(confDir, ".env")
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if config.BotID != "" {
|
||||||
|
envMap["WECOM_BOT_ID"] = config.BotID
|
||||||
|
} else {
|
||||||
|
delete(envMap, "WECOM_BOT_ID")
|
||||||
|
}
|
||||||
|
if config.Secret != "" {
|
||||||
|
envMap["WECOM_SECRET"] = config.Secret
|
||||||
|
} else {
|
||||||
|
delete(envMap, "WECOM_SECRET")
|
||||||
|
}
|
||||||
|
delete(envMap, "WECOM_ALLOWED_USERS")
|
||||||
|
delete(envMap, "WECOM_GROUP_ALLOWED_USERS")
|
||||||
|
delete(envMap, "WECOM_ALLOW_ALL_USERS")
|
||||||
|
delete(envMap, "WECOM_DM_POLICY")
|
||||||
|
delete(envMap, "WECOM_GROUP_POLICY")
|
||||||
|
if config.DmPolicy != "" {
|
||||||
|
envMap["WECOM_DM_POLICY"] = config.DmPolicy
|
||||||
|
}
|
||||||
|
if config.GroupPolicy != "" {
|
||||||
|
envMap["WECOM_GROUP_POLICY"] = config.GroupPolicy
|
||||||
|
}
|
||||||
|
if config.DmPolicy == "allowlist" {
|
||||||
|
if allow := joinHermesEnvList(config.AllowFrom); allow != "" {
|
||||||
|
envMap["WECOM_ALLOWED_USERS"] = allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.GroupPolicy == "allowlist" {
|
||||||
|
if allow := joinHermesEnvList(config.GroupAllowFrom); allow != "" {
|
||||||
|
envMap["WECOM_GROUP_ALLOWED_USERS"] = allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
|
"WECOM_BOT_ID",
|
||||||
|
"WECOM_SECRET",
|
||||||
|
"WECOM_DM_POLICY",
|
||||||
|
"WECOM_ALLOWED_USERS",
|
||||||
|
"WECOM_GROUP_POLICY",
|
||||||
|
"WECOM_GROUP_ALLOWED_USERS",
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := path.Join(confDir, "config.yaml")
|
||||||
|
cfg, err := readHermesConfigMap(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
platform := ensureChildMap(ensureChildMap(cfg, "platforms"), "wecom")
|
||||||
|
platform["enabled"] = config.Enabled && config.BotID != "" && config.Secret != ""
|
||||||
|
extra := ensureChildMap(platform, "extra")
|
||||||
|
extra["dm_policy"] = config.DmPolicy
|
||||||
|
extra["group_policy"] = config.GroupPolicy
|
||||||
|
delete(extra, "bot_id")
|
||||||
|
delete(extra, "secret")
|
||||||
|
if config.DmPolicy == "allowlist" {
|
||||||
|
extra["allow_from"] = append([]string(nil), config.AllowFrom...)
|
||||||
|
} else {
|
||||||
|
delete(extra, "allow_from")
|
||||||
|
}
|
||||||
|
if config.GroupPolicy == "allowlist" {
|
||||||
|
extra["group_allow_from"] = append([]string(nil), config.GroupAllowFrom...)
|
||||||
|
} else {
|
||||||
|
delete(extra, "group_allow_from")
|
||||||
|
}
|
||||||
|
return writeHermesConfigMap(configPath, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHermesWecomChannelConfig(confDir string) error {
|
||||||
|
if err := deleteHermesEnvKeys(confDir,
|
||||||
|
"WECOM_BOT_ID",
|
||||||
|
"WECOM_SECRET",
|
||||||
|
"WECOM_ALLOW_ALL_USERS",
|
||||||
|
"WECOM_DM_POLICY",
|
||||||
|
"WECOM_ALLOWED_USERS",
|
||||||
|
"WECOM_GROUP_POLICY",
|
||||||
|
"WECOM_GROUP_ALLOWED_USERS",
|
||||||
|
"WECOM_HOME_CHANNEL",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return deleteHermesConfigSections(confDir, nil, []string{"wecom"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesDingTalkChannelConfig(confDir string) (*dto.AgentDingTalkConfig, error) {
|
||||||
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg, err := readHermesConfigMap(path.Join(confDir, "config.yaml"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
platform := childMap(childMap(cfg, "platforms"), "dingtalk")
|
||||||
|
extra := childMap(platform, "extra")
|
||||||
|
clientID := envMap["DINGTALK_CLIENT_ID"]
|
||||||
|
clientSecret := envMap["DINGTALK_CLIENT_SECRET"]
|
||||||
|
allowFrom := splitHermesEnvList(envMap["DINGTALK_ALLOWED_USERS"])
|
||||||
|
dmPolicy := ""
|
||||||
|
if extractHermesEnvBool(envMap, "DINGTALK_ALLOW_ALL_USERS", false) {
|
||||||
|
dmPolicy = "open"
|
||||||
|
} else if len(allowFrom) > 0 {
|
||||||
|
dmPolicy = "allowlist"
|
||||||
|
} else if extractStringValue(extra["unauthorized_dm_behavior"]) == "ignore" {
|
||||||
|
dmPolicy = "disabled"
|
||||||
|
} else if clientID != "" || clientSecret != "" {
|
||||||
|
dmPolicy = "pairing"
|
||||||
|
}
|
||||||
|
result := &dto.AgentDingTalkConfig{
|
||||||
|
Enabled: extractBoolValue(platform["enabled"], clientID != "" && clientSecret != ""),
|
||||||
|
DmPolicy: dmPolicy,
|
||||||
|
AllowFrom: allowFrom,
|
||||||
|
GroupPolicy: "",
|
||||||
|
GroupAllowFrom: []string{},
|
||||||
|
}
|
||||||
|
if clientID != "" || clientSecret != "" {
|
||||||
|
result.Bots = []dto.AgentDingTalkBot{
|
||||||
|
{
|
||||||
|
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||||
|
AccountID: "default",
|
||||||
|
Name: "Default",
|
||||||
|
Enabled: true,
|
||||||
|
IsDefault: true,
|
||||||
|
},
|
||||||
|
ClientID: clientID,
|
||||||
|
ClientSecret: clientSecret,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesDingTalkChannelConfig(confDir string, config dto.AgentDingTalkConfig) error {
|
||||||
|
envPath := path.Join(confDir, ".env")
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
clientID, clientSecret := firstHermesDingTalkBotCredentials(config.Bots)
|
||||||
|
if config.Enabled && clientID != "" {
|
||||||
|
envMap["DINGTALK_CLIENT_ID"] = clientID
|
||||||
|
} else {
|
||||||
|
delete(envMap, "DINGTALK_CLIENT_ID")
|
||||||
|
}
|
||||||
|
if config.Enabled && clientSecret != "" {
|
||||||
|
envMap["DINGTALK_CLIENT_SECRET"] = clientSecret
|
||||||
|
} else {
|
||||||
|
delete(envMap, "DINGTALK_CLIENT_SECRET")
|
||||||
|
}
|
||||||
|
delete(envMap, "DINGTALK_ALLOWED_USERS")
|
||||||
|
delete(envMap, "DINGTALK_ALLOW_ALL_USERS")
|
||||||
|
if config.DmPolicy == "allowlist" {
|
||||||
|
if allow := joinHermesEnvList(config.AllowFrom); allow != "" {
|
||||||
|
envMap["DINGTALK_ALLOWED_USERS"] = allow
|
||||||
|
}
|
||||||
|
} else if config.DmPolicy == "open" {
|
||||||
|
envMap["DINGTALK_ALLOW_ALL_USERS"] = "true"
|
||||||
|
}
|
||||||
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
|
"DINGTALK_CLIENT_ID",
|
||||||
|
"DINGTALK_CLIENT_SECRET",
|
||||||
|
"DINGTALK_ALLOW_ALL_USERS",
|
||||||
|
"DINGTALK_ALLOWED_USERS",
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := path.Join(confDir, "config.yaml")
|
||||||
|
cfg, err := readHermesConfigMap(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
platform := ensureChildMap(ensureChildMap(cfg, "platforms"), "dingtalk")
|
||||||
|
platform["enabled"] = config.Enabled && clientID != "" && clientSecret != ""
|
||||||
|
extra := ensureChildMap(platform, "extra")
|
||||||
|
switch config.DmPolicy {
|
||||||
|
case "pairing":
|
||||||
|
extra["unauthorized_dm_behavior"] = "pair"
|
||||||
|
case "disabled":
|
||||||
|
extra["unauthorized_dm_behavior"] = "ignore"
|
||||||
|
default:
|
||||||
|
delete(extra, "unauthorized_dm_behavior")
|
||||||
|
}
|
||||||
|
return writeHermesConfigMap(configPath, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHermesDingTalkChannelConfig(confDir string) error {
|
||||||
|
if err := deleteHermesEnvKeys(confDir,
|
||||||
|
"DINGTALK_CLIENT_ID",
|
||||||
|
"DINGTALK_CLIENT_SECRET",
|
||||||
|
"DINGTALK_ALLOW_ALL_USERS",
|
||||||
|
"DINGTALK_ALLOWED_USERS",
|
||||||
|
"DINGTALK_HOME_CHANNEL",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return deleteHermesConfigSections(confDir, nil, []string{"dingtalk"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesFeishuChannelConfig(confDir string) (*dto.AgentFeishuConfig, error) {
|
||||||
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg, err := readHermesConfigMap(path.Join(confDir, "config.yaml"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
platform := childMap(childMap(cfg, "platforms"), "feishu")
|
||||||
|
appID := envMap["FEISHU_APP_ID"]
|
||||||
|
appSecret := envMap["FEISHU_APP_SECRET"]
|
||||||
|
allowFrom := splitHermesEnvList(envMap["FEISHU_ALLOWED_USERS"])
|
||||||
|
dmPolicy := ""
|
||||||
|
if extractHermesEnvBool(envMap, "FEISHU_ALLOW_ALL_USERS", false) {
|
||||||
|
dmPolicy = "open"
|
||||||
|
} else if len(allowFrom) > 0 {
|
||||||
|
dmPolicy = "allowlist"
|
||||||
|
} else if appID != "" || appSecret != "" {
|
||||||
|
dmPolicy = "pairing"
|
||||||
|
}
|
||||||
|
groupPolicy := envMap["FEISHU_GROUP_POLICY"]
|
||||||
|
result := &dto.AgentFeishuConfig{
|
||||||
|
Enabled: extractBoolValue(platform["enabled"], appID != "" && appSecret != ""),
|
||||||
|
ThreadSession: false,
|
||||||
|
ReplyMode: "",
|
||||||
|
Streaming: false,
|
||||||
|
RequireMention: "",
|
||||||
|
GroupPolicy: groupPolicy,
|
||||||
|
GroupAllowFrom: []string{},
|
||||||
|
Domain: envMap["FEISHU_DOMAIN"],
|
||||||
|
ConnectionMode: envMap["FEISHU_CONNECTION_MODE"],
|
||||||
|
}
|
||||||
|
if appID != "" || appSecret != "" {
|
||||||
|
result.Bots = []dto.AgentFeishuBot{
|
||||||
|
{
|
||||||
|
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||||
|
AccountID: "default",
|
||||||
|
Name: "Default",
|
||||||
|
Enabled: true,
|
||||||
|
IsDefault: true,
|
||||||
|
},
|
||||||
|
AppID: appID,
|
||||||
|
AppSecret: appSecret,
|
||||||
|
DmPolicy: dmPolicy,
|
||||||
|
AllowFrom: allowFrom,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHermesFeishuChannelConfig(confDir string, config dto.AgentFeishuConfig) error {
|
||||||
|
envPath := path.Join(confDir, ".env")
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bot := firstHermesFeishuBot(config.Bots)
|
||||||
|
if config.GroupPolicy == "allowlist" && bot.DmPolicy == "pairing" {
|
||||||
|
return buserr.New("ErrHermesFeishuGroupAllowlistRequiresAllowlist")
|
||||||
|
}
|
||||||
|
if bot.AppID != "" {
|
||||||
|
envMap["FEISHU_APP_ID"] = bot.AppID
|
||||||
|
} else {
|
||||||
|
delete(envMap, "FEISHU_APP_ID")
|
||||||
|
}
|
||||||
|
if bot.AppSecret != "" {
|
||||||
|
envMap["FEISHU_APP_SECRET"] = bot.AppSecret
|
||||||
|
} else {
|
||||||
|
delete(envMap, "FEISHU_APP_SECRET")
|
||||||
|
}
|
||||||
|
envMap["FEISHU_DOMAIN"] = "feishu"
|
||||||
|
envMap["FEISHU_CONNECTION_MODE"] = "websocket"
|
||||||
|
envMap["FEISHU_GROUP_POLICY"] = config.GroupPolicy
|
||||||
|
delete(envMap, "FEISHU_ALLOW_ALL_USERS")
|
||||||
|
delete(envMap, "FEISHU_ALLOWED_USERS")
|
||||||
|
switch bot.DmPolicy {
|
||||||
|
case "open":
|
||||||
|
envMap["FEISHU_ALLOW_ALL_USERS"] = "true"
|
||||||
|
case "allowlist":
|
||||||
|
if allow := joinHermesEnvList(bot.AllowFrom); allow != "" {
|
||||||
|
envMap["FEISHU_ALLOWED_USERS"] = allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.GroupPolicy == "allowlist" {
|
||||||
|
if allow := joinHermesEnvList(bot.AllowFrom); allow != "" {
|
||||||
|
envMap["FEISHU_ALLOWED_USERS"] = allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
|
"FEISHU_APP_ID",
|
||||||
|
"FEISHU_APP_SECRET",
|
||||||
|
"FEISHU_DOMAIN",
|
||||||
|
"FEISHU_CONNECTION_MODE",
|
||||||
|
"FEISHU_ALLOW_ALL_USERS",
|
||||||
|
"FEISHU_ALLOWED_USERS",
|
||||||
|
"FEISHU_GROUP_POLICY",
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := path.Join(confDir, "config.yaml")
|
||||||
|
cfg, err := readHermesConfigMap(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
platform := ensureChildMap(ensureChildMap(cfg, "platforms"), "feishu")
|
||||||
|
platform["enabled"] = config.Enabled && bot.AppID != "" && bot.AppSecret != ""
|
||||||
|
return writeHermesConfigMap(configPath, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHermesFeishuChannelConfig(confDir string) error {
|
||||||
|
if err := deleteHermesEnvKeys(confDir,
|
||||||
|
"FEISHU_APP_ID",
|
||||||
|
"FEISHU_APP_SECRET",
|
||||||
|
"FEISHU_DOMAIN",
|
||||||
|
"FEISHU_CONNECTION_MODE",
|
||||||
|
"FEISHU_ALLOW_ALL_USERS",
|
||||||
|
"FEISHU_ALLOWED_USERS",
|
||||||
|
"FEISHU_GROUP_POLICY",
|
||||||
|
"FEISHU_HOME_CHANNEL",
|
||||||
|
"FEISHU_VERIFICATION_TOKEN",
|
||||||
|
"FEISHU_ENCRYPT_KEY",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return deleteHermesConfigSections(confDir, nil, []string{"feishu"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesWeixinChannelConfig(confDir string) (*dto.AgentWeixinConfig, error) {
|
||||||
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dto.AgentWeixinConfig{
|
||||||
|
Enabled: envMap["WEIXIN_ACCOUNT_ID"] != "" || envMap["WEIXIN_TOKEN"] != "",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHermesWeixinChannelConfig(confDir string) error {
|
||||||
|
if err := deleteHermesEnvKeys(confDir,
|
||||||
|
"WEIXIN_ACCOUNT_ID",
|
||||||
|
"WEIXIN_TOKEN",
|
||||||
|
"WEIXIN_BASE_URL",
|
||||||
|
"WEIXIN_CDN_BASE_URL",
|
||||||
|
"WEIXIN_DM_POLICY",
|
||||||
|
"WEIXIN_ALLOW_ALL_USERS",
|
||||||
|
"WEIXIN_ALLOWED_USERS",
|
||||||
|
"WEIXIN_GROUP_POLICY",
|
||||||
|
"WEIXIN_GROUP_ALLOWED_USERS",
|
||||||
|
"WEIXIN_HOME_CHANNEL",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return deleteHermesConfigSections(confDir, nil, []string{"weixin"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstHermesDingTalkBotCredentials(bots []dto.AgentDingTalkBot) (string, string) {
|
||||||
|
for _, bot := range bots {
|
||||||
|
if bot.IsDefault || bot.AccountID == "default" {
|
||||||
|
return bot.ClientID, bot.ClientSecret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bots) == 0 {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
return bots[0].ClientID, bots[0].ClientSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstHermesFeishuBot(bots []dto.AgentFeishuBot) dto.AgentFeishuBot {
|
||||||
|
for _, bot := range bots {
|
||||||
|
if bot.IsDefault || bot.AccountID == "default" {
|
||||||
|
return bot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bots) == 0 {
|
||||||
|
return dto.AgentFeishuBot{}
|
||||||
|
}
|
||||||
|
return bots[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildHermesWeixinLoginArgs(containerName string) []string {
|
||||||
|
return buildHermesDockerExecCommandArgs(
|
||||||
|
containerName,
|
||||||
|
"/opt/hermes/.venv/bin/python",
|
||||||
|
"-u",
|
||||||
|
"-c",
|
||||||
|
hermesWeixinLoginScript,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||||
|
)
|
||||||
|
|
||||||
|
const hermesSessionListLimit = 100
|
||||||
|
|
||||||
|
func (a AgentService) GetHermesChatSessions(req dto.AgentIDReq) ([]dto.AgentHermesChatSessionItem, error) {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if agent.AgentType != constant.AppHermesAgent {
|
||||||
|
return nil, fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
|
return listHermesChatSessionsFromStateDB(filepath.Join(install.GetPath(), "data", "state.db"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AgentService) RenameHermesChatSession(req dto.AgentHermesChatSessionRenameReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType != constant.AppHermesAgent {
|
||||||
|
return fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout(
|
||||||
|
"docker",
|
||||||
|
buildHermesDockerExecArgs(install.ContainerName, "sessions", "rename", req.ID, req.Title)...,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AgentService) DeleteHermesChatSession(req dto.AgentHermesChatSessionDeleteReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType != constant.AppHermesAgent {
|
||||||
|
return fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout(
|
||||||
|
"docker",
|
||||||
|
buildHermesDockerExecArgs(install.ContainerName, "sessions", "delete", req.ID, "--yes")...,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func listHermesChatSessionsFromStateDB(stateDBPath string) ([]dto.AgentHermesChatSessionItem, error) {
|
||||||
|
if !files.NewFileOp().Stat(stateDBPath) {
|
||||||
|
return []dto.AgentHermesChatSessionItem{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite", stateDBPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
rows, err := db.Query(`
|
||||||
|
SELECT
|
||||||
|
s.id,
|
||||||
|
COALESCE(NULLIF(TRIM(s.title), ''), s.id) AS title,
|
||||||
|
COALESCE(s.model, '') AS model,
|
||||||
|
COALESCE(s.message_count, 0) AS message_count,
|
||||||
|
s.started_at,
|
||||||
|
COALESCE(MAX(m.timestamp), s.started_at) AS last_active
|
||||||
|
FROM sessions s
|
||||||
|
LEFT JOIN messages m ON m.session_id = s.id
|
||||||
|
WHERE s.source = 'cli'
|
||||||
|
GROUP BY s.id, title, model, s.message_count, s.started_at
|
||||||
|
ORDER BY last_active DESC
|
||||||
|
LIMIT ?
|
||||||
|
`, hermesSessionListLimit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
items := make([]dto.AgentHermesChatSessionItem, 0, 8)
|
||||||
|
for rows.Next() {
|
||||||
|
var item dto.AgentHermesChatSessionItem
|
||||||
|
var title sql.NullString
|
||||||
|
var model sql.NullString
|
||||||
|
var startedAt sql.NullFloat64
|
||||||
|
var lastActive sql.NullFloat64
|
||||||
|
if err := rows.Scan(&item.ID, &title, &model, &item.MessageCount, &startedAt, &lastActive); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
item.Title = strings.TrimSpace(title.String)
|
||||||
|
if item.Title == "" {
|
||||||
|
item.Title = item.ID
|
||||||
|
}
|
||||||
|
item.Model = strings.TrimSpace(model.String)
|
||||||
|
item.StartedAt = formatHermesSessionTimestamp(startedAt)
|
||||||
|
item.LastActive = formatHermesSessionTimestamp(lastActive)
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatHermesSessionTimestamp(value sql.NullFloat64) string {
|
||||||
|
if !value.Valid || value.Float64 <= 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
seconds, fraction := math.Modf(value.Float64)
|
||||||
|
return time.Unix(int64(seconds), int64(fraction*float64(time.Second))).UTC().Format(time.RFC3339)
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
type hermesSkillsListEntry struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type hermesSkillsListPayload struct {
|
||||||
|
Skills []hermesSkillsListEntry `json:"skills"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func listHermesSkills(containerName string) ([]dto.AgentSkillItem, error) {
|
||||||
|
output, err := runHermesSkillsCommandWithStdout(2*time.Minute, containerName, "list", "--source", "all")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items, err := parseHermesSkillsListOutput(output)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
metadata, err := readHermesSkillsListMetadata(containerName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mergeHermesSkillsWithMetadata(items, metadata), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchHermesSkills(containerName, source, keyword string) ([]dto.AgentSkillSearchItem, error) {
|
||||||
|
if source != "official" && source != "skills-sh" {
|
||||||
|
return nil, fmt.Errorf("unsupported hermes skill source: %s", source)
|
||||||
|
}
|
||||||
|
output, err := runHermesSkillsCommandWithStdout(
|
||||||
|
2*time.Minute,
|
||||||
|
containerName,
|
||||||
|
"search",
|
||||||
|
keyword,
|
||||||
|
"--source",
|
||||||
|
source,
|
||||||
|
"--limit",
|
||||||
|
"20",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return parseHermesSkillSearchOutput(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHermesSkillsCommandWithStdout(timeout time.Duration, containerName string, hermesArgs ...string) (string, error) {
|
||||||
|
args := []string{"exec", "-e", "COLUMNS=240", "-u", "hermes", containerName, "hermes", "skills"}
|
||||||
|
args = append(args, hermesArgs...)
|
||||||
|
return cmd.NewCommandMgr(cmd.WithTimeout(timeout)).RunWithStdout("docker", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHermesSkillsListMetadata(containerName string) (map[string]hermesSkillsListEntry, error) {
|
||||||
|
output, err := cmd.NewCommandMgr(cmd.WithTimeout(2*time.Minute)).RunWithStdout(
|
||||||
|
"docker",
|
||||||
|
"exec",
|
||||||
|
"-u",
|
||||||
|
"hermes",
|
||||||
|
containerName,
|
||||||
|
"python",
|
||||||
|
"-c",
|
||||||
|
"import sys; sys.path.insert(0, '/opt/hermes'); from tools.skills_tool import skills_list; print(skills_list())",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return parseHermesSkillsListMetadataOutput(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHermesSkillsListOutput(output string) ([]dto.AgentSkillItem, error) {
|
||||||
|
headers, rows, err := parseHermesTableOutput(output)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items := make([]dto.AgentSkillItem, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
item := dto.AgentSkillItem{
|
||||||
|
Name: row[headers["Name"]],
|
||||||
|
Category: row[headers["Category"]],
|
||||||
|
Source: row[headers["Source"]],
|
||||||
|
Trust: row[headers["Trust"]],
|
||||||
|
Uninstallable: row[headers["Source"]] != "builtin" && row[headers["Source"]] != "local",
|
||||||
|
}
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHermesSkillsListMetadataOutput(output string) (map[string]hermesSkillsListEntry, error) {
|
||||||
|
if strings.TrimSpace(output) == "" {
|
||||||
|
return map[string]hermesSkillsListEntry{}, nil
|
||||||
|
}
|
||||||
|
var payload hermesSkillsListPayload
|
||||||
|
if err := json.Unmarshal([]byte(output), &payload); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
metadata := make(map[string]hermesSkillsListEntry, len(payload.Skills))
|
||||||
|
for _, skill := range payload.Skills {
|
||||||
|
if skill.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
metadata[skill.Name] = skill
|
||||||
|
}
|
||||||
|
return metadata, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeHermesSkillsWithMetadata(items []dto.AgentSkillItem, metadata map[string]hermesSkillsListEntry) []dto.AgentSkillItem {
|
||||||
|
for i := range items {
|
||||||
|
entry, ok := metadata[items[i].Name]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
items[i].Description = entry.Description
|
||||||
|
if items[i].Category == "" && entry.Category != "" {
|
||||||
|
items[i].Category = entry.Category
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHermesSkillSearchOutput(output string) ([]dto.AgentSkillSearchItem, error) {
|
||||||
|
if strings.Contains(output, "No skills found matching your query.") {
|
||||||
|
return []dto.AgentSkillSearchItem{}, nil
|
||||||
|
}
|
||||||
|
headers, rows, err := parseHermesTableOutput(output)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items := make([]dto.AgentSkillSearchItem, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
identifier := row[headers["Identifier"]]
|
||||||
|
items = append(items, dto.AgentSkillSearchItem{
|
||||||
|
Slug: identifier,
|
||||||
|
Identifier: identifier,
|
||||||
|
Name: row[headers["Name"]],
|
||||||
|
Description: row[headers["Description"]],
|
||||||
|
Source: row[headers["Source"]],
|
||||||
|
Trust: row[headers["Trust"]],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHermesTableOutput(output string) (map[string]int, [][]string, error) {
|
||||||
|
lines := strings.Split(strings.TrimSpace(ansiEscapePattern.ReplaceAllString(output, "")), "\n")
|
||||||
|
var headers []string
|
||||||
|
rows := make([][]string, 0)
|
||||||
|
var current []string
|
||||||
|
|
||||||
|
for _, rawLine := range lines {
|
||||||
|
line := strings.TrimSpace(rawLine)
|
||||||
|
if (!strings.HasPrefix(line, "│") || !strings.HasSuffix(line, "│")) &&
|
||||||
|
(!strings.HasPrefix(line, "┃") || !strings.HasSuffix(line, "┃")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line = strings.ReplaceAll(line, "┃", "│")
|
||||||
|
parts := strings.Split(line, "│")
|
||||||
|
if len(parts) < 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cols := make([]string, 0, len(parts)-2)
|
||||||
|
for _, part := range parts[1 : len(parts)-1] {
|
||||||
|
cols = append(cols, strings.TrimSpace(part))
|
||||||
|
}
|
||||||
|
if len(headers) == 0 {
|
||||||
|
headers = cols
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cols[0] != "" {
|
||||||
|
if current != nil {
|
||||||
|
rows = append(rows, current)
|
||||||
|
}
|
||||||
|
current = cols
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for i := range cols {
|
||||||
|
if cols[i] == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current[i] == "" {
|
||||||
|
current[i] = cols[i]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
current[i] = current[i] + " " + cols[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if current != nil {
|
||||||
|
rows = append(rows, current)
|
||||||
|
}
|
||||||
|
if len(headers) == 0 {
|
||||||
|
return nil, nil, fmt.Errorf("hermes skills table not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
headerIndex := make(map[string]int, len(headers))
|
||||||
|
for i, header := range headers {
|
||||||
|
headerIndex[header] = i
|
||||||
|
}
|
||||||
|
return headerIndex, rows, nil
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -101,7 +102,7 @@ func countOpenclawConfiguredChannels(conf map[string]interface{}) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadOpenclawOverviewSkillStats(containerName string) (int, error) {
|
func loadOpenclawOverviewSkillStats(containerName string) (int, error) {
|
||||||
output, err := runDockerExecWithStdout(5*time.Minute, containerName, "sh", "-c", "openclaw skills list --json 2>&1")
|
output, err := cmd.RunDockerExecWithStdout(5*time.Minute, containerName, "openclaw", "skills", "list", "--json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,35 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"archive/zip"
|
||||||
|
"compress/gzip"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const clawhubGlobalRegistry = "https://clawhub.com"
|
const clawhubGlobalRegistry = "https://clawhub.com"
|
||||||
const clawhubChinaRegistry = "https://mirror-cn.clawhub.com"
|
const clawhubChinaRegistry = "https://mirror-cn.clawhub.com"
|
||||||
|
const localSkillHubSource = "local-hub"
|
||||||
|
const localSkillHubPublishedStatus = "published"
|
||||||
|
const clawHubSkillTmpSubDir = "1panel/tmp/clawhub-skills"
|
||||||
|
const hermesManagedSkillsDir = "/opt/data/skills"
|
||||||
|
|
||||||
type openclawSkillsList struct {
|
type openclawSkillsList struct {
|
||||||
Skills []openclawSkillListItem `json:"skills"`
|
Skills []openclawSkillListItem `json:"skills"`
|
||||||
@@ -37,18 +52,33 @@ type skillhubSearchPayload struct {
|
|||||||
Results []dto.AgentSkillSearchItem `json:"results"`
|
Results []dto.AgentSkillSearchItem `json:"results"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type localSkillHubItem struct {
|
||||||
|
ID uint
|
||||||
|
Name string
|
||||||
|
Status string
|
||||||
|
ApplicableAgent string
|
||||||
|
PackagePath string
|
||||||
|
}
|
||||||
|
|
||||||
var clawhubSearchLinePattern = regexp.MustCompile(`^(\S+)\s+(.+?)\s+\(([\d.]+)\)$`)
|
var clawhubSearchLinePattern = regexp.MustCompile(`^(\S+)\s+(.+?)\s+\(([\d.]+)\)$`)
|
||||||
var ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)
|
var ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)
|
||||||
|
var safeLocalSkillDirNamePattern = regexp.MustCompile(`[^a-zA-Z0-9._-]+`)
|
||||||
|
|
||||||
func (a AgentService) ListSkills(req dto.AgentIDReq) ([]dto.AgentSkillItem, error) {
|
func (a AgentService) ListSkills(req dto.AgentIDReq) ([]dto.AgentSkillItem, error) {
|
||||||
_, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
output, err := runDockerExecWithStdout(5*time.Minute, install.ContainerName, "sh", "-c", "openclaw skills list --json 2>&1")
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return listHermesSkills(install.ContainerName)
|
||||||
|
}
|
||||||
|
if agent.AgentType != constant.AppOpenclaw {
|
||||||
|
return nil, fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
|
output, err := cmd.RunDockerExecWithStdout(5*time.Minute, install.ContainerName, "openclaw", "skills", "list", "--json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -59,13 +89,25 @@ func (a AgentService) ListSkills(req dto.AgentIDReq) ([]dto.AgentSkillItem, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) SearchSkills(req dto.AgentSkillSearchReq) ([]dto.AgentSkillSearchItem, error) {
|
func (a AgentService) SearchSkills(req dto.AgentSkillSearchReq) ([]dto.AgentSkillSearchItem, error) {
|
||||||
_, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
|
if req.Source == localSkillHubSource {
|
||||||
|
return nil, fmt.Errorf("local skills hub is provided by enterprise edition")
|
||||||
|
}
|
||||||
|
if global.CONF.Base.IsOffline {
|
||||||
|
return nil, fmt.Errorf("offline environment cannot access remote Skills Hub")
|
||||||
|
}
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
return searchHermesSkills(install.ContainerName, req.Source, req.Keyword)
|
||||||
|
}
|
||||||
|
if agent.AgentType != constant.AppOpenclaw {
|
||||||
|
return nil, fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
output, err := loadOpenclawSkillSearchOutput(install.ContainerName, req.Source, req.Keyword)
|
output, err := loadOpenclawSkillSearchOutput(install.ContainerName, req.Source, req.Keyword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -102,20 +144,61 @@ func (a AgentService) UpdateSkill(req dto.AgentSkillUpdateReq) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
|
func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
|
||||||
_, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
|
if global.CONF.Base.IsOffline && req.Source != localSkillHubSource {
|
||||||
|
return fmt.Errorf("offline environment cannot access remote Skills Hub")
|
||||||
|
}
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if req.Source == localSkillHubSource {
|
||||||
|
if !global.IsMaster {
|
||||||
|
return fmt.Errorf("local skills hub is only available on the master node")
|
||||||
|
}
|
||||||
|
hostPath, skillName, err := resolveLocalSkillPackage(agent.AgentType, req.Slug)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
installTask, err := task.NewTaskWithOps(skillName, task.TaskInstall, task.TaskScopeAI, req.TaskID, req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
installTask.AddSubTask("Install local skill", func(t *task.Task) error {
|
||||||
|
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(20*time.Minute))
|
||||||
|
return installLocalSkillPackage(mgr, install.ContainerName, agent.AgentType, agent.ConfigPath, hostPath, skillName)
|
||||||
|
}, nil)
|
||||||
|
go func() {
|
||||||
|
if err := installTask.Execute(); err != nil {
|
||||||
|
global.LOG.Errorf("install local skill failed: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
installTask, err := task.NewTaskWithOps(req.Slug, task.TaskInstall, task.TaskScopeAI, req.TaskID, req.AgentID)
|
installTask, err := task.NewTaskWithOps(req.Slug, task.TaskInstall, task.TaskScopeAI, req.TaskID, req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
|
installTask.AddSubTask("Install Hermes skill", func(t *task.Task) error {
|
||||||
|
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(20*time.Minute))
|
||||||
|
return mgr.Run("docker", buildHermesDockerExecArgs(install.ContainerName, "skills", "install", req.Slug, "--yes")...)
|
||||||
|
}, nil)
|
||||||
|
go func() {
|
||||||
|
if err := installTask.Execute(); err != nil {
|
||||||
|
global.LOG.Errorf("install hermes skill failed: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if agent.AgentType != constant.AppOpenclaw {
|
||||||
|
return fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
installTask.AddSubTask("Install OpenClaw skill", func(t *task.Task) error {
|
installTask.AddSubTask("Install OpenClaw skill", func(t *task.Task) error {
|
||||||
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(20*time.Minute))
|
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(20*time.Minute))
|
||||||
return mgr.Run("docker", "exec", install.ContainerName, "sh", "-c", buildOpenclawSkillInstallCommand(req.Source, req.Slug))
|
return installOpenclawSkill(mgr, install.ContainerName, req.Source, req.Slug)
|
||||||
}, nil)
|
}, nil)
|
||||||
go func() {
|
go func() {
|
||||||
if err := installTask.Execute(); err != nil {
|
if err := installTask.Execute(); err != nil {
|
||||||
@@ -125,6 +208,537 @@ func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func installLocalSkillPackage(mgr *cmd.CommandHelper, containerName, agentType, configPath, hostPath, skillName string) error {
|
||||||
|
targetDir, err := resolveAgentLocalSkillDir(agentType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tempDir, err := os.MkdirTemp("", "1panel-agent-skill-*")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
extractRoot := filepath.Join(tempDir, "extract")
|
||||||
|
if err := os.MkdirAll(extractRoot, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := extractLocalSkillPackage(hostPath, extractRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
copyRoot, installedSkillName, err := normalizeLocalSkillInstallRoot(extractRoot, filepath.Join(tempDir, "install"), skillName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := mgr.Run("docker", "exec", containerName, "mkdir", "-p", targetDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := mgr.Run("docker", "cp", copyRoot+"/.", containerName+":"+targetDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agentType == constant.AppOpenclaw {
|
||||||
|
return registerOpenclawLocalSkill(containerName, configPath, installedSkillName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveAgentLocalSkillDir(agentType string) (string, error) {
|
||||||
|
switch agentType {
|
||||||
|
case constant.AppOpenclaw:
|
||||||
|
return openclawManagedSkillsDir, nil
|
||||||
|
case constant.AppHermesAgent:
|
||||||
|
return hermesManagedSkillsDir, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("%s does not support", agentType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveLocalSkillPackage(agentType, skillID string) (string, string, error) {
|
||||||
|
skillID = strings.TrimSpace(skillID)
|
||||||
|
id, err := strconv.ParseUint(skillID, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
return "", "", fmt.Errorf("invalid local skill id")
|
||||||
|
}
|
||||||
|
dbPath := filepath.Join(global.CONF.Base.InstallDir, "1panel", "db", "enterprise.db")
|
||||||
|
if _, err := os.Stat(dbPath); err != nil {
|
||||||
|
return "", "", fmt.Errorf("local skills hub is unavailable")
|
||||||
|
}
|
||||||
|
db, err := common.GetDBWithPath(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
defer common.CloseDB(db)
|
||||||
|
var item localSkillHubItem
|
||||||
|
if err := db.Table("skill_hub_items").
|
||||||
|
Select("id,name,status,applicable_agent,package_path").
|
||||||
|
Where("id = ? AND status = ?", uint(id), localSkillHubPublishedStatus).
|
||||||
|
First(&item).Error; err != nil {
|
||||||
|
return "", "", fmt.Errorf("local skill is not published or does not exist")
|
||||||
|
}
|
||||||
|
if !matchLocalSkillAgent(item.ApplicableAgent, agentType) {
|
||||||
|
return "", "", fmt.Errorf("local skill does not support %s", agentType)
|
||||||
|
}
|
||||||
|
packagePath, err := validateLocalSkillPackagePath(item.PackagePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return packagePath, sanitizeLocalSkillDirName(item.Name, packagePath, skillID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchLocalSkillAgent(applicableAgent, agentType string) bool {
|
||||||
|
applicableAgent = strings.TrimSpace(applicableAgent)
|
||||||
|
if applicableAgent == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, item := range strings.Split(applicableAgent, ",") {
|
||||||
|
normalized := normalizeLocalSkillAgent(strings.TrimSpace(item))
|
||||||
|
if normalized == "common" || normalized == normalizeLocalSkillAgent(agentType) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeLocalSkillAgent(agentType string) string {
|
||||||
|
switch strings.ToLower(strings.TrimSpace(agentType)) {
|
||||||
|
case constant.AppHermesAgent:
|
||||||
|
return "hermes"
|
||||||
|
default:
|
||||||
|
return strings.ToLower(strings.TrimSpace(agentType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeLocalSkillDirName(name, packagePath, fallback string) string {
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
if name == "" {
|
||||||
|
name = strings.TrimSuffix(filepath.Base(packagePath), localSkillPackageExtension(packagePath))
|
||||||
|
}
|
||||||
|
name = strings.Trim(safeLocalSkillDirNamePattern.ReplaceAllString(name, "-"), "-")
|
||||||
|
if name == "" || name == "." || name == ".." {
|
||||||
|
name = "skill-" + strings.TrimSpace(fallback)
|
||||||
|
}
|
||||||
|
if name == "skill-" {
|
||||||
|
return "skill"
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateLocalSkillPackagePath(packagePath string) (string, error) {
|
||||||
|
packagePath = strings.TrimSpace(packagePath)
|
||||||
|
if packagePath == "" {
|
||||||
|
return "", fmt.Errorf("skill package path is required")
|
||||||
|
}
|
||||||
|
if _, err := localSkillPackageFormat(packagePath); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
absPath, err := filepath.Abs(packagePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resolvedPath, err := filepath.EvalSymlinks(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !isLocalSkillPackageAllowedPath(resolvedPath) {
|
||||||
|
return "", fmt.Errorf("invalid local skill package path")
|
||||||
|
}
|
||||||
|
info, err := os.Stat(resolvedPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return "", fmt.Errorf("skill package must be a file")
|
||||||
|
}
|
||||||
|
return resolvedPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLocalSkillPackageAllowedPath(resolvedPath string) bool {
|
||||||
|
roots := []string{
|
||||||
|
filepath.Join(global.CONF.Base.InstallDir, "1panel", "uploads", "skills-hub"),
|
||||||
|
filepath.Join(global.CONF.Base.InstallDir, filepath.FromSlash(clawHubSkillTmpSubDir)),
|
||||||
|
}
|
||||||
|
for _, root := range roots {
|
||||||
|
if isPathInsideResolvedRoot(resolvedPath, root) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPathInsideResolvedRoot(resolvedPath, root string) bool {
|
||||||
|
absRoot, err := filepath.Abs(root)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
resolvedRoot, err := filepath.EvalSymlinks(absRoot)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
rel, err := filepath.Rel(resolvedRoot, resolvedPath)
|
||||||
|
return err == nil && rel != "." && rel != ".." && !strings.HasPrefix(rel, ".."+string(os.PathSeparator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeLocalSkillInstallRoot(extractRoot, installRoot, skillName string) (string, string, error) {
|
||||||
|
entries, err := os.ReadDir(extractRoot)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if len(entries) == 0 {
|
||||||
|
return "", "", fmt.Errorf("skill package is empty")
|
||||||
|
}
|
||||||
|
if len(entries) == 1 && entries[0].IsDir() {
|
||||||
|
return extractRoot, entries[0].Name(), nil
|
||||||
|
}
|
||||||
|
skillRoot := filepath.Join(installRoot, skillName)
|
||||||
|
if err := os.MkdirAll(skillRoot, 0755); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if err := copyLocalDirContents(extractRoot, skillRoot); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return installRoot, skillName, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerOpenclawLocalSkill(containerName, configPath, skillName string) error {
|
||||||
|
conf, err := readOpenclawConfig(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
skillKey, err := getOpenclawSkillKey(containerName, skillName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
setOpenclawSkillEnabled(conf, skillKey, true)
|
||||||
|
return writeOpenclawConfigRaw(configPath, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractLocalSkillPackage(packagePath, targetDir string) error {
|
||||||
|
format, err := localSkillPackageFormat(packagePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch format {
|
||||||
|
case "zip":
|
||||||
|
return unzipLocalSkillPackage(packagePath, targetDir)
|
||||||
|
case "tar", "targz":
|
||||||
|
return untarLocalSkillPackage(packagePath, targetDir, format == "targz")
|
||||||
|
case "7z":
|
||||||
|
return un7zLocalSkillPackage(packagePath, targetDir)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported skill package format")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unzipLocalSkillPackage(packagePath, targetDir string) error {
|
||||||
|
reader, err := zip.OpenReader(packagePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
root, err := filepath.Abs(targetDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, file := range reader.File {
|
||||||
|
name, err := safeLocalSkillZipEntryName(file.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
targetPath := filepath.Join(root, name)
|
||||||
|
rel, err := filepath.Rel(root, targetPath)
|
||||||
|
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
||||||
|
return fmt.Errorf("invalid zip entry: %s", file.Name)
|
||||||
|
}
|
||||||
|
if file.FileInfo().IsDir() {
|
||||||
|
if err := os.MkdirAll(targetPath, file.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if file.Mode()&os.ModeSymlink != 0 {
|
||||||
|
return fmt.Errorf("unsupported zip entry: %s", file.Name)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rc, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dst, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, file.Mode())
|
||||||
|
if err != nil {
|
||||||
|
_ = rc.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, copyErr := io.Copy(dst, rc)
|
||||||
|
closeErr := dst.Close()
|
||||||
|
_ = rc.Close()
|
||||||
|
if copyErr != nil {
|
||||||
|
return copyErr
|
||||||
|
}
|
||||||
|
if closeErr != nil {
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func untarLocalSkillPackage(packagePath, targetDir string, gzipped bool) error {
|
||||||
|
src, err := os.Open(packagePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
var reader io.Reader = src
|
||||||
|
var gzipReader *gzip.Reader
|
||||||
|
if gzipped {
|
||||||
|
gzipReader, err = gzip.NewReader(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer gzipReader.Close()
|
||||||
|
reader = gzipReader
|
||||||
|
}
|
||||||
|
root, err := filepath.Abs(targetDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tarReader := tar.NewReader(reader)
|
||||||
|
for {
|
||||||
|
header, err := tarReader.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name, err := safeLocalSkillZipEntryName(header.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
targetPath := filepath.Join(root, name)
|
||||||
|
rel, err := filepath.Rel(root, targetPath)
|
||||||
|
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
||||||
|
return fmt.Errorf("invalid tar entry: %s", header.Name)
|
||||||
|
}
|
||||||
|
info := header.FileInfo()
|
||||||
|
if info.IsDir() {
|
||||||
|
if err := os.MkdirAll(targetPath, info.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if header.Typeflag != tar.TypeReg && header.Typeflag != tar.TypeRegA {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dst, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, copyErr := io.Copy(dst, tarReader)
|
||||||
|
closeErr := dst.Close()
|
||||||
|
if copyErr != nil {
|
||||||
|
return copyErr
|
||||||
|
}
|
||||||
|
if closeErr != nil {
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func un7zLocalSkillPackage(packagePath, targetDir string) error {
|
||||||
|
if _, err := listLocal7zEntries(packagePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
root, err := filepath.Abs(targetDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bin, err := findLocal7zBinary()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tempDir, err := os.MkdirTemp("", "1panel-agent-skill-7z-*")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
if err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).Run(bin, "x", "-y", "-o"+tempDir, packagePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := validateExtractedLocalSkillPackage(tempDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return copyLocalDirContents(tempDir, root)
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeLocalSkillZipEntryName(name string) (string, error) {
|
||||||
|
clean := path.Clean(strings.ReplaceAll(name, "\\", "/"))
|
||||||
|
clean = strings.TrimLeft(clean, "/")
|
||||||
|
if clean == "." || clean == "" || clean == ".." || strings.HasPrefix(clean, "../") {
|
||||||
|
return "", fmt.Errorf("invalid zip entry: %s", name)
|
||||||
|
}
|
||||||
|
return clean, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyLocalDirContents(sourceRoot, targetRoot string) error {
|
||||||
|
return filepath.Walk(sourceRoot, func(sourcePath string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sourcePath == sourceRoot {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if info.Mode()&os.ModeSymlink != 0 || (!info.IsDir() && !info.Mode().IsRegular()) {
|
||||||
|
return fmt.Errorf("unsupported skill package entry: %s", sourcePath)
|
||||||
|
}
|
||||||
|
rel, err := filepath.Rel(sourceRoot, sourcePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name, err := safeLocalSkillZipEntryName(rel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
targetPath := filepath.Join(targetRoot, name)
|
||||||
|
if info.IsDir() {
|
||||||
|
return os.MkdirAll(targetPath, info.Mode())
|
||||||
|
}
|
||||||
|
return copyLocalSkillFile(sourcePath, targetPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyLocalSkillFile(source, target string) error {
|
||||||
|
src, err := os.Open(source)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dst, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(dst, src); err != nil {
|
||||||
|
_ = dst.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return dst.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateExtractedLocalSkillPackage(root string) error {
|
||||||
|
return filepath.Walk(root, func(currentPath string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if currentPath == root {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if info.Mode()&os.ModeSymlink != 0 || (!info.IsDir() && !info.Mode().IsRegular()) {
|
||||||
|
return fmt.Errorf("unsupported skill package entry: %s", currentPath)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func localSkillPackageExtension(name string) string {
|
||||||
|
lower := strings.ToLower(filepath.Base(strings.TrimSpace(name)))
|
||||||
|
if lower == "" || lower == "." {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(lower, ".tar.gz") {
|
||||||
|
return ".tar.gz"
|
||||||
|
}
|
||||||
|
return strings.ToLower(filepath.Ext(lower))
|
||||||
|
}
|
||||||
|
|
||||||
|
func localSkillPackageFormat(name string) (string, error) {
|
||||||
|
switch localSkillPackageExtension(name) {
|
||||||
|
case ".zip":
|
||||||
|
return "zip", nil
|
||||||
|
case ".7z":
|
||||||
|
return "7z", nil
|
||||||
|
case ".tar":
|
||||||
|
return "tar", nil
|
||||||
|
case ".tar.gz":
|
||||||
|
return "targz", nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("only .zip, .7z, .tar, and .tar.gz skill packages are supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLocal7zBinary() (string, error) {
|
||||||
|
for _, name := range []string{"7z", "7za", "7zr"} {
|
||||||
|
if path, err := exec.LookPath(name); err == nil {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("7z command is required to process .7z skill packages")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listLocal7zEntries(packagePath string) ([]string, error) {
|
||||||
|
bin, err := findLocal7zBinary()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
output, err := exec.Command(bin, "l", "-slt", packagePath).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
if message := strings.TrimSpace(string(output)); message != "" {
|
||||||
|
return nil, fmt.Errorf("7z failed: %w: %s", err, message)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("7z failed: %w", err)
|
||||||
|
}
|
||||||
|
entries := make([]string, 0)
|
||||||
|
for _, line := range strings.Split(string(output), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if !strings.HasPrefix(line, "Path = ") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entry := strings.TrimSpace(strings.TrimPrefix(line, "Path = "))
|
||||||
|
if entry == "" || entry == packagePath || entry == filepath.Base(packagePath) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, err := safeLocalSkillZipEntryName(entry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
entries = append(entries, name)
|
||||||
|
}
|
||||||
|
if len(entries) == 0 {
|
||||||
|
return nil, fmt.Errorf("skill package is empty")
|
||||||
|
}
|
||||||
|
return entries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AgentService) UninstallSkill(req dto.AgentSkillUninstallReq) error {
|
||||||
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if agent.AgentType != constant.AppHermesAgent {
|
||||||
|
return fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
|
}
|
||||||
|
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cmd.NewCommandMgr(cmd.WithTimeout(5*time.Minute)).Run(
|
||||||
|
"docker",
|
||||||
|
buildHermesDockerExecCommandArgs(
|
||||||
|
install.ContainerName,
|
||||||
|
"sh",
|
||||||
|
"-lc",
|
||||||
|
fmt.Sprintf(`printf 'y\n' | %s skills uninstall "$1"`, hermesExecutablePath),
|
||||||
|
"sh",
|
||||||
|
req.Name,
|
||||||
|
)...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func parseOpenclawSkillsList(output string) ([]dto.AgentSkillItem, error) {
|
func parseOpenclawSkillsList(output string) ([]dto.AgentSkillItem, error) {
|
||||||
payloadBytes, err := extractEmbeddedJSON(output)
|
payloadBytes, err := extractEmbeddedJSON(output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -153,15 +767,19 @@ func parseOpenclawSkillsList(output string) ([]dto.AgentSkillItem, error) {
|
|||||||
func loadOpenclawSkillSearchOutput(containerName, source, keyword string) (string, error) {
|
func loadOpenclawSkillSearchOutput(containerName, source, keyword string) (string, error) {
|
||||||
switch source {
|
switch source {
|
||||||
case "skillhub":
|
case "skillhub":
|
||||||
return runDockerExecWithStdout(2*time.Minute, containerName, "skillhub", "search", keyword, "--json")
|
return cmd.RunDockerExecWithStdout(2*time.Minute, containerName, "skillhub", "search", keyword, "--json")
|
||||||
default:
|
default:
|
||||||
return runDockerExecWithStdout(
|
return cmd.NewCommandMgr(cmd.WithTimeout(2*time.Minute)).
|
||||||
2*time.Minute,
|
RunWithStdout(
|
||||||
containerName,
|
"docker",
|
||||||
"sh",
|
"exec",
|
||||||
"-c",
|
"-e",
|
||||||
fmt.Sprintf("CLAWHUB_REGISTRY=%q clawhub search %q", resolveClawhubRegistry(source), keyword),
|
"CLAWHUB_REGISTRY="+resolveClawhubRegistry(source),
|
||||||
)
|
containerName,
|
||||||
|
"clawhub",
|
||||||
|
"search",
|
||||||
|
keyword,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,22 +827,28 @@ func parseClawhubSearchResult(output, source string) []dto.AgentSkillSearchItem
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildOpenclawSkillInstallCommand(source, slug string) string {
|
func installOpenclawSkill(mgr *cmd.CommandHelper, containerName, source, slug string) error {
|
||||||
|
if err := mgr.Run("docker", "exec", containerName, "mkdir", "-p", openclawManagedSkillsDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
switch source {
|
switch source {
|
||||||
case "clawhub-global", "clawhub-cn":
|
case "clawhub-global", "clawhub-cn":
|
||||||
return fmt.Sprintf(
|
return mgr.Run(
|
||||||
"mkdir -p %s && CLAWHUB_REGISTRY=%q clawhub --workdir /home/node/.openclaw --dir skills install %q",
|
"docker",
|
||||||
openclawManagedSkillsDir,
|
"exec",
|
||||||
resolveClawhubRegistry(source),
|
"-e",
|
||||||
|
"CLAWHUB_REGISTRY="+resolveClawhubRegistry(source),
|
||||||
|
containerName,
|
||||||
|
"clawhub",
|
||||||
|
"--workdir",
|
||||||
|
"/home/node/.openclaw",
|
||||||
|
"--dir",
|
||||||
|
"skills",
|
||||||
|
"install",
|
||||||
slug,
|
slug,
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf(
|
return mgr.Run("docker", "exec", containerName, "skillhub", "--dir", openclawManagedSkillsDir, "install", slug)
|
||||||
"mkdir -p %s && skillhub --dir %s install %q",
|
|
||||||
openclawManagedSkillsDir,
|
|
||||||
openclawManagedSkillsDir,
|
|
||||||
slug,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +862,7 @@ func resolveClawhubRegistry(source string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getOpenclawSkillKey(containerName, name string) (string, error) {
|
func getOpenclawSkillKey(containerName, name string) (string, error) {
|
||||||
output, err := runDockerExecWithStdout(2*time.Minute, containerName, "sh", "-c", fmt.Sprintf("openclaw skills info %q --json 2>&1", name))
|
output, err := cmd.RunDockerExecWithStdout(2*time.Minute, containerName, "openclaw", "skills", "info", name, "--json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -255,7 +879,7 @@ func parseOpenclawSkillKey(name, output string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if payload.SkillKey == "" {
|
if payload.SkillKey == "" {
|
||||||
return "", fmt.Errorf("skill %s does not have a skillKey", name)
|
return name, nil
|
||||||
}
|
}
|
||||||
return payload.SkillKey, nil
|
return payload.SkillKey, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,11 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||||
|
agentenv "github.com/1Panel-dev/1Panel/agent/utils/env"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,8 +44,8 @@ func loadOpenclawAgentByID(agentID uint) (*model.Agent, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if agent.AgentType == constant.AppCopaw {
|
if agent.AgentType != constant.AppOpenclaw {
|
||||||
return nil, fmt.Errorf("copaw does not support")
|
return nil, fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
}
|
}
|
||||||
return agent, nil
|
return agent, nil
|
||||||
}
|
}
|
||||||
@@ -60,11 +61,6 @@ func ensureContainerRunning(containerName string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDockerExecWithStdout(timeout time.Duration, containerName string, args ...string) (string, error) {
|
|
||||||
commandArgs := append([]string{"exec", containerName}, args...)
|
|
||||||
return cmd.NewCommandMgr(cmd.WithTimeout(timeout)).RunWithStdout("docker", commandArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveAgentAccountInput(provider, apiKey, baseURL string) (resolvedAgentAccountInput, error) {
|
func resolveAgentAccountInput(provider, apiKey, baseURL string) (resolvedAgentAccountInput, error) {
|
||||||
resolvedAPIKey := strings.TrimSpace(apiKey)
|
resolvedAPIKey := strings.TrimSpace(apiKey)
|
||||||
resolvedBaseURL := strings.TrimSpace(baseURL)
|
resolvedBaseURL := strings.TrimSpace(baseURL)
|
||||||
@@ -382,10 +378,19 @@ func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map
|
|||||||
item.BridgePort = toInt(bridge)
|
item.BridgePort = toInt(bridge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if agentType == constant.AppHermesAgent {
|
||||||
|
auth := readHermesDashboardAuthFromInstall(appInstall)
|
||||||
|
item.DashboardUsername = auth.Username
|
||||||
|
item.DashboardPassword = auth.Password
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isAgentAppKey(appKey string) bool {
|
||||||
|
return appKey == constant.AppOpenclaw || appKey == constant.AppCopaw || appKey == constant.AppHermesAgent
|
||||||
|
}
|
||||||
|
|
||||||
func isOpenclawLegacyHTTPVersion(version string) bool {
|
func isOpenclawLegacyHTTPVersion(version string) bool {
|
||||||
return !common.CompareAppVersion(version, openclawHTTPSVersion)
|
return !common.CompareAppVersion(version, openclawHTTPSVersion)
|
||||||
}
|
}
|
||||||
@@ -580,6 +585,9 @@ func checkAgentUpgradable(install model.AppInstall) bool {
|
|||||||
if install.App.ID == 0 {
|
if install.App.ID == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if ignoreUpdate(install) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(install.App.ID))
|
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(install.App.ID))
|
||||||
if err != nil || len(details) == 0 {
|
if err != nil || len(details) == 0 {
|
||||||
return false
|
return false
|
||||||
@@ -859,13 +867,15 @@ func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName,
|
|||||||
if err := writeOpenclawConfigRaw(configPath, conf); err != nil {
|
if err := writeOpenclawConfigRaw(configPath, conf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
envPath := path.Join(confDir, ".env")
|
envMap := map[string]string{
|
||||||
lines := []string{fmt.Sprintf("OPENCLAW_GATEWAY_TOKEN=%s", token)}
|
"OPENCLAW_GATEWAY_TOKEN": token,
|
||||||
if envKey := providercatalog.EnvKey(account.Provider); envKey != "" && account.APIKey != "" {
|
|
||||||
lines = append(lines, fmt.Sprintf("%s=%s", envKey, account.APIKey))
|
|
||||||
}
|
}
|
||||||
content := strings.Join(lines, "\n") + "\n"
|
order := []string{"OPENCLAW_GATEWAY_TOKEN"}
|
||||||
return fileOp.SaveFile(envPath, content, 0600)
|
if envKey := providercatalog.EnvKey(account.Provider); envKey != "" && account.APIKey != "" {
|
||||||
|
envMap[envKey] = account.APIKey
|
||||||
|
order = append(order, envKey)
|
||||||
|
}
|
||||||
|
return writeAgentEnvMap(path.Join(confDir, ".env"), envMap, order)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveOpenclawFallbackModels(account *model.AgentAccount, primaryModel string, fallbackIDs []string) ([]string, error) {
|
func resolveOpenclawFallbackModels(account *model.AgentAccount, primaryModel string, fallbackIDs []string) ([]string, error) {
|
||||||
@@ -1002,14 +1012,10 @@ func buildOpenclawModelsFromAccount(account *model.AgentAccount, selectedModel s
|
|||||||
return "", nil, nil, err
|
return "", nil, nil, err
|
||||||
}
|
}
|
||||||
if len(accountModels) == 0 {
|
if len(accountModels) == 0 {
|
||||||
return "", nil, nil, fmt.Errorf("model is required")
|
return "", nil, nil, buserr.New("ErrAgentModelNotInAccount")
|
||||||
}
|
|
||||||
selectedModel = strings.TrimSpace(selectedModel)
|
|
||||||
if selectedModel == "" {
|
|
||||||
selectedModel = strings.TrimSpace(accountModels[0].ID)
|
|
||||||
}
|
}
|
||||||
if selectedModel == "" {
|
if selectedModel == "" {
|
||||||
return "", nil, nil, fmt.Errorf("model is required")
|
return "", nil, nil, buserr.New("ErrAgentModelNotInAccount")
|
||||||
}
|
}
|
||||||
selectedAccountModel, err := requireAgentAccountModelForProvider(account.Provider, accountModels, selectedModel)
|
selectedAccountModel, err := requireAgentAccountModelForProvider(account.Provider, accountModels, selectedModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1460,9 +1466,6 @@ func ensureAccountModelsNotBound(account *model.AgentAccount, models []dto.Agent
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, agent := range agents {
|
for _, agent := range agents {
|
||||||
if strings.TrimSpace(agent.Model) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := findAgentAccountModelForProvider(account.Provider, models, agent.Model); !ok {
|
if _, ok := findAgentAccountModelForProvider(account.Provider, models, agent.Model); !ok {
|
||||||
return buserr.WithName("ErrAgentModelInUse", agent.Name)
|
return buserr.WithName("ErrAgentModelInUse", agent.Name)
|
||||||
}
|
}
|
||||||
@@ -1530,6 +1533,41 @@ func readInstallEnv(envStr string) map[string]interface{} {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readAgentEnvMap(envPath string) (map[string]string, error) {
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
if !fileOp.Stat(envPath) {
|
||||||
|
return map[string]string{}, nil
|
||||||
|
}
|
||||||
|
envMap, err := godotenv.Read(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return envMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeAgentEnvMap(envPath string, envMap map[string]string, order []string) error {
|
||||||
|
if len(envMap) == 0 {
|
||||||
|
return files.NewFileOp().SaveFile(envPath, "", 0600)
|
||||||
|
}
|
||||||
|
return agentenv.WriteWithOrder(envMap, envPath, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
func upsertAgentEnv(envPath string, values map[string]string, order []string, overwrite bool) error {
|
||||||
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for key, value := range values {
|
||||||
|
if key == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if overwrite || strings.TrimSpace(envMap[key]) == "" {
|
||||||
|
envMap[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writeAgentEnvMap(envPath, envMap, order)
|
||||||
|
}
|
||||||
|
|
||||||
func maskKey(value string) string {
|
func maskKey(value string) string {
|
||||||
trim := strings.TrimSpace(value)
|
trim := strings.TrimSpace(value)
|
||||||
if len(trim) <= 6 {
|
if len(trim) <= 6 {
|
||||||
|
|||||||
@@ -48,6 +48,27 @@ func (a AgentService) BindWebsite(req dto.AgentWebsiteBindReq) error {
|
|||||||
return ensureOpenclawWebsiteAllowedOrigin(agent, &website)
|
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 {
|
func hydrateAgentWebsiteItems(items []dto.AgentItem) error {
|
||||||
explicitWebsiteMap, err := loadAgentWebsiteMapByID(items)
|
explicitWebsiteMap, err := loadAgentWebsiteMapByID(items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -126,9 +147,11 @@ func fillAgentWebsiteItems(items []dto.AgentItem, explicitWebsiteMap map[uint]mo
|
|||||||
if !ok {
|
if !ok {
|
||||||
items[index].WebsiteID = 0
|
items[index].WebsiteID = 0
|
||||||
items[index].WebsitePrimaryDomain = ""
|
items[index].WebsitePrimaryDomain = ""
|
||||||
|
items[index].WebsiteType = ""
|
||||||
items[index].WebsiteProtocol = ""
|
items[index].WebsiteProtocol = ""
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
items[index].WebsiteType = website.Type
|
||||||
items[index].WebsiteProtocol = website.Protocol
|
items[index].WebsiteProtocol = website.Protocol
|
||||||
websiteDomains := websiteDomainMap[items[index].WebsiteID]
|
websiteDomains := websiteDomainMap[items[index].WebsiteID]
|
||||||
if len(websiteDomains) == 0 {
|
if len(websiteDomains) == 0 {
|
||||||
@@ -192,7 +215,7 @@ func bindDeploymentWebsiteToAgentByAppInstall(website *model.Website) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if appInstall.App.Key != constant.AppOpenclaw && appInstall.App.Key != constant.AppCopaw {
|
if appInstall.App.Key != constant.AppOpenclaw && appInstall.App.Key != constant.AppCopaw && appInstall.App.Key != constant.AppHermesAgent {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +238,7 @@ func bindDeploymentWebsiteToAgentByAppInstall(website *model.Website) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ensureOpenclawWebsiteAllowedOrigin(agent *model.Agent, website *model.Website) error {
|
func ensureOpenclawWebsiteAllowedOrigin(agent *model.Agent, website *model.Website) error {
|
||||||
if agent == nil || website == nil || agent.AgentType == constant.AppCopaw {
|
if agent == nil || website == nil || agent.AgentType != constant.AppOpenclaw {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ func (u *AIToolService) LoadDetail(name string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama show %s", containerName, name)
|
stdout, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "ollama", "show", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ func (u *AIToolService) Close(name string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := cmd.RunDefaultBashCf("docker exec %s ollama stop %s", containerName, name); err != nil {
|
if err := cmd.NewCommandMgr().Run("docker", "exec", containerName, "ollama", "stop", name); err != nil {
|
||||||
return fmt.Errorf("handle ollama stop %s failed, %v", name, err)
|
return fmt.Errorf("handle ollama stop %s failed, %v", name, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -194,7 +194,7 @@ func (u *AIToolService) Delete(req dto.ForceDelete) error {
|
|||||||
}
|
}
|
||||||
for _, item := range ollamaList {
|
for _, item := range ollamaList {
|
||||||
if item.Status != constant.StatusDeleted {
|
if item.Status != constant.StatusDeleted {
|
||||||
if err := cmd.RunDefaultBashCf("docker exec %s ollama rm %s", containerName, item.Name); err != nil && !req.ForceDelete {
|
if err := cmd.NewCommandMgr().Run("docker", "exec", containerName, "ollama", "rm", item.Name); err != nil && !req.ForceDelete {
|
||||||
return fmt.Errorf("handle ollama rm %s failed, %v", item.Name, err)
|
return fmt.Errorf("handle ollama rm %s failed, %v", item.Name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,7 +210,7 @@ func (u *AIToolService) Sync() ([]dto.OllamaModelDropList, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama list", containerName)
|
stdout, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "ollama", "list")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -382,12 +382,15 @@ func LoadContainerName() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadModelSize(name string, containerName string) (string, error) {
|
func loadModelSize(name string, containerName string) (string, error) {
|
||||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama list | grep %s", containerName, name)
|
stdout, err := cmd.RunDockerExecWithStdout(20*time.Second, containerName, "ollama", "list")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
lines := strings.Split(stdout, "\n")
|
lines := strings.Split(stdout, "\n")
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
|
if !strings.Contains(line, name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
parts := strings.Fields(line)
|
parts := strings.Fields(line)
|
||||||
if len(parts) < 5 {
|
if len(parts) < 5 {
|
||||||
continue
|
continue
|
||||||
|
|||||||
+213
-21
@@ -3,6 +3,13 @@ package service
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"mime"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||||
@@ -15,24 +22,27 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/agent/utils/email"
|
"github.com/1Panel-dev/1Panel/agent/utils/email"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||||
"github.com/shirou/gopsutil/v4/disk"
|
"github.com/shirou/gopsutil/v4/disk"
|
||||||
"mime"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AlertService struct{}
|
type AlertService struct{}
|
||||||
|
|
||||||
|
var eeHiddenAlertTypes = []string{"licenseException", "panelUpdate", "panelPwdEndTime"}
|
||||||
|
var communityAlertMethodTypeNames = map[string]string{
|
||||||
|
constant.WeCom: "WeCom",
|
||||||
|
constant.DingTalk: "DingTalk",
|
||||||
|
constant.FeiShu: "FeiShu",
|
||||||
|
constant.SMS: "SMS",
|
||||||
|
}
|
||||||
|
|
||||||
type IAlertService interface {
|
type IAlertService interface {
|
||||||
PageAlert(req dto.AlertSearch) (int64, []dto.AlertDTO, error)
|
PageAlert(req dto.AlertSearch) (int64, []dto.AlertDTO, error)
|
||||||
GetAlerts() ([]dto.AlertDTO, error)
|
GetAlerts() ([]dto.AlertDTO, error)
|
||||||
CreateAlert(create dto.AlertCreate) error
|
CreateAlert(create dto.AlertCreate, operator string) error
|
||||||
UpdateAlert(req dto.AlertUpdate) error
|
UpdateAlert(req dto.AlertUpdate, operator string) error
|
||||||
DeleteAlert(id uint) error
|
DeleteAlert(id uint) error
|
||||||
GetAlert(id uint) (dto.AlertDTO, error)
|
GetAlert(id uint) (dto.AlertDTO, error)
|
||||||
UpdateStatus(id uint, status string) error
|
UpdateStatus(id uint, status string) error
|
||||||
ExternalUpdateAlert(req dto.AlertCreate) error
|
ExternalUpdateAlert(req dto.AlertCreate, operator string) error
|
||||||
|
|
||||||
GetDisks() ([]dto.DiskDTO, error)
|
GetDisks() ([]dto.DiskDTO, error)
|
||||||
PageAlertLogs(req dto.AlertLogSearch) (int64, []dto.AlertLogDTO, error)
|
PageAlertLogs(req dto.AlertLogSearch) (int64, []dto.AlertLogDTO, error)
|
||||||
@@ -40,8 +50,9 @@ type IAlertService interface {
|
|||||||
GetClams() ([]dto.ClamDTO, error)
|
GetClams() ([]dto.ClamDTO, error)
|
||||||
GetCronJobs(req dto.CronJobReq) ([]dto.CronJobDTO, error)
|
GetCronJobs(req dto.CronJobReq) ([]dto.CronJobDTO, error)
|
||||||
|
|
||||||
GetAlertConfig() ([]model.AlertConfig, error)
|
GetAlertConfig(req dto.AlertConfigQuery) ([]model.AlertConfig, error)
|
||||||
UpdateAlertConfig(req dto.AlertConfigUpdate) error
|
PageAlertConfig(req dto.AlertConfigPageReq) (int64, []model.AlertConfig, error)
|
||||||
|
UpdateAlertConfig(req dto.AlertConfigUpdate, operator string) error
|
||||||
DeleteAlertConfig(id uint) error
|
DeleteAlertConfig(id uint) error
|
||||||
TestAlertConfig(req dto.AlertConfigTest) (bool, error)
|
TestAlertConfig(req dto.AlertConfigTest) (bool, error)
|
||||||
}
|
}
|
||||||
@@ -55,6 +66,9 @@ func (a AlertService) PageAlert(search dto.AlertSearch) (int64, []dto.AlertDTO,
|
|||||||
opts []repo.DBOption
|
opts []repo.DBOption
|
||||||
result []dto.AlertDTO
|
result []dto.AlertDTO
|
||||||
)
|
)
|
||||||
|
if global.CONF.Base.IsEnterprise {
|
||||||
|
opts = append(opts, alertRepo.WithByTypeNotIn(eeHiddenAlertTypes))
|
||||||
|
}
|
||||||
if search.Status != "" {
|
if search.Status != "" {
|
||||||
opts = append(opts, repo.WithByStatus(search.Status))
|
opts = append(opts, repo.WithByStatus(search.Status))
|
||||||
}
|
}
|
||||||
@@ -81,6 +95,8 @@ func (a AlertService) PageAlert(search dto.AlertSearch) (int64, []dto.AlertDTO,
|
|||||||
Status: item.Status,
|
Status: item.Status,
|
||||||
SendCount: item.SendCount,
|
SendCount: item.SendCount,
|
||||||
AdvancedParams: item.AdvancedParams,
|
AdvancedParams: item.AdvancedParams,
|
||||||
|
CreateUser: item.CreateUser,
|
||||||
|
UpdateUser: item.UpdateUser,
|
||||||
CreatedAt: item.CreatedAt,
|
CreatedAt: item.CreatedAt,
|
||||||
UpdatedAt: item.UpdatedAt,
|
UpdatedAt: item.UpdatedAt,
|
||||||
})
|
})
|
||||||
@@ -112,6 +128,8 @@ func (a AlertService) GetAlerts() ([]dto.AlertDTO, error) {
|
|||||||
Status: item.Status,
|
Status: item.Status,
|
||||||
SendCount: item.SendCount,
|
SendCount: item.SendCount,
|
||||||
AdvancedParams: item.AdvancedParams,
|
AdvancedParams: item.AdvancedParams,
|
||||||
|
CreateUser: item.CreateUser,
|
||||||
|
UpdateUser: item.UpdateUser,
|
||||||
CreatedAt: item.CreatedAt,
|
CreatedAt: item.CreatedAt,
|
||||||
UpdatedAt: item.UpdatedAt,
|
UpdatedAt: item.UpdatedAt,
|
||||||
})
|
})
|
||||||
@@ -120,7 +138,10 @@ func (a AlertService) GetAlerts() ([]dto.AlertDTO, error) {
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AlertService) CreateAlert(create dto.AlertCreate) error {
|
func (a AlertService) CreateAlert(create dto.AlertCreate, operator string) error {
|
||||||
|
if err := a.validateCommunityAlertMethod(create.Method); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var alertID uint
|
var alertID uint
|
||||||
var alertInfo model.Alert
|
var alertInfo model.Alert
|
||||||
if create.Project != "" {
|
if create.Project != "" {
|
||||||
@@ -137,7 +158,7 @@ func (a AlertService) CreateAlert(create dto.AlertCreate) error {
|
|||||||
return buserr.WithErr("ErrStructTransform", err)
|
return buserr.WithErr("ErrStructTransform", err)
|
||||||
}
|
}
|
||||||
upAlert.ID = alertID
|
upAlert.ID = alertID
|
||||||
err := a.UpdateAlert(upAlert)
|
err := a.UpdateAlert(upAlert, operator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -146,6 +167,8 @@ func (a AlertService) CreateAlert(create dto.AlertCreate) error {
|
|||||||
if err := copier.Copy(&alertInfo, &create); err != nil {
|
if err := copier.Copy(&alertInfo, &create); err != nil {
|
||||||
return buserr.WithErr("ErrStructTransform", err)
|
return buserr.WithErr("ErrStructTransform", err)
|
||||||
}
|
}
|
||||||
|
alertInfo.CreateUser = operator
|
||||||
|
alertInfo.UpdateUser = operator
|
||||||
|
|
||||||
if err := alertRepo.Create(&alertInfo); err != nil {
|
if err := alertRepo.Create(&alertInfo); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -156,7 +179,10 @@ func (a AlertService) CreateAlert(create dto.AlertCreate) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AlertService) UpdateAlert(req dto.AlertUpdate) error {
|
func (a AlertService) UpdateAlert(req dto.AlertUpdate, operator string) error {
|
||||||
|
if err := a.validateCommunityAlertMethod(req.Method); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
upMap := make(map[string]interface{})
|
upMap := make(map[string]interface{})
|
||||||
upMap["id"] = req.ID
|
upMap["id"] = req.ID
|
||||||
@@ -169,6 +195,7 @@ func (a AlertService) UpdateAlert(req dto.AlertUpdate) error {
|
|||||||
upMap["status"] = req.Status
|
upMap["status"] = req.Status
|
||||||
upMap["send_count"] = req.SendCount
|
upMap["send_count"] = req.SendCount
|
||||||
upMap["advanced_params"] = req.AdvancedParams
|
upMap["advanced_params"] = req.AdvancedParams
|
||||||
|
upMap["update_user"] = operator
|
||||||
|
|
||||||
if err := alertRepo.Update(upMap, repo.WithByID(req.ID)); err != nil {
|
if err := alertRepo.Update(upMap, repo.WithByID(req.ID)); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -308,12 +335,25 @@ func (a AlertService) GetDisks() ([]dto.DiskDTO, error) {
|
|||||||
|
|
||||||
func executeDiskCommand() (string, error) {
|
func executeDiskCommand() (string, error) {
|
||||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
|
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
|
||||||
stdout, err := cmdMgr.RunWithStdoutBashC("df -hT -P | grep '/' | grep -v tmpfs | grep -v 'snap/core' | grep -v udev")
|
stdout, err := cmdMgr.RunWithStdout("df", "-hT", "-P")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
|
cmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
|
||||||
stdout, err = cmdMgr2.RunWithStdoutBashC("df -lhT -P | grep '/' | grep -v tmpfs | grep -v 'snap/core' | grep -v udev")
|
stdout, err = cmdMgr2.RunWithStdout("df", "-lhT", "-P")
|
||||||
}
|
}
|
||||||
return stdout, err
|
if err != nil {
|
||||||
|
return stdout, err
|
||||||
|
}
|
||||||
|
var lines []string
|
||||||
|
for _, line := range strings.Split(stdout, "\n") {
|
||||||
|
if !strings.Contains(line, "/") || strings.Contains(line, "tmpfs") || strings.Contains(line, "snap/core") || strings.Contains(line, "udev") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
if len(lines) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return strings.Join(lines, "\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldExclude(fields []string, mountPoint string, excludes map[string]struct{}) bool {
|
func shouldExclude(fields []string, mountPoint string, excludes map[string]struct{}) bool {
|
||||||
@@ -441,17 +481,40 @@ func (a AlertService) GetCronJobs(req dto.CronJobReq) ([]dto.CronJobDTO, error)
|
|||||||
return cronJobs, err
|
return cronJobs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AlertService) GetAlertConfig() ([]model.AlertConfig, error) {
|
func (a AlertService) GetAlertConfig(req dto.AlertConfigQuery) ([]model.AlertConfig, error) {
|
||||||
var (
|
var (
|
||||||
opts []repo.DBOption
|
opts []repo.DBOption
|
||||||
configs []model.AlertConfig
|
configs []model.AlertConfig
|
||||||
)
|
)
|
||||||
|
if len(req.ExcludeTypes) > 0 {
|
||||||
|
opts = append(opts, alertRepo.WithByTypeNotIn(req.ExcludeTypes))
|
||||||
|
}
|
||||||
opts = append(opts, repo.WithByStatus(constant.AlertEnable))
|
opts = append(opts, repo.WithByStatus(constant.AlertEnable))
|
||||||
configs, err := alertRepo.AlertConfigList(opts...)
|
configs, err := alertRepo.AlertConfigList(opts...)
|
||||||
return configs, err
|
return configs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AlertService) UpdateAlertConfig(req dto.AlertConfigUpdate) error {
|
func (a AlertService) PageAlertConfig(req dto.AlertConfigPageReq) (int64, []model.AlertConfig, error) {
|
||||||
|
opts := []repo.DBOption{
|
||||||
|
alertRepo.WithByTypeNotIn([]string{"common"}),
|
||||||
|
repo.WithOrderDesc("created_at"),
|
||||||
|
}
|
||||||
|
if len(req.ExcludeTypes) > 0 {
|
||||||
|
opts = append(opts, alertRepo.WithByTypeNotIn(req.ExcludeTypes))
|
||||||
|
}
|
||||||
|
return alertRepo.PageAlertConfig(req.Page, req.PageSize, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AlertService) UpdateAlertConfig(req dto.AlertConfigUpdate, operator string) error {
|
||||||
|
if err := a.validateCommunityAlertConfigType(req.Type); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := a.checkAlertConfigDisplayNameUnique(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := a.checkAlertConfigSMSPhoneUnique(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if req.ID != 0 {
|
if req.ID != 0 {
|
||||||
upMap := make(map[string]interface{})
|
upMap := make(map[string]interface{})
|
||||||
upMap["id"] = req.ID
|
upMap["id"] = req.ID
|
||||||
@@ -459,6 +522,7 @@ func (a AlertService) UpdateAlertConfig(req dto.AlertConfigUpdate) error {
|
|||||||
upMap["title"] = req.Title
|
upMap["title"] = req.Title
|
||||||
upMap["status"] = req.Status
|
upMap["status"] = req.Status
|
||||||
upMap["config"] = req.Config
|
upMap["config"] = req.Config
|
||||||
|
upMap["update_user"] = operator
|
||||||
if err := alertRepo.UpdateAlertConfig(upMap, repo.WithByID(req.ID)); err != nil {
|
if err := alertRepo.UpdateAlertConfig(upMap, repo.WithByID(req.ID)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -467,6 +531,8 @@ func (a AlertService) UpdateAlertConfig(req dto.AlertConfigUpdate) error {
|
|||||||
if err := copier.Copy(&alertConfig, &req); err != nil {
|
if err := copier.Copy(&alertConfig, &req); err != nil {
|
||||||
return buserr.WithErr("ErrStructTransform", err)
|
return buserr.WithErr("ErrStructTransform", err)
|
||||||
}
|
}
|
||||||
|
alertConfig.CreateUser = operator
|
||||||
|
alertConfig.UpdateUser = operator
|
||||||
if err := alertRepo.CreateAlertConfig(&alertConfig); err != nil {
|
if err := alertRepo.CreateAlertConfig(&alertConfig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -475,7 +541,130 @@ func (a AlertService) UpdateAlertConfig(req dto.AlertConfigUpdate) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AlertService) checkAlertConfigSMSPhoneUnique(req dto.AlertConfigUpdate) error {
|
||||||
|
if req.Type != constant.SMSConfig {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
phone := alertConfigSMSPhone(req.Config)
|
||||||
|
configs, err := alertRepo.AlertConfigList(alertRepo.WithByType(req.Type))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, config := range configs {
|
||||||
|
if req.ID != 0 && config.ID == req.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if alertConfigSMSPhone(config.Config) == phone {
|
||||||
|
return buserr.New("ErrAlertConfigPhoneExist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AlertService) checkAlertConfigDisplayNameUnique(req dto.AlertConfigUpdate) error {
|
||||||
|
displayName := alertConfigDisplayName(req.Type, req.Config)
|
||||||
|
if displayName == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
configs, err := alertRepo.AlertConfigList(alertRepo.WithByType(req.Type))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, config := range configs {
|
||||||
|
if req.ID != 0 && config.ID == req.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if alertConfigDisplayName(config.Type, config.Config) == displayName {
|
||||||
|
return buserr.New("ErrNameIsExist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AlertService) validateCommunityAlertMethod(method string) error {
|
||||||
|
if global.CONF.Base.IsEnterprise || global.CONF.Base.Edition == "cn" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(method) == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range strings.Split(method, ",") {
|
||||||
|
item = strings.TrimSpace(item)
|
||||||
|
if item == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if configID, err := strconv.ParseUint(item, 10, 64); err == nil {
|
||||||
|
config, err := alertRepo.GetConfigById(uint(configID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, ok := communityAlertMethodTypeNames[config.Type]; ok {
|
||||||
|
return buserr.WithErr("ErrAlertMethodNotSupported", nil)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := communityAlertMethodTypeNames[item]; ok {
|
||||||
|
return buserr.WithErr("ErrAlertMethodNotSupported", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AlertService) validateCommunityAlertConfigType(configType string) error {
|
||||||
|
if global.CONF.Base.IsEnterprise || global.CONF.Base.Edition == "cn" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, ok := communityAlertMethodTypeNames[configType]; ok {
|
||||||
|
return buserr.WithErr("ErrAlertMethodNotSupported", nil)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func alertConfigDisplayName(configType, configData string) string {
|
||||||
|
switch configType {
|
||||||
|
case constant.Email, constant.WeCom, constant.DingTalk, constant.FeiShu, constant.Bark, constant.SMS:
|
||||||
|
var cfg struct {
|
||||||
|
DisplayName string `json:"displayName"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(configData), &cfg); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(cfg.DisplayName)
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func alertConfigSMSPhone(configData string) string {
|
||||||
|
var cfg struct {
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(configData), &cfg); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(cfg.Phone)
|
||||||
|
}
|
||||||
|
|
||||||
func (a AlertService) DeleteAlertConfig(id uint) error {
|
func (a AlertService) DeleteAlertConfig(id uint) error {
|
||||||
|
_, err := alertRepo.GetConfigById(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
usedAlerts, err := alertRepo.List(alertRepo.WithByAlertMethodContainsConfigID(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(usedAlerts) > 0 {
|
||||||
|
return buserr.New("ErrAlertConfigInUse")
|
||||||
|
}
|
||||||
return alertRepo.DeleteAlertConfig(repo.WithByID(id))
|
return alertRepo.DeleteAlertConfig(repo.WithByID(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,14 +690,17 @@ func (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {
|
|||||||
Body: i18n.GetMsgByKey("TestAlert"),
|
Body: i18n.GetMsgByKey("TestAlert"),
|
||||||
IsHTML: false,
|
IsHTML: false,
|
||||||
}
|
}
|
||||||
transport := xpack.LoadRequestTransport()
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
if err := email.SendMail(cfg, msg, transport); err != nil {
|
if err := email.SendMail(cfg, msg, transport); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate) error {
|
func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate, operator string) error {
|
||||||
|
if err := a.validateCommunityAlertMethod(updateAlert.Method); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
upMap := make(map[string]interface{})
|
upMap := make(map[string]interface{})
|
||||||
var newStatus string
|
var newStatus string
|
||||||
if updateAlert.SendCount == 0 {
|
if updateAlert.SendCount == 0 {
|
||||||
@@ -552,7 +744,7 @@ func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate) error {
|
|||||||
} else {
|
} else {
|
||||||
if updateAlert.Method != "" && updateAlert.Title != "" {
|
if updateAlert.Method != "" && updateAlert.Title != "" {
|
||||||
updateAlert.Status = newStatus
|
updateAlert.Status = newStatus
|
||||||
if err := a.CreateAlert(updateAlert); err != nil {
|
if err := a.CreateAlert(updateAlert, operator); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+233
-104
@@ -4,9 +4,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
@@ -23,7 +25,7 @@ import (
|
|||||||
"github.com/shirou/gopsutil/v4/disk"
|
"github.com/shirou/gopsutil/v4/disk"
|
||||||
"github.com/shirou/gopsutil/v4/load"
|
"github.com/shirou/gopsutil/v4/load"
|
||||||
"github.com/shirou/gopsutil/v4/mem"
|
"github.com/shirou/gopsutil/v4/mem"
|
||||||
"github.com/shirou/gopsutil/v4/net"
|
gnet "github.com/shirou/gopsutil/v4/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -34,7 +36,7 @@ const (
|
|||||||
|
|
||||||
type AlertTaskHelper struct {
|
type AlertTaskHelper struct {
|
||||||
DiskIO chan []disk.IOCountersStat
|
DiskIO chan []disk.IOCountersStat
|
||||||
NetIO chan []net.IOCountersStat
|
NetIO chan []gnet.IOCountersStat
|
||||||
}
|
}
|
||||||
|
|
||||||
type IAlertTaskHelper interface {
|
type IAlertTaskHelper interface {
|
||||||
@@ -46,6 +48,7 @@ type IAlertTaskHelper interface {
|
|||||||
|
|
||||||
var cpuLoad1, cpuLoad5, cpuLoad15 []float64
|
var cpuLoad1, cpuLoad5, cpuLoad15 []float64
|
||||||
var memoryLoad1, memoryLoad5, memoryLoad15 []float64
|
var memoryLoad1, memoryLoad5, memoryLoad15 []float64
|
||||||
|
var alertTaskMu sync.Mutex
|
||||||
|
|
||||||
var baseTypes = map[string]bool{"ssl": true, "siteEndTime": true, "panelPwdEndTime": true, "panelUpdate": true}
|
var baseTypes = map[string]bool{"ssl": true, "siteEndTime": true, "panelPwdEndTime": true, "panelUpdate": true}
|
||||||
var resourceTypes = map[string]bool{"cpu": true, "memory": true, "disk": true, "load": true, "panelLogin": true, "sshLogin": true, "nodeException": true, "licenseException": true}
|
var resourceTypes = map[string]bool{"cpu": true, "memory": true, "disk": true, "load": true, "panelLogin": true, "sshLogin": true, "nodeException": true, "licenseException": true}
|
||||||
@@ -53,36 +56,53 @@ var resourceTypes = map[string]bool{"cpu": true, "memory": true, "disk": true, "
|
|||||||
func NewIAlertTaskHelper() IAlertTaskHelper {
|
func NewIAlertTaskHelper() IAlertTaskHelper {
|
||||||
return &AlertTaskHelper{
|
return &AlertTaskHelper{
|
||||||
DiskIO: make(chan []disk.IOCountersStat, 1),
|
DiskIO: make(chan []disk.IOCountersStat, 1),
|
||||||
NetIO: make(chan []net.IOCountersStat, 1),
|
NetIO: make(chan []gnet.IOCountersStat, 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (m *AlertTaskHelper) StartTask() {
|
func (m *AlertTaskHelper) StartTask() {
|
||||||
|
alertTaskMu.Lock()
|
||||||
|
defer alertTaskMu.Unlock()
|
||||||
|
m.startTaskLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AlertTaskHelper) startTaskLocked() {
|
||||||
baseAlert, resourceAlert := m.getClassifiedAlerts()
|
baseAlert, resourceAlert := m.getClassifiedAlerts()
|
||||||
if len(baseAlert) == 0 && len(resourceAlert) == 0 {
|
if len(baseAlert) == 0 && len(resourceAlert) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handleBaseAlerts(baseAlert)
|
handleBaseAlertsLocked(baseAlert)
|
||||||
handleResourceAlerts(resourceAlert)
|
handleResourceAlertsLocked(resourceAlert)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AlertTaskHelper) StopTask() {
|
func (m *AlertTaskHelper) StopTask() {
|
||||||
stopBaseJob()
|
alertTaskMu.Lock()
|
||||||
stopResourceJob()
|
defer alertTaskMu.Unlock()
|
||||||
|
stopBaseJobLocked()
|
||||||
|
stopResourceJobLocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AlertTaskHelper) ResetTask() {
|
func (m *AlertTaskHelper) ResetTask() {
|
||||||
m.StopTask()
|
alertTaskMu.Lock()
|
||||||
m.StartTask()
|
defer alertTaskMu.Unlock()
|
||||||
|
stopBaseJobLocked()
|
||||||
|
stopResourceJobLocked()
|
||||||
|
m.startTaskLocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AlertTaskHelper) InitTask(alertType string) {
|
func (m *AlertTaskHelper) InitTask(alertType string) {
|
||||||
|
alertTaskMu.Lock()
|
||||||
|
defer alertTaskMu.Unlock()
|
||||||
|
m.initTaskLocked(alertType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AlertTaskHelper) initTaskLocked(alertType string) {
|
||||||
resetAlertState(alertType)
|
resetAlertState(alertType)
|
||||||
if baseTypes[alertType] {
|
if baseTypes[alertType] {
|
||||||
stopBaseJob()
|
stopBaseJobLocked()
|
||||||
} else if resourceTypes[alertType] {
|
} else if resourceTypes[alertType] {
|
||||||
stopResourceJob()
|
stopResourceJobLocked()
|
||||||
}
|
}
|
||||||
m.StartTask()
|
m.startTaskLocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetAlertState(alertType string) {
|
func resetAlertState(alertType string) {
|
||||||
@@ -110,14 +130,14 @@ func (m *AlertTaskHelper) getClassifiedAlerts() (baseAlerts, resourceAlerts []dt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleBaseAlerts(baseAlerts []dto.AlertDTO) {
|
func handleBaseAlertsLocked(baseAlerts []dto.AlertDTO) {
|
||||||
if len(baseAlerts) == 0 {
|
if len(baseAlerts) == 0 {
|
||||||
stopBaseJob()
|
stopBaseJobLocked()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if global.AlertBaseJobID == 0 {
|
if global.AlertBaseJobID == 0 {
|
||||||
baseTask(baseAlerts)
|
baseTask(baseAlerts)
|
||||||
jobID, err := global.Cron.AddFunc("*/30 * * * *", func() {
|
jobID, err := global.Cron.AddFunc("@every 30m", func() {
|
||||||
baseTask(baseAlerts)
|
baseTask(baseAlerts)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -129,9 +149,9 @@ func handleBaseAlerts(baseAlerts []dto.AlertDTO) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleResourceAlerts(resourceAlerts []dto.AlertDTO) {
|
func handleResourceAlertsLocked(resourceAlerts []dto.AlertDTO) {
|
||||||
if len(resourceAlerts) == 0 {
|
if len(resourceAlerts) == 0 {
|
||||||
stopResourceJob()
|
stopResourceJobLocked()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if global.AlertResourceJobID == 0 {
|
if global.AlertResourceJobID == 0 {
|
||||||
@@ -147,7 +167,7 @@ func handleResourceAlerts(resourceAlerts []dto.AlertDTO) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopBaseJob() {
|
func stopBaseJobLocked() {
|
||||||
if global.AlertBaseJobID != 0 {
|
if global.AlertBaseJobID != 0 {
|
||||||
global.Cron.Remove(global.AlertBaseJobID)
|
global.Cron.Remove(global.AlertBaseJobID)
|
||||||
global.AlertBaseJobID = 0
|
global.AlertBaseJobID = 0
|
||||||
@@ -155,7 +175,7 @@ func stopBaseJob() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopResourceJob() {
|
func stopResourceJobLocked() {
|
||||||
if global.AlertResourceJobID != 0 {
|
if global.AlertResourceJobID != 0 {
|
||||||
global.Cron.Remove(global.AlertResourceJobID)
|
global.Cron.Remove(global.AlertResourceJobID)
|
||||||
global.AlertResourceJobID = 0
|
global.AlertResourceJobID = 0
|
||||||
@@ -174,10 +194,16 @@ func baseTask(baseAlert []dto.AlertDTO) {
|
|||||||
case "siteEndTime":
|
case "siteEndTime":
|
||||||
loadWebsiteInfo(alert)
|
loadWebsiteInfo(alert)
|
||||||
case "panelPwdEndTime":
|
case "panelPwdEndTime":
|
||||||
|
if global.CONF.Base.IsEnterprise {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if global.IsMaster {
|
if global.IsMaster {
|
||||||
loadPanelPwd(alert)
|
loadPanelPwd(alert)
|
||||||
}
|
}
|
||||||
case "panelUpdate":
|
case "panelUpdate":
|
||||||
|
if global.CONF.Base.IsEnterprise {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if global.IsMaster {
|
if global.IsMaster {
|
||||||
loadPanelUpdate(alert)
|
loadPanelUpdate(alert)
|
||||||
}
|
}
|
||||||
@@ -210,6 +236,9 @@ func resourceTask(resourceAlert []dto.AlertDTO) {
|
|||||||
loadNodeException(alert)
|
loadNodeException(alert)
|
||||||
}
|
}
|
||||||
case "licenseException":
|
case "licenseException":
|
||||||
|
if global.CONF.Base.IsEnterprise {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if execute && global.IsMaster {
|
if execute && global.IsMaster {
|
||||||
loadLicenseException(alert)
|
loadLicenseException(alert)
|
||||||
}
|
}
|
||||||
@@ -457,6 +486,7 @@ func loadPanelLogin(alert dto.AlertDTO) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("Failed to check recent failed ip login logs: %v", err)
|
global.LOG.Errorf("Failed to check recent failed ip login logs: %v", err)
|
||||||
}
|
}
|
||||||
|
records = filterLoginLogsNotInWhitelist(records, whitelist)
|
||||||
if len(records) > 0 {
|
if len(records) > 0 {
|
||||||
quota := strings.Join(func() []string {
|
quota := strings.Join(func() []string {
|
||||||
var ips []string
|
var ips []string
|
||||||
@@ -506,6 +536,7 @@ func loadSSHLogin(alert dto.AlertDTO) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("Failed to check recent failed ip ssh login logs: %v", err)
|
global.LOG.Errorf("Failed to check recent failed ip ssh login logs: %v", err)
|
||||||
}
|
}
|
||||||
|
records = filterSSHLoginEntriesNotInWhitelist(records, whitelist)
|
||||||
if len(records) > 0 {
|
if len(records) > 0 {
|
||||||
quota := strings.Join(records, "\n")
|
quota := strings.Join(records, "\n")
|
||||||
params := []dto.Param{
|
params := []dto.Param{
|
||||||
@@ -524,9 +555,63 @@ func loadSSHLogin(alert dto.AlertDTO) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterLoginLogsNotInWhitelist(records []model.LoginLog, whitelist []string) []model.LoginLog {
|
||||||
|
filtered := make([]model.LoginLog, 0, len(records))
|
||||||
|
for _, record := range records {
|
||||||
|
if !isIPInWhitelist(record.IP, whitelist) {
|
||||||
|
filtered = append(filtered, record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterSSHLoginEntriesNotInWhitelist(records []string, whitelist []string) []string {
|
||||||
|
filtered := make([]string, 0, len(records))
|
||||||
|
for _, record := range records {
|
||||||
|
ip := record
|
||||||
|
if idx := strings.Index(record, "-"); idx >= 0 {
|
||||||
|
ip = record[:idx]
|
||||||
|
}
|
||||||
|
if !isIPInWhitelist(ip, whitelist) {
|
||||||
|
filtered = append(filtered, record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIPInWhitelist(ip string, whitelist []string) bool {
|
||||||
|
targetIP := net.ParseIP(strings.TrimSpace(ip))
|
||||||
|
if targetIP == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, item := range whitelist {
|
||||||
|
item = strings.TrimSpace(item)
|
||||||
|
if item == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if item == ip {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if whiteIP := net.ParseIP(item); whiteIP != nil {
|
||||||
|
if whiteIP.Equal(targetIP) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, ipNet, err := net.ParseCIDR(item)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ipNet.Contains(targetIP) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func loadNodeException(alert dto.AlertDTO) {
|
func loadNodeException(alert dto.AlertDTO) {
|
||||||
// only master alert
|
// only master alert
|
||||||
failCount, err := xpack.GetNodeErrorAlert()
|
failCount, err := xpack.AlertProvider.GetNodeErrorAlert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("error getting node, err: %s", err)
|
global.LOG.Errorf("error getting node, err: %s", err)
|
||||||
return
|
return
|
||||||
@@ -555,7 +640,7 @@ func loadNodeException(alert dto.AlertDTO) {
|
|||||||
|
|
||||||
func loadLicenseException(alert dto.AlertDTO) {
|
func loadLicenseException(alert dto.AlertDTO) {
|
||||||
// only master alert
|
// only master alert
|
||||||
failCount, err := xpack.GetLicenseErrorAlert()
|
failCount, err := xpack.AlertProvider.GetLicenseErrorAlert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("error getting license, err: %s", err)
|
global.LOG.Errorf("error getting license, err: %s", err)
|
||||||
return
|
return
|
||||||
@@ -590,94 +675,138 @@ func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params [
|
|||||||
if newDate.IsZero() || calculateMinutesDifference(newDate) > ResourceAlertInterval {
|
if newDate.IsZero() || calculateMinutesDifference(newDate) > ResourceAlertInterval {
|
||||||
for _, m := range methods {
|
for _, m := range methods {
|
||||||
m = strings.TrimSpace(m)
|
m = strings.TrimSpace(m)
|
||||||
switch m {
|
if configId, err := strconv.ParseUint(m, 10, 64); err == nil {
|
||||||
case constant.SMS:
|
sendAlertsByConfigId(alert, alertType, quota, quotaType, params, uint(configId))
|
||||||
if !alertUtil.CheckSMSSendLimit(constant.SMS) {
|
} else {
|
||||||
continue
|
sendAlertsByLegacyMethod(alert, alertType, quota, quotaType, params, m)
|
||||||
}
|
|
||||||
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, constant.SMS)
|
|
||||||
if !isValid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
create := dto.AlertLogCreate{
|
|
||||||
Type: alertType,
|
|
||||||
AlertId: alert.ID,
|
|
||||||
Count: todayCount + 1,
|
|
||||||
}
|
|
||||||
alertErr := xpack.CreateSMSAlertLog(alertType, alert, create, quotaType, params, constant.SMS)
|
|
||||||
if alertErr != nil {
|
|
||||||
global.LOG.Infof("%s alert sms push faild, err: %v", alertType, alertErr.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, constant.SMS)
|
|
||||||
case constant.Email:
|
|
||||||
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, constant.Email)
|
|
||||||
if !isValid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
create := dto.AlertLogCreate{
|
|
||||||
Type: alertType,
|
|
||||||
AlertId: alert.ID,
|
|
||||||
Count: todayCount + 1,
|
|
||||||
}
|
|
||||||
alertInfo := alert
|
|
||||||
alertInfo.Type = alertType
|
|
||||||
create.AlertRule = alertUtil.ProcessAlertRule(alert)
|
|
||||||
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, constant.Email)
|
|
||||||
transport := xpack.LoadRequestTransport()
|
|
||||||
agentInfo, _ := xpack.GetAgentInfo()
|
|
||||||
alertErr := alertUtil.CreateEmailAlertLog(create, alertInfo, params, transport, agentInfo)
|
|
||||||
if alertErr != nil {
|
|
||||||
global.LOG.Infof("%s alert email push faild, err: %v", alertType, alertErr.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, constant.Email)
|
|
||||||
case constant.WeCom, constant.DingTalk, constant.FeiShu:
|
|
||||||
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, m)
|
|
||||||
if !isValid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var create = dto.AlertLogCreate{
|
|
||||||
Type: alertUtil.GetCronJobType(alert.Type),
|
|
||||||
AlertId: alert.ID,
|
|
||||||
Count: todayCount + 1,
|
|
||||||
}
|
|
||||||
transport := xpack.LoadRequestTransport()
|
|
||||||
agentInfo, _ := xpack.GetAgentInfo()
|
|
||||||
err := xpack.CreateWebhookAlertLog(alertType, alert, create, quotaType, params, m, transport, agentInfo)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Infof("%s alert webhook %s push faild, err: %v", alertType, m, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, m)
|
|
||||||
case constant.Bark:
|
|
||||||
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, m)
|
|
||||||
if !isValid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var create = dto.AlertLogCreate{
|
|
||||||
Type: alertUtil.GetCronJobType(alert.Type),
|
|
||||||
AlertId: alert.ID,
|
|
||||||
Count: todayCount + 1,
|
|
||||||
}
|
|
||||||
alertInfo := alert
|
|
||||||
alertInfo.Type = alertType
|
|
||||||
create.AlertRule = alertUtil.ProcessAlertRule(alert)
|
|
||||||
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, m)
|
|
||||||
transport := xpack.LoadRequestTransport()
|
|
||||||
agentInfo, _ := xpack.GetAgentInfo()
|
|
||||||
alertErr := alertUtil.CreateBarkAlertLog(create, alertInfo, params, transport, agentInfo)
|
|
||||||
if alertErr != nil {
|
|
||||||
global.LOG.Infof("%s alert %s push failed, err: %v", alertType, m, alertErr.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, m)
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendAlertsByConfigId(alert dto.AlertDTO, alertType, quota, quotaType string, params []dto.Param, configId uint) {
|
||||||
|
config, err := alertRepo.GetConfigById(configId)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("alert config not found for id %d: %v", configId, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doSendAlert(alert, alertType, quota, quotaType, params, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendAlertsByLegacyMethod(alert dto.AlertDTO, alertType, quota, quotaType string, params []dto.Param, method string) {
|
||||||
|
typeMap := map[string]string{
|
||||||
|
"mail": constant.Email,
|
||||||
|
constant.Bark: constant.Bark,
|
||||||
|
constant.SMS: constant.SMS,
|
||||||
|
}
|
||||||
|
configType, ok := typeMap[method]
|
||||||
|
if !ok {
|
||||||
|
configType = method
|
||||||
|
}
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(configType))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doSendAlert(alert, alertType, quota, quotaType, params, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doSendAlert(alert dto.AlertDTO, alertType, quota, quotaType string, params []dto.Param, config model.AlertConfig) {
|
||||||
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
methodStr := strconv.Itoa(int(config.ID))
|
||||||
|
switch config.Type {
|
||||||
|
case constant.SMS:
|
||||||
|
if !alertUtil.CheckSMSSendLimit(config, methodStr) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, methodStr)
|
||||||
|
if !isValid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
create := dto.AlertLogCreate{
|
||||||
|
Type: alertType,
|
||||||
|
AlertId: alert.ID,
|
||||||
|
Count: todayCount + 1,
|
||||||
|
Method: methodStr,
|
||||||
|
}
|
||||||
|
alertErr := xpack.AlertProvider.CreateSMSAlertLog(alertType, alert, create, quotaType, params, config, methodStr)
|
||||||
|
if alertErr != nil {
|
||||||
|
global.LOG.Infof("%s alert sms push faild, err: %v", alertType, alertErr.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, methodStr)
|
||||||
|
|
||||||
|
case constant.Email:
|
||||||
|
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, methodStr)
|
||||||
|
if !isValid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
create := dto.AlertLogCreate{
|
||||||
|
Type: alertType,
|
||||||
|
AlertId: alert.ID,
|
||||||
|
Count: todayCount + 1,
|
||||||
|
Method: methodStr,
|
||||||
|
}
|
||||||
|
alertInfo := alert
|
||||||
|
alertInfo.Type = alertType
|
||||||
|
create.AlertRule = alertUtil.ProcessAlertRule(alert)
|
||||||
|
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, constant.Email)
|
||||||
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
|
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||||
|
alertErr := alertUtil.CreateEmailAlertLog(create, alertInfo, params, transport, agentInfo, config)
|
||||||
|
if alertErr != nil {
|
||||||
|
global.LOG.Infof("%s alert email push faild, err: %v", alertType, alertErr.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, methodStr)
|
||||||
|
|
||||||
|
case constant.Bark:
|
||||||
|
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, methodStr)
|
||||||
|
if !isValid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
create := dto.AlertLogCreate{
|
||||||
|
Type: alertType,
|
||||||
|
AlertId: alert.ID,
|
||||||
|
Count: todayCount + 1,
|
||||||
|
Method: methodStr,
|
||||||
|
}
|
||||||
|
alertInfo := alert
|
||||||
|
alertInfo.Type = alertType
|
||||||
|
create.AlertRule = alertUtil.ProcessAlertRule(alert)
|
||||||
|
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, constant.Bark)
|
||||||
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
|
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||||
|
alertErr := alertUtil.CreateBarkAlertLog(create, alertInfo, params, transport, agentInfo, config)
|
||||||
|
if alertErr != nil {
|
||||||
|
global.LOG.Infof("%s alert %s push failed, err: %v", alertType, methodStr, alertErr.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, methodStr)
|
||||||
|
|
||||||
|
case constant.WeCom, constant.DingTalk, constant.FeiShu:
|
||||||
|
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, methodStr)
|
||||||
|
if !isValid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
create := dto.AlertLogCreate{
|
||||||
|
Type: alertType,
|
||||||
|
AlertId: alert.ID,
|
||||||
|
Count: todayCount + 1,
|
||||||
|
Method: methodStr,
|
||||||
|
}
|
||||||
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
|
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||||
|
alertErr := xpack.AlertProvider.CreateWebhookAlertLog(alertType, alert, create, quotaType, params, config, transport, agentInfo)
|
||||||
|
if alertErr != nil {
|
||||||
|
global.LOG.Infof("%s alert webhook %s push faild, err: %v", alertType, methodStr, alertErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, methodStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------
|
// ------------------------------
|
||||||
func getRepoOptionsByProject(project string) []repo.DBOption {
|
func getRepoOptionsByProject(project string) []repo.DBOption {
|
||||||
var opts []repo.DBOption
|
var opts []repo.DBOption
|
||||||
|
|||||||
+276
-124
@@ -1,12 +1,16 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
|
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AlertSender struct {
|
type AlertSender struct {
|
||||||
@@ -22,115 +26,90 @@ func NewAlertSender(alert dto.AlertDTO, quotaType string) *AlertSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *AlertSender) Send(quota string, params []dto.Param) {
|
func (s *AlertSender) Send(quota string, params []dto.Param) {
|
||||||
methods := strings.Split(s.alert.Method, ",")
|
s.sendByConfigIds(s.alert.Method, quota, params, false)
|
||||||
for _, method := range methods {
|
|
||||||
method = strings.TrimSpace(method)
|
|
||||||
switch method {
|
|
||||||
case constant.SMS:
|
|
||||||
s.sendSMS(quota, params)
|
|
||||||
case constant.Email:
|
|
||||||
s.sendEmail(quota, params)
|
|
||||||
case constant.Bark:
|
|
||||||
s.sendBark(quota, params)
|
|
||||||
case constant.WeCom, constant.DingTalk, constant.FeiShu:
|
|
||||||
s.sendWebhook(quota, params, method)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AlertSender) ResourceSend(quota string, params []dto.Param) {
|
func (s *AlertSender) ResourceSend(quota string, params []dto.Param) {
|
||||||
methods := strings.Split(s.alert.Method, ",")
|
s.sendByConfigIds(s.alert.Method, quota, params, true)
|
||||||
for _, method := range methods {
|
}
|
||||||
method = strings.TrimSpace(method)
|
|
||||||
switch method {
|
func (s *AlertSender) sendByConfigIds(methodStr string, quota string, params []dto.Param, isResource bool) {
|
||||||
case constant.SMS:
|
alertRepo := repo.NewIAlertRepo()
|
||||||
s.sendResourceSMS(quota, params)
|
configIds := strings.Split(methodStr, ",")
|
||||||
case constant.Email:
|
for _, idStr := range configIds {
|
||||||
s.sendResourceEmail(quota, params)
|
idStr = strings.TrimSpace(idStr)
|
||||||
case constant.Bark:
|
configId, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
s.sendResourceBark(quota, params)
|
if err != nil {
|
||||||
case constant.WeCom, constant.DingTalk, constant.FeiShu:
|
s.sendByLegacyMethod(idStr, quota, params, isResource)
|
||||||
s.sendResourceWebhook(quota, params, method)
|
continue
|
||||||
|
}
|
||||||
|
config, err := alertRepo.GetConfigById(uint(configId))
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("alert config not found for id %d: %v", configId, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.sendByConfig(config, quota, params, isResource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendByConfig(config model.AlertConfig, quota string, params []dto.Param, isResource bool) {
|
||||||
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch config.Type {
|
||||||
|
case constant.SMS:
|
||||||
|
if isResource {
|
||||||
|
s.sendResourceSMSWithConfig(config, quota, params)
|
||||||
|
} else {
|
||||||
|
s.sendSMSWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
case constant.Email:
|
||||||
|
if isResource {
|
||||||
|
s.sendResourceEmailWithConfig(config, quota, params)
|
||||||
|
} else {
|
||||||
|
s.sendEmailWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
case constant.Bark:
|
||||||
|
if isResource {
|
||||||
|
s.sendResourceBarkWithConfig(config, quota, params)
|
||||||
|
} else {
|
||||||
|
s.sendBarkWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
case constant.WeCom, constant.DingTalk, constant.FeiShu:
|
||||||
|
if isResource {
|
||||||
|
s.sendResourceWebhookWithConfig(config, quota, params)
|
||||||
|
} else {
|
||||||
|
s.sendWebhookWithConfig(config, quota, params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AlertSender) sendSMS(quota string, params []dto.Param) {
|
func (s *AlertSender) sendByLegacyMethod(method string, quota string, params []dto.Param, isResource bool) {
|
||||||
if !alertUtil.CheckSMSSendLimit(constant.SMS) {
|
alertRepo := repo.NewIAlertRepo()
|
||||||
return
|
typeMap := map[string]string{"mail": constant.Email, constant.Bark: constant.Bark, constant.SMS: constant.SMS}
|
||||||
|
configType := method
|
||||||
|
if mapped, ok := typeMap[method]; ok {
|
||||||
|
configType = mapped
|
||||||
}
|
}
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(configType))
|
||||||
totalCount, isValid := s.canSendAlert(constant.SMS)
|
|
||||||
if !isValid {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
create := dto.AlertLogCreate{
|
|
||||||
Status: constant.AlertSuccess,
|
|
||||||
Count: totalCount + 1,
|
|
||||||
AlertId: s.alert.ID,
|
|
||||||
Type: s.alert.Type,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("%s alert sms push failed: %v", s.alert.Type, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.SMS)
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.sendByConfig(config, quota, params, isResource)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AlertSender) sendEmail(quota string, params []dto.Param) {
|
func (s *AlertSender) sendSMSWithConfig(config model.AlertConfig, quota string, params []dto.Param) {
|
||||||
totalCount, isValid := s.canSendAlert(constant.Email)
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
if !isValid {
|
return
|
||||||
|
}
|
||||||
|
method := strconv.Itoa(int(config.ID))
|
||||||
|
if !alertUtil.CheckSMSSendLimit(config, method) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
create := dto.AlertLogCreate{
|
|
||||||
Status: constant.AlertSuccess,
|
|
||||||
Count: totalCount + 1,
|
|
||||||
AlertId: s.alert.ID,
|
|
||||||
Type: s.alert.Type,
|
|
||||||
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
|
||||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
|
|
||||||
}
|
|
||||||
|
|
||||||
transport := xpack.LoadRequestTransport()
|
|
||||||
agentInfo, _ := xpack.GetAgentInfo()
|
|
||||||
err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorf("%s alert email push failed: %v", s.alert.Type, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AlertSender) sendBark(quota string, params []dto.Param) {
|
|
||||||
totalCount, isValid := s.canSendAlert(constant.Bark)
|
|
||||||
if !isValid {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
create := dto.AlertLogCreate{
|
|
||||||
Status: constant.AlertSuccess,
|
|
||||||
Count: totalCount + 1,
|
|
||||||
AlertId: s.alert.ID,
|
|
||||||
Type: s.alert.Type,
|
|
||||||
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
|
||||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
|
|
||||||
}
|
|
||||||
|
|
||||||
transport := xpack.LoadRequestTransport()
|
|
||||||
agentInfo, _ := xpack.GetAgentInfo()
|
|
||||||
err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorf("%s alert bark push failed: %v", s.alert.Type, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Bark)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AlertSender) sendWebhook(quota string, params []dto.Param, method string) {
|
|
||||||
totalCount, isValid := s.canSendAlert(method)
|
totalCount, isValid := s.canSendAlert(method)
|
||||||
if !isValid {
|
if !isValid {
|
||||||
return
|
return
|
||||||
@@ -141,43 +120,60 @@ func (s *AlertSender) sendWebhook(quota string, params []dto.Param, method strin
|
|||||||
Count: totalCount + 1,
|
Count: totalCount + 1,
|
||||||
AlertId: s.alert.ID,
|
AlertId: s.alert.ID,
|
||||||
Type: s.alert.Type,
|
Type: s.alert.Type,
|
||||||
|
Method: method,
|
||||||
}
|
}
|
||||||
transport := xpack.LoadRequestTransport()
|
|
||||||
agentInfo, _ := xpack.GetAgentInfo()
|
err := xpack.AlertProvider.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, config, method)
|
||||||
err := xpack.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, method, transport, agentInfo)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("%s alert %s webhook push failed: %v", s.alert.Type, method, err)
|
global.LOG.Errorf("%s alert sms push failed: %v", s.alert.Type, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, method)
|
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, method)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AlertSender) sendResourceSMS(quota string, params []dto.Param) {
|
func (s *AlertSender) sendSMS(quota string, params []dto.Param) {
|
||||||
if !alertUtil.CheckSMSSendLimit(constant.SMS) {
|
alertRepo := repo.NewIAlertRepo()
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(constant.SMS))
|
||||||
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s.sendSMSWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
|
||||||
todayCount, isValid := s.canResourceSendAlert(constant.SMS)
|
func (s *AlertSender) sendEmailWithConfig(config model.AlertConfig, quota string, params []dto.Param) {
|
||||||
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
totalCount, isValid := s.canSendAlert(strconv.Itoa(int(config.ID)))
|
||||||
if !isValid {
|
if !isValid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
create := dto.AlertLogCreate{
|
create := dto.AlertLogCreate{
|
||||||
Status: constant.AlertSuccess,
|
Status: constant.AlertSuccess,
|
||||||
Count: todayCount + 1,
|
Count: totalCount + 1,
|
||||||
AlertId: s.alert.ID,
|
AlertId: s.alert.ID,
|
||||||
Type: s.alert.Type,
|
Type: s.alert.Type,
|
||||||
|
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
||||||
|
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
|
||||||
|
Method: strconv.Itoa(int(config.ID)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS); err != nil {
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
global.LOG.Errorf("failed to send SMS alert: %v", err)
|
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||||
|
err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo, config)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("%s alert email push failed: %v", s.alert.Type, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.SMS)
|
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, strconv.Itoa(int(config.ID)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {
|
func (s *AlertSender) sendResourceEmailWithConfig(config model.AlertConfig, quota string, params []dto.Param) {
|
||||||
todayCount, isValid := s.canResourceSendAlert(constant.Email)
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
todayCount, isValid := s.canResourceSendAlert(strconv.Itoa(int(config.ID)))
|
||||||
if !isValid {
|
if !isValid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -189,19 +185,70 @@ func (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {
|
|||||||
Type: s.alert.Type,
|
Type: s.alert.Type,
|
||||||
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
||||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
|
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
|
||||||
|
Method: strconv.Itoa(int(config.ID)),
|
||||||
}
|
}
|
||||||
|
|
||||||
transport := xpack.LoadRequestTransport()
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
agentInfo, _ := xpack.GetAgentInfo()
|
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||||
if err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo); err != nil {
|
if err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo, config); err != nil {
|
||||||
global.LOG.Errorf("failed to send Email alert: %v", err)
|
global.LOG.Errorf("failed to send Email alert: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
|
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, strconv.Itoa(int(config.ID)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AlertSender) sendResourceBark(quota string, params []dto.Param) {
|
func (s *AlertSender) sendEmail(quota string, params []dto.Param) {
|
||||||
todayCount, isValid := s.canResourceSendAlert(constant.Bark)
|
alertRepo := repo.NewIAlertRepo()
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(constant.EmailConfig))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.sendEmailWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {
|
||||||
|
alertRepo := repo.NewIAlertRepo()
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(constant.EmailConfig))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.sendResourceEmailWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendBarkWithConfig(config model.AlertConfig, quota string, params []dto.Param) {
|
||||||
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
totalCount, isValid := s.canSendAlert(strconv.Itoa(int(config.ID)))
|
||||||
|
if !isValid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
create := dto.AlertLogCreate{
|
||||||
|
Status: constant.AlertSuccess,
|
||||||
|
Count: totalCount + 1,
|
||||||
|
AlertId: s.alert.ID,
|
||||||
|
Type: s.alert.Type,
|
||||||
|
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
||||||
|
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
|
||||||
|
Method: strconv.Itoa(int(config.ID)),
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
|
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||||
|
err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo, config)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("%s alert bark push failed: %v", s.alert.Type, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, strconv.Itoa(int(config.ID)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendResourceBarkWithConfig(config model.AlertConfig, quota string, params []dto.Param) {
|
||||||
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
todayCount, isValid := s.canResourceSendAlert(strconv.Itoa(int(config.ID)))
|
||||||
if !isValid {
|
if !isValid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -213,18 +260,114 @@ func (s *AlertSender) sendResourceBark(quota string, params []dto.Param) {
|
|||||||
Type: s.alert.Type,
|
Type: s.alert.Type,
|
||||||
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
AlertRule: alertUtil.ProcessAlertRule(s.alert),
|
||||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
|
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
|
||||||
|
Method: strconv.Itoa(int(config.ID)),
|
||||||
}
|
}
|
||||||
|
|
||||||
transport := xpack.LoadRequestTransport()
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
agentInfo, _ := xpack.GetAgentInfo()
|
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||||
if err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo); err != nil {
|
if err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo, config); err != nil {
|
||||||
global.LOG.Errorf("failed to send Bark alert: %v", err)
|
global.LOG.Errorf("failed to send Bark alert: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Bark)
|
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, strconv.Itoa(int(config.ID)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendBark(quota string, params []dto.Param) {
|
||||||
|
alertRepo := repo.NewIAlertRepo()
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(constant.Bark))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.sendBarkWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendResourceBark(quota string, params []dto.Param) {
|
||||||
|
alertRepo := repo.NewIAlertRepo()
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(constant.Bark))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.sendResourceBarkWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendWebhookWithConfig(config model.AlertConfig, quota string, params []dto.Param) {
|
||||||
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
totalCount, isValid := s.canSendAlert(strconv.Itoa(int(config.ID)))
|
||||||
|
if !isValid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
create := dto.AlertLogCreate{
|
||||||
|
Status: constant.AlertSuccess,
|
||||||
|
Count: totalCount + 1,
|
||||||
|
AlertId: s.alert.ID,
|
||||||
|
Type: s.alert.Type,
|
||||||
|
Method: strconv.Itoa(int(config.ID)),
|
||||||
|
}
|
||||||
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
|
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||||
|
err := xpack.AlertProvider.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, config, transport, agentInfo)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("%s alert %s webhook push failed: %v", s.alert.Type, config.Type, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, strconv.Itoa(int(config.ID)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendResourceWebhookWithConfig(config model.AlertConfig, quota string, params []dto.Param) {
|
||||||
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
todayCount, isValid := s.canResourceSendAlert(strconv.Itoa(int(config.ID)))
|
||||||
|
if !isValid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
create := dto.AlertLogCreate{
|
||||||
|
Status: constant.AlertSuccess,
|
||||||
|
Count: todayCount + 1,
|
||||||
|
AlertId: s.alert.ID,
|
||||||
|
Type: s.alert.Type,
|
||||||
|
Method: strconv.Itoa(int(config.ID)),
|
||||||
|
}
|
||||||
|
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||||
|
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||||
|
if err := xpack.AlertProvider.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, config, transport, agentInfo); err != nil {
|
||||||
|
global.LOG.Errorf("%s alert %s webhook push failed: %v", s.alert.Type, config.Type, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, strconv.Itoa(int(config.ID)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendWebhook(quota string, params []dto.Param, method string) {
|
||||||
|
alertRepo := repo.NewIAlertRepo()
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(method))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.sendWebhookWithConfig(config, quota, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AlertSender) sendResourceWebhook(quota string, params []dto.Param, method string) {
|
func (s *AlertSender) sendResourceWebhook(quota string, params []dto.Param, method string) {
|
||||||
|
alertRepo := repo.NewIAlertRepo()
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(method))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.sendResourceWebhookWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendResourceSMSWithConfig(config model.AlertConfig, quota string, params []dto.Param) {
|
||||||
|
if !alertUtil.IsAlertConfigEnabled(config) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
method := strconv.Itoa(int(config.ID))
|
||||||
|
if !alertUtil.CheckSMSSendLimit(config, method) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
todayCount, isValid := s.canResourceSendAlert(method)
|
todayCount, isValid := s.canResourceSendAlert(method)
|
||||||
if !isValid {
|
if !isValid {
|
||||||
return
|
return
|
||||||
@@ -235,16 +378,25 @@ func (s *AlertSender) sendResourceWebhook(quota string, params []dto.Param, meth
|
|||||||
Count: todayCount + 1,
|
Count: todayCount + 1,
|
||||||
AlertId: s.alert.ID,
|
AlertId: s.alert.ID,
|
||||||
Type: s.alert.Type,
|
Type: s.alert.Type,
|
||||||
|
Method: method,
|
||||||
}
|
}
|
||||||
transport := xpack.LoadRequestTransport()
|
|
||||||
agentInfo, _ := xpack.GetAgentInfo()
|
if err := xpack.AlertProvider.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, config, method); err != nil {
|
||||||
if err := xpack.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, method, transport, agentInfo); err != nil {
|
global.LOG.Errorf("failed to send SMS alert: %v", err)
|
||||||
global.LOG.Errorf("%s alert %s webhook push failed: %v", s.alert.Type, method, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, method)
|
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, method)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *AlertSender) sendResourceSMS(quota string, params []dto.Param) {
|
||||||
|
alertRepo := repo.NewIAlertRepo()
|
||||||
|
config, err := alertRepo.GetConfig(alertRepo.WithByType(constant.SMSConfig))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.sendResourceSMSWithConfig(config, quota, params)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *AlertSender) canSendAlert(method string) (uint, bool) {
|
func (s *AlertSender) canSendAlert(method string) (uint, bool) {
|
||||||
todayCount, totalCount, err := alertRepo.LoadTaskCount(s.alert.Type, s.quotaType, method)
|
todayCount, totalCount, err := alertRepo.LoadTaskCount(s.alert.Type, s.quotaType, method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (*response.
|
|||||||
lang := strings.ToLower(common.GetLang(ctx))
|
lang := strings.ToLower(common.GetLang(ctx))
|
||||||
for _, ap := range apps {
|
for _, ap := range apps {
|
||||||
if req.Type == "php" {
|
if req.Type == "php" {
|
||||||
if !global.CONF.Base.IsOffLine && (ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion))) {
|
if !global.CONF.Base.IsOffline && (ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion))) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -576,7 +577,7 @@ func (a AppService) installWithHooks(req request.AppInstallCreate, executeScript
|
|||||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||||
}
|
}
|
||||||
|
|
||||||
installTask.AddSubTask(task.GetTaskName(appInstall.Name, task.TaskInstall, task.TaskScopeApp), installApp, handleAppStatus)
|
installTask.AddSubTaskWithOps(task.GetTaskName(appInstall.Name, task.TaskInstall, task.TaskScopeApp), installApp, handleAppStatus, 0, time.Hour)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if taskErr := installTask.Execute(); taskErr != nil {
|
if taskErr := installTask.Execute(); taskErr != nil {
|
||||||
@@ -895,7 +896,7 @@ func getAppFromRepo(downloadPath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := fileOp.Decompress(packagePath, global.Dir.ResourceDir, files.SdkZip, ""); err != nil {
|
if err := fileOp.Decompress(context.Background(), packagePath, global.Dir.ResourceDir, files.SdkZip, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -965,7 +966,7 @@ func deleteCustomApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||||
if xpack.IsUseCustomApp() {
|
if xpack.MultiNodeProvider.IsUseCustomApp() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
|||||||
DetailID: req.DetailId,
|
DetailID: req.DetailId,
|
||||||
Backup: req.Backup,
|
Backup: req.Backup,
|
||||||
PullImage: req.PullImage,
|
PullImage: req.PullImage,
|
||||||
|
DeleteImage: req.DeleteImage,
|
||||||
DockerCompose: req.DockerCompose,
|
DockerCompose: req.DockerCompose,
|
||||||
TaskID: req.TaskID,
|
TaskID: req.TaskID,
|
||||||
}
|
}
|
||||||
@@ -589,7 +590,12 @@ func (a *AppInstallService) GetUpdateVersions(req request.AppUpdateVersion) ([]d
|
|||||||
if common.IsCrossVersion(install.Version, detail.Version) && !app.CrossVersionUpdate {
|
if common.IsCrossVersion(install.Version, detail.Version) && !app.CrossVersionUpdate {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if common.CompareVersion(detail.Version, install.Version) {
|
canUpgrade := common.CompareVersion(detail.Version, install.Version)
|
||||||
|
if app.Key == vllmAppKeyForUpgrade {
|
||||||
|
currentImage := loadVllmImageFromEnv(install.Env)
|
||||||
|
canUpgrade = isVllmUpgradeCandidate(install.Version, detail.Version, currentImage)
|
||||||
|
}
|
||||||
|
if canUpgrade {
|
||||||
var newCompose string
|
var newCompose string
|
||||||
if req.UpdateVersion != "" && req.UpdateVersion == detail.Version && detail.DockerCompose == "" && !app.IsLocalApp() {
|
if req.UpdateVersion != "" && req.UpdateVersion == detail.Version && detail.DockerCompose == "" && !app.IsLocalApp() {
|
||||||
filename := filepath.Base(detail.DownloadUrl)
|
filename := filepath.Base(detail.DownloadUrl)
|
||||||
@@ -874,7 +880,7 @@ func updateInstallInfoInDB(appKey, appName, param string, value interface{}) err
|
|||||||
envKey := ""
|
envKey := ""
|
||||||
switch param {
|
switch param {
|
||||||
case "password":
|
case "password":
|
||||||
if appKey == "mysql" || appKey == "mariadb" || appKey == "postgresql" {
|
if appKey == "mysql" || appKey == "mariadb" || appKey == "postgresql" || appKey == "mongodb" {
|
||||||
envKey = "PANEL_DB_ROOT_PASSWORD="
|
envKey = "PANEL_DB_ROOT_PASSWORD="
|
||||||
} else {
|
} else {
|
||||||
envKey = "PANEL_REDIS_ROOT_PASSWORD="
|
envKey = "PANEL_REDIS_ROOT_PASSWORD="
|
||||||
@@ -915,7 +921,7 @@ func updateInstallInfoInDB(appKey, appName, param string, value interface{}) err
|
|||||||
"param": strings.ReplaceAll(appInstall.Param, oldVal, newVal),
|
"param": strings.ReplaceAll(appInstall.Param, oldVal, newVal),
|
||||||
"env": strings.ReplaceAll(appInstall.Env, oldVal, newVal),
|
"env": strings.ReplaceAll(appInstall.Env, oldVal, newVal),
|
||||||
}, repo.WithByID(appInstall.ID))
|
}, repo.WithByID(appInstall.ID))
|
||||||
if appKey == "mysql" || appKey == "postgresql" {
|
if appKey == "mysql" || appKey == "postgresql" || appKey == "mongodb" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ func (a AppService) createSyncAppStoreTask(sharedCtx **appSyncContext) func(t *t
|
|||||||
|
|
||||||
ctx := &appSyncContext{
|
ctx := &appSyncContext{
|
||||||
task: t,
|
task: t,
|
||||||
httpClient: http.Client{Timeout: time.Duration(constant.TimeOut20s) * time.Second, Transport: xpack.LoadRequestTransport()},
|
httpClient: http.Client{Timeout: time.Duration(constant.TimeOut20s) * time.Second, Transport: xpack.MultiNodeProvider.LoadRequestTransport()},
|
||||||
baseRemoteUrl: fmt.Sprintf("%s/%s/1panel", global.AppRepoURL(), global.CONF.Base.Mode),
|
baseRemoteUrl: fmt.Sprintf("%s/%s/1panel", global.AppRepoURL(), global.CONF.Base.Mode),
|
||||||
systemVersion: setting.SystemVersion,
|
systemVersion: setting.SystemVersion,
|
||||||
settingService: settingService,
|
settingService: settingService,
|
||||||
@@ -279,9 +279,9 @@ func (c *appSyncContext) syncAppIconsAndDetails() error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
completed int
|
completed int
|
||||||
icon200Count int
|
icon200Count int
|
||||||
icon304Count int
|
icon304Count int
|
||||||
iconFailCount int
|
iconFailCount int
|
||||||
)
|
)
|
||||||
milestones := [4]int{totalWork / 4, totalWork / 2, totalWork * 3 / 4, totalWork}
|
milestones := [4]int{totalWork / 4, totalWork / 2, totalWork * 3 / 4, totalWork}
|
||||||
|
|||||||
+266
-67
@@ -89,7 +89,15 @@ func checkPort(key string, params map[string]interface{}) (int, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isPortInUse(port int, protocol string) bool {
|
||||||
|
return common.ScanPortWithProto(port, normalizeComposeProtocol(protocol))
|
||||||
|
}
|
||||||
|
|
||||||
func checkPortExist(port int) error {
|
func checkPortExist(port int) error {
|
||||||
|
return checkPortExistWithProtocol(port, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPortExistWithProtocol(port int, protocol string) error {
|
||||||
errMap := make(map[string]interface{})
|
errMap := make(map[string]interface{})
|
||||||
errMap["port"] = port
|
errMap["port"] = port
|
||||||
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))
|
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))
|
||||||
@@ -110,7 +118,7 @@ func checkPortExist(port int) error {
|
|||||||
errMap["name"] = domain.Domain
|
errMap["name"] = domain.Domain
|
||||||
return buserr.WithMap("ErrPortExist", errMap, nil)
|
return buserr.WithMap("ErrPortExist", errMap, nil)
|
||||||
}
|
}
|
||||||
if common.ScanPort(port) {
|
if isPortInUse(port, protocol) {
|
||||||
return buserr.WithDetail("ErrPortInUsed", port, nil)
|
return buserr.WithDetail("ErrPortInUsed", port, nil)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -357,33 +365,13 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if deleteReq.DeleteImage {
|
if deleteReq.DeleteImage {
|
||||||
delImageStr := i18n.GetMsgByKey("TaskDelete") + i18n.GetMsgByKey("Image")
|
|
||||||
content, err := op.GetContent(install.GetEnvPath())
|
content, err := op.GetContent(install.GetEnvPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
images, err := docker.GetImagesFromDockerCompose(content, []byte(install.DockerCompose))
|
if err = deleteAppImagesByCompose(t, content, []byte(install.DockerCompose), nil); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
client, err := docker.NewClient()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer client.Close()
|
|
||||||
for _, image := range images {
|
|
||||||
imageID, err := client.GetImageIDByName(image)
|
|
||||||
if err == nil {
|
|
||||||
imgStr := delImageStr + image
|
|
||||||
t.Log(imgStr)
|
|
||||||
|
|
||||||
if err = client.DeleteImage(imageID); err != nil {
|
|
||||||
t.LogFailedWithErr(imgStr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.LogSuccess(delImageStr + image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx, ctx := helper.GetTxAndContext()
|
tx, ctx := helper.GetTxAndContext()
|
||||||
@@ -392,7 +380,7 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
appKey := install.App.Key
|
appKey := install.App.Key
|
||||||
if appKey == constant.AppOpenclaw || appKey == constant.AppCopaw {
|
if isAgentAppKey(appKey) {
|
||||||
_ = agentRepo.DeleteByAppInstallIDWithCtx(ctx, install.ID)
|
_ = agentRepo.DeleteByAppInstallIDWithCtx(ctx, install.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,6 +413,8 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
|||||||
switch install.App.Key {
|
switch install.App.Key {
|
||||||
case constant.AppMysql, constant.AppMariaDB, constant.AppMysqlCluster:
|
case constant.AppMysql, constant.AppMariaDB, constant.AppMysqlCluster:
|
||||||
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
|
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
|
||||||
|
case constant.AppMongodb:
|
||||||
|
_ = mongodbRepo.Delete(ctx, mongodbRepo.WithByMongodbName(install.Name))
|
||||||
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
case constant.AppPostgresql, constant.AppPostgresqlCluster:
|
||||||
_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))
|
_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))
|
||||||
}
|
}
|
||||||
@@ -466,6 +456,69 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type appImageID struct {
|
||||||
|
name string
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAppImageIDsByCompose(client docker.Client, envContent, composeContent []byte) ([]appImageID, error) {
|
||||||
|
images, err := docker.GetImagesFromDockerCompose(envContent, composeContent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
imageIDs := make([]appImageID, 0, len(images))
|
||||||
|
for _, image := range images {
|
||||||
|
imageID, err := client.GetImageIDByName(image)
|
||||||
|
if err == nil && imageID != "" {
|
||||||
|
imageIDs = append(imageIDs, appImageID{name: image, id: imageID})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return imageIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteAppImagesByCompose(t *task.Task, envContent, composeContent []byte, excludeImages []string) error {
|
||||||
|
client, err := docker.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
imageIDs, err := getAppImageIDsByCompose(client, envContent, composeContent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return deleteAppImagesByIDs(t, client, imageIDs, excludeImages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteAppImagesByIDs(t *task.Task, client docker.Client, imageIDs []appImageID, excludeImages []string) error {
|
||||||
|
delImageStr := i18n.GetMsgByKey("TaskDelete") + i18n.GetMsgByKey("Image")
|
||||||
|
excludeImageIDs := make(map[string]struct{}, len(excludeImages))
|
||||||
|
for _, image := range excludeImages {
|
||||||
|
imageID, err := client.GetImageIDByName(image)
|
||||||
|
if err == nil && imageID != "" {
|
||||||
|
excludeImageIDs[imageID] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deletedImageIDs := make(map[string]struct{}, len(imageIDs))
|
||||||
|
for _, image := range imageIDs {
|
||||||
|
if _, ok := excludeImageIDs[image.id]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := deletedImageIDs[image.id]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
deletedImageIDs[image.id] = struct{}{}
|
||||||
|
imgStr := delImageStr + image.name
|
||||||
|
t.Log(imgStr)
|
||||||
|
if err := client.DeleteImage(image.id); err != nil {
|
||||||
|
t.LogFailedWithErr(imgStr, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.LogSuccess(imgStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func deleteLink(del dto.DelAppLink) error {
|
func deleteLink(del dto.DelAppLink) error {
|
||||||
install := del.Install
|
install := del.Install
|
||||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||||
@@ -663,7 +716,7 @@ func buildNginx(parentTask *task.Task) error {
|
|||||||
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("TaskBuild"), i18n.GetMsgByKey("Image"))
|
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("TaskBuild"), i18n.GetMsgByKey("Image"))
|
||||||
parentTask.LogStart(logStr)
|
parentTask.LogStart(logStr)
|
||||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*parentTask), cmd.WithTimeout(60*time.Minute))
|
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*parentTask), cmd.WithTimeout(60*time.Minute))
|
||||||
if err = cmdMgr.RunBashCf("docker compose -f %s build", nginxInstall.GetComposePath()); err != nil {
|
if err = cmdMgr.Run("docker", "compose", "-f", nginxInstall.GetComposePath(), "build"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
parentTask.LogSuccess(logStr)
|
parentTask.LogSuccess(logStr)
|
||||||
@@ -680,6 +733,9 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if install.App.Key == vllmAppKeyForUpgrade && !isVllmUpgradeVersionAllowed(install.Version, detail.Version, loadVllmImageFromEnv(install.Env)) {
|
||||||
|
return errors.New("vLLM can only upgrade within the same image type")
|
||||||
|
}
|
||||||
if install.Version == detail.Version {
|
if install.Version == detail.Version {
|
||||||
return errors.New("two version is same")
|
return errors.New("two version is same")
|
||||||
}
|
}
|
||||||
@@ -746,28 +802,23 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dockerCLi, _ := docker.NewClient()
|
oldEnvContent := append([]byte(nil), content...)
|
||||||
if req.PullImage {
|
oldDockerCompose := install.DockerCompose
|
||||||
composeContent := []byte(detail.DockerCompose)
|
if install.App.Key == vllmAppKeyForUpgrade {
|
||||||
if req.DockerCompose != "" {
|
envs := make(map[string]interface{})
|
||||||
composeContent = []byte(req.DockerCompose)
|
if err = json.Unmarshal([]byte(install.Env), &envs); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
images, err := docker.GetImagesFromDockerCompose(content, composeContent)
|
image := buildVllmUpgradeImage(loadVllmImageFromEnv(install.Env), oldVersion, detail.Version)
|
||||||
|
envs[vllmImageEnvKey] = image
|
||||||
|
paramByte, err := json.Marshal(envs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, image := range images {
|
install.Env = string(paramByte)
|
||||||
t.Log(i18n.GetWithName("PullImageStart", image))
|
content = setVllmImageInEnvContent(content, image)
|
||||||
if err = dockerCLi.PullImageWithProcess(t, image); err != nil {
|
|
||||||
err = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
t.LogSuccess(i18n.GetMsgByKey("PullImage"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
_ = copyAppDetailMissing(fileOp, detailDir, install.GetPath())
|
||||||
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
|
|
||||||
_, _ = command.CombinedOutput()
|
|
||||||
if install.App.Key == constant.AppOpenresty {
|
if install.App.Key == constant.AppOpenresty {
|
||||||
installBuildDir := path.Join(install.GetPath(), "build")
|
installBuildDir := path.Join(install.GetPath(), "build")
|
||||||
detailBuildDir := path.Join(detailDir, "build")
|
detailBuildDir := path.Join(detailDir, "build")
|
||||||
@@ -806,9 +857,13 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if req.DockerCompose == "" {
|
if req.DockerCompose == "" {
|
||||||
newCompose, err = getUpgradeCompose(install, detail)
|
if install.App.Key == vllmAppKeyForUpgrade {
|
||||||
if err != nil {
|
newCompose = install.DockerCompose
|
||||||
return err
|
} else {
|
||||||
|
newCompose, err = getUpgradeCompose(install, detail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newCompose = req.DockerCompose
|
newCompose = req.DockerCompose
|
||||||
@@ -818,6 +873,46 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||||||
install.Version = detail.Version
|
install.Version = detail.Version
|
||||||
install.AppDetailId = req.DetailID
|
install.AppDetailId = req.DetailID
|
||||||
|
|
||||||
|
var oldImageIDs []appImageID
|
||||||
|
if req.DeleteImage {
|
||||||
|
dockerCLi, err := docker.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oldImageIDs, err = getAppImageIDsByCompose(dockerCLi, oldEnvContent, []byte(oldDockerCompose))
|
||||||
|
dockerCLi.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.PullImage {
|
||||||
|
images, err := docker.GetImagesFromDockerCompose(content, []byte(install.DockerCompose))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dockerCLi, err := docker.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dockerCLi.Close()
|
||||||
|
for _, image := range images {
|
||||||
|
t.Log(i18n.GetWithName("PullImageStart", image))
|
||||||
|
if pullErr := dockerCLi.PullImageWithProcess(t, image); pullErr != nil {
|
||||||
|
if exist, _ := dockerCLi.ImageExists(image); exist {
|
||||||
|
t.Log(i18n.GetMsgByKey("UseExistImage"))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return buserr.WithNameAndErr("ErrDockerPullImage", "", pullErr)
|
||||||
|
}
|
||||||
|
exist, err := dockerCLi.ImageExists(image)
|
||||||
|
if err != nil || !exist {
|
||||||
|
return buserr.WithNameAndErr("ErrDockerPullImage", "", fmt.Errorf("image %s does not exist after pull: %v", image, err))
|
||||||
|
}
|
||||||
|
t.LogSuccess(i18n.GetMsgByKey("PullImage"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||||
if out != "" {
|
if out != "" {
|
||||||
upErr = errors.New(out)
|
upErr = errors.New(out)
|
||||||
@@ -868,7 +963,31 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||||||
}
|
}
|
||||||
t.LogSuccess(logStr)
|
t.LogSuccess(logStr)
|
||||||
install.Status = constant.StatusRunning
|
install.Status = constant.StatusRunning
|
||||||
return appInstallRepo.Save(context.Background(), &install)
|
if err = appInstallRepo.Save(context.Background(), &install); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if req.DeleteImage {
|
||||||
|
newEnvContent, err := fileOp.GetContent(install.GetEnvPath())
|
||||||
|
if err != nil {
|
||||||
|
t.LogFailedWithErr(i18n.GetMsgByKey("TaskDelete")+i18n.GetMsgByKey("Image"), err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
excludeImages, err := docker.GetImagesFromDockerCompose(newEnvContent, []byte(install.DockerCompose))
|
||||||
|
if err != nil {
|
||||||
|
t.LogFailedWithErr(i18n.GetMsgByKey("TaskDelete")+i18n.GetMsgByKey("Image"), err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dockerCLi, err := docker.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.LogFailedWithErr(i18n.GetMsgByKey("TaskDelete")+i18n.GetMsgByKey("Image"), err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer dockerCLi.Close()
|
||||||
|
if err = deleteAppImagesByIDs(t, dockerCLi, oldImageIDs, excludeImages); err != nil {
|
||||||
|
t.LogFailedWithErr(i18n.GetMsgByKey("TaskDelete")+i18n.GetMsgByKey("Image"), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rollBackApp := func(t *task.Task) {
|
rollBackApp := func(t *task.Task) {
|
||||||
@@ -883,7 +1002,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upgradeTask.AddSubTaskWithOps(task.GetTaskName(install.Name, task.TaskScopeApp, task.TaskUpgrade), upgradeApp, rollBackApp, 0, 1*time.Hour)
|
upgradeTask.AddSubTaskWithOps(task.GetTaskName(install.Name, task.TaskUpgrade, task.TaskScopeApp), upgradeApp, rollBackApp, 0, 1*time.Hour)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err = upgradeTask.Execute()
|
err = upgradeTask.Execute()
|
||||||
@@ -1032,7 +1151,7 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz, ""); err != nil {
|
if err = fileOp.Decompress(context.Background(), filePath, appResourceDir, files.SdkTarGz, ""); err != nil {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
||||||
} else {
|
} else {
|
||||||
@@ -1127,7 +1246,7 @@ func runScript(task *task.Task, appInstall *model.AppInstall, operate string) er
|
|||||||
task.LogStart(logStr)
|
task.LogStart(logStr)
|
||||||
|
|
||||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithWorkDir(workDir))
|
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithWorkDir(workDir))
|
||||||
if err := cmdMgr.RunBashC(scriptPath); err != nil {
|
if err := cmdMgr.Run("bash", scriptPath); err != nil {
|
||||||
task.LogFailedWithErr(logStr, err)
|
task.LogFailedWithErr(logStr, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1176,11 +1295,12 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
imagePrefix := xpack.GetImagePrefix()
|
imagePrefix := xpack.MultiNodeProvider.GetImagePrefix()
|
||||||
dockerCLi, err := docker.NewClient()
|
dockerCLi, err := docker.NewClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer dockerCLi.Close()
|
||||||
for _, image := range images {
|
for _, image := range images {
|
||||||
if imagePrefix != "" {
|
if imagePrefix != "" {
|
||||||
lastSlashIndex := strings.LastIndex(image, "/")
|
lastSlashIndex := strings.LastIndex(image, "/")
|
||||||
@@ -1594,6 +1714,9 @@ func handleInstalled(appInstallList []model.AppInstall, updated, sync, checkUpda
|
|||||||
synAppInstall(containersMap, &installed, false)
|
synAppInstall(containersMap, &installed, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceKeys := getAppInstallResourceKeys(installed.ID)
|
||||||
|
envMap := make(map[string]interface{})
|
||||||
|
_ = json.Unmarshal([]byte(installed.Env), &envMap)
|
||||||
installDTO := response.AppInstallDTO{
|
installDTO := response.AppInstallDTO{
|
||||||
ID: installed.ID,
|
ID: installed.ID,
|
||||||
Name: installed.Name,
|
Name: installed.Name,
|
||||||
@@ -1615,14 +1738,16 @@ func handleInstalled(appInstallList []model.AppInstall, updated, sync, checkUpda
|
|||||||
Website: installed.App.Website,
|
Website: installed.App.Website,
|
||||||
Document: installed.App.Document,
|
Document: installed.App.Document,
|
||||||
},
|
},
|
||||||
Favorite: installed.Favorite,
|
Favorite: installed.Favorite,
|
||||||
SortOrder: installed.SortOrder,
|
SortOrder: installed.SortOrder,
|
||||||
Container: installed.ContainerName,
|
Container: installed.ContainerName,
|
||||||
ServiceName: strings.ToLower(installed.ServiceName),
|
ServiceName: strings.ToLower(installed.ServiceName),
|
||||||
|
ResourceKeys: resourceKeys,
|
||||||
|
Env: envMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !updated && !checkUpdate {
|
if !updated && !checkUpdate {
|
||||||
installDTO.LinkDB = hasLinkDB(installed.ID)
|
installDTO.LinkDB = hasLinkDBFromKeys(resourceKeys)
|
||||||
res = append(res, installDTO)
|
res = append(res, installDTO)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -1630,7 +1755,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated, sync, checkUpda
|
|||||||
if installed.Version == "latest" {
|
if installed.Version == "latest" {
|
||||||
if checkUpdate {
|
if checkUpdate {
|
||||||
installDTO.CanUpdate = false
|
installDTO.CanUpdate = false
|
||||||
installDTO.LinkDB = hasLinkDB(installed.ID)
|
installDTO.LinkDB = hasLinkDBFromKeys(resourceKeys)
|
||||||
res = append(res, installDTO)
|
res = append(res, installDTO)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -1659,7 +1784,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated, sync, checkUpda
|
|||||||
if len(versions) == 0 {
|
if len(versions) == 0 {
|
||||||
if checkUpdate {
|
if checkUpdate {
|
||||||
installDTO.CanUpdate = false
|
installDTO.CanUpdate = false
|
||||||
installDTO.LinkDB = hasLinkDB(installed.ID)
|
installDTO.LinkDB = hasLinkDBFromKeys(resourceKeys)
|
||||||
res = append(res, installDTO)
|
res = append(res, installDTO)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -1691,7 +1816,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated, sync, checkUpda
|
|||||||
res = append(res, installDTO)
|
res = append(res, installDTO)
|
||||||
}
|
}
|
||||||
} else if checkUpdate {
|
} else if checkUpdate {
|
||||||
installDTO.LinkDB = hasLinkDB(installed.ID)
|
installDTO.LinkDB = hasLinkDBFromKeys(resourceKeys)
|
||||||
res = append(res, installDTO)
|
res = append(res, installDTO)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1776,7 +1901,7 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
|
|||||||
if !serviceValid {
|
if !serviceValid {
|
||||||
return buserr.New("ErrFileParse")
|
return buserr.New("ErrFileParse")
|
||||||
}
|
}
|
||||||
imagePreFix := xpack.GetImagePrefix()
|
imagePreFix := xpack.MultiNodeProvider.GetImagePrefix()
|
||||||
if imagePreFix != "" {
|
if imagePreFix != "" {
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
serviceValue := service.(map[string]interface{})
|
serviceValue := service.(map[string]interface{})
|
||||||
@@ -1933,6 +2058,39 @@ func isHostModel(dockerCompose string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyAppDetailMissing(fileOp files.FileOp, srcDir, dstDir string) error {
|
||||||
|
entries, err := os.ReadDir(srcDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if strings.HasPrefix(entry.Name(), ".") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
srcPath := path.Join(srcDir, entry.Name())
|
||||||
|
dstPath := path.Join(dstDir, entry.Name())
|
||||||
|
if !fileOp.Stat(dstPath) {
|
||||||
|
if entry.IsDir() {
|
||||||
|
if err := fileOp.CopyDir(srcPath, dstDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := fileOp.CopyFile(srcPath, dstDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := copyAppDetailMissing(fileOp, srcPath, dstPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getRestartPolicy(yml string) string {
|
func getRestartPolicy(yml string) string {
|
||||||
var project docker.ComposeProject
|
var project docker.ComposeProject
|
||||||
if err := yaml.Unmarshal([]byte(yml), &project); err != nil {
|
if err := yaml.Unmarshal([]byte(yml), &project); err != nil {
|
||||||
@@ -2119,7 +2277,7 @@ func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool, sslRe
|
|||||||
caRequest := request.WebsiteCAObtain{
|
caRequest := request.WebsiteCAObtain{
|
||||||
ID: ca.ID,
|
ID: ca.ID,
|
||||||
Domains: "localhost",
|
Domains: "localhost",
|
||||||
KeyType: "4096",
|
KeyType: "RSA4096",
|
||||||
Time: 99,
|
Time: 99,
|
||||||
Unit: "year",
|
Unit: "year",
|
||||||
Dir: sslDir,
|
Dir: sslDir,
|
||||||
@@ -2225,13 +2383,25 @@ func needsUpdate(localTag *model.Tag, remoteTag dto.Tag, translations string) bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func hasLinkDB(installID uint) bool {
|
func hasLinkDB(installID uint) bool {
|
||||||
|
return hasLinkDBFromKeys(getAppInstallResourceKeys(installID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAppInstallResourceKeys(installID uint) []string {
|
||||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(installID))
|
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(installID))
|
||||||
|
keys := make([]string, 0, len(resources))
|
||||||
|
for _, resource := range resources {
|
||||||
|
keys = append(keys, resource.Key)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasLinkDBFromKeys(resourceKeys []string) bool {
|
||||||
hasDB := false
|
hasDB := false
|
||||||
if len(resources) > 0 {
|
if len(resourceKeys) > 0 {
|
||||||
for _, resource := range resources {
|
for _, resourceKey := range resourceKeys {
|
||||||
if resource.Key == constant.AppPostgres || resource.Key == constant.AppMysql ||
|
if resourceKey == constant.AppPostgres || resourceKey == constant.AppMysql ||
|
||||||
resource.Key == constant.AppMariaDB || resource.Key == constant.AppMysqlCluster ||
|
resourceKey == constant.AppMariaDB || resourceKey == constant.AppMysqlCluster ||
|
||||||
resource.Key == constant.AppPostgresql || resource.Key == constant.AppPostgresqlCluster {
|
resourceKey == constant.AppPostgresql || resourceKey == constant.AppPostgresqlCluster {
|
||||||
hasDB = true
|
hasDB = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -2246,10 +2416,39 @@ func isEditCompose(installed model.AppInstall) bool {
|
|||||||
if rawCompose == "" || err != nil {
|
if rawCompose == "" || err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if rawCompose != installed.DockerCompose {
|
equal, err := composeEqualExceptImage(rawCompose, installed.DockerCompose)
|
||||||
return true
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !equal
|
||||||
|
}
|
||||||
|
|
||||||
|
func composeEqualExceptImage(expected, current string) (bool, error) {
|
||||||
|
expectedCompose := make(map[string]interface{})
|
||||||
|
if err := yaml.Unmarshal([]byte(expected), &expectedCompose); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
currentCompose := make(map[string]interface{})
|
||||||
|
if err := yaml.Unmarshal([]byte(current), ¤tCompose); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
removeComposeServiceImages(expectedCompose)
|
||||||
|
removeComposeServiceImages(currentCompose)
|
||||||
|
return reflect.DeepEqual(expectedCompose, currentCompose), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeComposeServiceImages(composeMap map[string]interface{}) {
|
||||||
|
services, ok := composeMap["services"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, service := range services {
|
||||||
|
serviceMap, ok := service.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delete(serviceMap, "image")
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAppVersions(key string, details []model.AppDetail) []string {
|
func getAppVersions(key string, details []model.AppDetail) []string {
|
||||||
|
|||||||
@@ -44,10 +44,13 @@ type IBackupService interface {
|
|||||||
|
|
||||||
MysqlBackup(db dto.CommonBackup) error
|
MysqlBackup(db dto.CommonBackup) error
|
||||||
PostgresqlBackup(db dto.CommonBackup) error
|
PostgresqlBackup(db dto.CommonBackup) error
|
||||||
|
MongodbBackup(db dto.CommonBackup) error
|
||||||
MysqlRecover(db dto.CommonRecover) error
|
MysqlRecover(db dto.CommonRecover) error
|
||||||
PostgresqlRecover(db dto.CommonRecover) error
|
PostgresqlRecover(db dto.CommonRecover) error
|
||||||
|
MongodbRecover(db dto.CommonRecover) error
|
||||||
MysqlRecoverByUpload(req dto.CommonRecover) error
|
MysqlRecoverByUpload(req dto.CommonRecover) error
|
||||||
PostgresqlRecoverByUpload(req dto.CommonRecover) error
|
PostgresqlRecoverByUpload(req dto.CommonRecover) error
|
||||||
|
MongodbRecoverByUpload(req dto.CommonRecover) error
|
||||||
|
|
||||||
RedisBackup(db dto.CommonBackup) error
|
RedisBackup(db dto.CommonBackup) error
|
||||||
RedisRecover(db dto.CommonRecover) error
|
RedisRecover(db dto.CommonRecover) error
|
||||||
|
|||||||
@@ -63,11 +63,30 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, er
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = handleAppBackup(&install, nil, record.ID, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
if !req.IsImmediate {
|
||||||
global.LOG.Errorf("backup app %s failed, err: %v", req.DetailName, err)
|
if err = handleAppBackup(&install, nil, record.ID, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
||||||
return nil, err
|
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||||
|
global.LOG.Errorf("backup app %s failed, err: %v", req.DetailName, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backupTask, err := task.NewTaskWithOps(install.Name, task.TaskBackup, task.TaskScopeBackup, req.TaskID, install.ID)
|
||||||
|
if err != nil {
|
||||||
|
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||||
|
record.Status = constant.StatusFailed
|
||||||
|
record.Message = err.Error()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = doAppBackup(&install, backupTask, backupDir, fileName, "", req.Secret); err != nil {
|
||||||
|
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||||
|
record.Status = constant.StatusFailed
|
||||||
|
record.Message = err.Error()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||||
|
record.Status = constant.StatusSuccess
|
||||||
return record, nil
|
return record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -595,35 +596,22 @@ func stepRecreateContainer(recoverCtx *containerRecoverContext, taskItem *task.T
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
networkConfig, extraNetworks := buildContainerRecoverNetworkConfig(recoverCtx.inspectInfo.NetworkSettings, hostConfig)
|
createRes, err := createContainerWithOldNetworks(ctx, recoverCtx.client, config, hostConfig, recoverCtx.inspectInfo.NetworkSettings, recoverCtx.targetName)
|
||||||
removeBridgeDriverIPAM(recoverCtx.client, networkConfig, extraNetworks)
|
|
||||||
createRes, err := recoverCtx.client.ContainerCreate(ctx, config, hostConfig, networkConfig, nil, recoverCtx.targetName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
recoverCtx.createdContainerID = createRes.ID
|
recoverCtx.createdContainerID = createRes.ID
|
||||||
|
|
||||||
extraNames := make([]string, 0, len(extraNetworks))
|
|
||||||
for name := range extraNetworks {
|
|
||||||
extraNames = append(extraNames, name)
|
|
||||||
}
|
|
||||||
sort.Strings(extraNames)
|
|
||||||
for _, item := range extraNames {
|
|
||||||
if err := recoverCtx.client.NetworkConnect(ctx, item, recoverCtx.createdContainerID, extraNetworks[item]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeBridgeDriverIPAM(cli *client.Client, primary *network.NetworkingConfig, extras map[string]*network.EndpointSettings) {
|
func removeUnsupportedEndpointStaticIPAM(cli *client.Client, primary *network.NetworkingConfig, extras map[string]*network.EndpointSettings) {
|
||||||
if primary != nil {
|
if primary != nil {
|
||||||
removeBridgeDriverIPAMFromEndpoints(cli, primary.EndpointsConfig)
|
removeUnsupportedEndpointStaticIPAMFromEndpoints(cli, primary.EndpointsConfig)
|
||||||
}
|
}
|
||||||
removeBridgeDriverIPAMFromEndpoints(cli, extras)
|
removeUnsupportedEndpointStaticIPAMFromEndpoints(cli, extras)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeBridgeDriverIPAMFromEndpoints(cli *client.Client, endpoints map[string]*network.EndpointSettings) {
|
func removeUnsupportedEndpointStaticIPAMFromEndpoints(cli *client.Client, endpoints map[string]*network.EndpointSettings) {
|
||||||
for netName, endpoint := range endpoints {
|
for netName, endpoint := range endpoints {
|
||||||
if endpoint == nil || endpoint.IPAMConfig == nil {
|
if endpoint == nil || endpoint.IPAMConfig == nil {
|
||||||
continue
|
continue
|
||||||
@@ -632,10 +620,59 @@ func removeBridgeDriverIPAMFromEndpoints(cli *client.Client, endpoints map[strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if info.Driver == "bridge" {
|
removeUnsupportedEndpointStaticIP(netName, info, endpoint)
|
||||||
endpoint.IPAMConfig = nil
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeUnsupportedEndpointStaticIP(netName string, info network.Inspect, endpoint *network.EndpointSettings) {
|
||||||
|
if endpoint == nil || endpoint.IPAMConfig == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isDefaultBridgeNetwork(netName, info) {
|
||||||
|
endpoint.IPAMConfig = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoint.IPAMConfig.IPv4Address != "" && !networkSupportsStaticIP(info, endpoint.IPAMConfig.IPv4Address, false) {
|
||||||
|
endpoint.IPAMConfig.IPv4Address = ""
|
||||||
|
}
|
||||||
|
if endpoint.IPAMConfig.IPv6Address != "" && !networkSupportsStaticIP(info, endpoint.IPAMConfig.IPv6Address, true) {
|
||||||
|
endpoint.IPAMConfig.IPv6Address = ""
|
||||||
|
}
|
||||||
|
if endpoint.IPAMConfig.IPv4Address == "" && endpoint.IPAMConfig.IPv6Address == "" {
|
||||||
|
endpoint.IPAMConfig = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDefaultBridgeNetwork(netName string, info network.Inspect) bool {
|
||||||
|
return info.Driver == "bridge" && (netName == "bridge" || info.Name == "bridge")
|
||||||
|
}
|
||||||
|
|
||||||
|
func networkSupportsStaticIP(info network.Inspect, ip string, isIPv6 bool) bool {
|
||||||
|
if ip == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
addr, err := netip.ParseAddr(ip)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if addr.Is6() != isIPv6 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range info.IPAM.Config {
|
||||||
|
if item.Subnet == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subnet, err := netip.ParsePrefix(item.Subnet)
|
||||||
|
if err != nil || subnet.Addr().Is6() != isIPv6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if subnet.Contains(addr) {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureContainerRecoverNetworks(recoverCtx *containerRecoverContext) error {
|
func ensureContainerRecoverNetworks(recoverCtx *containerRecoverContext) error {
|
||||||
@@ -745,11 +782,6 @@ func buildContainerRecoverNetworkConfig(networkSettings *container.NetworkSettin
|
|||||||
IPv4Address: endpoint.IPAMConfig.IPv4Address,
|
IPv4Address: endpoint.IPAMConfig.IPv4Address,
|
||||||
IPv6Address: endpoint.IPAMConfig.IPv6Address,
|
IPv6Address: endpoint.IPAMConfig.IPv6Address,
|
||||||
}
|
}
|
||||||
} else if endpoint.IPAddress != "" || endpoint.GlobalIPv6Address != "" {
|
|
||||||
endpointSetting.IPAMConfig = &network.EndpointIPAMConfig{
|
|
||||||
IPv4Address: endpoint.IPAddress,
|
|
||||||
IPv6Address: endpoint.GlobalIPv6Address,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if name == primaryName {
|
if name == primaryName {
|
||||||
config.EndpointsConfig[name] = endpointSetting
|
config.EndpointsConfig[name] = endpointSetting
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user