mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-12 08:00:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c04848a92a | ||
|
|
748f5f070a | ||
|
|
2dcb367622 | ||
|
|
26bd2d08be | ||
|
|
739686ff89 | ||
|
|
68a871a5d7 | ||
|
|
b47bba4827 | ||
|
|
3b91859a49 | ||
|
|
c417bc9c2b | ||
|
|
7c95becfeb | ||
|
|
6b1480c5fa | ||
|
|
7f450544e1 | ||
|
|
0c046edfe5 | ||
|
|
2a2e6607b8 | ||
|
|
f6ed11ac55 | ||
|
|
d330f60b2d | ||
|
|
10c8cafcee | ||
|
|
d1534c88aa | ||
|
|
eb6b4c7cb7 | ||
|
|
c77ddcfebe | ||
|
|
af12526a6c | ||
|
|
b347abe100 | ||
|
|
092b57413b | ||
|
|
e8f35e4e06 | ||
|
|
b019e7526d | ||
|
|
1b06467521 | ||
|
|
7f3fd0cb1d | ||
|
|
3e9848554b | ||
|
|
b02cdbc5f9 | ||
|
|
d7723fd42a | ||
|
|
8b265bb81e | ||
|
|
89b6c94e42 | ||
|
|
219ac160b6 | ||
|
|
51ca1eab18 | ||
|
|
1ab3da1fab | ||
|
|
1d1b12c2fa | ||
|
|
07a658582d | ||
|
|
61da74ae50 | ||
|
|
90334f3cb0 | ||
|
|
fb9278bfc6 | ||
|
|
7750936af6 | ||
|
|
7b695af996 | ||
|
|
2874849557 | ||
|
|
9fd1f3e661 | ||
|
|
85182d9164 | ||
|
|
d9b2ea051f | ||
|
|
097fe6dcfb | ||
|
|
d5beca6d96 | ||
|
|
e3a356f893 | ||
|
|
8be2a9ab02 | ||
|
|
3d7f0f5143 | ||
|
|
1fd62fbaf7 | ||
|
|
f1372dda85 | ||
|
|
64c354adb2 | ||
|
|
a379e1f840 | ||
|
|
270ba799f7 | ||
|
|
60dcfacb88 | ||
|
|
b474d720e9 | ||
|
|
b2192b7252 | ||
|
|
6a988ad96a | ||
|
|
029ac5efee | ||
|
|
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 |
@@ -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
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
name: LLM Code Review
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, reopened, synchronize]
|
|
||||||
jobs:
|
|
||||||
llm-code-review:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: fit2cloud/LLM-CodeReview-Action@main
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.FIT2CLOUDRD_LLM_CODE_REVIEW_TOKEN }}
|
|
||||||
OPENAI_API_KEY: ${{ secrets.ALIYUN_LLM_API_KEY }}
|
|
||||||
LANGUAGE: English
|
|
||||||
OPENAI_API_ENDPOINT: https://dashscope.aliyuncs.com/compatible-mode/v1
|
|
||||||
MODEL: qwen2.5-coder-3b-instruct
|
|
||||||
PROMPT: "Please check the following code differences for any irregularities, potential issues, or optimization suggestions, and provide your answers in English."
|
|
||||||
top_p: 1
|
|
||||||
temperature: 1
|
|
||||||
# max_tokens: 10000
|
|
||||||
MAX_PATCH_LENGTH: 10000
|
|
||||||
IGNORE_PATTERNS: "/node_modules,*.md,/dist,/.github"
|
|
||||||
FILE_PATTERNS: "*.java,*.go,*.py,*.vue,*.ts,*.js,*.css,*.scss,*.html"
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
name: SonarCloud Scan
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- dev
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
jobs:
|
|
||||||
sonarcloud:
|
|
||||||
name: SonarCloud
|
|
||||||
if: github.repository == '1Panel-dev/1Panel'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: SonarCloud Scan
|
|
||||||
uses: SonarSource/sonarcloud-github-action@master
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUBTOKEN }}
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
||||||
@@ -6,7 +6,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Mirror the Github organization repos to Gitee.
|
- name: Mirror the Github organization repos to Gitee.
|
||||||
uses: Yikun/hub-mirror-action@master
|
uses: Yikun/hub-mirror-action@ba51c01b28a6c9f95a25d4f1bcf6af2a147c0e18 # master
|
||||||
with:
|
with:
|
||||||
src: 'github/1Panel-dev'
|
src: 'github/1Panel-dev'
|
||||||
dst: 'gitee/fit2cloud-feizhiyun'
|
dst: 'gitee/fit2cloud-feizhiyun'
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
name: Typos Check
|
|
||||||
on: pull_request
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
name: Spell Check with Typos
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Actions Repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Check spelling
|
|
||||||
uses: crate-ci/typos@master
|
|
||||||
+16
-2
@@ -40,10 +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/xpack-ee
|
frontend/src/enterprise
|
||||||
agent/xpack
|
agent/xpack
|
||||||
|
agent/enterprise
|
||||||
|
agent/router/entry_xpack.go
|
||||||
|
agent/router/entry_enterprise.go
|
||||||
|
agent/server/init_xpack.go
|
||||||
|
agent/server/init_enterprise.go
|
||||||
|
agent/utils/xpack/xpack.go
|
||||||
|
agent/utils/xpack/enterprise.go
|
||||||
core/xpack
|
core/xpack
|
||||||
core/xpack-ee
|
core/enterprise
|
||||||
|
core/router/entry_xpack.go
|
||||||
|
core/router/entry_enterprise.go
|
||||||
|
core/server/init_xpack.go
|
||||||
|
core/server/init_enterprise.go
|
||||||
|
core/utils/xpack/xpack.go
|
||||||
|
core/utils/xpack/enterprise.go
|
||||||
|
|
||||||
.history/
|
.history/
|
||||||
dist/
|
dist/
|
||||||
@@ -65,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:"
|
|
||||||
@@ -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,8 @@
|
|||||||
<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>
|
||||||
|
<a href="/docs/README.lo.md"><img alt="ພາສາລາວ" src="https://img.shields.io/badge/%E0%BA%9E%E0%BA%B2%E0%BA%AA%E0%BA%B2%E0%BA%A5%E0%BA%B2%E0%BA%A7-d9d9d9"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+146
-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
|
||||||
@@ -296,11 +380,11 @@ func (b *BaseApi) DeleteHermesChatSession(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Get Providers
|
// @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 {
|
||||||
@@ -311,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 {
|
||||||
@@ -331,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 {
|
||||||
@@ -351,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 {
|
||||||
@@ -375,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 {
|
||||||
@@ -396,13 +501,34 @@ func (b *BaseApi) GetAgentAccountModels(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags AI
|
// @Tags AI
|
||||||
// @Summary Create Agent account model
|
// @Summary Discover custom provider models
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.AgentAccountModelDiscoverReq true "request"
|
||||||
|
// @Success 200 {array} dto.AgentAccountModel
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /ai/accounts/models/discover [post]
|
||||||
|
func (b *BaseApi) DiscoverAgentAccountModels(c *gin.Context) {
|
||||||
|
var req dto.AgentAccountModelDiscoverReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, err := agentService.DiscoverAccountModels(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags AI
|
||||||
|
// @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 {
|
||||||
@@ -416,13 +542,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 {
|
||||||
@@ -436,13 +562,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 {
|
||||||
@@ -456,13 +582,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 {
|
||||||
@@ -476,13 +602,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 {
|
||||||
|
|||||||
+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,10 +413,15 @@ 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)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -41,20 +41,40 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags Database Mysql
|
// @Tags Database Mysql
|
||||||
// @Summary Bind user of mysql database
|
// @Summary List mysql users
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.BindUser true "request"
|
// @Param request body dto.MysqlUserSearch true "request"
|
||||||
// @Success 200
|
// @Success 200 {array} dto.MysqlUser
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /databases/bind [post]
|
// @Router /databases/users/search [post]
|
||||||
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 mysql 数据库名 [database] [username]","formatEN":"bind mysql database [database] [username]"}
|
func (b *BaseApi) ListMysqlUsers(c *gin.Context) {
|
||||||
func (b *BaseApi) BindUser(c *gin.Context) {
|
var req dto.MysqlUserSearch
|
||||||
var req dto.BindUser
|
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
data, err := mysqlService.ListUsers(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mysql
|
||||||
|
// @Summary Create mysql user
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MysqlUserCreate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/users [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database","username","host"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 mysql 数据库 [database] 用户 [username]@[host]","formatEN":"create mysql database [database] user [username]@[host]"}
|
||||||
|
func (b *BaseApi) CreateMysqlUser(c *gin.Context) {
|
||||||
|
var req dto.MysqlUserCreate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if len(req.Password) != 0 {
|
if len(req.Password) != 0 {
|
||||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -63,8 +83,191 @@ func (b *BaseApi) BindUser(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
req.Password = string(password)
|
req.Password = string(password)
|
||||||
}
|
}
|
||||||
|
if err := mysqlService.CreateUser(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
if err := mysqlService.BindUser(req); err != nil {
|
// @Tags Database Mysql
|
||||||
|
// @Summary Delete mysql user
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MysqlUserDelete true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/users/del [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database","username","host"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除 mysql 数据库 [database] 用户 [username]@[host]","formatEN":"delete mysql database [database] user [username]@[host]"}
|
||||||
|
func (b *BaseApi) DeleteMysqlUser(c *gin.Context) {
|
||||||
|
var req dto.MysqlUserDelete
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := mysqlService.DeleteUser(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mysql
|
||||||
|
// @Summary Update mysql user
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MysqlUserUpdate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/users/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database","username","host","newHost","description"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 mysql 数据库 [database] 用户 [username] 访问权限 [host] => [newHost] 描述 [description]","formatEN":"update mysql database [database] user [username] access [host] => [newHost] description [description]"}
|
||||||
|
func (b *BaseApi) UpdateMysqlUser(c *gin.Context) {
|
||||||
|
var req dto.MysqlUserUpdate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := mysqlService.UpdateUser(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mysql
|
||||||
|
// @Summary Change mysql user password
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MysqlUserPassword true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/users/password [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database","username","host"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 mysql 数据库 [database] 用户 [username]@[host] 密码","formatEN":"update mysql database [database] user [username]@[host] password"}
|
||||||
|
func (b *BaseApi) ChangeMysqlUserPassword(c *gin.Context) {
|
||||||
|
var req dto.MysqlUserPassword
|
||||||
|
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 := mysqlService.ChangeUserPassword(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mysql
|
||||||
|
// @Summary Save mysql user password locally
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MysqlUserPassword true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/users/password/save [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database","username","host"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"补充 mysql 数据库 [database] 用户 [username]@[host] 密码","formatEN":"save mysql database [database] user [username]@[host] password locally"}
|
||||||
|
func (b *BaseApi) SaveMysqlUserPassword(c *gin.Context) {
|
||||||
|
var req dto.MysqlUserPassword
|
||||||
|
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 := mysqlService.SaveUserPassword(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mysql
|
||||||
|
// @Summary List mysql grants
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MysqlUserSearch true "request"
|
||||||
|
// @Success 200 {array} dto.MysqlGrant
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/grants/search [post]
|
||||||
|
func (b *BaseApi) ListMysqlGrants(c *gin.Context) {
|
||||||
|
var req dto.MysqlUserSearch
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := mysqlService.ListGrants(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mysql
|
||||||
|
// @Summary List mysql grant summary
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MysqlGrantSummarySearch true "request"
|
||||||
|
// @Success 200 {object} map[string][]dto.MysqlUser
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/grants/summary [post]
|
||||||
|
func (b *BaseApi) ListMysqlGrantSummary(c *gin.Context) {
|
||||||
|
var req dto.MysqlGrantSummarySearch
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := mysqlService.ListGrantSummary(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mysql
|
||||||
|
// @Summary Grant mysql user
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MysqlGrantCreate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/grants [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database","db","username","host"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"授权 mysql 数据库 [database] 用户 [username]@[host] 访问 [db]","formatEN":"grant mysql database [database] user [username]@[host] access to [db]"}
|
||||||
|
func (b *BaseApi) GrantMysqlUser(c *gin.Context) {
|
||||||
|
var req dto.MysqlGrantCreate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := mysqlService.GrantUser(req); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Mysql
|
||||||
|
// @Summary Revoke mysql grant
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.MysqlGrantDelete true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /databases/grants/del [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["database","db","username","host"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"取消 mysql 数据库 [database] 用户 [username]@[host] 对 [db] 的授权","formatEN":"revoke mysql database [database] user [username]@[host] access to [db]"}
|
||||||
|
func (b *BaseApi) RevokeMysqlGrant(c *gin.Context) {
|
||||||
|
var req dto.MysqlGrantDelete
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := mysqlService.RevokeGrant(req); err != nil {
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -94,14 +297,14 @@ func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags Database Mysql
|
// @Tags Database Mysql
|
||||||
// @Summary Change mysql password
|
// @Summary Change mysql root password
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.ChangeDBInfo true "request"
|
// @Param request body dto.ChangeDBInfo true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /databases/change/password [post]
|
// @Router /databases/change/password [post]
|
||||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
|
// @x-panel-log {"bodyKeys":["database"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新数据库 [database] root 密码","formatEN":"Update database [database] root password"}
|
||||||
func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
|
func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
|
||||||
var req dto.ChangeDBInfo
|
var req dto.ChangeDBInfo
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@@ -125,14 +328,14 @@ func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Tags Database Mysql
|
// @Tags Database Mysql
|
||||||
// @Summary Change mysql access
|
// @Summary Change mysql root access
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.ChangeDBInfo true "request"
|
// @Param request body dto.ChangeDBInfo true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /databases/change/access [post]
|
// @Router /databases/change/access [post]
|
||||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 访问权限","formatEN":"Update database [name] access"}
|
// @x-panel-log {"bodyKeys":["database"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新数据库 [database] root 访问权限","formatEN":"Update database [database] root access"}
|
||||||
func (b *BaseApi) ChangeMysqlAccess(c *gin.Context) {
|
func (b *BaseApi) ChangeMysqlAccess(c *gin.Context) {
|
||||||
var req dto.ChangeDBInfo
|
var req dto.ChangeDBInfo
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,14 +37,15 @@ var (
|
|||||||
|
|
||||||
cronjobService = service.NewICronjobService()
|
cronjobService = service.NewICronjobService()
|
||||||
|
|
||||||
fileService = service.NewIFileService()
|
fileService = service.NewIFileService()
|
||||||
fileHistoryService = service.NewIFileHistoryService()
|
fileHistoryService = service.NewIFileHistoryService()
|
||||||
fileShareService = service.NewIFileShareService()
|
fileShareService = service.NewIFileShareService()
|
||||||
sshService = service.NewISSHService()
|
sshService = service.NewISSHService()
|
||||||
firewallService = service.NewIFirewallService()
|
firewallService = service.NewIFirewallService()
|
||||||
iptablesService = service.NewIIptablesService()
|
iptablesService = service.NewIIptablesService()
|
||||||
monitorService = service.NewIMonitorService()
|
monitorService = service.NewIMonitorService()
|
||||||
systemService = service.NewISystemService()
|
systemService = service.NewISystemService()
|
||||||
|
runtimeDiagnosticsService = service.NewIRuntimeDiagnosticsService()
|
||||||
|
|
||||||
deviceService = service.NewIDeviceService()
|
deviceService = service.NewIDeviceService()
|
||||||
fail2banService = service.NewIFail2BanService()
|
fail2banService = service.NewIFail2BanService()
|
||||||
|
|||||||
+137
-20
@@ -12,7 +12,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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"
|
||||||
@@ -28,6 +30,11 @@ import (
|
|||||||
qrcode "github.com/skip2/go-qrcode"
|
qrcode "github.com/skip2/go-qrcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var cancelledChunkUploads = struct {
|
||||||
|
sync.RWMutex
|
||||||
|
ids map[string]struct{}
|
||||||
|
}{ids: make(map[string]struct{})}
|
||||||
|
|
||||||
// @Tags File
|
// @Tags File
|
||||||
// @Summary List files
|
// @Summary List files
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@@ -193,7 +200,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 +222,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 {
|
||||||
@@ -292,6 +299,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
|
||||||
@@ -391,7 +418,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) {
|
||||||
@@ -432,8 +459,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
|
||||||
@@ -456,7 +489,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)
|
||||||
@@ -614,6 +647,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)
|
||||||
@@ -649,6 +686,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 {
|
||||||
@@ -767,12 +808,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)
|
||||||
@@ -794,6 +836,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
|
||||||
}
|
}
|
||||||
@@ -837,7 +880,24 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
filename := c.PostForm("filename")
|
filename := c.PostForm("filename")
|
||||||
fileDir := filepath.Join(tmpDir, filename)
|
if filename == "" || filepath.Base(filename) != filename || strings.ContainsAny(filename, `/\\`) {
|
||||||
|
helper.BadRequest(c, errors.New("invalid filename"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uploadID := strings.TrimSpace(c.PostForm("uploadID"))
|
||||||
|
cancellable := uploadID != ""
|
||||||
|
if cancellable && (filepath.Base(uploadID) != uploadID || strings.ContainsAny(uploadID, `/\\`)) {
|
||||||
|
helper.BadRequest(c, errors.New("invalid upload ID"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !cancellable {
|
||||||
|
uploadID = filename
|
||||||
|
}
|
||||||
|
fileDir := filepath.Join(tmpDir, uploadID)
|
||||||
|
if cancellable && chunkUploadCancelled(uploadID) {
|
||||||
|
helper.BadRequest(c, errors.New("upload cancelled"))
|
||||||
|
return
|
||||||
|
}
|
||||||
if chunkIndex == 0 {
|
if chunkIndex == 0 {
|
||||||
if fileOp.Stat(fileDir) {
|
if fileOp.Stat(fileDir) {
|
||||||
_ = fileOp.DeleteDir(fileDir)
|
_ = fileOp.DeleteDir(fileDir)
|
||||||
@@ -848,7 +908,7 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.Remove(fileDir)
|
_ = os.RemoveAll(fileDir)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
var (
|
var (
|
||||||
@@ -868,6 +928,11 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
|||||||
helper.InternalServer(c, buserr.WithMap("ErrFileUpload", map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
helper.InternalServer(c, buserr.WithMap("ErrFileUpload", map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if cancellable && chunkUploadCancelled(uploadID) {
|
||||||
|
err = errors.New("upload cancelled")
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
|
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
|
||||||
err = os.WriteFile(chunkPath, chunkData, constant.DirPerm)
|
err = os.WriteFile(chunkPath, chunkData, constant.DirPerm)
|
||||||
@@ -886,19 +951,67 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
|||||||
helper.InternalServer(c, buserr.WithMap("ErrFileUpload", map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
helper.InternalServer(c, buserr.WithMap("ErrFileUpload", map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if cancellable {
|
||||||
|
cancelledChunkUploads.Lock()
|
||||||
|
delete(cancelledChunkUploads.ids, uploadID)
|
||||||
|
cancelledChunkUploads.Unlock()
|
||||||
|
}
|
||||||
helper.SuccessWithData(c, true)
|
helper.SuccessWithData(c, true)
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StopChunkUpload removes temporary chunks left by a cancelled upload.
|
||||||
|
func (b *BaseApi) StopChunkUpload(c *gin.Context) {
|
||||||
|
var req request.FileProcessReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uploadID := strings.TrimSpace(req.Key)
|
||||||
|
if uploadID == "" || filepath.Base(uploadID) != uploadID || strings.ContainsAny(uploadID, `/\\`) {
|
||||||
|
helper.BadRequest(c, errors.New("invalid upload ID"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cancelledChunkUploads.Lock()
|
||||||
|
cancelledChunkUploads.ids[uploadID] = struct{}{}
|
||||||
|
cancelledChunkUploads.Unlock()
|
||||||
|
time.AfterFunc(10*time.Minute, func() {
|
||||||
|
cancelledChunkUploads.Lock()
|
||||||
|
delete(cancelledChunkUploads.ids, uploadID)
|
||||||
|
cancelledChunkUploads.Unlock()
|
||||||
|
})
|
||||||
|
if err := os.RemoveAll(filepath.Join(global.Dir.TmpDir, "upload", uploadID)); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func chunkUploadCancelled(uploadID string) bool {
|
||||||
|
cancelledChunkUploads.RLock()
|
||||||
|
_, ok := cancelledChunkUploads.ids[uploadID]
|
||||||
|
cancelledChunkUploads.RUnlock()
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
var wsUpgrade = websocket.Upgrader{
|
var wsUpgrade = websocket.Upgrader{
|
||||||
CheckOrigin: func(r *http.Request) bool {
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @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
|
||||||
@@ -908,6 +1021,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-")
|
||||||
@@ -927,16 +1046,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)
|
||||||
@@ -969,16 +1092,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
|
||||||
@@ -1310,6 +1423,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)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
// @Summary Load file history list
|
// @Summary Load file history list
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.FileHistorySearchReq true "request"
|
// @Param request body request.FileHistorySearchReq true "request"
|
||||||
// @Success 200 {object} response.PageResult
|
// @Success 200 {object} dto.PageResult
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /files/history/search [post]
|
// @Router /files/history/search [post]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,3 +21,57 @@ func (b *BaseApi) GetSystemFiles(c *gin.Context) {
|
|||||||
|
|
||||||
helper.SuccessWithData(c, data)
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Logs
|
||||||
|
// @Summary Get host system log status
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} dto.SystemLogStatus
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /logs/system/status [get]
|
||||||
|
func (b *BaseApi) GetSystemLogStatus(c *gin.Context) {
|
||||||
|
data, err := logService.GetSystemLogStatus()
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Logs
|
||||||
|
// @Summary Read host logs
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.SystemLogReq true "request"
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} dto.SystemLogRes
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /logs/system/read [post]
|
||||||
|
func (b *BaseApi) ReadSystemLog(c *gin.Context) {
|
||||||
|
var req dto.SystemLogReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := logService.ReadSystemLog(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Logs
|
||||||
|
// @Summary List running host services
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {array} string
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /logs/system/services [get]
|
||||||
|
func (b *BaseApi) ListRunningServices(c *gin.Context) {
|
||||||
|
data, err := logService.ListRunningServices()
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags RuntimeDiagnostics
|
||||||
|
// @Summary Load runtime diagnostics summary
|
||||||
|
// @Success 200 {object} dto.RuntimeDiagnosticsSummary
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/diagnostics/summary [get]
|
||||||
|
func (b *BaseApi) LoadRuntimeDiagnosticsSummary(c *gin.Context) {
|
||||||
|
data, err := runtimeDiagnosticsService.Summary()
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags RuntimeDiagnostics
|
||||||
|
// @Summary Load grouped goroutine snapshot
|
||||||
|
// @Success 200 {object} dto.RuntimeGoroutineSnapshot
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/diagnostics/goroutines [get]
|
||||||
|
func (b *BaseApi) LoadRuntimeGoroutines(c *gin.Context) {
|
||||||
|
data, err := runtimeDiagnosticsService.Goroutines()
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithDataGzipped(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags RuntimeDiagnostics
|
||||||
|
// @Summary Capture runtime profile
|
||||||
|
// @Param request body dto.RuntimeProfileCreate true "request"
|
||||||
|
// @Success 200 {file} file
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/diagnostics/profiles [post]
|
||||||
|
func (b *BaseApi) CreateRuntimeProfile(c *gin.Context) {
|
||||||
|
var req dto.RuntimeProfileCreate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
profile, err := runtimeDiagnosticsService.CreateProfile(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequest(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.Remove(profile.Path)
|
||||||
|
c.Header("Content-Disposition", `attachment; filename="`+profile.Name+`"`)
|
||||||
|
c.Header("Content-Type", "application/octet-stream")
|
||||||
|
c.File(profile.Path)
|
||||||
|
c.Abort()
|
||||||
|
}
|
||||||
+51
-30
@@ -1,16 +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/dto/request"
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
|
||||||
"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
|
||||||
@@ -28,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 {
|
||||||
@@ -50,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
|
||||||
}
|
}
|
||||||
@@ -69,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 {
|
||||||
@@ -82,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 {
|
||||||
@@ -91,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 {
|
||||||
@@ -138,6 +164,16 @@ func (b *BaseApi) UpdateFileHistorySetting(c *gin.Context) {
|
|||||||
helper.Success(c)
|
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
|
||||||
@@ -158,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 {
|
||||||
@@ -173,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
|
||||||
@@ -223,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{
|
||||||
@@ -243,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
|
||||||
|
|||||||
@@ -224,6 +224,21 @@ func (b *BaseApi) ExportSSHLogs(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, tmpFile)
|
helper.SuccessWithData(c, tmpFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags SSH
|
||||||
|
// @Summary Clean host SSH logs
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Security Timestamp
|
||||||
|
// @Router /hosts/ssh/log/clean [post]
|
||||||
|
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空 SSH 登录日志","formatEN":"clean SSH login logs"}
|
||||||
|
func (b *BaseApi) CleanSSHLogs(c *gin.Context) {
|
||||||
|
if err := sshService.CleanLog(); err != nil {
|
||||||
|
helper.InternalServer(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.Success(c)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags SSH
|
// @Tags SSH
|
||||||
// @Summary Load host SSH conf
|
// @Summary Load host SSH conf
|
||||||
// @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 {
|
||||||
|
|||||||
+144
-105
@@ -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")
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
+172
-68
@@ -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,min=1,max=65535"`
|
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 hermes-agent"`
|
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 {
|
||||||
@@ -36,11 +118,11 @@ type AgentItem struct {
|
|||||||
ProviderName string `json:"providerName"`
|
ProviderName string `json:"providerName"`
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
APIType string `json:"apiType"`
|
APIType string `json:"apiType"`
|
||||||
MaxTokens int `json:"maxTokens"`
|
|
||||||
ContextWindow int `json:"contextWindow"`
|
|
||||||
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"`
|
||||||
@@ -208,19 +290,22 @@ type AgentOverviewSnapshot struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AgentAccountModel struct {
|
type AgentAccountModel struct {
|
||||||
RecordID uint `json:"recordId"`
|
RecordID uint `json:"recordId"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ContextWindow int `json:"contextWindow"`
|
|
||||||
MaxTokens int `json:"maxTokens"`
|
|
||||||
Reasoning bool `json:"reasoning"`
|
|
||||||
Input []string `json:"input"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentAccountModelReq struct {
|
type AgentAccountModelReq struct {
|
||||||
AccountID uint `json:"accountId" validate:"required"`
|
AccountID uint `json:"accountId" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentAccountModelDiscoverReq struct {
|
||||||
|
Provider string `json:"provider" validate:"required"`
|
||||||
|
BaseURL string `json:"baseURL" validate:"required"`
|
||||||
|
APIKey string `json:"apiKey" validate:"required"`
|
||||||
|
APIType string `json:"apiType" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type AgentAccountModelCreateReq struct {
|
type AgentAccountModelCreateReq struct {
|
||||||
AccountID uint `json:"accountId" validate:"required"`
|
AccountID uint `json:"accountId" validate:"required"`
|
||||||
Model AgentAccountModel `json:"model" validate:"required"`
|
Model AgentAccountModel `json:"model" validate:"required"`
|
||||||
@@ -244,6 +329,8 @@ type AgentAccountCreateReq struct {
|
|||||||
BaseURL string `json:"baseURL"`
|
BaseURL string `json:"baseURL"`
|
||||||
Models []AgentAccountModel `json:"models"`
|
Models []AgentAccountModel `json:"models"`
|
||||||
APIType string `json:"apiType" validate:"required"`
|
APIType string `json:"apiType" validate:"required"`
|
||||||
|
AuthMode string `json:"authMode"`
|
||||||
|
VerifyModel string `json:"verifyModel"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +341,8 @@ type AgentAccountUpdateReq struct {
|
|||||||
RememberAPIKey bool `json:"rememberApiKey"`
|
RememberAPIKey bool `json:"rememberApiKey"`
|
||||||
BaseURL string `json:"baseURL"`
|
BaseURL string `json:"baseURL"`
|
||||||
APIType string `json:"apiType" validate:"required"`
|
APIType string `json:"apiType" validate:"required"`
|
||||||
|
AuthMode string `json:"authMode"`
|
||||||
|
VerifyModel string `json:"verifyModel"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
SyncAgents bool `json:"syncAgents"`
|
SyncAgents bool `json:"syncAgents"`
|
||||||
}
|
}
|
||||||
@@ -262,6 +351,9 @@ type AgentAccountVerifyReq struct {
|
|||||||
Provider string `json:"provider" validate:"required"`
|
Provider string `json:"provider" validate:"required"`
|
||||||
APIKey string `json:"apiKey" validate:"required"`
|
APIKey string `json:"apiKey" validate:"required"`
|
||||||
BaseURL string `json:"baseURL"`
|
BaseURL string `json:"baseURL"`
|
||||||
|
APIType string `json:"apiType" validate:"required"`
|
||||||
|
AuthMode string `json:"authMode"`
|
||||||
|
Model string `json:"model"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentAccountDeleteReq struct {
|
type AgentAccountDeleteReq struct {
|
||||||
@@ -274,36 +366,49 @@ 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"`
|
AuthMode string `json:"authMode"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
VerifyModel string `json:"verifyModel"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProviderModelInfo struct {
|
type ProviderModelInfo struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ContextWindow int `json:"contextWindow"`
|
}
|
||||||
MaxTokens int `json:"maxTokens"`
|
|
||||||
Reasoning bool `json:"reasoning"`
|
type ProviderAPIInfo struct {
|
||||||
Input []string `json:"input"`
|
APIType string `json:"apiType"`
|
||||||
|
BaseURL string `json:"baseUrl"`
|
||||||
|
EditableBaseURL bool `json:"editableBaseUrl"`
|
||||||
|
DefaultAuthMode string `json:"defaultAuthMode"`
|
||||||
|
AuthModes []string `json:"authModes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProviderInfo struct {
|
type ProviderInfo struct {
|
||||||
Sort uint `json:"-"`
|
Sort uint `json:"-"`
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
DisplayName string `json:"displayName"`
|
DisplayName string `json:"displayName"`
|
||||||
BaseURL string `json:"baseUrl"`
|
BaseURL string `json:"baseUrl"`
|
||||||
Models []ProviderModelInfo `json:"models"`
|
DefaultAPIType string `json:"defaultApiType"`
|
||||||
|
APITypes []ProviderAPIInfo `json:"apiTypes"`
|
||||||
|
Models []ProviderModelInfo `json:"models"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentFeishuConfigReq struct {
|
type AgentFeishuConfigReq struct {
|
||||||
@@ -324,11 +429,6 @@ type AgentFeishuConfigUpdateReq struct {
|
|||||||
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"`
|
||||||
@@ -577,16 +677,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 {
|
||||||
@@ -604,7 +708,7 @@ 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 official skills-sh"`
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,7 +745,7 @@ 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 official skills-sh"`
|
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"`
|
||||||
}
|
}
|
||||||
|
|||||||
+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 {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package dto
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@@ -132,6 +133,8 @@ 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"`
|
||||||
|
Lo string `json:"lo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppForm struct {
|
type AppForm struct {
|
||||||
|
|||||||
@@ -65,14 +65,15 @@ type UploadForRecover struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CommonBackup struct {
|
type CommonBackup struct {
|
||||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mongodb 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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,10 +134,15 @@ type ExtraHost struct {
|
|||||||
IP string `json:"ip"`
|
IP string `json:"ip"`
|
||||||
}
|
}
|
||||||
type ContainerNetwork struct {
|
type ContainerNetwork struct {
|
||||||
Network string `json:"network"`
|
Network string `json:"network"`
|
||||||
Ipv4 string `json:"ipv4"`
|
Ipv4 string `json:"ipv4"`
|
||||||
Ipv6 string `json:"ipv6"`
|
Ipv6 string `json:"ipv6"`
|
||||||
MacAddr string `json:"macAddr"`
|
MacAddr string `json:"macAddr"`
|
||||||
|
Links []string `json:"links"`
|
||||||
|
Aliases []string `json:"aliases"`
|
||||||
|
DriverOpts map[string]string `json:"driverOpts"`
|
||||||
|
GwPriority int `json:"gwPriority"`
|
||||||
|
LinkLocalIPs []string `json:"linkLocalIPs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerCreateByCommand struct {
|
type ContainerCreateByCommand struct {
|
||||||
@@ -319,7 +324,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"`
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-10
@@ -60,25 +60,84 @@ type MysqlDBCreate struct {
|
|||||||
Database string `json:"database" validate:"required"`
|
Database string `json:"database" validate:"required"`
|
||||||
Format string `json:"format" validate:"required"`
|
Format string `json:"format" validate:"required"`
|
||||||
Collation string `json:"collation"`
|
Collation string `json:"collation"`
|
||||||
Username string `json:"username" validate:"required"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password" validate:"required"`
|
Password string `json:"password"`
|
||||||
Permission string `json:"permission" validate:"required"`
|
Permission string `json:"permission" validate:"required"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MysqlUser struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsDelete bool `json:"isDelete"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlGrant struct {
|
||||||
|
Database string `json:"database"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlGrantSummarySearch struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
DBs []string `json:"dbs" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlUserSearch struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlUserCreate struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Password string `json:"password" validate:"required"`
|
||||||
|
Host string `json:"host" validate:"required"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
DBs []string `json:"dbs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlUserDelete struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Host string `json:"host" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlUserUpdate struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Host string `json:"host" validate:"required"`
|
||||||
|
NewHost string `json:"newHost" validate:"required"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlUserPassword struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Host string `json:"host" validate:"required"`
|
||||||
|
Password string `json:"password" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlGrantCreate struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
DB string `json:"db" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Host string `json:"host" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlGrantDelete struct {
|
||||||
|
Database string `json:"database" validate:"required"`
|
||||||
|
DB string `json:"db" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Host string `json:"host" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type MysqlFormatCollationOption struct {
|
type MysqlFormatCollationOption struct {
|
||||||
Format string `json:"format"`
|
Format string `json:"format"`
|
||||||
Collations []string `json:"collations"`
|
Collations []string `json:"collations"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BindUser struct {
|
|
||||||
Database string `json:"database" validate:"required"`
|
|
||||||
DB string `json:"db" validate:"required"`
|
|
||||||
Username string `json:"username" validate:"required"`
|
|
||||||
Password string `json:"password" validate:"required"`
|
|
||||||
Permission string `json:"permission" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MysqlLoadDB struct {
|
type MysqlLoadDB struct {
|
||||||
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 mysql-cluster"`
|
Type string `json:"type" validate:"required,oneof=mysql mariadb mysql-cluster"`
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package dto
|
package dto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,3 +16,37 @@ type SearchTaskLogReq struct {
|
|||||||
type TaskDTO struct {
|
type TaskDTO struct {
|
||||||
model.Task
|
model.Task
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SystemLogReq struct {
|
||||||
|
PageSize int `json:"pageSize" validate:"omitempty,min=1,max=500"`
|
||||||
|
Cursor string `json:"cursor"`
|
||||||
|
StartTime time.Time `json:"startTime"`
|
||||||
|
EndTime time.Time `json:"endTime"`
|
||||||
|
Keyword string `json:"keyword"`
|
||||||
|
Priority string `json:"priority"`
|
||||||
|
Service string `json:"service"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemLogRes struct {
|
||||||
|
Source string `json:"source"`
|
||||||
|
Items []SystemLogItem `json:"items"`
|
||||||
|
HasMore bool `json:"hasMore"`
|
||||||
|
NextCursor string `json:"nextCursor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemLogStatus struct {
|
||||||
|
Source string `json:"source"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
KeywordFilterSupported bool `json:"keywordFilterSupported"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemLogItem struct {
|
||||||
|
Timestamp int64 `json:"-"`
|
||||||
|
Cursor string `json:"-"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
Priority string `json:"priority"`
|
||||||
|
Service string `json:"service"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Raw string `json:"raw"`
|
||||||
|
}
|
||||||
|
|||||||
+39
-5
@@ -1,6 +1,8 @@
|
|||||||
package dto
|
package dto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
|
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
|
||||||
)
|
)
|
||||||
@@ -87,9 +89,41 @@ var LBAlgorithms = map[string]struct{}{"ip_hash": {}, "least_conn": {}}
|
|||||||
var RealIPKeys = map[string]struct{}{"X-Forwarded-For": {}, "X-Real-IP": {}, "CF-Connecting-IP": {}}
|
var RealIPKeys = map[string]struct{}{"X-Forwarded-For": {}, "X-Real-IP": {}, "CF-Connecting-IP": {}}
|
||||||
|
|
||||||
type NginxModule struct {
|
type NginxModule struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Script string `json:"script"`
|
Custom bool `json:"custom,omitempty"`
|
||||||
Packages []string `json:"packages"`
|
Script string `json:"script"`
|
||||||
Params string `json:"params"`
|
Packages []string `json:"packages"`
|
||||||
Enable bool `json:"enable"`
|
Params string `json:"params"`
|
||||||
|
Enable bool `json:"enable"`
|
||||||
|
BuildMode string `json:"buildMode,omitempty"`
|
||||||
|
Provider string `json:"provider,omitempty"`
|
||||||
|
LoadOrder int `json:"loadOrder,omitempty"`
|
||||||
|
Builds []NginxModuleBuild `json:"builds,omitempty"`
|
||||||
|
LastError string `json:"lastError,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NginxModuleBuild struct {
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
BuildMode string `json:"buildMode"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Target NginxModuleTarget `json:"target"`
|
||||||
|
Artifacts []NginxModuleArtifact `json:"artifacts,omitempty"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
BuiltAt time.Time `json:"builtAt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NginxModuleTarget struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
OpenRestyVersion string `json:"openrestyVersion"`
|
||||||
|
Architecture string `json:"architecture"`
|
||||||
|
Image string `json:"image,omitempty"`
|
||||||
|
ImageDigest string `json:"imageDigest,omitempty"`
|
||||||
|
BuilderDigest string `json:"builderDigest,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NginxModuleArtifact struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Checksum string `json:"checksum"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,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 {
|
||||||
@@ -116,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 {
|
||||||
@@ -157,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"`
|
||||||
|
|||||||
@@ -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,10 @@ 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"`
|
||||||
|
GatewayArgs string `json:"gatewayArgs" validate:"max=4096"`
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type McpServerUpdate struct {
|
type McpServerUpdate struct {
|
||||||
@@ -32,11 +36,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"`
|
||||||
|
|||||||
@@ -114,17 +114,22 @@ type NginxRedirectUpdate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NginxBuildReq struct {
|
type NginxBuildReq struct {
|
||||||
TaskID string `json:"taskID" validate:"required"`
|
TaskID string `json:"taskID" validate:"required"`
|
||||||
Mirror string `json:"mirror" validate:"required"`
|
Mirror string `json:"mirror" validate:"required"`
|
||||||
|
Modules []string `json:"modules"`
|
||||||
|
Force bool `json:"force"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NginxModuleUpdate struct {
|
type NginxModuleUpdate struct {
|
||||||
Operate string `json:"operate" validate:"required,oneof=create delete update"`
|
Operate string `json:"operate" validate:"required,oneof=create delete update"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Script string `json:"script"`
|
Script string `json:"script"`
|
||||||
Packages string `json:"packages"`
|
Packages string `json:"packages"`
|
||||||
Enable bool `json:"enable"`
|
Enable bool `json:"enable"`
|
||||||
Params string `json:"params"`
|
Params string `json:"params"`
|
||||||
|
BuildMode string `json:"buildMode" validate:"omitempty,oneof=dynamic static"`
|
||||||
|
Provider string `json:"provider" validate:"omitempty,oneof=local prebuilt"`
|
||||||
|
LoadOrder int `json:"loadOrder" validate:"omitempty,min=0,max=9999"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NginxOperateReq struct {
|
type NginxOperateReq struct {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,16 +69,25 @@ type NginxProxyCache struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NginxModule struct {
|
type NginxModule struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Script string `json:"script"`
|
Custom bool `json:"custom"`
|
||||||
Packages string `json:"packages"`
|
Script string `json:"script"`
|
||||||
Params string `json:"params"`
|
Packages string `json:"packages"`
|
||||||
Enable bool `json:"enable"`
|
Params string `json:"params"`
|
||||||
|
Enable bool `json:"enable"`
|
||||||
|
BuildMode string `json:"buildMode"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
LoadOrder int `json:"loadOrder"`
|
||||||
|
BuildStatus string `json:"buildStatus"`
|
||||||
|
LoadStatus string `json:"loadStatus"`
|
||||||
|
Artifacts []dto.NginxModuleArtifact `json:"artifacts"`
|
||||||
|
LastError string `json:"lastError"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NginxBuildConfig struct {
|
type NginxBuildConfig struct {
|
||||||
Mirror string `json:"mirror"`
|
Mirror string `json:"mirror"`
|
||||||
Modules []NginxModule `json:"modules"`
|
DynamicSupported bool `json:"dynamicSupported"`
|
||||||
|
Modules []NginxModule `json:"modules"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NginxConfigRes struct {
|
type NginxConfigRes struct {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type RuntimeDiagnosticsSummary struct {
|
||||||
|
RSS uint64 `json:"rss"`
|
||||||
|
HeapAlloc uint64 `json:"heapAlloc"`
|
||||||
|
HeapObjects uint64 `json:"heapObjects"`
|
||||||
|
Goroutines int `json:"goroutines"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuntimeGoroutineGroup struct {
|
||||||
|
State string `json:"state"`
|
||||||
|
Top string `json:"top"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
Stack []string `json:"stack"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuntimeGoroutineSnapshot struct {
|
||||||
|
Total int `json:"total"`
|
||||||
|
GroupCount int `json:"groupCount"`
|
||||||
|
Truncated bool `json:"truncated"`
|
||||||
|
CapturedAt time.Time `json:"capturedAt"`
|
||||||
|
Goroutines []RuntimeGoroutineGroup `json:"goroutines"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuntimeProfileCreate struct {
|
||||||
|
Type string `json:"type" validate:"required,oneof=cpu heap goroutine mutex block"`
|
||||||
|
Duration int `json:"duration" validate:"omitempty,min=5,max=30"`
|
||||||
|
}
|
||||||
@@ -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,17 @@ 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"`
|
AuthMode string `json:"authMode"`
|
||||||
Verified bool `json:"verified"`
|
VerifyModel string `json:"verifyModel"`
|
||||||
Remark string `json:"remark"`
|
RememberAPIKey bool `json:"rememberApiKey"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
MasterAccountID uint `json:"masterAccountId" gorm:"index"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (AgentAccount) TableName() string {
|
func (AgentAccount) TableName() string {
|
||||||
|
|||||||
@@ -2,14 +2,10 @@ package model
|
|||||||
|
|
||||||
type AgentAccountModel struct {
|
type AgentAccountModel struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
AccountID uint `json:"accountId" gorm:"index"`
|
AccountID uint `json:"accountId" gorm:"index"`
|
||||||
Model string `json:"model" gorm:"index"`
|
Model string `json:"model" gorm:"index"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ContextWindow int `json:"contextWindow"`
|
SortOrder int `json:"sortOrder" gorm:"index"`
|
||||||
MaxTokens int `json:"maxTokens"`
|
|
||||||
Reasoning bool `json:"reasoning"`
|
|
||||||
Input string `json:"input" gorm:"type:text"`
|
|
||||||
SortOrder int `json:"sortOrder" gorm:"index"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (AgentAccountModel) TableName() string {
|
func (AgentAccountModel) 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,21 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type DatabaseUser struct {
|
||||||
|
BaseModel
|
||||||
|
Type string `json:"type" gorm:"not null;uniqueIndex:idx_database_user"`
|
||||||
|
Database string `json:"database" gorm:"not null;uniqueIndex:idx_database_user"`
|
||||||
|
Username string `json:"username" gorm:"not null;uniqueIndex:idx_database_user"`
|
||||||
|
Host string `json:"host" gorm:"uniqueIndex:idx_database_user"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsDelete bool `json:"isDelete"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DatabaseUserGrant struct {
|
||||||
|
BaseModel
|
||||||
|
Type string `json:"type" gorm:"not null;uniqueIndex:idx_database_user_grant"`
|
||||||
|
Database string `json:"database" gorm:"not null;uniqueIndex:idx_database_user_grant"`
|
||||||
|
DBName string `json:"dbName" gorm:"not null;uniqueIndex:idx_database_user_grant"`
|
||||||
|
Username string `json:"username" gorm:"not null;uniqueIndex:idx_database_user_grant"`
|
||||||
|
Host string `json:"host" gorm:"not null;uniqueIndex:idx_database_user_grant"`
|
||||||
|
}
|
||||||
@@ -18,4 +18,7 @@ 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"`
|
||||||
|
GatewayArgs string `json:"gatewayArgs"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|||||||
+242
-330
@@ -1,321 +1,181 @@
|
|||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Model struct {
|
type APIConfig struct {
|
||||||
ID string
|
APIType string
|
||||||
Name string
|
BaseURL string
|
||||||
ContextWindow int
|
EditableBaseURL bool
|
||||||
MaxTokens int
|
DefaultAuthMode string
|
||||||
Reasoning bool
|
AuthModes []string
|
||||||
Input []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeDefault struct {
|
const (
|
||||||
APIType string
|
AuthModeBearer = "bearer"
|
||||||
ContextWindow int
|
AuthModeXAPIKey = "x-api-key"
|
||||||
MaxTokens int
|
)
|
||||||
Input []string
|
|
||||||
|
type Model struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
Key string
|
Key string
|
||||||
DisplayName string
|
DisplayName string
|
||||||
Sort uint
|
Sort uint
|
||||||
DefaultBaseURL string
|
DefaultAPIType string
|
||||||
|
APIConfigs []APIConfig
|
||||||
EnvKey string
|
EnvKey string
|
||||||
Default RuntimeDefault
|
|
||||||
Models []Model
|
Models []Model
|
||||||
}
|
}
|
||||||
|
|
||||||
var catalog = map[string]Meta{
|
var catalog = map[string]Meta{
|
||||||
"custom": {
|
"custom": {
|
||||||
Key: "custom",
|
Key: "custom", DisplayName: "Custom", Sort: 10, DefaultAPIType: "openai-completions", EnvKey: "CUSTOM_API_KEY",
|
||||||
DisplayName: "Custom",
|
APIConfigs: editableAPIConfigs("openai-completions", "openai-responses", "anthropic-messages"),
|
||||||
Sort: 10,
|
|
||||||
DefaultBaseURL: "",
|
|
||||||
EnvKey: "CUSTOM_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 128000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
|
||||||
Models: []Model{},
|
|
||||||
},
|
},
|
||||||
"ollama": {
|
"ollama": {
|
||||||
Key: "ollama",
|
Key: "ollama", DisplayName: "Ollama", Sort: 15, DefaultAPIType: "openai-responses",
|
||||||
DisplayName: "Ollama",
|
APIConfigs: editableAPIConfigs("openai-responses", "openai-completions"),
|
||||||
Sort: 15,
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-responses",
|
|
||||||
ContextWindow: 160000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"vllm": {
|
"vllm": {
|
||||||
Key: "vllm",
|
Key: "vllm", DisplayName: "vLLM", Sort: 20, DefaultAPIType: "openai-completions", EnvKey: "VLLM_API_KEY",
|
||||||
DisplayName: "vLLM",
|
APIConfigs: editableAPIConfigs("openai-completions", "openai-responses", "anthropic-messages"),
|
||||||
Sort: 20,
|
|
||||||
DefaultBaseURL: "",
|
|
||||||
EnvKey: "VLLM_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 128000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
|
||||||
Models: []Model{},
|
|
||||||
},
|
},
|
||||||
"deepseek": {
|
"deepseek": {
|
||||||
Key: "deepseek",
|
Key: "deepseek", DisplayName: "DeepSeek", Sort: 25, DefaultAPIType: "openai-completions", EnvKey: "DEEPSEEK_API_KEY",
|
||||||
DisplayName: "DeepSeek",
|
APIConfigs: []APIConfig{
|
||||||
Sort: 25,
|
{APIType: "openai-completions", BaseURL: "https://api.deepseek.com/v1"},
|
||||||
DefaultBaseURL: "https://api.deepseek.com/v1",
|
anthropicAPIConfig("https://api.deepseek.com/anthropic", AuthModeXAPIKey),
|
||||||
EnvKey: "DEEPSEEK_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 131072,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "deepseek/deepseek-chat", Name: "DeepSeek Chat"},
|
|
||||||
{ID: "deepseek/deepseek-reasoner", Name: "DeepSeek Reasoner", MaxTokens: 65536, ContextWindow: 131072, Reasoning: true},
|
|
||||||
},
|
},
|
||||||
|
Models: []Model{{ID: "deepseek-v4-flash", Name: "deepseek-v4-flash"}, {ID: "deepseek-v4-pro", Name: "deepseek-v4-pro"}},
|
||||||
},
|
},
|
||||||
"bailian-coding-plan": {
|
"bailian-coding-plan": {
|
||||||
Key: "bailian-coding-plan",
|
Key: "bailian-coding-plan", DisplayName: "阿里云百炼 Coding Plan", Sort: 30, DefaultAPIType: "openai-completions", EnvKey: "QWEN_API_KEY",
|
||||||
DisplayName: "阿里云百炼 Coding Plan",
|
APIConfigs: []APIConfig{
|
||||||
Sort: 30,
|
{APIType: "openai-completions", BaseURL: "https://coding.dashscope.aliyuncs.com/v1"},
|
||||||
DefaultBaseURL: "https://coding.dashscope.aliyuncs.com/v1",
|
anthropicAPIConfig("https://coding.dashscope.aliyuncs.com/apps/anthropic", AuthModeBearer),
|
||||||
EnvKey: "QWEN_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 256000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
},
|
||||||
Models: []Model{
|
Models: []Model{
|
||||||
{ID: "bailian-coding-plan/qwen3.5-plus", Name: "Qwen3.5-Plus", Reasoning: true},
|
{ID: "qwen3-coder-plus", Name: "Qwen3-Coder-Plus"},
|
||||||
{ID: "bailian-coding-plan/qwen3-max", Name: "Qwen3-Max", Reasoning: true},
|
{ID: "qwen3-max-2026-01-23", Name: "Qwen3-Max-2026-01-23"},
|
||||||
{ID: "bailian-coding-plan/qwen3-coder-next", Name: "Qwen3-Coder-Next", Reasoning: true},
|
{ID: "qwen3-coder-next", Name: "Qwen3-Coder-Next"},
|
||||||
{ID: "bailian-coding-plan/qwen3-coder-plus", Name: "Qwen3-Coder-Plus", Reasoning: true},
|
{ID: "glm-4.7", Name: "GLM-4.7"},
|
||||||
{ID: "bailian-coding-plan/minimax-m2.5", Name: "MiniMax M2.5", Reasoning: true},
|
{ID: "kimi-k2.5", Name: "Kimi K2.5"},
|
||||||
{ID: "bailian-coding-plan/glm-5", Name: "GLM-5", Reasoning: true},
|
{ID: "qwen3.5-plus", Name: "Qwen3.5-Plus"},
|
||||||
{ID: "bailian-coding-plan/kimi-k2.5", Name: "Kimi-k2.5", Reasoning: true},
|
{ID: "glm-5", Name: "GLM-5"},
|
||||||
{ID: "bailian-coding-plan/glm-4.7", Name: "GLM-4.7", Reasoning: true},
|
{ID: "MiniMax-M2.5", Name: "MiniMax M2.5"},
|
||||||
|
{ID: "qwen3.6-plus", Name: "Qwen3.6-Plus"},
|
||||||
|
{ID: "qwen3.7-plus", Name: "Qwen3.7-Plus"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ark-coding-plan": {
|
"ark-coding-plan": {
|
||||||
Key: "ark-coding-plan",
|
Key: "ark-coding-plan", DisplayName: "方舟 Coding Plan", Sort: 35, DefaultAPIType: "openai-completions", EnvKey: "ARK_API_KEY",
|
||||||
DisplayName: "方舟 Coding Plan",
|
APIConfigs: []APIConfig{
|
||||||
Sort: 35,
|
{APIType: "openai-completions", BaseURL: "https://ark.cn-beijing.volces.com/api/coding/v3"},
|
||||||
DefaultBaseURL: "https://ark.cn-beijing.volces.com/api/coding/v3",
|
anthropicAPIConfig("https://ark.cn-beijing.volces.com/api/coding", AuthModeBearer),
|
||||||
EnvKey: "ARK_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 256000,
|
|
||||||
MaxTokens: 4096,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
},
|
||||||
Models: []Model{
|
Models: []Model{
|
||||||
{ID: "ark-coding-plan/ark-code-latest", Name: "Ark Coding Plan", ContextWindow: 256000, MaxTokens: 4096},
|
{ID: "ark-code-latest", Name: "Ark Coding Plan"}, {ID: "doubao-seed-code", Name: "Doubao Seed Code"},
|
||||||
{ID: "ark-coding-plan/doubao-seed-code", Name: "Doubao Seed Code", ContextWindow: 256000, MaxTokens: 4096},
|
{ID: "glm-4.7", Name: "GLM 4.7 Coding"}, {ID: "kimi-k2-thinking", Name: "Kimi K2 Thinking"},
|
||||||
{ID: "ark-coding-plan/glm-4.7", Name: "GLM 4.7 Coding", ContextWindow: 200000, MaxTokens: 4096},
|
{ID: "kimi-k2.5", Name: "Kimi K2.5 Coding"}, {ID: "doubao-seed-code-preview-251028", Name: "Doubao Seed Code Preview"},
|
||||||
{ID: "ark-coding-plan/kimi-k2-thinking", Name: "Kimi K2 Thinking", ContextWindow: 256000, MaxTokens: 4096},
|
|
||||||
{ID: "ark-coding-plan/kimi-k2.5", Name: "Kimi K2.5 Coding", ContextWindow: 256000, MaxTokens: 4096},
|
|
||||||
{ID: "ark-coding-plan/doubao-seed-code-preview-251028", Name: "Doubao Seed Code Preview", ContextWindow: 256000, MaxTokens: 4096},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"zai": {
|
"zai": {
|
||||||
Key: "zai",
|
Key: "zai", DisplayName: "Z.ai", Sort: 40, DefaultAPIType: "openai-completions", EnvKey: "ZAI_API_KEY",
|
||||||
DisplayName: "Z.ai",
|
APIConfigs: []APIConfig{{APIType: "openai-completions", BaseURL: "https://open.bigmodel.cn/api/paas/v4", EditableBaseURL: true}},
|
||||||
Sort: 40,
|
Models: []Model{{ID: "glm-5", Name: "GLM-5"}, {ID: "glm-4.7", Name: "GLM-4.7"}, {ID: "glm-4.7-flash", Name: "GLM-4.7-Flash"}, {ID: "glm-4.7-flashx", Name: "GLM-4.7-FlashX"}},
|
||||||
DefaultBaseURL: "https://open.bigmodel.cn/api/paas/v4",
|
|
||||||
EnvKey: "ZAI_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 204800,
|
|
||||||
MaxTokens: 131072,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "zai/glm-5", Name: "GLM-5", Reasoning: true},
|
|
||||||
{ID: "zai/glm-4.7", Name: "GLM-4.7", Reasoning: true},
|
|
||||||
{ID: "zai/glm-4.7-flash", Name: "GLM-4.7-Flash", Reasoning: true},
|
|
||||||
{ID: "zai/glm-4.7-flashx", Name: "GLM-4.7-FlashX", Reasoning: true},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"minimax": {
|
"minimax": {
|
||||||
Key: "minimax",
|
Key: "minimax", DisplayName: "MiniMax (CN)", Sort: 45, DefaultAPIType: "anthropic-messages", EnvKey: "MINIMAX_API_KEY",
|
||||||
DisplayName: "MiniMax (CN)",
|
APIConfigs: []APIConfig{
|
||||||
Sort: 45,
|
anthropicAPIConfig("https://api.minimaxi.com/anthropic", AuthModeXAPIKey, AuthModeBearer),
|
||||||
DefaultBaseURL: "https://api.minimaxi.com/anthropic",
|
{APIType: "openai-completions", BaseURL: "https://api.minimaxi.com/v1"},
|
||||||
EnvKey: "MINIMAX_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "anthropic-messages",
|
|
||||||
ContextWindow: 200000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "minimax/MiniMax-M2.7", Name: "MiniMax M2.7"},
|
|
||||||
{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"},
|
|
||||||
},
|
},
|
||||||
|
Models: []Model{{ID: "MiniMax-M3", Name: "MiniMax M3"}, {ID: "MiniMax-M2.7", Name: "MiniMax M2.7"}, {ID: "MiniMax-M2.7-highspeed", Name: "MiniMax M2.7 highspeed"}},
|
||||||
},
|
},
|
||||||
"xiaomi": {
|
"xiaomi": {
|
||||||
Key: "xiaomi",
|
Key: "xiaomi", DisplayName: "Xiaomi", Sort: 46, DefaultAPIType: "openai-completions", EnvKey: "XIAOMI_API_KEY",
|
||||||
DisplayName: "Xiaomi",
|
APIConfigs: []APIConfig{
|
||||||
Sort: 46,
|
{APIType: "openai-completions", BaseURL: "https://api.xiaomimimo.com/v1"},
|
||||||
DefaultBaseURL: "https://api.xiaomimimo.com/v1",
|
anthropicAPIConfig("https://api.xiaomimimo.com/anthropic", AuthModeBearer),
|
||||||
EnvKey: "XIAOMI_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 262144,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "xiaomi/mimo-v2-flash", Name: "Xiaomi MiMo V2 Flash", ContextWindow: 262144, MaxTokens: 8192, Input: []string{"text"}},
|
|
||||||
{ID: "xiaomi/mimo-v2-pro", Name: "Xiaomi MiMo V2 Pro", ContextWindow: 1048576, MaxTokens: 32000, Reasoning: true, Input: []string{"text"}},
|
|
||||||
{ID: "xiaomi/mimo-v2-omni", Name: "Xiaomi MiMo V2 Omni", ContextWindow: 262144, MaxTokens: 32000, Reasoning: true, Input: []string{"text", "image"}},
|
|
||||||
},
|
},
|
||||||
|
Models: []Model{{ID: "mimo-v2-flash", Name: "Xiaomi MiMo V2 Flash"}, {ID: "mimo-v2-pro", Name: "Xiaomi MiMo V2 Pro"}, {ID: "mimo-v2-omni", Name: "Xiaomi MiMo V2 Omni"}},
|
||||||
},
|
},
|
||||||
"kimi": {
|
"kimi": {
|
||||||
Key: "kimi",
|
Key: "kimi", DisplayName: "Kimi (CN)", Sort: 50, DefaultAPIType: "openai-completions", EnvKey: "KIMI_API_KEY",
|
||||||
DisplayName: "Kimi (CN)",
|
APIConfigs: []APIConfig{{APIType: "openai-completions", BaseURL: "https://api.moonshot.cn/v1"}},
|
||||||
Sort: 50,
|
Models: []Model{{ID: "kimi-k2.5", Name: "Kimi K2.5"}, {ID: "kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"}, {ID: "kimi-k2-thinking", Name: "Kimi K2 Thinking"}},
|
||||||
DefaultBaseURL: "https://api.moonshot.cn/v1",
|
|
||||||
EnvKey: "KIMI_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 256000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text", "image"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "kimi/kimi-k2.5", Name: "Kimi K2.5", Reasoning: true},
|
|
||||||
{ID: "kimi/kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"},
|
|
||||||
{ID: "kimi/kimi-k2-thinking", Name: "Kimi K2 Thinking", Reasoning: true},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"kimi-coding": {
|
"kimi-coding": {
|
||||||
Key: "kimi-coding",
|
Key: "kimi-coding", DisplayName: "Kimi Coding", Sort: 51, DefaultAPIType: "anthropic-messages", EnvKey: "KIMI_API_KEY",
|
||||||
DisplayName: "Kimi Coding",
|
APIConfigs: []APIConfig{anthropicAPIConfig("https://api.kimi.com/coding/", AuthModeXAPIKey)},
|
||||||
Sort: 51,
|
Models: []Model{{ID: "kimi-code", Name: "Kimi Code"}, {ID: "k2p5", Name: "Kimi K2.5"}},
|
||||||
DefaultBaseURL: "https://api.kimi.com/coding/",
|
|
||||||
EnvKey: "KIMI_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "anthropic-messages",
|
|
||||||
ContextWindow: 262144,
|
|
||||||
MaxTokens: 32768,
|
|
||||||
Input: []string{"text", "image"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "kimi-coding/kimi-code", Name: "Kimi Code", ContextWindow: 262144, MaxTokens: 32768, Reasoning: true, Input: []string{"text", "image"}},
|
|
||||||
{ID: "kimi-coding/k2p5", Name: "Kimi K2.5", ContextWindow: 262144, MaxTokens: 32768, Reasoning: true, Input: []string{"text", "image"}},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"openai": {
|
"openai": {
|
||||||
Key: "openai",
|
Key: "openai", DisplayName: "OpenAI", Sort: 55, DefaultAPIType: "openai-responses", EnvKey: "OPENAI_API_KEY",
|
||||||
DisplayName: "OpenAI",
|
APIConfigs: []APIConfig{
|
||||||
Sort: 55,
|
{APIType: "openai-responses", BaseURL: "https://api.openai.com/v1"},
|
||||||
DefaultBaseURL: "https://api.openai.com/v1",
|
{APIType: "openai-completions", BaseURL: "https://api.openai.com/v1"},
|
||||||
EnvKey: "OPENAI_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-responses",
|
|
||||||
ContextWindow: 272000,
|
|
||||||
MaxTokens: 128000,
|
|
||||||
Input: []string{"text", "image"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "openai/gpt-5.4", Name: "gpt-5.4", ContextWindow: 272000, MaxTokens: 128000, Reasoning: true, Input: []string{"text", "image"}},
|
|
||||||
{ID: "openai/gpt-5.4-pro", Name: "gpt-5.4-pro", ContextWindow: 1050000, MaxTokens: 128000, Reasoning: true, Input: []string{"text", "image"}},
|
|
||||||
{ID: "openai/gpt-5.4-mini", Name: "gpt-5.4-mini", ContextWindow: 400000, MaxTokens: 128000, Reasoning: true, Input: []string{"text", "image"}},
|
|
||||||
{ID: "openai/gpt-5.4-nano", Name: "gpt-5.4-nano", ContextWindow: 400000, MaxTokens: 128000, Reasoning: true, Input: []string{"text", "image"}},
|
|
||||||
},
|
},
|
||||||
|
Models: []Model{{ID: "gpt-5.4", Name: "gpt-5.4"}, {ID: "gpt-5.4-pro", Name: "gpt-5.4-pro"}, {ID: "gpt-5.4-mini", Name: "gpt-5.4-mini"}, {ID: "gpt-5.4-nano", Name: "gpt-5.4-nano"}},
|
||||||
},
|
},
|
||||||
"openrouter": {
|
"openrouter": {
|
||||||
Key: "openrouter",
|
Key: "openrouter", DisplayName: "OpenRouter", Sort: 56, DefaultAPIType: "openai-completions", EnvKey: "OPENROUTER_API_KEY",
|
||||||
DisplayName: "OpenRouter",
|
APIConfigs: []APIConfig{{APIType: "openai-completions", BaseURL: "https://openrouter.ai/api/v1"}},
|
||||||
Sort: 56,
|
Models: []Model{{ID: "openrouter/free", Name: "openrouter/free"}, {ID: "openrouter/auto", Name: "openrouter/auto"}},
|
||||||
DefaultBaseURL: "https://openrouter.ai/api/v1",
|
|
||||||
EnvKey: "OPENROUTER_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 128000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "openrouter/free", Name: "openrouter/free"},
|
|
||||||
{ID: "openrouter/auto", Name: "openrouter/auto"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"anthropic": {
|
"anthropic": {
|
||||||
Key: "anthropic",
|
Key: "anthropic", DisplayName: "Anthropic", Sort: 60, DefaultAPIType: "anthropic-messages", EnvKey: "ANTHROPIC_API_KEY",
|
||||||
DisplayName: "Anthropic",
|
APIConfigs: []APIConfig{anthropicAPIConfig("https://api.anthropic.com", AuthModeXAPIKey)},
|
||||||
Sort: 60,
|
Models: []Model{{ID: "claude-sonnet-4-6", Name: "Claude Sonnet 4.6"}, {ID: "claude-opus-4-6", Name: "Claude Opus 4.6"}, {ID: "claude-opus-4-5", Name: "Claude Opus 4.5"}, {ID: "claude-sonnet-4-5", Name: "Claude Sonnet 4.5"}, {ID: "claude-haiku-4-5", Name: "Claude Haiku 4.5"}},
|
||||||
DefaultBaseURL: "https://api.anthropic.com",
|
|
||||||
EnvKey: "ANTHROPIC_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "anthropic-messages",
|
|
||||||
ContextWindow: 256000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text", "image"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "anthropic/claude-sonnet-4-6", Name: "Claude Sonnet 4.6", Reasoning: true},
|
|
||||||
{ID: "anthropic/claude-opus-4-6", Name: "Claude Opus 4.6", Reasoning: true},
|
|
||||||
{ID: "anthropic/claude-opus-4-5", Name: "Claude Opus 4.5"},
|
|
||||||
{ID: "anthropic/claude-sonnet-4-5", Name: "Claude Sonnet 4.5"},
|
|
||||||
{ID: "anthropic/claude-haiku-4-5", Name: "Claude Haiku 4.5"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
Key: "gemini",
|
Key: "gemini", DisplayName: "Gemini", Sort: 65, DefaultAPIType: "gemini-generate-content", EnvKey: "GEMINI_API_KEY",
|
||||||
DisplayName: "Gemini",
|
APIConfigs: []APIConfig{{APIType: "gemini-generate-content", BaseURL: "https://generativelanguage.googleapis.com"}},
|
||||||
Sort: 65,
|
Models: []Model{{ID: "gemini-3-flash-preview", Name: "Gemini 3 Flash Preview"}, {ID: "gemini-flash-latest", Name: "Gemini Flash Latest"}, {ID: "gemini-3-pro-preview", Name: "Gemini 3 Pro Preview"}},
|
||||||
DefaultBaseURL: "https://generativelanguage.googleapis.com",
|
|
||||||
EnvKey: "GEMINI_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 256000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text", "image"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "google/gemini-3-flash-preview", Name: "Gemini 3 Flash Preview", Reasoning: true},
|
|
||||||
{ID: "google/gemini-flash-latest", Name: "Gemini Flash Latest"},
|
|
||||||
{ID: "google/gemini-3-pro-preview", Name: "Gemini 3 Pro Preview", Reasoning: true},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"moonshot": {
|
"moonshot": {
|
||||||
Key: "moonshot",
|
Key: "moonshot", DisplayName: "Moonshot (Global)", Sort: 70, DefaultAPIType: "openai-completions", EnvKey: "MOONSHOT_API_KEY",
|
||||||
DisplayName: "Moonshot (Global)",
|
APIConfigs: []APIConfig{{APIType: "openai-completions", BaseURL: "https://api.moonshot.ai/v1"}},
|
||||||
Sort: 70,
|
Models: []Model{{ID: "kimi-k2.5", Name: "Kimi K2.5"}, {ID: "kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"}, {ID: "kimi-k2-thinking", Name: "Kimi K2 Thinking"}},
|
||||||
DefaultBaseURL: "https://api.moonshot.ai/v1",
|
|
||||||
EnvKey: "MOONSHOT_API_KEY",
|
|
||||||
Default: RuntimeDefault{
|
|
||||||
APIType: "openai-completions",
|
|
||||||
ContextWindow: 256000,
|
|
||||||
MaxTokens: 8192,
|
|
||||||
Input: []string{"text"},
|
|
||||||
},
|
|
||||||
Models: []Model{
|
|
||||||
{ID: "moonshot/kimi-k2.5", Name: "Kimi K2.5", Reasoning: true},
|
|
||||||
{ID: "moonshot/kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"},
|
|
||||||
{ID: "moonshot/kimi-k2-thinking", Name: "Kimi K2 Thinking", Reasoning: true},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func editableAPIConfigs(apiTypes ...string) []APIConfig {
|
||||||
|
configs := make([]APIConfig, 0, len(apiTypes))
|
||||||
|
for _, apiType := range apiTypes {
|
||||||
|
if apiType == "anthropic-messages" {
|
||||||
|
config := anthropicAPIConfig("", AuthModeXAPIKey, AuthModeBearer)
|
||||||
|
config.EditableBaseURL = true
|
||||||
|
configs = append(configs, config)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
configs = append(configs, APIConfig{APIType: apiType, EditableBaseURL: true})
|
||||||
|
}
|
||||||
|
return configs
|
||||||
|
}
|
||||||
|
|
||||||
|
func anthropicAPIConfig(baseURL, defaultAuthMode string, additionalAuthModes ...string) APIConfig {
|
||||||
|
authModes := []string{defaultAuthMode}
|
||||||
|
for _, authMode := range additionalAuthModes {
|
||||||
|
if authMode != defaultAuthMode {
|
||||||
|
authModes = append(authModes, authMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return APIConfig{
|
||||||
|
APIType: "anthropic-messages",
|
||||||
|
BaseURL: baseURL,
|
||||||
|
DefaultAuthMode: defaultAuthMode,
|
||||||
|
AuthModes: authModes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Get(key string) (Meta, bool) {
|
func Get(key string) (Meta, bool) {
|
||||||
meta, ok := catalog[key]
|
meta, ok := catalog[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -332,12 +192,101 @@ func All() map[string]Meta {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultBaseURL(key string) (string, bool) {
|
func FindAPIConfig(key, apiType string) (APIConfig, bool) {
|
||||||
meta, ok := catalog[key]
|
meta, ok := catalog[key]
|
||||||
if !ok || strings.TrimSpace(meta.DefaultBaseURL) == "" {
|
if !ok {
|
||||||
|
return APIConfig{}, false
|
||||||
|
}
|
||||||
|
target := strings.TrimSpace(apiType)
|
||||||
|
if target == "" {
|
||||||
|
target = meta.DefaultAPIType
|
||||||
|
}
|
||||||
|
for _, config := range meta.APIConfigs {
|
||||||
|
if config.APIType == target {
|
||||||
|
return config, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return APIConfig{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAuthMode(provider, apiType, requested string) (string, error) {
|
||||||
|
config, ok := FindAPIConfig(provider, apiType)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("provider %s does not support api type %s", provider, apiType)
|
||||||
|
}
|
||||||
|
if config.APIType != "anthropic-messages" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
authMode := strings.TrimSpace(requested)
|
||||||
|
if authMode == "" {
|
||||||
|
authMode = config.DefaultAuthMode
|
||||||
|
}
|
||||||
|
for _, allowed := range config.AuthModes {
|
||||||
|
if authMode == allowed {
|
||||||
|
return authMode, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("provider %s does not support auth mode %s", provider, authMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultBaseURL(key string) (string, bool) {
|
||||||
|
config, ok := FindAPIConfig(key, "")
|
||||||
|
if !ok || strings.TrimSpace(config.BaseURL) == "" {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
return meta.DefaultBaseURL, true
|
return config.BaseURL, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultAPIType(key string) string {
|
||||||
|
meta, ok := catalog[key]
|
||||||
|
if !ok || strings.TrimSpace(meta.DefaultAPIType) == "" {
|
||||||
|
return "openai-completions"
|
||||||
|
}
|
||||||
|
return meta.DefaultAPIType
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveBaseURL(key, apiType, requested string) (string, error) {
|
||||||
|
config, ok := FindAPIConfig(key, apiType)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("provider %s does not support api type %s", key, apiType)
|
||||||
|
}
|
||||||
|
if !config.EditableBaseURL {
|
||||||
|
return strings.TrimRight(config.BaseURL, "/"), nil
|
||||||
|
}
|
||||||
|
baseURL := strings.TrimSpace(requested)
|
||||||
|
if baseURL == "" {
|
||||||
|
baseURL = config.BaseURL
|
||||||
|
}
|
||||||
|
if baseURL == "" {
|
||||||
|
return "", fmt.Errorf("base url is required")
|
||||||
|
}
|
||||||
|
parsed, err := url.Parse(baseURL)
|
||||||
|
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
|
||||||
|
return "", fmt.Errorf("invalid base url")
|
||||||
|
}
|
||||||
|
parsed.Path = normalizeEndpointPath(config.APIType, parsed.Path)
|
||||||
|
parsed.RawQuery = ""
|
||||||
|
parsed.Fragment = ""
|
||||||
|
return strings.TrimRight(parsed.String(), "/"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeEndpointPath(apiType, value string) string {
|
||||||
|
path := strings.TrimRight(value, "/")
|
||||||
|
suffixes := []string{}
|
||||||
|
switch apiType {
|
||||||
|
case "openai-completions":
|
||||||
|
suffixes = []string{"/chat/completions"}
|
||||||
|
case "openai-responses":
|
||||||
|
suffixes = []string{"/responses"}
|
||||||
|
case "anthropic-messages":
|
||||||
|
suffixes = []string{"/v1/messages", "/messages"}
|
||||||
|
}
|
||||||
|
for _, suffix := range suffixes {
|
||||||
|
if strings.HasSuffix(strings.ToLower(path), suffix) {
|
||||||
|
return path[:len(path)-len(suffix)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnvKey(key string) string {
|
func EnvKey(key string) string {
|
||||||
@@ -350,89 +299,52 @@ func EnvKey(key string) string {
|
|||||||
|
|
||||||
func DisplayName(key string) string {
|
func DisplayName(key string) string {
|
||||||
meta, ok := catalog[key]
|
meta, ok := catalog[key]
|
||||||
if !ok {
|
if !ok || strings.TrimSpace(meta.DisplayName) == "" {
|
||||||
return key
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(meta.DisplayName) == "" {
|
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
return meta.DisplayName
|
return meta.DisplayName
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindModel(key, modelID string) (Model, bool) {
|
func NormalizeModelID(provider, modelID string) string {
|
||||||
meta, ok := Get(key)
|
target := strings.TrimLeft(strings.TrimSpace(modelID), "/")
|
||||||
if !ok {
|
for _, prefix := range legacyModelPrefixes[provider] {
|
||||||
return Model{}, false
|
legacyPrefix := prefix + "/"
|
||||||
}
|
if !strings.HasPrefix(target, legacyPrefix) {
|
||||||
for _, item := range meta.Models {
|
continue
|
||||||
if item.ID == modelID {
|
}
|
||||||
return item, true
|
candidate := strings.TrimSpace(strings.TrimPrefix(target, legacyPrefix))
|
||||||
|
if candidate != "" {
|
||||||
|
return candidate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Model{}, false
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
var legacyModelPrefixes = map[string][]string{
|
||||||
|
"custom": {"custom"},
|
||||||
|
"vllm": {"custom"},
|
||||||
|
"ollama": {"ollama"},
|
||||||
|
"deepseek": {"deepseek"},
|
||||||
|
"bailian-coding-plan": {"bailian-coding-plan"},
|
||||||
|
"ark-coding-plan": {"ark-coding-plan"},
|
||||||
|
"zai": {"zai"},
|
||||||
|
"minimax": {"minimax"},
|
||||||
|
"xiaomi": {"xiaomi"},
|
||||||
|
"kimi": {"kimi", "moonshot"},
|
||||||
|
"kimi-coding": {"kimi-coding"},
|
||||||
|
"openai": {"openai"},
|
||||||
|
"anthropic": {"anthropic"},
|
||||||
|
"gemini": {"google", "gemini"},
|
||||||
|
"moonshot": {"moonshot"},
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneMeta(meta Meta) Meta {
|
func cloneMeta(meta Meta) Meta {
|
||||||
clone := meta
|
clone := meta
|
||||||
if len(meta.Default.Input) > 0 {
|
clone.APIConfigs = make([]APIConfig, len(meta.APIConfigs))
|
||||||
clone.Default.Input = make([]string, len(meta.Default.Input))
|
for index, config := range meta.APIConfigs {
|
||||||
copy(clone.Default.Input, meta.Default.Input)
|
clone.APIConfigs[index] = config
|
||||||
}
|
clone.APIConfigs[index].AuthModes = append([]string(nil), config.AuthModes...)
|
||||||
if len(meta.Models) > 0 {
|
|
||||||
clone.Models = make([]Model, len(meta.Models))
|
|
||||||
for i, item := range meta.Models {
|
|
||||||
clone.Models[i] = normalizeModel(meta, item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
clone.Models = append([]Model(nil), meta.Models...)
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeModel(meta Meta, model Model) Model {
|
|
||||||
clone := model
|
|
||||||
clone.ID = strings.TrimSpace(clone.ID)
|
|
||||||
clone.Name = strings.TrimSpace(clone.Name)
|
|
||||||
if clone.Name == "" {
|
|
||||||
clone.Name = clone.ID
|
|
||||||
}
|
|
||||||
if clone.MaxTokens <= 0 {
|
|
||||||
clone.MaxTokens = meta.Default.MaxTokens
|
|
||||||
}
|
|
||||||
if clone.ContextWindow <= 0 {
|
|
||||||
clone.ContextWindow = meta.Default.ContextWindow
|
|
||||||
}
|
|
||||||
if len(clone.Input) == 0 && len(meta.Default.Input) > 0 {
|
|
||||||
clone.Input = make([]string, len(meta.Default.Input))
|
|
||||||
copy(clone.Input, meta.Default.Input)
|
|
||||||
}
|
|
||||||
return clone
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResolveRuntimeParams(provider, apiType string, maxTokens, contextWindow int) (string, int, int) {
|
|
||||||
defaultAPIType := "openai-completions"
|
|
||||||
defaultMaxTokens := 8192
|
|
||||||
defaultContextWindow := 256000
|
|
||||||
if meta, ok := Get(provider); ok {
|
|
||||||
if meta.Default.APIType != "" {
|
|
||||||
defaultAPIType = meta.Default.APIType
|
|
||||||
}
|
|
||||||
if meta.Default.MaxTokens > 0 {
|
|
||||||
defaultMaxTokens = meta.Default.MaxTokens
|
|
||||||
}
|
|
||||||
if meta.Default.ContextWindow > 0 {
|
|
||||||
defaultContextWindow = meta.Default.ContextWindow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolvedAPI := apiType
|
|
||||||
if strings.TrimSpace(apiType) == "" {
|
|
||||||
resolvedAPI = defaultAPIType
|
|
||||||
}
|
|
||||||
resolvedMaxTokens := defaultMaxTokens
|
|
||||||
resolvedContextWindow := defaultContextWindow
|
|
||||||
if maxTokens > 0 {
|
|
||||||
resolvedMaxTokens = maxTokens
|
|
||||||
}
|
|
||||||
if contextWindow > 0 {
|
|
||||||
resolvedContextWindow = contextWindow
|
|
||||||
}
|
|
||||||
return resolvedAPI, resolvedMaxTokens, resolvedContextWindow
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverModels(baseURL, apiKey string) ([]string, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, buildModelDiscoveryURL(baseURL), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "Bearer "+apiKey)
|
||||||
|
resp, err := (&http.Client{Timeout: defaultVerifyTimeout}).Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
|
return nil, fmt.Errorf("request failed: %s", resp.Status)
|
||||||
|
}
|
||||||
|
var payload struct {
|
||||||
|
Data []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
models := make([]string, 0, len(payload.Data))
|
||||||
|
seen := make(map[string]struct{}, len(payload.Data))
|
||||||
|
for _, item := range payload.Data {
|
||||||
|
id := strings.TrimSpace(item.ID)
|
||||||
|
if id == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[id]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[id] = struct{}{}
|
||||||
|
models = append(models, id)
|
||||||
|
}
|
||||||
|
if len(models) == 0 {
|
||||||
|
return nil, fmt.Errorf("no models found")
|
||||||
|
}
|
||||||
|
return models, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildModelDiscoveryURL(baseURL string) string {
|
||||||
|
base := strings.TrimRight(strings.TrimSpace(baseURL), "/")
|
||||||
|
for _, apiType := range []string{"openai-completions", "openai-responses", "anthropic-messages"} {
|
||||||
|
base = normalizeEndpointPath(apiType, base)
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(base, "/v1/models"):
|
||||||
|
return base
|
||||||
|
case strings.HasSuffix(base, "/v1"):
|
||||||
|
return base + "/models"
|
||||||
|
default:
|
||||||
|
return base + "/v1/models"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,72 +15,49 @@ type OpenClawProviderPatch struct {
|
|||||||
AuthHeader bool
|
AuthHeader bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey string) (*OpenClawProviderPatch, error) {
|
func BuildOpenClawProviderPatch(provider, modelName, apiType, authMode, baseURL, apiKey string) (*OpenClawProviderPatch, error) {
|
||||||
if modelName == "" {
|
if modelName == "" {
|
||||||
return nil, fmt.Errorf("model is required")
|
return nil, fmt.Errorf("model is required")
|
||||||
}
|
}
|
||||||
resolvedAPIType, _, _ := ResolveRuntimeParams(provider, apiType, 0, 0)
|
resolvedAPIType := apiType
|
||||||
modelID := resolveOpenClawModelID(provider, modelName)
|
if _, ok := FindAPIConfig(provider, resolvedAPIType); !ok {
|
||||||
switch provider {
|
resolvedAPIType = DefaultAPIType(provider)
|
||||||
case "deepseek":
|
|
||||||
return newOpenClawProviderPatch(modelName, "deepseek", modelID, apiKey, baseURL, "openai-completions", false), nil
|
|
||||||
case "gemini":
|
|
||||||
return newOpenClawProviderPatch("google/"+modelID, "google", modelID, apiKey, baseURL, resolvedAPIType, false), nil
|
|
||||||
case "moonshot", "kimi":
|
|
||||||
return buildMoonshotProviderPatch(provider, modelName, modelID, baseURL, apiKey), nil
|
|
||||||
case "bailian-coding-plan":
|
|
||||||
return newOpenClawProviderPatch("bailian-coding-plan/"+modelID, "bailian-coding-plan", modelID, apiKey, baseURL, "openai-completions", false), nil
|
|
||||||
case "ark-coding-plan":
|
|
||||||
return newOpenClawProviderPatch("ark-coding-plan/"+modelID, "ark-coding-plan", modelID, apiKey, baseURL, "openai-completions", false), nil
|
|
||||||
case "minimax":
|
|
||||||
return newOpenClawProviderPatch("minimax/"+modelID, "minimax", modelID, apiKey, baseURL, "anthropic-messages", true), nil
|
|
||||||
case "xiaomi":
|
|
||||||
return newOpenClawProviderPatch("xiaomi/"+modelID, "xiaomi", modelID, apiKey, baseURL, "openai-completions", false), nil
|
|
||||||
case "custom", "vllm":
|
|
||||||
return newOpenClawProviderPatch(provider+"/"+modelID, provider, modelID, apiKey, baseURL, resolvedAPIType, false), nil
|
|
||||||
case "ollama":
|
|
||||||
return newOpenClawProviderPatch(modelName, "ollama", modelID, "ollama", baseURL, resolvedAPIType, false), nil
|
|
||||||
case "kimi-coding":
|
|
||||||
return newOpenClawProviderPatch(modelName, "kimi-coding", modelID, apiKey, baseURL, "anthropic-messages", false), nil
|
|
||||||
case "zai":
|
|
||||||
return newOpenClawProviderPatch("zai/"+modelID, "zai", modelID, apiKey, baseURL, "openai-completions", false), nil
|
|
||||||
default:
|
|
||||||
return newOpenClawProviderPatch(modelName, provider, modelID, apiKey, baseURL, resolvedAPIType, false), nil
|
|
||||||
}
|
}
|
||||||
}
|
resolvedAuthMode, err := ResolveAuthMode(provider, resolvedAPIType, authMode)
|
||||||
|
if err != nil {
|
||||||
func buildMoonshotProviderPatch(provider, modelName, modelID, baseURL, apiKey string) *OpenClawProviderPatch {
|
return nil, err
|
||||||
|
}
|
||||||
|
usesBearer := resolvedAuthMode == AuthModeBearer
|
||||||
|
modelID := NormalizeModelID(provider, modelName)
|
||||||
providerKey := provider
|
providerKey := provider
|
||||||
primaryModel := modelName
|
preserveQualifiedModel := false
|
||||||
if provider == "kimi" {
|
switch provider {
|
||||||
|
case "gemini":
|
||||||
|
providerKey = "google"
|
||||||
|
resolvedAPIType = "google-generative-ai"
|
||||||
|
usesBearer = false
|
||||||
|
case "moonshot", "kimi":
|
||||||
providerKey = "moonshot"
|
providerKey = "moonshot"
|
||||||
primaryModel = "moonshot/" + modelID
|
resolvedAPIType = "openai-completions"
|
||||||
|
usesBearer = false
|
||||||
|
case "ollama":
|
||||||
|
apiKey = "ollama"
|
||||||
|
usesBearer = false
|
||||||
|
case "openai", "openrouter", "anthropic":
|
||||||
|
preserveQualifiedModel = strings.Contains(modelName, "/")
|
||||||
}
|
}
|
||||||
return newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseURL, "openai-completions", false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseURL, apiType string, authHeader bool) *OpenClawProviderPatch {
|
primaryModel := providerKey + "/" + modelID
|
||||||
|
if preserveQualifiedModel {
|
||||||
|
primaryModel = modelName
|
||||||
|
}
|
||||||
return &OpenClawProviderPatch{
|
return &OpenClawProviderPatch{
|
||||||
PrimaryModel: primaryModel,
|
PrimaryModel: primaryModel,
|
||||||
ProviderKey: providerKey,
|
ProviderKey: providerKey,
|
||||||
ModelID: modelID,
|
ModelID: modelID,
|
||||||
APIKey: apiKey,
|
APIKey: apiKey,
|
||||||
BaseURL: baseURL,
|
BaseURL: baseURL,
|
||||||
APIType: apiType,
|
APIType: resolvedAPIType,
|
||||||
AuthHeader: authHeader,
|
AuthHeader: usesBearer,
|
||||||
}
|
}, nil
|
||||||
}
|
|
||||||
|
|
||||||
func resolveOpenClawModelID(provider, modelName string) string {
|
|
||||||
if provider == "custom" || provider == "vllm" {
|
|
||||||
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 {
|
|
||||||
return parts[1]
|
|
||||||
}
|
|
||||||
return modelName
|
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-119
@@ -3,7 +3,9 @@ package provider
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -18,160 +20,108 @@ type VerifyRequest struct {
|
|||||||
Body []byte
|
Body []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
type verifyErrorResponse struct {
|
||||||
defaultVerifyTimeout = 30 * time.Second
|
Error struct {
|
||||||
)
|
Message string `json:"message"`
|
||||||
|
} `json:"error"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
func SkipVerification(key string) bool {
|
const defaultVerifyTimeout = 30 * time.Second
|
||||||
switch key {
|
|
||||||
case "custom", "vllm", "ollama", "kimi-coding":
|
func SkipVerification(provider string) bool {
|
||||||
|
switch provider {
|
||||||
|
case "vllm", "ollama", "kimi-coding":
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyAccount(provider, baseURL, apiKey string) error {
|
func VerifyAccount(provider, apiType, authMode, baseURL, apiKey, model string) error {
|
||||||
req := BuildVerifyRequest(provider, baseURL, apiKey)
|
req := BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model)
|
||||||
var body *bytes.Buffer
|
httpReq, err := http.NewRequest(req.Method, req.URL, bytes.NewReader(req.Body))
|
||||||
if len(req.Body) > 0 {
|
|
||||||
body = bytes.NewBuffer(req.Body)
|
|
||||||
} else {
|
|
||||||
body = bytes.NewBuffer(nil)
|
|
||||||
}
|
|
||||||
httpReq, err := http.NewRequest(req.Method, req.URL, body)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for key, value := range req.Headers {
|
for key, value := range req.Headers {
|
||||||
httpReq.Header.Set(key, value)
|
httpReq.Header.Set(key, value)
|
||||||
}
|
}
|
||||||
resp, err := (&http.Client{Timeout: verifyTimeout()}).Do(httpReq)
|
httpReq.Header.Set("Accept", "application/json")
|
||||||
|
resp, err := (&http.Client{Timeout: defaultVerifyTimeout}).Do(httpReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return buserr.WithErr("ErrAgentAccountUnavailable", err)
|
return buserr.WithErr("ErrAgentAccountUnavailable", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode >= 400 {
|
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
|
||||||
return buserr.WithErr("ErrAgentAccountUnavailable", fmt.Errorf("verify failed: %s", resp.Status))
|
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 1024*1024))
|
||||||
|
if readErr != nil {
|
||||||
|
return buserr.WithErr("ErrAgentAccountUnavailable", readErr)
|
||||||
|
}
|
||||||
|
return buserr.WithErr("ErrAgentAccountUnavailable", errors.New(verifyHTTPError(resp.StatusCode, body)))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyTimeout() time.Duration {
|
func BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model string) VerifyRequest {
|
||||||
return defaultVerifyTimeout
|
baseURL = strings.TrimRight(strings.TrimSpace(baseURL), "/")
|
||||||
}
|
headers := map[string]string{"Content-Type": "application/json"}
|
||||||
|
request := VerifyRequest{Method: http.MethodPost, Headers: headers}
|
||||||
|
|
||||||
func BuildVerifyRequest(provider, baseURL, apiKey string) VerifyRequest {
|
if provider == "gemini" {
|
||||||
base := strings.TrimRight(strings.TrimSpace(baseURL), "/")
|
request.URL = baseURL + "/v1beta/models/" + strings.TrimSpace(model) + ":generateContent"
|
||||||
headers := map[string]string{}
|
|
||||||
request := VerifyRequest{Method: http.MethodGet, Headers: headers}
|
|
||||||
|
|
||||||
switch provider {
|
|
||||||
case "anthropic", "kimi-coding":
|
|
||||||
headers["x-api-key"] = apiKey
|
|
||||||
headers["anthropic-version"] = "2023-06-01"
|
|
||||||
if strings.Contains(base, "/v1") {
|
|
||||||
request.URL = base + "/models"
|
|
||||||
} else {
|
|
||||||
request.URL = base + "/v1/models"
|
|
||||||
}
|
|
||||||
case "gemini":
|
|
||||||
request.Method = http.MethodPost
|
|
||||||
if strings.Contains(base, "/v1beta") {
|
|
||||||
request.URL = base + "/models/gemini-3-flash-preview:generateContent"
|
|
||||||
} else {
|
|
||||||
request.URL = base + "/v1beta/models/gemini-3-flash-preview:generateContent"
|
|
||||||
}
|
|
||||||
headers["x-goog-api-key"] = apiKey
|
headers["x-goog-api-key"] = apiKey
|
||||||
headers["Content-Type"] = "application/json"
|
|
||||||
request.Body = mustJSON(map[string]interface{}{
|
request.Body = mustJSON(map[string]interface{}{
|
||||||
"contents": []map[string]interface{}{{
|
"contents": []map[string]interface{}{{"parts": []map[string]string{{"text": "test"}}}},
|
||||||
"parts": []map[string]string{{
|
|
||||||
"text": "Explain how AI works in a few words",
|
|
||||||
}},
|
|
||||||
}},
|
|
||||||
})
|
})
|
||||||
case "zai":
|
return request
|
||||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
}
|
||||||
request.URL = base + "/models"
|
|
||||||
case "bailian-coding-plan":
|
switch apiType {
|
||||||
request.Method = http.MethodPost
|
case "anthropic-messages":
|
||||||
if !strings.Contains(base, "/v1") {
|
request.URL = baseURL + "/v1/messages"
|
||||||
base = base + "/v1"
|
if authMode == AuthModeBearer {
|
||||||
|
headers["Authorization"] = "Bearer " + apiKey
|
||||||
|
} else {
|
||||||
|
headers["x-api-key"] = apiKey
|
||||||
}
|
}
|
||||||
request.URL = base + "/chat/completions"
|
|
||||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
|
||||||
headers["Content-Type"] = "application/json"
|
|
||||||
request.Body = mustJSON(map[string]interface{}{
|
|
||||||
"model": "qwen3.5-plus",
|
|
||||||
"messages": []map[string]string{{"role": "user", "content": "test"}},
|
|
||||||
"max_tokens": 1,
|
|
||||||
})
|
|
||||||
case "ark-coding-plan":
|
|
||||||
request.Method = http.MethodPost
|
|
||||||
if !strings.Contains(base, "/api/coding/v3") {
|
|
||||||
base = "https://ark.cn-beijing.volces.com/api/coding/v3"
|
|
||||||
}
|
|
||||||
request.URL = base + "/chat/completions"
|
|
||||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
|
||||||
headers["Content-Type"] = "application/json"
|
|
||||||
request.Body = mustJSON(map[string]interface{}{
|
|
||||||
"model": "ark-code-latest",
|
|
||||||
"messages": []map[string]string{{"role": "user", "content": "test"}},
|
|
||||||
"max_tokens": 1,
|
|
||||||
})
|
|
||||||
case "minimax":
|
|
||||||
request.Method = http.MethodPost
|
|
||||||
headers["x-api-key"] = apiKey
|
|
||||||
headers["anthropic-version"] = "2023-06-01"
|
headers["anthropic-version"] = "2023-06-01"
|
||||||
headers["Content-Type"] = "application/json"
|
|
||||||
if strings.Contains(base, "/v1") {
|
|
||||||
request.URL = base + "/messages"
|
|
||||||
} else {
|
|
||||||
request.URL = base + "/v1/messages"
|
|
||||||
}
|
|
||||||
request.Body = mustJSON(map[string]interface{}{
|
request.Body = mustJSON(map[string]interface{}{
|
||||||
"model": "MiniMax-M2.5",
|
"model": model, "max_tokens": 1, "stream": false,
|
||||||
"max_tokens": 1,
|
"messages": []map[string]interface{}{{"role": "user", "content": []map[string]string{{"type": "text", "text": "test"}}}},
|
||||||
"messages": []map[string]interface{}{{
|
|
||||||
"role": "user",
|
|
||||||
"content": []map[string]string{{
|
|
||||||
"type": "text",
|
|
||||||
"text": "test",
|
|
||||||
}},
|
|
||||||
}},
|
|
||||||
})
|
})
|
||||||
case "xiaomi":
|
case "openai-responses":
|
||||||
request.Method = http.MethodPost
|
request.URL = baseURL + "/responses"
|
||||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
headers["Authorization"] = "Bearer " + apiKey
|
||||||
headers["Content-Type"] = "application/json"
|
request.Body = mustJSON(map[string]interface{}{"model": model, "input": "test", "max_output_tokens": 1, "stream": false})
|
||||||
if !strings.Contains(base, "/v1") {
|
|
||||||
base = base + "/v1"
|
|
||||||
}
|
|
||||||
request.URL = base + "/chat/completions"
|
|
||||||
request.Body = mustJSON(map[string]interface{}{
|
|
||||||
"model": "mimo-v2-flash",
|
|
||||||
"max_tokens": 1,
|
|
||||||
"messages": []map[string]string{{"role": "user", "content": "test"}},
|
|
||||||
})
|
|
||||||
case "openrouter":
|
|
||||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
|
||||||
if strings.Contains(base, "/v1") {
|
|
||||||
request.URL = base + "/key"
|
|
||||||
} else {
|
|
||||||
request.URL = base + "/v1/key"
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
request.URL = baseURL + "/chat/completions"
|
||||||
if strings.Contains(base, "/v1") {
|
if provider != "ollama" || strings.TrimSpace(apiKey) != "" {
|
||||||
request.URL = base + "/models"
|
headers["Authorization"] = "Bearer " + apiKey
|
||||||
} else {
|
|
||||||
request.URL = base + "/v1/models"
|
|
||||||
}
|
}
|
||||||
|
request.Body = mustJSON(map[string]interface{}{
|
||||||
|
"model": model, "messages": []map[string]string{{"role": "user", "content": "test"}}, "max_tokens": 1, "stream": false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func verifyHTTPError(statusCode int, body []byte) string {
|
||||||
|
message := strings.TrimSpace(string(body))
|
||||||
|
var payload verifyErrorResponse
|
||||||
|
if err := json.Unmarshal(body, &payload); err == nil {
|
||||||
|
if value := strings.TrimSpace(payload.Error.Message); value != "" {
|
||||||
|
message = value
|
||||||
|
} else if value := strings.TrimSpace(payload.Message); value != "" {
|
||||||
|
message = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if message == "" {
|
||||||
|
return fmt.Sprintf("validation request returned status %d", statusCode)
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
func mustJSON(value interface{}) []byte {
|
func mustJSON(value interface{}) []byte {
|
||||||
payload, err := json.Marshal(value)
|
payload, err := json.Marshal(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ type IAppInstallResourceRpo interface {
|
|||||||
WithAppInstallId(appInstallId uint) DBOption
|
WithAppInstallId(appInstallId uint) DBOption
|
||||||
WithLinkId(linkId uint) DBOption
|
WithLinkId(linkId uint) DBOption
|
||||||
WithResourceId(resourceId uint) DBOption
|
WithResourceId(resourceId uint) DBOption
|
||||||
|
WithResourceIds(resourceIds []uint) DBOption
|
||||||
GetBy(opts ...DBOption) ([]model.AppInstallResource, error)
|
GetBy(opts ...DBOption) ([]model.AppInstallResource, error)
|
||||||
GetFirst(opts ...DBOption) (model.AppInstallResource, error)
|
GetFirst(opts ...DBOption) (model.AppInstallResource, error)
|
||||||
Create(ctx context.Context, resource *model.AppInstallResource) error
|
Create(ctx context.Context, resource *model.AppInstallResource) error
|
||||||
@@ -44,6 +45,12 @@ func (a AppInstallResourceRpo) WithResourceId(resourceId uint) DBOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AppInstallResourceRpo) WithResourceIds(resourceIds []uint) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("resource_id IN ?", resourceIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a AppInstallResourceRpo) GetBy(opts ...DBOption) ([]model.AppInstallResource, error) {
|
func (a AppInstallResourceRpo) GetBy(opts ...DBOption) ([]model.AppInstallResource, error) {
|
||||||
db := global.DB.Model(&model.AppInstallResource{})
|
db := global.DB.Model(&model.AppInstallResource{})
|
||||||
var resources []model.AppInstallResource
|
var resources []model.AppInstallResource
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
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 DatabaseUserRepo struct{}
|
||||||
|
|
||||||
|
type IDatabaseUserRepo interface {
|
||||||
|
Get(opts ...DBOption) (model.DatabaseUser, error)
|
||||||
|
List(opts ...DBOption) ([]model.DatabaseUser, error)
|
||||||
|
Save(user *model.DatabaseUser) error
|
||||||
|
Delete(opts ...DBOption) error
|
||||||
|
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||||
|
Update(vars map[string]interface{}, opts ...DBOption) error
|
||||||
|
WithByDatabase(database string) DBOption
|
||||||
|
WithByUser(username, host string) DBOption
|
||||||
|
WithByUserList(users [][2]string) DBOption
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIDatabaseUserRepo() IDatabaseUserRepo {
|
||||||
|
return &DatabaseUserRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserRepo) Get(opts ...DBOption) (model.DatabaseUser, error) {
|
||||||
|
var user model.DatabaseUser
|
||||||
|
db := global.DB.Model(&model.DatabaseUser{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
if err := db.First(&user).Error; err != nil {
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
password, err := encrypt.StringDecrypt(user.Password)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("decrypt database user %s password failed, err: %v", user.Username, err)
|
||||||
|
}
|
||||||
|
user.Password = password
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserRepo) List(opts ...DBOption) ([]model.DatabaseUser, error) {
|
||||||
|
var users []model.DatabaseUser
|
||||||
|
db := global.DB.Model(&model.DatabaseUser{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
if err := db.Find(&users).Error; err != nil {
|
||||||
|
return users, err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(users); i++ {
|
||||||
|
password, err := encrypt.StringDecrypt(users[i].Password)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("decrypt database user %s password failed, err: %v", users[i].Username, err)
|
||||||
|
}
|
||||||
|
users[i].Password = password
|
||||||
|
}
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserRepo) Save(user *model.DatabaseUser) error {
|
||||||
|
if len(user.Password) != 0 {
|
||||||
|
password, err := encrypt.StringEncrypt(user.Password)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("encrypt database user %s password failed, err: %v", user.Username, err)
|
||||||
|
}
|
||||||
|
user.Password = password
|
||||||
|
}
|
||||||
|
return global.DB.Save(user).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserRepo) Delete(opts ...DBOption) error {
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return db.Delete(&model.DatabaseUser{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||||
|
return getTx(ctx, opts...).Delete(&model.DatabaseUser{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserRepo) Update(vars map[string]interface{}, opts ...DBOption) error {
|
||||||
|
db := global.DB.Model(&model.DatabaseUser{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return db.Updates(vars).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserRepo) WithByDatabase(database string) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
return g.Where("database = ?", database)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserRepo) WithByUser(username, host string) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
return g.Where("username = ? AND host = ?", username, host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserRepo) WithByUserList(users [][2]string) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
if len(users) == 0 {
|
||||||
|
return g.Where("1 = 0")
|
||||||
|
}
|
||||||
|
values := make([][]interface{}, 0, len(users))
|
||||||
|
for _, user := range users {
|
||||||
|
values = append(values, []interface{}{user[0], user[1]})
|
||||||
|
}
|
||||||
|
return g.Where("(username, host) IN ?", values)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DatabaseUserGrantRepo struct{}
|
||||||
|
|
||||||
|
type IDatabaseUserGrantRepo interface {
|
||||||
|
Get(opts ...DBOption) (model.DatabaseUserGrant, error)
|
||||||
|
List(opts ...DBOption) ([]model.DatabaseUserGrant, error)
|
||||||
|
Save(grant *model.DatabaseUserGrant) error
|
||||||
|
Replace(dbType, database string, grants []model.DatabaseUserGrant) error
|
||||||
|
Delete(opts ...DBOption) error
|
||||||
|
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||||
|
Update(vars map[string]interface{}, opts ...DBOption) error
|
||||||
|
WithByDatabase(database string) DBOption
|
||||||
|
WithByDBName(dbName string) DBOption
|
||||||
|
WithByDBNames(dbNames []string) DBOption
|
||||||
|
WithByUser(username, host string) DBOption
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) Get(opts ...DBOption) (model.DatabaseUserGrant, error) {
|
||||||
|
var grant model.DatabaseUserGrant
|
||||||
|
db := global.DB.Model(&model.DatabaseUserGrant{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
err := db.First(&grant).Error
|
||||||
|
return grant, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIDatabaseUserGrantRepo() IDatabaseUserGrantRepo {
|
||||||
|
return &DatabaseUserGrantRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) List(opts ...DBOption) ([]model.DatabaseUserGrant, error) {
|
||||||
|
var grants []model.DatabaseUserGrant
|
||||||
|
db := global.DB.Model(&model.DatabaseUserGrant{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
err := db.Find(&grants).Error
|
||||||
|
return grants, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) Save(grant *model.DatabaseUserGrant) error {
|
||||||
|
return global.DB.Save(grant).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) Replace(dbType, database string, grants []model.DatabaseUserGrant) error {
|
||||||
|
return global.DB.Transaction(func(tx *gorm.DB) error {
|
||||||
|
if err := tx.Where("`type` = ? AND database = ?", dbType, database).Delete(&model.DatabaseUserGrant{}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(grants) != 0 {
|
||||||
|
return tx.Create(&grants).Error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) Delete(opts ...DBOption) error {
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return db.Delete(&model.DatabaseUserGrant{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||||
|
return getTx(ctx, opts...).Delete(&model.DatabaseUserGrant{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) Update(vars map[string]interface{}, opts ...DBOption) error {
|
||||||
|
db := global.DB.Model(&model.DatabaseUserGrant{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return db.Updates(vars).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) WithByDatabase(database string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("database = ?", database)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) WithByDBName(dbName string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("db_name = ?", dbName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) WithByDBNames(dbNames []string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("db_name IN ?", dbNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *DatabaseUserGrantRepo) WithByUser(username, host string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("username = ? AND host = ?", username, host)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,10 +17,12 @@ 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
|
||||||
WithDBType(dbType string) DBOption
|
WithDBType(dbType string) DBOption
|
||||||
|
WithDBTypes(dbTypes []string) DBOption
|
||||||
WithDBID(dbID uint) DBOption
|
WithDBID(dbID uint) DBOption
|
||||||
|
|
||||||
Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
|
Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
|
||||||
@@ -76,6 +78,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)
|
||||||
@@ -118,6 +133,12 @@ func (w *WebsiteRepo) WithDBType(dbType string) DBOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WebsiteRepo) WithDBTypes(dbTypes []string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("db_type IN ?", dbTypes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WebsiteRepo) WithDBID(dbID uint) DBOption {
|
func (w *WebsiteRepo) WithDBID(dbID uint) DBOption {
|
||||||
return func(db *gorm.DB) *gorm.DB {
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
return db.Where("db_id = ?", dbID)
|
return db.Where("db_id = ?", dbID)
|
||||||
|
|||||||
@@ -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
-106
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -372,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)
|
||||||
@@ -415,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)
|
||||||
@@ -455,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)
|
||||||
@@ -487,7 +487,7 @@ func (a AgentService) LoginWeixinChannel(req dto.AgentWeixinLoginReq) error {
|
|||||||
if agent.AgentType == constant.AppHermesAgent {
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
return mgr.Run("docker", buildHermesWeixinLoginArgs(install.ContainerName)...)
|
return mgr.Run("docker", buildHermesWeixinLoginArgs(install.ContainerName)...)
|
||||||
}
|
}
|
||||||
return mgr.RunBashCf("docker exec %s openclaw channels login --channel openclaw-weixin", install.ContainerName)
|
return mgr.Run("docker", "exec", install.ContainerName, "openclaw", "channels", "login", "--channel", "openclaw-weixin")
|
||||||
}, nil)
|
}, nil)
|
||||||
if agent.AgentType == constant.AppHermesAgent {
|
if agent.AgentType == constant.AppHermesAgent {
|
||||||
loginTask.AddSubTask("Restart Hermes-Agent container", func(t *task.Task) error {
|
loginTask.AddSubTask("Restart Hermes-Agent container", func(t *task.Task) error {
|
||||||
@@ -584,17 +584,26 @@ func (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveRe
|
|||||||
return validateHermesPairingApproveResult(output, err)
|
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,
|
||||||
)
|
)
|
||||||
@@ -802,9 +811,7 @@ func extractTelegramConfig(conf map[string]interface{}) dto.AgentTelegramConfig
|
|||||||
result.RequireMention = result.GroupPolicy == "allowlist"
|
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"])
|
||||||
@@ -830,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))
|
||||||
@@ -870,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{}{
|
||||||
@@ -879,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{"*"}
|
||||||
@@ -890,6 +897,29 @@ 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", AllowFrom: []string{}, RequireMention: false, GroupPolicy: "open"}
|
result := dto.AgentDiscordConfig{Enabled: true, DmPolicy: "pairing", AllowFrom: []string{}, RequireMention: false, GroupPolicy: "open"}
|
||||||
discord := getChannelConfig(conf, "discord")
|
discord := getChannelConfig(conf, "discord")
|
||||||
@@ -1289,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) {
|
||||||
@@ -1357,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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,19 @@ 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/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/joho/godotenv"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const hermesWorkspaceDir = "/opt/data/workspace"
|
const hermesWorkspaceDir = "/opt/data/workspace"
|
||||||
const hermesExecutablePath = "/opt/hermes/.venv/bin/hermes"
|
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 {
|
type hermesConfig struct {
|
||||||
Model hermesModelConfig `yaml:"model"`
|
Model hermesModelConfig `yaml:"model"`
|
||||||
@@ -71,17 +76,21 @@ func writeHermesConfig(confDir string, account *model.AgentAccount, modelName st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := resolveHermesProvider(account.Provider)
|
provider := resolveHermesProvider(account.Provider, account.APIType)
|
||||||
configPath := path.Join(confDir, "config.yaml")
|
configPath := path.Join(confDir, "config.yaml")
|
||||||
cfg, err := readHermesConfigMap(configPath)
|
cfg, err := readHermesConfigMap(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfg["model"] = map[string]interface{}{
|
model := map[string]interface{}{
|
||||||
"default": resolveHermesModel(account.Provider, provider, modelName),
|
"default": resolveHermesModel(account.Provider, provider, modelName),
|
||||||
"provider": provider,
|
"provider": provider,
|
||||||
"base_url": account.BaseURL,
|
"base_url": account.BaseURL,
|
||||||
}
|
}
|
||||||
|
if provider == "custom" && account.APIKey != "" {
|
||||||
|
model["api_key"] = "${CUSTOM_API_KEY}"
|
||||||
|
}
|
||||||
|
cfg["model"] = model
|
||||||
cfg["terminal"] = map[string]interface{}{
|
cfg["terminal"] = map[string]interface{}{
|
||||||
"backend": "local",
|
"backend": "local",
|
||||||
"cwd": hermesWorkspaceDir,
|
"cwd": hermesWorkspaceDir,
|
||||||
@@ -94,7 +103,7 @@ func writeHermesConfig(confDir string, account *model.AgentAccount, modelName st
|
|||||||
if err := writeHermesConfigMap(configPath, cfg); err != nil {
|
if err := writeHermesConfigMap(configPath, cfg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return writeHermesEnv(path.Join(confDir, ".env"), resolveHermesEnvEntries(account))
|
return writeHermesModelEnv(path.Join(confDir, ".env"), account)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareHermesInstallFiles(appInstall *model.AppInstall, account *model.AgentAccount, modelName string) error {
|
func prepareHermesInstallFiles(appInstall *model.AppInstall, account *model.AgentAccount, modelName string) error {
|
||||||
@@ -108,6 +117,52 @@ func prepareHermesInstallFiles(appInstall *model.AppInstall, account *model.Agen
|
|||||||
return files.NewFileOp().ChownR(dataDir, "1000", "1000", true)
|
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) {
|
func readHermesConfig(configPath string) (*hermesConfig, error) {
|
||||||
content, err := files.NewFileOp().GetContent(configPath)
|
content, err := files.NewFileOp().GetContent(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -148,7 +203,7 @@ func writeHermesConfigMap(configPath string, cfg map[string]interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readHermesTelegramChannelConfig(confDir string) (*dto.AgentTelegramConfig, error) {
|
func readHermesTelegramChannelConfig(confDir string) (*dto.AgentTelegramConfig, error) {
|
||||||
envMap, err := readHermesEnvMap(path.Join(confDir, ".env"))
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -205,7 +260,7 @@ func readHermesTelegramChannelConfig(confDir string) (*dto.AgentTelegramConfig,
|
|||||||
|
|
||||||
func writeHermesTelegramChannelConfig(confDir string, config dto.AgentTelegramConfig) error {
|
func writeHermesTelegramChannelConfig(confDir string, config dto.AgentTelegramConfig) error {
|
||||||
envPath := path.Join(confDir, ".env")
|
envPath := path.Join(confDir, ".env")
|
||||||
envMap, err := readHermesEnvMap(envPath)
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -225,7 +280,7 @@ func writeHermesTelegramChannelConfig(confDir string, config dto.AgentTelegramCo
|
|||||||
envMap["TELEGRAM_ALLOWED_USERS"] = allow
|
envMap["TELEGRAM_ALLOWED_USERS"] = allow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := writeHermesEnvMap(envPath, envMap, []string{
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
"TELEGRAM_BOT_TOKEN",
|
"TELEGRAM_BOT_TOKEN",
|
||||||
"TELEGRAM_ALLOWED_USERS",
|
"TELEGRAM_ALLOWED_USERS",
|
||||||
"TELEGRAM_ALLOW_ALL_USERS",
|
"TELEGRAM_ALLOW_ALL_USERS",
|
||||||
@@ -244,7 +299,7 @@ func writeHermesTelegramChannelConfig(confDir string, config dto.AgentTelegramCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readHermesDiscordChannelConfig(confDir string) (*dto.AgentDiscordConfig, error) {
|
func readHermesDiscordChannelConfig(confDir string) (*dto.AgentDiscordConfig, error) {
|
||||||
envMap, err := readHermesEnvMap(path.Join(confDir, ".env"))
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -296,7 +351,7 @@ func readHermesDiscordChannelConfig(confDir string) (*dto.AgentDiscordConfig, er
|
|||||||
|
|
||||||
func writeHermesDiscordChannelConfig(confDir string, config dto.AgentDiscordConfig) error {
|
func writeHermesDiscordChannelConfig(confDir string, config dto.AgentDiscordConfig) error {
|
||||||
envPath := path.Join(confDir, ".env")
|
envPath := path.Join(confDir, ".env")
|
||||||
envMap, err := readHermesEnvMap(envPath)
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -316,7 +371,7 @@ func writeHermesDiscordChannelConfig(confDir string, config dto.AgentDiscordConf
|
|||||||
envMap["DISCORD_ALLOWED_USERS"] = allow
|
envMap["DISCORD_ALLOWED_USERS"] = allow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := writeHermesEnvMap(envPath, envMap, []string{
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
"DISCORD_BOT_TOKEN",
|
"DISCORD_BOT_TOKEN",
|
||||||
"DISCORD_ALLOWED_USERS",
|
"DISCORD_ALLOWED_USERS",
|
||||||
"DISCORD_ALLOW_ALL_USERS",
|
"DISCORD_ALLOW_ALL_USERS",
|
||||||
@@ -336,14 +391,14 @@ func writeHermesDiscordChannelConfig(confDir string, config dto.AgentDiscordConf
|
|||||||
|
|
||||||
func deleteHermesEnvKeys(confDir string, keys ...string) error {
|
func deleteHermesEnvKeys(confDir string, keys ...string) error {
|
||||||
envPath := path.Join(confDir, ".env")
|
envPath := path.Join(confDir, ".env")
|
||||||
envMap, err := readHermesEnvMap(envPath)
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
delete(envMap, key)
|
delete(envMap, key)
|
||||||
}
|
}
|
||||||
return writeHermesEnvMap(envPath, envMap, keys)
|
return writeAgentEnvMap(envPath, envMap, keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteHermesConfigSections(confDir string, topLevelKeys []string, platformKeys []string) error {
|
func deleteHermesConfigSections(confDir string, topLevelKeys []string, platformKeys []string) error {
|
||||||
@@ -403,11 +458,18 @@ func normalizeHermesTimezone(timezone string) string {
|
|||||||
return timezone
|
return timezone
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveHermesProvider(provider string) string {
|
func resolveHermesProvider(provider, apiType string) string {
|
||||||
|
if apiType == "anthropic-messages" && (provider == "deepseek" || provider == "bailian-coding-plan" || provider == "ark-coding-plan" || provider == "xiaomi") {
|
||||||
|
return "anthropic"
|
||||||
|
}
|
||||||
switch provider {
|
switch provider {
|
||||||
case "":
|
case "":
|
||||||
return "custom"
|
return "custom"
|
||||||
case "openrouter", "anthropic", "gemini", "zai", "kimi-coding", "xiaomi":
|
case "moonshot":
|
||||||
|
return "kimi-coding"
|
||||||
|
case "kimi":
|
||||||
|
return "kimi-coding-cn"
|
||||||
|
case "openrouter", "anthropic", "deepseek", "gemini", "zai", "kimi-coding", "xiaomi":
|
||||||
return provider
|
return provider
|
||||||
case "minimax":
|
case "minimax":
|
||||||
return "minimax-cn"
|
return "minimax-cn"
|
||||||
@@ -424,17 +486,7 @@ func resolveHermesModel(sourceProvider, targetProvider, modelName string) string
|
|||||||
if targetProvider != "custom" {
|
if targetProvider != "custom" {
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
if sourceProvider == "custom" || sourceProvider == "vllm" {
|
return providercatalog.NormalizeModelID(sourceProvider, target)
|
||||||
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) {
|
func resolveHermesConfiguredModelID(account *model.AgentAccount, accountModels []dto.AgentAccountModel, configuredModel string) (string, error) {
|
||||||
@@ -445,7 +497,7 @@ func resolveHermesConfiguredModelID(account *model.AgentAccount, accountModels [
|
|||||||
if configuredModel == "" {
|
if configuredModel == "" {
|
||||||
return "", buserr.New("ErrAgentModelNotInAccount")
|
return "", buserr.New("ErrAgentModelNotInAccount")
|
||||||
}
|
}
|
||||||
provider := resolveHermesProvider(account.Provider)
|
provider := resolveHermesProvider(account.Provider, account.APIType)
|
||||||
for _, item := range accountModels {
|
for _, item := range accountModels {
|
||||||
if resolveHermesModel(account.Provider, provider, item.ID) == configuredModel {
|
if resolveHermesModel(account.Provider, provider, item.ID) == configuredModel {
|
||||||
return item.ID, nil
|
return item.ID, nil
|
||||||
@@ -458,6 +510,15 @@ func resolveHermesEnvEntries(account *model.AgentAccount) []hermesEnvEntry {
|
|||||||
if account == nil {
|
if account == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
resolvedProvider := resolveHermesProvider(account.Provider, account.APIType)
|
||||||
|
if resolvedProvider == "custom" {
|
||||||
|
if account.APIKey == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []hermesEnvEntry{
|
||||||
|
{Key: "CUSTOM_API_KEY", Value: account.APIKey},
|
||||||
|
}
|
||||||
|
}
|
||||||
apiKey := account.APIKey
|
apiKey := account.APIKey
|
||||||
baseURL := account.BaseURL
|
baseURL := account.BaseURL
|
||||||
entries := make([]hermesEnvEntry, 0, 4)
|
entries := make([]hermesEnvEntry, 0, 4)
|
||||||
@@ -468,6 +529,11 @@ func resolveHermesEnvEntries(account *model.AgentAccount) []hermesEnvEntry {
|
|||||||
}
|
}
|
||||||
entries = append(entries, hermesEnvEntry{Key: key, Value: value})
|
entries = append(entries, hermesEnvEntry{Key: key, Value: value})
|
||||||
}
|
}
|
||||||
|
if resolvedProvider == "anthropic" {
|
||||||
|
appendEntry("ANTHROPIC_API_KEY", apiKey)
|
||||||
|
appendEntry("ANTHROPIC_BASE_URL", baseURL)
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
switch account.Provider {
|
switch account.Provider {
|
||||||
case "openrouter":
|
case "openrouter":
|
||||||
@@ -475,6 +541,7 @@ func resolveHermesEnvEntries(account *model.AgentAccount) []hermesEnvEntry {
|
|||||||
appendEntry("OPENROUTER_BASE_URL", baseURL)
|
appendEntry("OPENROUTER_BASE_URL", baseURL)
|
||||||
case "anthropic":
|
case "anthropic":
|
||||||
appendEntry("ANTHROPIC_API_KEY", apiKey)
|
appendEntry("ANTHROPIC_API_KEY", apiKey)
|
||||||
|
appendEntry("ANTHROPIC_BASE_URL", baseURL)
|
||||||
case "gemini":
|
case "gemini":
|
||||||
appendEntry("GOOGLE_API_KEY", apiKey)
|
appendEntry("GOOGLE_API_KEY", apiKey)
|
||||||
appendEntry("GEMINI_API_KEY", apiKey)
|
appendEntry("GEMINI_API_KEY", apiKey)
|
||||||
@@ -519,44 +586,72 @@ func resolveHermesEnvEntries(account *model.AgentAccount) []hermesEnvEntry {
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeHermesEnv(envPath string, entries []hermesEnvEntry) error {
|
func writeHermesModelEnv(envPath string, account *model.AgentAccount) error {
|
||||||
envMap, err := readHermesEnvMap(envPath)
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for _, key := range hermesManagedModelEnvKeys() {
|
||||||
|
delete(envMap, key)
|
||||||
|
}
|
||||||
|
entries := resolveHermesEnvEntries(account)
|
||||||
|
order := make([]string, 0, len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if entry.Key == "" || entry.Value == "" {
|
if entry.Key == "" || entry.Value == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
envMap[entry.Key] = entry.Value
|
envMap[entry.Key] = entry.Value
|
||||||
}
|
|
||||||
order := make([]string, 0, len(entries))
|
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.Key == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
order = append(order, entry.Key)
|
order = append(order, entry.Key)
|
||||||
}
|
}
|
||||||
return writeHermesEnvMap(envPath, envMap, order)
|
return writeAgentEnvMap(envPath, envMap, order)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readHermesEnvMap(envPath string) (map[string]string, error) {
|
func hermesManagedModelEnvKeys() []string {
|
||||||
fileOp := files.NewFileOp()
|
keys := []string{
|
||||||
if !fileOp.Stat(envPath) {
|
"OPENROUTER_API_KEY",
|
||||||
return map[string]string{}, nil
|
"OPENROUTER_BASE_URL",
|
||||||
|
"ANTHROPIC_API_KEY",
|
||||||
|
"ANTHROPIC_BASE_URL",
|
||||||
|
"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",
|
||||||
}
|
}
|
||||||
envMap, err := godotenv.Read(envPath)
|
seen := make(map[string]struct{}, len(keys))
|
||||||
if err != nil {
|
result := make([]string, 0, len(keys))
|
||||||
return nil, err
|
appendKey := func(key string) {
|
||||||
|
if key == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := seen[key]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seen[key] = struct{}{}
|
||||||
|
result = append(result, key)
|
||||||
}
|
}
|
||||||
return envMap, nil
|
for _, key := range keys {
|
||||||
}
|
appendKey(key)
|
||||||
|
|
||||||
func writeHermesEnvMap(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)
|
for _, meta := range providercatalog.All() {
|
||||||
|
appendKey(meta.EnvKey)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitHermesEnvList(value string) []string {
|
func splitHermesEnvList(value string) []string {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ print('Restart the Hermes-Agent container to apply the new Weixin settings.')
|
|||||||
`
|
`
|
||||||
|
|
||||||
func readHermesQQBotChannelConfig(confDir string) (*dto.AgentQQBotConfig, error) {
|
func readHermesQQBotChannelConfig(confDir string) (*dto.AgentQQBotConfig, error) {
|
||||||
envMap, err := readHermesEnvMap(path.Join(confDir, ".env"))
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ func readHermesQQBotChannelConfig(confDir string) (*dto.AgentQQBotConfig, error)
|
|||||||
|
|
||||||
func writeHermesQQBotChannelConfig(confDir string, config dto.AgentQQBotConfig) error {
|
func writeHermesQQBotChannelConfig(confDir string, config dto.AgentQQBotConfig) error {
|
||||||
envPath := path.Join(confDir, ".env")
|
envPath := path.Join(confDir, ".env")
|
||||||
envMap, err := readHermesEnvMap(envPath)
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ func writeHermesQQBotChannelConfig(confDir string, config dto.AgentQQBotConfig)
|
|||||||
envMap["QQ_GROUP_ALLOWED_USERS"] = allow
|
envMap["QQ_GROUP_ALLOWED_USERS"] = allow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := writeHermesEnvMap(envPath, envMap, []string{
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
"QQ_APP_ID",
|
"QQ_APP_ID",
|
||||||
"QQ_CLIENT_SECRET",
|
"QQ_CLIENT_SECRET",
|
||||||
"QQ_ALLOW_ALL_USERS",
|
"QQ_ALLOW_ALL_USERS",
|
||||||
@@ -163,7 +163,7 @@ func deleteHermesQQBotChannelConfig(confDir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readHermesWecomChannelConfig(confDir string) (*dto.AgentWecomConfig, error) {
|
func readHermesWecomChannelConfig(confDir string) (*dto.AgentWecomConfig, error) {
|
||||||
envMap, err := readHermesEnvMap(path.Join(confDir, ".env"))
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ func readHermesWecomChannelConfig(confDir string) (*dto.AgentWecomConfig, error)
|
|||||||
|
|
||||||
func writeHermesWecomChannelConfig(confDir string, config dto.AgentWecomConfig) error {
|
func writeHermesWecomChannelConfig(confDir string, config dto.AgentWecomConfig) error {
|
||||||
envPath := path.Join(confDir, ".env")
|
envPath := path.Join(confDir, ".env")
|
||||||
envMap, err := readHermesEnvMap(envPath)
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -229,7 +229,7 @@ func writeHermesWecomChannelConfig(confDir string, config dto.AgentWecomConfig)
|
|||||||
envMap["WECOM_GROUP_ALLOWED_USERS"] = allow
|
envMap["WECOM_GROUP_ALLOWED_USERS"] = allow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := writeHermesEnvMap(envPath, envMap, []string{
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
"WECOM_BOT_ID",
|
"WECOM_BOT_ID",
|
||||||
"WECOM_SECRET",
|
"WECOM_SECRET",
|
||||||
"WECOM_DM_POLICY",
|
"WECOM_DM_POLICY",
|
||||||
@@ -282,7 +282,7 @@ func deleteHermesWecomChannelConfig(confDir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readHermesDingTalkChannelConfig(confDir string) (*dto.AgentDingTalkConfig, error) {
|
func readHermesDingTalkChannelConfig(confDir string) (*dto.AgentDingTalkConfig, error) {
|
||||||
envMap, err := readHermesEnvMap(path.Join(confDir, ".env"))
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -332,7 +332,7 @@ func readHermesDingTalkChannelConfig(confDir string) (*dto.AgentDingTalkConfig,
|
|||||||
|
|
||||||
func writeHermesDingTalkChannelConfig(confDir string, config dto.AgentDingTalkConfig) error {
|
func writeHermesDingTalkChannelConfig(confDir string, config dto.AgentDingTalkConfig) error {
|
||||||
envPath := path.Join(confDir, ".env")
|
envPath := path.Join(confDir, ".env")
|
||||||
envMap, err := readHermesEnvMap(envPath)
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -356,7 +356,7 @@ func writeHermesDingTalkChannelConfig(confDir string, config dto.AgentDingTalkCo
|
|||||||
} else if config.DmPolicy == "open" {
|
} else if config.DmPolicy == "open" {
|
||||||
envMap["DINGTALK_ALLOW_ALL_USERS"] = "true"
|
envMap["DINGTALK_ALLOW_ALL_USERS"] = "true"
|
||||||
}
|
}
|
||||||
if err := writeHermesEnvMap(envPath, envMap, []string{
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
"DINGTALK_CLIENT_ID",
|
"DINGTALK_CLIENT_ID",
|
||||||
"DINGTALK_CLIENT_SECRET",
|
"DINGTALK_CLIENT_SECRET",
|
||||||
"DINGTALK_ALLOW_ALL_USERS",
|
"DINGTALK_ALLOW_ALL_USERS",
|
||||||
@@ -398,7 +398,7 @@ func deleteHermesDingTalkChannelConfig(confDir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readHermesFeishuChannelConfig(confDir string) (*dto.AgentFeishuConfig, error) {
|
func readHermesFeishuChannelConfig(confDir string) (*dto.AgentFeishuConfig, error) {
|
||||||
envMap, err := readHermesEnvMap(path.Join(confDir, ".env"))
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -452,7 +452,7 @@ func readHermesFeishuChannelConfig(confDir string) (*dto.AgentFeishuConfig, erro
|
|||||||
|
|
||||||
func writeHermesFeishuChannelConfig(confDir string, config dto.AgentFeishuConfig) error {
|
func writeHermesFeishuChannelConfig(confDir string, config dto.AgentFeishuConfig) error {
|
||||||
envPath := path.Join(confDir, ".env")
|
envPath := path.Join(confDir, ".env")
|
||||||
envMap, err := readHermesEnvMap(envPath)
|
envMap, err := readAgentEnvMap(envPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -488,7 +488,7 @@ func writeHermesFeishuChannelConfig(confDir string, config dto.AgentFeishuConfig
|
|||||||
envMap["FEISHU_ALLOWED_USERS"] = allow
|
envMap["FEISHU_ALLOWED_USERS"] = allow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := writeHermesEnvMap(envPath, envMap, []string{
|
if err := writeAgentEnvMap(envPath, envMap, []string{
|
||||||
"FEISHU_APP_ID",
|
"FEISHU_APP_ID",
|
||||||
"FEISHU_APP_SECRET",
|
"FEISHU_APP_SECRET",
|
||||||
"FEISHU_DOMAIN",
|
"FEISHU_DOMAIN",
|
||||||
@@ -529,7 +529,7 @@ func deleteHermesFeishuChannelConfig(confDir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readHermesWeixinChannelConfig(confDir string) (*dto.AgentWeixinConfig, error) {
|
func readHermesWeixinChannelConfig(confDir string) (*dto.AgentWeixinConfig, error) {
|
||||||
envMap, err := readHermesEnvMap(path.Join(confDir, ".env"))
|
envMap, err := readAgentEnvMap(path.Join(confDir, ".env"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,9 +1,18 @@
|
|||||||
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"
|
||||||
|
|
||||||
@@ -12,10 +21,15 @@ import (
|
|||||||
"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/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"`
|
||||||
@@ -38,8 +52,17 @@ 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) {
|
||||||
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
@@ -55,7 +78,7 @@ func (a AgentService) ListSkills(req dto.AgentIDReq) ([]dto.AgentSkillItem, erro
|
|||||||
if agent.AgentType != constant.AppOpenclaw {
|
if agent.AgentType != constant.AppOpenclaw {
|
||||||
return nil, fmt.Errorf("%s does not support", agent.AgentType)
|
return nil, fmt.Errorf("%s does not support", agent.AgentType)
|
||||||
}
|
}
|
||||||
output, err := runDockerExecWithStdout(5*time.Minute, install.ContainerName, "sh", "-c", "openclaw skills list --json 2>&1")
|
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
|
||||||
}
|
}
|
||||||
@@ -66,6 +89,12 @@ 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) {
|
||||||
|
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)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -115,6 +144,9 @@ func (a AgentService) UpdateSkill(req dto.AgentSkillUpdateReq) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
|
func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
|
||||||
|
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)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -122,6 +154,29 @@ func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
|
|||||||
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
|
||||||
@@ -143,7 +198,7 @@ func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
|
|||||||
}
|
}
|
||||||
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 {
|
||||||
@@ -153,6 +208,513 @@ 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 {
|
func (a AgentService) UninstallSkill(req dto.AgentSkillUninstallReq) error {
|
||||||
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -205,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,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,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,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,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
|
||||||
}
|
}
|
||||||
@@ -307,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
|
||||||
}
|
}
|
||||||
|
|||||||
+183
-274
@@ -4,10 +4,12 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -19,10 +21,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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,6 +39,39 @@ type resolvedAgentAccountInput struct {
|
|||||||
Provider string
|
Provider string
|
||||||
APIKey string
|
APIKey string
|
||||||
BaseURL string
|
BaseURL string
|
||||||
|
APIType string
|
||||||
|
AuthMode string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureAgentAccountNameAvailable(provider, name string, excludeID uint) error {
|
||||||
|
opts := []repo.DBOption{repo.WithByProvider(provider), repo.WithByName(name)}
|
||||||
|
if excludeID > 0 {
|
||||||
|
opts = append(opts, repo.WithByNOTID(excludeID))
|
||||||
|
}
|
||||||
|
account, err := agentAccountRepo.GetFirst(opts...)
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if account != nil && account.ID > 0 {
|
||||||
|
return buserr.New("ErrRecordExist")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func agentAccountUsedBySetting(tx *gorm.DB, accountID uint, statusKey, accountIDKey string) (bool, error) {
|
||||||
|
var settings []model.Setting
|
||||||
|
if err := tx.Where("key IN ?", []string{statusKey, accountIDKey}).Find(&settings).Error; err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
values := make(map[string]string, len(settings))
|
||||||
|
for _, setting := range settings {
|
||||||
|
values[setting.Key] = setting.Value
|
||||||
|
}
|
||||||
|
return strings.EqualFold(strings.TrimSpace(values[statusKey]), constant.StatusEnable) &&
|
||||||
|
strings.TrimSpace(values[accountIDKey]) == strconv.FormatUint(uint64(accountID), 10), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadOpenclawAgentByID(agentID uint) (*model.Agent, error) {
|
func loadOpenclawAgentByID(agentID uint) (*model.Agent, error) {
|
||||||
@@ -60,24 +96,26 @@ func ensureContainerRunning(containerName string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDockerExecWithStdout(timeout time.Duration, containerName string, args ...string) (string, error) {
|
func resolveAgentAccountInput(provider, apiType, authMode, apiKey, baseURL, modelID string) (resolvedAgentAccountInput, error) {
|
||||||
commandArgs := append([]string{"exec", containerName}, args...)
|
|
||||||
return cmd.NewCommandMgr(cmd.WithTimeout(timeout)).RunWithStdout("docker", commandArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveAgentAccountInput(provider, apiKey, baseURL string) (resolvedAgentAccountInput, error) {
|
|
||||||
resolvedAPIKey := strings.TrimSpace(apiKey)
|
resolvedAPIKey := strings.TrimSpace(apiKey)
|
||||||
resolvedBaseURL := strings.TrimSpace(baseURL)
|
resolvedAPIType := strings.TrimSpace(apiType)
|
||||||
if resolvedBaseURL == "" {
|
resolvedAuthMode, err := providercatalog.ResolveAuthMode(provider, resolvedAPIType, authMode)
|
||||||
if requiresInitialAgentAccountModels(provider) {
|
if err != nil {
|
||||||
|
return resolvedAgentAccountInput{}, err
|
||||||
|
}
|
||||||
|
resolvedBaseURL, err := providercatalog.ResolveBaseURL(provider, resolvedAPIType, baseURL)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "base url is required") {
|
||||||
return resolvedAgentAccountInput{}, buserr.New("ErrAgentBaseURLRequired")
|
return resolvedAgentAccountInput{}, buserr.New("ErrAgentBaseURLRequired")
|
||||||
}
|
}
|
||||||
if defaultURL, ok := providercatalog.DefaultBaseURL(provider); ok {
|
return resolvedAgentAccountInput{}, err
|
||||||
resolvedBaseURL = defaultURL
|
}
|
||||||
}
|
modelID = strings.TrimSpace(modelID)
|
||||||
|
if modelID == "" {
|
||||||
|
return resolvedAgentAccountInput{}, buserr.New("ErrAgentAccountModelsRequired")
|
||||||
}
|
}
|
||||||
if !providercatalog.SkipVerification(provider) {
|
if !providercatalog.SkipVerification(provider) {
|
||||||
if err := providercatalog.VerifyAccount(provider, resolvedBaseURL, resolvedAPIKey); err != nil {
|
if err := providercatalog.VerifyAccount(provider, resolvedAPIType, resolvedAuthMode, resolvedBaseURL, resolvedAPIKey, modelID); err != nil {
|
||||||
return resolvedAgentAccountInput{}, err
|
return resolvedAgentAccountInput{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +123,8 @@ func resolveAgentAccountInput(provider, apiKey, baseURL string) (resolvedAgentAc
|
|||||||
Provider: provider,
|
Provider: provider,
|
||||||
APIKey: resolvedAPIKey,
|
APIKey: resolvedAPIKey,
|
||||||
BaseURL: resolvedBaseURL,
|
BaseURL: resolvedBaseURL,
|
||||||
|
APIType: resolvedAPIType,
|
||||||
|
AuthMode: resolvedAuthMode,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,26 +381,24 @@ func setOtherConfig(conf map[string]interface{}, config dto.AgentOtherConfig) {
|
|||||||
func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map[string]interface{}) dto.AgentItem {
|
func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map[string]interface{}) dto.AgentItem {
|
||||||
agentType := agent.AgentType
|
agentType := agent.AgentType
|
||||||
item := dto.AgentItem{
|
item := dto.AgentItem{
|
||||||
ID: agent.ID,
|
ID: agent.ID,
|
||||||
Name: agent.Name,
|
Name: agent.Name,
|
||||||
Remark: agent.Remark,
|
Remark: agent.Remark,
|
||||||
AgentType: agentType,
|
AgentType: agentType,
|
||||||
Provider: agent.Provider,
|
Provider: agent.Provider,
|
||||||
ProviderName: providercatalog.DisplayName(agent.Provider),
|
ProviderName: providercatalog.DisplayName(agent.Provider),
|
||||||
Model: agent.Model,
|
Model: agent.Model,
|
||||||
APIType: agent.APIType,
|
APIType: agent.APIType,
|
||||||
MaxTokens: agent.MaxTokens,
|
BaseURL: agent.BaseURL,
|
||||||
ContextWindow: agent.ContextWindow,
|
APIKey: maskKey(agent.APIKey),
|
||||||
BaseURL: agent.BaseURL,
|
Token: agent.Token,
|
||||||
APIKey: maskKey(agent.APIKey),
|
Status: agent.Status,
|
||||||
Token: agent.Token,
|
Message: agent.Message,
|
||||||
Status: agent.Status,
|
AppInstallID: agent.AppInstallID,
|
||||||
Message: agent.Message,
|
WebsiteID: agent.WebsiteID,
|
||||||
AppInstallID: agent.AppInstallID,
|
AccountID: agent.AccountID,
|
||||||
WebsiteID: agent.WebsiteID,
|
ConfigPath: agent.ConfigPath,
|
||||||
AccountID: agent.AccountID,
|
CreatedAt: agent.CreatedAt,
|
||||||
ConfigPath: agent.ConfigPath,
|
|
||||||
CreatedAt: agent.CreatedAt,
|
|
||||||
}
|
}
|
||||||
if appInstall != nil && appInstall.ID > 0 {
|
if appInstall != nil && appInstall.ID > 0 {
|
||||||
item.Container = appInstall.ContainerName
|
item.Container = appInstall.ContainerName
|
||||||
@@ -382,6 +420,11 @@ 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
|
||||||
}
|
}
|
||||||
@@ -584,6 +627,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
|
||||||
@@ -680,13 +726,9 @@ type modelProvider struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type modelEntry struct {
|
type modelEntry struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Reasoning bool `json:"reasoning"`
|
Input []string `json:"input,omitempty"`
|
||||||
Input []string `json:"input"`
|
|
||||||
ContextWindow int `json:"contextWindow"`
|
|
||||||
MaxTokens int `json:"maxTokens"`
|
|
||||||
Cost modelCost `json:"cost"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func requiresOpenclawProviderModels(provider string) bool {
|
func requiresOpenclawProviderModels(provider string) bool {
|
||||||
@@ -706,13 +748,6 @@ func applyOpenclawModelsConfig(conf map[string]interface{}, models *modelsConfig
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type modelCost struct {
|
|
||||||
Input float64 `json:"input"`
|
|
||||||
Output float64 `json:"output"`
|
|
||||||
CacheRead float64 `json:"cacheRead"`
|
|
||||||
CacheWrite float64 `json:"cacheWrite"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type browserConfig struct {
|
type browserConfig struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
ExecutablePath string `json:"executablePath"`
|
ExecutablePath string `json:"executablePath"`
|
||||||
@@ -863,13 +898,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) {
|
||||||
@@ -1056,7 +1093,7 @@ func buildOpenclawModelsFromAccount(account *model.AgentAccount, selectedModel s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func buildOpenclawAccountModelConfig(account *model.AgentAccount, model dto.AgentAccountModel) (string, modelEntry, string, modelProvider, error) {
|
func buildOpenclawAccountModelConfig(account *model.AgentAccount, model dto.AgentAccountModel) (string, modelEntry, string, modelProvider, error) {
|
||||||
providerPatch, err := providercatalog.BuildOpenClawProviderPatch(account.Provider, model.ID, account.APIType, account.BaseURL, account.APIKey)
|
providerPatch, err := providercatalog.BuildOpenClawProviderPatch(account.Provider, model.ID, account.APIType, account.AuthMode, account.BaseURL, account.APIKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", modelEntry{}, "", modelProvider{}, err
|
return "", modelEntry{}, "", modelProvider{}, err
|
||||||
}
|
}
|
||||||
@@ -1068,28 +1105,24 @@ func buildOpenclawAccountModelConfig(account *model.AgentAccount, model dto.Agen
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var openclawVisionModelPattern = regexp.MustCompile(`(?i)(\b(gpt-4o|gpt-4\.1|gpt-[5-9]|o[134])\b|\bclaude-(3|4|sonnet|opus|haiku)\b|\bgemini\b|\b(qwen[\w.-]*-?vl|qwen-vl|qwen3\.[5-9]-plus)\b|\b(kimi-k2\.(5|6)|kimi-k2\.7-code|minimax-m3)\b|\b(vision|llava|pixtral|internvl|mllama|minicpm-v|glm-4v|omni)\b|(^|[-_/])vl([-_/]|$))`)
|
||||||
|
|
||||||
func buildOpenclawModelEntry(modelID string, model dto.AgentAccountModel) modelEntry {
|
func buildOpenclawModelEntry(modelID string, model dto.AgentAccountModel) modelEntry {
|
||||||
name := strings.TrimSpace(model.Name)
|
name := strings.TrimSpace(model.Name)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = strings.TrimSpace(modelID)
|
name = strings.TrimSpace(modelID)
|
||||||
}
|
}
|
||||||
return modelEntry{
|
entry := modelEntry{ID: strings.TrimSpace(modelID), Name: name}
|
||||||
ID: strings.TrimSpace(modelID),
|
if openclawVisionModelPattern.MatchString(modelID) {
|
||||||
Name: name,
|
entry.Input = []string{"text", "image"}
|
||||||
Reasoning: model.Reasoning,
|
|
||||||
Input: sanitizeAgentAccountModelInputs(model.Input),
|
|
||||||
ContextWindow: model.ContextWindow,
|
|
||||||
MaxTokens: model.MaxTokens,
|
|
||||||
Cost: modelCost{},
|
|
||||||
}
|
}
|
||||||
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
type openclawAccountModelRuntime struct {
|
type openclawAccountModelRuntime struct {
|
||||||
StoredModel string
|
StoredModel string
|
||||||
PrimaryModel string
|
PrimaryModel string
|
||||||
APIType string
|
APIType string
|
||||||
MaxTokens int
|
|
||||||
ContextWindow int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildOpenclawAccountModelRuntime(account *model.AgentAccount, model dto.AgentAccountModel) (openclawAccountModelRuntime, error) {
|
func buildOpenclawAccountModelRuntime(account *model.AgentAccount, model dto.AgentAccountModel) (openclawAccountModelRuntime, error) {
|
||||||
@@ -1098,11 +1131,9 @@ func buildOpenclawAccountModelRuntime(account *model.AgentAccount, model dto.Age
|
|||||||
return openclawAccountModelRuntime{}, err
|
return openclawAccountModelRuntime{}, err
|
||||||
}
|
}
|
||||||
return openclawAccountModelRuntime{
|
return openclawAccountModelRuntime{
|
||||||
StoredModel: model.ID,
|
StoredModel: model.ID,
|
||||||
PrimaryModel: primaryModel,
|
PrimaryModel: primaryModel,
|
||||||
APIType: account.APIType,
|
APIType: account.APIType,
|
||||||
MaxTokens: model.MaxTokens,
|
|
||||||
ContextWindow: model.ContextWindow,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1122,15 +1153,11 @@ func buildInitialAgentAccountModels(account *model.AgentAccount, requested []dto
|
|||||||
if account == nil {
|
if account == nil {
|
||||||
return nil, fmt.Errorf("account is required")
|
return nil, fmt.Errorf("account is required")
|
||||||
}
|
}
|
||||||
if requiresInitialAgentAccountModels(account.Provider) && len(requested) > 1 {
|
if account.Provider != "custom" && requiresInitialAgentAccountModels(account.Provider) && len(requested) > 1 {
|
||||||
return nil, buserr.New("ErrAgentAccountSingleInitialModel")
|
return nil, buserr.New("ErrAgentAccountSingleInitialModel")
|
||||||
}
|
}
|
||||||
if len(requested) > 0 {
|
if len(requested) > 0 {
|
||||||
models := make([]dto.AgentAccountModel, 0, len(requested))
|
return normalizeAgentAccountModels(account, requested)
|
||||||
for _, item := range requested {
|
|
||||||
models = append(models, cloneAgentAccountModel(item))
|
|
||||||
}
|
|
||||||
return models, nil
|
|
||||||
}
|
}
|
||||||
meta, ok := providercatalog.Get(account.Provider)
|
meta, ok := providercatalog.Get(account.Provider)
|
||||||
if !ok || len(meta.Models) == 0 {
|
if !ok || len(meta.Models) == 0 {
|
||||||
@@ -1142,15 +1169,22 @@ func buildInitialAgentAccountModels(account *model.AgentAccount, requested []dto
|
|||||||
requested = make([]dto.AgentAccountModel, 0, len(meta.Models))
|
requested = make([]dto.AgentAccountModel, 0, len(meta.Models))
|
||||||
for _, item := range meta.Models {
|
for _, item := range meta.Models {
|
||||||
requested = append(requested, dto.AgentAccountModel{
|
requested = append(requested, dto.AgentAccountModel{
|
||||||
ID: item.ID,
|
ID: item.ID,
|
||||||
Name: item.Name,
|
Name: item.Name,
|
||||||
ContextWindow: item.ContextWindow,
|
|
||||||
MaxTokens: item.MaxTokens,
|
|
||||||
Reasoning: item.Reasoning,
|
|
||||||
Input: append([]string(nil), item.Input...),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return requested, nil
|
return normalizeAgentAccountModels(account, requested)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildDiscoveredAgentAccountModels(modelIDs []string) []dto.AgentAccountModel {
|
||||||
|
models := make([]dto.AgentAccountModel, 0, len(modelIDs))
|
||||||
|
for _, modelID := range modelIDs {
|
||||||
|
models = append(models, dto.AgentAccountModel{
|
||||||
|
ID: modelID,
|
||||||
|
Name: modelID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return models
|
||||||
}
|
}
|
||||||
|
|
||||||
func compactPersistedAgentAccountModelSortOrder(accountID uint) error {
|
func compactPersistedAgentAccountModelSortOrder(accountID uint) error {
|
||||||
@@ -1178,18 +1212,6 @@ func loadAgentAccountModels(account *model.AgentAccount) ([]dto.AgentAccountMode
|
|||||||
return listPersistedAgentAccountModels(account.ID)
|
return listPersistedAgentAccountModels(account.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneAgentAccountModel(model dto.AgentAccountModel) dto.AgentAccountModel {
|
|
||||||
return dto.AgentAccountModel{
|
|
||||||
RecordID: model.RecordID,
|
|
||||||
ID: model.ID,
|
|
||||||
Name: model.Name,
|
|
||||||
ContextWindow: model.ContextWindow,
|
|
||||||
MaxTokens: model.MaxTokens,
|
|
||||||
Reasoning: model.Reasoning,
|
|
||||||
Input: append([]string(nil), model.Input...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeCatalogAgentAccountModelsForMigration(account *model.AgentAccount, existing []dto.AgentAccountModel) ([]dto.AgentAccountModel, error) {
|
func MergeCatalogAgentAccountModelsForMigration(account *model.AgentAccount, existing []dto.AgentAccountModel) ([]dto.AgentAccountModel, error) {
|
||||||
if account == nil {
|
if account == nil {
|
||||||
return nil, fmt.Errorf("account is required")
|
return nil, fmt.Errorf("account is required")
|
||||||
@@ -1213,12 +1235,8 @@ func MergeCatalogAgentAccountModelsForMigration(account *model.AgentAccount, exi
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
requested = append(requested, dto.AgentAccountModel{
|
requested = append(requested, dto.AgentAccountModel{
|
||||||
ID: item.ID,
|
ID: item.ID,
|
||||||
Name: item.Name,
|
Name: item.Name,
|
||||||
ContextWindow: item.ContextWindow,
|
|
||||||
MaxTokens: item.MaxTokens,
|
|
||||||
Reasoning: item.Reasoning,
|
|
||||||
Input: append([]string(nil), item.Input...),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(requested) == len(existing) {
|
if len(requested) == len(existing) {
|
||||||
@@ -1237,18 +1255,10 @@ func listPersistedAgentAccountModels(accountID uint) ([]dto.AgentAccountModel, e
|
|||||||
}
|
}
|
||||||
result := make([]dto.AgentAccountModel, 0, len(rows))
|
result := make([]dto.AgentAccountModel, 0, len(rows))
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
inputs := []string{}
|
|
||||||
if strings.TrimSpace(row.Input) != "" {
|
|
||||||
_ = json.Unmarshal([]byte(row.Input), &inputs)
|
|
||||||
}
|
|
||||||
result = append(result, dto.AgentAccountModel{
|
result = append(result, dto.AgentAccountModel{
|
||||||
RecordID: row.ID,
|
RecordID: row.ID,
|
||||||
ID: strings.TrimSpace(row.Model),
|
ID: strings.TrimSpace(row.Model),
|
||||||
Name: strings.TrimSpace(row.Name),
|
Name: strings.TrimSpace(row.Name),
|
||||||
ContextWindow: row.ContextWindow,
|
|
||||||
MaxTokens: row.MaxTokens,
|
|
||||||
Reasoning: row.Reasoning,
|
|
||||||
Input: sanitizeAgentAccountModelInputs(inputs),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -1259,19 +1269,11 @@ func replacePersistedAgentAccountModelsWithTx(tx *gorm.DB, accountID uint, model
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for index, item := range models {
|
for index, item := range models {
|
||||||
inputPayload, err := json.Marshal(sanitizeAgentAccountModelInputs(item.Input))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
record := &model.AgentAccountModel{
|
record := &model.AgentAccountModel{
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
Model: strings.TrimSpace(item.ID),
|
Model: strings.TrimSpace(item.ID),
|
||||||
Name: strings.TrimSpace(item.Name),
|
Name: strings.TrimSpace(item.Name),
|
||||||
ContextWindow: item.ContextWindow,
|
SortOrder: index + 1,
|
||||||
MaxTokens: item.MaxTokens,
|
|
||||||
Reasoning: item.Reasoning,
|
|
||||||
Input: string(inputPayload),
|
|
||||||
SortOrder: index + 1,
|
|
||||||
}
|
}
|
||||||
if err := tx.Create(record).Error; err != nil {
|
if err := tx.Create(record).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -1312,86 +1314,17 @@ func normalizeAgentAccountModel(account *model.AgentAccount, model dto.AgentAcco
|
|||||||
if modelID == "" {
|
if modelID == "" {
|
||||||
return dto.AgentAccountModel{}, fmt.Errorf("model is required")
|
return dto.AgentAccountModel{}, fmt.Errorf("model is required")
|
||||||
}
|
}
|
||||||
|
modelID = providercatalog.NormalizeModelID(account.Provider, modelID)
|
||||||
name := strings.TrimSpace(model.Name)
|
name := strings.TrimSpace(model.Name)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = modelID
|
name = modelID
|
||||||
}
|
}
|
||||||
inputs := sanitizeAgentAccountModelInputs(model.Input)
|
|
||||||
return dto.AgentAccountModel{
|
return dto.AgentAccountModel{
|
||||||
ID: normalizeAgentAccountModelID(account.Provider, modelID),
|
ID: modelID,
|
||||||
Name: name,
|
Name: name,
|
||||||
ContextWindow: model.ContextWindow,
|
|
||||||
MaxTokens: model.MaxTokens,
|
|
||||||
Reasoning: model.Reasoning,
|
|
||||||
Input: inputs,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeAgentAccountModelID(provider, requestedID string) string {
|
|
||||||
switch provider {
|
|
||||||
case "custom", "vllm":
|
|
||||||
return normalizeCustomModel(requestedID)
|
|
||||||
case "ollama":
|
|
||||||
target := strings.TrimSpace(requestedID)
|
|
||||||
if strings.HasPrefix(target, "ollama/") {
|
|
||||||
return target
|
|
||||||
}
|
|
||||||
target = strings.TrimLeft(strings.TrimSpace(target), "/")
|
|
||||||
if target == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return "ollama/" + target
|
|
||||||
default:
|
|
||||||
target := strings.TrimSpace(requestedID)
|
|
||||||
if target == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
prefix := poolModelPrefix(provider)
|
|
||||||
if strings.Contains(target, "/") {
|
|
||||||
parts := strings.SplitN(target, "/", 2)
|
|
||||||
targetPrefix := parts[0]
|
|
||||||
targetModel := strings.TrimSpace(parts[1])
|
|
||||||
if targetModel == "" {
|
|
||||||
return strings.TrimSpace(target)
|
|
||||||
}
|
|
||||||
for _, item := range supportedProviderModelPrefixes(provider) {
|
|
||||||
if item == targetPrefix {
|
|
||||||
if prefix != "" {
|
|
||||||
return prefix + "/" + targetModel
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(target)
|
|
||||||
}
|
|
||||||
target = strings.TrimLeft(strings.TrimSpace(target), "/")
|
|
||||||
if prefix == "" {
|
|
||||||
return target
|
|
||||||
}
|
|
||||||
return prefix + "/" + target
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sanitizeAgentAccountModelInputs(values []string) []string {
|
|
||||||
result := make([]string, 0, len(values))
|
|
||||||
seen := make(map[string]struct{}, len(values))
|
|
||||||
for _, value := range values {
|
|
||||||
normalized := value
|
|
||||||
if normalized != "text" && normalized != "image" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := seen[normalized]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[normalized] = struct{}{}
|
|
||||||
result = append(result, normalized)
|
|
||||||
}
|
|
||||||
if len(result) == 0 {
|
|
||||||
return []string{"text"}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func requiresInitialAgentAccountModels(provider string) bool {
|
func requiresInitialAgentAccountModels(provider string) bool {
|
||||||
switch provider {
|
switch provider {
|
||||||
case "custom", "vllm", "ollama":
|
case "custom", "vllm", "ollama":
|
||||||
@@ -1401,36 +1334,14 @@ func requiresInitialAgentAccountModels(provider string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeComparableProviderModelID(provider, modelID string) string {
|
|
||||||
target := strings.TrimSpace(modelID)
|
|
||||||
if target == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if !strings.Contains(target, "/") {
|
|
||||||
return target
|
|
||||||
}
|
|
||||||
parts := strings.SplitN(target, "/", 2)
|
|
||||||
prefix := parts[0]
|
|
||||||
model := strings.TrimSpace(parts[1])
|
|
||||||
if model == "" {
|
|
||||||
return target
|
|
||||||
}
|
|
||||||
for _, item := range supportedProviderModelPrefixes(provider) {
|
|
||||||
if item == prefix {
|
|
||||||
return model
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target
|
|
||||||
}
|
|
||||||
|
|
||||||
func sameProviderModelID(provider, left, right string) bool {
|
func sameProviderModelID(provider, left, right string) bool {
|
||||||
leftTrimmed := strings.TrimSpace(left)
|
leftTrimmed := strings.TrimSpace(left)
|
||||||
rightTrimmed := strings.TrimSpace(right)
|
rightTrimmed := strings.TrimSpace(right)
|
||||||
if leftTrimmed == rightTrimmed {
|
if leftTrimmed == rightTrimmed {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
leftComparable := normalizeComparableProviderModelID(provider, leftTrimmed)
|
leftComparable := providercatalog.NormalizeModelID(provider, leftTrimmed)
|
||||||
rightComparable := normalizeComparableProviderModelID(provider, rightTrimmed)
|
rightComparable := providercatalog.NormalizeModelID(provider, rightTrimmed)
|
||||||
return leftComparable != "" && leftComparable == rightComparable
|
return leftComparable != "" && leftComparable == rightComparable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1451,6 +1362,20 @@ func requireAgentAccountModelForProvider(provider string, models []dto.AgentAcco
|
|||||||
return selectedAccountModel, nil
|
return selectedAccountModel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveAgentAccountVerifyModel(provider, requested string, models []dto.AgentAccountModel) (string, error) {
|
||||||
|
if len(models) == 0 {
|
||||||
|
return "", buserr.New("ErrAgentAccountModelsRequired")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(requested) == "" {
|
||||||
|
return models[0].ID, nil
|
||||||
|
}
|
||||||
|
selected, ok := findAgentAccountModelForProvider(provider, models, requested)
|
||||||
|
if !ok {
|
||||||
|
return "", buserr.New("ErrAgentModelNotInAccount")
|
||||||
|
}
|
||||||
|
return selected.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ensureAccountModelsNotBound(account *model.AgentAccount, models []dto.AgentAccountModel) error {
|
func ensureAccountModelsNotBound(account *model.AgentAccount, models []dto.AgentAccountModel) error {
|
||||||
if account == nil || account.ID == 0 {
|
if account == nil || account.ID == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -1527,6 +1452,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 {
|
||||||
@@ -1554,57 +1514,6 @@ func toInt(value interface{}) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeCustomModel(modelName string) string {
|
|
||||||
trim := strings.TrimSpace(modelName)
|
|
||||||
trim = strings.TrimLeft(trim, "/")
|
|
||||||
if parts := strings.SplitN(trim, "/", 2); len(parts) == 2 {
|
|
||||||
if strings.EqualFold(parts[0], "custom") {
|
|
||||||
return strings.TrimLeft(strings.TrimSpace(parts[1]), "/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return trim
|
|
||||||
}
|
|
||||||
|
|
||||||
func runtimeProviderModelPrefix(provider string) string {
|
|
||||||
switch provider {
|
|
||||||
case "gemini":
|
|
||||||
return "google"
|
|
||||||
case "kimi":
|
|
||||||
return "moonshot"
|
|
||||||
default:
|
|
||||||
return provider
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func poolModelPrefix(provider string) string {
|
|
||||||
meta, ok := providercatalog.Get(provider)
|
|
||||||
if ok && len(meta.Models) > 0 {
|
|
||||||
parts := strings.SplitN(strings.TrimSpace(meta.Models[0].ID), "/", 2)
|
|
||||||
if len(parts) == 2 && strings.TrimSpace(parts[0]) != "" {
|
|
||||||
return parts[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return provider
|
|
||||||
}
|
|
||||||
|
|
||||||
func supportedProviderModelPrefixes(provider string) []string {
|
|
||||||
values := []string{poolModelPrefix(provider), runtimeProviderModelPrefix(provider)}
|
|
||||||
result := make([]string, 0, len(values))
|
|
||||||
seen := make(map[string]struct{}, len(values))
|
|
||||||
for _, value := range values {
|
|
||||||
target := value
|
|
||||||
if target == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := seen[target]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[target] = struct{}{}
|
|
||||||
result = append(result, target)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateToken() string {
|
func generateToken() string {
|
||||||
bytes := make([]byte, 24)
|
bytes := make([]byte, 24)
|
||||||
if _, err := rand.Read(bytes); err != nil {
|
if _, err := rand.Read(bytes); err != nil {
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ func fillAgentWebsiteItems(items []dto.AgentItem, explicitWebsiteMap map[uint]mo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func uniqueDeploymentWebsiteMapByAppInstall(websites []model.Website) map[uint]model.Website {
|
func UniqueDeploymentWebsiteMapByAppInstall(websites []model.Website) map[uint]model.Website {
|
||||||
websiteMap := make(map[uint]model.Website)
|
websiteMap := make(map[uint]model.Website)
|
||||||
duplicateAppInstallIDs := make(map[uint]struct{})
|
duplicateAppInstallIDs := make(map[uint]struct{})
|
||||||
for _, website := range websites {
|
for _, website := range websites {
|
||||||
@@ -182,10 +182,6 @@ func uniqueDeploymentWebsiteMapByAppInstall(websites []model.Website) map[uint]m
|
|||||||
return websiteMap
|
return websiteMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func UniqueDeploymentWebsiteMapForMigration(websites []model.Website) map[uint]model.Website {
|
|
||||||
return uniqueDeploymentWebsiteMapByAppInstall(websites)
|
|
||||||
}
|
|
||||||
|
|
||||||
func uniqueUintList(items []uint) []uint {
|
func uniqueUintList(items []uint) []uint {
|
||||||
itemMap := make(map[uint]struct{}, len(items))
|
itemMap := make(map[uint]struct{}, len(items))
|
||||||
uniq := make([]uint, 0, len(items))
|
uniq := make([]uint, 0, len(items))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
vllmAppKeyForUpgrade = "vllm"
|
||||||
|
vllmImageEnvKey = "IMAGE"
|
||||||
|
vllmImageTypeNvidia = "nvidia"
|
||||||
|
vllmImageTypeIntel = "intel"
|
||||||
|
vllmImageTypeAscend = "ascend"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resolveVllmVersionFamily(version, image string) string {
|
||||||
|
normalizedVersion := strings.ToLower(strings.TrimSpace(version))
|
||||||
|
if strings.HasPrefix(normalizedVersion, vllmImageTypeIntel+"-") {
|
||||||
|
return vllmImageTypeIntel
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(normalizedVersion, vllmImageTypeAscend+"-") {
|
||||||
|
return vllmImageTypeAscend
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(normalizedVersion, vllmImageTypeNvidia+"-") {
|
||||||
|
return vllmImageTypeNvidia
|
||||||
|
}
|
||||||
|
normalizedImage := strings.ToLower(strings.TrimSpace(image))
|
||||||
|
if strings.Contains(normalizedImage, "intel/") || strings.Contains(normalizedImage, "llm-scaler-vllm") {
|
||||||
|
return vllmImageTypeIntel
|
||||||
|
}
|
||||||
|
if strings.Contains(normalizedImage, "ascend/") || strings.Contains(normalizedImage, "vllm-ascend") {
|
||||||
|
return vllmImageTypeAscend
|
||||||
|
}
|
||||||
|
return vllmImageTypeNvidia
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimVllmVersionFamily(version string) string {
|
||||||
|
trimmed := strings.TrimSpace(version)
|
||||||
|
normalized := strings.ToLower(trimmed)
|
||||||
|
for _, family := range []string{vllmImageTypeNvidia, vllmImageTypeIntel, vllmImageTypeAscend} {
|
||||||
|
prefix := family + "-"
|
||||||
|
if strings.HasPrefix(normalized, prefix) {
|
||||||
|
return strings.TrimSpace(trimmed[len(prefix):])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildDefaultVllmImageByVersion(version string) string {
|
||||||
|
tag := trimVllmVersionFamily(version)
|
||||||
|
family := resolveVllmVersionFamily(version, "")
|
||||||
|
if family == vllmImageTypeIntel {
|
||||||
|
return "intel/llm-scaler-vllm:" + tag
|
||||||
|
}
|
||||||
|
if tag != "" && !strings.HasPrefix(strings.ToLower(tag), "v") {
|
||||||
|
tag = "v" + tag
|
||||||
|
}
|
||||||
|
if family == vllmImageTypeAscend {
|
||||||
|
return "quay.io/ascend/vllm-ascend:" + tag
|
||||||
|
}
|
||||||
|
return "vllm/vllm-openai:" + tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func isVllmUpgradeVersionAllowed(currentVersion, targetVersion, currentImage string) bool {
|
||||||
|
currentFamily := resolveVllmVersionFamily(currentVersion, currentImage)
|
||||||
|
targetFamily := resolveVllmVersionFamily(targetVersion, "")
|
||||||
|
return currentFamily == targetFamily
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasVllmVersionFamilyPrefix(version string) bool {
|
||||||
|
normalized := strings.ToLower(strings.TrimSpace(version))
|
||||||
|
return strings.HasPrefix(normalized, vllmImageTypeNvidia+"-") ||
|
||||||
|
strings.HasPrefix(normalized, vllmImageTypeIntel+"-") ||
|
||||||
|
strings.HasPrefix(normalized, vllmImageTypeAscend+"-")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isVllmUpgradeCandidate(currentVersion, targetVersion, currentImage string) bool {
|
||||||
|
if strings.TrimSpace(currentVersion) == strings.TrimSpace(targetVersion) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !isVllmUpgradeVersionAllowed(currentVersion, targetVersion, currentImage) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if common.CompareVersion(targetVersion, currentVersion) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !hasVllmVersionFamilyPrefix(currentVersion) &&
|
||||||
|
resolveVllmVersionFamily(targetVersion, "") == vllmImageTypeNvidia &&
|
||||||
|
trimVllmVersionFamily(currentVersion) == trimVllmVersionFamily(targetVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildVllmUpgradeImage(currentImage, currentVersion, targetVersion string) string {
|
||||||
|
trimmedImage := strings.TrimSpace(currentImage)
|
||||||
|
if trimmedImage == "" || trimmedImage == buildDefaultVllmImageByVersion(currentVersion) {
|
||||||
|
return buildDefaultVllmImageByVersion(targetVersion)
|
||||||
|
}
|
||||||
|
return trimmedImage
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadVllmImageFromEnv(raw string) string {
|
||||||
|
envs := make(map[string]interface{})
|
||||||
|
if strings.TrimSpace(raw) == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(raw), &envs); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if image, ok := envs[vllmImageEnvKey].(string); ok {
|
||||||
|
return strings.TrimSpace(image)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func setVllmImageInEnvContent(content []byte, image string) []byte {
|
||||||
|
normalizedImage := strings.TrimSpace(image)
|
||||||
|
if normalizedImage == "" {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
replaced := false
|
||||||
|
for index, line := range lines {
|
||||||
|
if strings.HasPrefix(line, vllmImageEnvKey+"=") {
|
||||||
|
lines[index] = vllmImageEnvKey + "=" + normalizedImage
|
||||||
|
replaced = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !replaced {
|
||||||
|
if len(lines) > 0 && lines[len(lines)-1] == "" {
|
||||||
|
lines[len(lines)-1] = vllmImageEnvKey + "=" + normalizedImage
|
||||||
|
lines = append(lines, "")
|
||||||
|
} else {
|
||||||
|
lines = append(lines, vllmImageEnvKey+"="+normalizedImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []byte(strings.Join(lines, "\n"))
|
||||||
|
}
|
||||||
+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 {
|
||||||
|
|||||||
@@ -126,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,6 +303,9 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
|
|||||||
filename := filepath.Base(appDetailDTO.DownloadUrl)
|
filename := filepath.Base(appDetailDTO.DownloadUrl)
|
||||||
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml")
|
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml")
|
||||||
statusCode, composeRes, err := req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
statusCode, composeRes, err := req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
||||||
|
if statusCode == http.StatusNotFound {
|
||||||
|
return appDetailDTO, buserr.New("ErrAppVersionUnavailable")
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err)
|
return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err)
|
||||||
}
|
}
|
||||||
@@ -426,7 +429,12 @@ func (a AppService) installWithHooks(req request.AppInstallCreate, executeScript
|
|||||||
} else {
|
} else {
|
||||||
if appDetail.DockerCompose == "" {
|
if appDetail.DockerCompose == "" {
|
||||||
dockerComposeUrl := fmt.Sprintf("%s/%s/1panel/%s/%s/docker-compose.yml", global.AppRepoURL(), global.CONF.Base.Mode, app.Key, appDetail.Version)
|
dockerComposeUrl := fmt.Sprintf("%s/%s/1panel/%s/%s/docker-compose.yml", global.AppRepoURL(), global.CONF.Base.Mode, app.Key, appDetail.Version)
|
||||||
_, composeRes, err = req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
var statusCode int
|
||||||
|
statusCode, composeRes, err = req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
||||||
|
if statusCode == http.StatusNotFound {
|
||||||
|
err = buserr.New("ErrAppVersionUnavailable")
|
||||||
|
return
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -896,7 +904,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() {
|
||||||
@@ -966,7 +974,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,12 +590,20 @@ 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)
|
||||||
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(detail.DownloadUrl, filename), "docker-compose.yml")
|
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(detail.DownloadUrl, filename), "docker-compose.yml")
|
||||||
statusCode, composeRes, err := req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
statusCode, composeRes, err := req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
||||||
|
if statusCode == http.StatusNotFound {
|
||||||
|
return versions, buserr.New("ErrAppVersionUnavailable")
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return versions, err
|
return versions, err
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user