mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
282
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abd3c24562 | ||
|
|
f4e64ca8b3 | ||
|
|
97fde371d9 | ||
|
|
85bbb9cb15 | ||
|
|
1d34b4aff7 | ||
|
|
dc8c50c07a | ||
|
|
4ac490c6b4 | ||
|
|
0d548c7df9 | ||
|
|
8ec5a5bd4b | ||
|
|
2e8bcc09e6 | ||
|
|
29a304e2c8 | ||
|
|
b1adc37920 | ||
|
|
03fac3ec06 | ||
|
|
6e465ac215 | ||
|
|
21bbadc37b | ||
|
|
3061d640ce | ||
|
|
874717cbcd | ||
|
|
6850acabae | ||
|
|
890ea42749 | ||
|
|
6aaf61cde4 | ||
|
|
73930c5e29 | ||
|
|
8f8d280e90 | ||
|
|
b46192024f | ||
|
|
8f939ad5be | ||
|
|
a6363ce4e5 | ||
|
|
fd490b2642 | ||
|
|
7624670ee0 | ||
|
|
8c3145e1c6 | ||
|
|
bec7b6989f | ||
|
|
326f2654e6 | ||
|
|
708499a335 | ||
|
|
bdeb842939 | ||
|
|
edefdce3ff | ||
|
|
b348c36571 | ||
|
|
6ae150b64e | ||
|
|
3687b9bea2 | ||
|
|
711431da82 | ||
|
|
e93afc33aa | ||
|
|
82dd993751 | ||
|
|
9ba293e154 | ||
|
|
8fa9f128d8 | ||
|
|
ed9f1599e6 | ||
|
|
787a29769d | ||
|
|
6b379389d3 | ||
|
|
5bcbf32823 | ||
|
|
0aa90e1b2d | ||
|
|
a7b87211b4 | ||
|
|
2c666933a5 | ||
|
|
f280b533c6 | ||
|
|
47a16bc9a9 | ||
|
|
713202b6eb | ||
|
|
79020abb1c | ||
|
|
83ef41cf1a | ||
|
|
91675c69e9 | ||
|
|
0e68065b40 | ||
|
|
600b769083 | ||
|
|
e7670f96de | ||
|
|
342c04b30a | ||
|
|
5ab8377ac1 | ||
|
|
74eb2c8dd6 | ||
|
|
f136d97941 | ||
|
|
a5629e3f42 | ||
|
|
515d6ca500 | ||
|
|
c0ccc0318d | ||
|
|
8196c11010 | ||
|
|
170bd22062 | ||
|
|
097a33c68f | ||
|
|
3be3bf9926 | ||
|
|
038f2b4f54 | ||
|
|
f84e284c20 | ||
|
|
48e75b5916 | ||
|
|
a12ceaee6e | ||
|
|
9050680115 | ||
|
|
0022a9b508 | ||
|
|
3437d62f31 | ||
|
|
dc880835d0 | ||
|
|
1663bf8677 | ||
|
|
aed236cbc9 | ||
|
|
90be74504e | ||
|
|
bba8aab18c | ||
|
|
47d135ecca | ||
|
|
d96fe49993 | ||
|
|
ac396b61ce | ||
|
|
6798d0c7ee | ||
|
|
1490ed7541 | ||
|
|
48b3032de6 | ||
|
|
ed12bec460 | ||
|
|
3c4a1ea614 | ||
|
|
79eb0288b4 | ||
|
|
99775e1eb4 | ||
|
|
db848b41d2 | ||
|
|
b5b11dff86 | ||
|
|
9cf4bfc606 | ||
|
|
221162d44f | ||
|
|
617970a1f8 | ||
|
|
5610c73d67 | ||
|
|
51fe02661f | ||
|
|
8f491b2933 | ||
|
|
b7e16bd6d5 | ||
|
|
38ca2ddd01 | ||
|
|
402b41100b | ||
|
|
766beae4e1 | ||
|
|
05f72e9dcf | ||
|
|
23553c68ae | ||
|
|
4dd1eb7a1e | ||
|
|
db307732c6 | ||
|
|
33d4ade1d4 | ||
|
|
186075ff9a | ||
|
|
34df745d1c | ||
|
|
ec363ac157 | ||
|
|
193b0f3cd4 | ||
|
|
67c1bf2924 | ||
|
|
b1373ff77e | ||
|
|
55d0e27212 | ||
|
|
1c052b7c13 | ||
|
|
6c8d48c2dc | ||
|
|
b61dae638b | ||
|
|
872a93aa07 | ||
|
|
b1f337020c | ||
|
|
56858dd74c | ||
|
|
358415f737 | ||
|
|
5e73260103 | ||
|
|
e169f46784 | ||
|
|
39416abd47 | ||
|
|
640a0ca815 | ||
|
|
8e54cd5162 | ||
|
|
0f0099accb | ||
|
|
718ebeb203 | ||
|
|
4101bf691d | ||
|
|
a97deb3dca | ||
|
|
10203d5c0c | ||
|
|
a357d6697d | ||
|
|
555b0aa999 | ||
|
|
6dd0dbe2c3 | ||
|
|
c31bbae768 | ||
|
|
2bb0f15973 | ||
|
|
be229551e3 | ||
|
|
6283eb4c69 | ||
|
|
c329fb6599 | ||
|
|
c3685f6b74 | ||
|
|
f4f5a36475 | ||
|
|
ea1261b48e | ||
|
|
0c68f16a66 | ||
|
|
33e5c9230f | ||
|
|
96eefab36f | ||
|
|
b0b6bf458f | ||
|
|
f3d85d1309 | ||
|
|
39385ea0a0 | ||
|
|
e492c6573c | ||
|
|
10a7fb3be2 | ||
|
|
8bc7cfade3 | ||
|
|
ea044d6c32 | ||
|
|
f6ee3dc75b | ||
|
|
367a74503f | ||
|
|
53b1a7af4c | ||
|
|
078d7fbe1e | ||
|
|
c709e48ceb | ||
|
|
0741f60896 | ||
|
|
af8eef4a91 | ||
|
|
f233ef7069 | ||
|
|
1c199bd3d7 | ||
|
|
036c973fec | ||
|
|
8bb225d272 | ||
|
|
ab932ae154 | ||
|
|
89f969ec6e | ||
|
|
afa8cf22b1 | ||
|
|
67aa8471dc | ||
|
|
0617b81b3b | ||
|
|
98c535a12c | ||
|
|
ec46518936 | ||
|
|
a7b034387f | ||
|
|
7905dd00a6 | ||
|
|
74583aa1aa | ||
|
|
4be9d71edf | ||
|
|
57bfc38eb8 | ||
|
|
aaaa5980b5 | ||
|
|
4f57dfc76e | ||
|
|
2c31ffc66b | ||
|
|
b104a13b71 | ||
|
|
28633d2fef | ||
|
|
9052386c83 | ||
|
|
7bc45f2d77 | ||
|
|
2ef6ecb951 | ||
|
|
ca0dc71338 | ||
|
|
04a1ec9a9a | ||
|
|
999e5a779a | ||
|
|
99f62b7230 | ||
|
|
4dee4e1cc2 | ||
|
|
539addc656 | ||
|
|
1a35b307fa | ||
|
|
a04f4a8b96 | ||
|
|
6b88e1adf2 | ||
|
|
99bc8177d4 | ||
|
|
cf2a1372f2 | ||
|
|
42c30f324a | ||
|
|
dab83fee20 | ||
|
|
6f3c5586fc | ||
|
|
aab8df6abc | ||
|
|
b9be79b05a | ||
|
|
ec3024f2c3 | ||
|
|
070f61e229 | ||
|
|
a82a4ebfc5 | ||
|
|
87009ddb27 | ||
|
|
3650ff27b3 | ||
|
|
6c08a3114a | ||
|
|
381cb61185 | ||
|
|
8d69da7702 | ||
|
|
8922395fdc | ||
|
|
83db40e261 | ||
|
|
95ec6c62ef | ||
|
|
56fc9ea428 | ||
|
|
88c6fc050b | ||
|
|
2392f91567 | ||
|
|
5f0c740eb0 | ||
|
|
120d0dd7c6 | ||
|
|
2844f6db7f | ||
|
|
570074f92d | ||
|
|
a13047c855 | ||
|
|
ca0d24bf56 | ||
|
|
2b18251360 | ||
|
|
f7c875acda | ||
|
|
a291a50c9f | ||
|
|
5bc7bc6aac | ||
|
|
9c376300ff | ||
|
|
5187b20252 | ||
|
|
fc401ffd8f | ||
|
|
667ee5256e | ||
|
|
f8356de93c | ||
|
|
55bb997d51 | ||
|
|
7c46f54e15 | ||
|
|
6dbb6d2247 | ||
|
|
9d0ae45edc | ||
|
|
b467dfa3b2 | ||
|
|
c13387bc70 | ||
|
|
41b2523750 | ||
|
|
afe82bd4be | ||
|
|
0f296fb70b | ||
|
|
d96ccd06a3 | ||
|
|
14095affa9 | ||
|
|
ed60565bc9 | ||
|
|
8313912c4f | ||
|
|
6274015d06 | ||
|
|
d195ce9155 | ||
|
|
cd0cd8a152 | ||
|
|
1f45ed6b06 | ||
|
|
7f6ad89edf | ||
|
|
5cdea288e2 | ||
|
|
e56feaf826 | ||
|
|
1288a09647 | ||
|
|
0cdaafa1c0 | ||
|
|
0b3334253c | ||
|
|
10f72f12d2 | ||
|
|
01643b2816 | ||
|
|
56e0da4fe3 | ||
|
|
144c397bf4 | ||
|
|
e06541984f | ||
|
|
ea7e92db84 | ||
|
|
8d48640c1d | ||
|
|
cfe5cb4b6b | ||
|
|
05c4d7dd38 | ||
|
|
13c6e0df99 | ||
|
|
dafd6ba71e | ||
|
|
2751c37716 | ||
|
|
50c01762e7 | ||
|
|
a77ebd3eb6 | ||
|
|
d84f0b5597 | ||
|
|
a3cdf87ea2 | ||
|
|
8ab3c16b3d | ||
|
|
18e943fd06 | ||
|
|
3b7a067dda | ||
|
|
4636ba815b | ||
|
|
317fad2a28 | ||
|
|
0ae28a1dd9 | ||
|
|
bbfe5f1d96 | ||
|
|
b713f20b66 | ||
|
|
3eb7a5adc5 | ||
|
|
956af78749 | ||
|
|
69ad01687e | ||
|
|
e83d07e88e | ||
|
|
6683696032 | ||
|
|
3396a5ffea | ||
|
|
18a14d3b75 |
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.2'
|
||||
node-version: '22.19.0'
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:pro
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
go-version: '1.24'
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.2'
|
||||
node-version: '22.19.0'
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:pro
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
go-version: '1.24'
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
|
||||
+6
-4
@@ -10,7 +10,7 @@ before:
|
||||
- ./ci/script.sh
|
||||
- sed -i 's@ORIGINAL_VERSION=.*@ORIGINAL_VERSION=v{{ .Version }}@g' 1pctl
|
||||
- go mod tidy
|
||||
|
||||
|
||||
builds:
|
||||
- main: ./cmd/server/main.go
|
||||
binary: 1panel
|
||||
@@ -31,17 +31,19 @@ builds:
|
||||
- arm
|
||||
- ppc64le
|
||||
- s390x
|
||||
- riscv64
|
||||
|
||||
archives:
|
||||
- format: tar.gz
|
||||
- formats: [ 'tar.gz' ]
|
||||
name_template: "1panel-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{- if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- 1pctl
|
||||
- 1panel.service
|
||||
- install.sh
|
||||
- GeoIP.mmdb
|
||||
- 1panel.service
|
||||
- initscript/*
|
||||
- lang/*
|
||||
- GeoIP.mmdb
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ PR are always welcome, even if they only contain small fixes like typos or a few
|
||||
|
||||
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot of features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
|
||||
|
||||
This [development guideline](https://docs.1panel.hk/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.
|
||||
This [development guideline](https://docs.1panel.pro/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.
|
||||
|
||||
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ build_frontend:
|
||||
|
||||
build_backend_on_linux:
|
||||
cd $(SERVER_PATH) \
|
||||
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
&& CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_backend_on_darwin:
|
||||
cd $(SERVER_PATH) \
|
||||
@@ -31,4 +31,4 @@ build_backend_on_darwin:
|
||||
|
||||
build_all: build_frontend build_backend_on_linux
|
||||
|
||||
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
|
||||
build_on_local: clean_assets build_frontend build_backend_on_darwin
|
||||
|
||||
@@ -1,48 +1,78 @@
|
||||
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
|
||||
<p align="center"><b>Top-Rated Web-based Linux Server Management Tool</b></p>
|
||||
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
|
||||
<p align="center"><b>Simplify Linux Server Management</b></p>
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 180px; height: auto;" /></a>
|
||||
<a href="https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light" alt="1Panel - Top-Rated web-based Linux server management tool | Product Hunt" style="width: 180px; height: auto;" /></a>
|
||||
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 240px; height: auto;" /></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF" alt="License: GPL v3"></a>
|
||||
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
|
||||
<a href="https://discord.gg/bUpUqWqdRr" target="_blank">
|
||||
<img src="https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||
alt="chat on Discord"></a>
|
||||
<a href="https://github.com/1Panel-dev/1Panel/releases"><img src="https://img.shields.io/github/v/release/1Panel-dev/1Panel" alt="GitHub release"></a>
|
||||
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a><br>
|
||||
[<a href="/README.md">English</a>] | [<a href="/docs/README.zh-Hans.md">中文(简体)</a>] | [<a href="/docs/README.zh-Hant.md">中文(繁體)</a>] | [<a href="/docs/README.ja.md">日本語</a>] | [<a href="/docs/README.pt-br.md">Português (Brasil)</a>] | [<a href="/docs/README.ko.md">한국어</a>]| [<a href="/docs/README.id.md">Bahasa Indonesia</a>]
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="/README.md"><img alt="English" src="https://img.shields.io/badge/English-d9d9d9"></a>
|
||||
<a href="/docs/README.zh-Hans.md"><img alt="中文(简体)" src="https://img.shields.io/badge/中文(简体)-d9d9d9"></a>
|
||||
<a href="/docs/README.ja.md"><img alt="日本語" src="https://img.shields.io/badge/日本語-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.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><br>
|
||||
<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.id.md"><img alt="Bahasa Indonesia" src="https://img.shields.io/badge/Bahasa Indonesia-d9d9d9"></a>
|
||||
<a href="/docs/README.zh-Hant.md"><img alt="中文(繁體)" src="https://img.shields.io/badge/中文(繁體)-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/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9"></a>
|
||||
<a href="/docs/README.ms.md"><img alt="Bahasa Melayu" src="https://img.shields.io/badge/Bahasa Melayu-d9d9d9"></a>
|
||||
</p>
|
||||
|
||||
------------------------------
|
||||
|
||||
1Panel is an open-source, modern web-based control panel for Linux server management.
|
||||
1Panel provides an intuitive web interface and MCP Server to manage websites, files, containers, databases, and LLMs on a Linux server.
|
||||
|
||||
- **Efficient Management**: Through a user-friendly web graphical interface, 1Panel enables users to effortlessly manage their Linux servers. Key features include host monitoring, file management, database administration, and container management.
|
||||
- **Efficient Management**: Through a user-friendly web graphical interface, 1Panel enables users to effortlessly manage their Linux servers. Key features include host monitoring, file management, database administration, container management, LLMs management.
|
||||
- **Rapid Website Deployment**: With deep integration of the popular open-source website building software WordPress, 1Panel streamlines the process of domain binding and SSL certificate configuration, all achievable with just one click.
|
||||
- **Application Store**: 1Panel curates a wide range of high-quality open-source tools and applications, facilitating easy installation and updates for its users.
|
||||
- **Security and Reliability**: By leveraging containerization and secure application deployment practices, 1Panel minimizes vulnerability exposure. It further enhances security through integrated firewall management and log auditing capabilities.
|
||||
- **One-Click Backup & Restore**: Data protection is made simple with 1Panel's one-click backup and restore functionality, supporting various cloud storage solutions to ensure data integrity and availability.
|
||||
- **MCP Server**: [1Panel MCP Server](https://github.com/1Panel-dev/mcp-1panel) allow user to execute server operations via natural language.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Execute the script below and follow the prompts to install 1Panel:
|
||||
|
||||
```bash
|
||||
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
|
||||
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
|
||||
```
|
||||
|
||||
Please refer to our [documentation](https://docs.1panel.hk/quick_start/) for more details.
|
||||
Please refer to our [documentation](https://docs.1panel.pro/quick_start/) for more details.
|
||||
|
||||
中国用户请使用此 [安装脚本](https://1panel.cn/docs/v2/installation/online_installation/),默认安装 V2 版本,应用数量相较国际版本更丰富。
|
||||
|
||||
## Screenshot
|
||||
|
||||

|
||||

|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#1Panel-dev/1Panel&Date)
|
||||
|
||||
## Pro Edition
|
||||
|
||||
Compared to the OSS Edition, 1Panel Pro Edition provides users with a wealth of enhanced features and technical support services. Enhanced features include WAF enhancement, Website monitoring, Mobile APP, custom logo and theme, etc.
|
||||
|
||||
[Click to see Pro Edition details](https://1panel.pro/pricing).
|
||||
|
||||
## Security Information
|
||||
|
||||
If you discover any security issues, please contact us through: wanghe@fit2cloud.com.
|
||||
If you discover any security issues, please refer to [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## GitAds Sponsored
|
||||
|
||||
[](https://gitads.dev/v1/ad-track?source=1panel-dev/1panel@github)
|
||||
|
||||
## License
|
||||
|
||||
@@ -51,3 +81,7 @@ Licensed under The GNU General Public License version 3 (GPLv3) (the "License")
|
||||
<https://www.gnu.org/licenses/gpl-3.0.html>
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
<!-- GitAds-Verify: 9M8AMBB11GYJ3RUNXGB5R1MR71KPATU9 -->
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/xpu"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Create Ollama model
|
||||
// @Accept json
|
||||
// @Param request body dto.OllamaModelName true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型 [name]","formatEN":"add Ollama model [name]"}
|
||||
func (b *BaseApi) CreateOllamaModel(c *gin.Context) {
|
||||
var req dto.OllamaModelName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := AIToolService.Create(req.Name); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Rereate Ollama model
|
||||
// @Accept json
|
||||
// @Param request body dto.OllamaModelName true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model/recreate [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型重试 [name]","formatEN":"re-add Ollama model [name]"}
|
||||
func (b *BaseApi) RecreateOllamaModel(c *gin.Context) {
|
||||
var req dto.OllamaModelName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := AIToolService.Recreate(req.Name); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Close Ollama model conn
|
||||
// @Accept json
|
||||
// @Param request body dto.OllamaModelName true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model/close [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"关闭 Ollama 模型连接 [name]","formatEN":"close conn for Ollama model [name]"}
|
||||
func (b *BaseApi) CloseOllamaModel(c *gin.Context) {
|
||||
var req dto.OllamaModelName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := AIToolService.Close(req.Name); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Sync Ollama model list
|
||||
// @Success 200 {array} dto.OllamaModelDropList
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model/sync [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"同步 Ollama 模型列表","formatEN":"sync Ollama model list"}
|
||||
func (b *BaseApi) SyncOllamaModel(c *gin.Context) {
|
||||
list, err := AIToolService.Sync()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Page Ollama models
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model/search [post]
|
||||
func (b *BaseApi) SearchOllamaModel(c *gin.Context) {
|
||||
var req dto.SearchWithPage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := AIToolService.Search(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Page Ollama models
|
||||
// @Accept json
|
||||
// @Param request body dto.OllamaModelName true "request"
|
||||
// @Success 200 {string} details
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model/load [post]
|
||||
func (b *BaseApi) LoadOllamaModelDetail(c *gin.Context) {
|
||||
var req dto.OllamaModelName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
detail, err := AIToolService.LoadDetail(req.Name)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, detail)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Delete Ollama model
|
||||
// @Accept json
|
||||
// @Param request body dto.ForceDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/ollama/model/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"ollama_models","output_column":"name","output_value":"names"}],"formatZH":"删除 Ollama 模型 [names]","formatEN":"remove Ollama model [names]"}
|
||||
func (b *BaseApi) DeleteOllamaModel(c *gin.Context) {
|
||||
var req dto.ForceDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := AIToolService.Delete(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithOutData(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.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
return
|
||||
}
|
||||
xpuOK, xpuClient := xpu.New()
|
||||
if xpuOK {
|
||||
info, err := xpuClient.LoadGpuInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, &common.GpuInfo{})
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Bind domain
|
||||
// @Accept json
|
||||
// @Param request body dto.OllamaBindDomain true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/domain/bind [post]
|
||||
func (b *BaseApi) BindDomain(c *gin.Context) {
|
||||
var req dto.OllamaBindDomain
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := AIToolService.BindDomain(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get bind domain
|
||||
// @Accept json
|
||||
// @Param request body dto.OllamaBindDomainReq true "request"
|
||||
// @Success 200 {object} dto.OllamaBindDomainRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/domain/get [post]
|
||||
func (b *BaseApi) GetBindDomain(c *gin.Context) {
|
||||
var req dto.OllamaBindDomainReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
res, err := AIToolService.GetBindDomain(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// Tags AI
|
||||
// Summary Update bind domain
|
||||
// Accept json
|
||||
// Param request body dto.OllamaBindDomain true "request"
|
||||
// Success 200
|
||||
// Security ApiKeyAuth
|
||||
// Security Timestamp
|
||||
// Router /ai/domain/update [post]
|
||||
func (b *BaseApi) UpdateBindDomain(c *gin.Context) {
|
||||
var req dto.OllamaBindDomain
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := AIToolService.UpdateBindDomain(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
+10
-10
@@ -13,7 +13,7 @@ import (
|
||||
// @Summary List apps
|
||||
// @Accept json
|
||||
// @Param request body request.AppSearch true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.AppRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/search [post]
|
||||
@@ -22,7 +22,7 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
list, err := appService.PageApp(req)
|
||||
list, err := appService.PageApp(c, req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -68,14 +68,14 @@ func (b *BaseApi) SyncApp(c *gin.Context) {
|
||||
// @Success 200 {object} response.AppDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/:key [get]
|
||||
// @Router /apps/{key} [get]
|
||||
func (b *BaseApi) GetApp(c *gin.Context) {
|
||||
appKey, err := helper.GetStrParamByKey(c, "key")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
appDTO, err := appService.GetApp(appKey)
|
||||
appDTO, err := appService.GetApp(c, appKey)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -88,11 +88,11 @@ func (b *BaseApi) GetApp(c *gin.Context) {
|
||||
// @Accept json
|
||||
// @Param appId path integer true "app id"
|
||||
// @Param version path string true "app version"
|
||||
// @Param version path string true "app type"
|
||||
// @Param type path string true "app type"
|
||||
// @Success 200 {object} response.AppDetailDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/detail/:appId/:version/:type [get]
|
||||
// @Router /apps/detail/{appId}/{version}/{type} [get]
|
||||
func (b *BaseApi) GetAppDetail(c *gin.Context) {
|
||||
appID, err := helper.GetIntParamByKey(c, "appId")
|
||||
if err != nil {
|
||||
@@ -112,11 +112,11 @@ func (b *BaseApi) GetAppDetail(c *gin.Context) {
|
||||
// @Tags App
|
||||
// @Summary Get app detail by id
|
||||
// @Accept json
|
||||
// @Param appId path integer true "id"
|
||||
// @Param id path integer true "id"
|
||||
// @Success 200 {object} response.AppDetailDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/details/:id [get]
|
||||
// @Router /apps/details/{id} [get]
|
||||
func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
|
||||
appDetailID, err := helper.GetIntParamByKey(c, "id")
|
||||
if err != nil {
|
||||
@@ -173,7 +173,7 @@ func (b *BaseApi) InstallApp(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetAppTags(c *gin.Context) {
|
||||
tags, err := appService.GetAppTags()
|
||||
tags, err := appService.GetAppTags(c)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -183,7 +183,7 @@ func (b *BaseApi) GetAppTags(c *gin.Context) {
|
||||
|
||||
// @Tags App
|
||||
// @Summary Get app list update
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.AppUpdateRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/checkupdate [get]
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// @Summary Page app installed
|
||||
// @Accept json
|
||||
// @Param request body request.AppInstalledSearch true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/installed/search [post]
|
||||
@@ -28,7 +28,10 @@ func (b *BaseApi) SearchAppInstalled(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: int64(len(list)),
|
||||
})
|
||||
} else {
|
||||
total, list, err := appInstallService.Page(req)
|
||||
if err != nil {
|
||||
@@ -45,7 +48,7 @@ func (b *BaseApi) SearchAppInstalled(c *gin.Context) {
|
||||
// @Tags App
|
||||
// @Summary List app installed
|
||||
// @Accept json
|
||||
// @Success 200 array dto.AppInstallInfo
|
||||
// @Success 200 {array} dto.AppInstallInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/installed/list [get]
|
||||
@@ -104,10 +107,11 @@ func (b *BaseApi) LoadPort(c *gin.Context) {
|
||||
// @Summary Search app password by key
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200 {string} response.DatabaseConn
|
||||
// @Param key path string true "key"
|
||||
// @Success 200 {object} response.DatabaseConn
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/installed/conninfo/:key [get]
|
||||
// @Router /apps/installed/conninfo/{key} [get]
|
||||
func (b *BaseApi) LoadConnInfo(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -128,7 +132,7 @@ func (b *BaseApi) LoadConnInfo(c *gin.Context) {
|
||||
// @Success 200 {array} dto.AppResource
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/installed/delete/check/:appInstallId [get]
|
||||
// @Router /apps/installed/delete/check/{appInstallId} [get]
|
||||
func (b *BaseApi) DeleteCheck(c *gin.Context) {
|
||||
appInstallId, err := helper.GetIntParamByKey(c, "appInstallId")
|
||||
if err != nil {
|
||||
@@ -187,7 +191,7 @@ func (b *BaseApi) OperateInstalled(c *gin.Context) {
|
||||
// @Success 200 {array} response.AppService
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/services/:key [get]
|
||||
// @Router /apps/services/{key} [get]
|
||||
func (b *BaseApi) GetServices(c *gin.Context) {
|
||||
key := c.Param("key")
|
||||
services, err := appInstallService.GetServices(key)
|
||||
@@ -201,7 +205,6 @@ func (b *BaseApi) GetServices(c *gin.Context) {
|
||||
// @Tags App
|
||||
// @Summary Search app update version by install id
|
||||
// @Accept json
|
||||
// @Param appInstallId path integer true "request"
|
||||
// @Success 200 {array} dto.AppVersion
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
@@ -266,10 +269,10 @@ func (b *BaseApi) GetDefaultConfig(c *gin.Context) {
|
||||
// @Summary Search params by appInstallId
|
||||
// @Accept json
|
||||
// @Param appInstallId path string true "request"
|
||||
// @Success 200 {object} response.AppParam
|
||||
// @Success 200 {object} response.AppConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/installed/params/:appInstallId [get]
|
||||
// @Router /apps/installed/params/{appInstallId} [get]
|
||||
func (b *BaseApi) GetParams(c *gin.Context) {
|
||||
appInstallId, err := helper.GetIntParamByKey(c, "appInstallId")
|
||||
if err != nil {
|
||||
|
||||
+18
-14
@@ -2,6 +2,7 @@ package v1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/captcha"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/geo"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -28,7 +28,9 @@ func (b *BaseApi) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.AuthMethod != "jwt" && !req.IgnoreCaptcha {
|
||||
ip := common.GetRealClientIP(c)
|
||||
needCaptcha := global.IPTracker.NeedCaptcha(ip)
|
||||
if needCaptcha {
|
||||
if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -50,9 +52,11 @@ func (b *BaseApi) Login(c *gin.Context) {
|
||||
user, err := authService.Login(c, req, string(entrance))
|
||||
go saveLoginLogs(c, err)
|
||||
if err != nil {
|
||||
global.IPTracker.SetNeedCaptcha(ip)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
global.IPTracker.Clear(ip)
|
||||
helper.SuccessWithData(c, user)
|
||||
}
|
||||
|
||||
@@ -121,7 +125,7 @@ func (b *BaseApi) GetResponsePage(c *gin.Context) {
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Check System isDemo
|
||||
// @Success 200
|
||||
// @Success 200 {boolean} isDemo
|
||||
// @Router /auth/demo [get]
|
||||
func (b *BaseApi) CheckIsDemo(c *gin.Context) {
|
||||
helper.SuccessWithData(c, global.CONF.System.IsDemo)
|
||||
@@ -129,23 +133,28 @@ func (b *BaseApi) CheckIsDemo(c *gin.Context) {
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Check System isIntl
|
||||
// @Success 200
|
||||
// @Success 200 {boolean} isIntl
|
||||
// @Router /auth/intl [get]
|
||||
func (b *BaseApi) CheckIsIntl(c *gin.Context) {
|
||||
helper.SuccessWithData(c, global.CONF.System.IsIntl)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Load System Language
|
||||
// @Success 200
|
||||
// @Router /auth/language [get]
|
||||
func (b *BaseApi) GetLanguage(c *gin.Context) {
|
||||
// @Summary Load System Setting for login
|
||||
// @Success 200 {object} dto.LoginSetting
|
||||
// @Router /auth/setting [get]
|
||||
func (b *BaseApi) GetAuthSetting(c *gin.Context) {
|
||||
settingInfo, err := settingService.GetSettingInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, settingInfo.Language)
|
||||
ip := common.GetRealClientIP(c)
|
||||
needCaptcha := global.IPTracker.NeedCaptcha(ip)
|
||||
helper.SuccessWithData(c, dto.LoginSetting{
|
||||
NeedCaptcha: needCaptcha,
|
||||
Language: settingInfo.Language,
|
||||
})
|
||||
}
|
||||
|
||||
func saveLoginLogs(c *gin.Context, err error) {
|
||||
@@ -157,11 +166,6 @@ func saveLoginLogs(c *gin.Context, err error) {
|
||||
logs.Status = constant.StatusSuccess
|
||||
}
|
||||
logs.IP = c.ClientIP()
|
||||
address, err := geo.GetIPLocation(logs.IP, common.GetLang(c))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get ip location failed: %s", err)
|
||||
}
|
||||
logs.Agent = c.GetHeader("User-Agent")
|
||||
logs.Address = address
|
||||
_ = logService.CreateLoginLog(logs)
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (b *BaseApi) RefreshOneDriveToken(c *gin.Context) {
|
||||
// @Summary List buckets
|
||||
// @Accept json
|
||||
// @Param request body dto.ForBuckets true "request"
|
||||
// @Success 200 {array} string
|
||||
// @Success 200 {array} object
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/backup/search [post]
|
||||
@@ -140,7 +140,7 @@ func (b *BaseApi) DeleteBackup(c *gin.Context) {
|
||||
// @Summary Page backup records
|
||||
// @Accept json
|
||||
// @Param request body dto.RecordSearch true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/backup/record/search [post]
|
||||
@@ -162,11 +162,34 @@ func (b *BaseApi) SearchBackupRecords(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Load backup records size
|
||||
// @Accept json
|
||||
// @Param request body dto.RecordSearch true "request"
|
||||
// @Success 200 {array} dto.BackupFile
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/backup/record/size [post]
|
||||
func (b *BaseApi) LoadBackupSize(c *gin.Context) {
|
||||
var req dto.RecordSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
list, err := backupService.LoadSize(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Page backup records by cronjob
|
||||
// @Accept json
|
||||
// @Param request body dto.RecordSearchByCronjob true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/backup/record/search/bycronjob [post]
|
||||
@@ -188,11 +211,34 @@ func (b *BaseApi) SearchBackupRecordsByCronjob(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Load backup records size for cronjob
|
||||
// @Accept json
|
||||
// @Param request body dto.RecordSearchByCronjob true "request"
|
||||
// @Success 200 {array} dto.BackupFile
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/backup/record/size/bycronjob [post]
|
||||
func (b *BaseApi) LoadBackupSizeByCronjob(c *gin.Context) {
|
||||
var req dto.RecordSearchByCronjob
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
list, err := backupService.LoadSizeByCronjob(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Download backup record
|
||||
// @Accept json
|
||||
// @Param request body dto.DownloadRecord true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} filePath
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/backup/record/download [post]
|
||||
|
||||
@@ -120,6 +120,7 @@ func (b *BaseApi) LoadClamBaseInfo(c *gin.Context) {
|
||||
// @Summary Operate Clam
|
||||
// @Accept json
|
||||
// @Param request body dto.Operate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/clam/operate [post]
|
||||
@@ -142,6 +143,7 @@ func (b *BaseApi) OperateClam(c *gin.Context) {
|
||||
// @Summary Clean clam record
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/clam/record/clean [post]
|
||||
@@ -189,7 +191,7 @@ func (b *BaseApi) SearchClamRecord(c *gin.Context) {
|
||||
// @Summary Load clam record detail
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamLogReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/clam/record/log [post]
|
||||
@@ -212,7 +214,7 @@ func (b *BaseApi) LoadClamRecordLog(c *gin.Context) {
|
||||
// @Summary Load clam file
|
||||
// @Accept json
|
||||
// @Param request body dto.ClamFileReq true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/clam/file/search [post]
|
||||
|
||||
@@ -106,7 +106,7 @@ func (b *BaseApi) SearchRedisCommand(c *gin.Context) {
|
||||
// @Tags Command
|
||||
// @Summary Tree commands
|
||||
// @Accept json
|
||||
// @Success 200 {Array} dto.CommandTree
|
||||
// @Success 200 {array} dto.CommandTree
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/command/tree [get]
|
||||
@@ -122,7 +122,7 @@ func (b *BaseApi) SearchCommandTree(c *gin.Context) {
|
||||
|
||||
// @Tags Redis Command
|
||||
// @Summary List redis commands
|
||||
// @Success 200 {Array} dto.RedisCommand
|
||||
// @Success 200 {array} dto.RedisCommand
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/command/redis [get]
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// @Tags Container
|
||||
@@ -40,7 +41,7 @@ func (b *BaseApi) SearchContainer(c *gin.Context) {
|
||||
// @Summary List containers
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/list [post]
|
||||
@@ -82,7 +83,7 @@ func (b *BaseApi) SearchCompose(c *gin.Context) {
|
||||
// @Summary Test compose
|
||||
// @Accept json
|
||||
// @Param request body dto.ComposeCreate true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {boolean} isOK
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/compose/test [post]
|
||||
@@ -105,7 +106,7 @@ func (b *BaseApi) TestCompose(c *gin.Context) {
|
||||
// @Summary Create compose
|
||||
// @Accept json
|
||||
// @Param request body dto.ComposeCreate true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} log
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/compose [post]
|
||||
@@ -311,7 +312,7 @@ func (b *BaseApi) CleanContainerLog(c *gin.Context) {
|
||||
// @Summary Load container log
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/load/log [post]
|
||||
@@ -390,11 +391,11 @@ func (b *BaseApi) ContainerOperation(c *gin.Context) {
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Container stats
|
||||
// @Param id path integer true "container id"
|
||||
// @Param id path string true "container id"
|
||||
// @Success 200 {object} dto.ContainerStats
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/stats/:id [get]
|
||||
// @Router /containers/stats/{id} [get]
|
||||
func (b *BaseApi) ContainerStats(c *gin.Context) {
|
||||
containerID, ok := c.Params.Get("id")
|
||||
if !ok {
|
||||
@@ -438,6 +439,7 @@ func (b *BaseApi) Inspect(c *gin.Context) {
|
||||
// @Param since query string false "since"
|
||||
// @Param follow query string false "follow"
|
||||
// @Param tail query string false "tail"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/search/log [post]
|
||||
@@ -464,6 +466,7 @@ func (b *BaseApi) ContainerLogs(c *gin.Context) {
|
||||
// @Summary Download Container logs
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerLog true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/download/log [post]
|
||||
@@ -680,6 +683,7 @@ func (b *BaseApi) ComposeUpdate(c *gin.Context) {
|
||||
// @Param since query string false "date"
|
||||
// @Param follow query string false "follow"
|
||||
// @Param tail query string false "tail"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/compose/search/log [get]
|
||||
|
||||
@@ -92,7 +92,7 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
|
||||
// @Summary Load Cronjob record log
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /cronjobs/records/log [post]
|
||||
|
||||
@@ -34,7 +34,7 @@ func (b *BaseApi) LoadDashboardOsInfo(c *gin.Context) {
|
||||
// @Success 200 {object} dto.DashboardBase
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /dashboard/base/:ioOption/:netOption [get]
|
||||
// @Router /dashboard/base/{ioOption}/{netOption} [get]
|
||||
func (b *BaseApi) LoadDashboardBaseInfo(c *gin.Context) {
|
||||
ioOption, ok := c.Params.Get("ioOption")
|
||||
if !ok {
|
||||
@@ -78,7 +78,7 @@ func (b *BaseApi) LoadDashboardCurrentInfo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /dashboard/system/restart/:operation [post]
|
||||
// @Router /dashboard/system/restart/{operation} [post]
|
||||
func (b *BaseApi) SystemRestart(c *gin.Context) {
|
||||
operation, ok := c.Params.Get("operation")
|
||||
if !ok {
|
||||
|
||||
@@ -43,7 +43,7 @@ func (b *BaseApi) CreateDatabase(c *gin.Context) {
|
||||
// @Summary Check database
|
||||
// @Accept json
|
||||
// @Param request body dto.DatabaseCreate true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {boolean} isOk
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/db/check [post]
|
||||
@@ -93,10 +93,11 @@ func (b *BaseApi) SearchDatabase(c *gin.Context) {
|
||||
|
||||
// @Tags Database
|
||||
// @Summary List databases
|
||||
// @Param type path string true "type"
|
||||
// @Success 200 {array} dto.DatabaseOption
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/db/list/:type [get]
|
||||
// @Router /databases/db/list/{type} [get]
|
||||
func (b *BaseApi) ListDatabase(c *gin.Context) {
|
||||
dbType, err := helper.GetStrParamByKey(c, "type")
|
||||
if err != nil {
|
||||
@@ -114,10 +115,11 @@ func (b *BaseApi) ListDatabase(c *gin.Context) {
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Retrieve database list based on type
|
||||
// @Param type path string true "type"
|
||||
// @Success 200 {array} dto.DatabaseItem
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/db/item/:type [get]
|
||||
// @Router /databases/db/item/{type} [get]
|
||||
func (b *BaseApi) LoadDatabaseItems(c *gin.Context) {
|
||||
dbType, err := helper.GetStrParamByKey(c, "type")
|
||||
if err != nil {
|
||||
@@ -135,10 +137,11 @@ func (b *BaseApi) LoadDatabaseItems(c *gin.Context) {
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Get databases
|
||||
// @Param name path string true "name"
|
||||
// @Success 200 {object} dto.DatabaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/db/:name [get]
|
||||
// @Router /databases/db/{name} [get]
|
||||
func (b *BaseApi) GetDatabase(c *gin.Context) {
|
||||
name, err := helper.GetStrParamByKey(c, "name")
|
||||
if err != nil {
|
||||
|
||||
@@ -34,7 +34,7 @@ func (b *BaseApi) LoadDBBaseInfo(c *gin.Context) {
|
||||
// @Summary Load Database conf
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithNameAndType true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/common/load/file [post]
|
||||
|
||||
@@ -217,6 +217,7 @@ func (b *BaseApi) ListDBName(c *gin.Context) {
|
||||
// @Summary Load mysql database from remote
|
||||
// @Accept json
|
||||
// @Param request body dto.MysqlLoadDB true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/load [post]
|
||||
|
||||
@@ -168,9 +168,11 @@ func (b *BaseApi) SearchPostgresql(c *gin.Context) {
|
||||
// @Summary Load postgresql database from remote
|
||||
// @Accept json
|
||||
// @Param request body dto.PostgresqlLoadDB true "request"
|
||||
// @Param database path string true "database"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/pg/:database/load [post]
|
||||
// @Router /databases/pg/{database}/load [post]
|
||||
func (b *BaseApi) LoadPostgresqlDBFromRemote(c *gin.Context) {
|
||||
database, err := helper.GetStrParamByKey(c, "database")
|
||||
if err != nil {
|
||||
|
||||
@@ -28,7 +28,7 @@ func (b *BaseApi) LoadDeviceBaseInfo(c *gin.Context) {
|
||||
// @Tags Device
|
||||
// @Summary list time zone options
|
||||
// @Accept json
|
||||
// @Success 200 {Array} string
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/device/zone/options [get]
|
||||
@@ -46,7 +46,7 @@ func (b *BaseApi) LoadTimeOption(c *gin.Context) {
|
||||
// @Summary load conf
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/device/conf [post]
|
||||
@@ -185,7 +185,7 @@ func (b *BaseApi) UpdateDeviceSwap(c *gin.Context) {
|
||||
// @Summary Check device DNS conf
|
||||
// @Accept json
|
||||
// @Param request body dto.SettingUpdate true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {boolean} data
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/device/check/dns [post]
|
||||
@@ -206,7 +206,7 @@ func (b *BaseApi) CheckDNS(c *gin.Context) {
|
||||
|
||||
// @Tags Device
|
||||
// @Summary Scan system
|
||||
// @Success 200
|
||||
// @Success 200 {object} dto.CleanData
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/scan [post]
|
||||
|
||||
@@ -15,6 +15,8 @@ var (
|
||||
appService = service.NewIAppService()
|
||||
appInstallService = service.NewIAppInstalledService()
|
||||
|
||||
AIToolService = service.NewIAIToolService()
|
||||
|
||||
containerService = service.NewIContainerService()
|
||||
composeTemplateService = service.NewIComposeTemplateService()
|
||||
imageRepoService = service.NewIImageRepoService()
|
||||
@@ -66,4 +68,6 @@ var (
|
||||
favoriteService = service.NewIFavoriteService()
|
||||
|
||||
websiteCAService = service.NewIWebsiteCAService()
|
||||
|
||||
mcpServerService = service.NewIMcpServerService()
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ func (b *BaseApi) LoadFail2BanBaseInfo(c *gin.Context) {
|
||||
// @Summary Page fail2ban ip list
|
||||
// @Accept json
|
||||
// @Param request body dto.Fail2BanSearch true "request"
|
||||
// @Success 200 {Array} string
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/fail2ban/search [post]
|
||||
@@ -52,6 +52,7 @@ func (b *BaseApi) SearchFail2Ban(c *gin.Context) {
|
||||
// @Summary Operate fail2ban
|
||||
// @Accept json
|
||||
// @Param request body dto.Operate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/fail2ban/operate [post]
|
||||
@@ -74,6 +75,7 @@ func (b *BaseApi) OperateFail2Ban(c *gin.Context) {
|
||||
// @Summary Operate sshd of fail2ban
|
||||
// @Accept json
|
||||
// @Param request body dto.Operate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/fail2ban/operate/sshd [post]
|
||||
@@ -116,7 +118,7 @@ func (b *BaseApi) UpdateFail2BanConf(c *gin.Context) {
|
||||
// @Tags Fail2ban
|
||||
// @Summary Load fail2ban conf
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Success 200 {string} file
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/fail2ban/load/conf [get]
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// @Summary List favorites
|
||||
// @Accept json
|
||||
// @Param request body dto.PageInfo true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/favorite/search [post]
|
||||
@@ -36,7 +36,7 @@ func (b *BaseApi) SearchFavorite(c *gin.Context) {
|
||||
// @Summary Create favorite
|
||||
// @Accept json
|
||||
// @Param request body request.FavoriteCreate true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} model.Favorite
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/favorite [post]
|
||||
|
||||
+38
-10
@@ -39,19 +39,19 @@ func (b *BaseApi) ListFiles(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
files, err := fileService.GetFileList(req)
|
||||
fileList, err := fileService.GetFileList(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, files)
|
||||
helper.SuccessWithData(c, fileList)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Page file
|
||||
// @Accept json
|
||||
// @Param request body request.SearchUploadWithPage true "request"
|
||||
// @Success 200 {array} response.FileInfo
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/upload/search [post]
|
||||
@@ -290,6 +290,7 @@ func (b *BaseApi) SaveContent(c *gin.Context) {
|
||||
|
||||
// @Tags File
|
||||
// @Summary Upload file
|
||||
// @Accept multipart/form-data
|
||||
// @Param file formData file true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
@@ -400,7 +401,7 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
// @Summary Check file exist
|
||||
// @Accept json
|
||||
// @Param request body request.FilePathCheck true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {boolean} isOk
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/check [post]
|
||||
@@ -409,11 +410,37 @@ func (b *BaseApi) CheckFile(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err := os.Stat(req.Path); err != nil {
|
||||
helper.SuccessWithData(c, false)
|
||||
fileOp := files.NewFileOp()
|
||||
if fileOp.Stat(req.Path) {
|
||||
helper.SuccessWithData(c, true)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, true)
|
||||
if req.WithInit {
|
||||
if err := fileOp.CreateDir(req.Path, 0644); err != nil {
|
||||
helper.SuccessWithData(c, false)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, true)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, false)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Batch check file exist
|
||||
// @Accept json
|
||||
// @Param request body request.FilePathsCheck true "request"
|
||||
// @Success 200 {array} response.ExistFileInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/batch/check [post]
|
||||
func (b *BaseApi) BatchCheckFiles(c *gin.Context) {
|
||||
var req request.FilePathsCheck
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
fileList := fileService.BatchCheckFiles(req)
|
||||
helper.SuccessWithData(c, fileList)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
@@ -441,7 +468,7 @@ func (b *BaseApi) ChangeFileName(c *gin.Context) {
|
||||
// @Summary Wget file
|
||||
// @Accept json
|
||||
// @Param request body request.FileWget true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.FileWgetRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/wget [post]
|
||||
@@ -583,7 +610,7 @@ func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
|
||||
// @Summary Load file size
|
||||
// @Accept json
|
||||
// @Param request body request.DirSizeReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.DirSizeRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/size [post]
|
||||
@@ -662,6 +689,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int,
|
||||
|
||||
// @Tags File
|
||||
// @Summary Chunk upload file
|
||||
// @Accept multipart/form-data
|
||||
// @Param file formData file true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
@@ -784,7 +812,7 @@ func (b *BaseApi) Keys(c *gin.Context) {
|
||||
// @Tags File
|
||||
// @Summary Read file by Line
|
||||
// @Param request body request.FileReadByLineReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.FileLineContent
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/read [post]
|
||||
|
||||
@@ -53,7 +53,7 @@ func (b *BaseApi) SearchFirewallRule(c *gin.Context) {
|
||||
// @Summary Page firewall status
|
||||
// @Accept json
|
||||
// @Param request body dto.FirewallOperation true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/firewall/operate [post]
|
||||
@@ -203,7 +203,7 @@ func (b *BaseApi) UpdatePortRule(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Uodate address group
|
||||
// @Summary Update address group
|
||||
// @Accept json
|
||||
// @Param request body dto.AddrRuleUpdate true "request"
|
||||
// @Success 200
|
||||
|
||||
@@ -55,6 +55,7 @@ func (b *BaseApi) LoadFtpLogInfo(c *gin.Context) {
|
||||
// @Summary Operate FTP
|
||||
// @Accept json
|
||||
// @Param request body dto.Operate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /toolbox/ftp/operate [post]
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// @Summary Create host
|
||||
// @Accept json
|
||||
// @Param request body dto.HostOperate true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} dto.HostInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts [post]
|
||||
@@ -35,7 +35,7 @@ func (b *BaseApi) CreateHost(c *gin.Context) {
|
||||
// @Summary Test host conn by info
|
||||
// @Accept json
|
||||
// @Param request body dto.HostConnTest true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {boolean} connStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/test/byinfo [post]
|
||||
@@ -56,7 +56,7 @@ func (b *BaseApi) TestByInfo(c *gin.Context) {
|
||||
// @Success 200 {boolean} connStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/test/byid/:id [post]
|
||||
// @Router /hosts/test/byid/{id} [post]
|
||||
func (b *BaseApi) TestByID(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
@@ -95,7 +95,7 @@ func (b *BaseApi) HostTree(c *gin.Context) {
|
||||
// @Summary Page host
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchHostWithPage true "request"
|
||||
// @Success 200 {array} dto.HostTree
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/search [post]
|
||||
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
// @Summary Get tool status
|
||||
// @Accept json
|
||||
// @Param request body request.HostToolReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.HostToolRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool [post]
|
||||
// @Router /hosts/tool [post]
|
||||
func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
||||
var req request.HostToolReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -36,7 +36,7 @@ func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/create [post]
|
||||
// @Router /hosts/tool/create [post]
|
||||
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 [type] 配置","formatEN":"create [type] config"}
|
||||
func (b *BaseApi) InitToolConfig(c *gin.Context) {
|
||||
var req request.HostToolCreate
|
||||
@@ -58,7 +58,7 @@ func (b *BaseApi) InitToolConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/operate [post]
|
||||
// @Router /hosts/tool/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operate","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] [type] ","formatEN":"[operate] [type]"}
|
||||
func (b *BaseApi) OperateTool(c *gin.Context) {
|
||||
var req request.HostToolReq
|
||||
@@ -77,10 +77,10 @@ func (b *BaseApi) OperateTool(c *gin.Context) {
|
||||
// @Summary Get tool config
|
||||
// @Accept json
|
||||
// @Param request body request.HostToolConfig true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.HostToolConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/config [post]
|
||||
// @Router /hosts/tool/config [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机工具配置文件 ","formatEN":"[operate] tool config"}
|
||||
func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
||||
var req request.HostToolConfig
|
||||
@@ -100,10 +100,10 @@ func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
||||
// @Summary Get tool logs
|
||||
// @Accept json
|
||||
// @Param request body request.HostToolLogReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} logContent
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/log [post]
|
||||
// @Router /hosts/tool/log [post]
|
||||
func (b *BaseApi) GetToolLog(c *gin.Context) {
|
||||
var req request.HostToolLogReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -125,7 +125,7 @@ func (b *BaseApi) GetToolLog(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/supervisor/process [post]
|
||||
// @Router /hosts/tool/supervisor/process [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 守护进程 ","formatEN":"[operate] process"}
|
||||
func (b *BaseApi) OperateProcess(c *gin.Context) {
|
||||
var req request.SupervisorProcessConfig
|
||||
@@ -144,10 +144,10 @@ func (b *BaseApi) OperateProcess(c *gin.Context) {
|
||||
// @Tags Host tool
|
||||
// @Summary Get Supervisor process config
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.SupervisorProcessConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/supervisor/process [get]
|
||||
// @Router /hosts/tool/supervisor/process [get]
|
||||
func (b *BaseApi) GetProcess(c *gin.Context) {
|
||||
configs, err := hostToolService.GetSupervisorProcessConfig()
|
||||
if err != nil {
|
||||
@@ -161,10 +161,10 @@ func (b *BaseApi) GetProcess(c *gin.Context) {
|
||||
// @Summary Get Supervisor process config
|
||||
// @Accept json
|
||||
// @Param request body request.SupervisorProcessFileReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/supervisor/process/file [post]
|
||||
// @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) GetProcessFile(c *gin.Context) {
|
||||
var req request.SupervisorProcessFileReq
|
||||
|
||||
@@ -94,7 +94,7 @@ func (b *BaseApi) ImageBuild(c *gin.Context) {
|
||||
// @Summary Pull image
|
||||
// @Accept json
|
||||
// @Param request body dto.ImagePull true "request"
|
||||
// @Success 200 {string} log
|
||||
// @Success 200 {string} logPath
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/image/pull [post]
|
||||
@@ -118,7 +118,7 @@ func (b *BaseApi) ImagePull(c *gin.Context) {
|
||||
// @Summary Push image
|
||||
// @Accept json
|
||||
// @Param request body dto.ImagePush true "request"
|
||||
// @Success 200 {string} log
|
||||
// @Success 200 {string} logPath
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/image/push [post]
|
||||
|
||||
@@ -76,7 +76,7 @@ func (b *BaseApi) CheckRepoStatus(c *gin.Context) {
|
||||
// @Tags Container Image-repo
|
||||
// @Summary Create image repo
|
||||
// @Accept json
|
||||
// @Param request body dto.ImageRepoDelete true "request"
|
||||
// @Param request body dto.ImageRepoCreate true "request"
|
||||
// @Produce json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
@@ -99,20 +99,20 @@ func (b *BaseApi) CreateRepo(c *gin.Context) {
|
||||
// @Tags Container Image-repo
|
||||
// @Summary Delete image repo
|
||||
// @Accept json
|
||||
// @Param request body dto.ImageRepoDelete true "request"
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Produce json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/repo/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"image_repos","output_column":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_column":"name","output_value":"name"}],"formatZH":"删除镜像仓库 [name]","formatEN":"delete image repo [name]"}
|
||||
func (b *BaseApi) DeleteRepo(c *gin.Context) {
|
||||
var req dto.ImageRepoDelete
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := imageRepoService.BatchDelete(req); err != nil {
|
||||
if err := imageRepoService.Delete(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func (b *BaseApi) GetLoginLogs(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := logService.PageLoginLog(req)
|
||||
total, list, err := logService.PageLoginLog(c, req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -63,7 +63,7 @@ func (b *BaseApi) GetOperationLogs(c *gin.Context) {
|
||||
// @Summary Clean operation logs
|
||||
// @Accept json
|
||||
// @Param request body dto.CleanLog true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /logs/clean [post]
|
||||
@@ -84,7 +84,7 @@ func (b *BaseApi) CleanLogs(c *gin.Context) {
|
||||
|
||||
// @Tags Logs
|
||||
// @Summary Load system log files
|
||||
// @Success 200
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /logs/system/files [get]
|
||||
@@ -100,7 +100,7 @@ func (b *BaseApi) GetSystemFiles(c *gin.Context) {
|
||||
|
||||
// @Tags Logs
|
||||
// @Summary Load system logs
|
||||
// @Success 200
|
||||
// @Success 200 {string} data
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /logs/system [post]
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags McpServer
|
||||
// @Summary List mcp servers
|
||||
// @Accept json
|
||||
// @Param request body request.McpServerSearch true "request"
|
||||
// @Success 200 {object} response.McpServersRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/search [post]
|
||||
func (b *BaseApi) PageMcpServers(c *gin.Context) {
|
||||
var req request.McpServerSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
list := mcpServerService.Page(req)
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags McpServer
|
||||
// @Summary Create mcp server
|
||||
// @Accept json
|
||||
// @Param request body request.McpServerCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/server [post]
|
||||
func (b *BaseApi) CreateMcpServer(c *gin.Context) {
|
||||
var req request.McpServerCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := mcpServerService.Create(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags McpServer
|
||||
// @Summary Update mcp server
|
||||
// @Accept json
|
||||
// @Param request body request.McpServerUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/server/update [post]
|
||||
func (b *BaseApi) UpdateMcpServer(c *gin.Context) {
|
||||
var req request.McpServerUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := mcpServerService.Update(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags McpServer
|
||||
// @Summary Delete mcp server
|
||||
// @Accept json
|
||||
// @Param request body request.McpServerDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/server/del [post]
|
||||
func (b *BaseApi) DeleteMcpServer(c *gin.Context) {
|
||||
var req request.McpServerDelete
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := mcpServerService.Delete(req.ID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags McpServer
|
||||
// @Summary Operate mcp server
|
||||
// @Accept json
|
||||
// @Param request body request.McpServerOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/server/op [post]
|
||||
func (b *BaseApi) OperateMcpServer(c *gin.Context) {
|
||||
var req request.McpServerOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := mcpServerService.Operate(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags McpServer
|
||||
// @Summary Bind Domain for mcp server
|
||||
// @Accept json
|
||||
// @Param request body request.McpBindDomain true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/domain/bind [post]
|
||||
func (b *BaseApi) BindMcpDomain(c *gin.Context) {
|
||||
var req request.McpBindDomain
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := mcpServerService.BindDomain(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags McpServer
|
||||
// @Summary Update bind Domain for mcp server
|
||||
// @Accept json
|
||||
// @Param request body request.McpBindDomainUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/domain/update [post]
|
||||
func (b *BaseApi) UpdateMcpBindDomain(c *gin.Context) {
|
||||
var req request.McpBindDomainUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := mcpServerService.UpdateBindDomain(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags McpServer
|
||||
// @Summary Get bin Domain for mcp server
|
||||
// @Accept json
|
||||
// @Success 200 {object} response.McpBindDomainRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /mcp/domain/get [get]
|
||||
func (b *BaseApi) GetMcpBindDomain(c *gin.Context) {
|
||||
res, err := mcpServerService.GetBindDomain()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
// @Tags Monitor
|
||||
// @Summary Load monitor datas
|
||||
// @Param request body dto.MonitorSearch true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {array} dto.MonitorData
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/monitor/search [post]
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// @Tags OpenResty
|
||||
// @Summary Load OpenResty conf
|
||||
// @Success 200 {object} response.FileInfo
|
||||
// @Success 200 {object} response.NginxFile
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /openresty [get]
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// @Summary Page Extensions
|
||||
// @Accept json
|
||||
// @Param request body request.PHPExtensionsSearch true "request"
|
||||
// @Success 200 {array} response.PHPExtensionsDTO
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/php/extensions/search [post]
|
||||
@@ -27,7 +27,10 @@ func (b *BaseApi) PagePHPExtensions(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Total: int64(len(list)),
|
||||
Items: list,
|
||||
})
|
||||
} else {
|
||||
total, list, err := phpExtensionsService.Page(req)
|
||||
if err != nil {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// @Summary List Recycle Bin files
|
||||
// @Accept json
|
||||
// @Param request body dto.PageInfo true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/recycle/search [post]
|
||||
@@ -72,7 +72,7 @@ func (b *BaseApi) ClearRecycleBinFile(c *gin.Context) {
|
||||
// @Tags File
|
||||
// @Summary Get Recycle Bin status
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/recycle/status [get]
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// @Summary List runtimes
|
||||
// @Accept json
|
||||
// @Param request body request.RuntimeSearch true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/search [post]
|
||||
@@ -36,7 +36,7 @@ func (b *BaseApi) SearchRuntimes(c *gin.Context) {
|
||||
// @Summary Create runtime
|
||||
// @Accept json
|
||||
// @Param request body request.RuntimeCreate true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} model.Runtime
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes [post]
|
||||
@@ -115,10 +115,10 @@ func (b *BaseApi) UpdateRuntime(c *gin.Context) {
|
||||
// @Summary Get runtime
|
||||
// @Accept json
|
||||
// @Param id path string true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.RuntimeDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/:id [get]
|
||||
// @Router /runtimes/{id} [get]
|
||||
func (b *BaseApi) GetRuntime(c *gin.Context) {
|
||||
id, err := helper.GetIntParamByKey(c, "id")
|
||||
if err != nil {
|
||||
@@ -137,7 +137,7 @@ func (b *BaseApi) GetRuntime(c *gin.Context) {
|
||||
// @Summary Get Node package scripts
|
||||
// @Accept json
|
||||
// @Param request body request.NodePackageReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {array} response.PackageScripts
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/node/package [post]
|
||||
@@ -180,7 +180,7 @@ func (b *BaseApi) OperateRuntime(c *gin.Context) {
|
||||
// @Summary Get Node modules
|
||||
// @Accept json
|
||||
// @Param request body request.NodeModuleReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {array} response.NodeModule
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /runtimes/node/modules [post]
|
||||
|
||||
@@ -3,8 +3,10 @@ package v1
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -53,11 +55,21 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if req.Key == "SecurityEntrance" {
|
||||
if !checkEntrancePattern(req.Value) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("the format of the security entrance %s is incorrect.", req.Value))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := settingService.Update(req.Key, req.Value); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if req.Key == "SecurityEntrance" {
|
||||
entranceValue := base64.StdEncoding.EncodeToString([]byte(req.Value))
|
||||
c.SetCookie("SecurityEntrance", entranceValue, 0, "", "", false, true)
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
@@ -160,7 +172,7 @@ func (b *BaseApi) UpdateSSL(c *gin.Context) {
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load system cert info
|
||||
// @Success 200 {object} dto.SettingInfo
|
||||
// @Success 200 {object} dto.SSLInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/ssl/info [get]
|
||||
@@ -192,7 +204,7 @@ func (b *BaseApi) DownloadSSL(c *gin.Context) {
|
||||
// @Tags System Setting
|
||||
// @Summary Load system address
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/interface [get]
|
||||
@@ -346,7 +358,7 @@ func (b *BaseApi) MFABind(c *gin.Context) {
|
||||
// @Tags System Setting
|
||||
// @Summary Generate api key
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Success 200 {string} apiKey
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/api/config/generate/key [post]
|
||||
@@ -391,3 +403,11 @@ func (b *BaseApi) UpdateApiConfig(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func checkEntrancePattern(val string) bool {
|
||||
if len(val) == 0 {
|
||||
return true
|
||||
}
|
||||
result, _ := regexp.MatchString("^[a-zA-Z0-9]{5,116}$", val)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func (b *BaseApi) ImportSnapshot(c *gin.Context) {
|
||||
// @Summary Load Snapshot status
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} dto.SnapshotStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/snapshot/status [post]
|
||||
@@ -124,7 +124,7 @@ func (b *BaseApi) SearchSnapshot(c *gin.Context) {
|
||||
// @Summary Load system snapshot size
|
||||
// @Accept json
|
||||
// @Param request body dto.PageSnapshot true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {array} dto.SnapshotFile
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/snapshot/size [post]
|
||||
|
||||
+11
-10
@@ -12,7 +12,7 @@ import (
|
||||
// @Success 200 {object} dto.SSHInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/search [post]
|
||||
// @Router /hosts/ssh/search [post]
|
||||
func (b *BaseApi) GetSSHInfo(c *gin.Context) {
|
||||
info, err := sshService.GetSSHInfo()
|
||||
if err != nil {
|
||||
@@ -26,9 +26,10 @@ func (b *BaseApi) GetSSHInfo(c *gin.Context) {
|
||||
// @Summary Operate SSH
|
||||
// @Accept json
|
||||
// @Param request body dto.Operate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/operate [post]
|
||||
// @Router /hosts/ssh/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] SSH ","formatEN":"[operation] SSH"}
|
||||
func (b *BaseApi) OperateSSH(c *gin.Context) {
|
||||
var req dto.Operate
|
||||
@@ -50,7 +51,7 @@ func (b *BaseApi) OperateSSH(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/update [post]
|
||||
// @Router /hosts/ssh/update [post]
|
||||
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置 [key] => [value]","formatEN":"update SSH setting [key] => [value]"}
|
||||
func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
var req dto.SSHUpdate
|
||||
@@ -72,7 +73,7 @@ func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/conffile/update [post]
|
||||
// @Router /hosts/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置文件","formatEN":"update SSH conf"}
|
||||
func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
var req dto.SSHConf
|
||||
@@ -94,7 +95,7 @@ func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/generate [post]
|
||||
// @Router /hosts/ssh/generate [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
|
||||
func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
var req dto.GenerateSSH
|
||||
@@ -113,10 +114,10 @@ func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
// @Summary Load host SSH secret
|
||||
// @Accept json
|
||||
// @Param request body dto.GenerateLoad true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} data
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/secret [post]
|
||||
// @Router /hosts/ssh/secret [post]
|
||||
func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
var req dto.GenerateLoad
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -138,7 +139,7 @@ func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
// @Success 200 {object} dto.SSHLog
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/log [post]
|
||||
// @Router /hosts/ssh/log [post]
|
||||
func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
|
||||
var req dto.SearchSSHLog
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -155,10 +156,10 @@ func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
|
||||
|
||||
// @Tags SSH
|
||||
// @Summary Load host SSH conf
|
||||
// @Success 200
|
||||
// @Success 200 {string} data
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/conf [get]
|
||||
// @Router /hosts/ssh/conf [get]
|
||||
func (b *BaseApi) LoadSSHConf(c *gin.Context) {
|
||||
data, err := sshService.LoadSSHConf()
|
||||
if err != nil {
|
||||
|
||||
@@ -68,12 +68,11 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
|
||||
<-quitChan
|
||||
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
dt := time.Now().Add(time.Second)
|
||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||
}
|
||||
|
||||
func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
@@ -95,38 +94,30 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
name := c.Query("name")
|
||||
from := c.Query("from")
|
||||
commands := []string{"redis-cli"}
|
||||
database, err := databaseService.Get(name)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
||||
return
|
||||
}
|
||||
if from == "local" {
|
||||
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
||||
source := c.Query("source")
|
||||
var containerID string
|
||||
var initCmd []string
|
||||
switch source {
|
||||
case "redis":
|
||||
containerID, initCmd, err = loadRedisInitCmd(c)
|
||||
case "ollama":
|
||||
containerID, initCmd, err = loadOllamaInitCmd(c)
|
||||
case "container":
|
||||
containerID, initCmd, err = loadContainerInitCmd(c)
|
||||
default:
|
||||
if wshandleError(wsConn, fmt.Errorf("not support such source %s", source)) {
|
||||
return
|
||||
}
|
||||
name = redisInfo.ContainerName
|
||||
if len(database.Password) != 0 {
|
||||
commands = []string{"redis-cli", "-a", database.Password, "--no-auth-warning"}
|
||||
}
|
||||
} else {
|
||||
itemPort := fmt.Sprintf("%v", database.Port)
|
||||
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort}
|
||||
if len(database.Password) != 0 {
|
||||
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort, "-a", database.Password, "--no-auth-warning"}
|
||||
}
|
||||
name = "1Panel-redis-cli-tools"
|
||||
}
|
||||
|
||||
pidMap := loadMapFromDockerTop(name)
|
||||
itemCmds := append([]string{"exec", "-it", name}, commands...)
|
||||
slave, err := terminal.NewCommand(itemCmds)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(name, strings.Join(commands, " "), pidMap)
|
||||
pidMap := loadMapFromDockerTop(containerID)
|
||||
slave, err := terminal.NewCommand(initCmd)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, strings.ReplaceAll(strings.Join(initCmd, " "), fmt.Sprintf("exec -it %s ", containerID), ""), pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||
@@ -141,83 +132,68 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
<-quitChan
|
||||
|
||||
global.LOG.Info("websocket finished")
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
dt := time.Now().Add(time.Second)
|
||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||
|
||||
}
|
||||
|
||||
func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
func loadRedisInitCmd(c *gin.Context) (string, []string, error) {
|
||||
name := c.Query("name")
|
||||
from := c.Query("from")
|
||||
commands := []string{"exec", "-it"}
|
||||
database, err := databaseService.Get(name)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
return "", nil, fmt.Errorf("no such database in db, err: %v", err)
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
if global.CONF.System.IsDemo {
|
||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||
return
|
||||
if from == "local" {
|
||||
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
|
||||
}
|
||||
name = redisInfo.ContainerName
|
||||
commands = append(commands, []string{name, "redis-cli"}...)
|
||||
if len(database.Password) != 0 {
|
||||
commands = append(commands, []string{"-a", database.Password, "--no-auth-warning"}...)
|
||||
}
|
||||
} else {
|
||||
name = "1Panel-redis-cli-tools"
|
||||
commands = append(commands, []string{name, "redis-cli", "-h", database.Address, "-p", fmt.Sprintf("%v", database.Port)}...)
|
||||
if len(database.Password) != 0 {
|
||||
commands = append(commands, []string{"-a", database.Password, "--no-auth-warning"}...)
|
||||
}
|
||||
}
|
||||
return name, commands, nil
|
||||
}
|
||||
|
||||
func loadOllamaInitCmd(c *gin.Context) (string, []string, error) {
|
||||
name := c.Query("name")
|
||||
if cmd.CheckIllegal(name) {
|
||||
return "", nil, fmt.Errorf("ollama model %s contains illegal characters", name)
|
||||
}
|
||||
ollamaInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: "", Type: "ollama"})
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
|
||||
}
|
||||
containerName := ollamaInfo.ContainerName
|
||||
return containerName, []string{"exec", "-it", containerName, "ollama", "run", name}, nil
|
||||
}
|
||||
|
||||
func loadContainerInitCmd(c *gin.Context) (string, []string, error) {
|
||||
containerID := c.Query("containerid")
|
||||
command := c.Query("command")
|
||||
user := c.Query("user")
|
||||
if len(command) == 0 || len(containerID) == 0 {
|
||||
if wshandleError(wsConn, errors.New("error param of command or containerID")) {
|
||||
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
|
||||
}
|
||||
|
||||
cmds := []string{"exec", containerID, command}
|
||||
if len(user) != 0 {
|
||||
cmds = []string{"exec", "-u", user, containerID, command}
|
||||
}
|
||||
if cmd.CheckIllegal(user, containerID, command) {
|
||||
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
|
||||
return
|
||||
}
|
||||
return "", nil, fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
|
||||
}
|
||||
stdout, err := cmd.ExecWithCheck("docker", cmds...)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, stdout)) {
|
||||
return
|
||||
if len(command) == 0 || len(containerID) == 0 {
|
||||
return "", nil, fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
|
||||
}
|
||||
|
||||
commands := []string{"exec", "-it", containerID, command}
|
||||
if len(user) != 0 {
|
||||
commands = []string{"exec", "-it", "-u", user, containerID, command}
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(containerID)
|
||||
slave, err := terminal.NewCommand(commands)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, command, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
tty.Start(quitChan)
|
||||
go slave.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
|
||||
global.LOG.Info("websocket finished")
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
return containerID, commands, nil
|
||||
}
|
||||
|
||||
func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
|
||||
@@ -26,7 +26,7 @@ func (b *BaseApi) GetUpgradeInfo(c *gin.Context) {
|
||||
// @Summary Load release notes by version
|
||||
// @Accept json
|
||||
// @Param request body dto.Upgrade true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {string} notes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/upgrade [get]
|
||||
|
||||
@@ -51,7 +51,7 @@ func (b *BaseApi) GetWebsites(c *gin.Context) {
|
||||
|
||||
// @Tags Website
|
||||
// @Summary List website names
|
||||
// @Success 200 {array} string
|
||||
// @Success 200 {array} response.WebsiteOption
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/options [get]
|
||||
@@ -166,7 +166,7 @@ func (b *BaseApi) UpdateWebsite(c *gin.Context) {
|
||||
// @Success 200 {object} response.WebsiteDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/:id [get]
|
||||
// @Router /websites/{id} [get]
|
||||
func (b *BaseApi) GetWebsite(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
@@ -185,10 +185,11 @@ func (b *BaseApi) GetWebsite(c *gin.Context) {
|
||||
// @Summary Search website nginx by id
|
||||
// @Accept json
|
||||
// @Param id path integer true "request"
|
||||
// @Param type path string true "type"
|
||||
// @Success 200 {object} response.FileInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/:id/config/:type [get]
|
||||
// @Router /websites/{id}/config/{type} [get]
|
||||
func (b *BaseApi) GetWebsiteNginx(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
@@ -254,7 +255,7 @@ func (b *BaseApi) UpdateNginxConfig(c *gin.Context) {
|
||||
// @Success 200 {object} response.WebsiteHTTPS
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/:id/https [get]
|
||||
// @Router /websites/{id}/https [get]
|
||||
func (b *BaseApi) GetHTTPSConfig(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
@@ -272,11 +273,12 @@ func (b *BaseApi) GetHTTPSConfig(c *gin.Context) {
|
||||
// @Tags Website HTTPS
|
||||
// @Summary Update https conf
|
||||
// @Accept json
|
||||
// @Param id path integer true "request"
|
||||
// @Param request body request.WebsiteHTTPSOp true "request"
|
||||
// @Success 200 {object} response.WebsiteHTTPS
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @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"}
|
||||
func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {
|
||||
var req request.WebsiteHTTPSOp
|
||||
@@ -386,7 +388,7 @@ func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
|
||||
// @Success 200 {object} response.PHPConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/php/config/:id [get]
|
||||
// @Router /websites/php/config/{id} [get]
|
||||
func (b *BaseApi) GetWebsitePHPConfig(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
@@ -468,7 +470,7 @@ func (b *BaseApi) ChangePHPVersion(c *gin.Context) {
|
||||
// @Summary Get rewrite conf
|
||||
// @Accept json
|
||||
// @Param request body request.NginxRewriteReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.NginxRewriteRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/rewrite [post]
|
||||
@@ -552,7 +554,7 @@ func (b *BaseApi) UpdateSiteDirPermission(c *gin.Context) {
|
||||
// @Summary Get proxy conf
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {array} request.WebsiteProxyConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/proxies [post]
|
||||
@@ -569,6 +571,28 @@ func (b *BaseApi) GetProxyConfig(c *gin.Context) {
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Delete proxy conf
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyDel true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/proxies/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除网站 [domain] 反向代理配置","formatEN":"Delete domain [domain] proxy config"}
|
||||
func (b *BaseApi) DeleteProxyConfig(c *gin.Context) {
|
||||
var req request.WebsiteProxyDel
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := websiteService.DeleteProxy(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update proxy conf
|
||||
// @Accept json
|
||||
@@ -616,7 +640,7 @@ func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {
|
||||
// @Summary Get AuthBasic conf
|
||||
// @Accept json
|
||||
// @Param request body request.NginxAuthReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.NginxAuthRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/auths [post]
|
||||
@@ -657,7 +681,7 @@ func (b *BaseApi) UpdateAuthConfig(c *gin.Context) {
|
||||
// @Summary Get AntiLeech conf
|
||||
// @Accept json
|
||||
// @Param request body request.NginxCommonReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.NginxAntiLeechRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/leech [post]
|
||||
@@ -720,7 +744,7 @@ func (b *BaseApi) UpdateRedirectConfig(c *gin.Context) {
|
||||
// @Summary Get redirect conf
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {array} response.NginxRedirectConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/redirect [post]
|
||||
@@ -762,7 +786,7 @@ func (b *BaseApi) UpdateRedirectConfigFile(c *gin.Context) {
|
||||
// @Summary Get website dir
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteCommonReq true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.WebsiteDirConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/dir [post]
|
||||
@@ -782,10 +806,11 @@ func (b *BaseApi) GetDirConfig(c *gin.Context) {
|
||||
// @Tags Website
|
||||
// @Summary Get default html
|
||||
// @Accept json
|
||||
// @Success 200 {object} response.FileInfo
|
||||
// @Param type path string true "type"
|
||||
// @Success 200 {object} response.WebsiteHtmlRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/default/html/:type [get]
|
||||
// @Router /websites/default/html/{type} [get]
|
||||
func (b *BaseApi) GetDefaultHtml(c *gin.Context) {
|
||||
resourceType, err := helper.GetStrParamByKey(c, "type")
|
||||
if err != nil {
|
||||
|
||||
@@ -32,7 +32,7 @@ func (b *BaseApi) DeleteWebDomain(c *gin.Context) {
|
||||
// @Summary Create website domain
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteDomainCreate true "request"
|
||||
// @Success 200 {object} model.WebsiteDomain
|
||||
// @Success 200 {array} model.WebsiteDomain
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/domains [post]
|
||||
@@ -57,7 +57,7 @@ func (b *BaseApi) CreateWebDomain(c *gin.Context) {
|
||||
// @Success 200 {array} model.WebsiteDomain
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/domains/:websiteId [get]
|
||||
// @Router /websites/domains/{websiteId} [get]
|
||||
func (b *BaseApi) GetWebDomains(c *gin.Context) {
|
||||
websiteId, err := helper.GetIntParamByKey(c, "websiteId")
|
||||
if err != nil {
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// @Summary Page website ssl
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteSSLSearch true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {array} response.WebsiteSSLDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/ssl/search [post]
|
||||
@@ -135,10 +135,10 @@ func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
|
||||
// @Summary Search website ssl by website id
|
||||
// @Accept json
|
||||
// @Param websiteId path integer true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.WebsiteSSLDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/ssl/website/:websiteId [get]
|
||||
// @Router /websites/ssl/website/{websiteId} [get]
|
||||
func (b *BaseApi) GetWebsiteSSLByWebsiteId(c *gin.Context) {
|
||||
websiteId, err := helper.GetIntParamByKey(c, "websiteId")
|
||||
if err != nil {
|
||||
@@ -157,10 +157,10 @@ func (b *BaseApi) GetWebsiteSSLByWebsiteId(c *gin.Context) {
|
||||
// @Summary Search website ssl by id
|
||||
// @Accept json
|
||||
// @Param id path integer true "request"
|
||||
// @Success 200
|
||||
// @Success 200 {object} response.WebsiteSSLDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/ssl/:id [get]
|
||||
// @Router /websites/ssl/{id} [get]
|
||||
func (b *BaseApi) GetWebsiteSSLById(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type OllamaModelInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Size string `json:"size"`
|
||||
From string `json:"from"`
|
||||
LogFileExist bool `json:"logFileExist"`
|
||||
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
type OllamaModelDropList struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type OllamaModelName struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type OllamaBindDomain struct {
|
||||
Domain string `json:"domain" validate:"required"`
|
||||
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||
SSLID uint `json:"sslID"`
|
||||
WebsiteID uint `json:"websiteID"`
|
||||
IPList string `json:"ipList"`
|
||||
}
|
||||
|
||||
type OllamaBindDomainReq struct {
|
||||
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||
}
|
||||
|
||||
type OllamaBindDomainRes struct {
|
||||
Domain string `json:"domain"`
|
||||
SSLID uint `json:"sslID"`
|
||||
AcmeAccountID uint `json:"acmeAccountID"`
|
||||
AllowIPs []string `json:"allowIPs"`
|
||||
WebsiteID uint `json:"websiteID"`
|
||||
ConnUrl string `json:"connUrl"`
|
||||
}
|
||||
+21
-10
@@ -1,9 +1,5 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
|
||||
type AppDatabase struct {
|
||||
ServiceName string `json:"PANEL_DB_HOST"`
|
||||
DbName string `json:"PANEL_DB_NAME"`
|
||||
@@ -62,7 +58,7 @@ type AppDefine struct {
|
||||
}
|
||||
|
||||
type LocalAppAppDefine struct {
|
||||
AppProperty model.App `json:"additionalProperties" yaml:"additionalProperties"`
|
||||
AppProperty AppProperty `json:"additionalProperties" yaml:"additionalProperties"`
|
||||
}
|
||||
|
||||
type LocalAppParam struct {
|
||||
@@ -82,8 +78,9 @@ type AppProperty struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Tags []string `json:"tags"`
|
||||
ShortDescZh string `json:"shortDescZh"`
|
||||
ShortDescEn string `json:"shortDescEn"`
|
||||
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh"`
|
||||
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn"`
|
||||
Description Locale `json:"description"`
|
||||
Key string `json:"key"`
|
||||
Required []string `json:"Required"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
@@ -93,6 +90,7 @@ type AppProperty struct {
|
||||
Github string `json:"github"`
|
||||
Document string `json:"document"`
|
||||
Version float64 `json:"version"`
|
||||
GpuSupport bool `json:"gpuSupport"`
|
||||
}
|
||||
|
||||
type AppConfigVersion struct {
|
||||
@@ -104,9 +102,21 @@ type AppConfigVersion struct {
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
Sort int `json:"sort"`
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
Sort int `json:"sort"`
|
||||
Locales Locale `json:"locales"`
|
||||
}
|
||||
|
||||
type Locale struct {
|
||||
En string `json:"en"`
|
||||
Ja string `json:"ja"`
|
||||
Ms string `json:"ms"`
|
||||
PtBr string `json:"pt-br" yaml:"pt-br"`
|
||||
Ru string `json:"ru"`
|
||||
ZhHant string `json:"zh-hant" yaml:"zh-hant"`
|
||||
Zh string `json:"zh"`
|
||||
Ko string `json:"ko"`
|
||||
}
|
||||
|
||||
type AppForm struct {
|
||||
@@ -118,6 +128,7 @@ type AppFormFields struct {
|
||||
Type string `json:"type"`
|
||||
LabelZh string `json:"labelZh"`
|
||||
LabelEn string `json:"labelEn"`
|
||||
Label Locale `json:"label"`
|
||||
Required bool `json:"required"`
|
||||
Default interface{} `json:"default"`
|
||||
EnvKey string `json:"envKey"`
|
||||
|
||||
+11
-7
@@ -23,13 +23,12 @@ type MfaCredential struct {
|
||||
}
|
||||
|
||||
type Login struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
IgnoreCaptcha bool `json:"ignoreCaptcha"`
|
||||
Captcha string `json:"captcha"`
|
||||
CaptchaID string `json:"captchaID"`
|
||||
AuthMethod string `json:"authMethod" validate:"required,oneof=jwt session"`
|
||||
Language string `json:"language" validate:"required,oneof=zh en tw"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Captcha string `json:"captcha"`
|
||||
CaptchaID string `json:"captchaID"`
|
||||
AuthMethod string `json:"authMethod" validate:"required,oneof=jwt session"`
|
||||
Language string `json:"language" validate:"required,oneof=zh en tw ja ko ru ms 'pt-BR'"`
|
||||
}
|
||||
|
||||
type MFALogin struct {
|
||||
@@ -38,3 +37,8 @@ type MFALogin struct {
|
||||
Code string `json:"code" validate:"required"`
|
||||
AuthMethod string `json:"authMethod"`
|
||||
}
|
||||
|
||||
type LoginSetting struct {
|
||||
NeedCaptcha bool `json:"needCaptcha"`
|
||||
Language string `json:"language"`
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@ type BackupInfo struct {
|
||||
Vars string `json:"vars"`
|
||||
}
|
||||
|
||||
type BackupFile struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type OneDriveInfo struct {
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
@@ -66,7 +72,6 @@ type BackupRecords struct {
|
||||
BackupType string `json:"backupType"`
|
||||
FileDir string `json:"fileDir"`
|
||||
FileName string `json:"fileName"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type DownloadRecord struct {
|
||||
|
||||
@@ -52,3 +52,8 @@ type OperationWithNameAndType struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type ForceDelete struct {
|
||||
IDs []uint `json:"ids"`
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
}
|
||||
|
||||
@@ -71,14 +71,16 @@ type CronjobDownload struct {
|
||||
}
|
||||
|
||||
type CronjobClean struct {
|
||||
IsDelete bool `json:"isDelete"`
|
||||
CleanData bool `json:"cleanData"`
|
||||
CronjobID uint `json:"cronjobID" validate:"required"`
|
||||
IsDelete bool `json:"isDelete"`
|
||||
CleanData bool `json:"cleanData"`
|
||||
CleanRemoteData bool `json:"cleanRemoteData"`
|
||||
CronjobID uint `json:"cronjobID" validate:"required"`
|
||||
}
|
||||
|
||||
type CronjobBatchDelete struct {
|
||||
CleanData bool `json:"cleanData"`
|
||||
IDs []uint `json:"ids" validate:"required"`
|
||||
CleanData bool `json:"cleanData"`
|
||||
CleanRemoteData bool `json:"cleanRemoteData"`
|
||||
IDs []uint `json:"ids" validate:"required"`
|
||||
}
|
||||
|
||||
type CronjobInfo struct {
|
||||
|
||||
@@ -19,7 +19,7 @@ type Fail2BanSearch struct {
|
||||
}
|
||||
|
||||
type Fail2BanUpdate struct {
|
||||
Key string `json:"key" validate:"required,oneof=port bantime findtime maxretry banaction logpath port"`
|
||||
Key string `json:"key" validate:"required,oneof=port bantime findtime maxretry banaction logpath"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ type PortRuleOperate struct {
|
||||
}
|
||||
|
||||
type ForwardRuleOperate struct {
|
||||
Rules []struct {
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
Rules []struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=add remove"`
|
||||
Num string `json:"num"`
|
||||
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
|
||||
|
||||
@@ -6,8 +6,8 @@ type ImageRepoCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
Protocol string `json:"protocol"`
|
||||
Username string `json:"username" validate:"max=256"`
|
||||
Password string `json:"password" validate:"max=256"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Auth bool `json:"auth"`
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ type ImageRepoUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
Protocol string `json:"protocol"`
|
||||
Username string `json:"username" validate:"max=256"`
|
||||
Password string `json:"password" validate:"max=256"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Auth bool `json:"auth"`
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package dto
|
||||
|
||||
type DockerComposeService struct {
|
||||
Image string `yaml:"image"`
|
||||
ContainerName string `yaml:"container_name"`
|
||||
Restart string `yaml:"restart"`
|
||||
Ports []string `yaml:"ports"`
|
||||
Environment []string `yaml:"environment"`
|
||||
Command []string `yaml:"command"`
|
||||
}
|
||||
@@ -33,6 +33,7 @@ type AppContainerConfig struct {
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
HostMode bool `json:"hostMode"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
GpuConfig bool `json:"gpuConfig"`
|
||||
}
|
||||
|
||||
type AppInstalledSearch struct {
|
||||
|
||||
@@ -76,7 +76,12 @@ type FileRename struct {
|
||||
}
|
||||
|
||||
type FilePathCheck struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
WithInit bool `json:"withInit"`
|
||||
}
|
||||
|
||||
type FilePathsCheck struct {
|
||||
Paths []string `json:"paths" validate:"required"`
|
||||
}
|
||||
|
||||
type FileWget struct {
|
||||
@@ -87,11 +92,12 @@ type FileWget struct {
|
||||
}
|
||||
|
||||
type FileMove struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
OldPaths []string `json:"oldPaths" validate:"required"`
|
||||
NewPath string `json:"newPath" validate:"required"`
|
||||
Name string `json:"name"`
|
||||
Cover bool `json:"cover"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
OldPaths []string `json:"oldPaths" validate:"required"`
|
||||
NewPath string `json:"newPath" validate:"required"`
|
||||
Name string `json:"name"`
|
||||
Cover bool `json:"cover"`
|
||||
CoverPaths []string `json:"coverPaths"`
|
||||
}
|
||||
|
||||
type FileDownload struct {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package request
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
|
||||
type McpServerSearch struct {
|
||||
dto.PageInfo
|
||||
Name string `json:"name"`
|
||||
Sync bool `json:"sync"`
|
||||
}
|
||||
|
||||
type McpServerCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
Environments []Environment `json:"environments"`
|
||||
Volumes []Volume `json:"volumes"`
|
||||
Port int `json:"port" validate:"required"`
|
||||
ContainerName string `json:"containerName"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
SsePath string `json:"ssePath"`
|
||||
HostIP string `json:"hostIP"`
|
||||
}
|
||||
|
||||
type McpServerUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
McpServerCreate
|
||||
}
|
||||
|
||||
type Environment struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type Volume struct {
|
||||
Source string `json:"source"`
|
||||
Target string `json:"target"`
|
||||
}
|
||||
|
||||
type McpServerDelete struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type McpServerOperate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
}
|
||||
|
||||
type McpBindDomain struct {
|
||||
Domain string `json:"domain" validate:"required"`
|
||||
SSLID uint `json:"sslID"`
|
||||
IPList string `json:"ipList"`
|
||||
}
|
||||
|
||||
type McpBindDomainUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
SSLID uint `json:"sslID"`
|
||||
IPList string `json:"ipList"`
|
||||
}
|
||||
@@ -193,6 +193,11 @@ type WebsiteProxyConfig struct {
|
||||
ProxySSLName string `json:"proxySSLName"`
|
||||
}
|
||||
|
||||
type WebsiteProxyDel struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteProxyReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
)
|
||||
|
||||
type AppRes struct {
|
||||
Items []*AppDto `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
Items []*AppItem `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
type AppUpdateRes struct {
|
||||
@@ -23,29 +23,31 @@ type AppUpdateRes struct {
|
||||
|
||||
type AppDTO struct {
|
||||
model.App
|
||||
Installed bool `json:"installed"`
|
||||
Versions []string `json:"versions"`
|
||||
Tags []model.Tag `json:"tags"`
|
||||
Installed bool `json:"installed"`
|
||||
Versions []string `json:"versions"`
|
||||
Tags []TagDTO `json:"tags"`
|
||||
}
|
||||
|
||||
type AppDto struct {
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
ID uint `json:"id"`
|
||||
ShortDescZh string `json:"shortDescZh"`
|
||||
ShortDescEn string `json:"shortDescEn"`
|
||||
Icon string `json:"icon"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Resource string `json:"resource"`
|
||||
Installed bool `json:"installed"`
|
||||
Versions []string `json:"versions"`
|
||||
Limit int `json:"limit"`
|
||||
Tags []model.Tag `json:"tags"`
|
||||
type AppItem struct {
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
ID uint `json:"id"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Resource string `json:"resource"`
|
||||
Installed bool `json:"installed"`
|
||||
Versions []string `json:"versions"`
|
||||
Limit int `json:"limit"`
|
||||
Tags []TagDTO `json:"tags"`
|
||||
GpuSupport bool `json:"gpuSupport"`
|
||||
}
|
||||
|
||||
type TagDTO struct {
|
||||
model.Tag
|
||||
ID uint `json:"id"`
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type AppInstalledCheck struct {
|
||||
@@ -65,10 +67,11 @@ type AppInstalledCheck struct {
|
||||
|
||||
type AppDetailDTO struct {
|
||||
model.AppDetail
|
||||
Enable bool `json:"enable"`
|
||||
Params interface{} `json:"params"`
|
||||
Image string `json:"image"`
|
||||
HostMode bool `json:"hostMode"`
|
||||
Enable bool `json:"enable"`
|
||||
Params interface{} `json:"params"`
|
||||
Image string `json:"image"`
|
||||
HostMode bool `json:"hostMode"`
|
||||
GpuSupport bool `json:"gpuSupport"`
|
||||
}
|
||||
|
||||
type IgnoredApp struct {
|
||||
@@ -89,9 +92,10 @@ type AppInstalledDTO struct {
|
||||
}
|
||||
|
||||
type AppDetail struct {
|
||||
Website string `json:"website"`
|
||||
Document string `json:"document"`
|
||||
Github string `json:"github"`
|
||||
Website string `json:"website"`
|
||||
Document string `json:"document"`
|
||||
Github string `json:"github"`
|
||||
GpuSupport bool `json:"gpuSupport"`
|
||||
}
|
||||
|
||||
type AppInstallDTO struct {
|
||||
|
||||
@@ -2,6 +2,7 @@ package response
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FileInfo struct {
|
||||
@@ -46,3 +47,11 @@ type FileLineContent struct {
|
||||
type FileExist struct {
|
||||
Exist bool `json:"exist"`
|
||||
}
|
||||
|
||||
type ExistFileInfo struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Size float64 `json:"size"`
|
||||
ModTime time.Time `json:"modTime"`
|
||||
IsDir bool `json:"isDir"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
|
||||
type McpServersRes struct {
|
||||
Items []McpServerDTO `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
type McpServerDTO struct {
|
||||
model.McpServer
|
||||
Environments []request.Environment `json:"environments"`
|
||||
Volumes []request.Volume `json:"volumes"`
|
||||
}
|
||||
|
||||
type McpBindDomainRes struct {
|
||||
Domain string `json:"domain"`
|
||||
SSLID uint `json:"sslID"`
|
||||
AcmeAccountID uint `json:"acmeAccountID"`
|
||||
AllowIPs []string `json:"allowIPs"`
|
||||
WebsiteID uint `json:"websiteID"`
|
||||
ConnUrl string `json:"connUrl"`
|
||||
}
|
||||
@@ -70,6 +70,8 @@ type SettingInfo struct {
|
||||
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
IpWhiteList string `json:"ipWhiteList"`
|
||||
ApiKeyValidityTime string `json:"apiKeyValidityTime"`
|
||||
LicenseVerify string `json:"licenseVerify"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
@@ -240,4 +242,5 @@ type ApiInterfaceConfig struct {
|
||||
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
IpWhiteList string `json:"ipWhiteList"`
|
||||
ApiKeyValidityTime string `json:"apiKeyValidityTime"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package model
|
||||
|
||||
type OllamaModel struct {
|
||||
BaseModel
|
||||
|
||||
Name string `json:"name"`
|
||||
Size string `json:"size"`
|
||||
From string `json:"from"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
@@ -12,10 +15,12 @@ type App struct {
|
||||
Key string `json:"key" gorm:"type:varchar(64);not null;"`
|
||||
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh" gorm:"type:longtext;"`
|
||||
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn" gorm:"type:longtext;"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon" gorm:"type:longtext;"`
|
||||
Type string `json:"type" gorm:"type:varchar(64);not null"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
Required string `json:"required" gorm:"type:varchar(64);"`
|
||||
GpuSupport bool `json:"gpuSupport"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"`
|
||||
Limit int `json:"limit" gorm:"type:Integer;not null"`
|
||||
Website string `json:"website" gorm:"type:varchar(64);not null"`
|
||||
@@ -36,8 +41,20 @@ func (i *App) IsLocalApp() bool {
|
||||
}
|
||||
func (i *App) GetAppResourcePath() string {
|
||||
if i.IsLocalApp() {
|
||||
//这里要去掉本地应用的local前缀
|
||||
return filepath.Join(constant.LocalAppResourceDir, strings.TrimPrefix(i.Key, "local"))
|
||||
}
|
||||
return filepath.Join(constant.RemoteAppResourceDir, i.Key)
|
||||
}
|
||||
|
||||
func (i *App) GetDescription(ctx *gin.Context) string {
|
||||
var translations = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(i.Description), &translations)
|
||||
lang := strings.ToLower(common.GetLang(ctx))
|
||||
if desc, ok := translations[lang]; ok && desc != "" {
|
||||
return desc
|
||||
}
|
||||
if lang == "zh" {
|
||||
return i.ShortDescZh
|
||||
}
|
||||
return i.ShortDescEn
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package model
|
||||
|
||||
type McpServer struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Message string `json:"message"`
|
||||
Port int `json:"port"`
|
||||
Status string `json:"status"`
|
||||
Env string `json:"env"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
SsePath string `json:"ssePath"`
|
||||
WebsiteID int `json:"websiteID"`
|
||||
Dir string `json:"dir"`
|
||||
HostIP string `json:"hostIP"`
|
||||
}
|
||||
@@ -2,7 +2,8 @@ package model
|
||||
|
||||
type Tag struct {
|
||||
BaseModel
|
||||
Key string `json:"key" gorm:"type:varchar(64);not null"`
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||
Sort int `json:"sort" gorm:"type:int;not null;default:1"`
|
||||
Key string `json:"key" gorm:"type:varchar(64);not null"`
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||
Translations string `json:"translations"`
|
||||
Sort int `json:"sort" gorm:"type:int;not null;default:1"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
)
|
||||
|
||||
type AiRepo struct{}
|
||||
|
||||
type IAiRepo interface {
|
||||
Get(opts ...DBOption) (model.OllamaModel, error)
|
||||
List(opts ...DBOption) ([]model.OllamaModel, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.OllamaModel, error)
|
||||
Create(cronjob *model.OllamaModel) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIAiRepo() IAiRepo {
|
||||
return &AiRepo{}
|
||||
}
|
||||
|
||||
func (u *AiRepo) Get(opts ...DBOption) (model.OllamaModel, error) {
|
||||
var item model.OllamaModel
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&item).Error
|
||||
return item, err
|
||||
}
|
||||
|
||||
func (u *AiRepo) List(opts ...DBOption) ([]model.OllamaModel, error) {
|
||||
var list []model.OllamaModel
|
||||
db := global.DB.Model(&model.OllamaModel{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&list).Error
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (u *AiRepo) Page(page, size int, opts ...DBOption) (int64, []model.OllamaModel, error) {
|
||||
var list []model.OllamaModel
|
||||
db := global.DB.Model(&model.OllamaModel{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&list).Error
|
||||
return count, list, err
|
||||
}
|
||||
|
||||
func (u *AiRepo) Create(item *model.OllamaModel) error {
|
||||
return global.DB.Create(item).Error
|
||||
}
|
||||
|
||||
func (u *AiRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.OllamaModel{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *AiRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.OllamaModel{}).Error
|
||||
}
|
||||
@@ -15,6 +15,7 @@ type DBOption func(*gorm.DB) *gorm.DB
|
||||
type ICommonRepo interface {
|
||||
WithByID(id uint) DBOption
|
||||
WithByName(name string) DBOption
|
||||
WithByLowerName(name string) DBOption
|
||||
WithByType(tp string) DBOption
|
||||
WithOrderBy(orderStr string) DBOption
|
||||
WithOrderRuleBy(orderBy, order string) DBOption
|
||||
@@ -45,6 +46,12 @@ func (c *CommonRepo) WithByName(name string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommonRepo) WithByLowerName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("LOWER(name) = LOWER(?)", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommonRepo) WithByDate(startTime, endTime time.Time) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("start_time > ? AND start_time < ?", startTime, endTime)
|
||||
|
||||
@@ -16,6 +16,7 @@ type IGroupRepo interface {
|
||||
Delete(opts ...DBOption) error
|
||||
CancelDefault(groupType string) error
|
||||
WithByHostDefault() DBOption
|
||||
WithByWebsiteDefault() DBOption
|
||||
}
|
||||
|
||||
func NewIGroupRepo() IGroupRepo {
|
||||
@@ -56,6 +57,12 @@ func (u *GroupRepo) WithByHostDefault() DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *GroupRepo) WithByWebsiteDefault() DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("is_default = ? AND type = ?", 1, "website")
|
||||
}
|
||||
}
|
||||
|
||||
func (u *GroupRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package repo
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
|
||||
type McpServerRepo struct {
|
||||
}
|
||||
|
||||
type IMcpServerRepo interface {
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.McpServer, error)
|
||||
GetFirst(opts ...DBOption) (*model.McpServer, error)
|
||||
Create(mcpServer *model.McpServer) error
|
||||
Save(mcpServer *model.McpServer) error
|
||||
DeleteBy(opts ...DBOption) error
|
||||
List(opts ...DBOption) ([]model.McpServer, error)
|
||||
}
|
||||
|
||||
func NewIMcpServerRepo() IMcpServerRepo {
|
||||
return &McpServerRepo{}
|
||||
}
|
||||
|
||||
func (m McpServerRepo) Page(page, size int, opts ...DBOption) (int64, []model.McpServer, error) {
|
||||
var servers []model.McpServer
|
||||
db := getDb(opts...).Model(&model.McpServer{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&servers).Error
|
||||
return count, servers, err
|
||||
}
|
||||
|
||||
func (m McpServerRepo) GetFirst(opts ...DBOption) (*model.McpServer, error) {
|
||||
var mcpServer model.McpServer
|
||||
if err := getDb(opts...).First(&mcpServer).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mcpServer, nil
|
||||
}
|
||||
|
||||
func (m McpServerRepo) List(opts ...DBOption) ([]model.McpServer, error) {
|
||||
var mcpServers []model.McpServer
|
||||
if err := getDb(opts...).Find(&mcpServers).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mcpServers, nil
|
||||
}
|
||||
|
||||
func (m McpServerRepo) Create(mcpServer *model.McpServer) error {
|
||||
return getDb().Create(mcpServer).Error
|
||||
}
|
||||
|
||||
func (m McpServerRepo) Save(mcpServer *model.McpServer) error {
|
||||
return getDb().Save(mcpServer).Error
|
||||
}
|
||||
|
||||
func (m McpServerRepo) DeleteBy(opts ...DBOption) error {
|
||||
return getDb(opts...).Delete(&model.McpServer{}).Error
|
||||
}
|
||||
@@ -16,6 +16,7 @@ type ISettingRepo interface {
|
||||
Create(key, value string) error
|
||||
Update(key, value string) error
|
||||
WithByKey(key string) DBOption
|
||||
UpdateOrCreate(key, value string) error
|
||||
|
||||
CreateMonitorBase(model model.MonitorBase) error
|
||||
BatchCreateMonitorIO(ioList []model.MonitorIO) error
|
||||
@@ -85,3 +86,7 @@ func (u *SettingRepo) DelMonitorIO(timeForDelete time.Time) error {
|
||||
func (u *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
}
|
||||
|
||||
func (u *SettingRepo) UpdateOrCreate(key, value string) error {
|
||||
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Assign(model.Setting{Key: key, Value: value}).FirstOrCreate(&model.Setting{}).Error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type AIToolService struct{}
|
||||
|
||||
type IAIToolService interface {
|
||||
Search(search dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error)
|
||||
Create(name string) error
|
||||
Close(name string) error
|
||||
Recreate(name string) error
|
||||
Delete(req dto.ForceDelete) error
|
||||
Sync() ([]dto.OllamaModelDropList, error)
|
||||
LoadDetail(name string) (string, error)
|
||||
BindDomain(req dto.OllamaBindDomain) error
|
||||
GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error)
|
||||
UpdateBindDomain(req dto.OllamaBindDomain) error
|
||||
}
|
||||
|
||||
func NewIAIToolService() IAIToolService {
|
||||
return &AIToolService{}
|
||||
}
|
||||
|
||||
func (u *AIToolService) Search(req dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error) {
|
||||
var options []repo.DBOption
|
||||
if len(req.Info) != 0 {
|
||||
options = append(options, commonRepo.WithLikeName(req.Info))
|
||||
}
|
||||
total, list, err := aiRepo.Page(req.Page, req.PageSize, options...)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var dtoLists []dto.OllamaModelInfo
|
||||
for _, itemModel := range list {
|
||||
var item dto.OllamaModelInfo
|
||||
if err := copier.Copy(&item, &itemModel); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
logPath := path.Join(global.CONF.System.DataDir, "log", "AITools", itemModel.Name)
|
||||
if _, err := os.Stat(logPath); err == nil {
|
||||
item.LogFileExist = true
|
||||
}
|
||||
dtoLists = append(dtoLists, item)
|
||||
}
|
||||
return int64(total), dtoLists, err
|
||||
}
|
||||
|
||||
func (u *AIToolService) LoadDetail(name string) (string, error) {
|
||||
if cmd.CheckIllegal(name) {
|
||||
return "", buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.Execf("docker exec %s ollama show %s", containerName, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func (u *AIToolService) Create(name string) error {
|
||||
if cmd.CheckIllegal(name) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
modelInfo, _ := aiRepo.Get(commonRepo.WithByName(name))
|
||||
if modelInfo.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logItem := path.Join(global.CONF.System.DataDir, "log", "AITools", name)
|
||||
if _, err := os.Stat(path.Dir(logItem)); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(logItem), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
info := model.OllamaModel{
|
||||
Name: name,
|
||||
From: "local",
|
||||
Status: constant.StatusWaiting,
|
||||
}
|
||||
if err := aiRepo.Create(&info); err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go pullOllamaModel(file, containerName, info)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AIToolService) Close(name string) error {
|
||||
if cmd.CheckIllegal(name) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdout, err := cmd.Execf("docker exec %s ollama stop %s", containerName, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle ollama stop %s failed, stdout: %s, err: %v", name, stdout, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AIToolService) Recreate(name string) error {
|
||||
if cmd.CheckIllegal(name) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
modelInfo, _ := aiRepo.Get(commonRepo.WithByName(name))
|
||||
if modelInfo.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := aiRepo.Update(modelInfo.ID, map[string]interface{}{"status": constant.StatusWaiting, "from": "local"}); err != nil {
|
||||
return err
|
||||
}
|
||||
logItem := path.Join(global.CONF.System.DataDir, "log", "AITools", name)
|
||||
if _, err := os.Stat(path.Dir(logItem)); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(logItem), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go pullOllamaModel(file, containerName, modelInfo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AIToolService) Delete(req dto.ForceDelete) error {
|
||||
ollamaList, _ := aiRepo.List(commonRepo.WithIdsIn(req.IDs))
|
||||
if len(ollamaList) == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
for _, item := range ollamaList {
|
||||
if item.Status != constant.StatusDeleted {
|
||||
stdout, err := cmd.Execf("docker exec %s ollama rm %s", containerName, item.Name)
|
||||
if err != nil && !req.ForceDelete {
|
||||
return fmt.Errorf("handle ollama rm %s failed, stdout: %s, err: %v", item.Name, stdout, err)
|
||||
}
|
||||
}
|
||||
_ = aiRepo.Delete(commonRepo.WithByID(item.ID))
|
||||
logItem := path.Join(global.CONF.System.DataDir, "log", "AITools", item.Name)
|
||||
_ = os.Remove(logItem)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AIToolService) Sync() ([]dto.OllamaModelDropList, error) {
|
||||
containerName, err := LoadContainerName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stdout, err := cmd.Execf("docker exec %s ollama list", containerName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []model.OllamaModel
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 5 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == "NAME" {
|
||||
continue
|
||||
}
|
||||
list = append(list, model.OllamaModel{Name: parts[0], Size: parts[2] + " " + parts[3]})
|
||||
}
|
||||
listInDB, _ := aiRepo.List()
|
||||
var dropList []dto.OllamaModelDropList
|
||||
for _, itemModel := range listInDB {
|
||||
isExit := false
|
||||
for i := 0; i < len(list); i++ {
|
||||
if list[i].Name == itemModel.Name {
|
||||
_ = aiRepo.Update(itemModel.ID, map[string]interface{}{"status": constant.StatusSuccess, "message": "", "size": list[i].Size})
|
||||
list = append(list[:i], list[(i+1):]...)
|
||||
isExit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isExit && itemModel.Status != constant.StatusWaiting {
|
||||
_ = aiRepo.Update(itemModel.ID, map[string]interface{}{"status": constant.StatusDeleted, "message": "not exist", "size": ""})
|
||||
dropList = append(dropList, dto.OllamaModelDropList{ID: itemModel.ID, Name: itemModel.Name})
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, item := range list {
|
||||
item.Status = constant.StatusSuccess
|
||||
item.From = "remote"
|
||||
_ = aiRepo.Create(&item)
|
||||
}
|
||||
|
||||
return dropList, nil
|
||||
}
|
||||
|
||||
func (u *AIToolService) BindDomain(req dto.OllamaBindDomain) error {
|
||||
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
|
||||
if nginxInstall.ID == 0 {
|
||||
return buserr.New("ErrOpenrestyInstall")
|
||||
}
|
||||
var (
|
||||
ipList []string
|
||||
err error
|
||||
)
|
||||
if len(req.IPList) > 0 {
|
||||
ipList, err = common.HandleIPList(req.IPList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ssl.Pem == "" {
|
||||
return buserr.New("ErrSSL")
|
||||
}
|
||||
}
|
||||
createWebsiteReq := request.WebsiteCreate{
|
||||
PrimaryDomain: req.Domain,
|
||||
Alias: strings.ToLower(req.Domain),
|
||||
Type: constant.Deployment,
|
||||
AppType: constant.InstalledApp,
|
||||
AppInstallID: req.AppInstallID,
|
||||
}
|
||||
websiteService := NewIWebsiteService()
|
||||
if err := websiteService.CreateWebsite(createWebsiteReq); err != nil {
|
||||
return err
|
||||
}
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(strings.ToLower(req.Domain)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ipList) > 0 {
|
||||
if err = ConfigAllowIPs(ipList, website); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
sslReq := request.WebsiteHTTPSOp{
|
||||
WebsiteID: website.ID,
|
||||
Enable: true,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: req.SSLID,
|
||||
HttpConfig: "HTTPSOnly",
|
||||
}
|
||||
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = ConfigAIProxy(website); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AIToolService) GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error) {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.AppInstallID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &dto.OllamaBindDomainRes{}
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(install.ID))
|
||||
if website.ID == 0 {
|
||||
return res, nil
|
||||
}
|
||||
res.WebsiteID = website.ID
|
||||
res.Domain = website.PrimaryDomain
|
||||
if website.WebsiteSSLID > 0 {
|
||||
res.SSLID = website.WebsiteSSLID
|
||||
ssl, _ := websiteSSLRepo.GetFirst(commonRepo.WithByID(website.WebsiteSSLID))
|
||||
res.AcmeAccountID = ssl.AcmeAccountID
|
||||
}
|
||||
res.ConnUrl = fmt.Sprintf("%s://%s", strings.ToLower(website.Protocol), website.PrimaryDomain)
|
||||
res.AllowIPs = GetAllowIps(website)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (u *AIToolService) UpdateBindDomain(req dto.OllamaBindDomain) error {
|
||||
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
|
||||
if nginxInstall.ID == 0 {
|
||||
return buserr.New("ErrOpenrestyInstall")
|
||||
}
|
||||
var (
|
||||
ipList []string
|
||||
err error
|
||||
)
|
||||
if len(req.IPList) > 0 {
|
||||
ipList, err = common.HandleIPList(req.IPList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ssl.Pem == "" {
|
||||
return buserr.New("ErrSSL")
|
||||
}
|
||||
}
|
||||
websiteService := NewIWebsiteService()
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ConfigAllowIPs(ipList, website); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
sslReq := request.WebsiteHTTPSOp{
|
||||
WebsiteID: website.ID,
|
||||
Enable: true,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: req.SSLID,
|
||||
HttpConfig: "HTTPSOnly",
|
||||
}
|
||||
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if website.WebsiteSSLID > 0 && req.SSLID == 0 {
|
||||
sslReq := request.WebsiteHTTPSOp{
|
||||
WebsiteID: website.ID,
|
||||
Enable: false,
|
||||
}
|
||||
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadContainerName() (string, error) {
|
||||
ollamaBaseInfo, err := appInstallRepo.LoadBaseInfo("ollama", "")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ollama service is not found, err: %v", err)
|
||||
}
|
||||
if ollamaBaseInfo.Status != constant.Running {
|
||||
return "", fmt.Errorf("container %s of ollama is not running, please check and retry!", ollamaBaseInfo.ContainerName)
|
||||
}
|
||||
return ollamaBaseInfo.ContainerName, nil
|
||||
}
|
||||
|
||||
func pullOllamaModel(file *os.File, containerName string, info model.OllamaModel) {
|
||||
defer file.Close()
|
||||
cmd := exec.Command("docker", "exec", containerName, "ollama", "pull", info.Name)
|
||||
multiWriter := io.MultiWriter(os.Stdout, file)
|
||||
cmd.Stdout = multiWriter
|
||||
cmd.Stderr = multiWriter
|
||||
_ = cmd.Run()
|
||||
itemSize, err := loadModelSize(info.Name, containerName)
|
||||
if len(itemSize) != 0 {
|
||||
_ = aiRepo.Update(info.ID, map[string]interface{}{"status": constant.StatusSuccess, "size": itemSize})
|
||||
} else {
|
||||
_ = aiRepo.Update(info.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
}
|
||||
_, _ = file.WriteString("ollama pull completed!")
|
||||
}
|
||||
|
||||
func loadModelSize(name string, containerName string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker exec %s ollama list | grep %s", containerName, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
lines := strings.Split(string(stdout), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 5 {
|
||||
continue
|
||||
}
|
||||
return parts[2] + " " + parts[3], nil
|
||||
}
|
||||
return "", fmt.Errorf("no such model %s in ollama list, std: %s", name, string(stdout))
|
||||
}
|
||||
+53
-35
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -34,9 +35,9 @@ type AppService struct {
|
||||
}
|
||||
|
||||
type IAppService interface {
|
||||
PageApp(req request.AppSearch) (interface{}, error)
|
||||
GetAppTags() ([]response.TagDTO, error)
|
||||
GetApp(key string) (*response.AppDTO, error)
|
||||
PageApp(ctx *gin.Context, req request.AppSearch) (interface{}, error)
|
||||
GetAppTags(ctx *gin.Context) ([]response.TagDTO, error)
|
||||
GetApp(ctx *gin.Context, key string) (*response.AppDTO, error)
|
||||
GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)
|
||||
Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error)
|
||||
SyncAppListFromRemote() error
|
||||
@@ -50,7 +51,7 @@ func NewIAppService() IAppService {
|
||||
return &AppService{}
|
||||
}
|
||||
|
||||
func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
||||
func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{}, error) {
|
||||
var opts []repo.DBOption
|
||||
opts = append(opts, appRepo.OrderByRecommend())
|
||||
if req.Name != "" {
|
||||
@@ -89,31 +90,24 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var appDTOs []*response.AppDto
|
||||
var appDTOs []*response.AppItem
|
||||
lang := strings.ToLower(common.GetLang(ctx))
|
||||
for _, ap := range apps {
|
||||
appDTO := &response.AppDto{
|
||||
ID: ap.ID,
|
||||
Name: ap.Name,
|
||||
Key: ap.Key,
|
||||
Type: ap.Type,
|
||||
Icon: ap.Icon,
|
||||
ShortDescZh: ap.ShortDescZh,
|
||||
ShortDescEn: ap.ShortDescEn,
|
||||
Resource: ap.Resource,
|
||||
Limit: ap.Limit,
|
||||
appDTO := &response.AppItem{
|
||||
ID: ap.ID,
|
||||
Name: ap.Name,
|
||||
Key: ap.Key,
|
||||
Type: ap.Type,
|
||||
Icon: ap.Icon,
|
||||
Resource: ap.Resource,
|
||||
Limit: ap.Limit,
|
||||
GpuSupport: ap.GpuSupport,
|
||||
}
|
||||
appDTO.Description = ap.GetDescription(ctx)
|
||||
appDTOs = append(appDTOs, appDTO)
|
||||
appTags, err := appTagRepo.GetByAppId(ap.ID)
|
||||
tags, err := getAppTags(ap.ID, lang)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var tagIds []uint
|
||||
for _, at := range appTags {
|
||||
tagIds = append(tagIds, at.TagId)
|
||||
}
|
||||
tags, err := tagRepo.GetByIds(tagIds)
|
||||
if err != nil {
|
||||
continue
|
||||
return nil, err
|
||||
}
|
||||
appDTO.Tags = tags
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(ap.ID))
|
||||
@@ -125,18 +119,32 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) GetAppTags() ([]response.TagDTO, error) {
|
||||
func (a AppService) GetAppTags(ctx *gin.Context) ([]response.TagDTO, error) {
|
||||
tags, _ := tagRepo.All()
|
||||
res := make([]response.TagDTO, 0)
|
||||
lang := strings.ToLower(common.GetLang(ctx))
|
||||
for _, tag := range tags {
|
||||
res = append(res, response.TagDTO{
|
||||
Tag: tag,
|
||||
})
|
||||
tagDTO := response.TagDTO{
|
||||
ID: tag.ID,
|
||||
Key: tag.Key,
|
||||
}
|
||||
var translations = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(tag.Translations), &translations)
|
||||
if name, ok := translations[lang]; ok && name != "" {
|
||||
tagDTO.Name = name
|
||||
} else {
|
||||
if lang == "zh" || lang == "zh-Hant" {
|
||||
tagDTO.Name = tag.Name
|
||||
} else {
|
||||
tagDTO.Name = tag.Key
|
||||
}
|
||||
}
|
||||
res = append(res, tagDTO)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
||||
func (a AppService) GetApp(ctx *gin.Context, key string) (*response.AppDTO, error) {
|
||||
var appDTO response.AppDTO
|
||||
if key == "postgres" {
|
||||
key = "postgresql"
|
||||
@@ -146,6 +154,7 @@ func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
||||
return nil, err
|
||||
}
|
||||
appDTO.App = app
|
||||
appDTO.App.Description = app.GetDescription(ctx)
|
||||
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -155,7 +164,12 @@ func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
||||
versionsRaw = append(versionsRaw, detail.Version)
|
||||
}
|
||||
appDTO.Versions = common.GetSortedVersions(versionsRaw)
|
||||
|
||||
tags, err := getAppTags(app.ID, strings.ToLower(common.GetLang(ctx)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appDTO.GpuSupport = app.GpuSupport
|
||||
appDTO.Tags = tags
|
||||
return &appDTO, nil
|
||||
}
|
||||
|
||||
@@ -252,6 +266,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
|
||||
if err := checkLimit(app); err != nil {
|
||||
appDetailDTO.Enable = false
|
||||
}
|
||||
appDetailDTO.GpuSupport = app.GpuSupport
|
||||
return appDetailDTO, nil
|
||||
}
|
||||
func (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) {
|
||||
@@ -296,7 +311,8 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
if list, _ := appInstallRepo.ListBy(commonRepo.WithByName(req.Name)); len(list) > 0 {
|
||||
|
||||
if list, _ := appInstallRepo.ListBy(commonRepo.WithByLowerName(req.Name)); len(list) > 0 {
|
||||
err = buserr.New(constant.ErrAppNameExist)
|
||||
return
|
||||
}
|
||||
@@ -827,10 +843,11 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||
oldAppIds []uint
|
||||
)
|
||||
for _, t := range list.Extra.Tags {
|
||||
translations, _ := json.Marshal(t.Locales)
|
||||
tags = append(tags, &model.Tag{
|
||||
Key: t.Key,
|
||||
Name: t.Name,
|
||||
Sort: t.Sort,
|
||||
Key: t.Key,
|
||||
Translations: string(translations),
|
||||
Sort: t.Sort,
|
||||
})
|
||||
}
|
||||
oldApps, err := appRepo.GetBy(appRepo.WithResource(constant.AppResourceRemote))
|
||||
@@ -848,6 +865,7 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||
global.LOG.Infof("Starting synchronization of application details...")
|
||||
for _, l := range list.Apps {
|
||||
app := appsMap[l.AppProperty.Key]
|
||||
app.GpuSupport = l.AppProperty.GpuSupport
|
||||
|
||||
if l.AppProperty.Version > 0 && common.CompareVersion(strconv.FormatFloat(l.AppProperty.Version, 'f', -1, 64), setting.SystemVersion) {
|
||||
delete(appsMap, l.AppProperty.Key)
|
||||
|
||||
@@ -750,6 +750,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
}
|
||||
res.AppContainerConfig = config
|
||||
res.HostMode = isHostModel(install.DockerCompose)
|
||||
res.GpuConfig = isGpuConfig(install.DockerCompose)
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
@@ -838,6 +839,9 @@ func updateInstallInfoInDB(appKey, appName, param string, value interface{}) err
|
||||
"param": strings.ReplaceAll(appInstall.Param, oldVal, newVal),
|
||||
"env": strings.ReplaceAll(appInstall.Env, oldVal, newVal),
|
||||
}, commonRepo.WithByID(appInstall.ID))
|
||||
if appKey == "mysql" || appKey == "postgresql" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if param == "user-password" {
|
||||
oldVal = fmt.Sprintf("\"PANEL_DB_USER_PASSWORD\":\"%v\"", appInstall.UserPassword)
|
||||
|
||||
@@ -442,17 +442,14 @@ func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, f
|
||||
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
}
|
||||
|
||||
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
|
||||
if detail.DockerCompose == "" {
|
||||
return "", nil
|
||||
}
|
||||
func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map[string]interface{}, error) {
|
||||
composeMap := make(map[string]interface{})
|
||||
if err := yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
value, ok := composeMap["services"]
|
||||
if !ok || value == nil {
|
||||
return "", buserr.New(constant.ErrFileParse)
|
||||
return nil, buserr.New("ErrFileParse")
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
if len(servicesMap) == 1 {
|
||||
@@ -470,6 +467,34 @@ func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string
|
||||
delete(servicesMap, oldServiceName)
|
||||
}
|
||||
}
|
||||
serviceValue := servicesMap[install.ServiceName].(map[string]interface{})
|
||||
|
||||
oldComposeMap := make(map[string]interface{})
|
||||
if err := yaml.Unmarshal([]byte(install.DockerCompose), &oldComposeMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldValue, ok := oldComposeMap["services"]
|
||||
if !ok || oldValue == nil {
|
||||
return nil, buserr.New("ErrFileParse")
|
||||
}
|
||||
oldValueMap := oldValue.(map[string]interface{})
|
||||
oldServiceValue := oldValueMap[install.ServiceName].(map[string]interface{})
|
||||
if oldServiceValue["deploy"] != nil {
|
||||
serviceValue["deploy"] = oldServiceValue["deploy"]
|
||||
}
|
||||
servicesMap[install.ServiceName] = serviceValue
|
||||
composeMap["services"] = servicesMap
|
||||
return composeMap, nil
|
||||
}
|
||||
|
||||
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
|
||||
if detail.DockerCompose == "" {
|
||||
return "", nil
|
||||
}
|
||||
composeMap, err := handleUpgradeCompose(install, detail)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
envs := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(install.Env), &envs); err != nil {
|
||||
return "", err
|
||||
@@ -1051,6 +1076,8 @@ func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
|
||||
app.Key = key
|
||||
app.ShortDescZh = config.ShortDescZh
|
||||
app.ShortDescEn = config.ShortDescEn
|
||||
description, _ := json.Marshal(config.Description)
|
||||
app.Description = string(description)
|
||||
app.Website = config.Website
|
||||
app.Document = config.Document
|
||||
app.Github = config.Github
|
||||
@@ -1150,14 +1177,31 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
|
||||
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||
return
|
||||
}
|
||||
app = &localAppDefine.AppProperty
|
||||
appDefine := localAppDefine.AppProperty
|
||||
app = &model.App{}
|
||||
app.Name = appDefine.Name
|
||||
app.TagsKey = append(appDefine.Tags, "Local")
|
||||
app.Type = appDefine.Type
|
||||
app.CrossVersionUpdate = appDefine.CrossVersionUpdate
|
||||
app.Limit = appDefine.Limit
|
||||
app.Recommend = appDefine.Recommend
|
||||
app.Website = appDefine.Website
|
||||
app.Github = appDefine.Github
|
||||
app.Document = appDefine.Document
|
||||
if appDefine.ShortDescZh != "" {
|
||||
app.ShortDescZh = appDefine.ShortDescZh
|
||||
}
|
||||
if appDefine.ShortDescEn != "" {
|
||||
app.ShortDescEn = appDefine.ShortDescEn
|
||||
}
|
||||
desc, _ := json.Marshal(appDefine.Description)
|
||||
app.Description = string(desc)
|
||||
|
||||
app.Key = "local" + appDefine.Key
|
||||
app.Resource = constant.AppResourceLocal
|
||||
app.Status = constant.AppNormal
|
||||
app.Recommend = 9999
|
||||
app.TagsKey = append(app.TagsKey, "Local")
|
||||
app.Key = "local" + app.Key
|
||||
readMePath := path.Join(appDir, "README.md")
|
||||
readMeByte, err := fileOp.GetContent(readMePath)
|
||||
readMeByte, err := fileOp.GetContent(path.Join(appDir, "README.md"))
|
||||
if err == nil {
|
||||
app.ReadMe = string(readMeByte)
|
||||
}
|
||||
@@ -1319,9 +1363,10 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
|
||||
Path: installed.GetPath(),
|
||||
CreatedAt: installed.CreatedAt,
|
||||
App: response.AppDetail{
|
||||
Github: installed.App.Github,
|
||||
Website: installed.App.Website,
|
||||
Document: installed.App.Document,
|
||||
Github: installed.App.Github,
|
||||
Website: installed.App.Website,
|
||||
Document: installed.App.Document,
|
||||
GpuSupport: installed.App.GpuSupport,
|
||||
},
|
||||
}
|
||||
if updated {
|
||||
@@ -1480,6 +1525,20 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
|
||||
deploy["resources"] = resource
|
||||
serviceValue["deploy"] = deploy
|
||||
|
||||
if req.GpuConfig {
|
||||
resource["reservations"] = map[string]interface{}{
|
||||
"devices": []map[string]interface{}{
|
||||
{
|
||||
"driver": "nvidia",
|
||||
"count": "all",
|
||||
"capabilities": []string{"gpu"},
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
delete(resource, "reservations")
|
||||
}
|
||||
|
||||
ports, ok := serviceValue["ports"].([]interface{})
|
||||
if ok {
|
||||
for i, port := range ports {
|
||||
@@ -1581,9 +1640,74 @@ func isHostModel(dockerCompose string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func isGpuConfig(dockerCompose string) bool {
|
||||
composeMap := make(map[string]interface{})
|
||||
_ = yaml.Unmarshal([]byte(dockerCompose), &composeMap)
|
||||
services, serviceValid := composeMap["services"].(map[string]interface{})
|
||||
if !serviceValid {
|
||||
return false
|
||||
}
|
||||
for _, service := range services {
|
||||
serviceValue := service.(map[string]interface{})
|
||||
deploy := map[string]interface{}{}
|
||||
if de, ok := serviceValue["deploy"]; ok {
|
||||
deploy = de.(map[string]interface{})
|
||||
}
|
||||
resource := map[string]interface{}{}
|
||||
if res, ok := deploy["resources"]; ok {
|
||||
resource = res.(map[string]interface{})
|
||||
}
|
||||
if reservations, ok := resource["reservations"]; ok {
|
||||
reservationsMap := reservations.(map[string]interface{})
|
||||
if _, dOk := reservationsMap["devices"]; dOk {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func RequestDownloadCallBack(downloadCallBackUrl string) {
|
||||
if downloadCallBackUrl == "" {
|
||||
return
|
||||
}
|
||||
_, _, _ = httpUtil.HandleGet(downloadCallBackUrl, http.MethodGet, constant.TimeOut5s)
|
||||
}
|
||||
|
||||
func getAppTags(appID uint, lang string) ([]response.TagDTO, error) {
|
||||
appTags, err := appTagRepo.GetByAppId(appID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tagIds []uint
|
||||
for _, at := range appTags {
|
||||
tagIds = append(tagIds, at.TagId)
|
||||
}
|
||||
tags, err := tagRepo.GetByIds(tagIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []response.TagDTO
|
||||
for _, t := range tags {
|
||||
if t.Name != "" {
|
||||
tagDTO := response.TagDTO{
|
||||
ID: t.ID,
|
||||
Key: t.Key,
|
||||
Name: t.Name,
|
||||
}
|
||||
res = append(res, tagDTO)
|
||||
} else {
|
||||
var translations = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(t.Translations), &translations)
|
||||
if name, ok := translations[lang]; ok {
|
||||
tagDTO := response.TagDTO{
|
||||
ID: t.ID,
|
||||
Key: t.Key,
|
||||
Name: name,
|
||||
}
|
||||
res = append(res, tagDTO)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
+31
-19
@@ -38,16 +38,11 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
if nameSetting.Value != info.Name {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
|
||||
return nil, constant.ErrAuth
|
||||
if err = checkPassword(info.Password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
@@ -83,17 +78,12 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
|
||||
if nameSetting.Value != info.Name {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
if err = checkPassword(info.Password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -214,8 +204,30 @@ func (u *AuthService) GetSecurityEntrance() string {
|
||||
func (u *AuthService) IsLogin(c *gin.Context) bool {
|
||||
sID, _ := c.Cookie(constant.SessionName)
|
||||
_, err := global.SESSION.Get(sID)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func checkPassword(password string) error {
|
||||
priKey, _ := settingRepo.Get(settingRepo.WithByKey("PASSWORD_PRIVATE_KEY"))
|
||||
|
||||
privateKey, err := encrypt.ParseRSAPrivateKey(priKey.Value)
|
||||
if err != nil {
|
||||
return false
|
||||
return err
|
||||
}
|
||||
return true
|
||||
loginPassword, err := encrypt.DecryptPassword(password, privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return errors.WithMessage(constant.ErrRecordNotFound, err.Error())
|
||||
}
|
||||
existPassword, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !hmac.Equal([]byte(loginPassword), []byte(existPassword)) {
|
||||
return constant.ErrAuth
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -30,7 +29,9 @@ type BackupService struct{}
|
||||
type IBackupService interface {
|
||||
List() ([]dto.BackupInfo, error)
|
||||
SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)
|
||||
LoadSize(req dto.RecordSearch) ([]dto.BackupFile, error)
|
||||
SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error)
|
||||
LoadSizeByCronjob(req dto.RecordSearchByCronjob) ([]dto.BackupFile, error)
|
||||
LoadOneDriveInfo() (dto.OneDriveInfo, error)
|
||||
DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
Create(backupDto dto.BackupOperate) error
|
||||
@@ -94,11 +95,29 @@ func (u *BackupService) SearchRecordsWithPage(search dto.RecordSearch) (int64, [
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
datas, err := u.loadRecordSize(records)
|
||||
sort.Slice(datas, func(i, j int) bool {
|
||||
return datas[i].CreatedAt.After(datas[j].CreatedAt)
|
||||
})
|
||||
return total, datas, err
|
||||
var list []dto.BackupRecords
|
||||
for _, item := range records {
|
||||
var itemRecord dto.BackupRecords
|
||||
if err := copier.Copy(&itemRecord, &item); err != nil {
|
||||
continue
|
||||
}
|
||||
list = append(list, itemRecord)
|
||||
}
|
||||
return total, list, err
|
||||
}
|
||||
|
||||
func (u *BackupService) LoadSize(req dto.RecordSearch) ([]dto.BackupFile, error) {
|
||||
_, records, err := backupRepo.PageRecord(
|
||||
req.Page, req.PageSize,
|
||||
commonRepo.WithOrderBy("created_at desc"),
|
||||
commonRepo.WithByName(req.Name),
|
||||
commonRepo.WithByType(req.Type),
|
||||
backupRepo.WithByDetailName(req.DetailName),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return u.loadRecordSize(records)
|
||||
}
|
||||
|
||||
func (u *BackupService) ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error) {
|
||||
@@ -125,11 +144,27 @@ func (u *BackupService) SearchRecordsByCronjobWithPage(search dto.RecordSearchBy
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
datas, err := u.loadRecordSize(records)
|
||||
sort.Slice(datas, func(i, j int) bool {
|
||||
return datas[i].CreatedAt.After(datas[j].CreatedAt)
|
||||
})
|
||||
return total, datas, err
|
||||
var list []dto.BackupRecords
|
||||
for _, item := range records {
|
||||
var itemRecord dto.BackupRecords
|
||||
if err := copier.Copy(&itemRecord, &item); err != nil {
|
||||
continue
|
||||
}
|
||||
list = append(list, itemRecord)
|
||||
}
|
||||
return total, list, err
|
||||
}
|
||||
|
||||
func (u *BackupService) LoadSizeByCronjob(req dto.RecordSearchByCronjob) ([]dto.BackupFile, error) {
|
||||
_, records, err := backupRepo.PageRecord(
|
||||
req.Page, req.PageSize,
|
||||
commonRepo.WithOrderBy("created_at desc"),
|
||||
backupRepo.WithByCronID(req.CronjobID),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return u.loadRecordSize(records)
|
||||
}
|
||||
|
||||
type loadSizeHelper struct {
|
||||
@@ -378,9 +413,9 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
||||
if strings.HasSuffix(dirStr, "/") && dirStr != "/" {
|
||||
dirStr = dirStr[:strings.LastIndex(dirStr, "/")]
|
||||
}
|
||||
if err := copyDir(oldDir, dirStr); err != nil {
|
||||
if err := changeLocalBackup(oldDir, dirStr); err != nil {
|
||||
_ = backupRepo.Update(req.ID, map[string]interface{}{"vars": oldVars})
|
||||
return err
|
||||
return fmt.Errorf("copy dir from %s to %s failed, err: %v", oldDir, dirStr, err)
|
||||
}
|
||||
global.CONF.System.Backup = dirStr
|
||||
}
|
||||
@@ -482,49 +517,41 @@ func (u *BackupService) loadAccessToken(backup *model.BackupAccount) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) loadRecordSize(records []model.BackupRecord) ([]dto.BackupRecords, error) {
|
||||
var datas []dto.BackupRecords
|
||||
func (u *BackupService) loadRecordSize(records []model.BackupRecord) ([]dto.BackupFile, error) {
|
||||
data := make([]dto.BackupFile, len(records))
|
||||
clientMap := make(map[string]loadSizeHelper)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < len(records); i++ {
|
||||
var item dto.BackupRecords
|
||||
if err := copier.Copy(&item, &records[i]); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
data[i].ID = records[i].ID
|
||||
data[i].Name = records[i].FileName
|
||||
itemPath := path.Join(records[i].FileDir, records[i].FileName)
|
||||
if _, ok := clientMap[records[i].Source]; !ok {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType(records[i].Source))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup model %s from db failed, err: %v", records[i].Source, err)
|
||||
clientMap[records[i].Source] = loadSizeHelper{}
|
||||
datas = append(datas, item)
|
||||
continue
|
||||
}
|
||||
client, err := u.NewClient(&backup)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup client %s from db failed, err: %v", records[i].Source, err)
|
||||
clientMap[records[i].Source] = loadSizeHelper{}
|
||||
datas = append(datas, item)
|
||||
continue
|
||||
}
|
||||
item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
|
||||
datas = append(datas, item)
|
||||
data[i].Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
|
||||
clientMap[records[i].Source] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client, isOk: true}
|
||||
continue
|
||||
}
|
||||
if clientMap[records[i].Source].isOk {
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
item.Size, _ = clientMap[records[index].Source].client.Size(path.Join(clientMap[records[index].Source].backupPath, itemPath))
|
||||
datas = append(datas, item)
|
||||
data[i].Size, _ = clientMap[records[index].Source].client.Size(path.Join(clientMap[records[index].Source].backupPath, itemPath))
|
||||
wg.Done()
|
||||
}(i)
|
||||
} else {
|
||||
datas = append(datas, item)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
return datas, nil
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func loadLocalDir() (string, error) {
|
||||
@@ -650,3 +677,44 @@ func (u *BackupService) Run() {
|
||||
}).Error
|
||||
global.LOG.Info("Successfully refreshed OneDrive token.")
|
||||
}
|
||||
|
||||
func changeLocalBackup(oldPath, newPath string) error {
|
||||
fileOp := fileUtils.NewFileOp()
|
||||
if fileOp.Stat(path.Join(oldPath, "app")) {
|
||||
if err := fileOp.CopyDir(path.Join(oldPath, "app"), newPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if fileOp.Stat(path.Join(oldPath, "database")) {
|
||||
if err := fileOp.CopyDir(path.Join(oldPath, "database"), newPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if fileOp.Stat(path.Join(oldPath, "directory")) {
|
||||
if err := fileOp.CopyDir(path.Join(oldPath, "directory"), newPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if fileOp.Stat(path.Join(oldPath, "system_snapshot")) {
|
||||
if err := fileOp.CopyDir(path.Join(oldPath, "system_snapshot"), newPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if fileOp.Stat(path.Join(oldPath, "website")) {
|
||||
if err := fileOp.CopyDir(path.Join(oldPath, "website"), newPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if fileOp.Stat(path.Join(oldPath, "log")) {
|
||||
if err := fileOp.CopyDir(path.Join(oldPath, "log"), newPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "app"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "database"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "directory"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "system_snapshot"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "website"))
|
||||
_ = fileOp.RmRf(path.Join(oldPath, "log"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -169,8 +170,9 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("rollback mysql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
|
||||
} else {
|
||||
global.LOG.Infof("rollback mysql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
}
|
||||
global.LOG.Infof("rollback mysql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
@@ -186,6 +188,7 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("recover mysql db %s from %s failed, err: %v", req.DetailName, req.File, err)
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
|
||||
@@ -2,13 +2,14 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
pgclient "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
@@ -157,8 +158,9 @@ func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
|
||||
} else {
|
||||
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
}
|
||||
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
@@ -171,6 +173,7 @@ func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
Username: dbInfo.Username,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("recover postgresql db %s from %s failed, err: %v", req.DetailName, req.File, err)
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
|
||||
+103
-129
@@ -1,7 +1,6 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -28,14 +27,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
clamServiceNameCentOs = "clamd@scan.service"
|
||||
clamServiceNameUbuntu = "clamav-daemon.service"
|
||||
freshClamService = "clamav-freshclam.service"
|
||||
resultDir = "clamav"
|
||||
clamServiceKey = "clam"
|
||||
freshClamServiceKey = "freshclam"
|
||||
resultDir = "clamav"
|
||||
)
|
||||
|
||||
type ClamService struct {
|
||||
serviceName string
|
||||
serviceName string
|
||||
freshClamService string
|
||||
}
|
||||
|
||||
type IClamService interface {
|
||||
@@ -63,23 +62,32 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
|
||||
var baseInfo dto.ClamBaseInfo
|
||||
baseInfo.Version = "-"
|
||||
baseInfo.FreshVersion = "-"
|
||||
exist1, _ := systemctl.IsExist(clamServiceNameCentOs)
|
||||
if exist1 {
|
||||
c.serviceName = clamServiceNameCentOs
|
||||
baseInfo.IsExist = true
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameCentOs)
|
||||
clamSvc, err := systemctl.GetServiceName(clamServiceKey)
|
||||
if err != nil {
|
||||
baseInfo.IsExist = false
|
||||
return baseInfo, nil
|
||||
}
|
||||
exist2, _ := systemctl.IsExist(clamServiceNameUbuntu)
|
||||
if exist2 {
|
||||
c.serviceName = clamServiceNameUbuntu
|
||||
baseInfo.IsExist = true
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameUbuntu)
|
||||
c.serviceName = clamSvc
|
||||
isExist, err := systemctl.IsExist(c.serviceName)
|
||||
if err != nil {
|
||||
baseInfo.IsExist = false
|
||||
}
|
||||
freshExist, _ := systemctl.IsExist(freshClamService)
|
||||
if freshExist {
|
||||
baseInfo.FreshIsExist = true
|
||||
baseInfo.FreshIsActive, _ = systemctl.IsActive(freshClamService)
|
||||
baseInfo.IsExist = isExist
|
||||
baseInfo.IsActive, _ = systemctl.IsActive(clamSvc)
|
||||
|
||||
freshSvc, err := systemctl.GetServiceName(freshClamServiceKey)
|
||||
if err != nil {
|
||||
baseInfo.FreshIsExist = false
|
||||
return baseInfo, nil
|
||||
}
|
||||
c.freshClamService = freshSvc
|
||||
freshisExist, err := systemctl.IsExist(c.freshClamService)
|
||||
if err != nil {
|
||||
baseInfo.FreshIsExist = false
|
||||
}
|
||||
baseInfo.FreshIsExist = freshisExist
|
||||
baseInfo.FreshIsActive, _ = systemctl.IsActive(freshSvc)
|
||||
|
||||
if !cmd.Which("clamdscan") {
|
||||
baseInfo.IsActive = false
|
||||
}
|
||||
@@ -110,22 +118,27 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
|
||||
}
|
||||
|
||||
func (c *ClamService) Operate(operate string) error {
|
||||
var err error
|
||||
switch operate {
|
||||
case "start", "restart", "stop":
|
||||
stdout, err := cmd.Execf("systemctl %s %s", operate, c.serviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
|
||||
}
|
||||
return nil
|
||||
case "fresh-start", "fresh-restart", "fresh-stop":
|
||||
stdout, err := cmd.Execf("systemctl %s %s", strings.TrimPrefix(operate, "fresh-"), freshClamService)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
|
||||
}
|
||||
return nil
|
||||
case "start":
|
||||
err = systemctl.Start(c.serviceName)
|
||||
case "stop":
|
||||
err = systemctl.Stop(c.serviceName)
|
||||
case "restart":
|
||||
err = systemctl.Restart(c.serviceName)
|
||||
case "fresh-start":
|
||||
err = systemctl.Start(c.freshClamService)
|
||||
case "fresh-stop":
|
||||
err = systemctl.Stop(c.freshClamService)
|
||||
case "fresh-restart":
|
||||
err = systemctl.Restart(c.freshClamService)
|
||||
default:
|
||||
return fmt.Errorf("not support such operation: %v", operate)
|
||||
return fmt.Errorf("unsupported operation: %s", operate)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s %s failed: %v", operate, c.serviceName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) SearchWithPage(req dto.SearchClamWithPage) (int64, interface{}, error) {
|
||||
@@ -432,102 +445,80 @@ func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
|
||||
filePath := ""
|
||||
switch req.Name {
|
||||
case "clamd":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
filePath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
filePath = c.getConfigPath("clamd")
|
||||
case "clamd-log":
|
||||
filePath = c.loadLogPath("clamd-log")
|
||||
if len(filePath) != 0 {
|
||||
break
|
||||
}
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/var/log/clamav/clamav.log"
|
||||
} else {
|
||||
filePath = "/var/log/clamd.scan"
|
||||
}
|
||||
case "freshclam":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
filePath = "/etc/freshclam.conf"
|
||||
}
|
||||
filePath = c.getConfigPath("freshclam")
|
||||
case "freshclam-log":
|
||||
filePath = c.loadLogPath("freshclam-log")
|
||||
if len(filePath) != 0 {
|
||||
break
|
||||
}
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/var/log/clamav/freshclam.log"
|
||||
} else {
|
||||
filePath = "/var/log/freshclam.log"
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("not support such type")
|
||||
return "", fmt.Errorf("unsupported file type")
|
||||
}
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
|
||||
content, err := systemctl.ViewConfig(filePath, systemctl.ConfigOption{TailLines: req.Tail})
|
||||
if err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
}
|
||||
var tail string
|
||||
if req.Tail != "0" {
|
||||
tail = req.Tail
|
||||
} else {
|
||||
tail = "+1"
|
||||
}
|
||||
cmd := exec.Command("tail", "-n", tail, filePath)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("tail -n %v failed, err: %v", req.Tail, err)
|
||||
}
|
||||
return string(stdout), nil
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
|
||||
filePath := ""
|
||||
service := ""
|
||||
var (
|
||||
filePath string
|
||||
service string
|
||||
)
|
||||
|
||||
switch req.Name {
|
||||
case "clamd":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
service = clamServiceNameUbuntu
|
||||
filePath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
service = clamServiceNameCentOs
|
||||
filePath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
filePath = c.getConfigPath("clamd")
|
||||
service = c.serviceName
|
||||
case "freshclam":
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
filePath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
filePath = "/etc/freshclam.conf"
|
||||
}
|
||||
service = "clamav-freshclam.service"
|
||||
filePath = c.getConfigPath("freshclam")
|
||||
service = c.freshClamService
|
||||
default:
|
||||
return fmt.Errorf("not support such type")
|
||||
return fmt.Errorf("unsupported file type")
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(req.File)
|
||||
write.Flush()
|
||||
|
||||
_ = systemctl.Restart(service)
|
||||
if _, err := file.WriteString(req.File); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := systemctl.Restart(service); err != nil {
|
||||
return fmt.Errorf("restart %s failed: %v", service, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClamService) getConfigPath(confType string) string {
|
||||
switch confType {
|
||||
case "clamd":
|
||||
if _, err := os.Stat("/etc/clamav/clamd.conf"); err == nil {
|
||||
return "/etc/clamav/clamd.conf"
|
||||
}
|
||||
return "/etc/clamd.d/scan.conf"
|
||||
case "freshclam":
|
||||
if _, err := os.Stat("/etc/clamav/freshclam.conf"); err == nil {
|
||||
return "/etc/clamav/freshclam.conf"
|
||||
}
|
||||
return "/etc/freshclam.conf"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func StopAllCronJob(withCheck bool) bool {
|
||||
if withCheck {
|
||||
isActive := false
|
||||
exist1, _ := systemctl.IsExist(clamServiceNameCentOs)
|
||||
if exist1 {
|
||||
isActive, _ = systemctl.IsActive(clamServiceNameCentOs)
|
||||
}
|
||||
exist2, _ := systemctl.IsExist(clamServiceNameUbuntu)
|
||||
if exist2 {
|
||||
isActive, _ = systemctl.IsActive(clamServiceNameUbuntu)
|
||||
isexist, _ := systemctl.IsExist(clamServiceKey)
|
||||
if isexist {
|
||||
isActive, _ = systemctl.IsActive(clamServiceKey)
|
||||
}
|
||||
if isActive {
|
||||
return false
|
||||
@@ -590,42 +581,25 @@ func loadResultFromLog(pathItem string) dto.ClamLog {
|
||||
return data
|
||||
}
|
||||
func (c *ClamService) loadLogPath(name string) string {
|
||||
confPath := ""
|
||||
if name == "clamd-log" {
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
confPath = "/etc/clamav/clamd.conf"
|
||||
} else {
|
||||
confPath = "/etc/clamd.d/scan.conf"
|
||||
}
|
||||
} else {
|
||||
if c.serviceName == clamServiceNameUbuntu {
|
||||
confPath = "/etc/clamav/freshclam.conf"
|
||||
} else {
|
||||
confPath = "/etc/freshclam.conf"
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(confPath); err != nil {
|
||||
return ""
|
||||
configKey := "clamd"
|
||||
searchPrefix := "LogFile "
|
||||
if name != "clamd-log" {
|
||||
configKey = "freshclam"
|
||||
searchPrefix = "UpdateLogFile "
|
||||
}
|
||||
confPath := c.getConfigPath(configKey)
|
||||
|
||||
content, err := os.ReadFile(confPath)
|
||||
if err != nil {
|
||||
global.LOG.Debugf("Failed to read %s config: %v", configKey, err)
|
||||
return ""
|
||||
}
|
||||
lines := strings.Split(string(content), "\n")
|
||||
if name == "clamd-log" {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "LogFile ") {
|
||||
return strings.Trim(strings.ReplaceAll(line, "LogFile ", ""), " ")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "UpdateLogFile ") {
|
||||
return strings.Trim(strings.ReplaceAll(line, "UpdateLogFile ", ""), " ")
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(content), "\n") {
|
||||
if strings.HasPrefix(line, searchPrefix) {
|
||||
return strings.TrimSpace(strings.TrimPrefix(line, searchPrefix))
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
@@ -255,14 +255,14 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []dto.ContainerListStats
|
||||
datas := make([]dto.ContainerListStats, len(list))
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(list))
|
||||
for i := 0; i < len(list); i++ {
|
||||
go func(item types.Container) {
|
||||
datas = append(datas, loadCpuAndMem(client, item.ID))
|
||||
go func(index int, item types.Container) {
|
||||
datas[index] = loadCpuAndMem(client, item.ID)
|
||||
wg.Done()
|
||||
}(list[i])
|
||||
}(i, list[i])
|
||||
}
|
||||
wg.Wait()
|
||||
return datas, nil
|
||||
@@ -450,12 +450,14 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||
networkSettings := oldContainer.NetworkSettings
|
||||
bridgeNetworkSettings := networkSettings.Networks[data.Network]
|
||||
if bridgeNetworkSettings.IPAMConfig != nil {
|
||||
ipv4Address := bridgeNetworkSettings.IPAMConfig.IPv4Address
|
||||
data.Ipv4 = ipv4Address
|
||||
ipv6Address := bridgeNetworkSettings.IPAMConfig.IPv6Address
|
||||
data.Ipv6 = ipv6Address
|
||||
if data.Network != "bridge" {
|
||||
data.Ipv4 = bridgeNetworkSettings.IPAMConfig.IPv4Address
|
||||
data.Ipv6 = bridgeNetworkSettings.IPAMConfig.IPv6Address
|
||||
}
|
||||
} else {
|
||||
data.Ipv4 = bridgeNetworkSettings.IPAddress
|
||||
if data.Network != "bridge" {
|
||||
data.Ipv4 = bridgeNetworkSettings.IPAddress
|
||||
}
|
||||
}
|
||||
|
||||
data.Cmd = oldContainer.Config.Cmd
|
||||
@@ -556,10 +558,7 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
|
||||
hostConf := oldContainer.HostConfig
|
||||
var networkConf network.NetworkingConfig
|
||||
if oldContainer.NetworkSettings != nil {
|
||||
for networkKey := range oldContainer.NetworkSettings.Networks {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}
|
||||
break
|
||||
}
|
||||
networkConf.EndpointsConfig = oldContainer.NetworkSettings.Networks
|
||||
}
|
||||
if err := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true}); err != nil {
|
||||
return err
|
||||
@@ -937,15 +936,27 @@ func calculateCPUPercentUnix(stats *types.StatsJSON) float64 {
|
||||
}
|
||||
return cpuPercent
|
||||
}
|
||||
|
||||
func calculateMemPercentUnix(memStats types.MemoryStats) float64 {
|
||||
memPercent := 0.0
|
||||
memUsage := float64(memStats.Usage)
|
||||
memLimit := float64(memStats.Limit)
|
||||
memUsage := calculateMemUsageUnixNoCache(memStats)
|
||||
memLimit := memStats.Limit
|
||||
if memUsage > 0.0 && memLimit > 0.0 {
|
||||
memPercent = (memUsage / memLimit) * 100.0
|
||||
memPercent = (float64(memUsage) / float64(memLimit)) * 100.0
|
||||
}
|
||||
return memPercent
|
||||
}
|
||||
|
||||
func calculateMemUsageUnixNoCache(mem types.MemoryStats) uint64 {
|
||||
if v, isCgroup1 := mem.Stats["total_inactive_file"]; isCgroup1 && v < mem.Usage {
|
||||
return mem.Usage - v
|
||||
}
|
||||
if v := mem.Stats["inactive_file"]; v < mem.Usage {
|
||||
return mem.Usage - v
|
||||
}
|
||||
return mem.Usage
|
||||
}
|
||||
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead float64, blkWrite float64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
@@ -1063,7 +1074,7 @@ func loadCpuAndMem(client *client.Client, container string) dto.ContainerListSta
|
||||
data.PercpuUsage = len(stats.CPUStats.CPUUsage.PercpuUsage)
|
||||
|
||||
data.MemoryCache = stats.MemoryStats.Stats["cache"]
|
||||
data.MemoryUsage = stats.MemoryStats.Usage
|
||||
data.MemoryUsage = calculateMemUsageUnixNoCache(stats.MemoryStats)
|
||||
data.MemoryLimit = stats.MemoryStats.Limit
|
||||
|
||||
data.MemoryPercent = calculateMemPercentUnix(stats.MemoryStats)
|
||||
@@ -1183,9 +1194,10 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||
for _, volume := range req.Volumes {
|
||||
if volume.Type == "volume" {
|
||||
hostConf.Mounts = append(hostConf.Mounts, mount.Mount{
|
||||
Type: mount.Type(volume.Type),
|
||||
Source: volume.SourceDir,
|
||||
Target: volume.ContainerDir,
|
||||
Type: mount.Type(volume.Type),
|
||||
Source: volume.SourceDir,
|
||||
Target: volume.ContainerDir,
|
||||
ReadOnly: volume.Mode == "ro",
|
||||
})
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
} else {
|
||||
@@ -1200,10 +1212,7 @@ func reCreateAfterUpdate(name string, client *client.Client, config *container.C
|
||||
|
||||
var oldNetworkConf network.NetworkingConfig
|
||||
if networkConf != nil {
|
||||
for networkKey := range networkConf.Networks {
|
||||
oldNetworkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}
|
||||
break
|
||||
}
|
||||
oldNetworkConf.EndpointsConfig = networkConf.Networks
|
||||
}
|
||||
|
||||
oldContainer, err := client.ContainerCreate(ctx, config, hostConf, &oldNetworkConf, &v1.Platform{}, name)
|
||||
@@ -1262,12 +1271,12 @@ func loadContainerPortForInfo(itemPorts []types.Port) []dto.PortHelper {
|
||||
var exposedPorts []dto.PortHelper
|
||||
samePortMap := make(map[string]dto.PortHelper)
|
||||
ports := transPortToStr(itemPorts)
|
||||
var itemPort dto.PortHelper
|
||||
for _, item := range ports {
|
||||
itemStr := strings.Split(item, "->")
|
||||
if len(itemStr) < 2 {
|
||||
continue
|
||||
}
|
||||
var itemPort dto.PortHelper
|
||||
lastIndex := strings.LastIndex(itemStr[0], ":")
|
||||
if lastIndex == -1 {
|
||||
itemPort.HostPort = itemStr[0]
|
||||
|
||||
@@ -114,8 +114,17 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !req.CleanRemoteData {
|
||||
for key := range accountMap {
|
||||
if key != constant.Local {
|
||||
delete(accountMap, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
cronjob.RetainCopies = 0
|
||||
u.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{})
|
||||
if len(accountMap) != 0 {
|
||||
u.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{})
|
||||
}
|
||||
} else {
|
||||
u.removeExpiredLog(cronjob)
|
||||
}
|
||||
@@ -249,7 +258,7 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
||||
global.Cron.Remove(cron.EntryID(idItem))
|
||||
}
|
||||
global.LOG.Infof("stop cronjob entryID: %s", cronjob.EntryIDs)
|
||||
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData, IsDelete: true}); err != nil {
|
||||
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData, CleanRemoteData: req.CleanRemoteData, IsDelete: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
||||
|
||||
@@ -322,7 +322,8 @@ func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap
|
||||
if len(account) != 0 {
|
||||
global.LOG.Debugf("start upload file to %s, dir: %s", account, path.Join(accountMap[account].backupPath, cloudSrc))
|
||||
if _, err := accountMap[account].client.Upload(file, path.Join(accountMap[account].backupPath, cloudSrc)); err != nil {
|
||||
return "", err
|
||||
global.LOG.Errorf("upload file to %s failed, err: %v", account, err)
|
||||
continue
|
||||
}
|
||||
global.LOG.Debugf("upload successful!")
|
||||
}
|
||||
@@ -349,12 +350,18 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
|
||||
if cronjob.Type == "snapshot" {
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
if _, ok := accountMap[account]; !ok {
|
||||
continue
|
||||
}
|
||||
_, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName))
|
||||
}
|
||||
}
|
||||
_ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(records[i].FileName, ".tar.gz")))
|
||||
} else {
|
||||
for _, account := range accounts {
|
||||
if _, ok := accountMap[account]; !ok {
|
||||
continue
|
||||
}
|
||||
if len(account) != 0 {
|
||||
_, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName))
|
||||
}
|
||||
|
||||
@@ -10,19 +10,19 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/xpu"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
)
|
||||
|
||||
type DashboardService struct{}
|
||||
@@ -341,7 +341,17 @@ func loadDiskInfo() []dto.DiskInfo {
|
||||
}
|
||||
|
||||
func loadGPUInfo() []dto.GPUInfo {
|
||||
list := xpack.LoadGpuInfo()
|
||||
ok, client := gpu.New()
|
||||
var list []interface{}
|
||||
if ok {
|
||||
info, err := client.LoadGpuInfo()
|
||||
if err != nil || len(info.GPUs) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, item := range info.GPUs {
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -359,7 +369,17 @@ func loadGPUInfo() []dto.GPUInfo {
|
||||
}
|
||||
|
||||
func loadXpuInfo() []dto.XPUInfo {
|
||||
list := xpack.LoadXpuInfo()
|
||||
var list []interface{}
|
||||
ok, xpuClient := xpu.New()
|
||||
if ok {
|
||||
xpus, err := xpuClient.LoadDashData()
|
||||
if err != nil || len(xpus) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, item := range xpus {
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -50,10 +50,6 @@ func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (st
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
|
||||
case "redis-conf":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
|
||||
case "mysql-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
|
||||
case "mariadb-slow-logs":
|
||||
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
|
||||
}
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
)
|
||||
|
||||
@@ -121,7 +122,7 @@ func (u *DeviceService) Update(key, value string) error {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
err := systemctl.Restart("1panel")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system for new time zone failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
fileUtils "github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/google/uuid"
|
||||
@@ -285,7 +285,7 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
|
||||
if restart {
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
err := systemctl.Restart("1panel")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system port failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -365,17 +365,18 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
|
||||
|
||||
func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
||||
service := "docker"
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
dockerCmd, err := getDockerRestartCommand()
|
||||
h, err := systemctl.DefaultHandler(service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Operation == "stop" {
|
||||
isSocketActive, _ := systemctl.IsActive("docker.socket")
|
||||
if isSocketActive {
|
||||
std, err := cmd.Execf("%s systemctl stop docker.socket", sudo)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("handle systemctl stop docker.socket failed, err: %v", std)
|
||||
socketHandle, err := systemctl.DefaultHandler("docker.socket")
|
||||
if err == nil {
|
||||
status, err := socketHandle.IsActive()
|
||||
if err == nil && status.IsActive {
|
||||
if std, err := socketHandle.ExecuteAction("stop"); err != nil {
|
||||
global.LOG.Errorf("handle stop docker.socket failed, err: %v", std)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,13 +387,20 @@ func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
||||
}
|
||||
}
|
||||
|
||||
stdout, err := cmd.Execf("%s %s %s", dockerCmd, req.Operation, service)
|
||||
if isDockerSnapInstalled() {
|
||||
command := fmt.Sprintf("snap %s docker", req.Operation)
|
||||
stdout, err := cmd.Exec(command)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to restart docker: %v", stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
result, err := h.ExecuteAction(req.Operation)
|
||||
if err != nil {
|
||||
return errors.New(stdout)
|
||||
return errors.New(result.Output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func changeLogOption(daemonMap map[string]interface{}, logMaxFile, logMaxSize string) {
|
||||
if opts, ok := daemonMap["log-opts"]; ok {
|
||||
if len(logMaxFile) != 0 || len(logMaxSize) != 0 {
|
||||
@@ -451,31 +459,27 @@ func validateDockerConfig() error {
|
||||
return nil
|
||||
}
|
||||
if err != nil || (stdout != "" && strings.TrimSpace(stdout) != "configuration OK") {
|
||||
return fmt.Errorf("Docker configuration validation failed, err: %v", stdout)
|
||||
return fmt.Errorf("docker configuration validation failed, err: %v", stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDockerRestartCommand() (string, error) {
|
||||
func isDockerSnapInstalled() bool {
|
||||
stdout, err := cmd.Exec("which docker")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to find docker: %v", err)
|
||||
return false
|
||||
}
|
||||
dockerPath := stdout
|
||||
if strings.Contains(dockerPath, "snap") {
|
||||
return "snap", nil
|
||||
}
|
||||
return "systemctl", nil
|
||||
stdout = strings.TrimSpace(stdout)
|
||||
return strings.Contains(stdout, "snap")
|
||||
}
|
||||
|
||||
func restartDocker() error {
|
||||
restartCmd, err := getDockerRestartCommand()
|
||||
if err != nil {
|
||||
return err
|
||||
if isDockerSnapInstalled() {
|
||||
stdout, err := cmd.Exec("snap restart docker")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to restart docker: %v", stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
stdout, err := cmd.Execf("%s restart docker", restartCmd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to restart Docker: %s", stdout)
|
||||
}
|
||||
return nil
|
||||
return systemctl.Restart("docker")
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ var (
|
||||
appInstallRepo = repo.NewIAppInstallRepo()
|
||||
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
||||
|
||||
aiRepo = repo.NewIAiRepo()
|
||||
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
postgresqlRepo = repo.NewIPostgresqlRepo()
|
||||
databaseRepo = repo.NewIDatabaseRepo()
|
||||
@@ -44,4 +46,6 @@ var (
|
||||
phpExtensionsRepo = repo.NewIPHPExtensionsRepo()
|
||||
|
||||
favoriteRepo = repo.NewIFavoriteRepo()
|
||||
|
||||
mcpServerRepo = repo.NewIMcpServerRepo()
|
||||
)
|
||||
|
||||
@@ -50,6 +50,7 @@ type IFileService interface {
|
||||
ChangeMode(op request.FileCreate) error
|
||||
BatchChangeModeAndOwner(op request.FileRoleReq) error
|
||||
ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error)
|
||||
BatchCheckFiles(req request.FilePathsCheck) []response.ExistFileInfo
|
||||
}
|
||||
|
||||
var filteredPaths = []string{
|
||||
@@ -365,10 +366,18 @@ func (f *FileService) MvFile(m request.FileMove) error {
|
||||
return buserr.New(constant.ErrMovePathFailed)
|
||||
}
|
||||
}
|
||||
var errs []error
|
||||
if m.Type == "cut" {
|
||||
if len(m.CoverPaths) > 0 {
|
||||
for _, src := range m.CoverPaths {
|
||||
if err := fo.CopyAndReName(src, m.NewPath, "", true); err != nil {
|
||||
errs = append(errs, err)
|
||||
global.LOG.Errorf("cut copy file [%s] to [%s] failed, err: %s", src, m.NewPath, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
return fo.Cut(m.OldPaths, m.NewPath, m.Name, m.Cover)
|
||||
}
|
||||
var errs []error
|
||||
if m.Type == "copy" {
|
||||
for _, src := range m.OldPaths {
|
||||
if err := fo.CopyAndReName(src, m.NewPath, m.Name, m.Cover); err != nil {
|
||||
@@ -478,6 +487,12 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
}
|
||||
case "image-pull", "image-push", "image-build", "compose-create":
|
||||
logFilePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
|
||||
case "ollama-model":
|
||||
logFilePath = path.Join(global.CONF.System.DataDir, "log", "AITools", req.Name)
|
||||
case "mysql-slow-logs":
|
||||
logFilePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
|
||||
case "mariadb-slow-logs":
|
||||
logFilePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
|
||||
}
|
||||
|
||||
lines, isEndOfFile, total, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)
|
||||
@@ -501,3 +516,19 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (f *FileService) BatchCheckFiles(req request.FilePathsCheck) []response.ExistFileInfo {
|
||||
fileList := make([]response.ExistFileInfo, 0, len(req.Paths))
|
||||
for _, filePath := range req.Paths {
|
||||
if info, err := os.Stat(filePath); err == nil {
|
||||
fileList = append(fileList, response.ExistFileInfo{
|
||||
Size: float64(info.Size()),
|
||||
Name: info.Name(),
|
||||
Path: filePath,
|
||||
ModTime: info.ModTime(),
|
||||
IsDir: info.IsDir(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return fileList
|
||||
}
|
||||
|
||||
@@ -381,6 +381,10 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
|
||||
TargetIP: r.TargetIP,
|
||||
TargetPort: r.TargetPort,
|
||||
}, r.Operation); err != nil {
|
||||
if req.ForceDelete {
|
||||
global.LOG.Error(err)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+16
-11
@@ -103,23 +103,28 @@ func (f *FtpService) Sync() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentData := make(map[string]model.Ftp)
|
||||
for _, item := range listsInDB {
|
||||
currentData[item.User] = item
|
||||
}
|
||||
|
||||
sameData := make(map[string]struct{})
|
||||
for _, item := range lists {
|
||||
for _, itemInDB := range listsInDB {
|
||||
if item.User == itemInDB.User {
|
||||
sameData[item.User] = struct{}{}
|
||||
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
|
||||
_ = ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status})
|
||||
if itemInDB, ok := currentData[item.User]; ok {
|
||||
sameData[item.User] = struct{}{}
|
||||
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
|
||||
if err := ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status}); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if err := ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, item := range lists {
|
||||
if _, ok := sameData[item.User]; !ok {
|
||||
_ = ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status})
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range listsInDB {
|
||||
if _, ok := sameData[item.User]; !ok {
|
||||
_ = ftpRepo.Update(item.ID, map[string]interface{}{"status": "deleted"})
|
||||
|
||||
@@ -3,6 +3,12 @@ package service
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
@@ -14,11 +20,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/ini.v1"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HostToolService struct{}
|
||||
@@ -39,93 +40,108 @@ func NewIHostToolService() IHostToolService {
|
||||
}
|
||||
|
||||
func (h *HostToolService) GetToolStatus(req request.HostToolReq) (*response.HostToolRes, error) {
|
||||
res := &response.HostToolRes{}
|
||||
res.Type = req.Type
|
||||
res := &response.HostToolRes{Type: req.Type}
|
||||
switch req.Type {
|
||||
case constant.Supervisord:
|
||||
supervisorConfig := &response.Supervisor{}
|
||||
if !cmd.Which(constant.Supervisord) {
|
||||
supervisorConfig.IsExist = false
|
||||
res.Config = supervisorConfig
|
||||
return res, nil
|
||||
}
|
||||
supervisorConfig.IsExist = true
|
||||
serviceExist, _ := systemctl.IsExist(constant.Supervisord)
|
||||
if !serviceExist {
|
||||
serviceExist, _ = systemctl.IsExist(constant.Supervisor)
|
||||
if !serviceExist {
|
||||
supervisorConfig.IsExist = false
|
||||
res.Config = supervisorConfig
|
||||
return res, nil
|
||||
} else {
|
||||
supervisorConfig.ServiceName = constant.Supervisor
|
||||
}
|
||||
} else {
|
||||
supervisorConfig.ServiceName = constant.Supervisord
|
||||
}
|
||||
|
||||
serviceNameSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorServiceName))
|
||||
if serviceNameSet.ID != 0 || serviceNameSet.Value != "" {
|
||||
supervisorConfig.ServiceName = serviceNameSet.Value
|
||||
}
|
||||
|
||||
versionRes, _ := cmd.Exec("supervisord -v")
|
||||
supervisorConfig.Version = strings.TrimSuffix(versionRes, "\n")
|
||||
_, ctlRrr := exec.LookPath("supervisorctl")
|
||||
supervisorConfig.CtlExist = ctlRrr == nil
|
||||
|
||||
active, _ := systemctl.IsActive(supervisorConfig.ServiceName)
|
||||
if active {
|
||||
supervisorConfig.Status = "running"
|
||||
} else {
|
||||
supervisorConfig.Status = "stopped"
|
||||
}
|
||||
|
||||
pathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))
|
||||
if pathSet.ID != 0 || pathSet.Value != "" {
|
||||
supervisorConfig.ConfigPath = pathSet.Value
|
||||
res.Config = supervisorConfig
|
||||
return res, nil
|
||||
} else {
|
||||
supervisorConfig.Init = true
|
||||
}
|
||||
|
||||
servicePath := "/usr/lib/systemd/system/supervisor.service"
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(servicePath) {
|
||||
servicePath = "/usr/lib/systemd/system/supervisord.service"
|
||||
}
|
||||
if fileOp.Stat(servicePath) {
|
||||
startCmd, _ := ini_conf.GetIniValue(servicePath, "Service", "ExecStart")
|
||||
if startCmd != "" {
|
||||
args := strings.Fields(startCmd)
|
||||
cIndex := -1
|
||||
for i, arg := range args {
|
||||
if arg == "-c" {
|
||||
cIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if cIndex != -1 && cIndex+1 < len(args) {
|
||||
supervisorConfig.ConfigPath = args[cIndex+1]
|
||||
}
|
||||
}
|
||||
}
|
||||
if supervisorConfig.ConfigPath == "" {
|
||||
configPath := "/etc/supervisord.conf"
|
||||
if !fileOp.Stat(configPath) {
|
||||
configPath = "/etc/supervisor/supervisord.conf"
|
||||
if fileOp.Stat(configPath) {
|
||||
supervisorConfig.ConfigPath = configPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.Config = supervisorConfig
|
||||
return h.getSupervisorStatus(res)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *HostToolService) getSupervisorStatus(res *response.HostToolRes) (*response.HostToolRes, error) {
|
||||
supervisorConfig := &response.Supervisor{}
|
||||
if !cmd.Which(constant.Supervisord) {
|
||||
supervisorConfig.IsExist = false
|
||||
res.Config = supervisorConfig
|
||||
return res, nil
|
||||
}
|
||||
supervisorConfig.IsExist = true
|
||||
|
||||
serviceName, err := h.determineServiceName()
|
||||
if err != nil || serviceName == "" {
|
||||
supervisorConfig.IsExist = false
|
||||
res.Config = supervisorConfig
|
||||
return res, nil
|
||||
}
|
||||
supervisorConfig.ServiceName = serviceName
|
||||
|
||||
if nameSetting, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorServiceName)); nameSetting.Value != "" {
|
||||
supervisorConfig.ServiceName = nameSetting.Value
|
||||
}
|
||||
|
||||
if version, err := cmd.Exec("supervisord -v"); err == nil {
|
||||
supervisorConfig.Version = strings.TrimSpace(version)
|
||||
}
|
||||
|
||||
_, errCtl := exec.LookPath("supervisorctl")
|
||||
supervisorConfig.CtlExist = errCtl == nil
|
||||
|
||||
if active, err := systemctl.IsActive(supervisorConfig.ServiceName); err == nil && active {
|
||||
supervisorConfig.Status = "running"
|
||||
} else {
|
||||
supervisorConfig.Status = "stopped"
|
||||
}
|
||||
|
||||
h.resolveConfigPath(supervisorConfig)
|
||||
|
||||
res.Config = supervisorConfig
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *HostToolService) determineServiceName() (string, error) {
|
||||
if setting, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorServiceName)); setting.Value != "" {
|
||||
serviceName, err := systemctl.GetServiceName(setting.Value)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get service name error: %v", err)
|
||||
}
|
||||
return serviceName, nil
|
||||
}
|
||||
if serviceName, err := systemctl.GetServiceName(constant.Supervisord); err == nil {
|
||||
return serviceName, nil
|
||||
}
|
||||
return systemctl.GetServiceName(constant.Supervisor)
|
||||
}
|
||||
|
||||
func (h *HostToolService) resolveConfigPath(config *response.Supervisor) {
|
||||
if pathSetting, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath)); pathSetting.Value != "" {
|
||||
config.ConfigPath = pathSetting.Value
|
||||
return
|
||||
}
|
||||
|
||||
config.Init = true
|
||||
|
||||
if servicePath, err := systemctl.GetServicePath(config.ServiceName); err == nil {
|
||||
if startCmd, _ := ini_conf.GetIniValue(servicePath, "Service", "ExecStart"); startCmd != "" {
|
||||
if path := parseConfigPathFromCommand(startCmd); path != "" {
|
||||
config.ConfigPath = path
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultPaths := []string{
|
||||
"/etc/supervisord.conf",
|
||||
"/etc/supervisor/supervisord.conf",
|
||||
"/usr/local/etc/supervisord.conf",
|
||||
}
|
||||
for _, path := range defaultPaths {
|
||||
if systemctl.FileExist(path) {
|
||||
config.ConfigPath = path
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseConfigPathFromCommand(cmd string) string {
|
||||
parts := strings.Fields(cmd)
|
||||
for i, part := range parts {
|
||||
if part == "-c" && i+1 < len(parts) {
|
||||
return parts[i+1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *HostToolService) CreateToolConfig(req request.HostToolCreate) error {
|
||||
switch req.Type {
|
||||
case constant.Supervisord:
|
||||
@@ -200,14 +216,28 @@ func (h *HostToolService) CreateToolConfig(req request.HostToolCreate) error {
|
||||
}
|
||||
|
||||
func (h *HostToolService) OperateTool(req request.HostToolReq) error {
|
||||
serviceName := req.Type
|
||||
if req.Type == constant.Supervisord {
|
||||
var serviceName string
|
||||
var err error
|
||||
switch req.Type {
|
||||
case constant.Supervisord:
|
||||
serviceNameSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorServiceName))
|
||||
if serviceNameSet.ID != 0 || serviceNameSet.Value != "" {
|
||||
serviceName = serviceNameSet.Value
|
||||
} else {
|
||||
serviceName = req.Type
|
||||
}
|
||||
default:
|
||||
serviceName = req.Type
|
||||
}
|
||||
return systemctl.Operate(req.Operate, serviceName)
|
||||
serviceName, err = systemctl.GetServiceName(serviceName)
|
||||
if err != nil {
|
||||
if errors.Is(err, systemctl.ErrServiceNotFound) {
|
||||
return fmt.Errorf("%s service unavailable. Please ensure supervisor server is installed and configured", serviceName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
_, err = systemctl.CustomAction(req.Operate, serviceName)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *HostToolService) OperateToolConfig(req request.HostToolConfig) (*response.HostToolConfig, error) {
|
||||
|
||||
@@ -439,6 +439,9 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
|
||||
}
|
||||
return buserr.WithDetail(constant.ErrInUsed, id, nil)
|
||||
}
|
||||
if strings.Contains(err.Error(), "image has dependent") {
|
||||
return buserr.New(constant.ErrObjectBeDependent)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type IImageRepoService interface {
|
||||
Login(req dto.OperateByID) error
|
||||
Create(req dto.ImageRepoCreate) error
|
||||
Update(req dto.ImageRepoUpdate) error
|
||||
BatchDelete(req dto.ImageRepoDelete) error
|
||||
Delete(req dto.OperateByID) error
|
||||
}
|
||||
|
||||
func NewIImageRepoService() IImageRepoService {
|
||||
@@ -89,31 +89,7 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
|
||||
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
|
||||
}
|
||||
if err := validateDockerConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
defer ticker.Stop()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
if err := func() error {
|
||||
for range ticker.C {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cancel()
|
||||
return errors.New("the docker service cannot be restarted")
|
||||
default:
|
||||
stdout, err := cmd.Exec("systemctl is-active docker")
|
||||
if string(stdout) == "active\n" && err == nil {
|
||||
global.LOG.Info("docker restart with new conf successful!")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}(); err != nil {
|
||||
if err := stopBeforeUpdateRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -131,15 +107,32 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
return imageRepoRepo.Create(&imageRepo)
|
||||
}
|
||||
|
||||
func (u *ImageRepoService) BatchDelete(req dto.ImageRepoDelete) error {
|
||||
for _, id := range req.Ids {
|
||||
if id == 1 {
|
||||
return errors.New("The default value cannot be edit !")
|
||||
}
|
||||
func (u *ImageRepoService) Delete(req dto.OperateByID) error {
|
||||
if req.ID == 1 {
|
||||
return errors.New("The default value cannot be edit !")
|
||||
}
|
||||
if err := imageRepoRepo.Delete(commonRepo.WithIdsIn(req.Ids)); err != nil {
|
||||
itemRepo, _ := imageRepoRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if itemRepo.ID == 0 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if itemRepo.Auth {
|
||||
_, _ = cmd.Execf("docker logout -i %s", itemRepo.DownloadUrl)
|
||||
}
|
||||
if itemRepo.Protocol == "https" {
|
||||
return imageRepoRepo.Delete(commonRepo.WithByID(req.ID))
|
||||
}
|
||||
if err := u.handleRegistries("", itemRepo.DownloadUrl, "delete"); err != nil {
|
||||
return fmt.Errorf("delete registry %s failed, err: %v", itemRepo.DownloadUrl, err)
|
||||
}
|
||||
if err := validateDockerConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := imageRepoRepo.Delete(commonRepo.WithByID(req.ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
_ = restartDocker()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -154,37 +147,39 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needRestart := false
|
||||
if repo.Protocol == "http" && req.Protocol == "https" {
|
||||
if err := u.handleRegistries("", repo.DownloadUrl, "delete"); err != nil {
|
||||
return fmt.Errorf("delete registry %s failed, err: %v", repo.DownloadUrl, err)
|
||||
}
|
||||
needRestart = true
|
||||
}
|
||||
if repo.Protocol == "http" && req.Protocol == "http" {
|
||||
if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil {
|
||||
return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err)
|
||||
if repo.DownloadUrl != req.DownloadUrl {
|
||||
if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil {
|
||||
return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err)
|
||||
}
|
||||
needRestart = true
|
||||
}
|
||||
}
|
||||
if repo.Protocol == "https" && req.Protocol == "http" {
|
||||
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
|
||||
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
|
||||
}
|
||||
needRestart = true
|
||||
}
|
||||
if repo.Auth != req.Auth || repo.DownloadUrl != req.DownloadUrl {
|
||||
if repo.Auth {
|
||||
_, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl)
|
||||
}
|
||||
if req.Auth {
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
if needRestart {
|
||||
if err := stopBeforeUpdateRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateDockerConfig(); err != nil {
|
||||
return err
|
||||
if repo.Auth {
|
||||
_, _ = cmd.Execf("docker logout -i %s", repo.DownloadUrl)
|
||||
}
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
if req.Auth {
|
||||
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
@@ -256,3 +251,34 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopBeforeUpdateRepo() error {
|
||||
if err := validateDockerConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
defer ticker.Stop()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
if err := func() error {
|
||||
for range ticker.C {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cancel()
|
||||
return errors.New("the docker service cannot be restarted")
|
||||
default:
|
||||
stdout, err := cmd.Exec("systemctl is-active docker")
|
||||
if string(stdout) == "active\n" && err == nil {
|
||||
global.LOG.Info("docker restart with new conf successful!")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/geo"
|
||||
"github.com/gin-gonic/gin"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -26,7 +29,7 @@ const logs = "https://resource.fit2cloud.com/installation-log.sh"
|
||||
type ILogService interface {
|
||||
ListSystemLogFile() ([]string, error)
|
||||
CreateLoginLog(operation model.LoginLog) error
|
||||
PageLoginLog(search dto.SearchLgLogWithPage) (int64, interface{}, error)
|
||||
PageLoginLog(c *gin.Context, search dto.SearchLgLogWithPage) (int64, interface{}, error)
|
||||
|
||||
CreateOperationLog(operation model.OperationLog) error
|
||||
PageOperationLog(search dto.SearchOpLogWithPage) (int64, interface{}, error)
|
||||
@@ -77,7 +80,7 @@ func (u *LogService) ListSystemLogFile() ([]string, error) {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (u *LogService) PageLoginLog(req dto.SearchLgLogWithPage) (int64, interface{}, error) {
|
||||
func (u *LogService) PageLoginLog(c *gin.Context, req dto.SearchLgLogWithPage) (int64, interface{}, error) {
|
||||
total, ops, err := logRepo.PageLoginLog(
|
||||
req.Page,
|
||||
req.PageSize,
|
||||
@@ -91,6 +94,7 @@ func (u *LogService) PageLoginLog(req dto.SearchLgLogWithPage) (int64, interface
|
||||
if err := copier.Copy(&item, &op); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
item.Address, _ = geo.GetIPLocation(item.IP, common.GetLang(c))
|
||||
dtoOps = append(dtoOps, item)
|
||||
}
|
||||
return total, dtoOps, err
|
||||
|
||||
@@ -0,0 +1,643 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/mcp"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
|
||||
"github.com/subosito/gotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type McpServerService struct{}
|
||||
|
||||
type IMcpServerService interface {
|
||||
Page(req request.McpServerSearch) response.McpServersRes
|
||||
Create(create request.McpServerCreate) error
|
||||
Update(req request.McpServerUpdate) error
|
||||
Delete(id uint) error
|
||||
Operate(req request.McpServerOperate) error
|
||||
GetBindDomain() (response.McpBindDomainRes, error)
|
||||
BindDomain(req request.McpBindDomain) error
|
||||
UpdateBindDomain(req request.McpBindDomainUpdate) error
|
||||
}
|
||||
|
||||
func NewIMcpServerService() IMcpServerService {
|
||||
return &McpServerService{}
|
||||
}
|
||||
|
||||
func (m McpServerService) Page(req request.McpServerSearch) response.McpServersRes {
|
||||
var (
|
||||
res response.McpServersRes
|
||||
items []response.McpServerDTO
|
||||
)
|
||||
|
||||
total, data, _ := mcpServerRepo.Page(req.PageInfo.Page, req.PageInfo.PageSize)
|
||||
for _, item := range data {
|
||||
_ = syncMcpServerContainerStatus(&item)
|
||||
serverDTO := response.McpServerDTO{
|
||||
McpServer: item,
|
||||
Environments: make([]request.Environment, 0),
|
||||
Volumes: make([]request.Volume, 0),
|
||||
}
|
||||
project, _ := docker.GetComposeProject(item.Name, path.Join(constant.McpDir, item.Name), []byte(item.DockerCompose), []byte(item.Env), true)
|
||||
for _, service := range project.Services {
|
||||
if service.Environment != nil {
|
||||
for key, value := range service.Environment {
|
||||
serverDTO.Environments = append(serverDTO.Environments, request.Environment{
|
||||
Key: key,
|
||||
Value: *value,
|
||||
})
|
||||
}
|
||||
}
|
||||
if service.Volumes != nil {
|
||||
for _, volume := range service.Volumes {
|
||||
serverDTO.Volumes = append(serverDTO.Volumes, request.Volume{
|
||||
Source: volume.Source,
|
||||
Target: volume.Target,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
items = append(items, serverDTO)
|
||||
}
|
||||
res.Total = total
|
||||
res.Items = items
|
||||
return res
|
||||
}
|
||||
|
||||
func (m McpServerService) Update(req request.McpServerUpdate) error {
|
||||
mcpServer, err := mcpServerRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if mcpServer.Port != req.Port {
|
||||
if err := checkPortExist(req.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if mcpServer.ContainerName != req.ContainerName {
|
||||
if err := checkContainerName(req.ContainerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
mcpServer.Name = req.Name
|
||||
mcpServer.ContainerName = req.ContainerName
|
||||
mcpServer.Port = req.Port
|
||||
mcpServer.Command = req.Command
|
||||
mcpServer.BaseURL = req.BaseURL
|
||||
mcpServer.SsePath = req.SsePath
|
||||
mcpServer.HostIP = req.HostIP
|
||||
if err := handleCreateParams(mcpServer, req.Environments, req.Volumes); err != nil {
|
||||
return err
|
||||
}
|
||||
env := handleEnv(mcpServer)
|
||||
mcpDir := path.Join(constant.McpDir, mcpServer.Name)
|
||||
envPath := path.Join(mcpDir, ".env")
|
||||
if err := gotenv.Write(env, envPath); err != nil {
|
||||
return err
|
||||
}
|
||||
dockerComposePath := path.Join(mcpDir, "docker-compose.yml")
|
||||
if err := files.NewFileOp().SaveFile(dockerComposePath, mcpServer.DockerCompose, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
mcpServer.Status = constant.RuntimeStarting
|
||||
if err := mcpServerRepo.Save(mcpServer); err != nil {
|
||||
return err
|
||||
}
|
||||
go startMcp(mcpServer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m McpServerService) Create(create request.McpServerCreate) error {
|
||||
servers, _ := mcpServerRepo.List()
|
||||
for _, server := range servers {
|
||||
if server.Port == create.Port {
|
||||
return buserr.New("ErrPortInUsed")
|
||||
}
|
||||
if server.ContainerName == create.ContainerName {
|
||||
return buserr.New("ErrContainerName")
|
||||
}
|
||||
if server.Name == create.Name {
|
||||
return buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
if server.SsePath == create.SsePath {
|
||||
return buserr.New("ErrSsePath")
|
||||
}
|
||||
}
|
||||
if err := checkPortExist(create.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkContainerName(create.ContainerName); err != nil {
|
||||
return err
|
||||
}
|
||||
mcpDir := path.Join(constant.McpDir, create.Name)
|
||||
mcpServer := &model.McpServer{
|
||||
Name: create.Name,
|
||||
ContainerName: create.ContainerName,
|
||||
Port: create.Port,
|
||||
Command: create.Command,
|
||||
Status: constant.RuntimeStarting,
|
||||
BaseURL: create.BaseURL,
|
||||
SsePath: create.SsePath,
|
||||
Dir: mcpDir,
|
||||
HostIP: create.HostIP,
|
||||
}
|
||||
if err := handleCreateParams(mcpServer, create.Environments, create.Volumes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
env := handleEnv(mcpServer)
|
||||
filesOP := files.NewFileOp()
|
||||
if !filesOP.Stat(mcpDir) {
|
||||
_ = filesOP.CreateDir(mcpDir, 0644)
|
||||
}
|
||||
envPath := path.Join(mcpDir, ".env")
|
||||
if err := gotenv.Write(env, envPath); err != nil {
|
||||
return err
|
||||
}
|
||||
dockerComposePath := path.Join(mcpDir, "docker-compose.yml")
|
||||
if err := filesOP.SaveFile(dockerComposePath, mcpServer.DockerCompose, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mcpServerRepo.Create(mcpServer); err != nil {
|
||||
return err
|
||||
}
|
||||
addProxy(mcpServer)
|
||||
go startMcp(mcpServer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m McpServerService) Delete(id uint) error {
|
||||
mcpServer, err := mcpServerRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
composePath := path.Join(constant.McpDir, mcpServer.Name, "docker-compose.yml")
|
||||
_, _ = compose.Down(composePath)
|
||||
_ = files.NewFileOp().DeleteDir(path.Join(constant.McpDir, mcpServer.Name))
|
||||
|
||||
websiteID := GetWebsiteID()
|
||||
if websiteID > 0 {
|
||||
websiteService := NewIWebsiteService()
|
||||
delProxyReq := request.WebsiteProxyDel{
|
||||
ID: websiteID,
|
||||
Name: mcpServer.Name,
|
||||
}
|
||||
_ = websiteService.DeleteProxy(delProxyReq)
|
||||
}
|
||||
return mcpServerRepo.DeleteBy(commonRepo.WithByID(id))
|
||||
}
|
||||
|
||||
func (m McpServerService) Operate(req request.McpServerOperate) error {
|
||||
mcpServer, err := mcpServerRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
composePath := path.Join(mcpServer.Dir, "docker-compose.yml")
|
||||
var out string
|
||||
switch req.Operate {
|
||||
case "start":
|
||||
out, err = compose.Up(composePath)
|
||||
mcpServer.Status = constant.RuntimeRunning
|
||||
case "stop":
|
||||
out, err = compose.Down(composePath)
|
||||
mcpServer.Status = constant.RuntimeStopped
|
||||
case "restart":
|
||||
out, err = compose.Restart(composePath)
|
||||
mcpServer.Status = constant.RuntimeRunning
|
||||
}
|
||||
if err != nil {
|
||||
mcpServer.Status = constant.RuntimeError
|
||||
mcpServer.Message = out
|
||||
}
|
||||
return mcpServerRepo.Save(mcpServer)
|
||||
}
|
||||
|
||||
func (m McpServerService) GetBindDomain() (response.McpBindDomainRes, error) {
|
||||
var res response.McpBindDomainRes
|
||||
websiteID := GetWebsiteID()
|
||||
if websiteID == 0 {
|
||||
return res, nil
|
||||
}
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(websiteID))
|
||||
if err != nil {
|
||||
return res, nil
|
||||
}
|
||||
res.WebsiteID = website.ID
|
||||
res.Domain = website.PrimaryDomain
|
||||
if website.WebsiteSSLID > 0 {
|
||||
res.SSLID = website.WebsiteSSLID
|
||||
ssl, _ := websiteSSLRepo.GetFirst(commonRepo.WithByID(website.WebsiteSSLID))
|
||||
res.AcmeAccountID = ssl.AcmeAccountID
|
||||
}
|
||||
res.ConnUrl = fmt.Sprintf("%s://%s", strings.ToLower(website.Protocol), website.PrimaryDomain)
|
||||
res.AllowIPs = GetAllowIps(website)
|
||||
return res, nil
|
||||
|
||||
}
|
||||
|
||||
func (m McpServerService) BindDomain(req request.McpBindDomain) error {
|
||||
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
|
||||
if nginxInstall.ID == 0 {
|
||||
return buserr.New("ErrOpenrestyInstall")
|
||||
}
|
||||
var (
|
||||
ipList []string
|
||||
err error
|
||||
)
|
||||
if len(req.IPList) > 0 {
|
||||
ipList, err = common.HandleIPList(req.IPList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ssl.Pem == "" {
|
||||
return buserr.New("ErrSSL")
|
||||
}
|
||||
}
|
||||
group, _ := groupRepo.Get(groupRepo.WithByWebsiteDefault())
|
||||
createWebsiteReq := request.WebsiteCreate{
|
||||
PrimaryDomain: req.Domain,
|
||||
Alias: strings.ToLower(req.Domain),
|
||||
Type: constant.Static,
|
||||
WebsiteGroupID: group.ID,
|
||||
}
|
||||
websiteService := NewIWebsiteService()
|
||||
if err := websiteService.CreateWebsite(createWebsiteReq); err != nil {
|
||||
return err
|
||||
}
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(strings.ToLower(req.Domain)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = settingRepo.UpdateOrCreate("MCP_WEBSITE_ID", fmt.Sprintf("%d", website.ID))
|
||||
if len(ipList) > 0 {
|
||||
if err = ConfigAllowIPs(ipList, website); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
sslReq := request.WebsiteHTTPSOp{
|
||||
WebsiteID: website.ID,
|
||||
Enable: true,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: req.SSLID,
|
||||
HttpConfig: "HTTPSOnly",
|
||||
}
|
||||
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = addMCPProxy(website.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m McpServerService) UpdateBindDomain(req request.McpBindDomainUpdate) error {
|
||||
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
|
||||
if nginxInstall.ID == 0 {
|
||||
return buserr.New("ErrOpenrestyInstall")
|
||||
}
|
||||
var (
|
||||
ipList []string
|
||||
err error
|
||||
)
|
||||
if len(req.IPList) > 0 {
|
||||
ipList, err = common.HandleIPList(req.IPList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ssl.Pem == "" {
|
||||
return buserr.New("ErrSSL")
|
||||
}
|
||||
}
|
||||
websiteService := NewIWebsiteService()
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ConfigAllowIPs(ipList, website); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
sslReq := request.WebsiteHTTPSOp{
|
||||
WebsiteID: website.ID,
|
||||
Enable: true,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: req.SSLID,
|
||||
HttpConfig: "HTTPSOnly",
|
||||
}
|
||||
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if website.WebsiteSSLID > 0 && req.SSLID == 0 {
|
||||
sslReq := request.WebsiteHTTPSOp{
|
||||
WebsiteID: website.ID,
|
||||
Enable: false,
|
||||
}
|
||||
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
go updateMcpConfig(website.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateMcpConfig(websiteID uint) {
|
||||
servers, _ := mcpServerRepo.List()
|
||||
if len(servers) == 0 {
|
||||
return
|
||||
}
|
||||
website, _ := websiteRepo.GetFirst(commonRepo.WithByID(websiteID))
|
||||
websiteDomain := website.Domains[0]
|
||||
var baseUrl string
|
||||
if website.Protocol == constant.ProtocolHTTP {
|
||||
baseUrl = fmt.Sprintf("http://%s", websiteDomain.Domain)
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("https://%s", websiteDomain.Domain)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for _, server := range servers {
|
||||
if server.BaseURL != baseUrl {
|
||||
server.BaseURL = baseUrl
|
||||
server.HostIP = "127.0.0.1"
|
||||
_ = updateMcpServer(&server)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func addProxy(server *model.McpServer) {
|
||||
websiteID := GetWebsiteID()
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(websiteID))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("[mcp] add proxy failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("[mcp] add proxy failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy")
|
||||
if !fileOp.Stat(includeDir) {
|
||||
if err = fileOp.CreateDir(includeDir, 0644); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
config, err := parser.NewStringParser(string(nginx_conf.SSE)).Parse()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
includePath := path.Join(includeDir, server.Name+".conf")
|
||||
config.FilePath = includePath
|
||||
directives := config.Directives
|
||||
location, ok := directives[0].(*components.Location)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
location.UpdateDirective("proxy_pass", []string{fmt.Sprintf("http://127.0.0.1:%d%s", server.Port, server.SsePath)})
|
||||
location.ChangePath("^~", server.SsePath)
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
global.LOG.Errorf("write config failed, err: %v", buserr.WithErr(constant.ErrUpdateBuWebsite, err))
|
||||
return
|
||||
}
|
||||
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
||||
if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil {
|
||||
global.LOG.Errorf("update nginx config failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func addMCPProxy(websiteID uint) error {
|
||||
servers, _ := mcpServerRepo.List()
|
||||
if len(servers) == 0 {
|
||||
return nil
|
||||
}
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(websiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy")
|
||||
if !fileOp.Stat(includeDir) {
|
||||
if err = fileOp.CreateDir(includeDir, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
config, err := parser.NewStringParser(string(nginx_conf.SSE)).Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
websiteDomain := website.Domains[0]
|
||||
var baseUrl string
|
||||
if website.Protocol == constant.ProtocolHTTP {
|
||||
baseUrl = fmt.Sprintf("http://%s", websiteDomain.Domain)
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("https://%s", websiteDomain.Domain)
|
||||
}
|
||||
if websiteDomain.Port != 80 && websiteDomain.Port != 443 {
|
||||
baseUrl = fmt.Sprintf("%s:%d", baseUrl, websiteDomain.Port)
|
||||
}
|
||||
for _, server := range servers {
|
||||
includePath := path.Join(includeDir, server.Name+".conf")
|
||||
config.FilePath = includePath
|
||||
directives := config.Directives
|
||||
location, ok := directives[0].(*components.Location)
|
||||
if !ok {
|
||||
err = errors.New("error")
|
||||
return err
|
||||
}
|
||||
location.UpdateDirective("proxy_pass", []string{fmt.Sprintf("http://127.0.0.1:%d%s", server.Port, server.SsePath)})
|
||||
location.ChangePath("^~", server.SsePath)
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
server.BaseURL = baseUrl
|
||||
server.HostIP = "127.0.0.1"
|
||||
go updateMcpServer(&server)
|
||||
}
|
||||
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
||||
if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateMcpServer(mcpServer *model.McpServer) error {
|
||||
env := handleEnv(mcpServer)
|
||||
if err := gotenv.Write(env, path.Join(mcpServer.Dir, ".env")); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = mcpServerRepo.Save(mcpServer)
|
||||
composePath := path.Join(constant.McpDir, mcpServer.Name, "docker-compose.yml")
|
||||
_, _ = compose.Down(composePath)
|
||||
if _, err := compose.Up(composePath); err != nil {
|
||||
mcpServer.Status = constant.RuntimeError
|
||||
mcpServer.Message = err.Error()
|
||||
}
|
||||
return mcpServerRepo.Save(mcpServer)
|
||||
}
|
||||
|
||||
func handleEnv(mcpServer *model.McpServer) gotenv.Env {
|
||||
env := make(gotenv.Env)
|
||||
env["CONTAINER_NAME"] = mcpServer.ContainerName
|
||||
env["COMMAND"] = mcpServer.Command
|
||||
env["PANEL_APP_PORT_HTTP"] = strconv.Itoa(mcpServer.Port)
|
||||
env["BASE_URL"] = mcpServer.BaseURL
|
||||
env["SSE_PATH"] = mcpServer.SsePath
|
||||
env["HOST_IP"] = mcpServer.HostIP
|
||||
envStr, _ := gotenv.Marshal(env)
|
||||
mcpServer.Env = envStr
|
||||
return env
|
||||
}
|
||||
|
||||
func handleCreateParams(mcpServer *model.McpServer, environments []request.Environment, volumes []request.Volume) error {
|
||||
var composeContent []byte
|
||||
if mcpServer.ID == 0 {
|
||||
composeContent = mcp.DefaultMcpCompose
|
||||
} else {
|
||||
composeContent = []byte(mcpServer.DockerCompose)
|
||||
}
|
||||
composeMap := make(map[string]interface{})
|
||||
if err := yaml.Unmarshal(composeContent, &composeMap); err != nil {
|
||||
return err
|
||||
}
|
||||
services, serviceValid := composeMap["services"].(map[string]interface{})
|
||||
if !serviceValid {
|
||||
return buserr.New(constant.ErrFileParse)
|
||||
}
|
||||
serviceName := ""
|
||||
serviceValue := make(map[string]interface{})
|
||||
|
||||
if mcpServer.ID > 0 {
|
||||
serviceName = mcpServer.Name
|
||||
serviceValue = services[serviceName].(map[string]interface{})
|
||||
} else {
|
||||
for name, service := range services {
|
||||
serviceName = name
|
||||
serviceValue = service.(map[string]interface{})
|
||||
break
|
||||
}
|
||||
delete(services, serviceName)
|
||||
}
|
||||
delete(serviceValue, "environment")
|
||||
if len(environments) > 0 {
|
||||
envMap := make(map[string]string)
|
||||
for _, env := range environments {
|
||||
envMap[env.Key] = env.Value
|
||||
}
|
||||
serviceValue["environment"] = envMap
|
||||
}
|
||||
delete(serviceValue, "volumes")
|
||||
if len(volumes) > 0 {
|
||||
volumeList := make([]string, 0)
|
||||
for _, volume := range volumes {
|
||||
volumeList = append(volumeList, fmt.Sprintf("%s:%s", volume.Source, volume.Target))
|
||||
}
|
||||
serviceValue["volumes"] = volumeList
|
||||
}
|
||||
|
||||
services[mcpServer.Name] = serviceValue
|
||||
composeByte, err := yaml.Marshal(composeMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mcpServer.DockerCompose = string(composeByte)
|
||||
return nil
|
||||
}
|
||||
|
||||
func startMcp(mcpServer *model.McpServer) {
|
||||
composePath := path.Join(constant.McpDir, mcpServer.Name, "docker-compose.yml")
|
||||
if mcpServer.Status != constant.RuntimeNormal {
|
||||
_, _ = compose.Down(composePath)
|
||||
}
|
||||
if out, err := compose.Up(composePath); err != nil {
|
||||
mcpServer.Status = constant.RuntimeError
|
||||
mcpServer.Message = out
|
||||
} else {
|
||||
mcpServer.Status = constant.RuntimeRunning
|
||||
mcpServer.Message = ""
|
||||
}
|
||||
_ = syncMcpServerContainerStatus(mcpServer)
|
||||
}
|
||||
|
||||
func syncMcpServerContainerStatus(mcpServer *model.McpServer) error {
|
||||
containerNames := []string{mcpServer.ContainerName}
|
||||
cli, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
containers, err := cli.ListContainersByName(containerNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(containers) == 0 {
|
||||
mcpServer.Status = constant.RuntimeStopped
|
||||
return mcpServerRepo.Save(mcpServer)
|
||||
}
|
||||
container := containers[0]
|
||||
switch container.State {
|
||||
case "exited":
|
||||
mcpServer.Status = constant.RuntimeError
|
||||
case "running":
|
||||
mcpServer.Status = constant.RuntimeRunning
|
||||
case "paused":
|
||||
mcpServer.Status = constant.RuntimeStopped
|
||||
case "restarting":
|
||||
mcpServer.Status = constant.RuntimeReStarting
|
||||
default:
|
||||
if mcpServer.Status != constant.RuntimeBuildIng {
|
||||
mcpServer.Status = constant.RuntimeStopped
|
||||
}
|
||||
}
|
||||
return mcpServerRepo.Save(mcpServer)
|
||||
}
|
||||
|
||||
func GetWebsiteID() uint {
|
||||
websiteID, _ := settingRepo.Get(settingRepo.WithByKey("MCP_WEBSITE_ID"))
|
||||
if websiteID.Value == "" {
|
||||
return 0
|
||||
}
|
||||
websiteIDUint, _ := strconv.ParseUint(websiteID.Value, 10, 64)
|
||||
return uint(websiteIDUint)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
@@ -19,10 +21,10 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
@@ -42,6 +44,7 @@ type ISettingService interface {
|
||||
HandlePasswordExpired(c *gin.Context, old, new string) error
|
||||
GenerateApiKey() (string, error)
|
||||
UpdateApiConfig(req dto.ApiInterfaceConfig) error
|
||||
GenerateRSAKey() error
|
||||
}
|
||||
|
||||
func NewISettingService() ISettingService {
|
||||
@@ -163,7 +166,7 @@ func (u *SettingService) UpdateBindInfo(req dto.BindInfo) error {
|
||||
}
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
err := systemctl.Restart("1panel")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system with new bind info failed, err: %v", err)
|
||||
}
|
||||
@@ -211,7 +214,7 @@ func (u *SettingService) UpdatePort(port uint) error {
|
||||
}
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
err := systemctl.Restart("1panel")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system port failed, err: %v", err)
|
||||
}
|
||||
@@ -234,7 +237,7 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", false, true)
|
||||
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
err := systemctl.Restart("1panel")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system failed, err: %v", err)
|
||||
}
|
||||
@@ -332,7 +335,7 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
||||
c.SetCookie(constant.SessionName, sID, 0, "", "", true, true)
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
err := systemctl.Restart("1panel")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system failed, err: %v", err)
|
||||
}
|
||||
@@ -510,5 +513,53 @@ func (u *SettingService) UpdateApiConfig(req dto.ApiInterfaceConfig) error {
|
||||
return err
|
||||
}
|
||||
global.CONF.System.IpWhiteList = req.IpWhiteList
|
||||
if err := settingRepo.Update("ApiKeyValidityTime", req.ApiKeyValidityTime); err != nil {
|
||||
return err
|
||||
}
|
||||
global.CONF.System.ApiKeyValidityTime = req.ApiKeyValidityTime
|
||||
return nil
|
||||
}
|
||||
|
||||
func exportPrivateKeyToPEM(privateKey *rsa.PrivateKey) string {
|
||||
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
|
||||
privateKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: privateKeyBytes,
|
||||
})
|
||||
return string(privateKeyPEM)
|
||||
}
|
||||
|
||||
func exportPublicKeyToPEM(publicKey *rsa.PublicKey) (string, error) {
|
||||
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
publicKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: publicKeyBytes,
|
||||
})
|
||||
return string(publicKeyPEM), nil
|
||||
}
|
||||
|
||||
func (u *SettingService) GenerateRSAKey() error {
|
||||
priKey, _ := settingRepo.Get(settingRepo.WithByKey("PASSWORD_PRIVATE_KEY"))
|
||||
pubKey, _ := settingRepo.Get(settingRepo.WithByKey("PASSWORD_PUBLIC_KEY"))
|
||||
if priKey.Value != "" && pubKey.Value != "" {
|
||||
return nil
|
||||
}
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
privateKeyPEM := exportPrivateKeyToPEM(privateKey)
|
||||
publicKeyPEM, _ := exportPublicKeyToPEM(&privateKey.PublicKey)
|
||||
err = settingRepo.UpdateOrCreate("PASSWORD_PRIVATE_KEY", privateKeyPEM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = settingRepo.UpdateOrCreate("PASSWORD_PUBLIC_KEY", publicKeyPEM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user