mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
302
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53756c382d | ||
|
|
8533a71670 | ||
|
|
6b3c06a057 | ||
|
|
4fd87568cd | ||
|
|
56f8caf561 | ||
|
|
7d2f7a8d3b | ||
|
|
01a9589be9 | ||
|
|
72eb02fd39 | ||
|
|
c1332235a0 | ||
|
|
212c8634ea | ||
|
|
b966ab3e11 | ||
|
|
25c2246782 | ||
|
|
2ae1db4730 | ||
|
|
43652b2a54 | ||
|
|
574a504ec3 | ||
|
|
52a8331c78 | ||
|
|
afa9eecf35 | ||
|
|
46e13d754f | ||
|
|
0157326d61 | ||
|
|
7d08875f95 | ||
|
|
bd2a49e299 | ||
|
|
06f1d03b93 | ||
|
|
4c39955f2f | ||
|
|
56f92310f8 | ||
|
|
cb47806fd6 | ||
|
|
16ae193e9f | ||
|
|
8770939328 | ||
|
|
d5bff6473e | ||
|
|
a1c0408aae | ||
|
|
ab523b6879 | ||
|
|
740b4c52d2 | ||
|
|
dbb94942df | ||
|
|
1e4ea2f8c3 | ||
|
|
8083837333 | ||
|
|
dd89933613 | ||
|
|
5101dace59 | ||
|
|
81c56a740c | ||
|
|
27cc3bcccd | ||
|
|
8ddaa26a57 | ||
|
|
3a848428c3 | ||
|
|
4c18f3aa1d | ||
|
|
be6278c0b7 | ||
|
|
d5e08d5db2 | ||
|
|
2d321b4a79 | ||
|
|
fcd764d521 | ||
|
|
7d4a8782d8 | ||
|
|
eea28e8481 | ||
|
|
71d679d426 | ||
|
|
2b23523bec | ||
|
|
149c26728c | ||
|
|
8c5c2440fe | ||
|
|
f22caed20c | ||
|
|
ed3e7e7c26 | ||
|
|
54bc33a1a2 | ||
|
|
900e141297 | ||
|
|
ca1eca476c | ||
|
|
c4d2593a42 | ||
|
|
932811c167 | ||
|
|
95ad101a08 | ||
|
|
a0c329019f | ||
|
|
8a92913230 | ||
|
|
5a8deddc63 | ||
|
|
2a1e60da44 | ||
|
|
6ca3deeac1 | ||
|
|
1b2edbb79a | ||
|
|
49c9929a53 | ||
|
|
8ce73a38dd | ||
|
|
28ebf7a0cc | ||
|
|
4f168656fc | ||
|
|
7f1f758e60 | ||
|
|
f8ab71953d | ||
|
|
eb50ee1c03 | ||
|
|
a184cb9bc4 | ||
|
|
0b50a10fb8 | ||
|
|
9bafdc1b0b | ||
|
|
63af54353b | ||
|
|
b67739af13 | ||
|
|
40633619ca | ||
|
|
e672d1d896 | ||
|
|
c69d162f5a | ||
|
|
2fd97b1753 | ||
|
|
5f893929da | ||
|
|
1c06b7b608 | ||
|
|
3ac64d65b6 | ||
|
|
f4f83283d3 | ||
|
|
aed4a55655 | ||
|
|
5f55a56a9e | ||
|
|
e95e79b572 | ||
|
|
15c0d0074b | ||
|
|
f4716cb62f | ||
|
|
1f2dfce7d1 | ||
|
|
c679631440 | ||
|
|
c62fd4841a | ||
|
|
a073113817 | ||
|
|
09d462b829 | ||
|
|
379b171f0a | ||
|
|
c424e22924 | ||
|
|
18e171446e | ||
|
|
c0a1555c73 | ||
|
|
3b0c844f33 | ||
|
|
566a4f3568 | ||
|
|
2c8b19bff2 | ||
|
|
292dca6419 | ||
|
|
ebe0f98209 | ||
|
|
eba1e5495f | ||
|
|
0ebd04f012 | ||
|
|
c5e8a3fa04 | ||
|
|
42b76fd82d | ||
|
|
bbf610569d | ||
|
|
eeb5fa81a1 | ||
|
|
f65ca6678f | ||
|
|
680f48dcba | ||
|
|
8947e00302 | ||
|
|
b42cf32326 | ||
|
|
5b68332b9a | ||
|
|
d7f53862e9 | ||
|
|
7887bf96de | ||
|
|
db2aa35b2f | ||
|
|
936b0e59ab | ||
|
|
6e3923d0da | ||
|
|
4377575206 | ||
|
|
0c09b12680 | ||
|
|
8a32d8032f | ||
|
|
9f12a91f4a | ||
|
|
dd0ca4bcaf | ||
|
|
14cc97eb44 | ||
|
|
2d31c5b005 | ||
|
|
05e7506f61 | ||
|
|
37806f1113 | ||
|
|
79622f324b | ||
|
|
34e84081e3 | ||
|
|
edf07be281 | ||
|
|
1208514f37 | ||
|
|
bfd857ec4b | ||
|
|
9435290bb6 | ||
|
|
acf64dcf25 | ||
|
|
2dd88364f8 | ||
|
|
d900b52a50 | ||
|
|
bd8d96be4d | ||
|
|
17dd07fe32 | ||
|
|
a06e5f28b3 | ||
|
|
d5f400670c | ||
|
|
5222388ba1 | ||
|
|
549ccbe6a0 | ||
|
|
7fd6afb17f | ||
|
|
2ff4da5e46 | ||
|
|
0f6e98be20 | ||
|
|
4399ffa9a4 | ||
|
|
44a1d9d16c | ||
|
|
565fd1c605 | ||
|
|
09ac40846f | ||
|
|
a0b820649e | ||
|
|
6cee4bfe7c | ||
|
|
ad0c859b54 | ||
|
|
d660b7d315 | ||
|
|
2dbc7f28fd | ||
|
|
e224bc4b24 | ||
|
|
9c52977825 | ||
|
|
cb151dc985 | ||
|
|
24b9f8f705 | ||
|
|
5592063e69 | ||
|
|
7b19aab305 | ||
|
|
5c50695bdc | ||
|
|
1086597e3a | ||
|
|
2944ea508e | ||
|
|
5e887bd00c | ||
|
|
76b3cf4d2b | ||
|
|
4a9895218e | ||
|
|
222593ea69 | ||
|
|
05b7fd1f63 | ||
|
|
256c04f3b8 | ||
|
|
02577ef746 | ||
|
|
059c3a0b80 | ||
|
|
5ce1b1591a | ||
|
|
6595ad6228 | ||
|
|
e7608673d7 | ||
|
|
4c8fc1defa | ||
|
|
975663f0ff | ||
|
|
9603389586 | ||
|
|
cd79cac0af | ||
|
|
d151e98fab | ||
|
|
6115ffe0fc | ||
|
|
b646c5385d | ||
|
|
04a1cff37e | ||
|
|
a5be9ca226 | ||
|
|
0356bdbf54 | ||
|
|
4c276ff383 | ||
|
|
f8432ba521 | ||
|
|
7f75ea06c2 | ||
|
|
11d3e98155 | ||
|
|
01185306f2 | ||
|
|
6ff9c4335f | ||
|
|
b2e38c320d | ||
|
|
c63897ded4 | ||
|
|
d6dcb59ab7 | ||
|
|
bd1ced0af7 | ||
|
|
1bbf501783 | ||
|
|
b16308b7ea | ||
|
|
42531dae5a | ||
|
|
aeb9135cde | ||
|
|
f092927ab8 | ||
|
|
3ac467fc53 | ||
|
|
b9e1de8446 | ||
|
|
245bbf651b | ||
|
|
a8b83cf4ed | ||
|
|
38725097a6 | ||
|
|
e935fa128f | ||
|
|
ef16934952 | ||
|
|
550872a564 | ||
|
|
e746a959af | ||
|
|
ab0f4380b2 | ||
|
|
7c236ccc3a | ||
|
|
52030dbea0 | ||
|
|
4e20ec1c9b | ||
|
|
0ddbdfeac9 | ||
|
|
a5fd55e90e | ||
|
|
844a6c11b4 | ||
|
|
a4cab09b62 | ||
|
|
12d010351a | ||
|
|
d00e5b0421 | ||
|
|
4c2fb7095d | ||
|
|
cca1406f0f | ||
|
|
1bba2664b6 | ||
|
|
cc51eaef3f | ||
|
|
2bd289defa | ||
|
|
47d090d481 | ||
|
|
4a4c2b24dd | ||
|
|
8603d4347b | ||
|
|
188a3e0ac5 | ||
|
|
a22efc90f6 | ||
|
|
d0d76c023f | ||
|
|
241a4e62ac | ||
|
|
295f2a5cf2 | ||
|
|
e3cf522565 | ||
|
|
0f1107314f | ||
|
|
18c5c99705 | ||
|
|
18b4c98daa | ||
|
|
24246da71c | ||
|
|
49ab26200d | ||
|
|
5c524f0d23 | ||
|
|
15d7d74c1b | ||
|
|
29979b23c2 | ||
|
|
3de223144d | ||
|
|
18029d8369 | ||
|
|
fb62ac17e5 | ||
|
|
dbe70ecc28 | ||
|
|
74b6af64e9 | ||
|
|
fa83199d7b | ||
|
|
750a2a445e | ||
|
|
6fb1e690aa | ||
|
|
77c0eb99f0 | ||
|
|
db64cf02bd | ||
|
|
57a6417812 | ||
|
|
12beef49b5 | ||
|
|
155363afa6 | ||
|
|
3b3fad7278 | ||
|
|
b6c4c4539f | ||
|
|
807302f6cd | ||
|
|
8902111c23 | ||
|
|
e45ef455ef | ||
|
|
0eb25d8413 | ||
|
|
a0e4c266a1 | ||
|
|
e452dfdb1f | ||
|
|
e3b542665d | ||
|
|
a481a8b322 | ||
|
|
1b5387dc5a | ||
|
|
281cf48aaa | ||
|
|
ce2b92ee01 | ||
|
|
daba12ee42 | ||
|
|
363a67b0f2 | ||
|
|
1a1a14719d | ||
|
|
5706de5ca7 | ||
|
|
947293f34e | ||
|
|
04a76fd94d | ||
|
|
c629fa9575 | ||
|
|
d4c1caa26a | ||
|
|
22d9bdacf6 | ||
|
|
64a954df53 | ||
|
|
1949be2490 | ||
|
|
8be00dad7f | ||
|
|
bf9a37623a | ||
|
|
3de0ae1b0f | ||
|
|
57a2c2616b | ||
|
|
c63967158b | ||
|
|
8902fdc78a | ||
|
|
1c5d01b11c | ||
|
|
c1c324af23 | ||
|
|
db74d010d7 | ||
|
|
c9d36d84f7 | ||
|
|
d5f446d7cf | ||
|
|
a2fcdabb7b | ||
|
|
a434bbbc12 | ||
|
|
2585801f8a | ||
|
|
c501d9fefe | ||
|
|
1d99559d4c | ||
|
|
1d5797fe68 | ||
|
|
bbe08ed218 | ||
|
|
6e12eba356 | ||
|
|
3457b99df6 | ||
|
|
4ad3b82c84 | ||
|
|
85fc07c900 | ||
|
|
d71e2a74b4 |
@@ -11,15 +11,19 @@ SERVER_PATH=$(BASE_PAH)/backend
|
||||
MAIN= $(BASE_PAH)/cmd/server/main.go
|
||||
APP_NAME=1panel
|
||||
|
||||
build_web:
|
||||
build_frontend:
|
||||
cd $(WEB_PATH) && npm install && npm run build:dev
|
||||
|
||||
build_bin:
|
||||
build_backend_on_linux:
|
||||
cd $(SERVER_PATH) \
|
||||
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -tags 'osusergo,netgo' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_linux_on_mac:
|
||||
build_backend_on_darwin:
|
||||
cd $(SERVER_PATH) \
|
||||
&& CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++ $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_all: build_web build_bin
|
||||
build_backend_on_archlinux:
|
||||
cd $(SERVER_PATH) \
|
||||
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-fpic"' -tags osusergo -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_all: build_frontend build_backend_on_linux
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<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://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>
|
||||
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel.svg?type=shield" alt="FOSSA Status"></a>
|
||||
</p>
|
||||
|
||||
------------------------------
|
||||
@@ -13,9 +14,9 @@
|
||||
1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。1Panel 的功能和优势包括:
|
||||
|
||||
- **快速建站**:深度集成 Wordpress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等一键搞定;
|
||||
- **高效管理**:通过 Web 端轻松管理 Linux 服务器,包括应用管理、主机监控、文件管理、数据库管理、容器管理等;
|
||||
- **安全可靠**:最小漏洞暴露面,提供防火墙和安全审计等功能;
|
||||
- **一键备份**:支持一键备份和恢复,备份数据云端存储,永不丢失。
|
||||
- **高效管理**:通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等;
|
||||
- **安全可靠**:基于容器来管理和部署应用,最小漏洞暴露面,提供防火墙和日志审计等功能;
|
||||
- **一键备份**:支持一键备份和恢复,备份数据到各类云端存储,永不丢失。
|
||||
|
||||
## UI 展示
|
||||
|
||||
@@ -41,12 +42,9 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
|
||||
|
||||
- [在线文档](https://1panel.cn/docs/)
|
||||
- [教学视频](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
|
||||
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
|
||||
|
||||
## 社区
|
||||
|
||||
如果您在使用过程中有任何疑问或对建议,欢迎提交 GitHub Issue 或加入到我们微信交流群进行交流沟通。
|
||||
|
||||
**微信交流群**
|
||||
**加入微信交流群**
|
||||
|
||||
<img src="https://1panel.cn/img/wechat-group.jpg" width="156" height="156"/>
|
||||
|
||||
@@ -61,6 +59,10 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
|
||||
|
||||
[](https://star-history.com/#1Panel-dev/1Panel&Date)
|
||||
|
||||
## FOSSA Status
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_large)
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
如果您发现安全问题,请直接联系我们:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- zhengkun@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
|
||||
@@ -13,6 +14,7 @@
|
||||
All security bugs should be reported to the contact as below:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- zhengkun@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
|
||||
|
||||
+37
-11
@@ -38,13 +38,15 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
|
||||
// @Router /apps/sync [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
|
||||
func (b *BaseApi) SyncApp(c *gin.Context) {
|
||||
global.LOG.Infof("sync app list start ...")
|
||||
if err := appService.SyncAppList(); err != nil {
|
||||
global.LOG.Errorf("sync app list error [%s]", err.Error())
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
global.LOG.Infof("sync app list success!")
|
||||
go appService.SyncAppListFromLocal()
|
||||
go func() {
|
||||
global.LOG.Infof("sync app list start ...")
|
||||
if err := appService.SyncAppListFromRemote(); err != nil {
|
||||
global.LOG.Errorf("sync app list error [%s]", err.Error())
|
||||
} else {
|
||||
global.LOG.Infof("sync app list success!")
|
||||
}
|
||||
}()
|
||||
helper.SuccessWithData(c, "")
|
||||
}
|
||||
|
||||
@@ -71,14 +73,15 @@ func (b *BaseApi) GetApp(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Search app detail by id
|
||||
// @Description 通过 id 获取应用详情
|
||||
// @Summary Search app detail by appid
|
||||
// @Description 通过 appid 获取应用详情
|
||||
// @Accept json
|
||||
// @Param appId path integer true "app id"
|
||||
// @Param version path string true "app 版本"
|
||||
// @Param version path string true "app 类型"
|
||||
// @Success 200 {object} response.AppDetailDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/detail/:appId/:version [get]
|
||||
// @Router /apps/detail/:appId/:version/:type [get]
|
||||
func (b *BaseApi) GetAppDetail(c *gin.Context) {
|
||||
appId, err := helper.GetIntParamByKey(c, "appId")
|
||||
if err != nil {
|
||||
@@ -86,7 +89,30 @@ func (b *BaseApi) GetAppDetail(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
version := c.Param("version")
|
||||
appDetailDTO, err := appService.GetAppDetail(appId, version)
|
||||
appType := c.Param("type")
|
||||
appDetailDTO, err := appService.GetAppDetail(appId, version, appType)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, appDetailDTO)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Get app detail by id
|
||||
// @Description 通过 id 获取应用详情
|
||||
// @Accept json
|
||||
// @Param appId path integer true "id"
|
||||
// @Success 200 {object} response.AppDetailDTO
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/details/:id [get]
|
||||
func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
|
||||
appDetailID, err := helper.GetIntParamByKey(c, "id")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
appDetailDTO, err := appService.GetAppDetailByID(appDetailID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
|
||||
@@ -93,24 +93,24 @@ func (b *BaseApi) LoadPort(c *gin.Context) {
|
||||
|
||||
// @Tags App
|
||||
// @Summary Search app password by key
|
||||
// @Description 获取应用密码
|
||||
// @Description 获取应用连接信息
|
||||
// @Accept json
|
||||
// @Param key path string true "request"
|
||||
// @Success 200 {string} password
|
||||
// @Success 200 {string} response.DatabaseConn
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/installed/loadpassword/:key [get]
|
||||
func (b *BaseApi) LoadPassword(c *gin.Context) {
|
||||
// @Router /apps/installed/conninfo/:key [get]
|
||||
func (b *BaseApi) LoadConnInfo(c *gin.Context) {
|
||||
key, ok := c.Params.Get("key")
|
||||
if !ok {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error key in path"))
|
||||
return
|
||||
}
|
||||
password, err := appInstallService.LoadPassword(key)
|
||||
conn, err := appInstallService.LoadConnInfo(key)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, password)
|
||||
helper.SuccessWithData(c, conn)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"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"
|
||||
@@ -28,7 +26,7 @@ func (b *BaseApi) Login(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if req.AuthMethod != "jwt" {
|
||||
if req.AuthMethod != "jwt" && !req.IgnoreCaptcha {
|
||||
if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -102,71 +100,10 @@ func (b *BaseApi) Captcha(c *gin.Context) {
|
||||
// @Summary Load safety status
|
||||
// @Description 获取系统安全登录状态
|
||||
// @Success 200
|
||||
// @Failure 402
|
||||
// @Router /auth/status [get]
|
||||
func (b *BaseApi) GetSafetyStatus(c *gin.Context) {
|
||||
if err := authService.SafetyStatus(c); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) SafeEntrance(c *gin.Context) {
|
||||
code, exist := c.Params.Get("code")
|
||||
if !exist {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
|
||||
return
|
||||
}
|
||||
ok, err := authService.VerifyCode(code)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
|
||||
return
|
||||
}
|
||||
if err := authService.SafeEntrance(c, code); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Check is First login
|
||||
// @Description 判断是否为首次登录
|
||||
// @Success 200
|
||||
// @Router /auth/status [get]
|
||||
func (b *BaseApi) CheckIsFirstLogin(c *gin.Context) {
|
||||
helper.SuccessWithData(c, authService.CheckIsFirst())
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Init user
|
||||
// @Description 初始化用户
|
||||
// @Accept json
|
||||
// @Param request body dto.InitUser true "request"
|
||||
// @Success 200
|
||||
// @Router /auth/init [post]
|
||||
func (b *BaseApi) InitUserInfo(c *gin.Context) {
|
||||
var req dto.InitUser
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := authService.InitUser(c, req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
// @Router /auth/issafety [get]
|
||||
func (b *BaseApi) CheckIsSafety(c *gin.Context) {
|
||||
code := c.DefaultQuery("code", "")
|
||||
helper.SuccessWithData(c, authService.CheckIsSafety(code))
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
|
||||
@@ -2,6 +2,8 @@ package v1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -104,9 +106,9 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"backup_accounts","output_colume":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":true,"db":"backup_accounts","output_colume":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
func (b *BaseApi) DeleteBackup(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
var req dto.OperateByID
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
@@ -116,7 +118,7 @@ func (b *BaseApi) DeleteBackup(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := backupService.BatchDelete(req.Ids); err != nil {
|
||||
if err := backupService.Delete(req.ID); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
@@ -356,6 +358,14 @@ func (b *BaseApi) Recover(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.Source != "LOCAL" {
|
||||
downloadPath, err := backupService.DownloadRecord(dto.DownloadRecord{Source: req.Source, FileDir: path.Dir(req.File), FileName: path.Base(req.File)})
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("download file failed, err: %v", err))
|
||||
return
|
||||
}
|
||||
req.File = downloadPath
|
||||
}
|
||||
switch req.Type {
|
||||
case "mysql":
|
||||
if err := backupService.MysqlRecover(req); err != nil {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"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/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -77,8 +78,9 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.StartTime = req.StartTime.Add(8 * time.Hour)
|
||||
req.EndTime = req.EndTime.Add(8 * time.Hour)
|
||||
loc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
req.StartTime = req.StartTime.In(loc)
|
||||
req.EndTime = req.EndTime.In(loc)
|
||||
|
||||
total, list, err := cronjobService.SearchRecords(req)
|
||||
if err != nil {
|
||||
@@ -92,17 +94,41 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Clean job records
|
||||
// @Description 清空计划任务记录
|
||||
// @Accept json
|
||||
// @Param request body dto.CronjobClean true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/records/clean [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"}
|
||||
func (b *BaseApi) CleanRecord(c *gin.Context) {
|
||||
var req dto.CronjobClean
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := cronjobService.CleanRecord(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Delete cronjob
|
||||
// @Description 删除计划任务
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchDeleteReq true "request"
|
||||
// @Param request body dto.CronjobBatchDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /cronjobs/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"cronjobs","output_colume":"name","output_value":"names"}],"formatZH":"删除计划任务 [names]","formatEN":"delete cronjob [names]"}
|
||||
func (b *BaseApi) DeleteCronjob(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
var req dto.CronjobBatchDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
@@ -112,7 +138,7 @@ func (b *BaseApi) DeleteCronjob(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := cronjobService.Delete(req.Ids); err != nil {
|
||||
if err := cronjobService.Delete(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
@@ -35,7 +34,7 @@ func (b *BaseApi) LoadDaemonJsonFile(c *gin.Context) {
|
||||
helper.SuccessWithData(c, "daemon.json is not find in path")
|
||||
return
|
||||
}
|
||||
content, err := ioutil.ReadFile(constant.DaemonJsonPath)
|
||||
content, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
|
||||
+28
-25
@@ -9,40 +9,43 @@ type ApiGroup struct {
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
|
||||
var (
|
||||
authService = service.ServiceGroupApp.AuthService
|
||||
dashboardService = service.ServiceGroupApp.DashboardService
|
||||
authService = service.NewIAuthService()
|
||||
dashboardService = service.NewIDashboardService()
|
||||
|
||||
appService = service.NewIAppService()
|
||||
appInstallService = service.ServiceGroupApp.AppInstallService
|
||||
appInstallService = service.NewIAppInstalledService()
|
||||
|
||||
containerService = service.ServiceGroupApp.ContainerService
|
||||
composeTemplateService = service.ServiceGroupApp.ComposeTemplateService
|
||||
imageRepoService = service.ServiceGroupApp.ImageRepoService
|
||||
imageService = service.ServiceGroupApp.ImageService
|
||||
dockerService = service.ServiceGroupApp.DockerService
|
||||
containerService = service.NewIContainerService()
|
||||
composeTemplateService = service.NewIComposeTemplateService()
|
||||
imageRepoService = service.NewIImageRepoService()
|
||||
imageService = service.NewIImageService()
|
||||
dockerService = service.NewIDockerService()
|
||||
|
||||
mysqlService = service.ServiceGroupApp.MysqlService
|
||||
redisService = service.ServiceGroupApp.RedisService
|
||||
mysqlService = service.NewIMysqlService()
|
||||
redisService = service.NewIRedisService()
|
||||
|
||||
cronjobService = service.ServiceGroupApp.CronjobService
|
||||
cronjobService = service.NewICronjobService()
|
||||
|
||||
hostService = service.ServiceGroupApp.HostService
|
||||
groupService = service.ServiceGroupApp.GroupService
|
||||
fileService = service.ServiceGroupApp.FileService
|
||||
hostService = service.NewIHostService()
|
||||
groupService = service.NewIGroupService()
|
||||
fileService = service.NewIFileService()
|
||||
firewallService = service.NewIFirewallService()
|
||||
|
||||
settingService = service.ServiceGroupApp.SettingService
|
||||
backupService = service.ServiceGroupApp.BackupService
|
||||
settingService = service.NewISettingService()
|
||||
backupService = service.NewIBackupService()
|
||||
|
||||
commandService = service.ServiceGroupApp.CommandService
|
||||
commandService = service.NewICommandService()
|
||||
|
||||
websiteService = service.ServiceGroupApp.WebsiteService
|
||||
websiteDnsAccountService = service.ServiceGroupApp.WebsiteDnsAccountService
|
||||
websiteSSLService = service.ServiceGroupApp.WebsiteSSLService
|
||||
websiteAcmeAccountService = service.ServiceGroupApp.WebsiteAcmeAccountService
|
||||
websiteService = service.NewIWebsiteService()
|
||||
websiteDnsAccountService = service.NewIWebsiteDnsAccountService()
|
||||
websiteSSLService = service.NewIWebsiteSSLService()
|
||||
websiteAcmeAccountService = service.NewIWebsiteAcmeAccountService()
|
||||
|
||||
nginxService = service.ServiceGroupApp.NginxService
|
||||
nginxService = service.NewINginxService()
|
||||
|
||||
logService = service.ServiceGroupApp.LogService
|
||||
snapshotService = service.ServiceGroupApp.SnapshotService
|
||||
upgradeService = service.ServiceGroupApp.UpgradeService
|
||||
logService = service.NewILogService()
|
||||
snapshotService = service.NewISnapshotService()
|
||||
upgradeService = service.NewIUpgradeService()
|
||||
|
||||
runtimeService = service.NewRuntimeService()
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ package v1
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
@@ -186,7 +186,29 @@ func (b *BaseApi) ChangeFileMode(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Change file owner
|
||||
// @Description 修改文件用户/组
|
||||
// @Accept json
|
||||
// @Param request body request.FileRoleUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/owner [post]
|
||||
// @x-panel-log {"bodyKeys":["path","user","group"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改用户/组 [paths] => [user]/[group]","formatEN":"Change owner [paths] => [user]/[group]"}
|
||||
func (b *BaseApi) ChangeFileOwner(c *gin.Context) {
|
||||
var req request.FileRoleUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := fileService.ChangeOwner(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
@@ -498,7 +520,6 @@ func (b *BaseApi) Size(c *gin.Context) {
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/loadfile [post]
|
||||
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"读取文件 [path]","formatEN":"Read file [path]"}
|
||||
func (b *BaseApi) LoadFromFile(c *gin.Context) {
|
||||
var req dto.FilePath
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -510,7 +531,7 @@ func (b *BaseApi) LoadFromFile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(req.Path)
|
||||
content, err := os.ReadFile(req.Path)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -533,7 +554,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
|
||||
|
||||
for i := 0; i < chunkCount; i++ {
|
||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", fileName, i))
|
||||
chunkData, err := ioutil.ReadFile(chunkPath)
|
||||
chunkData, err := os.ReadFile(chunkPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -599,14 +620,14 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
}
|
||||
defer emptyFile.Close()
|
||||
|
||||
chunkData, err := ioutil.ReadAll(uploadFile)
|
||||
chunkData, err := io.ReadAll(uploadFile)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrFileUpload, err)
|
||||
return
|
||||
}
|
||||
|
||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
|
||||
err = ioutil.WriteFile(chunkPath, chunkData, 0644)
|
||||
err = os.WriteFile(chunkPath, chunkData, 0644)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
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/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Load firewall base info
|
||||
// @Description 获取防火墙基础信息
|
||||
// @Success 200 {object} dto.FirewallBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/firewall/base [get]
|
||||
func (b *BaseApi) LoadFirewallBaseInfo(c *gin.Context) {
|
||||
data, err := firewallService.LoadBaseInfo()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Page firewall rules
|
||||
// @Description 获取防火墙规则列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/firewall/search [post]
|
||||
func (b *BaseApi) SearchFirewallRule(c *gin.Context) {
|
||||
var req dto.RuleSearch
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := firewallService.SearchWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Page firewall status
|
||||
// @Description 修改防火墙状态
|
||||
// @Accept json
|
||||
// @Param request body dto.FirewallOperation true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/firewall/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"[operation] 防火墙","formatEN":"[operation] firewall"}
|
||||
func (b *BaseApi) OperateFirewall(c *gin.Context) {
|
||||
var req dto.FirewallOperation
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := firewallService.OperateFirewall(req.Operation); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Description 创建防火墙端口规则
|
||||
// @Accept json
|
||||
// @Param request body dto.PortRuleOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/firewall/port [post]
|
||||
// @x-panel-log {"bodyKeys":["port","strategy"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"添加端口规则 [strategy] [port]","formatEN":"create port rules [strategy][port]"}
|
||||
func (b *BaseApi) OperatePortRule(c *gin.Context) {
|
||||
var req dto.PortRuleOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := firewallService.OperatePortRule(req, true); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Description 创建防火墙 IP 规则
|
||||
// @Accept json
|
||||
// @Param request body dto.AddrRuleOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/firewall/ip [post]
|
||||
// @x-panel-log {"bodyKeys":["strategy","address"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"添加 ip 规则 [strategy] [address]","formatEN":"create address rules [strategy][address]"}
|
||||
func (b *BaseApi) OperateIPRule(c *gin.Context) {
|
||||
var req dto.AddrRuleOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := firewallService.OperateAddressRule(req, true); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Description 批量删除防火墙规则
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchRuleOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/firewall/batch [post]
|
||||
func (b *BaseApi) BatchOperateRule(c *gin.Context) {
|
||||
var req dto.BatchRuleOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := firewallService.BacthOperateRule(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Description 更新端口防火墙规则
|
||||
// @Accept json
|
||||
// @Param request body dto.PortRuleUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/firewall/update/port [post]
|
||||
func (b *BaseApi) UpdatePortRule(c *gin.Context) {
|
||||
var req dto.PortRuleUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := firewallService.UpdatePortRule(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Firewall
|
||||
// @Summary Create group
|
||||
// @Description 更新 ip 防火墙规则
|
||||
// @Accept json
|
||||
// @Param request body dto.AddrRuleUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/firewall/update/addr [post]
|
||||
func (b *BaseApi) UpdateAddrRule(c *gin.Context) {
|
||||
var req dto.AddrRuleUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := firewallService.UpdateAddrRule(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
// @Param request body dto.GroupCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/group [post]
|
||||
// @Router /groups [post]
|
||||
// @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建组 [name][type]","formatEN":"create group [name][type]"}
|
||||
func (b *BaseApi) CreateGroup(c *gin.Context) {
|
||||
var req dto.GroupCreate
|
||||
@@ -41,7 +41,7 @@ func (b *BaseApi) CreateGroup(c *gin.Context) {
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/group/del [post]
|
||||
// @Router /groups/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"name","output_value":"name"},{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"}
|
||||
func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
@@ -68,7 +68,7 @@ func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
||||
// @Param request body dto.GroupUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/group/update [post]
|
||||
// @Router /groups/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新组 [name][type]","formatEN":"update group [name][type]"}
|
||||
func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
||||
var req dto.GroupUpdate
|
||||
@@ -94,7 +94,7 @@ func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
||||
// @Param request body dto.GroupSearch true "request"
|
||||
// @Success 200 {anrry} dto.GroupInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/group/search [post]
|
||||
// @Router /groups/search [post]
|
||||
func (b *BaseApi) ListGroup(c *gin.Context) {
|
||||
var req dto.GroupSearch
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -83,6 +83,15 @@ func SuccessWithData(ctx *gin.Context, data interface{}) {
|
||||
ctx.Abort()
|
||||
}
|
||||
|
||||
func SuccessWithOutData(ctx *gin.Context) {
|
||||
res := dto.Response{
|
||||
Code: constant.CodeSuccess,
|
||||
Message: "success",
|
||||
}
|
||||
ctx.JSON(http.StatusOK, res)
|
||||
ctx.Abort()
|
||||
}
|
||||
|
||||
func SuccessWithMsg(ctx *gin.Context, msg string) {
|
||||
res := dto.Response{
|
||||
Code: constant.CodeSuccess,
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ssh"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -74,33 +73,9 @@ func (b *BaseApi) TestByInfo(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if req.AuthMode == "password" && len(req.Password) != 0 {
|
||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.Password = string(password)
|
||||
}
|
||||
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
|
||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.PrivateKey = string(privateKey)
|
||||
}
|
||||
|
||||
var connInfo ssh.ConnInfo
|
||||
_ = copier.Copy(&connInfo, &req)
|
||||
connInfo.PrivateKey = []byte(req.PrivateKey)
|
||||
client, err := connInfo.NewClient()
|
||||
if err != nil {
|
||||
helper.SuccessWithData(c, false)
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
helper.SuccessWithData(c, true)
|
||||
connStatus := hostService.TestByInfo(req)
|
||||
helper.SuccessWithData(c, connStatus)
|
||||
}
|
||||
|
||||
// @Tags Host
|
||||
@@ -270,11 +245,13 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
upMap["port"] = req.Port
|
||||
upMap["user"] = req.User
|
||||
upMap["auth_mode"] = req.AuthMode
|
||||
upMap["remember_password"] = req.RememberPassword
|
||||
if len(req.Password) != 0 {
|
||||
upMap["password"] = req.Password
|
||||
}
|
||||
if len(req.PrivateKey) != 0 {
|
||||
upMap["private_key"] = req.PrivateKey
|
||||
upMap["pass_phrase"] = req.PassPhrase
|
||||
}
|
||||
upMap["description"] = req.Description
|
||||
if err := hostService.Update(req.ID, upMap); err != nil {
|
||||
@@ -291,7 +268,7 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
// @Param request body dto.ChangeHostGroup true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/update [post]
|
||||
// @Router /hosts/update/group [post]
|
||||
// @x-panel-log {"bodyKeys":["id","group"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"hosts","output_colume":"addr","output_value":"addr"}],"formatZH":"切换主机[addr]分组 => [group]","formatEN":"change host [addr] group => [group]"}
|
||||
func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
|
||||
var req dto.ChangeHostGroup
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
@@ -24,8 +25,9 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.StartTime = req.StartTime.Add(8 * time.Hour)
|
||||
req.EndTime = req.EndTime.Add(8 * time.Hour)
|
||||
loc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
req.StartTime = req.StartTime.In(loc)
|
||||
req.EndTime = req.EndTime.In(loc)
|
||||
|
||||
var backdatas []dto.MonitorData
|
||||
if req.Param == "all" || req.Param == "cpu" || req.Param == "memory" || req.Param == "load" {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
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/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Runtime
|
||||
// @Summary List runtimes
|
||||
// @Description 获取运行环境列表
|
||||
// @Accept json
|
||||
// @Param request body request.RuntimeSearch true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/search [post]
|
||||
func (b *BaseApi) SearchRuntimes(c *gin.Context) {
|
||||
var req request.RuntimeSearch
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
total, items, err := runtimeService.Page(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Total: total,
|
||||
Items: items,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Runtime
|
||||
// @Summary Create runtime
|
||||
// @Description 创建运行环境
|
||||
// @Accept json
|
||||
// @Param request body request.RuntimeCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建运行环境 [name]","formatEN":"Create runtime [name]"}
|
||||
func (b *BaseApi) CreateRuntime(c *gin.Context) {
|
||||
var req request.RuntimeCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := runtimeService.Create(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Delete runtime
|
||||
// @Description 删除运行环境
|
||||
// @Accept json
|
||||
// @Param request body request.RuntimeDelete true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"删除网站 [name]","formatEN":"Delete website [name]"}
|
||||
func (b *BaseApi) DeleteRuntime(c *gin.Context) {
|
||||
var req request.RuntimeDelete
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
err := runtimeService.Delete(req.ID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Runtime
|
||||
// @Summary Update runtime
|
||||
// @Description 更新运行环境
|
||||
// @Accept json
|
||||
// @Param request body request.RuntimeUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新运行环境 [name]","formatEN":"Update runtime [name]"}
|
||||
func (b *BaseApi) UpdateRuntime(c *gin.Context) {
|
||||
var req request.RuntimeUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := runtimeService.Update(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Runtime
|
||||
// @Summary Get runtime
|
||||
// @Description 获取运行环境
|
||||
// @Accept json
|
||||
// @Param id path string true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/:id [get]
|
||||
func (b *BaseApi) GetRuntime(c *gin.Context) {
|
||||
id, err := helper.GetIntParamByKey(c, "id")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
res, err := runtimeService.Get(id)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
@@ -92,6 +92,48 @@ func (b *BaseApi) UpdatePassword(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system ssl
|
||||
// @Description 修改系统 ssl 登录
|
||||
// @Accept json
|
||||
// @Param request body dto.SSLUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/ssl/update [post]
|
||||
// @x-panel-log {"bodyKeys":["ssl"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改系统 ssl => [ssl]","formatEN":"update system ssl => [ssl]"}
|
||||
func (b *BaseApi) UpdateSSL(c *gin.Context) {
|
||||
var req dto.SSLUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.UpdateSSL(c, req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load system cert info
|
||||
// @Description 获取证书信息
|
||||
// @Success 200 {object} dto.SettingInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/ssl/info [get]
|
||||
func (b *BaseApi) LoadFromCert(c *gin.Context) {
|
||||
info, err := settingService.LoadFromCert()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system port
|
||||
// @Description 更新系统端口
|
||||
@@ -154,7 +196,7 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
|
||||
// @Router /settings/time/sync [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步","formatEN":"sync system time"}
|
||||
func (b *BaseApi) SyncTime(c *gin.Context) {
|
||||
ntime, err := ntp.Getremotetime()
|
||||
ntime, err := ntp.GetRemoteTime()
|
||||
if err != nil {
|
||||
helper.SuccessWithData(c, time.Now().Format("2006-01-02 15:04:05 MST -0700"))
|
||||
return
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -42,6 +45,9 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
var connInfo ssh.ConnInfo
|
||||
_ = copier.Copy(&connInfo, &host)
|
||||
connInfo.PrivateKey = []byte(host.PrivateKey)
|
||||
if len(host.PassPhrase) != 0 {
|
||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||
}
|
||||
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
@@ -102,14 +108,15 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
commands := fmt.Sprintf("docker exec -it %s redis-cli", redisConf.ContainerName)
|
||||
commands := "redis-cli"
|
||||
if len(redisConf.Requirepass) != 0 {
|
||||
commands = fmt.Sprintf("docker exec -it %s redis-cli -a %s --no-auth-warning", redisConf.ContainerName, redisConf.Requirepass)
|
||||
commands = fmt.Sprintf("redis-cli -a %s --no-auth-warning", redisConf.Requirepass)
|
||||
}
|
||||
slave, err := terminal.NewCommand(commands)
|
||||
slave, err := terminal.NewCommand(fmt.Sprintf("docker exec -it %s %s", redisConf.ContainerName, commands))
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(redisConf.ContainerName, commands)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
@@ -172,6 +179,7 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, command)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
@@ -196,13 +204,30 @@ func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
global.LOG.Errorf("handler ws faled:, err: %v", err)
|
||||
dt := time.Now().Add(time.Second)
|
||||
if ctlerr := ws.WriteControl(websocket.CloseMessage, []byte(err.Error()), dt); ctlerr != nil {
|
||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
wsData, err := json.Marshal(terminal.WsMsg{
|
||||
Type: terminal.WsMsgCmd,
|
||||
Data: base64.StdEncoding.EncodeToString([]byte(err.Error())),
|
||||
})
|
||||
if err != nil {
|
||||
_ = ws.WriteMessage(websocket.TextMessage, []byte("{\"type\":\"cmd\",\"data\":\"failed to encoding to json\"}"))
|
||||
} else {
|
||||
_ = ws.WriteMessage(websocket.TextMessage, wsData)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func killBash(containerID, comm string) {
|
||||
sudo := ""
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
sudo = "sudo"
|
||||
}
|
||||
command := exec.Command("sh", "-c", fmt.Sprintf("%s kill -9 $(docker top %s -eo pid,command | grep '%s' | awk '{print $1}')", sudo, containerID, comm))
|
||||
_, _ = command.CombinedOutput()
|
||||
}
|
||||
|
||||
var upGrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024 * 1024 * 10,
|
||||
|
||||
+272
-10
@@ -78,14 +78,12 @@ func (b *BaseApi) CreateWebsite(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
tx, ctx := helper.GetTxAndContext()
|
||||
err := websiteService.CreateWebsite(ctx, req)
|
||||
|
||||
err := websiteService.CreateWebsite(req)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
tx.Commit()
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
@@ -127,14 +125,12 @@ func (b *BaseApi) DeleteWebsite(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
tx, ctx := helper.GetTxAndContext()
|
||||
err := websiteService.DeleteWebsite(ctx, req)
|
||||
|
||||
err := websiteService.DeleteWebsite(req)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
tx.Commit()
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
@@ -189,14 +185,16 @@ func (b *BaseApi) GetWebsite(c *gin.Context) {
|
||||
// @Param id path integer true "request"
|
||||
// @Success 200 {object} response.FileInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/:id/nginx [get]
|
||||
// @Router /websites/:id/config/:type [get]
|
||||
func (b *BaseApi) GetWebsiteNginx(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
fileInfo, err := websiteService.GetWebsiteNginxConfig(id)
|
||||
configType := c.Param("type")
|
||||
|
||||
fileInfo, err := websiteService.GetWebsiteNginxConfig(id, configType)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -496,3 +494,267 @@ func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Load websit php conf
|
||||
// @Description 获取网站 php 配置
|
||||
// @Accept json
|
||||
// @Param id path integer true "request"
|
||||
// @Success 200 {object} response.PHPConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/php/config/:id [get]
|
||||
func (b *BaseApi) GetWebsitePHPConfig(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
data, err := websiteService.GetPHPConfig(id)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Website PHP
|
||||
// @Summary Update website php conf
|
||||
// @Description 更新 网站 PHP 配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsitePHPConfigUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/php/config [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"}
|
||||
func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) {
|
||||
var req request.WebsitePHPConfigUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdatePHPConfig(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website PHP
|
||||
// @Summary Update php conf
|
||||
// @Description 更新 php 配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsitePHPFileUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/php/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
|
||||
func (b *BaseApi) UpdatePHPFile(c *gin.Context) {
|
||||
var req request.WebsitePHPFileUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdatePHPConfigFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get rewrite conf
|
||||
// @Description 获取伪静态配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxRewriteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/rewrite [post]
|
||||
func (b *BaseApi) GetRewriteConfig(c *gin.Context) {
|
||||
var req request.NginxRewriteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetRewriteConfig(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update rewrite conf
|
||||
// @Description 更新伪静态配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxRewriteUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/rewrite/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"伪静态配置修改 [domain]","formatEN":"Nginx conf rewrite update [domain]"}
|
||||
func (b *BaseApi) UpdateRewriteConfig(c *gin.Context) {
|
||||
var req request.NginxRewriteUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateRewriteConfig(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update Site Dir
|
||||
// @Description 更新网站目录
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteUpdateDir true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dir/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录","formatEN":"Update domain [domain] dir"}
|
||||
func (b *BaseApi) UpdateSiteDir(c *gin.Context) {
|
||||
var req request.WebsiteUpdateDir
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateSiteDir(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update Site Dir permission
|
||||
// @Description 更新网站目录权限
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteUpdateDirPermission true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dir/permission [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录权限","formatEN":"Update domain [domain] dir permission"}
|
||||
func (b *BaseApi) UpdateSiteDirPermission(c *gin.Context) {
|
||||
var req request.WebsiteUpdateDirPermission
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateSitePermission(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get proxy conf
|
||||
// @Description 获取反向代理配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxies [post]
|
||||
func (b *BaseApi) GetProxyConfig(c *gin.Context) {
|
||||
var req request.WebsiteProxyReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetProxies(req.ID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update proxy conf
|
||||
// @Description 修改反向代理配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyConfig true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxies/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 ","formatEN":"Update domain [domain] proxy config"}
|
||||
func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
var req request.WebsiteProxyConfig
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
err := websiteService.OperateProxy(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update proxy file
|
||||
// @Description 更新反向代理文件
|
||||
// @Accept json
|
||||
// @Param request body request.NginxProxyUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxy/file [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新反向代理文件 [domain]","formatEN":"Nginx conf proxy file update [domain]"}
|
||||
func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {
|
||||
var req request.NginxProxyUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateProxyFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get AuthBasic conf
|
||||
// @Description 获取密码访问配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxAuthReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/auths [post]
|
||||
func (b *BaseApi) GetAuthConfig(c *gin.Context) {
|
||||
var req request.NginxAuthReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetAuthBasics(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get AuthBasic conf
|
||||
// @Description 更新密码访问配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxAuthUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/auths/update [post]
|
||||
func (b *BaseApi) UpdateAuthConfig(c *gin.Context) {
|
||||
var req request.NginxAuthUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateAuthBasic(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ type AppFormFields struct {
|
||||
Disabled bool `json:"disabled"`
|
||||
Edit bool `json:"edit"`
|
||||
Rule string `json:"rule"`
|
||||
Multiple bool `json:"multiple"`
|
||||
Values []AppFormValue `json:"values"`
|
||||
}
|
||||
|
||||
|
||||
+6
-10
@@ -17,11 +17,12 @@ type MfaCredential struct {
|
||||
}
|
||||
|
||||
type Login struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Captcha string `json:"captcha"`
|
||||
CaptchaID string `json:"captchaID"`
|
||||
AuthMethod string `json:"authMethod"`
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
IgnoreCaptcha bool `json:"ignoreCaptcha"`
|
||||
Captcha string `json:"captcha"`
|
||||
CaptchaID string `json:"captchaID"`
|
||||
AuthMethod string `json:"authMethod"`
|
||||
}
|
||||
|
||||
type MFALogin struct {
|
||||
@@ -30,8 +31,3 @@ type MFALogin struct {
|
||||
Code string `json:"code"`
|
||||
AuthMethod string `json:"authMethod"`
|
||||
}
|
||||
|
||||
type InitUser struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ type CommonBackup struct {
|
||||
DetailName string `json:"detailName"`
|
||||
}
|
||||
type CommonRecover struct {
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql redis website"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
@@ -59,7 +60,7 @@ type BackupRecords struct {
|
||||
}
|
||||
|
||||
type DownloadRecord struct {
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL"`
|
||||
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO"`
|
||||
FileDir string `json:"fileDir" validate:"required"`
|
||||
FileName string `json:"fileName" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ type PageInfo struct {
|
||||
|
||||
type UpdateDescription struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Description string `json:"description"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
|
||||
type OperationWithName struct {
|
||||
|
||||
@@ -22,6 +22,10 @@ type ContainerInfo struct {
|
||||
State string `json:"state"`
|
||||
RunTime string `json:"runTime"`
|
||||
|
||||
CPUPercent float64 `json:"cpuPercent"`
|
||||
MemoryPercent float64 `json:"memoryPercent"`
|
||||
Ports []string `json:"ports"`
|
||||
|
||||
IsFromApp bool `json:"isFromApp"`
|
||||
IsFromCompose bool `json:"isFromCompose"`
|
||||
}
|
||||
@@ -59,8 +63,10 @@ type VolumeHelper struct {
|
||||
Mode string `json:"mode"`
|
||||
}
|
||||
type PortHelper struct {
|
||||
ContainerPort int `json:"containerPort"`
|
||||
HostPort int `json:"hostPort"`
|
||||
HostIP string `json:"hostIP"`
|
||||
HostPort string `json:"hostPort"`
|
||||
ContainerPort string `json:"containerPort"`
|
||||
Protocol string `json:"protocol"`
|
||||
}
|
||||
|
||||
type ContainerLog struct {
|
||||
@@ -140,6 +146,7 @@ type ComposeOperation struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start stop down"`
|
||||
WithFile bool `json:"withFile"`
|
||||
}
|
||||
type ComposeUpdate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
|
||||
@@ -52,6 +52,16 @@ type CronjobDownload struct {
|
||||
BackupAccountID uint `json:"backupAccountID" validate:"required"`
|
||||
}
|
||||
|
||||
type CronjobClean struct {
|
||||
CleanData bool `json:"cleanData"`
|
||||
CronjobID uint `json:"cronjobID" validate:"required"`
|
||||
}
|
||||
|
||||
type CronjobBatchDelete struct {
|
||||
CleanData bool `json:"cleanData"`
|
||||
IDs []uint `json:"ids"`
|
||||
}
|
||||
|
||||
type CronjobInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -5,11 +5,13 @@ type DaemonJsonUpdateByFile struct {
|
||||
}
|
||||
|
||||
type DaemonJsonConf struct {
|
||||
IsSwarm bool `json:"isSwarm"`
|
||||
Status string `json:"status"`
|
||||
Version string `json:"version"`
|
||||
Mirrors []string `json:"registryMirrors"`
|
||||
Registries []string `json:"insecureRegistries"`
|
||||
LiveRestore bool `json:"liveRestore"`
|
||||
IPTables bool `json:"iptables"`
|
||||
CgroupDriver string `json:"cgroupDriver"`
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package dto
|
||||
|
||||
type FirewallBaseInfo struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Version string `json:"version"`
|
||||
PingStatus string `json:"pingStatus"`
|
||||
}
|
||||
|
||||
type RuleSearch struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type FirewallOperation struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=start stop disablePing enablePing"`
|
||||
}
|
||||
|
||||
type PortRuleOperate struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=add remove"`
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port" validate:"required"`
|
||||
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
|
||||
}
|
||||
|
||||
type AddrRuleOperate struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=add remove"`
|
||||
Address string `json:"address" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
|
||||
}
|
||||
|
||||
type PortRuleUpdate struct {
|
||||
OldRule PortRuleOperate `json:"oldRule"`
|
||||
NewRule PortRuleOperate `json:"newRule"`
|
||||
}
|
||||
|
||||
type AddrRuleUpdate struct {
|
||||
OldRule AddrRuleOperate `json:"oldRule"`
|
||||
NewRule AddrRuleOperate `json:"newRule"`
|
||||
}
|
||||
|
||||
type BatchRuleOperate struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
Rules []PortRuleOperate `json:"rules"`
|
||||
}
|
||||
+26
-19
@@ -5,15 +5,17 @@ import (
|
||||
)
|
||||
|
||||
type HostOperate struct {
|
||||
ID uint `json:"id"`
|
||||
GroupID uint `json:"groupID"`
|
||||
Name string `json:"name"`
|
||||
Addr string `json:"addr" validate:"required"`
|
||||
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
|
||||
User string `json:"user" validate:"required"`
|
||||
AuthMode string `json:"authMode" validate:"oneof=password key"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Password string `json:"password"`
|
||||
ID uint `json:"id"`
|
||||
GroupID uint `json:"groupID"`
|
||||
Name string `json:"name"`
|
||||
Addr string `json:"addr" validate:"required"`
|
||||
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
|
||||
User string `json:"user" validate:"required"`
|
||||
AuthMode string `json:"authMode" validate:"oneof=password key"`
|
||||
Password string `json:"password"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
PassPhrase string `json:"passPhrase"`
|
||||
RememberPassword bool `json:"rememberPassword"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
@@ -23,8 +25,9 @@ type HostConnTest struct {
|
||||
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
|
||||
User string `json:"user" validate:"required"`
|
||||
AuthMode string `json:"authMode" validate:"oneof=password key"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Password string `json:"password"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
PassPhrase string `json:"passPhrase"`
|
||||
}
|
||||
|
||||
type SearchHostWithPage struct {
|
||||
@@ -43,15 +46,19 @@ type ChangeHostGroup struct {
|
||||
}
|
||||
|
||||
type HostInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
GroupID uint `json:"groupID"`
|
||||
GroupBelong string `json:"groupBelong"`
|
||||
Name string `json:"name"`
|
||||
Addr string `json:"addr"`
|
||||
Port uint `json:"port"`
|
||||
User string `json:"user"`
|
||||
AuthMode string `json:"authMode"`
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
GroupID uint `json:"groupID"`
|
||||
GroupBelong string `json:"groupBelong"`
|
||||
Name string `json:"name"`
|
||||
Addr string `json:"addr"`
|
||||
Port uint `json:"port"`
|
||||
User string `json:"user"`
|
||||
AuthMode string `json:"authMode"`
|
||||
Password string `json:"password"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
PassPhrase string `json:"passPhrase"`
|
||||
RememberPassword bool `json:"rememberPassword"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -28,13 +28,20 @@ type NginxParam struct {
|
||||
Params []string
|
||||
}
|
||||
|
||||
type NginxAuth struct {
|
||||
Username string `json:"username"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type NginxKey string
|
||||
|
||||
const (
|
||||
Index NginxKey = "index"
|
||||
LimitConn NginxKey = "limit-conn"
|
||||
SSL NginxKey = "ssl"
|
||||
HttpPer NginxKey = "http-per"
|
||||
Index NginxKey = "index"
|
||||
LimitConn NginxKey = "limit-conn"
|
||||
SSL NginxKey = "ssl"
|
||||
CACHE NginxKey = "cache"
|
||||
HttpPer NginxKey = "http-per"
|
||||
ProxyCache NginxKey = "proxy-cache"
|
||||
)
|
||||
|
||||
var ScopeKeyMap = map[NginxKey][]string{
|
||||
@@ -46,5 +53,7 @@ var ScopeKeyMap = map[NginxKey][]string{
|
||||
|
||||
var StaticFileKeyMap = map[NginxKey]struct {
|
||||
}{
|
||||
SSL: {},
|
||||
SSL: {},
|
||||
CACHE: {},
|
||||
ProxyCache: {},
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ type FileCreate struct {
|
||||
IsLink bool `json:"isLink"`
|
||||
IsSymlink bool `json:"isSymlink"`
|
||||
LinkPath string `json:"linkPath"`
|
||||
Sub bool `json:"sub"`
|
||||
}
|
||||
|
||||
type FileDelete struct {
|
||||
@@ -88,3 +89,10 @@ type DirSizeReq struct {
|
||||
type FileProcessReq struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type FileRoleUpdate struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
User string `json:"user" validate:"required"`
|
||||
Group string `json:"group" validate:"required"`
|
||||
Sub bool `json:"sub" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -19,3 +19,32 @@ type NginxConfigUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Params interface{} `json:"params"`
|
||||
}
|
||||
|
||||
type NginxRewriteReq struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxRewriteUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxProxyUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxAuthUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type NginxAuthReq struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package request
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
|
||||
type RuntimeSearch struct {
|
||||
dto.PageInfo
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type RuntimeCreate struct {
|
||||
AppDetailID uint `json:"appDetailId"`
|
||||
Name string `json:"name"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Resource string `json:"resource"`
|
||||
Image string `json:"image"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type RuntimeDelete struct {
|
||||
ID uint `json:"id"`
|
||||
}
|
||||
|
||||
type RuntimeUpdate struct {
|
||||
Name string `json:"name"`
|
||||
ID uint `json:"id"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Image string `json:"image"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
@@ -23,6 +23,14 @@ type WebsiteCreate struct {
|
||||
AppInstall NewAppInstall `json:"appInstall"`
|
||||
AppID uint `json:"appID"`
|
||||
AppInstallID uint `json:"appInstallID"`
|
||||
|
||||
RuntimeID uint `json:"runtimeID"`
|
||||
RuntimeConfig
|
||||
}
|
||||
|
||||
type RuntimeConfig struct {
|
||||
ProxyType string `json:"proxyType"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
type NewAppInstall struct {
|
||||
@@ -126,3 +134,46 @@ type WebsiteLogReq struct {
|
||||
type WebsiteDefaultUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsitePHPConfigUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Params map[string]string `json:"params" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsitePHPFileUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteUpdateDir struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
SiteDir string `json:"siteDir" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteUpdateDirPermission struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
User string `json:"user" validate:"required"`
|
||||
Group string `json:"group" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteProxyConfig struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
Enable bool `json:"enable" validate:"required"`
|
||||
Cache bool `json:"cache" validate:"required"`
|
||||
CacheTime int `json:"cacheTime" validate:"required"`
|
||||
CacheUnit string `json:"cacheUnit" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Modifier string `json:"modifier" validate:"required"`
|
||||
Match string `json:"match" validate:"required"`
|
||||
ProxyPass string `json:"proxyPass" validate:"required"`
|
||||
ProxyHost string `json:"proxyHost" validate:"required"`
|
||||
Content string `json:"content"`
|
||||
FilePath string `json:"filePath"`
|
||||
Replaces map[string]string `json:"replaces"`
|
||||
}
|
||||
|
||||
type WebsiteProxyReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
|
||||
type AppRes struct {
|
||||
@@ -43,6 +44,7 @@ type AppDetailDTO struct {
|
||||
model.AppDetail
|
||||
Enable bool `json:"enable"`
|
||||
Params interface{} `json:"params"`
|
||||
Image string `json:"image"`
|
||||
}
|
||||
|
||||
type AppInstalledDTO struct {
|
||||
@@ -54,6 +56,12 @@ type AppInstalledDTO struct {
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
}
|
||||
|
||||
type DatabaseConn struct {
|
||||
Password string `json:"password"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
type AppService struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
@@ -70,4 +78,6 @@ type AppParam struct {
|
||||
Type string `json:"type"`
|
||||
Values interface{} `json:"values"`
|
||||
ShowValue string `json:"showValue"`
|
||||
Required bool `json:"required"`
|
||||
Multiple bool `json:"multiple"`
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package response
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
|
||||
type NginxStatus struct {
|
||||
Active string `json:"active"`
|
||||
Accepts string `json:"accepts"`
|
||||
@@ -14,3 +16,8 @@ type NginxParam struct {
|
||||
Name string `json:"name"`
|
||||
Params []string `json:"params"`
|
||||
}
|
||||
|
||||
type NginxAuthRes struct {
|
||||
Enable bool `json:"enable"`
|
||||
Items []dto.NginxAuth `json:"items"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package response
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
|
||||
type RuntimeRes struct {
|
||||
model.Runtime
|
||||
AppParams []AppParam `json:"appParams"`
|
||||
AppID uint `json:"appId"`
|
||||
}
|
||||
@@ -10,6 +10,7 @@ type WebsiteDTO struct {
|
||||
AccessLogPath string `json:"accessLogPath"`
|
||||
SitePath string `json:"sitePath"`
|
||||
AppName string `json:"appName"`
|
||||
RuntimeName string `json:"runtimeName"`
|
||||
}
|
||||
|
||||
type WebsitePreInstallCheck struct {
|
||||
@@ -42,3 +43,11 @@ type WebsiteLog struct {
|
||||
Enable bool `json:"enable"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type PHPConfig struct {
|
||||
Params map[string]string `json:"params"`
|
||||
}
|
||||
|
||||
type NginxRewriteRes struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ type SettingInfo struct {
|
||||
Language string `json:"language"`
|
||||
|
||||
ServerPort string `json:"serverPort"`
|
||||
SSL string `json:"ssl"`
|
||||
SSLType string `json:"sslType"`
|
||||
SecurityEntrance string `json:"securityEntrance"`
|
||||
ExpirationDays string `json:"expirationDays"`
|
||||
ExpirationTime string `json:"expirationTime"`
|
||||
@@ -39,6 +41,23 @@ type SettingUpdate struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type SSLUpdate struct {
|
||||
SSLType string `json:"sslType"`
|
||||
Domain string `json:"domain"`
|
||||
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
|
||||
Cert string `json:"cert"`
|
||||
Key string `json:"key"`
|
||||
SSLID uint `json:"sslID"`
|
||||
}
|
||||
type SSLInfo struct {
|
||||
Domain string `json:"domain"`
|
||||
Timeout string `json:"timeout"`
|
||||
RootPath string `json:"rootPath"`
|
||||
Cert string `json:"cert"`
|
||||
Key string `json:"key"`
|
||||
SSLID uint `json:"sslID"`
|
||||
}
|
||||
|
||||
type PasswordUpdate struct {
|
||||
OldPassword string `json:"oldPassword" validate:"required"`
|
||||
NewPassword string `json:"newPassword" validate:"required"`
|
||||
@@ -49,8 +68,8 @@ type PortUpdate struct {
|
||||
}
|
||||
|
||||
type SnapshotCreate struct {
|
||||
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO"`
|
||||
Description string `json:"description"`
|
||||
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotRecover struct {
|
||||
IsNew bool `json:"isNew"`
|
||||
@@ -60,12 +79,12 @@ type SnapshotRecover struct {
|
||||
type SnapshotImport struct {
|
||||
From string `json:"from"`
|
||||
Names []string `json:"names"`
|
||||
Description string `json:"description"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
From string `json:"from"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
|
||||
@@ -16,6 +16,7 @@ type App struct {
|
||||
Github string `json:"github" gorm:"type:varchar(64);not null"`
|
||||
Document string `json:"document" gorm:"type:varchar(64);not null"`
|
||||
Recommend int `json:"recommend" gorm:"type:Integer;not null"`
|
||||
Resource string `json:"resource" gorm:"type:varchar;not null;default:remote"`
|
||||
Details []AppDetail `json:"-" gorm:"-:migration"`
|
||||
TagsKey []string `json:"-" gorm:"-"`
|
||||
AppTags []AppTag `json:"-" gorm:"-:migration"`
|
||||
|
||||
@@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
)
|
||||
@@ -26,9 +27,21 @@ type AppInstall struct {
|
||||
}
|
||||
|
||||
func (i *AppInstall) GetPath() string {
|
||||
return path.Join(constant.AppInstallDir, i.App.Key, i.Name)
|
||||
return path.Join(i.getAppPath(), i.Name)
|
||||
}
|
||||
|
||||
func (i *AppInstall) GetComposePath() string {
|
||||
return path.Join(constant.AppInstallDir, i.App.Key, i.Name, "docker-compose.yml")
|
||||
return path.Join(i.getAppPath(), i.Name, "docker-compose.yml")
|
||||
}
|
||||
|
||||
func (i *AppInstall) GetEnvPath() string {
|
||||
return path.Join(i.getAppPath(), i.Name, ".env")
|
||||
}
|
||||
|
||||
func (i *AppInstall) getAppPath() string {
|
||||
if i.App.Resource == constant.AppResourceLocal {
|
||||
return path.Join(constant.LocalAppInstallDir, strings.TrimPrefix(i.App.Key, constant.AppResourceLocal))
|
||||
} else {
|
||||
return path.Join(constant.AppInstallDir, i.App.Key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,17 @@ package model
|
||||
|
||||
type Host struct {
|
||||
BaseModel
|
||||
GroupID uint `gorm:"type:decimal;not null" json:"group_id"`
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
Addr string `gorm:"type:varchar(16);not null" json:"addr"`
|
||||
Port int `gorm:"type:decimal;not null" json:"port"`
|
||||
User string `gorm:"type:varchar(64);not null" json:"user"`
|
||||
AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"`
|
||||
Password string `gorm:"type:varchar(64)" json:"password"`
|
||||
PrivateKey string `gorm:"type:varchar(256)" json:"privateKey"`
|
||||
|
||||
GroupID uint `gorm:"type:decimal;not null" json:"group_id"`
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
Addr string `gorm:"type:varchar(16);not null" json:"addr"`
|
||||
Port int `gorm:"type:decimal;not null" json:"port"`
|
||||
User string `gorm:"type:varchar(64);not null" json:"user"`
|
||||
AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"`
|
||||
Password string `gorm:"type:varchar(64)" json:"password"`
|
||||
PrivateKey string `gorm:"type:varchar(256)" json:"privateKey"`
|
||||
PassPhrase string `gorm:"type:varchar(256)" json:"passPhrase"`
|
||||
RememberPassword bool `json:"rememberPassword"`
|
||||
|
||||
Description string `gorm:"type:varchar(256)" json:"description"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package model
|
||||
|
||||
type Runtime struct {
|
||||
BaseModel
|
||||
Name string `gorm:"type:varchar;not null" json:"name"`
|
||||
AppDetailID uint `gorm:"type:integer" json:"appDetailId"`
|
||||
Image string `gorm:"type:varchar" json:"image"`
|
||||
WorkDir string `gorm:"type:varchar" json:"workDir"`
|
||||
DockerCompose string `gorm:"type:varchar" json:"dockerCompose"`
|
||||
Env string `gorm:"type:varchar" json:"env"`
|
||||
Params string `gorm:"type:varchar" json:"params"`
|
||||
Version string `gorm:"type:varchar;not null" json:"version"`
|
||||
Type string `gorm:"type:varchar;not null" json:"type"`
|
||||
Status string `gorm:"type:varchar;not null" json:"status"`
|
||||
Resource string `gorm:"type:varchar;not null" json:"resource"`
|
||||
Message string `gorm:"type:longtext;" json:"message"`
|
||||
}
|
||||
@@ -4,23 +4,33 @@ import "time"
|
||||
|
||||
type Website struct {
|
||||
BaseModel
|
||||
Protocol string `gorm:"type:varchar(64);not null" json:"protocol"`
|
||||
PrimaryDomain string `gorm:"type:varchar(128);not null" json:"primaryDomain"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Alias string `gorm:"type:varchar(128);not null" json:"alias"`
|
||||
Remark string `gorm:"type:longtext;" json:"remark"`
|
||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||
HttpConfig string `gorm:"type:varchar(64);not null" json:"httpConfig"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
|
||||
WebsiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
|
||||
WebsiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
|
||||
Proxy string `gorm:"type:varchar(128);not null" json:"proxy"`
|
||||
ErrorLog bool `json:"errorLog"`
|
||||
AccessLog bool `json:"accessLog"`
|
||||
DefaultServer bool `json:"defaultServer"`
|
||||
Domains []WebsiteDomain `json:"domains" gorm:"-:migration"`
|
||||
WebsiteSSL WebsiteSSL `json:"webSiteSSL" gorm:"-:migration"`
|
||||
Protocol string `gorm:"type:varchar;not null" json:"protocol"`
|
||||
PrimaryDomain string `gorm:"type:varchar;not null" json:"primaryDomain"`
|
||||
Type string `gorm:"type:varchar;not null" json:"type"`
|
||||
Alias string `gorm:"type:varchar;not null" json:"alias"`
|
||||
Remark string `gorm:"type:longtext;" json:"remark"`
|
||||
Status string `gorm:"type:varchar;not null" json:"status"`
|
||||
HttpConfig string `gorm:"type:varchar;not null" json:"httpConfig"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
|
||||
Proxy string `gorm:"type:varchar;" json:"proxy"`
|
||||
ProxyType string `gorm:"type:varchar;" json:"proxyType"`
|
||||
SiteDir string `gorm:"type:varchar;" json:"siteDir"`
|
||||
ErrorLog bool `json:"errorLog"`
|
||||
AccessLog bool `json:"accessLog"`
|
||||
DefaultServer bool `json:"defaultServer"`
|
||||
Rewrite string `gorm:"type:varchar" json:"rewrite"`
|
||||
|
||||
WebsiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
|
||||
WebsiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
|
||||
RuntimeID uint `gorm:"type:integer" json:"runtimeID"`
|
||||
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
|
||||
|
||||
User string `gorm:"type:varchar;" json:"user"`
|
||||
Group string `gorm:"type:varchar;" json:"group"`
|
||||
|
||||
Domains []WebsiteDomain `json:"domains" gorm:"-:migration"`
|
||||
WebsiteSSL WebsiteSSL `json:"webSiteSSL" gorm:"-:migration"`
|
||||
}
|
||||
|
||||
func (w Website) TableName() string {
|
||||
|
||||
+26
-1
@@ -11,6 +11,25 @@ import (
|
||||
type AppRepo struct {
|
||||
}
|
||||
|
||||
type IAppRepo interface {
|
||||
WithKey(key string) DBOption
|
||||
WithType(typeStr string) DBOption
|
||||
OrderByRecommend() DBOption
|
||||
GetRecommend() DBOption
|
||||
WithResource(resource string) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.App, error)
|
||||
GetFirst(opts ...DBOption) (model.App, error)
|
||||
GetBy(opts ...DBOption) ([]model.App, error)
|
||||
BatchCreate(ctx context.Context, apps []model.App) error
|
||||
GetByKey(ctx context.Context, key string) (model.App, error)
|
||||
Create(ctx context.Context, app *model.App) error
|
||||
Save(ctx context.Context, app *model.App) error
|
||||
}
|
||||
|
||||
func NewIAppRepo() IAppRepo {
|
||||
return &AppRepo{}
|
||||
}
|
||||
|
||||
func (a AppRepo) WithKey(key string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("key = ?", key)
|
||||
@@ -35,12 +54,18 @@ func (a AppRepo) GetRecommend() DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppRepo) WithResource(resource string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("resource = ?", resource)
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppRepo) Page(page, size int, opts ...DBOption) (int64, []model.App, error) {
|
||||
var apps []model.App
|
||||
db := getDb(opts...).Model(&model.App{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Preload("AppTags").Find(&apps).Error
|
||||
err := db.Debug().Limit(size).Offset(size * (page - 1)).Preload("AppTags").Find(&apps).Error
|
||||
return count, apps, err
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,21 @@ import (
|
||||
type AppDetailRepo struct {
|
||||
}
|
||||
|
||||
type IAppDetailRepo interface {
|
||||
WithVersion(version string) DBOption
|
||||
WithAppId(id uint) DBOption
|
||||
GetFirst(opts ...DBOption) (model.AppDetail, error)
|
||||
Update(ctx context.Context, detail model.AppDetail) error
|
||||
BatchCreate(ctx context.Context, details []model.AppDetail) error
|
||||
DeleteByAppIds(ctx context.Context, appIds []uint) error
|
||||
GetBy(opts ...DBOption) ([]model.AppDetail, error)
|
||||
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIAppDetailRepo() IAppDetailRepo {
|
||||
return &AppDetailRepo{}
|
||||
}
|
||||
|
||||
func (a AppDetailRepo) WithVersion(version string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("version = ?", version)
|
||||
|
||||
@@ -3,6 +3,7 @@ package repo
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
@@ -11,6 +12,31 @@ import (
|
||||
|
||||
type AppInstallRepo struct{}
|
||||
|
||||
type IAppInstallRepo interface {
|
||||
WithDetailIdsIn(detailIds []uint) DBOption
|
||||
WithDetailIdNotIn(detailIds []uint) DBOption
|
||||
WithAppId(appId uint) DBOption
|
||||
WithAppIdsIn(appIds []uint) DBOption
|
||||
WithStatus(status string) DBOption
|
||||
WithServiceName(serviceName string) DBOption
|
||||
WithPort(port int) DBOption
|
||||
WithIdNotInWebsite() DBOption
|
||||
ListBy(opts ...DBOption) ([]model.AppInstall, error)
|
||||
GetFirst(opts ...DBOption) (model.AppInstall, error)
|
||||
Create(ctx context.Context, install *model.AppInstall) error
|
||||
Save(ctx context.Context, install *model.AppInstall) error
|
||||
DeleteBy(opts ...DBOption) error
|
||||
Delete(ctx context.Context, install model.AppInstall) error
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.AppInstall, error)
|
||||
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
|
||||
LoadBaseInfo(key string, name string) (*RootInfo, error)
|
||||
GetFirstByCtx(ctx context.Context, opts ...DBOption) (model.AppInstall, error)
|
||||
}
|
||||
|
||||
func NewIAppInstallRepo() IAppInstallRepo {
|
||||
return &AppInstallRepo{}
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) WithDetailIdsIn(detailIds []uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_detail_id in (?)", detailIds)
|
||||
@@ -73,13 +99,20 @@ func (a *AppInstallRepo) GetFirst(opts ...DBOption) (model.AppInstall, error) {
|
||||
return install, err
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) Create(ctx context.Context, install *model.AppInstall) error {
|
||||
db := getTx(ctx).Model(&model.AppInstall{})
|
||||
return db.Create(&install).Error
|
||||
func (a *AppInstallRepo) GetFirstByCtx(ctx context.Context, opts ...DBOption) (model.AppInstall, error) {
|
||||
var install model.AppInstall
|
||||
db := getTx(ctx, opts...).Model(&model.AppInstall{})
|
||||
err := db.Preload("App").First(&install).Error
|
||||
return install, err
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) Save(install *model.AppInstall) error {
|
||||
return getDb().Save(&install).Error
|
||||
func (a *AppInstallRepo) Create(ctx context.Context, install *model.AppInstall) error {
|
||||
db := getTx(ctx).Model(&model.AppInstall{})
|
||||
return db.Omit(clause.Associations).Create(&install).Error
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) Save(ctx context.Context, install *model.AppInstall) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Save(&install).Error
|
||||
}
|
||||
|
||||
func (a *AppInstallRepo) DeleteBy(opts ...DBOption) error {
|
||||
@@ -112,9 +145,11 @@ type RootInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Port int64 `json:"port"`
|
||||
HttpsPort int64 `json:"httpsPort"`
|
||||
Password string `json:"password"`
|
||||
UserPassword string `json:"userPassword"`
|
||||
ContainerName string `json:"containerName"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Param string `json:"param"`
|
||||
Env string `json:"env"`
|
||||
Key string `json:"key"`
|
||||
@@ -152,7 +187,9 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
|
||||
info.UserPassword = userPassword
|
||||
}
|
||||
info.Port = int64(appInstall.HttpPort)
|
||||
info.HttpsPort = int64(appInstall.HttpsPort)
|
||||
info.ID = appInstall.ID
|
||||
info.ServiceName = appInstall.ServiceName
|
||||
info.ContainerName = appInstall.ContainerName
|
||||
info.Name = appInstall.Name
|
||||
info.Env = appInstall.Env
|
||||
|
||||
@@ -11,6 +11,20 @@ import (
|
||||
type AppInstallResourceRpo struct {
|
||||
}
|
||||
|
||||
type IAppInstallResourceRpo interface {
|
||||
WithAppInstallId(appInstallId uint) DBOption
|
||||
WithLinkId(linkId uint) DBOption
|
||||
WithResourceId(resourceId uint) DBOption
|
||||
GetBy(opts ...DBOption) ([]model.AppInstallResource, error)
|
||||
GetFirst(opts ...DBOption) (model.AppInstallResource, error)
|
||||
Create(ctx context.Context, resource *model.AppInstallResource) error
|
||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIAppInstallResourceRpo() IAppInstallResourceRpo {
|
||||
return &AppInstallResourceRpo{}
|
||||
}
|
||||
|
||||
func (a AppInstallResourceRpo) WithAppInstallId(appInstallId uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("app_install_id = ?", appInstallId)
|
||||
|
||||
@@ -8,6 +8,18 @@ import (
|
||||
type AppTagRepo struct {
|
||||
}
|
||||
|
||||
type IAppTagRepo interface {
|
||||
BatchCreate(ctx context.Context, tags []*model.AppTag) error
|
||||
DeleteByAppIds(ctx context.Context, appIds []uint) error
|
||||
DeleteAll(ctx context.Context) error
|
||||
GetByAppId(appId uint) ([]model.AppTag, error)
|
||||
GetByTagIds(tagIds []uint) ([]model.AppTag, error)
|
||||
}
|
||||
|
||||
func NewIAppTagRepo() IAppTagRepo {
|
||||
return &AppTagRepo{}
|
||||
}
|
||||
|
||||
func (a AppTagRepo) BatchCreate(ctx context.Context, tags []*model.AppTag) error {
|
||||
return getTx(ctx).Create(&tags).Error
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ type IBackupRepo interface {
|
||||
Delete(opts ...DBOption) error
|
||||
DeleteRecord(ctx context.Context, opts ...DBOption) error
|
||||
WithByDetailName(detailName string) DBOption
|
||||
WithByFileName(fileName string) DBOption
|
||||
WithByType(backupType string) DBOption
|
||||
}
|
||||
|
||||
func NewIBackupRepo() IBackupRepo {
|
||||
|
||||
@@ -15,6 +15,7 @@ type ICommandRepo interface {
|
||||
Create(command *model.Command) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
Get(opts ...DBOption) (model.Command, error)
|
||||
}
|
||||
|
||||
func NewICommandRepo() ICommandRepo {
|
||||
|
||||
@@ -21,10 +21,15 @@ type ICommonRepo interface {
|
||||
WithIdsIn(ids []uint) DBOption
|
||||
WithByDate(startTime, endTime time.Time) DBOption
|
||||
WithByStartDate(startTime time.Time) DBOption
|
||||
WithByStatus(status string) DBOption
|
||||
}
|
||||
|
||||
type CommonRepo struct{}
|
||||
|
||||
func NewCommonRepo() ICommonRepo {
|
||||
return &CommonRepo{}
|
||||
}
|
||||
|
||||
func (c *CommonRepo) WithByID(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id = ?", id)
|
||||
|
||||
@@ -17,6 +17,7 @@ type IComposeTemplateRepo interface {
|
||||
|
||||
CreateRecord(compose *model.Compose) error
|
||||
DeleteRecord(opts ...DBOption) error
|
||||
ListRecord() ([]model.Compose, error)
|
||||
}
|
||||
|
||||
func NewIComposeTemplateRepo() IComposeTemplateRepo {
|
||||
|
||||
@@ -20,12 +20,15 @@ type ICronjobRepo interface {
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
|
||||
Create(cronjob *model.Cronjob) error
|
||||
WithByJobID(id int) DBOption
|
||||
WithByBackupID(id uint) DBOption
|
||||
WithByRecordDropID(id int) DBOption
|
||||
Save(id uint, cronjob model.Cronjob) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
DeleteRecord(opts ...DBOption) error
|
||||
StartRecords(cronjobID uint, fromLocal bool, targetPath string) model.JobRecords
|
||||
EndRecords(record model.JobRecords, status, message, records string)
|
||||
PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error)
|
||||
}
|
||||
|
||||
func NewICronjobRepo() ICronjobRepo {
|
||||
@@ -112,6 +115,18 @@ func (c *CronjobRepo) WithByJobID(id int) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByBackupID(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("target_dir_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByRecordDropID(id int) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id < ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) StartRecords(cronjobID uint, fromLocal bool, targetPath string) model.JobRecords {
|
||||
var record model.JobRecords
|
||||
record.StartTime = time.Now()
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package repo
|
||||
|
||||
type RepoGroup struct {
|
||||
CommonRepo
|
||||
AppRepo
|
||||
AppTagRepo
|
||||
TagRepo
|
||||
AppDetailRepo
|
||||
AppInstallRepo
|
||||
AppInstallResourceRpo
|
||||
ImageRepoRepo
|
||||
ComposeTemplateRepo
|
||||
MysqlRepo
|
||||
CronjobRepo
|
||||
HostRepo
|
||||
CommandRepo
|
||||
GroupRepo
|
||||
SettingRepo
|
||||
BackupRepo
|
||||
WebsiteRepo
|
||||
WebsiteDomainRepo
|
||||
WebsiteDnsAccountRepo
|
||||
WebsiteSSLRepo
|
||||
WebsiteAcmeAccountRepo
|
||||
LogRepo
|
||||
SnapshotRepo
|
||||
}
|
||||
|
||||
var RepoGroupApp = new(RepoGroup)
|
||||
+11
-11
@@ -25,7 +25,7 @@ func NewIHostRepo() IHostRepo {
|
||||
return &HostRepo{}
|
||||
}
|
||||
|
||||
func (u *HostRepo) Get(opts ...DBOption) (model.Host, error) {
|
||||
func (h *HostRepo) Get(opts ...DBOption) (model.Host, error) {
|
||||
var host model.Host
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
@@ -35,7 +35,7 @@ func (u *HostRepo) Get(opts ...DBOption) (model.Host, error) {
|
||||
return host, err
|
||||
}
|
||||
|
||||
func (u *HostRepo) GetList(opts ...DBOption) ([]model.Host, error) {
|
||||
func (h *HostRepo) GetList(opts ...DBOption) ([]model.Host, error) {
|
||||
var hosts []model.Host
|
||||
db := global.DB.Model(&model.Host{})
|
||||
for _, opt := range opts {
|
||||
@@ -45,7 +45,7 @@ func (u *HostRepo) GetList(opts ...DBOption) ([]model.Host, error) {
|
||||
return hosts, err
|
||||
}
|
||||
|
||||
func (u *HostRepo) Page(page, size int, opts ...DBOption) (int64, []model.Host, error) {
|
||||
func (h *HostRepo) Page(page, size int, opts ...DBOption) (int64, []model.Host, error) {
|
||||
var users []model.Host
|
||||
db := global.DB.Model(&model.Host{})
|
||||
for _, opt := range opts {
|
||||
@@ -57,7 +57,7 @@ func (u *HostRepo) Page(page, size int, opts ...DBOption) (int64, []model.Host,
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (c *HostRepo) WithByInfo(info string) DBOption {
|
||||
func (h *HostRepo) WithByInfo(info string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(info) == 0 {
|
||||
return g
|
||||
@@ -67,22 +67,22 @@ func (c *HostRepo) WithByInfo(info string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *HostRepo) WithByPort(port uint) DBOption {
|
||||
func (h *HostRepo) WithByPort(port uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("port = ?", port)
|
||||
}
|
||||
}
|
||||
func (u *HostRepo) WithByUser(user string) DBOption {
|
||||
func (h *HostRepo) WithByUser(user string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("user = ?", user)
|
||||
}
|
||||
}
|
||||
func (u *HostRepo) WithByAddr(addr string) DBOption {
|
||||
func (h *HostRepo) WithByAddr(addr string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("addr = ?", addr)
|
||||
}
|
||||
}
|
||||
func (u *HostRepo) WithByGroup(group string) DBOption {
|
||||
func (h *HostRepo) WithByGroup(group string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(group) == 0 {
|
||||
return g
|
||||
@@ -91,15 +91,15 @@ func (u *HostRepo) WithByGroup(group string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *HostRepo) Create(host *model.Host) error {
|
||||
func (h *HostRepo) Create(host *model.Host) error {
|
||||
return global.DB.Create(host).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
func (h *HostRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Host{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) Delete(opts ...DBOption) error {
|
||||
func (h *HostRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type RuntimeRepo struct {
|
||||
}
|
||||
|
||||
type IRuntimeRepo interface {
|
||||
WithName(name string) DBOption
|
||||
WithImage(image string) DBOption
|
||||
WithNotId(id uint) DBOption
|
||||
WithStatus(status string) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
|
||||
Create(ctx context.Context, runtime *model.Runtime) error
|
||||
Save(runtime *model.Runtime) error
|
||||
DeleteBy(opts ...DBOption) error
|
||||
GetFirst(opts ...DBOption) (*model.Runtime, error)
|
||||
}
|
||||
|
||||
func NewIRunTimeRepo() IRuntimeRepo {
|
||||
return &RuntimeRepo{}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("name = ?", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithStatus(status string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("status = ?", status)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithImage(image string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("image = ?", image)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithNotId(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id != ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error) {
|
||||
var runtimes []model.Runtime
|
||||
db := getDb(opts...).Model(&model.Runtime{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&runtimes).Error
|
||||
return count, runtimes, err
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) Create(ctx context.Context, runtime *model.Runtime) error {
|
||||
db := getTx(ctx).Model(&model.Runtime{})
|
||||
return db.Create(&runtime).Error
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) Save(runtime *model.Runtime) error {
|
||||
return getDb().Save(&runtime).Error
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) DeleteBy(opts ...DBOption) error {
|
||||
return getDb(opts...).Delete(&model.Runtime{}).Error
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) GetFirst(opts ...DBOption) (*model.Runtime, error) {
|
||||
var runtime model.Runtime
|
||||
if err := getDb(opts...).First(&runtime).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &runtime, nil
|
||||
}
|
||||
@@ -8,6 +8,19 @@ import (
|
||||
type TagRepo struct {
|
||||
}
|
||||
|
||||
type ITagRepo interface {
|
||||
BatchCreate(ctx context.Context, tags []*model.Tag) error
|
||||
DeleteAll(ctx context.Context) error
|
||||
All() ([]model.Tag, error)
|
||||
GetByIds(ids []uint) ([]model.Tag, error)
|
||||
GetByKeys(keys []string) ([]model.Tag, error)
|
||||
GetByAppId(appId uint) ([]model.Tag, error)
|
||||
}
|
||||
|
||||
func NewITagRepo() ITagRepo {
|
||||
return &TagRepo{}
|
||||
}
|
||||
|
||||
func (t TagRepo) BatchCreate(ctx context.Context, tags []*model.Tag) error {
|
||||
return getTx(ctx).Create(&tags).Error
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type IWebsiteRepo interface {
|
||||
WithGroupID(groupId uint) DBOption
|
||||
WithDefaultServer() DBOption
|
||||
WithDomainLike(domain string) DBOption
|
||||
WithRuntimeID(runtimeID uint) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
|
||||
List(opts ...DBOption) ([]model.Website, error)
|
||||
GetFirst(opts ...DBOption) (model.Website, error)
|
||||
@@ -40,6 +41,12 @@ func (w *WebsiteRepo) WithAppInstallId(appInstallId uint) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) WithRuntimeID(runtimeID uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("runtime_id = ?", runtimeID)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) WithDomain(domain string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("primary_domain = ?", domain)
|
||||
|
||||
@@ -7,6 +7,19 @@ import (
|
||||
type WebsiteDnsAccountRepo struct {
|
||||
}
|
||||
|
||||
type IWebsiteDnsAccountRepo interface {
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDnsAccount, error)
|
||||
GetFirst(opts ...DBOption) (*model.WebsiteDnsAccount, error)
|
||||
List(opts ...DBOption) ([]model.WebsiteDnsAccount, error)
|
||||
Create(account model.WebsiteDnsAccount) error
|
||||
Save(account model.WebsiteDnsAccount) error
|
||||
DeleteBy(opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIWebsiteDnsAccountRepo() IWebsiteDnsAccountRepo {
|
||||
return &WebsiteDnsAccountRepo{}
|
||||
}
|
||||
|
||||
func (w WebsiteDnsAccountRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDnsAccount, error) {
|
||||
var accounts []model.WebsiteDnsAccount
|
||||
db := getDb(opts...).Model(&model.WebsiteDnsAccount{})
|
||||
|
||||
@@ -10,6 +10,23 @@ import (
|
||||
type WebsiteDomainRepo struct {
|
||||
}
|
||||
|
||||
type IWebsiteDomainRepo interface {
|
||||
WithWebsiteId(websiteId uint) DBOption
|
||||
WithPort(port int) DBOption
|
||||
WithDomain(domain string) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDomain, error)
|
||||
GetFirst(opts ...DBOption) (model.WebsiteDomain, error)
|
||||
GetBy(opts ...DBOption) ([]model.WebsiteDomain, error)
|
||||
BatchCreate(ctx context.Context, domains []model.WebsiteDomain) error
|
||||
Create(ctx context.Context, app *model.WebsiteDomain) error
|
||||
Save(ctx context.Context, app *model.WebsiteDomain) error
|
||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIWebsiteDomainRepo() IWebsiteDomainRepo {
|
||||
return &WebsiteDomainRepo{}
|
||||
}
|
||||
|
||||
func (w WebsiteDomainRepo) WithWebsiteId(websiteId uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("website_id = ?", websiteId)
|
||||
|
||||
+290
-58
@@ -5,14 +5,15 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
|
||||
"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"
|
||||
@@ -32,10 +33,12 @@ type IAppService interface {
|
||||
PageApp(req request.AppSearch) (interface{}, error)
|
||||
GetAppTags() ([]response.TagDTO, error)
|
||||
GetApp(key string) (*response.AppDTO, error)
|
||||
GetAppDetail(appId uint, version string) (response.AppDetailDTO, error)
|
||||
GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)
|
||||
Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error)
|
||||
SyncAppList() error
|
||||
SyncAppListFromRemote() error
|
||||
GetAppUpdate() (*response.AppUpdateRes, error)
|
||||
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
||||
SyncAppListFromLocal()
|
||||
}
|
||||
|
||||
func NewIAppService() IAppService {
|
||||
@@ -138,7 +141,7 @@ func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
||||
return &appDTO, nil
|
||||
}
|
||||
|
||||
func (a AppService) GetAppDetail(appId uint, version string) (response.AppDetailDTO, error) {
|
||||
func (a AppService) GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error) {
|
||||
var (
|
||||
appDetailDTO response.AppDetailDTO
|
||||
opts []repo.DBOption
|
||||
@@ -148,14 +151,55 @@ func (a AppService) GetAppDetail(appId uint, version string) (response.AppDetail
|
||||
if err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
paramMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(detail.Params), ¶mMap); err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
appDetailDTO.AppDetail = detail
|
||||
appDetailDTO.Params = paramMap
|
||||
appDetailDTO.Enable = true
|
||||
|
||||
if appType == "runtime" {
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(appId))
|
||||
if err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
buildPath := path.Join(constant.AppResourceDir, app.Key, "versions", detail.Version, "build")
|
||||
paramsPath := path.Join(buildPath, "config.json")
|
||||
if !fileOp.Stat(paramsPath) {
|
||||
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
||||
}
|
||||
param, err := fileOp.GetContent(paramsPath)
|
||||
if err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
paramMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal(param, ¶mMap); err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
appDetailDTO.Params = paramMap
|
||||
composePath := path.Join(buildPath, "docker-compose.yml")
|
||||
if !fileOp.Stat(composePath) {
|
||||
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
||||
}
|
||||
compose, err := fileOp.GetContent(composePath)
|
||||
if err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
composeMap := make(map[string]interface{})
|
||||
if err := yaml.Unmarshal(compose, &composeMap); err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
if service, ok := composeMap["services"]; ok {
|
||||
servicesMap := service.(map[string]interface{})
|
||||
for k := range servicesMap {
|
||||
appDetailDTO.Image = k
|
||||
}
|
||||
}
|
||||
} else {
|
||||
paramMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(detail.Params), ¶mMap); err != nil {
|
||||
return appDetailDTO, err
|
||||
}
|
||||
appDetailDTO.Params = paramMap
|
||||
}
|
||||
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(detail.AppId))
|
||||
if err != nil {
|
||||
return appDetailDTO, err
|
||||
@@ -165,57 +209,75 @@ func (a AppService) GetAppDetail(appId uint, version string) (response.AppDetail
|
||||
}
|
||||
return appDetailDTO, nil
|
||||
}
|
||||
func (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) {
|
||||
res := &response.AppDetailDTO{}
|
||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.AppDetail = appDetail
|
||||
paramMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(appDetail.Params), ¶mMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Params = paramMap
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error) {
|
||||
if err := docker.CreateDefaultDockerNetwork(); err != nil {
|
||||
return nil, buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
|
||||
func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (appInstall *model.AppInstall, err error) {
|
||||
if err = docker.CreateDefaultDockerNetwork(); err != nil {
|
||||
err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
if list, _ := appInstallRepo.ListBy(commonRepo.WithByName(req.Name)); len(list) > 0 {
|
||||
return nil, buserr.New(constant.ErrNameIsExist)
|
||||
err = buserr.New(constant.ErrNameIsExist)
|
||||
return
|
||||
}
|
||||
httpPort, err := checkPort("PANEL_APP_PORT_HTTP", req.Params)
|
||||
var (
|
||||
httpPort int
|
||||
httpsPort int
|
||||
appDetail model.AppDetail
|
||||
app model.App
|
||||
)
|
||||
httpPort, err = checkPort("PANEL_APP_PORT_HTTP", req.Params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
httpsPort, err = checkPort("PANEL_APP_PORT_HTTPS", req.Params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appDetail, err = appDetailRepo.GetFirst(commonRepo.WithByID(req.AppDetailId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
app, err = appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpsPort, err := checkPort("PANEL_APP_PORT_HTTPS", req.Params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(req.AppDetailId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := checkRequiredAndLimit(app); err != nil {
|
||||
return nil, err
|
||||
if err = checkRequiredAndLimit(app); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
paramByte, err := json.Marshal(req.Params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appInstall := model.AppInstall{
|
||||
appInstall = &model.AppInstall{
|
||||
Name: req.Name,
|
||||
AppId: appDetail.AppId,
|
||||
AppDetailId: appDetail.ID,
|
||||
Version: appDetail.Version,
|
||||
Status: constant.Installing,
|
||||
Env: string(paramByte),
|
||||
HttpPort: httpPort,
|
||||
HttpsPort: httpsPort,
|
||||
App: app,
|
||||
}
|
||||
composeMap := make(map[string]interface{})
|
||||
if err := yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {
|
||||
return nil, err
|
||||
if err = yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
value, ok := composeMap["services"]
|
||||
if !ok {
|
||||
return nil, buserr.New("")
|
||||
err = buserr.New("")
|
||||
return
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
changeKeys := make(map[string]string, len(servicesMap))
|
||||
@@ -236,30 +298,52 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
servicesMap[v] = servicesMap[k]
|
||||
delete(servicesMap, k)
|
||||
}
|
||||
composeByte, err := yaml.Marshal(composeMap)
|
||||
|
||||
var (
|
||||
composeByte []byte
|
||||
paramByte []byte
|
||||
)
|
||||
|
||||
composeByte, err = yaml.Marshal(composeMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
appInstall.DockerCompose = string(composeByte)
|
||||
|
||||
if err := copyAppData(app.Key, appDetail.Version, req.Name, req.Params); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
hErr := handleAppInstallErr(ctx, appInstall)
|
||||
if hErr != nil {
|
||||
global.LOG.Errorf("delete app dir error %s", hErr.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = copyAppData(app.Key, appDetail.Version, req.Name, req.Params, app.Resource == constant.AppResourceLocal); err != nil {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.WriteFile(appInstall.GetComposePath(), strings.NewReader(string(composeByte)), 0775); err != nil {
|
||||
return nil, err
|
||||
if err = fileOp.WriteFile(appInstall.GetComposePath(), strings.NewReader(string(composeByte)), 0775); err != nil {
|
||||
return
|
||||
}
|
||||
paramByte, err = json.Marshal(req.Params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appInstall.Env = string(paramByte)
|
||||
|
||||
if err := appInstallRepo.Create(ctx, &appInstall); err != nil {
|
||||
return nil, err
|
||||
if err = appInstallRepo.Create(ctx, appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
if err := createLink(ctx, app, &appInstall, req.Params); err != nil {
|
||||
return nil, err
|
||||
if err = createLink(ctx, app, appInstall, req.Params); err != nil {
|
||||
return
|
||||
}
|
||||
go upApp(appInstall.GetComposePath(), appInstall)
|
||||
if err = upAppPre(app, appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
go upApp(appInstall)
|
||||
go updateToolApp(appInstall)
|
||||
return &appInstall, nil
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
@@ -272,12 +356,11 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
}
|
||||
versionUrl := fmt.Sprintf("%s/%s/%s/appstore/apps.json", global.CONF.System.RepoUrl, global.CONF.System.Mode, setting.SystemVersion)
|
||||
versionRes, err := http.Get(versionUrl)
|
||||
global.LOG.Infof("get current version from [%s]", versionUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer versionRes.Body.Close()
|
||||
body, err := ioutil.ReadAll(versionRes.Body)
|
||||
body, err := io.ReadAll(versionRes.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -294,7 +377,155 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) SyncAppList() error {
|
||||
func (a AppService) SyncAppListFromLocal() {
|
||||
fileOp := files.NewFileOp()
|
||||
appDir := constant.LocalAppResourceDir
|
||||
listFile := path.Join(appDir, "list.json")
|
||||
if !fileOp.Stat(listFile) {
|
||||
return
|
||||
}
|
||||
global.LOG.Infof("start sync local apps...")
|
||||
content, err := fileOp.GetContent(listFile)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get list.json content failed %s", err.Error())
|
||||
return
|
||||
}
|
||||
list := &dto.AppList{}
|
||||
if err := json.Unmarshal(content, list); err != nil {
|
||||
global.LOG.Errorf("unmarshal list.json failed %s", err.Error())
|
||||
return
|
||||
}
|
||||
oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))
|
||||
appsMap := getApps(oldApps, list.Items, true)
|
||||
for _, l := range list.Items {
|
||||
localKey := "local" + l.Key
|
||||
app := appsMap[localKey]
|
||||
icon, err := os.ReadFile(path.Join(appDir, l.Key, "metadata", "logo.png"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] icon error: %s", l.Name, err.Error())
|
||||
continue
|
||||
}
|
||||
iconStr := base64.StdEncoding.EncodeToString(icon)
|
||||
app.Icon = iconStr
|
||||
app.TagsKey = append(l.Tags, "Local")
|
||||
app.Recommend = 9999
|
||||
versions := l.Versions
|
||||
detailsMap := getAppDetails(app.Details, versions)
|
||||
|
||||
for _, v := range versions {
|
||||
detail := detailsMap[v]
|
||||
detailPath := path.Join(appDir, l.Key, "versions", v)
|
||||
if _, err := os.Stat(detailPath); err != nil {
|
||||
global.LOG.Errorf("get [%s] folder error: %s", detailPath, err.Error())
|
||||
continue
|
||||
}
|
||||
readmeStr, err := os.ReadFile(path.Join(detailPath, "README.md"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] README error: %s", detailPath, err.Error())
|
||||
}
|
||||
detail.Readme = string(readmeStr)
|
||||
dockerComposeStr, err := os.ReadFile(path.Join(detailPath, "docker-compose.yml"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] docker-compose.yml error: %s", detailPath, err.Error())
|
||||
continue
|
||||
}
|
||||
detail.DockerCompose = string(dockerComposeStr)
|
||||
paramStr, err := os.ReadFile(path.Join(detailPath, "config.json"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get [%s] form.json error: %s", detailPath, err.Error())
|
||||
}
|
||||
detail.Params = string(paramStr)
|
||||
detailsMap[v] = detail
|
||||
}
|
||||
var newDetails []model.AppDetail
|
||||
for _, v := range detailsMap {
|
||||
newDetails = append(newDetails, v)
|
||||
}
|
||||
app.Details = newDetails
|
||||
appsMap[localKey] = app
|
||||
}
|
||||
var (
|
||||
addAppArray []model.App
|
||||
updateArray []model.App
|
||||
appIds []uint
|
||||
)
|
||||
for _, v := range appsMap {
|
||||
if v.ID == 0 {
|
||||
addAppArray = append(addAppArray, v)
|
||||
} else {
|
||||
updateArray = append(updateArray, v)
|
||||
appIds = append(appIds, v.ID)
|
||||
}
|
||||
}
|
||||
tx, ctx := getTxAndContext()
|
||||
if len(addAppArray) > 0 {
|
||||
if err := appRepo.BatchCreate(ctx, addAppArray); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, update := range updateArray {
|
||||
if err := appRepo.Save(ctx, &update); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := appTagRepo.DeleteByAppIds(ctx, appIds); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
apps := append(addAppArray, updateArray...)
|
||||
var (
|
||||
addDetails []model.AppDetail
|
||||
updateDetails []model.AppDetail
|
||||
appTags []*model.AppTag
|
||||
)
|
||||
tags, _ := tagRepo.All()
|
||||
tagMap := make(map[string]uint, len(tags))
|
||||
for _, app := range tags {
|
||||
tagMap[app.Key] = app.ID
|
||||
}
|
||||
for _, a := range apps {
|
||||
for _, t := range a.TagsKey {
|
||||
tagId, ok := tagMap[t]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: a.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, d := range a.Details {
|
||||
d.AppId = a.ID
|
||||
if d.ID == 0 {
|
||||
addDetails = append(addDetails, d)
|
||||
} else {
|
||||
updateDetails = append(updateDetails, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(addDetails) > 0 {
|
||||
if err := appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, u := range updateDetails {
|
||||
if err := appDetailRepo.Update(ctx, u); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(appTags) > 0 {
|
||||
if err := appTagRepo.BatchCreate(ctx, appTags); err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
global.LOG.Infof("sync local apps success")
|
||||
}
|
||||
func (a AppService) SyncAppListFromRemote() error {
|
||||
updateRes, err := a.GetAppUpdate()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -327,11 +558,11 @@ func (a AppService) SyncAppList() error {
|
||||
Name: t.Name,
|
||||
})
|
||||
}
|
||||
oldApps, err := appRepo.GetBy()
|
||||
oldApps, err := appRepo.GetBy(appRepo.WithResource(constant.AppResourceRemote))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appsMap := getApps(oldApps, list.Items)
|
||||
appsMap := getApps(oldApps, list.Items, false)
|
||||
for _, l := range list.Items {
|
||||
app := appsMap[l.Key]
|
||||
icon, err := os.ReadFile(path.Join(appDir, l.Key, "metadata", "logo.png"))
|
||||
@@ -387,8 +618,9 @@ func (a AppService) SyncAppList() error {
|
||||
var (
|
||||
addAppArray []model.App
|
||||
updateArray []model.App
|
||||
tagMap = make(map[string]uint, len(tags))
|
||||
)
|
||||
tagMap := make(map[string]uint, len(tags))
|
||||
|
||||
for _, v := range appsMap {
|
||||
if v.ID == 0 {
|
||||
addAppArray = append(addAppArray, v)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
@@ -38,7 +38,7 @@ type IAppInstallService interface {
|
||||
Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error)
|
||||
CheckExist(key string) (*response.AppInstalledCheck, error)
|
||||
LoadPort(key string) (int64, error)
|
||||
LoadPassword(key string) (string, error)
|
||||
LoadConnInfo(key string) (response.DatabaseConn, error)
|
||||
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error)
|
||||
Operate(req request.AppInstalledOperate) error
|
||||
Update(req request.AppInstalledUpdate) error
|
||||
@@ -133,12 +133,16 @@ func (a *AppInstallService) LoadPort(key string) (int64, error) {
|
||||
return app.Port, nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) LoadPassword(key string) (string, error) {
|
||||
func (a *AppInstallService) LoadConnInfo(key string) (response.DatabaseConn, error) {
|
||||
var data response.DatabaseConn
|
||||
app, err := appInstallRepo.LoadBaseInfo(key, "")
|
||||
if err != nil {
|
||||
return "", nil
|
||||
return data, nil
|
||||
}
|
||||
return app.Password, nil
|
||||
data.Password = app.Password
|
||||
data.ServiceName = app.ServiceName
|
||||
data.Port = app.Port
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error) {
|
||||
@@ -175,7 +179,7 @@ func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]
|
||||
}
|
||||
|
||||
func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallId))
|
||||
install, err := appInstallRepo.GetFirstByCtx(context.Background(), commonRepo.WithByID(req.InstallId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -202,17 +206,14 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
}
|
||||
return syncById(install.ID)
|
||||
case constant.Delete:
|
||||
tx, ctx := getTxAndContext()
|
||||
if err := deleteAppInstall(ctx, install, req.DeleteBackup, req.ForceDelete, req.DeleteDB); err != nil && !req.ForceDelete {
|
||||
tx.Rollback()
|
||||
if err := deleteAppInstall(install, req.DeleteBackup, req.ForceDelete, req.DeleteDB); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
case constant.Sync:
|
||||
return syncById(install.ID)
|
||||
case constant.Upgrade:
|
||||
return updateInstall(install.ID, req.DetailId)
|
||||
return upgradeInstall(install.ID, req.DetailId)
|
||||
default:
|
||||
return errors.New("operate not support")
|
||||
}
|
||||
@@ -224,27 +225,35 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
return err
|
||||
}
|
||||
changePort := false
|
||||
var (
|
||||
oldPorts []int
|
||||
newPorts []int
|
||||
)
|
||||
port, ok := req.Params["PANEL_APP_PORT_HTTP"]
|
||||
if ok {
|
||||
portN := int(math.Ceil(port.(float64)))
|
||||
if portN != installed.HttpPort {
|
||||
oldPorts = append(oldPorts, installed.HttpPort)
|
||||
changePort = true
|
||||
httpPort, err := checkPort("PANEL_APP_PORT_HTTP", req.Params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installed.HttpPort = httpPort
|
||||
newPorts = append(newPorts, httpPort)
|
||||
}
|
||||
}
|
||||
ports, ok := req.Params["PANEL_APP_PORT_HTTPS"]
|
||||
if ok {
|
||||
portN := int(math.Ceil(ports.(float64)))
|
||||
if portN != installed.HttpsPort {
|
||||
oldPorts = append(oldPorts, installed.HttpsPort)
|
||||
httpsPort, err := checkPort("PANEL_APP_PORT_HTTPS", req.Params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installed.HttpsPort = httpsPort
|
||||
newPorts = append(newPorts, httpsPort)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +271,7 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
if err := env.Write(oldEnvMaps, envPath); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = appInstallRepo.Save(&installed)
|
||||
_ = appInstallRepo.Save(context.Background(), &installed)
|
||||
|
||||
if err := rebuildApp(installed); err != nil {
|
||||
return err
|
||||
@@ -289,6 +298,11 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
}
|
||||
if changePort {
|
||||
go func() {
|
||||
_ = OperateFirewallPort(oldPorts, newPorts)
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -302,7 +316,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
if systemInit {
|
||||
i.Status = constant.Error
|
||||
i.Message = "System restart causes application exception"
|
||||
_ = appInstallRepo.Save(&i)
|
||||
_ = appInstallRepo.Save(context.Background(), &i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -387,6 +401,10 @@ func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := OperateFirewallPort([]int{int(appInstall.Port)}, []int{int(req.Port)}); err != nil {
|
||||
global.LOG.Errorf("allow firewall failed, err: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -400,14 +418,12 @@ func (a *AppInstallService) DeleteCheck(installId uint) ([]dto.AppResource, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if app.Type == "website" {
|
||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(appInstall.ID))
|
||||
for _, website := range websites {
|
||||
res = append(res, dto.AppResource{
|
||||
Type: "website",
|
||||
Name: website.PrimaryDomain,
|
||||
})
|
||||
}
|
||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(appInstall.ID))
|
||||
for _, website := range websites {
|
||||
res = append(res, dto.AppResource{
|
||||
Type: "website",
|
||||
Name: website.PrimaryDomain,
|
||||
})
|
||||
}
|
||||
if app.Key == constant.AppOpenresty {
|
||||
websites, _ := websiteRepo.GetBy()
|
||||
@@ -567,15 +583,15 @@ func syncById(installId uint) error {
|
||||
if containerCount == 0 {
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = "container is not found"
|
||||
return appInstallRepo.Save(&appInstall)
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
if errCount == 0 && existedCount == 0 {
|
||||
appInstall.Status = constant.Running
|
||||
return appInstallRepo.Save(&appInstall)
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
if existedCount == normalCount {
|
||||
appInstall.Status = constant.Stopped
|
||||
return appInstallRepo.Save(&appInstall)
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
if errCount == normalCount {
|
||||
appInstall.Status = constant.Error
|
||||
@@ -600,7 +616,7 @@ func syncById(installId uint) error {
|
||||
errMsg.Write([]byte("\n"))
|
||||
}
|
||||
appInstall.Message = errMsg.String()
|
||||
return appInstallRepo.Save(&appInstall)
|
||||
return appInstallRepo.Save(context.Background(), &appInstall)
|
||||
}
|
||||
|
||||
func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value interface{}) error {
|
||||
@@ -612,7 +628,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
|
||||
return nil
|
||||
}
|
||||
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, appKey, appInstall.Name)
|
||||
lineBytes, err := ioutil.ReadFile(envPath)
|
||||
lineBytes, err := os.ReadFile(envPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,8 +4,12 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/subosito/gotenv"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -23,6 +27,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -125,7 +130,23 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteAppInstall(ctx context.Context, install model.AppInstall, deleteBackup bool, forceDelete bool, deleteDB bool) error {
|
||||
func handleAppInstallErr(ctx context.Context, install *model.AppInstall) error {
|
||||
op := files.NewFileOp()
|
||||
appDir := install.GetPath()
|
||||
dir, _ := os.Stat(appDir)
|
||||
if dir != nil {
|
||||
_, _ = compose.Down(install.GetComposePath())
|
||||
if err := op.DeleteDir(appDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := deleteLink(ctx, install, true, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete bool, deleteDB bool) error {
|
||||
op := files.NewFileOp()
|
||||
appDir := install.GetPath()
|
||||
dir, _ := os.Stat(appDir)
|
||||
@@ -134,36 +155,34 @@ func deleteAppInstall(ctx context.Context, install model.AppInstall, deleteBacku
|
||||
if err != nil && !forceDelete {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
if err := op.DeleteDir(appDir); err != nil && !forceDelete {
|
||||
return err
|
||||
}
|
||||
}
|
||||
tx, ctx := helper.GetTxAndContext()
|
||||
defer tx.Rollback()
|
||||
if err := appInstallRepo.Delete(ctx, install); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := deleteLink(ctx, &install, deleteDB, forceDelete); err != nil && !forceDelete {
|
||||
return err
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
|
||||
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(install.App.Key))
|
||||
if install.App.Key == constant.AppMysql {
|
||||
_ = mysqlRepo.DeleteAll(ctx)
|
||||
}
|
||||
uploadDir := fmt.Sprintf("%s/1panel/uploads/app/%s/%s", global.CONF.System.BaseDir, install.App.Key, install.Name)
|
||||
if _, err := os.Stat(uploadDir); err == nil {
|
||||
_ = os.RemoveAll(uploadDir)
|
||||
}
|
||||
if deleteBackup {
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil && !forceDelete {
|
||||
return err
|
||||
}
|
||||
localDir, _ := loadLocalDir()
|
||||
backupDir := fmt.Sprintf("%s/app/%s/%s", localDir, install.App.Key, install.Name)
|
||||
if _, err := os.Stat(backupDir); err == nil {
|
||||
_ = os.RemoveAll(backupDir)
|
||||
}
|
||||
global.LOG.Infof("delete app %s-%s backups successful", install.App.Key, install.Name)
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
|
||||
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(install.App.Key))
|
||||
if install.App.Key == constant.AppMysql {
|
||||
_ = mysqlRepo.DeleteAll(ctx)
|
||||
}
|
||||
_ = op.DeleteDir(appDir)
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -190,7 +209,7 @@ func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, f
|
||||
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
}
|
||||
|
||||
func updateInstall(installId uint, detailId uint) error {
|
||||
func upgradeInstall(installId uint, detailId uint) error {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -205,7 +224,25 @@ func updateInstall(installId uint, detailId uint) error {
|
||||
if err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = compose.Down(install.GetComposePath()); err != nil {
|
||||
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Key, "versions", detail.Version)
|
||||
if install.App.Resource == constant.AppResourceLocal {
|
||||
detailDir = path.Join(constant.ResourceDir, "localApps", strings.TrimPrefix(install.App.Key, "local"), "versions", detail.Version)
|
||||
}
|
||||
|
||||
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if stdout != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
}
|
||||
return err
|
||||
}
|
||||
install.DockerCompose = detail.DockerCompose
|
||||
@@ -216,32 +253,51 @@ func updateInstall(installId uint, detailId uint) error {
|
||||
if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = compose.Up(install.GetComposePath()); err != nil {
|
||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return appInstallRepo.Save(&install)
|
||||
return appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
|
||||
func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||
composeMap := install.DockerCompose
|
||||
envMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal([]byte(install.Env), &envMap)
|
||||
newEnvMap := make(map[string]string, len(envMap))
|
||||
handleMap(envMap, newEnvMap)
|
||||
project, err := compose.GetComposeProject([]byte(composeMap), newEnvMap)
|
||||
envStr, err := coverEnvJsonToStr(install.Env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containerNames := []string{install.ContainerName}
|
||||
project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containerMap := make(map[string]struct{})
|
||||
containerMap[install.ContainerName] = struct{}{}
|
||||
for _, service := range project.AllServices() {
|
||||
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" {
|
||||
continue
|
||||
}
|
||||
containerNames = append(containerNames, service.ContainerName)
|
||||
containerMap[service.ContainerName] = struct{}{}
|
||||
}
|
||||
var containerNames []string
|
||||
for k := range containerMap {
|
||||
containerNames = append(containerNames, k)
|
||||
}
|
||||
return containerNames, nil
|
||||
}
|
||||
|
||||
func coverEnvJsonToStr(envJson string) (string, error) {
|
||||
envMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal([]byte(envJson), &envMap)
|
||||
newEnvMap := make(map[string]string, len(envMap))
|
||||
handleMap(envMap, newEnvMap)
|
||||
envStr, err := gotenv.Marshal(newEnvMap)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return envStr, nil
|
||||
}
|
||||
|
||||
func checkLimit(app model.App) error {
|
||||
if app.Limit > 0 {
|
||||
installs, err := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID))
|
||||
@@ -256,11 +312,9 @@ func checkLimit(app model.App) error {
|
||||
}
|
||||
|
||||
func checkRequiredAndLimit(app model.App) error {
|
||||
|
||||
if err := checkLimit(app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if app.Required != "" {
|
||||
var requiredArray []string
|
||||
if err := json.Unmarshal([]byte(app.Required), &requiredArray); err != nil {
|
||||
@@ -289,7 +343,6 @@ func checkRequiredAndLimit(app model.App) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -306,10 +359,17 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
|
||||
}
|
||||
}
|
||||
|
||||
func copyAppData(key, version, installName string, params map[string]interface{}) (err error) {
|
||||
func copyAppData(key, version, installName string, params map[string]interface{}, isLocal bool) (err error) {
|
||||
fileOp := files.NewFileOp()
|
||||
resourceDir := path.Join(constant.AppResourceDir, key, "versions", version)
|
||||
appResourceDir := constant.AppResourceDir
|
||||
installAppDir := path.Join(constant.AppInstallDir, key)
|
||||
appKey := key
|
||||
if isLocal {
|
||||
appResourceDir = constant.LocalAppResourceDir
|
||||
appKey = strings.TrimPrefix(key, "local")
|
||||
installAppDir = path.Join(constant.LocalAppInstallDir, appKey)
|
||||
}
|
||||
resourceDir := path.Join(appResourceDir, appKey, "versions", version)
|
||||
|
||||
if !fileOp.Stat(installAppDir) {
|
||||
if err = fileOp.CreateDir(installAppDir, 0755); err != nil {
|
||||
@@ -339,19 +399,64 @@ func copyAppData(key, version, installName string, params map[string]interface{}
|
||||
return
|
||||
}
|
||||
|
||||
func upApp(composeFilePath string, appInstall model.AppInstall) {
|
||||
out, err := compose.Up(composeFilePath)
|
||||
if err != nil {
|
||||
if out != "" {
|
||||
appInstall.Message = out
|
||||
} else {
|
||||
appInstall.Message = err.Error()
|
||||
// 处理文件夹权限等问题
|
||||
func upAppPre(app model.App, appInstall *model.AppInstall) error {
|
||||
if app.Key == "nexus" {
|
||||
dataPath := path.Join(appInstall.GetPath(), "data")
|
||||
if err := files.NewFileOp().Chown(dataPath, 200, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getServiceFromInstall(appInstall *model.AppInstall) (service *composeV2.ComposeService, err error) {
|
||||
var (
|
||||
project *types.Project
|
||||
envStr string
|
||||
)
|
||||
envStr, err = coverEnvJsonToStr(appInstall.Env)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
project, err = composeV2.GetComposeProject(appInstall.Name, appInstall.GetPath(), []byte(appInstall.DockerCompose), []byte(envStr), true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
service, err = composeV2.NewComposeService()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
service.SetProject(project)
|
||||
return
|
||||
}
|
||||
|
||||
func upApp(appInstall *model.AppInstall) {
|
||||
upProject := func(appInstall *model.AppInstall) (err error) {
|
||||
if err == nil {
|
||||
var composeService *composeV2.ComposeService
|
||||
composeService, err = getServiceFromInstall(appInstall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = composeService.ComposeUp()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := upProject(appInstall); err != nil {
|
||||
appInstall.Status = constant.Error
|
||||
_ = appInstallRepo.Save(&appInstall)
|
||||
appInstall.Message = err.Error()
|
||||
} else {
|
||||
appInstall.Status = constant.Running
|
||||
_ = appInstallRepo.Save(&appInstall)
|
||||
}
|
||||
exist, _ := appInstallRepo.GetFirst(commonRepo.WithByID(appInstall.ID))
|
||||
if exist.ID > 0 {
|
||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,20 +495,29 @@ func getAppDetails(details []model.AppDetail, versions []string) map[string]mode
|
||||
return appDetails
|
||||
}
|
||||
|
||||
func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
|
||||
func getApps(oldApps []model.App, items []dto.AppDefine, isLocal bool) map[string]model.App {
|
||||
apps := make(map[string]model.App, len(oldApps))
|
||||
for _, old := range oldApps {
|
||||
old.Status = constant.AppTakeDown
|
||||
apps[old.Key] = old
|
||||
}
|
||||
for _, item := range items {
|
||||
app, ok := apps[item.Key]
|
||||
key := item.Key
|
||||
if isLocal {
|
||||
key = "local" + key
|
||||
}
|
||||
app, ok := apps[key]
|
||||
if !ok {
|
||||
app = model.App{}
|
||||
}
|
||||
if isLocal {
|
||||
app.Resource = constant.AppResourceLocal
|
||||
} else {
|
||||
app.Resource = constant.AppResourceRemote
|
||||
}
|
||||
app.Name = item.Name
|
||||
app.Limit = item.Limit
|
||||
app.Key = item.Key
|
||||
app.Key = key
|
||||
app.ShortDescZh = item.ShortDescZh
|
||||
app.ShortDescEn = item.ShortDescEn
|
||||
app.Website = item.Website
|
||||
@@ -413,7 +527,7 @@ func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
|
||||
app.CrossVersionUpdate = item.CrossVersionUpdate
|
||||
app.Required = item.GetRequired()
|
||||
app.Status = constant.AppNormal
|
||||
apps[item.Key] = app
|
||||
apps[key] = app
|
||||
}
|
||||
return apps
|
||||
}
|
||||
@@ -426,7 +540,7 @@ func handleErr(install model.AppInstall, err error, out string) error {
|
||||
reErr = errors.New(out)
|
||||
install.Status = constant.Error
|
||||
}
|
||||
_ = appInstallRepo.Save(&install)
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
return reErr
|
||||
}
|
||||
|
||||
@@ -456,6 +570,9 @@ func getAppFromRepo(downloadPath, version string) error {
|
||||
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
|
||||
var res []response.AppInstalledDTO
|
||||
for _, installed := range appInstallList {
|
||||
if updated && installed.App.Type == "php" {
|
||||
continue
|
||||
}
|
||||
installDTO := response.AppInstalledDTO{
|
||||
AppInstall: installed,
|
||||
}
|
||||
@@ -501,7 +618,7 @@ func getAppInstallByKey(key string) (model.AppInstall, error) {
|
||||
return appInstall, nil
|
||||
}
|
||||
|
||||
func updateToolApp(installed model.AppInstall) {
|
||||
func updateToolApp(installed *model.AppInstall) {
|
||||
tooKey, ok := dto.AppToolMap[installed.App.Key]
|
||||
if !ok {
|
||||
return
|
||||
@@ -537,7 +654,7 @@ func updateToolApp(installed model.AppInstall) {
|
||||
return
|
||||
}
|
||||
toolInstall.Env = string(contentByte)
|
||||
if err := appInstallRepo.Save(&toolInstall); err != nil {
|
||||
if err := appInstallRepo.Save(context.Background(), &toolInstall); err != nil {
|
||||
global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
@@ -19,35 +17,17 @@ import (
|
||||
type AuthService struct{}
|
||||
|
||||
type IAuthService interface {
|
||||
SafetyStatus(c *gin.Context) error
|
||||
CheckIsFirst() bool
|
||||
InitUser(c *gin.Context, req dto.InitUser) error
|
||||
CheckIsSafety(code string) bool
|
||||
VerifyCode(code string) (bool, error)
|
||||
SafeEntrance(c *gin.Context, code string) error
|
||||
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
|
||||
LogOut(c *gin.Context) error
|
||||
MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLoginInfo, error)
|
||||
}
|
||||
|
||||
func NewIAuthService() IAuthService {
|
||||
return &AuthService{}
|
||||
}
|
||||
|
||||
func (u *AuthService) SafeEntrance(c *gin.Context, code string) error {
|
||||
codeWithMD5 := encrypt.Md5(code)
|
||||
cookieValue, _ := encrypt.StringEncrypt(codeWithMD5)
|
||||
c.SetCookie(codeWithMD5, cookieValue, 604800, "", "", false, false)
|
||||
|
||||
expiredSetting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeout, _ := strconv.Atoi(expiredSetting.Value)
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) {
|
||||
nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName"))
|
||||
if err != nil {
|
||||
@@ -163,57 +143,13 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
|
||||
return setting.Value == code, nil
|
||||
}
|
||||
|
||||
func (u *AuthService) SafetyStatus(c *gin.Context) error {
|
||||
setting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
func (u *AuthService) CheckIsSafety(code string) bool {
|
||||
status, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
return err
|
||||
return false
|
||||
}
|
||||
codeWithEcrypt, err := c.Cookie(encrypt.Md5(setting.Value))
|
||||
if err != nil {
|
||||
return err
|
||||
if len(status.Value) == 0 {
|
||||
return true
|
||||
}
|
||||
code, err := encrypt.StringDecrypt(codeWithEcrypt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if code != encrypt.Md5(setting.Value) {
|
||||
return errors.New("code not match")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AuthService) CheckIsFirst() bool {
|
||||
user, _ := settingRepo.Get(settingRepo.WithByKey("UserName"))
|
||||
pass, _ := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
return len(user.Value) == 0 || len(pass.Value) == 0
|
||||
}
|
||||
|
||||
func (u *AuthService) InitUser(c *gin.Context, req dto.InitUser) error {
|
||||
user, _ := settingRepo.Get(settingRepo.WithByKey("UserName"))
|
||||
pass, _ := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if len(user.Value) == 0 || len(pass.Value) == 0 {
|
||||
newPass, err := encrypt.StringEncrypt(req.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("UserName", req.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("Password", newPass); err != nil {
|
||||
return err
|
||||
}
|
||||
expiredSetting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeout, _ := strconv.Atoi(expiredSetting.Value)
|
||||
if timeout != 0 {
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("can't init user because user %s is in system", user.Value)
|
||||
return status.Value == code
|
||||
}
|
||||
|
||||
@@ -5,14 +5,16 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"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/cloud_storage"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
fileUtils "github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -26,7 +28,7 @@ type IBackupService interface {
|
||||
Create(backupDto dto.BackupOperate) error
|
||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
|
||||
Update(ireq dto.BackupOperate) error
|
||||
BatchDelete(ids []uint) error
|
||||
Delete(id uint) error
|
||||
BatchDeleteRecord(ids []uint) error
|
||||
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
|
||||
|
||||
@@ -53,36 +55,13 @@ func NewIBackupService() IBackupService {
|
||||
func (u *BackupService) List() ([]dto.BackupInfo, error) {
|
||||
ops, err := backupRepo.List(commonRepo.WithOrderBy("created_at desc"))
|
||||
var dtobas []dto.BackupInfo
|
||||
ossExist, s3Exist, sftpExist, minioExist := false, false, false, false
|
||||
for _, group := range ops {
|
||||
switch group.Type {
|
||||
case "OSS":
|
||||
ossExist = true
|
||||
case "S3":
|
||||
s3Exist = true
|
||||
case "SFTP":
|
||||
sftpExist = true
|
||||
case "MINIO":
|
||||
minioExist = true
|
||||
}
|
||||
var item dto.BackupInfo
|
||||
if err := copier.Copy(&item, &group); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtobas = append(dtobas, item)
|
||||
}
|
||||
if !ossExist {
|
||||
dtobas = append(dtobas, dto.BackupInfo{Type: "OSS"})
|
||||
}
|
||||
if !s3Exist {
|
||||
dtobas = append(dtobas, dto.BackupInfo{Type: "S3"})
|
||||
}
|
||||
if !sftpExist {
|
||||
dtobas = append(dtobas, dto.BackupInfo{Type: "SFTP"})
|
||||
}
|
||||
if !minioExist {
|
||||
dtobas = append(dtobas, dto.BackupInfo{Type: "MINIO"})
|
||||
}
|
||||
dtobas = append(dtobas, u.loadByType("LOCAL", ops))
|
||||
dtobas = append(dtobas, u.loadByType("OSS", ops))
|
||||
dtobas = append(dtobas, u.loadByType("S3", ops))
|
||||
dtobas = append(dtobas, u.loadByType("SFTP", ops))
|
||||
dtobas = append(dtobas, u.loadByType("MINIO", ops))
|
||||
dtobas = append(dtobas, u.loadByType("COS", ops))
|
||||
dtobas = append(dtobas, u.loadByType("KODO", ops))
|
||||
return dtobas, err
|
||||
}
|
||||
|
||||
@@ -123,7 +102,7 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
case constant.Sftp:
|
||||
varMap["username"] = backup.AccessKey
|
||||
varMap["password"] = backup.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo:
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
varMap["accessKey"] = backup.AccessKey
|
||||
varMap["secretKey"] = backup.Credential
|
||||
}
|
||||
@@ -131,15 +110,15 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new cloud storage client failed, err: %v", err)
|
||||
}
|
||||
tempPath := fmt.Sprintf("%sdownload%s", constant.DataDir, info.FileDir)
|
||||
if _, err := os.Stat(tempPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(tempPath, os.ModePerm); err != nil {
|
||||
global.LOG.Errorf("mkdir %s failed, err: %v", tempPath, err)
|
||||
targetPath := fmt.Sprintf("%s/download/%s/%s", constant.DataDir, info.FileDir, info.FileName)
|
||||
if _, err := os.Stat(path.Dir(targetPath)); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(targetPath), os.ModePerm); err != nil {
|
||||
global.LOG.Errorf("mkdir %s failed, err: %v", path.Dir(targetPath), err)
|
||||
}
|
||||
}
|
||||
targetPath := tempPath + info.FileName
|
||||
if _, err = os.Stat(targetPath); err != nil && os.IsNotExist(err) {
|
||||
isOK, err := backClient.Download(info.FileName, targetPath)
|
||||
srcPath := fmt.Sprintf("%s/%s", info.FileDir, info.FileName)
|
||||
if exist, _ := backClient.Exist(srcPath); exist {
|
||||
isOK, err := backClient.Download(srcPath, targetPath)
|
||||
if !isOK {
|
||||
return "", fmt.Errorf("cloud storage download failed, err: %v", err)
|
||||
}
|
||||
@@ -171,7 +150,7 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
||||
case constant.Sftp:
|
||||
varMap["username"] = backupDto.AccessKey
|
||||
varMap["password"] = backupDto.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo:
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
varMap["accessKey"] = backupDto.AccessKey
|
||||
varMap["secretKey"] = backupDto.Credential
|
||||
}
|
||||
@@ -182,8 +161,12 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
||||
return client.ListBuckets()
|
||||
}
|
||||
|
||||
func (u *BackupService) BatchDelete(ids []uint) error {
|
||||
return backupRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||
func (u *BackupService) Delete(id uint) error {
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByBackupID(id))
|
||||
if len(cronjobs) != 0 {
|
||||
return buserr.New(constant.ErrBackupInUsed)
|
||||
}
|
||||
return backupRepo.Delete(commonRepo.WithByID(id))
|
||||
}
|
||||
|
||||
func (u *BackupService) BatchDeleteRecord(ids []uint) error {
|
||||
@@ -197,7 +180,7 @@ func (u *BackupService) BatchDeleteRecord(ids []uint) error {
|
||||
global.LOG.Errorf("remove file %s failed, err: %v", record.FileDir+record.FileName, err)
|
||||
}
|
||||
} else {
|
||||
backupAccount, err := backupRepo.Get(commonRepo.WithByName(record.Source))
|
||||
backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -241,7 +224,7 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
||||
if strings.HasSuffix(dirStr, "/") {
|
||||
dirStr = dirStr[:strings.LastIndex(dirStr, "/")]
|
||||
}
|
||||
if err := updateBackupDir(dirStr, oldDir); err != nil {
|
||||
if err := copyDir(oldDir, dirStr); err != nil {
|
||||
_ = backupRepo.Update(req.ID, (map[string]interface{}{"vars": oldVars}))
|
||||
return err
|
||||
}
|
||||
@@ -277,7 +260,7 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
||||
case constant.Sftp:
|
||||
varMap["username"] = backup.AccessKey
|
||||
varMap["password"] = backup.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo:
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
varMap["accessKey"] = backup.AccessKey
|
||||
varMap["secretKey"] = backup.Credential
|
||||
}
|
||||
@@ -290,6 +273,19 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
||||
return backClient, nil
|
||||
}
|
||||
|
||||
func (u *BackupService) loadByType(accountType string, accounts []model.BackupAccount) dto.BackupInfo {
|
||||
for _, account := range accounts {
|
||||
if account.Type == accountType {
|
||||
var item dto.BackupInfo
|
||||
if err := copier.Copy(&item, &account); err != nil {
|
||||
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
|
||||
}
|
||||
return item
|
||||
}
|
||||
}
|
||||
return dto.BackupInfo{Type: accountType}
|
||||
}
|
||||
|
||||
func loadLocalDir() (string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType("LOCAL"))
|
||||
if err != nil {
|
||||
@@ -314,18 +310,33 @@ func loadLocalDir() (string, error) {
|
||||
return "", fmt.Errorf("error type dir: %T", varMap["dir"])
|
||||
}
|
||||
|
||||
func updateBackupDir(dir, oldDir string) error {
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return err
|
||||
func copyDir(src, dst string) error {
|
||||
srcInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = os.MkdirAll(dst, srcInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
files, err := os.ReadDir(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileOP := fileUtils.NewFileOp()
|
||||
for _, file := range files {
|
||||
srcPath := fmt.Sprintf("%s/%s", src, file.Name())
|
||||
dstPath := fmt.Sprintf("%s/%s", dst, file.Name())
|
||||
if file.IsDir() {
|
||||
if err = copyDir(srcPath, dstPath); err != nil {
|
||||
global.LOG.Errorf("copy dir %s to %s failed, err: %v", srcPath, dstPath, err)
|
||||
}
|
||||
} else {
|
||||
if err := fileOP.CopyFile(srcPath, dst); err != nil {
|
||||
global.LOG.Errorf("copy file %s to %s failed, err: %v", srcPath, dstPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.HasSuffix(oldDir, "/") {
|
||||
oldDir = oldDir[:strings.LastIndex(oldDir, "/")]
|
||||
}
|
||||
stdout, err := cmd.Execf("cp -r %s/* %s", oldDir, dir)
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
@@ -32,6 +33,7 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) error {
|
||||
return err
|
||||
}
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
|
||||
backupDir := fmt.Sprintf("%s/app/%s/%s", localDir, req.Name, req.DetailName)
|
||||
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow)
|
||||
@@ -97,7 +99,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
appPath := fmt.Sprintf("%s/%s/%s", constant.AppInstallDir, install.App.Key, install.Name)
|
||||
appPath := fmt.Sprintf("%s/%s", install.GetPath(), install.Name)
|
||||
if err := handleTar(appPath, tmpDir, "app.tar.gz", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -192,7 +194,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
}
|
||||
|
||||
oldInstall.Status = constant.Running
|
||||
if err := appInstallRepo.Save(install); err != nil {
|
||||
if err := appInstallRepo.Save(context.Background(), install); err != nil {
|
||||
global.LOG.Errorf("save db app install failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -176,11 +175,11 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
|
||||
if appendonly == "yes" && redisInfo.Version == "6.0.16" {
|
||||
itemName = "appendonly.aof"
|
||||
}
|
||||
input, err := ioutil.ReadFile(recoverFile)
|
||||
input, err := os.ReadFile(recoverFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ioutil.WriteFile(composeDir+"/data/"+itemName, input, 0640); err != nil {
|
||||
if err = os.WriteFile(composeDir+"/data/"+itemName, input, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -47,6 +47,8 @@ type IContainerService interface {
|
||||
CreateNetwork(req dto.NetworkCreat) error
|
||||
DeleteVolume(req dto.BatchDelete) error
|
||||
CreateVolume(req dto.VolumeCreat) error
|
||||
TestCompose(req dto.ComposeCreate) (bool, error)
|
||||
ComposeUpdate(req dto.ComposeUpdate) error
|
||||
}
|
||||
|
||||
func NewIContainerService() IContainerService {
|
||||
@@ -96,27 +98,45 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
records = list[start:end]
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(records))
|
||||
for _, container := range records {
|
||||
IsFromCompose := false
|
||||
if _, ok := container.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := container.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
backDatas = append(backDatas, dto.ContainerInfo{
|
||||
ContainerID: container.ID,
|
||||
CreateTime: time.Unix(container.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: container.Names[0][1:],
|
||||
ImageId: strings.Split(container.ImageID, ":")[1],
|
||||
ImageName: container.Image,
|
||||
State: container.State,
|
||||
RunTime: container.Status,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
})
|
||||
go func(item types.Container) {
|
||||
IsFromCompose := false
|
||||
if _, ok := item.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
|
||||
var ports []string
|
||||
for _, port := range item.Ports {
|
||||
if port.IP == "::" || port.PublicPort == 0 {
|
||||
continue
|
||||
}
|
||||
ports = append(ports, fmt.Sprintf("%v:%v/%s", port.PublicPort, port.PrivatePort, port.Type))
|
||||
}
|
||||
cpu, mem := loadCpuAndMem(client, item.ID)
|
||||
backDatas = append(backDatas, dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: item.Names[0][1:],
|
||||
ImageId: strings.Split(item.ImageID, ":")[1],
|
||||
ImageName: item.Image,
|
||||
State: item.State,
|
||||
RunTime: item.Status,
|
||||
CPUPercent: cpu,
|
||||
MemoryPercent: mem,
|
||||
Ports: ports,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
})
|
||||
wg.Done()
|
||||
}(container)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
@@ -146,24 +166,27 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
if len(req.ExposedPorts) != 0 {
|
||||
for _, port := range req.ExposedPorts {
|
||||
if common.ScanPort(port.HostPort) {
|
||||
return buserr.WithDetail(constant.ErrPortInUsed, port.HostPort, nil)
|
||||
}
|
||||
}
|
||||
portMap, err := checkPortStats(req.ExposedPorts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exposeds := make(nat.PortSet)
|
||||
for port := range portMap {
|
||||
exposeds[port] = struct{}{}
|
||||
}
|
||||
config := &container.Config{
|
||||
Image: req.Image,
|
||||
Cmd: req.Cmd,
|
||||
Env: req.Env,
|
||||
Labels: stringsToMap(req.Labels),
|
||||
Tty: true,
|
||||
OpenStdin: true,
|
||||
Image: req.Image,
|
||||
Cmd: req.Cmd,
|
||||
Env: req.Env,
|
||||
Labels: stringsToMap(req.Labels),
|
||||
Tty: true,
|
||||
OpenStdin: true,
|
||||
ExposedPorts: exposeds,
|
||||
}
|
||||
hostConf := &container.HostConfig{
|
||||
AutoRemove: req.AutoRemove,
|
||||
@@ -180,11 +203,7 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
hostConf.Memory = req.Memory
|
||||
}
|
||||
if len(req.ExposedPorts) != 0 {
|
||||
hostConf.PortBindings = make(nat.PortMap)
|
||||
for _, port := range req.ExposedPorts {
|
||||
bindItem := nat.PortBinding{HostPort: strconv.Itoa(port.HostPort)}
|
||||
hostConf.PortBindings[nat.Port(fmt.Sprintf("%d/tcp", port.ContainerPort))] = []nat.PortBinding{bindItem}
|
||||
}
|
||||
hostConf.PortBindings = portMap
|
||||
}
|
||||
if len(req.Volumes) != 0 {
|
||||
config.Volumes = make(map[string]struct{})
|
||||
@@ -197,8 +216,10 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
|
||||
|
||||
ctx := context.Background()
|
||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
||||
return err
|
||||
if !checkImageExist(client, req.Image) {
|
||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
container, err := client.ContainerCreate(ctx, config, hostConf, &network.NetworkingConfig{}, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
@@ -225,9 +246,9 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
case constant.ContainerOpStart:
|
||||
err = client.ContainerStart(ctx, req.Name, types.ContainerStartOptions{})
|
||||
case constant.ContainerOpStop:
|
||||
err = client.ContainerStop(ctx, req.Name, nil)
|
||||
err = client.ContainerStop(ctx, req.Name, container.StopOptions{})
|
||||
case constant.ContainerOpRestart:
|
||||
err = client.ContainerRestart(ctx, req.Name, nil)
|
||||
err = client.ContainerRestart(ctx, req.Name, container.StopOptions{})
|
||||
case constant.ContainerOpKill:
|
||||
err = client.ContainerKill(ctx, req.Name, "SIGKILL")
|
||||
case constant.ContainerOpPause:
|
||||
@@ -265,18 +286,18 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainterStats, erro
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
res.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
res.Body.Close()
|
||||
var stats *types.StatsJSON
|
||||
if err := json.Unmarshal(body, &stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data dto.ContainterStats
|
||||
previousCPU := stats.PreCPUStats.CPUUsage.TotalUsage
|
||||
previousSystem := stats.PreCPUStats.SystemUsage
|
||||
data.CPUPercent = calculateCPUPercentUnix(previousCPU, previousSystem, stats)
|
||||
data.CPUPercent = calculateCPUPercentUnix(stats)
|
||||
data.IORead, data.IOWrite = calculateBlockIO(stats.BlkioStats)
|
||||
data.Memory = float64(stats.MemoryStats.Usage) / 1024 / 1024
|
||||
if cache, ok := stats.MemoryStats.Stats["cache"]; ok {
|
||||
@@ -291,25 +312,36 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainterStats, erro
|
||||
func stringsToMap(list []string) map[string]string {
|
||||
var lableMap = make(map[string]string)
|
||||
for _, label := range list {
|
||||
sps := strings.Split(label, "=")
|
||||
if len(sps) > 1 {
|
||||
if strings.Contains(label, "=") {
|
||||
sps := strings.SplitN(label, "=", 2)
|
||||
lableMap[sps[0]] = sps[1]
|
||||
}
|
||||
}
|
||||
return lableMap
|
||||
}
|
||||
func calculateCPUPercentUnix(previousCPU, previousSystem uint64, v *types.StatsJSON) float64 {
|
||||
var (
|
||||
cpuPercent = 0.0
|
||||
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)
|
||||
systemDelta = float64(v.CPUStats.SystemUsage) - float64(previousSystem)
|
||||
)
|
||||
|
||||
func calculateCPUPercentUnix(stats *types.StatsJSON) float64 {
|
||||
cpuPercent := 0.0
|
||||
cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage) - float64(stats.PreCPUStats.CPUUsage.TotalUsage)
|
||||
systemDelta := float64(stats.CPUStats.SystemUsage) - float64(stats.PreCPUStats.SystemUsage)
|
||||
|
||||
if systemDelta > 0.0 && cpuDelta > 0.0 {
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||
cpuPercent = (cpuDelta / systemDelta) * 100.0
|
||||
if len(stats.CPUStats.CPUUsage.PercpuUsage) != 0 {
|
||||
cpuPercent = cpuPercent * float64(len(stats.CPUStats.CPUUsage.PercpuUsage))
|
||||
}
|
||||
}
|
||||
return cpuPercent
|
||||
}
|
||||
func calculateMemPercentUnix(memStats types.MemoryStats) float64 {
|
||||
memPercent := 0.0
|
||||
memUsage := float64(memStats.Usage - memStats.Stats["cache"])
|
||||
memLimit := float64(memStats.Limit)
|
||||
if memUsage > 0.0 && memLimit > 0.0 {
|
||||
memPercent = (memUsage / memLimit) * 100.0
|
||||
}
|
||||
return memPercent
|
||||
}
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead float64, blkWrite float64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
@@ -331,15 +363,100 @@ func calculateNetwork(network map[string]types.NetworkStats) (float64, float64)
|
||||
return rx, tx
|
||||
}
|
||||
|
||||
func checkImageExist(client *client.Client, image string) bool {
|
||||
images, err := client.ImageList(context.Background(), types.ImageListOptions{})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
|
||||
for _, img := range images {
|
||||
for _, tag := range img.RepoTags {
|
||||
if tag == image || tag == image+":latest" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func pullImages(ctx context.Context, client *client.Client, image string) error {
|
||||
out, err := client.ImagePull(ctx, image, types.ImagePullOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
_, err = io.Copy(ioutil.Discard, out)
|
||||
_, err = io.Copy(io.Discard, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadCpuAndMem(client *client.Client, container string) (float64, float64) {
|
||||
res, err := client.ContainerStats(context.Background(), container, false)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
res.Body.Close()
|
||||
return 0, 0
|
||||
}
|
||||
res.Body.Close()
|
||||
var stats *types.StatsJSON
|
||||
if err := json.Unmarshal(body, &stats); err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
CPUPercent := calculateCPUPercentUnix(stats)
|
||||
MemPercent := calculateMemPercentUnix(stats.MemoryStats)
|
||||
return CPUPercent, MemPercent
|
||||
}
|
||||
|
||||
func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
|
||||
portMap := make(nat.PortMap)
|
||||
if len(ports) == 0 {
|
||||
return portMap, nil
|
||||
}
|
||||
for _, port := range ports {
|
||||
if strings.Contains(port.ContainerPort, "-") {
|
||||
if !strings.Contains(port.HostPort, "-") {
|
||||
return portMap, buserr.New(constant.ErrPortRules)
|
||||
}
|
||||
hostStart, _ := strconv.Atoi(strings.Split(port.HostPort, "-")[0])
|
||||
hostEnd, _ := strconv.Atoi(strings.Split(port.HostPort, "-")[1])
|
||||
containerStart, _ := strconv.Atoi(strings.Split(port.ContainerPort, "-")[0])
|
||||
containerEnd, _ := strconv.Atoi(strings.Split(port.ContainerPort, "-")[1])
|
||||
if (hostEnd-hostStart) <= 0 || (containerEnd-containerStart) <= 0 {
|
||||
return portMap, buserr.New(constant.ErrPortRules)
|
||||
}
|
||||
if (containerEnd - containerStart) != (hostEnd - hostStart) {
|
||||
return portMap, buserr.New(constant.ErrPortRules)
|
||||
}
|
||||
for i := 0; i <= hostEnd-hostStart; i++ {
|
||||
bindItem := nat.PortBinding{HostPort: strconv.Itoa(hostStart + i), HostIP: port.HostIP}
|
||||
portMap[nat.Port(fmt.Sprintf("%d/%s", containerStart+i, port.Protocol))] = []nat.PortBinding{bindItem}
|
||||
}
|
||||
for i := hostStart; i <= hostEnd; i++ {
|
||||
if common.ScanPort(i) {
|
||||
return portMap, buserr.WithDetail(constant.ErrPortInUsed, i, nil)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
portItem := 0
|
||||
if strings.Contains(port.HostPort, "-") {
|
||||
portItem, _ = strconv.Atoi(strings.Split(port.HostPort, "-")[0])
|
||||
} else {
|
||||
portItem, _ = strconv.Atoi(port.HostPort)
|
||||
}
|
||||
if common.ScanPort(portItem) {
|
||||
return portMap, buserr.WithDetail(constant.ErrPortInUsed, portItem, nil)
|
||||
}
|
||||
bindItem := nat.PortBinding{HostPort: strconv.Itoa(portItem), HostIP: port.HostIP}
|
||||
portMap[nat.Port(fmt.Sprintf("%s/%s", port.ContainerPort, port.Protocol))] = []nat.PortBinding{bindItem}
|
||||
}
|
||||
}
|
||||
return portMap, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -154,9 +155,10 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
go func() {
|
||||
defer file.Close()
|
||||
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
_, _ = file.Write(stdout)
|
||||
if err != nil {
|
||||
multiWriter := io.MultiWriter(os.Stdout, file)
|
||||
cmd.Stdout = multiWriter
|
||||
cmd.Stderr = multiWriter
|
||||
if err := cmd.Run(); err != nil {
|
||||
global.LOG.Errorf("docker-compose up %s failed, err: %v", req.Name, err)
|
||||
_, _ = compose.Down(req.Path)
|
||||
_, _ = file.WriteString("docker-compose up failed!")
|
||||
@@ -180,7 +182,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
|
||||
if req.Operation == "down" {
|
||||
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
|
||||
_ = os.RemoveAll(strings.ReplaceAll(req.Path, "/docker-compose.yml", ""))
|
||||
if req.WithFile {
|
||||
_ = os.RemoveAll(path.Dir(req.Path))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -211,15 +215,7 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
|
||||
}
|
||||
|
||||
func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
|
||||
if req.From == "template" {
|
||||
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.From = "edit"
|
||||
req.File = template.Content
|
||||
}
|
||||
if req.From == "edit" {
|
||||
if req.From == "template" || req.From == "edit" {
|
||||
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
)
|
||||
@@ -37,14 +36,14 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
|
||||
}
|
||||
var (
|
||||
data []dto.Volume
|
||||
records []*types.Volume
|
||||
records []*volume.Volume
|
||||
)
|
||||
sort.Slice(list.Volumes, func(i, j int) bool {
|
||||
return list.Volumes[i].CreatedAt > list.Volumes[j].CreatedAt
|
||||
})
|
||||
total, start, end := len(list.Volumes), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
records = make([]*types.Volume, 0)
|
||||
records = make([]*volume.Volume, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
@@ -119,7 +118,7 @@ func (u *ContainerService) CreateVolume(req dto.VolumeCreat) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
options := volume.VolumeCreateBody{
|
||||
options := volume.CreateOptions{
|
||||
Name: req.Name,
|
||||
Driver: req.Driver,
|
||||
DriverOpts: stringsToMap(req.Options),
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -24,7 +25,10 @@ type ICronjobService interface {
|
||||
HandleOnce(id uint) error
|
||||
Update(id uint, req dto.CronjobUpdate) error
|
||||
UpdateStatus(id uint, status string) error
|
||||
Delete(ids []uint) error
|
||||
Delete(req dto.CronjobBatchDelete) error
|
||||
Download(down dto.CronjobDownload) (string, error)
|
||||
StartJob(cronjob *model.Cronjob) (int, error)
|
||||
CleanRecord(req dto.CronjobClean) error
|
||||
}
|
||||
|
||||
func NewICronjobService() ICronjobService {
|
||||
@@ -76,6 +80,44 @@ func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interfac
|
||||
return total, dtoCronjobs, err
|
||||
}
|
||||
|
||||
func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
cronjob, err := cronjobRepo.Get(commonRepo.WithByID(req.CronjobID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.CleanData && cronjob.Type != "shell" && cronjob.Type != "curl" {
|
||||
cronjob.RetainCopies = 0
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, localDir, &cronjob, client)
|
||||
} else {
|
||||
u.HandleRmExpired(backup.Type, "", &cronjob, nil)
|
||||
}
|
||||
}
|
||||
delRecords, err := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(req.CronjobID)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, del := range delRecords {
|
||||
_ = os.RemoveAll(del.Records)
|
||||
}
|
||||
if err := cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(req.CronjobID))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
|
||||
record, _ := cronjobRepo.GetRecord(commonRepo.WithByID(down.RecordID))
|
||||
if record.ID == 0 {
|
||||
@@ -89,24 +131,19 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
|
||||
if cronjob.ID == 0 {
|
||||
return "", constant.ErrRecordNotFound
|
||||
}
|
||||
if backup.Type == "LOCAL" {
|
||||
if backup.Type == "LOCAL" || record.FromLocal {
|
||||
if _, err := os.Stat(record.File); err != nil && os.IsNotExist(err) {
|
||||
return "", constant.ErrRecordNotFound
|
||||
}
|
||||
return record.File, nil
|
||||
}
|
||||
if record.FromLocal {
|
||||
local, _ := loadLocalDir()
|
||||
if _, err := os.Stat(local + "/" + record.File); err == nil {
|
||||
return local + "/" + record.File, nil
|
||||
}
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tempPath := fmt.Sprintf("%s/download/%s", constant.DataDir, record.File)
|
||||
isOK, _ := client.Download(record.File, tempPath)
|
||||
_ = os.MkdirAll(path.Dir(tempPath), os.ModePerm)
|
||||
isOK, err := client.Download(record.File, tempPath)
|
||||
if !isOK || err != nil {
|
||||
return "", constant.ErrRecordNotFound
|
||||
}
|
||||
@@ -157,21 +194,23 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob) (int, error) {
|
||||
return entryID, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) Delete(ids []uint) error {
|
||||
if len(ids) == 1 {
|
||||
if err := u.HandleDelete(ids[0]); err != nil {
|
||||
func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
||||
for _, id := range req.IDs {
|
||||
cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(id))
|
||||
if cronjob.ID == 0 {
|
||||
return errors.New("find cronjob in db failed")
|
||||
}
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
|
||||
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
return cronjobRepo.Delete(commonRepo.WithByID(ids[0]))
|
||||
}
|
||||
cronjobs, err := cronjobRepo.List(commonRepo.WithIdsIn(ids))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range cronjobs {
|
||||
_ = u.HandleDelete(ids[i])
|
||||
}
|
||||
return cronjobRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
@@ -194,6 +233,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["entry_id"] = newEntryID
|
||||
upMap["name"] = req.Name
|
||||
upMap["spec"] = cronjob.Spec
|
||||
upMap["script"] = req.Script
|
||||
upMap["spec_type"] = req.SpecType
|
||||
upMap["week"] = req.Week
|
||||
|
||||
@@ -3,18 +3,18 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
@@ -29,11 +29,12 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
if len(cronjob.Script) == 0 {
|
||||
return
|
||||
}
|
||||
stdout, errExec := cmd.Exec(cronjob.Script)
|
||||
stdout, errExec := cmd.ExecCronjobWithTimeOut(cronjob.Script, 5*time.Minute)
|
||||
if errExec != nil {
|
||||
err = errExec
|
||||
}
|
||||
message = []byte(stdout)
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
case "website":
|
||||
record.File, err = u.HandleBackup(cronjob, record.StartTime)
|
||||
case "database":
|
||||
@@ -47,11 +48,12 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
if len(cronjob.URL) == 0 {
|
||||
return
|
||||
}
|
||||
stdout, errCurl := cmd.Exec("curl " + cronjob.URL)
|
||||
stdout, errCurl := cmd.ExecWithTimeOut("curl "+cronjob.URL, 5*time.Minute)
|
||||
if err != nil {
|
||||
err = errCurl
|
||||
}
|
||||
message = []byte(stdout)
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
}
|
||||
if err != nil {
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
||||
@@ -68,11 +70,6 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
var (
|
||||
backupDir string
|
||||
fileName string
|
||||
record model.BackupRecord
|
||||
)
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -89,144 +86,60 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fileName = fmt.Sprintf("db_%s_%s.sql.gz", cronjob.DBName, startTime.Format("20060102150405"))
|
||||
backupDir = fmt.Sprintf("%s/database/mysql/%s/%s", localDir, app.Name, cronjob.DBName)
|
||||
if err = handleMysqlBackup(app, backupDir, cronjob.DBName, fileName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
record.Type = "mysql"
|
||||
record.Name = app.Name
|
||||
record.DetailName = cronjob.DBName
|
||||
paths, err := u.handleDatabase(*cronjob, app, backup, startTime)
|
||||
return strings.Join(paths, ","), err
|
||||
case "website":
|
||||
fileName = fmt.Sprintf("website_%s_%s.tar.gz", cronjob.Website, startTime.Format("20060102150405"))
|
||||
backupDir = fmt.Sprintf("%s/website/%s", localDir, cronjob.Website)
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(cronjob.Website))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := handleWebsiteBackup(&website, backupDir, fileName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
record.Type = "website"
|
||||
record.Name = website.PrimaryDomain
|
||||
paths, err := u.handleWebsite(*cronjob, backup, startTime)
|
||||
return strings.Join(paths, ","), err
|
||||
default:
|
||||
fileName = fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405"))
|
||||
backupDir = fmt.Sprintf("%s/%s/%s", localDir, cronjob.Type, cronjob.Name)
|
||||
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405"))
|
||||
backupDir := fmt.Sprintf("%s/%s/%s", localDir, cronjob.Type, cronjob.Name)
|
||||
itemFileDir := fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name)
|
||||
global.LOG.Infof("handle tar %s to %s", backupDir, fileName)
|
||||
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
|
||||
if len(record.Name) != 0 {
|
||||
record.FileName = fileName
|
||||
record.FileDir = backupDir
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = itemFileDir
|
||||
}
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
fullPath := fmt.Sprintf("%s/%s", backupDir, fileName)
|
||||
if backup.Type != "LOCAL" {
|
||||
fullPath = fmt.Sprintf("%s/%s", itemFileDir, fileName)
|
||||
}
|
||||
if backup.Type == "LOCAL" {
|
||||
u.HandleRmExpired(backup.Type, backupDir, cronjob, nil)
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, fileName))
|
||||
}()
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return fullPath, err
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
|
||||
return fullPath, err
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, itemFileDir, cronjob, client)
|
||||
if cronjob.KeepLocal {
|
||||
u.HandleRmExpired("LOCAL", backupDir, cronjob, client)
|
||||
}
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleDelete(id uint) error {
|
||||
cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(id))
|
||||
if cronjob.ID == 0 {
|
||||
return errors.New("find cronjob in db failed")
|
||||
}
|
||||
commonDir := fmt.Sprintf("%s/%s/", cronjob.Type, cronjob.Name)
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
|
||||
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(id)))
|
||||
|
||||
dir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronjob.Type, cronjob.Name)
|
||||
if _, err := os.Stat(dir); err == nil {
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
global.LOG.Errorf("rm file %s/task/%s failed, err: %v", constant.DataDir, commonDir, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleRmExpired(backType, backupDir string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
|
||||
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
|
||||
if backType != "LOCAL" {
|
||||
currentObjs, err := backClient.ListObjects(backupDir + "/")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("list bucket object %s failed, err: %v", backupDir, err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(currentObjs)-int(cronjob.RetainCopies); i++ {
|
||||
_, _ = backClient.Delete(currentObjs[i].(string))
|
||||
}
|
||||
return
|
||||
}
|
||||
files, err := ioutil.ReadDir(backupDir)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("read dir %s failed, err: %v", backupDir, err)
|
||||
return
|
||||
}
|
||||
if len(files) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
prefix := ""
|
||||
switch cronjob.Type {
|
||||
case "database":
|
||||
prefix = "db_"
|
||||
case "website":
|
||||
prefix = "website_"
|
||||
case "directory":
|
||||
prefix = "directory_"
|
||||
}
|
||||
|
||||
dbCopies := uint64(0)
|
||||
for i := len(files) - 1; i >= 0; i-- {
|
||||
if strings.HasPrefix(files[i].Name(), prefix) {
|
||||
dbCopies++
|
||||
if dbCopies > cronjob.RetainCopies {
|
||||
_ = os.Remove(backupDir + "/" + files[i].Name())
|
||||
_ = backupRepo.DeleteRecord(context.Background(), backupRepo.WithByFileName(files[i].Name()))
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, fileName))
|
||||
}()
|
||||
}
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, localDir, cronjob, client)
|
||||
if backup.Type == "LOCAL" || cronjob.KeepLocal {
|
||||
return fmt.Sprintf("%s/%s/%s/%s", localDir, cronjob.Type, cronjob.Name, fileName), nil
|
||||
}
|
||||
return fmt.Sprintf("%s/%s/%s", cronjob.Type, cronjob.Name, fileName), nil
|
||||
}
|
||||
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)))
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleRmExpired(backType, localDir string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
|
||||
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
|
||||
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc"))
|
||||
if len(records) > int(cronjob.RetainCopies) {
|
||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(records[i].ID)))
|
||||
files := strings.Split(records[i].File, ",")
|
||||
for _, file := range files {
|
||||
if backType != "LOCAL" {
|
||||
_, _ = backClient.Delete(strings.ReplaceAll(file, localDir+"/", ""))
|
||||
_ = os.Remove(file)
|
||||
} else {
|
||||
_ = os.Remove(file)
|
||||
}
|
||||
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
|
||||
}
|
||||
|
||||
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
|
||||
_ = os.Remove(records[i].Records)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,7 +170,7 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
|
||||
commands := fmt.Sprintf("tar zcvf %s %s %s", targetDir+"/"+name, excludeRules, path)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.Exec(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 5*time.Minute)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
@@ -274,10 +187,144 @@ func handleUnTar(sourceFile, targetDir string) error {
|
||||
|
||||
commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.Exec(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 5*time.Minute)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInfo, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
var dblist []string
|
||||
if cronjob.DBName == "all" {
|
||||
mysqlService := NewIMysqlService()
|
||||
dblist, err = mysqlService.ListDBName()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
} else {
|
||||
dblist = append(dblist, cronjob.DBName)
|
||||
}
|
||||
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, dbName := range dblist {
|
||||
var record model.BackupRecord
|
||||
|
||||
record.Type = "mysql"
|
||||
record.Name = app.Name
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
|
||||
backupDir := fmt.Sprintf("%s/database/mysql/%s/%s", localDir, app.Name, dbName)
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbName, startTime.Format("20060102150405"))
|
||||
if err = handleMysqlBackup(app, backupDir, dbName, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
record.DetailName = dbName
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = itemFileDir
|
||||
}
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return paths, err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
var weblist []string
|
||||
if cronjob.Website == "all" {
|
||||
weblist, err = NewIWebsiteService().GetWebsiteOptions()
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
} else {
|
||||
weblist = append(weblist, cronjob.Website)
|
||||
}
|
||||
|
||||
var client cloud_storage.CloudStorageClient
|
||||
if backup.Type != "LOCAL" {
|
||||
client, err = NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, websiteItem := range weblist {
|
||||
var record model.BackupRecord
|
||||
record.Type = "website"
|
||||
record.Name = cronjob.Website
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(websiteItem))
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
backupDir := fmt.Sprintf("%s/website/%s", localDir, website.PrimaryDomain)
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
|
||||
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
|
||||
record.Source = backup.Type
|
||||
record.FileDir = strings.ReplaceAll(backupDir, localDir+"/", "")
|
||||
}
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
|
||||
if err := handleWebsiteBackup(&website, backupDir, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
record.Name = website.PrimaryDomain
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return paths, err
|
||||
}
|
||||
if backup.Type != "LOCAL" {
|
||||
if !cronjob.KeepLocal {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
|
||||
}()
|
||||
}
|
||||
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
}
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
@@ -141,29 +143,57 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
|
||||
return ¤tInfo
|
||||
}
|
||||
|
||||
type diskInfo struct {
|
||||
Type string
|
||||
Mount string
|
||||
Device string
|
||||
}
|
||||
|
||||
func loadDiskInfo() []dto.DiskInfo {
|
||||
var datas []dto.DiskInfo
|
||||
parts, err := disk.Partitions(false)
|
||||
stdout, err := cmd.Exec("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")
|
||||
if err != nil {
|
||||
return datas
|
||||
}
|
||||
lines := strings.Split(stdout, "\n")
|
||||
|
||||
var mounts []diskInfo
|
||||
var excludes = []string{"/mnt/cdrom", "/boot", "/boot/efi", "/dev", "/dev/shm", "/run/lock", "/run", "/run/shm", "/run/user"}
|
||||
for i := 0; i < len(parts); i++ {
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 7 {
|
||||
continue
|
||||
}
|
||||
if fields[1] == "tmpfs" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(fields[2], "M") || strings.Contains(fields[2], "K") {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(fields[6], "docker") {
|
||||
continue
|
||||
}
|
||||
isExclude := false
|
||||
for _, exclude := range excludes {
|
||||
if parts[i].Mountpoint == exclude {
|
||||
if exclude == fields[6] {
|
||||
isExclude = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isExclude {
|
||||
continue
|
||||
}
|
||||
state, _ := disk.Usage(parts[i].Mountpoint)
|
||||
mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: fields[6]})
|
||||
}
|
||||
|
||||
for i := 0; i < len(mounts); i++ {
|
||||
state, err := disk.Usage(mounts[i].Mount)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var itemData dto.DiskInfo
|
||||
itemData.Path = parts[i].Mountpoint
|
||||
itemData.Type = parts[i].Fstype
|
||||
itemData.Device = parts[i].Device
|
||||
itemData.Path = mounts[i].Mount
|
||||
itemData.Type = mounts[i].Type
|
||||
itemData.Device = mounts[i].Device
|
||||
itemData.Total = state.Total
|
||||
itemData.Free = state.Free
|
||||
itemData.Used = state.Used
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"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"
|
||||
@@ -94,25 +94,13 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
}
|
||||
|
||||
createSql := fmt.Sprintf("create database `%s` default character set %s collate %s", req.Name, req.Format, formatMap[req.Format])
|
||||
if err := excuteSql(app.ContainerName, app.Password, createSql); err != nil {
|
||||
if err := excSQL(app.ContainerName, app.Password, createSql); err != nil {
|
||||
if strings.Contains(err.Error(), "ERROR 1007") {
|
||||
return nil, buserr.New(constant.ErrDatabaseIsExist)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
tmpPermission := req.Permission
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("create user '%s'@'%s' identified by '%s';", req.Username, tmpPermission, req.Password)); err != nil {
|
||||
_ = excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop database `%s`", req.Name))
|
||||
if strings.Contains(err.Error(), "ERROR 1396") {
|
||||
return nil, buserr.New(constant.ErrUserIsExist)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to '%s'@'%s'", req.Name, req.Username, tmpPermission)
|
||||
if app.Version == "5.7.39" {
|
||||
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, grantStr); err != nil {
|
||||
if err := u.createUser(app, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -161,10 +149,10 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
||||
return err
|
||||
}
|
||||
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", db.Username, db.Permission)); err != nil && !req.ForceDelete {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", db.Username, db.Permission)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop database if exists `%s`", db.Name)); err != nil && !req.ForceDelete {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database if exists `%s`", db.Name)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("execute delete database sql successful, now start to drop uploads and records")
|
||||
@@ -269,6 +257,9 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.Value == mysql.Permission {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
@@ -282,21 +273,30 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
||||
}
|
||||
|
||||
if info.Value != mysql.Permission {
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", mysql.Username, mysql.Permission)); err != nil {
|
||||
return err
|
||||
var userlist []string
|
||||
if strings.Contains(mysql.Permission, ",") {
|
||||
userlist = strings.Split(mysql.Permission, ",")
|
||||
} else {
|
||||
userlist = append(userlist, mysql.Permission)
|
||||
}
|
||||
for _, user := range userlist {
|
||||
if len(user) != 0 {
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", mysql.Username, user)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if info.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("create user if not exists '%s'@'%s' identified by '%s';", mysql.Username, info.Value, mysql.Password)); err != nil {
|
||||
return err
|
||||
}
|
||||
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to '%s'@'%s'", mysql.Name, mysql.Username, info.Value)
|
||||
if app.Version == "5.7.39" {
|
||||
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, mysql.Password)
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, grantStr); err != nil {
|
||||
|
||||
if err := u.createUser(app, dto.MysqlDBCreate{
|
||||
Username: mysql.Username,
|
||||
Name: mysql.Name,
|
||||
Permission: info.Value,
|
||||
Password: mysql.Password,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, "flush privileges"); err != nil {
|
||||
@@ -339,7 +339,7 @@ func (u *MysqlService) UpdateVariables(updatas []dto.MysqlVariablesUpdate) error
|
||||
var files []string
|
||||
|
||||
path := fmt.Sprintf("%s/mysql/%s/conf/my.cnf", constant.AppInstallDir, app.Name)
|
||||
lineBytes, err := ioutil.ReadFile(path)
|
||||
lineBytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -470,6 +470,50 @@ func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) createUser(app *repo.RootInfo, req dto.MysqlDBCreate) error {
|
||||
var userlist []string
|
||||
if strings.Contains(req.Permission, ",") {
|
||||
ips := strings.Split(req.Permission, ",")
|
||||
for _, ip := range ips {
|
||||
if len(ip) != 0 {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", req.Username, ip))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", req.Username, req.Permission))
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
||||
handleCreateError(req.Name, userlist, app)
|
||||
if strings.Contains(err.Error(), "ERROR 1396") {
|
||||
return buserr.New(constant.ErrUserIsExist)
|
||||
}
|
||||
return err
|
||||
}
|
||||
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", req.Name, user)
|
||||
if req.Name == "*" {
|
||||
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
|
||||
}
|
||||
if app.Version == "5.7.39" {
|
||||
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
|
||||
}
|
||||
if err := excSQL(app.ContainerName, app.Password, grantStr); err != nil {
|
||||
handleCreateError(req.Name, userlist, app)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func handleCreateError(dbName string, userlist []string, app *repo.RootInfo) {
|
||||
_ = excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database `%s`", dbName))
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
||||
global.LOG.Errorf("drop user failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func excuteSqlForMaps(containerName, password, command string) (map[string]string, error) {
|
||||
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
@@ -509,6 +553,21 @@ func excuteSql(containerName, password, command string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func excSQL(containerName, password, command string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return buserr.WithDetail(constant.ErrExecTimeOut, containerName, nil)
|
||||
}
|
||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
return errors.New(stdStr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateMyCnf(oldFiles []string, group string, param string, value interface{}) []string {
|
||||
isOn := false
|
||||
hasGroup := false
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -215,7 +214,7 @@ type redisConfig struct {
|
||||
|
||||
func confSet(redisName string, changeConf []redisConfig) error {
|
||||
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisName)
|
||||
lineBytes, err := ioutil.ReadFile(path)
|
||||
lineBytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -35,6 +34,7 @@ type daemonJsonItem struct {
|
||||
Mirrors []string `json:"registry-mirrors"`
|
||||
Registries []string `json:"insecure-registries"`
|
||||
LiveRestore bool `json:"live-restore"`
|
||||
IPTables bool `json:"iptables"`
|
||||
ExecOpts []string `json:"exec-opts"`
|
||||
}
|
||||
|
||||
@@ -49,55 +49,60 @@ func (u *DockerService) LoadDockerStatus() string {
|
||||
}
|
||||
|
||||
func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
|
||||
status := constant.StatusRunning
|
||||
var data dto.DaemonJsonConf
|
||||
data.IPTables = true
|
||||
data.Status = constant.StatusRunning
|
||||
stdout, err := cmd.Exec("systemctl is-active docker")
|
||||
if string(stdout) != "active\n" || err != nil {
|
||||
status = constant.Stopped
|
||||
data.Status = constant.Stopped
|
||||
}
|
||||
version := "-"
|
||||
data.IsSwarm = false
|
||||
stdout2, _ := cmd.Exec("docker info | grep Swarm")
|
||||
if string(stdout2) == " Swarm: active\n" {
|
||||
data.IsSwarm = true
|
||||
}
|
||||
data.Version = "-"
|
||||
client, err := docker.NewDockerClient()
|
||||
if err == nil {
|
||||
ctx := context.Background()
|
||||
itemVersion, err := client.ServerVersion(ctx)
|
||||
if err == nil {
|
||||
version = itemVersion.Version
|
||||
data.Version = itemVersion.Version
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil {
|
||||
return &dto.DaemonJsonConf{Status: status, Version: version}
|
||||
return &data
|
||||
}
|
||||
file, err := ioutil.ReadFile(constant.DaemonJsonPath)
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return &dto.DaemonJsonConf{Status: status, Version: version}
|
||||
return &data
|
||||
}
|
||||
var conf daemonJsonItem
|
||||
deamonMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal(file, &deamonMap); err != nil {
|
||||
return &dto.DaemonJsonConf{Status: status, Version: version}
|
||||
return &data
|
||||
}
|
||||
arr, err := json.Marshal(deamonMap)
|
||||
if err != nil {
|
||||
return &dto.DaemonJsonConf{Status: status, Version: version}
|
||||
return &data
|
||||
}
|
||||
if err := json.Unmarshal(arr, &conf); err != nil {
|
||||
return &dto.DaemonJsonConf{Status: status, Version: version}
|
||||
return &data
|
||||
}
|
||||
driver := "cgroupfs"
|
||||
if _, ok := deamonMap["iptables"]; !ok {
|
||||
conf.IPTables = true
|
||||
}
|
||||
data.CgroupDriver = "cgroupfs"
|
||||
for _, opt := range conf.ExecOpts {
|
||||
if strings.HasPrefix(opt, "native.cgroupdriver=") {
|
||||
driver = strings.ReplaceAll(opt, "native.cgroupdriver=", "")
|
||||
data.CgroupDriver = strings.ReplaceAll(opt, "native.cgroupdriver=", "")
|
||||
break
|
||||
}
|
||||
}
|
||||
data := dto.DaemonJsonConf{
|
||||
Status: status,
|
||||
Version: version,
|
||||
Mirrors: conf.Mirrors,
|
||||
Registries: conf.Registries,
|
||||
LiveRestore: conf.LiveRestore,
|
||||
CgroupDriver: driver,
|
||||
}
|
||||
|
||||
data.Mirrors = conf.Mirrors
|
||||
data.Registries = conf.Registries
|
||||
data.IPTables = conf.IPTables
|
||||
data.LiveRestore = conf.LiveRestore
|
||||
return &data
|
||||
}
|
||||
|
||||
@@ -109,7 +114,7 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
}
|
||||
|
||||
file, err := ioutil.ReadFile(constant.DaemonJsonPath)
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -131,6 +136,11 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
|
||||
} else {
|
||||
deamonMap["live-restore"] = req.LiveRestore
|
||||
}
|
||||
if req.IPTables {
|
||||
delete(deamonMap, "iptables")
|
||||
} else {
|
||||
deamonMap["iptables"] = false
|
||||
}
|
||||
if opts, ok := deamonMap["exec-opts"]; ok {
|
||||
if optsValue, isArray := opts.([]interface{}); isArray {
|
||||
for i := 0; i < len(optsValue); i++ {
|
||||
@@ -147,11 +157,15 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
|
||||
deamonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"}
|
||||
}
|
||||
}
|
||||
if len(deamonMap) == 0 {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
return nil
|
||||
}
|
||||
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
|
||||
if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -163,6 +177,16 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
|
||||
}
|
||||
|
||||
func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
|
||||
if len(req.File) == 0 {
|
||||
_ = os.Remove(constant.DaemonJsonPath)
|
||||
return nil
|
||||
}
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = os.Create(constant.DaemonJsonPath)
|
||||
}
|
||||
file, err := os.OpenFile(constant.DaemonJsonPath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -2,76 +2,38 @@ package service
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
|
||||
type ServiceGroup struct {
|
||||
AuthService
|
||||
DashboardService
|
||||
|
||||
AppService
|
||||
AppInstallService
|
||||
|
||||
ContainerService
|
||||
ImageService
|
||||
ImageRepoService
|
||||
ComposeTemplateService
|
||||
DockerService
|
||||
|
||||
MysqlService
|
||||
RedisService
|
||||
|
||||
CronjobService
|
||||
|
||||
HostService
|
||||
GroupService
|
||||
CommandService
|
||||
FileService
|
||||
|
||||
SettingService
|
||||
BackupService
|
||||
|
||||
WebsiteService
|
||||
WebsiteDnsAccountService
|
||||
WebsiteSSLService
|
||||
WebsiteAcmeAccountService
|
||||
|
||||
NginxService
|
||||
|
||||
LogService
|
||||
SnapshotService
|
||||
UpgradeService
|
||||
}
|
||||
|
||||
var ServiceGroupApp = new(ServiceGroup)
|
||||
|
||||
var (
|
||||
commonRepo = repo.RepoGroupApp.CommonRepo
|
||||
commonRepo = repo.NewCommonRepo()
|
||||
|
||||
appRepo = repo.RepoGroupApp.AppRepo
|
||||
appTagRepo = repo.RepoGroupApp.AppTagRepo
|
||||
appDetailRepo = repo.RepoGroupApp.AppDetailRepo
|
||||
tagRepo = repo.RepoGroupApp.TagRepo
|
||||
appInstallRepo = repo.RepoGroupApp.AppInstallRepo
|
||||
appInstallResourceRepo = repo.RepoGroupApp.AppInstallResourceRpo
|
||||
appRepo = repo.NewIAppRepo()
|
||||
appTagRepo = repo.NewIAppTagRepo()
|
||||
appDetailRepo = repo.NewIAppDetailRepo()
|
||||
tagRepo = repo.NewITagRepo()
|
||||
appInstallRepo = repo.NewIAppInstallRepo()
|
||||
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
||||
|
||||
mysqlRepo = repo.RepoGroupApp.MysqlRepo
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
|
||||
imageRepoRepo = repo.RepoGroupApp.ImageRepoRepo
|
||||
composeRepo = repo.RepoGroupApp.ComposeTemplateRepo
|
||||
imageRepoRepo = repo.NewIImageRepoRepo()
|
||||
composeRepo = repo.NewIComposeTemplateRepo()
|
||||
|
||||
cronjobRepo = repo.RepoGroupApp.CronjobRepo
|
||||
cronjobRepo = repo.NewICronjobRepo()
|
||||
|
||||
hostRepo = repo.RepoGroupApp.HostRepo
|
||||
groupRepo = repo.RepoGroupApp.GroupRepo
|
||||
commandRepo = repo.RepoGroupApp.CommandRepo
|
||||
hostRepo = repo.NewIHostRepo()
|
||||
groupRepo = repo.NewIGroupRepo()
|
||||
commandRepo = repo.NewICommandRepo()
|
||||
|
||||
settingRepo = repo.RepoGroupApp.SettingRepo
|
||||
backupRepo = repo.RepoGroupApp.BackupRepo
|
||||
settingRepo = repo.NewISettingRepo()
|
||||
backupRepo = repo.NewIBackupRepo()
|
||||
|
||||
websiteRepo = repo.NewIWebsiteRepo()
|
||||
websiteDomainRepo = repo.RepoGroupApp.WebsiteDomainRepo
|
||||
websiteDnsRepo = repo.RepoGroupApp.WebsiteDnsAccountRepo
|
||||
websiteDomainRepo = repo.NewIWebsiteDomainRepo()
|
||||
websiteDnsRepo = repo.NewIWebsiteDnsAccountRepo()
|
||||
websiteSSLRepo = repo.NewISSLRepo()
|
||||
websiteAcmeRepo = repo.NewIAcmeAccountRepo()
|
||||
|
||||
logRepo = repo.RepoGroupApp.LogRepo
|
||||
logRepo = repo.NewILogRepo()
|
||||
snapshotRepo = repo.NewISnapshotRepo()
|
||||
|
||||
runtimeRepo = repo.NewIRunTimeRepo()
|
||||
)
|
||||
|
||||
+50
-17
@@ -22,7 +22,31 @@ import (
|
||||
type FileService struct {
|
||||
}
|
||||
|
||||
func (f FileService) GetFileList(op request.FileOption) (response.FileInfo, error) {
|
||||
type IFileService interface {
|
||||
GetFileList(op request.FileOption) (response.FileInfo, error)
|
||||
SearchUploadWithPage(req request.SearchUploadWithPage) (int64, interface{}, error)
|
||||
GetFileTree(op request.FileOption) ([]response.FileTree, error)
|
||||
Create(op request.FileCreate) error
|
||||
Delete(op request.FileDelete) error
|
||||
BatchDelete(op request.FileBatchDelete) error
|
||||
ChangeMode(op request.FileCreate) error
|
||||
Compress(c request.FileCompress) error
|
||||
DeCompress(c request.FileDeCompress) error
|
||||
GetContent(op request.FileOption) (response.FileInfo, error)
|
||||
SaveContent(edit request.FileEdit) error
|
||||
FileDownload(d request.FileDownload) (string, error)
|
||||
DirSize(req request.DirSizeReq) (response.DirSizeRes, error)
|
||||
ChangeName(req request.FileRename) error
|
||||
Wget(w request.FileWget) (string, error)
|
||||
MvFile(m request.FileMove) error
|
||||
ChangeOwner(req request.FileRoleUpdate) error
|
||||
}
|
||||
|
||||
func NewIFileService() IFileService {
|
||||
return &FileService{}
|
||||
}
|
||||
|
||||
func (f *FileService) GetFileList(op request.FileOption) (response.FileInfo, error) {
|
||||
var fileInfo response.FileInfo
|
||||
if _, err := os.Stat(op.Path); err != nil && os.IsNotExist(err) {
|
||||
return fileInfo, nil
|
||||
@@ -35,7 +59,7 @@ func (f FileService) GetFileList(op request.FileOption) (response.FileInfo, erro
|
||||
return fileInfo, nil
|
||||
}
|
||||
|
||||
func (f FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (int64, interface{}, error) {
|
||||
func (f *FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (int64, interface{}, error) {
|
||||
var (
|
||||
files []response.UploadInfo
|
||||
backData []response.UploadInfo
|
||||
@@ -65,7 +89,7 @@ func (f FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (int
|
||||
return int64(total), backData, nil
|
||||
}
|
||||
|
||||
func (f FileService) GetFileTree(op request.FileOption) ([]response.FileTree, error) {
|
||||
func (f *FileService) GetFileTree(op request.FileOption) ([]response.FileTree, error) {
|
||||
var treeArray []response.FileTree
|
||||
info, err := files.NewFileInfo(op.FileOption)
|
||||
if err != nil {
|
||||
@@ -88,7 +112,7 @@ func (f FileService) GetFileTree(op request.FileOption) ([]response.FileTree, er
|
||||
return append(treeArray, node), nil
|
||||
}
|
||||
|
||||
func (f FileService) Create(op request.FileCreate) error {
|
||||
func (f *FileService) Create(op request.FileCreate) error {
|
||||
fo := files.NewFileOp()
|
||||
if fo.Stat(op.Path) {
|
||||
return buserr.New(constant.ErrFileIsExit)
|
||||
@@ -107,7 +131,7 @@ func (f FileService) Create(op request.FileCreate) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (f FileService) Delete(op request.FileDelete) error {
|
||||
func (f *FileService) Delete(op request.FileDelete) error {
|
||||
fo := files.NewFileOp()
|
||||
if op.IsDir {
|
||||
return fo.DeleteDir(op.Path)
|
||||
@@ -116,7 +140,7 @@ func (f FileService) Delete(op request.FileDelete) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (f FileService) BatchDelete(op request.FileBatchDelete) error {
|
||||
func (f *FileService) BatchDelete(op request.FileBatchDelete) error {
|
||||
fo := files.NewFileOp()
|
||||
if op.IsDir {
|
||||
for _, file := range op.Paths {
|
||||
@@ -134,12 +158,21 @@ func (f FileService) BatchDelete(op request.FileBatchDelete) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FileService) ChangeMode(op request.FileCreate) error {
|
||||
func (f *FileService) ChangeMode(op request.FileCreate) error {
|
||||
fo := files.NewFileOp()
|
||||
return fo.Chmod(op.Path, fs.FileMode(op.Mode))
|
||||
if op.Sub {
|
||||
return fo.ChmodR(op.Path, op.Mode)
|
||||
} else {
|
||||
return fo.Chmod(op.Path, fs.FileMode(op.Mode))
|
||||
}
|
||||
}
|
||||
|
||||
func (f FileService) Compress(c request.FileCompress) error {
|
||||
func (f *FileService) ChangeOwner(req request.FileRoleUpdate) error {
|
||||
fo := files.NewFileOp()
|
||||
return fo.ChownR(req.Path, req.User, req.Group, req.Sub)
|
||||
}
|
||||
|
||||
func (f *FileService) Compress(c request.FileCompress) error {
|
||||
fo := files.NewFileOp()
|
||||
if !c.Replace && fo.Stat(filepath.Join(c.Dst, c.Name)) {
|
||||
return buserr.New(constant.ErrFileIsExit)
|
||||
@@ -147,12 +180,12 @@ func (f FileService) Compress(c request.FileCompress) error {
|
||||
return fo.Compress(c.Files, c.Dst, c.Name, files.CompressType(c.Type))
|
||||
}
|
||||
|
||||
func (f FileService) DeCompress(c request.FileDeCompress) error {
|
||||
func (f *FileService) DeCompress(c request.FileDeCompress) error {
|
||||
fo := files.NewFileOp()
|
||||
return fo.Decompress(c.Path, c.Dst, files.CompressType(c.Type))
|
||||
}
|
||||
|
||||
func (f FileService) GetContent(op request.FileOption) (response.FileInfo, error) {
|
||||
func (f *FileService) GetContent(op request.FileOption) (response.FileInfo, error) {
|
||||
info, err := files.NewFileInfo(op.FileOption)
|
||||
if err != nil {
|
||||
return response.FileInfo{}, err
|
||||
@@ -160,7 +193,7 @@ func (f FileService) GetContent(op request.FileOption) (response.FileInfo, error
|
||||
return response.FileInfo{FileInfo: *info}, nil
|
||||
}
|
||||
|
||||
func (f FileService) SaveContent(edit request.FileEdit) error {
|
||||
func (f *FileService) SaveContent(edit request.FileEdit) error {
|
||||
info, err := files.NewFileInfo(files.FileOption{
|
||||
Path: edit.Path,
|
||||
Expand: false,
|
||||
@@ -173,18 +206,18 @@ func (f FileService) SaveContent(edit request.FileEdit) error {
|
||||
return fo.WriteFile(edit.Path, strings.NewReader(edit.Content), info.FileMode)
|
||||
}
|
||||
|
||||
func (f FileService) ChangeName(req request.FileRename) error {
|
||||
func (f *FileService) ChangeName(req request.FileRename) error {
|
||||
fo := files.NewFileOp()
|
||||
return fo.Rename(req.OldName, req.NewName)
|
||||
}
|
||||
|
||||
func (f FileService) Wget(w request.FileWget) (string, error) {
|
||||
func (f *FileService) Wget(w request.FileWget) (string, error) {
|
||||
fo := files.NewFileOp()
|
||||
key := "file-wget-" + common.GetUuid()
|
||||
return key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key)
|
||||
}
|
||||
|
||||
func (f FileService) MvFile(m request.FileMove) error {
|
||||
func (f *FileService) MvFile(m request.FileMove) error {
|
||||
fo := files.NewFileOp()
|
||||
if !fo.Stat(m.NewPath) {
|
||||
return buserr.New(constant.ErrPathNotFound)
|
||||
@@ -217,7 +250,7 @@ func (f FileService) MvFile(m request.FileMove) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FileService) FileDownload(d request.FileDownload) (string, error) {
|
||||
func (f *FileService) FileDownload(d request.FileDownload) (string, error) {
|
||||
filePath := d.Paths[0]
|
||||
if d.Compress {
|
||||
tempPath := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
@@ -233,7 +266,7 @@ func (f FileService) FileDownload(d request.FileDownload) (string, error) {
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
func (f FileService) DirSize(req request.DirSizeReq) (response.DirSizeRes, error) {
|
||||
func (f *FileService) DirSize(req request.DirSizeReq) (response.DirSizeRes, error) {
|
||||
fo := files.NewFileOp()
|
||||
size, err := fo.GetDirSize(req.Path)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,448 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/firewall"
|
||||
fireClient "github.com/1Panel-dev/1Panel/backend/utils/firewall/client"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
const confPath = "/etc/sysctl.conf"
|
||||
|
||||
type FirewallService struct{}
|
||||
|
||||
type IFirewallService interface {
|
||||
LoadBaseInfo() (dto.FirewallBaseInfo, error)
|
||||
SearchWithPage(search dto.RuleSearch) (int64, interface{}, error)
|
||||
OperateFirewall(operation string) error
|
||||
OperatePortRule(req dto.PortRuleOperate, reload bool) error
|
||||
OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
|
||||
UpdatePortRule(req dto.PortRuleUpdate) error
|
||||
UpdateAddrRule(req dto.AddrRuleUpdate) error
|
||||
BacthOperateRule(req dto.BatchRuleOperate) error
|
||||
}
|
||||
|
||||
func NewIFirewallService() IFirewallService {
|
||||
return &FirewallService{}
|
||||
}
|
||||
|
||||
func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
|
||||
var baseInfo dto.FirewallBaseInfo
|
||||
baseInfo.PingStatus = u.pingStatus()
|
||||
baseInfo.Status = "not running"
|
||||
baseInfo.Version = "-"
|
||||
baseInfo.Name = "-"
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
if err.Error() == "no such type" {
|
||||
return baseInfo, nil
|
||||
}
|
||||
return baseInfo, err
|
||||
}
|
||||
baseInfo.Name = client.Name()
|
||||
baseInfo.Status, err = client.Status()
|
||||
if err != nil {
|
||||
return baseInfo, err
|
||||
}
|
||||
if baseInfo.Status == "not running" {
|
||||
return baseInfo, err
|
||||
}
|
||||
baseInfo.Version, err = client.Version()
|
||||
if err != nil {
|
||||
return baseInfo, err
|
||||
}
|
||||
return baseInfo, nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}, error) {
|
||||
var (
|
||||
datas []fireClient.FireInfo
|
||||
backDatas []fireClient.FireInfo
|
||||
)
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if req.Type == "port" {
|
||||
ports, err := client.ListPort()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
for _, port := range ports {
|
||||
if strings.Contains(port.Port, req.Info) {
|
||||
datas = append(datas, port)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
datas = ports
|
||||
}
|
||||
} else {
|
||||
addrs, err := client.ListAddress()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
for _, addr := range addrs {
|
||||
if strings.Contains(addr.Address, req.Info) {
|
||||
datas = append(datas, addr)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
datas = addrs
|
||||
}
|
||||
}
|
||||
total, start, end := len(datas), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
backDatas = make([]fireClient.FireInfo, 0)
|
||||
} else {
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
backDatas = datas[start:end]
|
||||
}
|
||||
|
||||
if req.Type == "port" {
|
||||
apps := u.loadPortByApp()
|
||||
for i := 0; i < len(backDatas); i++ {
|
||||
port, _ := strconv.Atoi(backDatas[i].Port)
|
||||
backDatas[i].IsUsed = common.ScanPort(port)
|
||||
if backDatas[i].Protocol == "udp" {
|
||||
backDatas[i].IsUsed = common.ScanUDPPort(port)
|
||||
continue
|
||||
}
|
||||
for _, app := range apps {
|
||||
if app.HttpPort == backDatas[i].Port || app.HttpsPort == backDatas[i].Port {
|
||||
backDatas[i].APPName = app.AppName
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch operation {
|
||||
case "start":
|
||||
if err := client.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.addPortsBeforeStart(client); err != nil {
|
||||
_ = client.Stop()
|
||||
return err
|
||||
}
|
||||
_, _ = cmd.Exec("systemctl restart docker")
|
||||
return nil
|
||||
case "stop":
|
||||
if err := client.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = cmd.Exec("systemctl restart docker")
|
||||
return nil
|
||||
case "disablePing":
|
||||
return u.updatePingStatus("0")
|
||||
case "enablePing":
|
||||
return u.updatePingStatus("1")
|
||||
}
|
||||
return fmt.Errorf("not support such operation: %s", operation)
|
||||
}
|
||||
|
||||
func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if client.Name() == "ufw" {
|
||||
req.Port = strings.ReplaceAll(req.Port, "-", ":")
|
||||
if req.Operation == "remove" && req.Protocol == "tcp/udp" {
|
||||
req.Protocol = ""
|
||||
return u.operatePort(client, req)
|
||||
}
|
||||
}
|
||||
if req.Protocol == "tcp/udp" {
|
||||
if client.Name() == "firewalld" && strings.Contains(req.Port, ",") {
|
||||
ports := strings.Split(req.Port, ",")
|
||||
for _, port := range ports {
|
||||
if len(port) == 0 {
|
||||
continue
|
||||
}
|
||||
req.Port = port
|
||||
req.Protocol = "tcp"
|
||||
if err := u.operatePort(client, req); err != nil {
|
||||
return err
|
||||
}
|
||||
req.Protocol = "udp"
|
||||
if err := u.operatePort(client, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
req.Protocol = "tcp"
|
||||
if err := u.operatePort(client, req); err != nil {
|
||||
return err
|
||||
}
|
||||
req.Protocol = "udp"
|
||||
if err := u.operatePort(client, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if strings.Contains(req.Port, ",") {
|
||||
ports := strings.Split(req.Port, ",")
|
||||
for _, port := range ports {
|
||||
req.Port = port
|
||||
if err := u.operatePort(client, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := u.operatePort(client, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if reload {
|
||||
return client.Reload()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload bool) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var fireInfo fireClient.FireInfo
|
||||
if err := copier.Copy(&fireInfo, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addressList := strings.Split(req.Address, ",")
|
||||
for _, addr := range addressList {
|
||||
if len(addr) == 0 {
|
||||
continue
|
||||
}
|
||||
fireInfo.Address = addr
|
||||
if err := client.RichRules(fireInfo, req.Operation); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if reload {
|
||||
return client.Reload()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) UpdatePortRule(req dto.PortRuleUpdate) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.OperatePortRule(req.OldRule, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.OperatePortRule(req.NewRule, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return client.Reload()
|
||||
}
|
||||
|
||||
func (u *FirewallService) UpdateAddrRule(req dto.AddrRuleUpdate) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.OperateAddressRule(req.OldRule, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.OperateAddressRule(req.NewRule, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return client.Reload()
|
||||
}
|
||||
|
||||
func (u *FirewallService) BacthOperateRule(req dto.BatchRuleOperate) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Type == "port" {
|
||||
for _, rule := range req.Rules {
|
||||
if err := u.OperatePortRule(rule, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return client.Reload()
|
||||
}
|
||||
for _, rule := range req.Rules {
|
||||
itemRule := dto.AddrRuleOperate{Operation: rule.Operation, Address: rule.Address, Strategy: rule.Strategy}
|
||||
if err := u.OperateAddressRule(itemRule, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return client.Reload()
|
||||
}
|
||||
|
||||
func OperateFirewallPort(oldPorts, newPorts []int) error {
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, port := range newPorts {
|
||||
|
||||
if err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, port := range oldPorts {
|
||||
if err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: "tcp", Strategy: "accept"}, "remove"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return client.Reload()
|
||||
}
|
||||
|
||||
func (u *FirewallService) operatePort(client firewall.FirewallClient, req dto.PortRuleOperate) error {
|
||||
var fireInfo fireClient.FireInfo
|
||||
if err := copier.Copy(&fireInfo, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if client.Name() == "ufw" {
|
||||
if len(fireInfo.Address) != 0 && fireInfo.Address != "Anywhere" {
|
||||
return client.RichRules(fireInfo, req.Operation)
|
||||
}
|
||||
return client.Port(fireInfo, req.Operation)
|
||||
}
|
||||
|
||||
if len(fireInfo.Address) != 0 || fireInfo.Strategy == "drop" {
|
||||
return client.RichRules(fireInfo, req.Operation)
|
||||
}
|
||||
return client.Port(fireInfo, req.Operation)
|
||||
}
|
||||
|
||||
type portOfApp struct {
|
||||
AppName string
|
||||
HttpPort string
|
||||
HttpsPort string
|
||||
}
|
||||
|
||||
func (u *FirewallService) loadPortByApp() []portOfApp {
|
||||
var datas []portOfApp
|
||||
apps, err := appInstallRepo.ListBy()
|
||||
if err != nil {
|
||||
return datas
|
||||
}
|
||||
for i := 0; i < len(apps); i++ {
|
||||
datas = append(datas, portOfApp{
|
||||
AppName: apps[i].App.Key,
|
||||
HttpPort: strconv.Itoa(apps[i].HttpPort),
|
||||
HttpsPort: strconv.Itoa(apps[i].HttpsPort),
|
||||
})
|
||||
}
|
||||
systemPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort"))
|
||||
if err != nil {
|
||||
return datas
|
||||
}
|
||||
datas = append(datas, portOfApp{AppName: "1panel", HttpPort: systemPort.Value})
|
||||
|
||||
return datas
|
||||
}
|
||||
|
||||
func (u *FirewallService) pingStatus() string {
|
||||
if _, err := os.Stat("/etc/sysctl.conf"); err != nil {
|
||||
return constant.StatusNone
|
||||
}
|
||||
commond := "cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= "
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
commond = "sudo cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= "
|
||||
}
|
||||
stdout, _ := cmd.Exec(commond)
|
||||
if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" {
|
||||
return constant.StatusEnable
|
||||
}
|
||||
return constant.StatusDisable
|
||||
}
|
||||
|
||||
func (u *FirewallService) updatePingStatus(enabel string) error {
|
||||
lineBytes, err := os.ReadFile(confPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files := strings.Split(string(lineBytes), "\n")
|
||||
var newFiles []string
|
||||
hasLine := false
|
||||
for _, line := range files {
|
||||
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") {
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
|
||||
hasLine = true
|
||||
} else {
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
}
|
||||
if !hasLine {
|
||||
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
|
||||
}
|
||||
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = file.WriteString(strings.Join(newFiles, "\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commond := "sysctl -p"
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
commond = "sudo sysctl -p"
|
||||
}
|
||||
stdout, err := cmd.Exec(commond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update ping status failed, err: %v", stdout)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) error {
|
||||
serverPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: serverPort.Value, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: "22", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: "80", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: "443", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
apps := u.loadPortByApp()
|
||||
for _, app := range apps {
|
||||
if err := client.Port(fireClient.FireInfo{Port: app.HttpPort, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return client.Reload()
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -15,6 +16,7 @@ type HostService struct{}
|
||||
|
||||
type IHostService interface {
|
||||
TestLocalConn(id uint) bool
|
||||
TestByInfo(req dto.HostConnTest) bool
|
||||
GetHostInfo(id uint) (*model.Host, error)
|
||||
SearchForTree(search dto.SearchForTree) ([]dto.HostTree, error)
|
||||
SearchWithPage(search dto.SearchHostWithPage) (int64, interface{}, error)
|
||||
@@ -27,6 +29,46 @@ func NewIHostService() IHostService {
|
||||
return &HostService{}
|
||||
}
|
||||
|
||||
func (u *HostService) TestByInfo(req dto.HostConnTest) bool {
|
||||
if req.AuthMode == "password" && len(req.Password) != 0 {
|
||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
req.Password = string(password)
|
||||
}
|
||||
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
|
||||
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
req.PrivateKey = string(privateKey)
|
||||
}
|
||||
if len(req.Password) == 0 && len(req.PrivateKey) == 0 {
|
||||
host, err := hostRepo.Get(hostRepo.WithByAddr(req.Addr))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
req.Password = host.Password
|
||||
req.AuthMode = host.AuthMode
|
||||
req.PrivateKey = host.PrivateKey
|
||||
req.PassPhrase = host.PassPhrase
|
||||
}
|
||||
|
||||
var connInfo ssh.ConnInfo
|
||||
_ = copier.Copy(&connInfo, &req)
|
||||
connInfo.PrivateKey = []byte(req.PrivateKey)
|
||||
if len(req.PassPhrase) != 0 {
|
||||
connInfo.PassPhrase = []byte(req.PassPhrase)
|
||||
}
|
||||
client, err := connInfo.NewClient()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer client.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
func (u *HostService) TestLocalConn(id uint) bool {
|
||||
var (
|
||||
host model.Host
|
||||
@@ -47,6 +89,10 @@ func (u *HostService) TestLocalConn(id uint) bool {
|
||||
if err := copier.Copy(&connInfo, &host); err != nil {
|
||||
return false
|
||||
}
|
||||
connInfo.PrivateKey = []byte(host.PrivateKey)
|
||||
if len(host.PassPhrase) != 0 {
|
||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||
}
|
||||
client, err := connInfo.NewClient()
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -77,6 +123,11 @@ func (u *HostService) SearchWithPage(search dto.SearchHostWithPage) (int64, inte
|
||||
}
|
||||
group, _ := groupRepo.Get(commonRepo.WithByID(host.GroupID))
|
||||
item.GroupBelong = group.Name
|
||||
if !item.RememberPassword {
|
||||
item.Password = ""
|
||||
item.PrivateKey = ""
|
||||
item.PassPhrase = ""
|
||||
}
|
||||
dtoHosts = append(dtoHosts, item)
|
||||
}
|
||||
return total, dtoHosts, err
|
||||
@@ -144,6 +195,8 @@ func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
|
||||
upMap["auth_mode"] = req.AuthMode
|
||||
upMap["password"] = req.Password
|
||||
upMap["private_key"] = req.PrivateKey
|
||||
upMap["pass_phrase"] = req.PassPhrase
|
||||
upMap["remember_password"] = req.RememberPassword
|
||||
upMap["description"] = req.Description
|
||||
if err := hostRepo.Update(sameHostID, upMap); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -34,6 +33,7 @@ type IImageService interface {
|
||||
ImageSave(req dto.ImageSave) error
|
||||
ImagePush(req dto.ImagePush) (string, error)
|
||||
ImageRemove(req dto.BatchDelete) error
|
||||
ImageTag(req dto.ImageTag) error
|
||||
}
|
||||
|
||||
func NewIImageService() IImageService {
|
||||
@@ -173,7 +173,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("build image %s failed, err: %v", req.Name, err)
|
||||
_, _ = file.WriteString(fmt.Sprintf("build image %s failed, err: %v", req.Name, err))
|
||||
@@ -274,7 +274,7 @@ func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content, err := ioutil.ReadAll(res.Body)
|
||||
content, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -190,7 +189,7 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
|
||||
}
|
||||
|
||||
deamonMap := make(map[string]interface{})
|
||||
file, err := ioutil.ReadFile(constant.DaemonJsonPath)
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -226,7 +225,7 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
|
||||
if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
@@ -18,6 +19,18 @@ import (
|
||||
type NginxService struct {
|
||||
}
|
||||
|
||||
type INginxService interface {
|
||||
GetNginxConfig() (response.FileInfo, error)
|
||||
GetConfigByScope(req request.NginxScopeReq) ([]response.NginxParam, error)
|
||||
UpdateConfigByScope(req request.NginxConfigUpdate) error
|
||||
GetStatus() (response.NginxStatus, error)
|
||||
UpdateConfigFile(req request.NginxConfigFileUpdate) error
|
||||
}
|
||||
|
||||
func NewINginxService() INginxService {
|
||||
return &NginxService{}
|
||||
}
|
||||
|
||||
func (n NginxService) GetNginxConfig() (response.FileInfo, error) {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
@@ -55,7 +68,7 @@ func (n NginxService) GetStatus() (response.NginxStatus, error) {
|
||||
if err != nil {
|
||||
return response.NginxStatus{}, err
|
||||
}
|
||||
content, err := ioutil.ReadAll(res.Body)
|
||||
content, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return response.NginxStatus{}, err
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getNginxFull(website *model.Website) (dto.NginxFull, error) {
|
||||
@@ -160,6 +161,10 @@ func getNginxParamsFromStaticFile(scope dto.NginxKey, newParams []dto.NginxParam
|
||||
switch scope {
|
||||
case dto.SSL:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
|
||||
case dto.CACHE:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.Cache)).Parse()
|
||||
case dto.ProxyCache:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.ProxyCache)).Parse()
|
||||
}
|
||||
for _, dir := range newConfig.GetDirectives() {
|
||||
addParam := dto.NginxParam{
|
||||
@@ -191,7 +196,7 @@ func opNginx(containerName, operate string) error {
|
||||
if operate == constant.NginxCheck {
|
||||
nginxCmd = fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -t")
|
||||
}
|
||||
if out, err := cmd.Exec(nginxCmd); err != nil {
|
||||
if out, err := cmd.ExecWithTimeOut(nginxCmd, 2*time.Second); err != nil {
|
||||
return errors.New(out)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"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/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/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/subosito/gotenv"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RuntimeService struct {
|
||||
}
|
||||
|
||||
type IRuntimeService interface {
|
||||
Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error)
|
||||
Create(create request.RuntimeCreate) error
|
||||
Delete(id uint) error
|
||||
Update(req request.RuntimeUpdate) error
|
||||
Get(id uint) (res *response.RuntimeRes, err error)
|
||||
}
|
||||
|
||||
func NewRuntimeService() IRuntimeService {
|
||||
return &RuntimeService{}
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithName(create.Name))
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
if create.Resource == constant.ResourceLocal {
|
||||
runtime := &model.Runtime{
|
||||
Name: create.Name,
|
||||
Resource: create.Resource,
|
||||
Type: create.Type,
|
||||
Version: create.Version,
|
||||
Status: constant.RuntimeNormal,
|
||||
}
|
||||
return runtimeRepo.Create(context.Background(), runtime)
|
||||
}
|
||||
exist, _ = runtimeRepo.GetFirst(runtimeRepo.WithImage(create.Image))
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
buildDir := path.Join(constant.AppResourceDir, app.Key, "versions", appDetail.Version, "build")
|
||||
if !fileOp.Stat(buildDir) {
|
||||
return buserr.New(constant.ErrDirNotFound)
|
||||
}
|
||||
runtimeDir := path.Join(constant.RuntimeDir, create.Type)
|
||||
tempDir := filepath.Join(runtimeDir, fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
if err = fileOp.CopyDir(buildDir, tempDir); err != nil {
|
||||
return
|
||||
}
|
||||
oldDir := path.Join(tempDir, "build")
|
||||
newNameDir := path.Join(runtimeDir, create.Name)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = fileOp.DeleteDir(newNameDir)
|
||||
}
|
||||
}()
|
||||
if oldDir != newNameDir {
|
||||
if err = fileOp.Rename(oldDir, newNameDir); err != nil {
|
||||
return
|
||||
}
|
||||
if err = fileOp.DeleteDir(tempDir); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
composeContent, envContent, forms, err := handleParams(create.Image, create.Type, newNameDir, create.Params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
composeService, err := getComposeService(create.Name, newNameDir, composeContent, envContent, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
runtime := &model.Runtime{
|
||||
Name: create.Name,
|
||||
DockerCompose: string(composeContent),
|
||||
Env: string(envContent),
|
||||
AppDetailID: create.AppDetailID,
|
||||
Type: create.Type,
|
||||
Image: create.Image,
|
||||
Resource: create.Resource,
|
||||
Status: constant.RuntimeBuildIng,
|
||||
Version: create.Version,
|
||||
Params: string(forms),
|
||||
}
|
||||
if err = runtimeRepo.Create(context.Background(), runtime); err != nil {
|
||||
return
|
||||
}
|
||||
go buildRuntime(runtime, composeService, "")
|
||||
return
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error) {
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
res []response.RuntimeRes
|
||||
)
|
||||
if req.Name != "" {
|
||||
opts = append(opts, commonRepo.WithLikeName(req.Name))
|
||||
}
|
||||
if req.Status != "" {
|
||||
opts = append(opts, runtimeRepo.WithStatus(req.Status))
|
||||
}
|
||||
total, runtimes, err := runtimeRepo.Page(req.Page, req.PageSize, opts...)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, runtime := range runtimes {
|
||||
res = append(res, response.RuntimeRes{
|
||||
Runtime: runtime,
|
||||
})
|
||||
}
|
||||
return total, res, nil
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Delete(id uint) error {
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithRuntimeID(id))
|
||||
if website.ID > 0 {
|
||||
return buserr.New(constant.ErrDelWithWebsite)
|
||||
}
|
||||
if runtime.Resource == constant.ResourceAppstore {
|
||||
client, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
imageID, err := client.GetImageIDByName(runtime.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if imageID != "" {
|
||||
if err := client.DeleteImage(imageID); err != nil {
|
||||
global.LOG.Errorf("delete image id [%s] error %v", imageID, err)
|
||||
}
|
||||
}
|
||||
runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
|
||||
if err := files.NewFileOp().DeleteDir(runtimeDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return runtimeRepo.DeleteBy(commonRepo.WithByID(id))
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Get(id uint) (*response.RuntimeRes, error) {
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &response.RuntimeRes{}
|
||||
res.Runtime = *runtime
|
||||
if runtime.Resource == constant.ResourceLocal {
|
||||
return res, nil
|
||||
}
|
||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(runtime.AppDetailID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.AppID = appDetail.AppId
|
||||
var (
|
||||
appForm dto.AppForm
|
||||
appParams []response.AppParam
|
||||
)
|
||||
if err := json.Unmarshal([]byte(runtime.Params), &appForm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envs, err := gotenv.Unmarshal(runtime.Env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, form := range appForm.FormFields {
|
||||
if v, ok := envs[form.EnvKey]; ok {
|
||||
appParam := response.AppParam{
|
||||
Edit: false,
|
||||
Key: form.EnvKey,
|
||||
Rule: form.Rule,
|
||||
Type: form.Type,
|
||||
Required: form.Required,
|
||||
}
|
||||
if form.Edit {
|
||||
appParam.Edit = true
|
||||
}
|
||||
appParam.LabelZh = form.LabelZh
|
||||
appParam.LabelEn = form.LabelEn
|
||||
appParam.Multiple = form.Multiple
|
||||
appParam.Value = v
|
||||
if form.Type == "select" {
|
||||
if form.Multiple {
|
||||
if v == "" {
|
||||
appParam.Value = []string{}
|
||||
} else {
|
||||
appParam.Value = strings.Split(v, ",")
|
||||
}
|
||||
} else {
|
||||
for _, fv := range form.Values {
|
||||
if fv.Value == v {
|
||||
appParam.ShowValue = fv.Label
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
appParam.Values = form.Values
|
||||
}
|
||||
appParams = append(appParams, appParam)
|
||||
}
|
||||
}
|
||||
res.AppParams = appParams
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldImage := runtime.Image
|
||||
if runtime.Resource == constant.ResourceLocal {
|
||||
runtime.Version = req.Version
|
||||
return runtimeRepo.Save(runtime)
|
||||
}
|
||||
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithImage(req.Name), runtimeRepo.WithNotId(req.ID))
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
|
||||
composeContent, envContent, _, err := handleParams(req.Image, runtime.Type, runtimeDir, req.Params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
composeService, err := getComposeService(runtime.Name, runtimeDir, composeContent, envContent, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runtime.Image = req.Image
|
||||
runtime.Env = string(envContent)
|
||||
runtime.DockerCompose = string(composeContent)
|
||||
runtime.Status = constant.RuntimeBuildIng
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
client, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
imageID, err := client.GetImageIDByName(oldImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go buildRuntime(runtime, composeService, imageID)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/subosito/gotenv"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func buildRuntime(runtime *model.Runtime, service *docker.ComposeService, oldImageID string) {
|
||||
err := service.ComposeBuild()
|
||||
if err != nil {
|
||||
runtime.Status = constant.RuntimeError
|
||||
runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + err.Error()
|
||||
} else {
|
||||
runtime.Status = constant.RuntimeNormal
|
||||
if oldImageID != "" {
|
||||
client, err := docker.NewClient()
|
||||
if err == nil {
|
||||
newImageID, err := client.GetImageIDByName(runtime.Image)
|
||||
if err == nil && newImageID != oldImageID {
|
||||
global.LOG.Infof("delete imageID [%s] ", oldImageID)
|
||||
if err := client.DeleteImage(oldImageID); err != nil {
|
||||
global.LOG.Errorf("delete imageID [%s] error %v", oldImageID, err)
|
||||
} else {
|
||||
global.LOG.Infof("delete old image success")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
global.LOG.Errorf("delete imageID [%s] error %v", oldImageID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
}
|
||||
|
||||
func handleParams(image, runtimeType, runtimeDir string, params map[string]interface{}) (composeContent []byte, envContent []byte, forms []byte, err error) {
|
||||
fileOp := files.NewFileOp()
|
||||
composeContent, err = fileOp.GetContent(path.Join(runtimeDir, "docker-compose.yml"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
env, err := gotenv.Read(path.Join(runtimeDir, ".env"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
forms, err = fileOp.GetContent(path.Join(runtimeDir, "config.json"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params["IMAGE_NAME"] = image
|
||||
if runtimeType == constant.RuntimePHP {
|
||||
if extends, ok := params["PHP_EXTENSIONS"]; ok {
|
||||
if extendsArray, ok := extends.([]interface{}); ok {
|
||||
strArray := make([]string, len(extendsArray))
|
||||
for i, v := range extendsArray {
|
||||
strArray[i] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
params["PHP_EXTENSIONS"] = strings.Join(strArray, ",")
|
||||
}
|
||||
}
|
||||
}
|
||||
newMap := make(map[string]string)
|
||||
handleMap(params, newMap)
|
||||
for k, v := range newMap {
|
||||
env[k] = v
|
||||
}
|
||||
envStr, err := gotenv.Marshal(env)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = gotenv.Write(env, path.Join(runtimeDir, ".env")); err != nil {
|
||||
return
|
||||
}
|
||||
envContent = []byte(envStr)
|
||||
return
|
||||
}
|
||||
|
||||
func getComposeService(name, runtimeDir string, composeFile, env []byte, skipNormalization bool) (*docker.ComposeService, error) {
|
||||
project, err := docker.GetComposeProject(name, runtimeDir, composeFile, env, skipNormalization)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logPath := path.Join(runtimeDir, "build.log")
|
||||
fileOp := files.NewFileOp()
|
||||
if fileOp.Stat(logPath) {
|
||||
_ = fileOp.DeleteFile(logPath)
|
||||
}
|
||||
file, err := os.Create(logPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
composeService, err := docker.NewComposeService(command.WithOutputStream(file))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
composeService.SetProject(project)
|
||||
return composeService, nil
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -12,6 +18,8 @@ import (
|
||||
"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/ssl"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -22,6 +30,8 @@ type ISettingService interface {
|
||||
Update(key, value string) error
|
||||
UpdatePassword(c *gin.Context, old, new string) error
|
||||
UpdatePort(port uint) error
|
||||
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
|
||||
LoadFromCert() (*dto.SSLInfo, error)
|
||||
HandlePasswordExpired(c *gin.Context, old, new string) error
|
||||
}
|
||||
|
||||
@@ -70,7 +80,14 @@ func (u *SettingService) UpdatePort(port uint) error {
|
||||
if common.ScanPort(int(port)) {
|
||||
return buserr.WithDetail(constant.ErrPortInUsed, port, nil)
|
||||
}
|
||||
|
||||
serverPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
portValue, _ := strconv.Atoi(serverPort.Value)
|
||||
if err := OperateFirewallPort([]int{portValue}, []int{int(port)}); err != nil {
|
||||
global.LOG.Errorf("set system firewall ports failed, err: %v", err)
|
||||
}
|
||||
if err := settingRepo.Update("ServerPort", strconv.Itoa(int(port))); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -83,6 +100,135 @@ func (u *SettingService) UpdatePort(port uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
||||
secretDir := global.CONF.System.BaseDir + "/1panel/secret/"
|
||||
if req.SSL == "disable" {
|
||||
if err := settingRepo.Update("SSL", "disable"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("SSLType", "self"); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = os.Remove(secretDir + "server.crt")
|
||||
_ = os.Remove(secretDir + "server.key")
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := os.Stat(secretDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(secretDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := settingRepo.Update("SSLType", req.SSLType); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.SSLType == "self" {
|
||||
if len(req.Domain) == 0 {
|
||||
return fmt.Errorf("load domain failed")
|
||||
}
|
||||
if err := ssl.GenerateSSL(req.Domain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLType == "select" {
|
||||
sslInfo, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Cert = sslInfo.Pem
|
||||
req.Key = sslInfo.PrivateKey
|
||||
req.SSLType = "import"
|
||||
if err := settingRepo.Update("SSLID", strconv.Itoa(int(req.SSLID))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLType == "import" {
|
||||
cert, err := os.OpenFile(secretDir+"server.crt.tmp", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cert.Close()
|
||||
if _, err := cert.WriteString(req.Cert); err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := os.OpenFile(secretDir+"server.key.tmp", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := key.WriteString(req.Key); err != nil {
|
||||
return err
|
||||
}
|
||||
defer key.Close()
|
||||
}
|
||||
if err := checkCertValid(req.Domain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.Rename(secretDir+"server.crt.tmp", secretDir+"server.crt"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.Rename(secretDir+"server.key.tmp", secretDir+"server.key"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("SSL", req.SSL); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
|
||||
ssl, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ssl.Value == "disable" {
|
||||
return &dto.SSLInfo{}, nil
|
||||
}
|
||||
sslType, err := settingRepo.Get(settingRepo.WithByKey("SSLType"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := loadInfoFromCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch sslType.Value {
|
||||
case "import":
|
||||
if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret/server.crt"); err != nil {
|
||||
return nil, fmt.Errorf("load server.crt file failed, err: %v", err)
|
||||
}
|
||||
certFile, _ := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt")
|
||||
data.Cert = string(certFile)
|
||||
|
||||
if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret/server.key"); err != nil {
|
||||
return nil, fmt.Errorf("load server.key file failed, err: %v", err)
|
||||
}
|
||||
keyFile, _ := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key")
|
||||
data.Key = string(keyFile)
|
||||
case "select":
|
||||
sslID, err := settingRepo.Get(settingRepo.WithByKey("SSLID"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id, _ := strconv.Atoi(sslID.Value)
|
||||
data.SSLID = uint(id)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error {
|
||||
setting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
@@ -121,3 +267,60 @@ func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
|
||||
_ = global.SESSION.Clean()
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadInfoFromCert() (*dto.SSLInfo, error) {
|
||||
var info dto.SSLInfo
|
||||
certFile := global.CONF.System.BaseDir + "/1panel/secret/server.crt"
|
||||
if _, err := os.Stat(certFile); err != nil {
|
||||
return &info, err
|
||||
}
|
||||
certData, err := os.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return &info, err
|
||||
}
|
||||
certBlock, _ := pem.Decode(certData)
|
||||
if certBlock == nil {
|
||||
return &info, err
|
||||
}
|
||||
certObj, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return &info, err
|
||||
}
|
||||
var domains []string
|
||||
if len(certObj.IPAddresses) != 0 {
|
||||
for _, ip := range certObj.IPAddresses {
|
||||
domains = append(domains, ip.String())
|
||||
}
|
||||
}
|
||||
if len(certObj.DNSNames) != 0 {
|
||||
domains = append(domains, certObj.DNSNames...)
|
||||
}
|
||||
return &dto.SSLInfo{
|
||||
Domain: strings.Join(domains, ","),
|
||||
Timeout: certObj.NotAfter.Format("2006-01-02 15:04:05"),
|
||||
RootPath: global.CONF.System.BaseDir + "/1panel/secret/server.crt",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkCertValid(domain string) error {
|
||||
certificate, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt.tmp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key.tmp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tls.X509KeyPair(certificate, key); err != nil {
|
||||
return err
|
||||
}
|
||||
certBlock, _ := pem.Decode(certificate)
|
||||
if certBlock == nil {
|
||||
return err
|
||||
}
|
||||
if _, err := x509.ParseCertificate(certBlock.Bytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -484,7 +483,7 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
|
||||
|
||||
func (u *SnapshotService) saveJson(snapJson SnapshotJson, path string) error {
|
||||
remarkInfo, _ := json.MarshalIndent(snapJson, "", "\t")
|
||||
if err := ioutil.WriteFile(fmt.Sprintf("%s/snapshot.json", path), remarkInfo, 0640); err != nil {
|
||||
if err := os.WriteFile(fmt.Sprintf("%s/snapshot.json", path), remarkInfo, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -793,7 +792,7 @@ func (u *SnapshotService) updateLiveRestore(enabled bool) error {
|
||||
if _, err := os.Stat(constant.DaemonJsonPath); err != nil {
|
||||
return fmt.Errorf("load docker daemon.json conf failed, err: %v", err)
|
||||
}
|
||||
file, err := ioutil.ReadFile(constant.DaemonJsonPath)
|
||||
file, err := os.ReadFile(constant.DaemonJsonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -809,7 +808,7 @@ func (u *SnapshotService) updateLiveRestore(enabled bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
|
||||
if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -855,7 +854,7 @@ func (u *SnapshotService) handleTar(sourceDir, targetDir, name, exclusionRules s
|
||||
|
||||
commands := fmt.Sprintf("tar --warning=no-file-changed -zcf %s %s -C %s .", targetDir+"/"+name, exStr, sourceDir)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.Exec(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
@@ -872,7 +871,7 @@ func (u *SnapshotService) handleUnTar(sourceDir, targetDir string) error {
|
||||
|
||||
commands := fmt.Sprintf("tar -zxf %s -C %s .", sourceDir, targetDir)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.Exec(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -148,7 +148,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
go writeLogs(req.Version)
|
||||
_ = settingRepo.Update("SystemVersion", req.Version)
|
||||
_ = settingRepo.Update("SystemStatus", "Free")
|
||||
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
|
||||
_, _ = cmd.ExecWithTimeOut("systemctl daemon-reload && systemctl restart 1panel.service", 1*time.Minute)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
@@ -203,7 +203,7 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
|
||||
return "", err
|
||||
}
|
||||
defer latestVersionRes.Body.Close()
|
||||
version, err := ioutil.ReadAll(latestVersionRes.Body)
|
||||
version, err := io.ReadAll(latestVersionRes.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -231,7 +231,7 @@ func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
defer releaseNotes.Body.Close()
|
||||
release, err := ioutil.ReadAll(releaseNotes.Body)
|
||||
release, err := io.ReadAll(releaseNotes.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+896
-76
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,16 @@ import (
|
||||
type WebsiteAcmeAccountService struct {
|
||||
}
|
||||
|
||||
type IWebsiteAcmeAccountService interface {
|
||||
Page(search dto.PageInfo) (int64, []response.WebsiteAcmeAccountDTO, error)
|
||||
Create(create request.WebsiteAcmeAccountCreate) (response.WebsiteAcmeAccountDTO, error)
|
||||
Delete(id uint) error
|
||||
}
|
||||
|
||||
func NewIWebsiteAcmeAccountService() IWebsiteAcmeAccountService {
|
||||
return &WebsiteAcmeAccountService{}
|
||||
}
|
||||
|
||||
func (w WebsiteAcmeAccountService) Page(search dto.PageInfo) (int64, []response.WebsiteAcmeAccountDTO, error) {
|
||||
total, accounts, err := websiteAcmeRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
|
||||
var accountDTOs []response.WebsiteAcmeAccountDTO
|
||||
|
||||
@@ -13,6 +13,17 @@ import (
|
||||
type WebsiteDnsAccountService struct {
|
||||
}
|
||||
|
||||
type IWebsiteDnsAccountService interface {
|
||||
Page(search dto.PageInfo) (int64, []response.WebsiteDnsAccountDTO, error)
|
||||
Create(create request.WebsiteDnsAccountCreate) (request.WebsiteDnsAccountCreate, error)
|
||||
Update(update request.WebsiteDnsAccountUpdate) (request.WebsiteDnsAccountUpdate, error)
|
||||
Delete(id uint) error
|
||||
}
|
||||
|
||||
func NewIWebsiteDnsAccountService() IWebsiteDnsAccountService {
|
||||
return &WebsiteDnsAccountService{}
|
||||
}
|
||||
|
||||
func (w WebsiteDnsAccountService) Page(search dto.PageInfo) (int64, []response.WebsiteDnsAccountDTO, error) {
|
||||
total, accounts, err := websiteDnsRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
|
||||
var accountDTOs []response.WebsiteDnsAccountDTO
|
||||
|
||||
@@ -83,6 +83,8 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
|
||||
return res, err
|
||||
}
|
||||
|
||||
var websiteSSL model.WebsiteSSL
|
||||
|
||||
switch create.Provider {
|
||||
case constant.DNSAccount:
|
||||
dnsAccount, err := websiteDnsRepo.GetFirst(commonRepo.WithByID(create.DnsAccountID))
|
||||
@@ -92,6 +94,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
|
||||
if err := client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization); err != nil {
|
||||
return res, err
|
||||
}
|
||||
websiteSSL.AutoRenew = create.AutoRenew
|
||||
case constant.Http:
|
||||
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
@@ -100,6 +103,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
|
||||
if err := client.UseHTTP(path.Join(constant.AppInstallDir, constant.AppOpenresty, appInstall.Name, "root")); err != nil {
|
||||
return res, err
|
||||
}
|
||||
websiteSSL.AutoRenew = create.AutoRenew
|
||||
case constant.DnsManual:
|
||||
if err := client.UseManualDns(); err != nil {
|
||||
return res, err
|
||||
@@ -115,7 +119,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
var websiteSSL model.WebsiteSSL
|
||||
|
||||
websiteSSL.DnsAccountID = create.DnsAccountID
|
||||
websiteSSL.AcmeAccountID = acmeAccount.ID
|
||||
websiteSSL.Provider = create.Provider
|
||||
@@ -133,7 +137,6 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
|
||||
websiteSSL.StartDate = cert.NotBefore
|
||||
websiteSSL.Type = cert.Issuer.CommonName
|
||||
websiteSSL.Organization = cert.Issuer.Organization[0]
|
||||
websiteSSL.AutoRenew = create.AutoRenew
|
||||
|
||||
if err := websiteSSLRepo.Create(context.TODO(), &websiteSSL); err != nil {
|
||||
return res, err
|
||||
|
||||
@@ -2,6 +2,9 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -20,10 +23,8 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func getDomain(domainStr string, websiteID uint) (model.WebsiteDomain, error) {
|
||||
domain := model.WebsiteDomain{
|
||||
WebsiteID: websiteID,
|
||||
}
|
||||
func getDomain(domainStr string) (model.WebsiteDomain, error) {
|
||||
domain := model.WebsiteDomain{}
|
||||
domainArray := strings.Split(domainStr, ":")
|
||||
if len(domainArray) == 1 {
|
||||
domain.Domain = domainArray[0]
|
||||
@@ -43,15 +44,26 @@ func getDomain(domainStr string, websiteID uint) (model.WebsiteDomain, error) {
|
||||
return model.WebsiteDomain{}, nil
|
||||
}
|
||||
|
||||
func createStaticHtml(website *model.Website) error {
|
||||
func createIndexFile(website *model.Website, runtime *model.Runtime) error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
indexFolder := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", website.Alias, "index")
|
||||
indexPath := path.Join(indexFolder, "index.html")
|
||||
indexContent := string(nginx_conf.Index)
|
||||
indexPath := ""
|
||||
indexContent := ""
|
||||
switch website.Type {
|
||||
case constant.Static:
|
||||
indexPath = path.Join(indexFolder, "index.html")
|
||||
indexContent = string(nginx_conf.Index)
|
||||
case constant.Runtime:
|
||||
if runtime.Type == constant.RuntimePHP {
|
||||
indexPath = path.Join(indexFolder, "index.php")
|
||||
indexContent = string(nginx_conf.IndexPHP)
|
||||
}
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(indexFolder) {
|
||||
if err := fileOp.CreateDir(indexFolder, 0755); err != nil {
|
||||
@@ -63,13 +75,52 @@ func createStaticHtml(website *model.Website) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if website.Type == constant.Runtime && runtime.Resource == constant.ResourceAppstore {
|
||||
if err := chownRootDir(indexFolder); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := fileOp.WriteFile(indexPath, strings.NewReader(indexContent), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website) error {
|
||||
func createProxyFile(website *model.Website, runtime *model.Runtime) error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proxyFolder := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", website.Alias, "proxy")
|
||||
filePath := path.Join(proxyFolder, "root.conf")
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(proxyFolder) {
|
||||
if err := fileOp.CreateDir(proxyFolder, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !fileOp.Stat(filePath) {
|
||||
if err := fileOp.CreateFile(filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
config := parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
|
||||
config.FilePath = filePath
|
||||
directives := config.Directives
|
||||
location, ok := directives[0].(*components.Location)
|
||||
if !ok {
|
||||
return errors.New("error")
|
||||
}
|
||||
location.ChangePath("^~", "/")
|
||||
location.UpdateDirective("proxy_pass", []string{website.Proxy})
|
||||
location.UpdateDirective("proxy_set_header", []string{"Host", "$host"})
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website, runtime *model.Runtime) error {
|
||||
nginxFolder := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name)
|
||||
siteFolder := path.Join(nginxFolder, "www", "sites", website.Alias)
|
||||
fileOp := files.NewFileOp()
|
||||
@@ -86,14 +137,30 @@ func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website)
|
||||
if err := fileOp.CreateFile(path.Join(siteFolder, "log", "error.log")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.CreateDir(path.Join(siteFolder, "index"), 0755); err != nil {
|
||||
if err := fileOp.CreateDir(path.Join(siteFolder, "index"), 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.CreateDir(path.Join(siteFolder, "ssl"), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if website.Type == constant.Static {
|
||||
if err := createStaticHtml(website); err != nil {
|
||||
if website.Type == constant.Runtime {
|
||||
if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceLocal {
|
||||
phpPoolDir := path.Join(siteFolder, "php-pool")
|
||||
if err := fileOp.CreateDir(phpPoolDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.CreateFile(path.Join(phpPoolDir, "php-fpm.sock")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if website.Type == constant.Static || website.Type == constant.Runtime {
|
||||
if err := createIndexFile(website, runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if website.Type == constant.Proxy {
|
||||
if err := createProxyFile(website, runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -101,12 +168,12 @@ func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website)
|
||||
return fileOp.CopyDir(path.Join(nginxFolder, "www", "common", "waf", "rules"), path.Join(siteFolder, "waf"))
|
||||
}
|
||||
|
||||
func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall) error {
|
||||
func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime) error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := createWebsiteFolder(nginxInstall, website); err != nil {
|
||||
if err := createWebsiteFolder(nginxInstall, website, runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -134,22 +201,38 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
server.UpdateDirective("set", []string{"$RulePath", path.Join(siteFolder, "waf", "rules")})
|
||||
server.UpdateDirective("set", []string{"$logdir", path.Join(siteFolder, "log")})
|
||||
|
||||
rootIndex := path.Join("/www/sites", website.Alias, "index")
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", appInstall.HttpPort)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
case constant.Static:
|
||||
server.UpdateRoot(path.Join("/www/sites", website.Alias, "index"))
|
||||
server.UpdateRootLocation()
|
||||
server.UpdateRoot(rootIndex)
|
||||
case constant.Proxy:
|
||||
server.UpdateRootProxy([]string{website.Proxy})
|
||||
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
||||
server.UpdateDirective("include", []string{nginxInclude})
|
||||
case constant.Runtime:
|
||||
if runtime.Resource == constant.ResourceLocal {
|
||||
switch runtime.Type {
|
||||
case constant.RuntimePHP:
|
||||
server.UpdateRoot(rootIndex)
|
||||
localPath := path.Join(nginxInstall.GetPath(), rootIndex, "index.php")
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
|
||||
}
|
||||
}
|
||||
if runtime.Resource == constant.ResourceAppstore {
|
||||
switch runtime.Type {
|
||||
case constant.RuntimePHP:
|
||||
server.UpdateRoot(rootIndex)
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.FilePath = configPath
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {
|
||||
_ = deleteWebsiteFolder(nginxInstall, website)
|
||||
return err
|
||||
@@ -218,7 +301,7 @@ func addListenAndServerName(website model.Website, ports []int, domains []string
|
||||
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName)
|
||||
}
|
||||
|
||||
func deleteListenAndServerName(website model.Website, ports []int, domains []string) error {
|
||||
func deleteListenAndServerName(website model.Website, binds []string, domains []string) error {
|
||||
nginxFull, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -226,8 +309,8 @@ func deleteListenAndServerName(website model.Website, ports []int, domains []str
|
||||
nginxConfig := nginxFull.SiteConfig
|
||||
config := nginxFull.SiteConfig.Config
|
||||
server := config.FindServers()[0]
|
||||
for _, port := range ports {
|
||||
server.DeleteListen(strconv.Itoa(port))
|
||||
for _, bind := range binds {
|
||||
server.DeleteListen(bind)
|
||||
}
|
||||
for _, domain := range domains {
|
||||
server.DeleteServerName(domain)
|
||||
@@ -288,11 +371,12 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
|
||||
}
|
||||
config := nginxFull.SiteConfig.Config
|
||||
server := config.FindServers()[0]
|
||||
server.UpdateListen("443", false, "ssl")
|
||||
server.UpdateListen("443", website.DefaultServer, "ssl")
|
||||
|
||||
switch req.HttpConfig {
|
||||
case constant.HTTPSOnly:
|
||||
server.RemoveListenByBind("80")
|
||||
server.RemoveListenByBind("[::]:80")
|
||||
server.RemoveDirective("if", []string{"($scheme"})
|
||||
case constant.HTTPToHTTPS:
|
||||
server.UpdateListen("80", website.DefaultServer)
|
||||
@@ -410,13 +494,32 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
}
|
||||
server := servers[0]
|
||||
if operate == constant.StopWeb {
|
||||
if website.Type != constant.Static {
|
||||
proxyInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
||||
server.RemoveDirective("include", []string{proxyInclude})
|
||||
rewriteInclude := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.Alias)
|
||||
server.RemoveDirective("include", []string{rewriteInclude})
|
||||
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
server.RemoveDirective("location", []string{"/"})
|
||||
case constant.Runtime:
|
||||
server.RemoveDirective("location", []string{"~", "[^/]\\.php(/|$)"})
|
||||
}
|
||||
server.UpdateRoot("/usr/share/nginx/html/stop")
|
||||
website.Status = constant.WebStopped
|
||||
}
|
||||
if operate == constant.StartWeb {
|
||||
proxyInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
||||
absoluteIncludeDir := path.Join(nginxInstall.Install.GetPath(), fmt.Sprintf("/www/sites/%s/proxy", website.Alias))
|
||||
if files.NewFileOp().Stat(absoluteIncludeDir) {
|
||||
server.UpdateDirective("include", []string{proxyInclude})
|
||||
}
|
||||
server.UpdateDirective("include", []string{proxyInclude})
|
||||
rewriteInclude := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.Alias)
|
||||
absoluteRewritePath := path.Join(nginxInstall.Install.GetPath(), rewriteInclude)
|
||||
if files.NewFileOp().Stat(absoluteRewritePath) {
|
||||
server.UpdateDirective("include", []string{rewriteInclude})
|
||||
}
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
server.RemoveDirective("root", nil)
|
||||
@@ -431,7 +534,14 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
server.UpdateRootLocation()
|
||||
case constant.Proxy:
|
||||
server.RemoveDirective("root", nil)
|
||||
server.UpdateRootProxy([]string{website.Proxy})
|
||||
case constant.Runtime:
|
||||
rootIndex := path.Join("/www/sites", website.Alias, "index")
|
||||
server.UpdateRoot(rootIndex)
|
||||
localPath := ""
|
||||
if website.ProxyType == constant.RuntimeProxyUnix {
|
||||
localPath = path.Join(nginxInstall.Install.GetPath(), rootIndex, "index.php")
|
||||
}
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
|
||||
}
|
||||
website.Status = constant.WebRunning
|
||||
now := time.Now()
|
||||
@@ -446,3 +556,22 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
}
|
||||
return nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName)
|
||||
}
|
||||
|
||||
func checkIsLinkApp(website model.Website) bool {
|
||||
if website.Type == constant.Deployment {
|
||||
return true
|
||||
}
|
||||
if website.Type == constant.Runtime {
|
||||
runtime, _ := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
|
||||
return runtime.Resource == constant.ResourceAppstore
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func chownRootDir(path string) error {
|
||||
_, err := cmd.ExecWithTimeOut(fmt.Sprintf("chown -R 1000:1000 %s", path), 1*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+19
-14
@@ -1,18 +1,23 @@
|
||||
package configs
|
||||
|
||||
type System struct {
|
||||
Port string `mapstructure:"port"`
|
||||
DbFile string `mapstructure:"db_file"`
|
||||
DbPath string `mapstructure:"db_path"`
|
||||
LogPath string `mapstructure:"log_path"`
|
||||
DataDir string `mapstructure:"data_dir"`
|
||||
TmpDir string `mapstructure:"tmp_dir"`
|
||||
Cache string `mapstructure:"cache"`
|
||||
Backup string `mapstructure:"backup"`
|
||||
EncryptKey string `mapstructure:"encrypt_key"`
|
||||
BaseDir string `mapstructure:"base_dir"`
|
||||
Mode string `mapstructure:"mode"`
|
||||
RepoUrl string `mapstructure:"repo_url"`
|
||||
Version string `mapstructure:"version"`
|
||||
IsDemo bool `mapstructure:"is_demo"`
|
||||
Port string `mapstructure:"port"`
|
||||
SSL string `mapstructure:"ssl"`
|
||||
DbFile string `mapstructure:"db_file"`
|
||||
DbPath string `mapstructure:"db_path"`
|
||||
LogPath string `mapstructure:"log_path"`
|
||||
DataDir string `mapstructure:"data_dir"`
|
||||
TmpDir string `mapstructure:"tmp_dir"`
|
||||
Cache string `mapstructure:"cache"`
|
||||
Backup string `mapstructure:"backup"`
|
||||
EncryptKey string `mapstructure:"encrypt_key"`
|
||||
BaseDir string `mapstructure:"base_dir"`
|
||||
Mode string `mapstructure:"mode"`
|
||||
RepoUrl string `mapstructure:"repo_url"`
|
||||
Version string `mapstructure:"version"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
Entrance string `mapstructure:"entrance"`
|
||||
IsDemo bool `mapstructure:"is_demo"`
|
||||
ChangeUserInfo bool `mapstructure:"change_user_info"`
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ const (
|
||||
AppOpenresty = "openresty"
|
||||
AppMysql = "mysql"
|
||||
AppRedis = "redis"
|
||||
|
||||
AppResourceLocal = "local"
|
||||
AppResourceRemote = "remote"
|
||||
)
|
||||
|
||||
type AppOperate string
|
||||
|
||||
@@ -8,4 +8,6 @@ const (
|
||||
OSS = "OSS"
|
||||
Sftp = "SFTP"
|
||||
MinIo = "MINIO"
|
||||
Cos = "COS"
|
||||
Kodo = "KODO"
|
||||
)
|
||||
|
||||
@@ -7,8 +7,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
DataDir = global.CONF.System.DataDir
|
||||
ResourceDir = path.Join(DataDir, "resource")
|
||||
AppResourceDir = path.Join(ResourceDir, "apps")
|
||||
AppInstallDir = path.Join(DataDir, "apps")
|
||||
DataDir = global.CONF.System.DataDir
|
||||
ResourceDir = path.Join(DataDir, "resource")
|
||||
AppResourceDir = path.Join(ResourceDir, "apps")
|
||||
AppInstallDir = path.Join(DataDir, "apps")
|
||||
LocalAppResourceDir = path.Join(ResourceDir, "localApps")
|
||||
LocalAppInstallDir = path.Join(DataDir, "localApps")
|
||||
RuntimeDir = path.Join(DataDir, "runtime")
|
||||
)
|
||||
|
||||
+29
-10
@@ -46,6 +46,7 @@ var (
|
||||
ErrTypeNotSafety = "ErrNotSafety"
|
||||
ErrNameIsExist = "ErrNameIsExist"
|
||||
ErrDemoEnvironment = "ErrDemoEnvironment"
|
||||
ErrInitUser = "ErrInitUser"
|
||||
)
|
||||
|
||||
// app
|
||||
@@ -60,17 +61,20 @@ var (
|
||||
ErrDbUserNotValid = "ErrDbUserNotValid"
|
||||
ErrUpdateBuWebsite = "ErrUpdateBuWebsite"
|
||||
Err1PanelNetworkFailed = "Err1PanelNetworkFailed"
|
||||
ErrCmdTimeout = "ErrCmdTimeout"
|
||||
)
|
||||
|
||||
//website
|
||||
// website
|
||||
var (
|
||||
ErrDomainIsExist = "ErrDomainIsExist"
|
||||
ErrAliasIsExist = "ErrAliasIsExist"
|
||||
ErrAppDelete = "ErrAppDelete"
|
||||
ErrGroupIsUsed = "ErrGroupIsUsed"
|
||||
ErrDomainIsExist = "ErrDomainIsExist"
|
||||
ErrAliasIsExist = "ErrAliasIsExist"
|
||||
ErrAppDelete = "ErrAppDelete"
|
||||
ErrGroupIsUsed = "ErrGroupIsUsed"
|
||||
ErrUsernameIsExist = "ErrUsernameIsExist"
|
||||
ErrUsernameIsNotExist = "ErrUsernameIsNotExist"
|
||||
)
|
||||
|
||||
//ssl
|
||||
// ssl
|
||||
var (
|
||||
ErrSSLCannotDelete = "ErrSSLCannotDelete"
|
||||
ErrAccountCannotDelete = "ErrAccountCannotDelete"
|
||||
@@ -78,7 +82,7 @@ var (
|
||||
ErrEmailIsExist = "ErrEmailIsExist"
|
||||
)
|
||||
|
||||
//file
|
||||
// file
|
||||
var (
|
||||
ErrPathNotFound = "ErrPathNotFound"
|
||||
ErrMovePathFailed = "ErrMovePathFailed"
|
||||
@@ -87,19 +91,34 @@ var (
|
||||
ErrFileUpload = "ErrFileUpload"
|
||||
)
|
||||
|
||||
//mysql
|
||||
// mysql
|
||||
var (
|
||||
ErrUserIsExist = "ErrUserIsExist"
|
||||
ErrDatabaseIsExist = "ErrDatabaseIsExist"
|
||||
ErrExecTimeOut = "ErrExecTimeOut"
|
||||
)
|
||||
|
||||
//redis
|
||||
// redis
|
||||
var (
|
||||
ErrTypeOfRedis = "ErrTypeOfRedis"
|
||||
)
|
||||
|
||||
//container
|
||||
// container
|
||||
var (
|
||||
ErrInUsed = "ErrInUsed"
|
||||
ErrObjectInUsed = "ErrObjectInUsed"
|
||||
ErrPortRules = "ErrPortRules"
|
||||
)
|
||||
|
||||
// runtime
|
||||
var (
|
||||
ErrDirNotFound = "ErrDirNotFound"
|
||||
ErrFileNotExist = "ErrFileNotExist"
|
||||
ErrImageBuildErr = "ErrImageBuildErr"
|
||||
ErrImageExist = "ErrImageExist"
|
||||
ErrDelWithWebsite = "ErrDelWithWebsite"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBackupInUsed = "ErrBackupInUsed"
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user