mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 08:00:27 +00:00
Compare commits
168
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
884a169a6f | ||
|
|
e79a5f9ae2 | ||
|
|
25608e7378 | ||
|
|
dad8bac715 | ||
|
|
a4ee16cf1f | ||
|
|
7f7465b383 | ||
|
|
b7c3dc0d2a | ||
|
|
bb02991b05 | ||
|
|
a5fdff61ff | ||
|
|
4832081b82 | ||
|
|
ef5a13f87f | ||
|
|
e2bc8e800a | ||
|
|
85a397949e | ||
|
|
5cfdcedf9a | ||
|
|
c5bb8dab8c | ||
|
|
f1421ba7f9 | ||
|
|
9e44db53a7 | ||
|
|
4814a76c47 | ||
|
|
2ec4b2561c | ||
|
|
4efe7240ad | ||
|
|
ed13136221 | ||
|
|
735fc75e35 | ||
|
|
b8a52ae707 | ||
|
|
2501a9f78a | ||
|
|
5a8d6db43e | ||
|
|
a3312331d2 | ||
|
|
fbd0e836d7 | ||
|
|
4935d88301 | ||
|
|
8625979cc3 | ||
|
|
c77742e72e | ||
|
|
43a6b4b735 | ||
|
|
f4ee534101 | ||
|
|
c055a07e57 | ||
|
|
62bead5c50 | ||
|
|
9edafc13a3 | ||
|
|
e6ccf8be1f | ||
|
|
222089d56a | ||
|
|
96e48847d0 | ||
|
|
38b0c42555 | ||
|
|
2eae8274f6 | ||
|
|
0de79e537e | ||
|
|
e37b87d63e | ||
|
|
37a0329d4a | ||
|
|
e037b69f52 | ||
|
|
34498392dc | ||
|
|
1687b99526 | ||
|
|
0bf6db1cf8 | ||
|
|
0cd06803e2 | ||
|
|
45f6a415de | ||
|
|
3e3f0d9452 | ||
|
|
35785f1e40 | ||
|
|
437d6eb0fd | ||
|
|
1e2c85e771 | ||
|
|
ef075df3fe | ||
|
|
1e22769c8b | ||
|
|
0751326b46 | ||
|
|
db4973cfde | ||
|
|
0b47d692b8 | ||
|
|
672ef66be8 | ||
|
|
7ab005540b | ||
|
|
7c928735cd | ||
|
|
9ec1610f34 | ||
|
|
4f2a340212 | ||
|
|
e69fd3b388 | ||
|
|
6991c773da | ||
|
|
9b88979b69 | ||
|
|
7c9608727d | ||
|
|
62828c464e | ||
|
|
f1ed051135 | ||
|
|
33a0c2ba30 | ||
|
|
494da3c209 | ||
|
|
646fc4e678 | ||
|
|
cdbe24a523 | ||
|
|
10012c6148 | ||
|
|
bb4fd69f3a | ||
|
|
6ff0f14841 | ||
|
|
1088034a5c | ||
|
|
8a011b1cf9 | ||
|
|
e3eebc938a | ||
|
|
08da7802c6 | ||
|
|
79ca1b4d6b | ||
|
|
47119e508b | ||
|
|
13bb5e0d30 | ||
|
|
218c165c26 | ||
|
|
d6006d34c0 | ||
|
|
810ea03ac1 | ||
|
|
e046315616 | ||
|
|
0a7f1eb286 | ||
|
|
b0411b2df6 | ||
|
|
471b7d9657 | ||
|
|
1527c63d37 | ||
|
|
0ebeeaebd6 | ||
|
|
1ae5bef40d | ||
|
|
9b9fe5444d | ||
|
|
a5d853c61a | ||
|
|
6610a3debe | ||
|
|
6b244b2b03 | ||
|
|
0b26d70927 | ||
|
|
18faed79f3 | ||
|
|
b0cc1248f0 | ||
|
|
8013c7fd96 | ||
|
|
306082d625 | ||
|
|
821b8dfe63 | ||
|
|
8d88ed812c | ||
|
|
aa93a6d373 | ||
|
|
b4e8c8503c | ||
|
|
4de733e084 | ||
|
|
6c67dc6ed4 | ||
|
|
ba6991bfb6 | ||
|
|
33309d2d93 | ||
|
|
1b2208a4d0 | ||
|
|
e3fb97fc8c | ||
|
|
1ff5bf85e9 | ||
|
|
062b2f2ae6 | ||
|
|
44445aca93 | ||
|
|
94de830637 | ||
|
|
9160726127 | ||
|
|
3cd0c89c4c | ||
|
|
c27fcf18fc | ||
|
|
b1f20d125e | ||
|
|
cd43bff44d | ||
|
|
8cf8375911 | ||
|
|
ebc3195df4 | ||
|
|
ce69f6a142 | ||
|
|
f6777d51b5 | ||
|
|
d916f2ba6f | ||
|
|
0e159f944a | ||
|
|
329bb94d3c | ||
|
|
7f2f7fa195 | ||
|
|
67c79bcde4 | ||
|
|
d7d7c7fd73 | ||
|
|
94028d97cf | ||
|
|
cbfedb5d5e | ||
|
|
7784ee9575 | ||
|
|
29d5d82fa5 | ||
|
|
46f01032b8 | ||
|
|
341f45b4ef | ||
|
|
a0907592b2 | ||
|
|
825ebd19fb | ||
|
|
8f24432a01 | ||
|
|
6ce35b723b | ||
|
|
a33eabb4f0 | ||
|
|
34a6587ebc | ||
|
|
17020ffe9f | ||
|
|
0f9b0d5d82 | ||
|
|
be36103475 | ||
|
|
a3b5869c2b | ||
|
|
0807da41a5 | ||
|
|
ea6870e3c9 | ||
|
|
70b7daf23a | ||
|
|
1d01fa1e14 | ||
|
|
b643fa2ca3 | ||
|
|
77f4f64f0d | ||
|
|
3b697c7520 | ||
|
|
8ca6b9b6fa | ||
|
|
0b66be7a3a | ||
|
|
f9f68a975c | ||
|
|
ba63907a65 | ||
|
|
2687c91e65 | ||
|
|
eb82c168c5 | ||
|
|
21b1242a94 | ||
|
|
a6784772b9 | ||
|
|
cf4fcaa07f | ||
|
|
62b1bb3191 | ||
|
|
8ce066a9d4 | ||
|
|
7d8da406cf | ||
|
|
b3d6b2b0de | ||
|
|
21f24f2e10 |
+5
-1
@@ -1 +1,5 @@
|
||||
*.ts linguist-language=Go
|
||||
*.ts linguist-language=Go
|
||||
*.js linguist-language=Go
|
||||
*.css linguist-language=Go
|
||||
*.scss linguist-language=Go
|
||||
*.html linguist-language=Go
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 问题与讨论 / Questions & Discussions
|
||||
url: https://github.com/1Panel-dev/1Panel/discussions
|
||||
about: 提出针对本项目安装部署、使用及其他方面的相关问题 / Raise questions about the installation, deployment, use and other aspects of the project.
|
||||
- name: 对 1Panel 项目有其他问题
|
||||
url: https://1panel.cn
|
||||
about: 如果想要进一步了解 1Panel 项目,欢迎发送邮件到 support@fit2cloud.com 进行提问。
|
||||
- name: 反馈安全问题 (Report Security Bug)
|
||||
url: mailto://support@fit2cloud.com
|
||||
about: 请通过邮箱 support@fit2cloud.com 反馈安全问题 (Please report security vulnerabilities to support@fit2cloud.com)
|
||||
url: https://bbs.fit2cloud.com/c/1p/7
|
||||
about: 如果你对 1Panel 有其他想要提问的,我们欢迎到我们的官方社区进行提问。
|
||||
@@ -1,12 +0,0 @@
|
||||
name: 问题咨询
|
||||
description: 提出针对本项目安装部署、使用及其他方面的相关问题
|
||||
title: "[QUESTION]"
|
||||
labels: question
|
||||
assignees: wanghe-fit2cloud
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: "请描述您的问题"
|
||||
validations:
|
||||
required: true
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
name: 为 PR 添加标签
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUBTOKEN }}
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
uses: Yikun/hub-mirror-action@master
|
||||
with:
|
||||
src: 'github/1Panel-dev'
|
||||
dst: 'gitee/fit2cloud-xlab'
|
||||
dst: 'gitee/fit2cloud-feizhiyun'
|
||||
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
dst_token: ${{ secrets.GITEE_TOKEN }}
|
||||
static_list: "1Panel"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
name: Typos Check
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@master
|
||||
+4
-6
@@ -16,7 +16,8 @@ build/1panel
|
||||
*.factorypath
|
||||
|
||||
# IntelliJ IDEA
|
||||
.idea
|
||||
.idea/*
|
||||
!.idea/icon.png
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
@@ -41,11 +42,7 @@ frontend/src/xpack
|
||||
backend/xpack
|
||||
backend/router/entry_xpack.go
|
||||
backend/server/init_xpack.go
|
||||
plugins/openresty/waf/db
|
||||
plugins/openresty/waf/data
|
||||
plugins/openresty/waf/sites
|
||||
plugins/openresty/waf/conf/websites.json
|
||||
|
||||
backend/utils/xpack/xpack_xpack.go
|
||||
|
||||
.history/
|
||||
dist/
|
||||
@@ -55,3 +52,4 @@ install.sh
|
||||
quick_start.sh
|
||||
cmd/server/fileList.txt
|
||||
.fileList.txt
|
||||
1Panel.code-workspace
|
||||
|
||||
@@ -13,6 +13,7 @@ builds:
|
||||
- main: ./cmd/server/main.go
|
||||
binary: 1panel
|
||||
flags:
|
||||
- -tags=xpack
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -w -s
|
||||
|
||||
Generated
BIN
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
+2
-2
@@ -5,7 +5,7 @@ PR are always welcome, even if they only contain small fixes like typos or a few
|
||||
|
||||
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
|
||||
|
||||
This [development guideline](https://kubeoperator.io/docs/dev/dev_manual/) contains information about repository structure, how to setup development environment, how to run it, and more.
|
||||
This [development guideline](https://1panel.cn/docs/dev_manual/dev_manual/) contains information about repository structure, how to setup development environment, how to run it, and more.
|
||||
|
||||
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
|
||||
|
||||
@@ -22,4 +22,4 @@ When reporting issues, always include:
|
||||
* Snapshots or log files if needed
|
||||
|
||||
Because the issues are open to the public, when submitting files, be sure to remove any sensitive information, e.g. user name, password, IP address, and company name. You can
|
||||
replace those parts with "REDACTED" or other strings like "****".
|
||||
replace those parts with "REDACTED" or other strings like "****".
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
- **安全可靠**:基于容器管理并部署应用,实现最小的漏洞暴露面,同时提供防火墙和日志审计等功能;
|
||||
- **一键备份**:支持一键备份和恢复,用户可以将数据备份到各类云端存储介质,永不丢失。
|
||||
|
||||
1Panel 三分钟速览:https://www.bilibili.com/video/BV1Mt421n7LZ/
|
||||
|
||||
## UI 展示
|
||||
|
||||

|
||||
@@ -50,13 +52,19 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
|
||||
|
||||
如果您在使用过程中发现任何安全问题,请通过以下方式直接联系我们:
|
||||
|
||||
- 邮箱:support@fit2cloud.com
|
||||
- 电话:400-052-0755
|
||||
- 邮箱:wanghe@fit2cloud.com
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#1Panel-dev/1Panel&Date)
|
||||
|
||||
## 我们的其他明星项目
|
||||
- [JumpServer](https://github.com/jumpserver/jumpserver/) - 广受欢迎的开源堡垒机
|
||||
- [DataEase](https://github.com/dataease/dataease/) - 人人可用的开源数据可视化分析工具
|
||||
- [MeterSphere](https://github.com/metersphere/metersphere/) - 一站式开源自动化测试平台
|
||||
- [Halo](https://github.com/halo-dev/halo/) - 强大易用的开源建站工具
|
||||
|
||||
|
||||
## FOSSA Status
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_large)
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
如果您发现安全问题,请直接联系我们:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- zhengkun@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
|
||||
感谢您的支持!
|
||||
|
||||
@@ -14,8 +11,5 @@
|
||||
All security bugs should be reported to the contact as below:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- zhengkun@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- +86 400-052-0755
|
||||
|
||||
Thanks for your support!
|
||||
|
||||
@@ -137,7 +137,7 @@ func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
|
||||
// @Accept json
|
||||
// @Success 200 {object} response.IgnoredApp
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/ingored [get]
|
||||
// @Router /apps/ignored [get]
|
||||
func (b *BaseApi) GetIgnoredApp(c *gin.Context) {
|
||||
res, err := appService.GetIgnoredApp()
|
||||
if err != nil {
|
||||
|
||||
@@ -121,6 +121,15 @@ func (b *BaseApi) CheckIsSafety(c *gin.Context) {
|
||||
helper.SuccessWithData(c, status)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetResponsePage(c *gin.Context) {
|
||||
pageCode, err := authService.GetResponsePage()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, pageCode)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Check System isDemo
|
||||
// @Description 判断是否为demo环境
|
||||
|
||||
@@ -301,28 +301,83 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
files := form.File["file"]
|
||||
uploadFiles := form.File["file"]
|
||||
paths := form.Value["path"]
|
||||
|
||||
overwrite := true
|
||||
if ow, ok := form.Value["overwrite"]; ok {
|
||||
if len(ow) != 0 {
|
||||
parseBool, _ := strconv.ParseBool(ow[0])
|
||||
overwrite = parseBool
|
||||
}
|
||||
}
|
||||
|
||||
if len(paths) == 0 || !strings.Contains(paths[0], "/") {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error paths in request"))
|
||||
return
|
||||
}
|
||||
dir := path.Dir(paths[0])
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
|
||||
info, err := os.Stat(dir)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
mode, err := files.GetParentMode(dir)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err = os.MkdirAll(dir, mode); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("mkdir %s failed, err: %v", dir, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
info, err = os.Stat(dir)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
mode := info.Mode()
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
|
||||
success := 0
|
||||
failures := make(buserr.MultiErr)
|
||||
for _, file := range files {
|
||||
if err := c.SaveUploadedFile(file, path.Join(paths[0], file.Filename)); err != nil {
|
||||
for _, file := range uploadFiles {
|
||||
dstFilename := path.Join(paths[0], file.Filename)
|
||||
dstDir := path.Dir(dstFilename)
|
||||
if !fileOp.Stat(dstDir) {
|
||||
if err = fileOp.CreateDir(dstDir, mode); err != nil {
|
||||
e := fmt.Errorf("create dir [%s] failed, err: %v", path.Dir(dstFilename), err)
|
||||
failures[file.Filename] = e
|
||||
global.LOG.Error(e)
|
||||
continue
|
||||
}
|
||||
}
|
||||
tmpFilename := dstFilename + ".tmp"
|
||||
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
|
||||
_ = os.Remove(tmpFilename)
|
||||
e := fmt.Errorf("upload [%s] file failed, err: %v", file.Filename, err)
|
||||
failures[file.Filename] = e
|
||||
global.LOG.Error(e)
|
||||
continue
|
||||
}
|
||||
dstInfo, statErr := os.Stat(dstFilename)
|
||||
if overwrite {
|
||||
_ = os.Remove(dstFilename)
|
||||
}
|
||||
|
||||
err = os.Rename(tmpFilename, dstFilename)
|
||||
if err != nil {
|
||||
_ = os.Remove(tmpFilename)
|
||||
e := fmt.Errorf("upload [%s] file failed, err: %v", file.Filename, err)
|
||||
failures[file.Filename] = e
|
||||
global.LOG.Error(e)
|
||||
continue
|
||||
}
|
||||
if statErr == nil {
|
||||
_ = os.Chmod(dstFilename, dstInfo.Mode())
|
||||
} else {
|
||||
_ = os.Chmod(dstFilename, mode)
|
||||
}
|
||||
success++
|
||||
}
|
||||
if success == 0 {
|
||||
@@ -539,16 +594,20 @@ func (b *BaseApi) Size(c *gin.Context) {
|
||||
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
|
||||
op := files.NewFileOp()
|
||||
dstDir = strings.TrimSpace(dstDir)
|
||||
mode, _ := files.GetParentMode(dstDir)
|
||||
if mode == 0 {
|
||||
mode = 0755
|
||||
}
|
||||
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
|
||||
if err = op.CreateDir(dstDir, os.ModePerm); err != nil {
|
||||
if err = op.CreateDir(dstDir, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
targetFile, err := os.Create(filepath.Join(dstDir, fileName))
|
||||
|
||||
targetFile, err := os.OpenFile(filepath.Join(dstDir, fileName), os.O_RDWR|os.O_CREATE, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer targetFile.Close()
|
||||
|
||||
for i := 0; i < chunkCount; i++ {
|
||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", fileName, i))
|
||||
|
||||
@@ -15,6 +15,13 @@ import (
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
)
|
||||
|
||||
// @Tags Monitor
|
||||
// @Summary Load monitor datas
|
||||
// @Description 获取监控数据
|
||||
// @Param request body dto.MonitorSearch true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/monitor/search [post]
|
||||
func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
var req dto.MonitorSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -28,7 +35,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
var backdatas []dto.MonitorData
|
||||
if req.Param == "all" || req.Param == "cpu" || req.Param == "memory" || req.Param == "load" {
|
||||
var bases []model.MonitorBase
|
||||
if err := global.DB.
|
||||
if err := global.MonitorDB.
|
||||
Where("created_at > ? AND created_at < ?", req.StartTime, req.EndTime).
|
||||
Find(&bases).Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -45,7 +52,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
}
|
||||
if req.Param == "all" || req.Param == "io" {
|
||||
var bases []model.MonitorIO
|
||||
if err := global.DB.
|
||||
if err := global.MonitorDB.
|
||||
Where("created_at > ? AND created_at < ?", req.StartTime, req.EndTime).
|
||||
Find(&bases).Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -62,7 +69,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
}
|
||||
if req.Param == "all" || req.Param == "network" {
|
||||
var bases []model.MonitorNetwork
|
||||
if err := global.DB.
|
||||
if err := global.MonitorDB.
|
||||
Where("name = ? AND created_at > ? AND created_at < ?", req.Info, req.StartTime, req.EndTime).
|
||||
Find(&bases).Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
@@ -80,6 +87,30 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
helper.SuccessWithData(c, backdatas)
|
||||
}
|
||||
|
||||
// @Tags Monitor
|
||||
// @Summary Clean monitor datas
|
||||
// @Description 清空监控数据
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/monitor/clean [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空监控数据","formatEN":"clean monitor datas"}
|
||||
func (b *BaseApi) CleanMonitor(c *gin.Context) {
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_bases").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_ios").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_networks").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
|
||||
netStat, _ := net.IOCounters(true)
|
||||
var options []string
|
||||
|
||||
@@ -203,3 +203,19 @@ func (b *BaseApi) OperateNodeModules(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Runtime
|
||||
// @Summary Sync runtime status
|
||||
// @Description 同步运行环境状态
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /runtimes/sync [post]
|
||||
func (b *BaseApi) SyncStatus(c *gin.Context) {
|
||||
err := runtimeService.SyncRuntimeStatus()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
@@ -60,6 +60,28 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system setting
|
||||
// @Description 隐藏高级功能菜单
|
||||
// @Accept json
|
||||
// @Param request body dto.SettingUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/menu/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"隐藏高级功能菜单","formatEN":"Hide advanced feature menu."}
|
||||
func (b *BaseApi) UpdateMenu(c *gin.Context) {
|
||||
var req dto.SettingUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.Update(req.Key, req.Value); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system password
|
||||
// @Description 更新系统登录密码
|
||||
@@ -227,30 +249,6 @@ func (b *BaseApi) LoadBaseDir(c *gin.Context) {
|
||||
helper.SuccessWithData(c, global.CONF.System.DataDir)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Clean monitor datas
|
||||
// @Description 清空监控数据
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/monitor/clean [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空监控数据","formatEN":"clean monitor datas"}
|
||||
func (b *BaseApi) CleanMonitor(c *gin.Context) {
|
||||
if err := global.DB.Exec("DELETE FROM monitor_bases").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err := global.DB.Exec("DELETE FROM monitor_ios").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if err := global.DB.Exec("DELETE FROM monitor_networks").Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load mfa info
|
||||
// @Description 获取 mfa 信息
|
||||
|
||||
@@ -102,7 +102,7 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(redisConf.ContainerName, commands, pidMap)
|
||||
defer killBash(redisConf.ContainerName, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||
@@ -170,7 +170,7 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, command, pidMap)
|
||||
defer killBash(containerID, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true)
|
||||
@@ -221,26 +221,26 @@ func loadMapFromDockerTop(containerID string) map[string]string {
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) != 2 {
|
||||
if len(parts) == 0 {
|
||||
continue
|
||||
}
|
||||
pidMap[parts[0]] = parts[1]
|
||||
pidMap[parts[0]] = strings.Join(parts, " ")
|
||||
}
|
||||
return pidMap
|
||||
}
|
||||
|
||||
func killBash(containerID, comm string, pidMap map[string]string) {
|
||||
func killBash(containerID string, pidMap map[string]string) {
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
newPidMap := loadMapFromDockerTop(containerID)
|
||||
for pid, command := range newPidMap {
|
||||
for pid, newCmd := range newPidMap {
|
||||
isOld := false
|
||||
for pid2 := range pidMap {
|
||||
if pid == pid2 {
|
||||
for pid2, oldCmd := range pidMap {
|
||||
if pid == pid2 && oldCmd == newCmd {
|
||||
isOld = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isOld && command == comm {
|
||||
if !isOld {
|
||||
_, _ = cmd.Execf("%s kill -9 %s", sudo, pid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,69 +305,6 @@ func (b *BaseApi) CreateWebsiteCheck(c *gin.Context) {
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Website WAF
|
||||
// @Summary Load websit waf conf
|
||||
// @Description 获取网站 waf 配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteWafReq true "request"
|
||||
// @Success 200 {object} response.WebsiteWafConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/waf/config [post]
|
||||
func (b *BaseApi) GetWebsiteWafConfig(c *gin.Context) {
|
||||
var req request.WebsiteWafReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := websiteService.GetWafConfig(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Website WAF
|
||||
// @Summary Update website waf conf
|
||||
// @Description 更新 网站 waf 配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteWafUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/waf/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置修改 [domain]","formatEN":"WAF conf update [domain]"}
|
||||
func (b *BaseApi) UpdateWebsiteWafConfig(c *gin.Context) {
|
||||
var req request.WebsiteWafUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateWafConfig(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website WAF
|
||||
// @Summary Update website waf file
|
||||
// @Description 更新 网站 waf 配置文件
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteWafUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/waf/file/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置文件修改 [domain]","formatEN":"WAF conf file update [domain]"}
|
||||
func (b *BaseApi) UpdateWebsiteWafFile(c *gin.Context) {
|
||||
var req request.WebsiteWafFileUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateWafFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website Nginx
|
||||
// @Summary Update website nginx conf
|
||||
// @Description 更新 网站 nginx 配置
|
||||
@@ -433,7 +370,7 @@ func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Load websit php conf
|
||||
// @Summary Load website php conf
|
||||
// @Description 获取网站 php 配置
|
||||
// @Accept json
|
||||
// @Param id path integer true "request"
|
||||
|
||||
@@ -48,6 +48,8 @@ type ContainerOperate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Image string `json:"image" validate:"required"`
|
||||
Network string `json:"network"`
|
||||
Ipv4 string `json:"ipv4"`
|
||||
Ipv6 string `json:"ipv6"`
|
||||
PublishAllPorts bool `json:"publishAllPorts"`
|
||||
ExposedPorts []PortHelper `json:"exposedPorts"`
|
||||
Tty bool `json:"tty"`
|
||||
@@ -98,6 +100,7 @@ type ContainerStats struct {
|
||||
}
|
||||
|
||||
type VolumeHelper struct {
|
||||
Type string `json:"type"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
ContainerDir string `json:"containerDir"`
|
||||
Mode string `json:"mode"`
|
||||
|
||||
@@ -8,6 +8,7 @@ type CronjobCreate struct {
|
||||
Spec string `json:"spec" validate:"required"`
|
||||
|
||||
Script string `json:"script"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
@@ -28,6 +29,7 @@ type CronjobUpdate struct {
|
||||
Spec string `json:"spec" validate:"required"`
|
||||
|
||||
Script string `json:"script"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
@@ -70,6 +72,7 @@ type CronjobInfo struct {
|
||||
Spec string `json:"spec"`
|
||||
|
||||
Script string `json:"script"`
|
||||
Command string `json:"command"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
|
||||
@@ -69,6 +69,8 @@ type DashboardCurrent struct {
|
||||
NetBytesSent uint64 `json:"netBytesSent"`
|
||||
NetBytesRecv uint64 `json:"netBytesRecv"`
|
||||
|
||||
GPUData []GPUInfo `json:"gpuData"`
|
||||
|
||||
ShotTime time.Time `json:"shotTime"`
|
||||
}
|
||||
|
||||
@@ -86,3 +88,18 @@ type DiskInfo struct {
|
||||
InodesFree uint64 `json:"inodesFree"`
|
||||
InodesUsedPercent float64 `json:"inodesUsedPercent"`
|
||||
}
|
||||
|
||||
type GPUInfo struct {
|
||||
Index uint `json:"index"`
|
||||
ProductName string `json:"productName"`
|
||||
GPUUtil string `json:"gpuUtil"`
|
||||
Temperature string `json:"temperature"`
|
||||
PerformanceState string `json:"performanceState"`
|
||||
PowerUsage string `json:"powerUsage"`
|
||||
PowerDraw string `json:"powerDraw"`
|
||||
MaxPowerLimit string `json:"maxPowerLimit"`
|
||||
MemoryUsage string `json:"memoryUsage"`
|
||||
MemUsed string `json:"memUsed"`
|
||||
MemTotal string `json:"memTotal"`
|
||||
FanSpeed string `json:"fanSpeed"`
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ type MysqlDBInfo struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Permission string `json:"permission"`
|
||||
BackupCount int `json:"backupCount"`
|
||||
IsDelete bool `json:"isDelete"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ type MysqlStatus struct {
|
||||
}
|
||||
|
||||
type MysqlVariables struct {
|
||||
BinlogCachSize string `json:"binlog_cache_size"`
|
||||
BinlogCacheSize string `json:"binlog_cache_size"`
|
||||
InnodbBufferPoolSize string `json:"innodb_buffer_pool_size"`
|
||||
InnodbLogBufferSize string `json:"innodb_log_buffer_size"`
|
||||
JoinBufferSize string `json:"join_buffer_size"`
|
||||
|
||||
@@ -20,7 +20,7 @@ type PostgresqlDBInfo struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
SuperUser bool `json:"superUser"`
|
||||
BackupCount int `json:"backupCount"`
|
||||
IsDelete bool `json:"isDelete"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ type FileCreate struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Content string `json:"content"`
|
||||
IsDir bool `json:"isDir"`
|
||||
Mode int64 `json:"mode" validate:"required"`
|
||||
Mode int64 `json:"mode"`
|
||||
IsLink bool `json:"isLink"`
|
||||
IsSymlink bool `json:"isSymlink"`
|
||||
LinkPath string `json:"linkPath"`
|
||||
|
||||
@@ -69,24 +69,12 @@ type WebsiteOp struct {
|
||||
Operate string `json:"operate"`
|
||||
}
|
||||
|
||||
type WebsiteWafReq struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Key string `json:"key" validate:"required"`
|
||||
Rule string `json:"rule" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteRedirectUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Key string `json:"key" validate:"required"`
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
type WebsiteWafUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Key string `json:"key" validate:"required"`
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
type WebsiteRecover struct {
|
||||
WebsiteName string `json:"websiteName" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
@@ -207,12 +195,12 @@ type WebsiteRedirectReq struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteWafFileUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=cc ip_white ip_block url_white url_block cookie_block args_check post_check ua_check file_ext_block user_agent"`
|
||||
}
|
||||
|
||||
type WebsiteCommonReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type WafWebsite struct {
|
||||
Key string `json:"key"`
|
||||
Domains []string `json:"domains"`
|
||||
Host []string `json:"host"`
|
||||
}
|
||||
|
||||
@@ -31,11 +31,6 @@ type WebsiteNginxConfig struct {
|
||||
Params []NginxParam `json:"params"`
|
||||
}
|
||||
|
||||
type WebsiteWafConfig struct {
|
||||
Enable bool `json:"enable"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type WebsiteHTTPS struct {
|
||||
Enable bool `json:"enable"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
|
||||
@@ -19,6 +19,7 @@ type SettingInfo struct {
|
||||
BindAddress string `json:"bindAddress"`
|
||||
PanelName string `json:"panelName"`
|
||||
Theme string `json:"theme"`
|
||||
MenuTabs string `json:"menuTabs"`
|
||||
Language string `json:"language"`
|
||||
DefaultNetwork string `json:"defaultNetwork"`
|
||||
LastCleanTime string `json:"lastCleanTime"`
|
||||
@@ -52,6 +53,10 @@ type SettingInfo struct {
|
||||
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
|
||||
|
||||
FileRecycleBin string `json:"fileRecycleBin"`
|
||||
|
||||
SnapshotIgnore string `json:"snapshotIgnore"`
|
||||
XpackHideMenu string `json:"xpackHideMenu"`
|
||||
NoAuthSetting string `json:"noAuthSetting"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
|
||||
@@ -9,6 +9,7 @@ type Cronjob struct {
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Spec string `gorm:"type:varchar(64);not null" json:"spec"`
|
||||
|
||||
Command string `gorm:"type:varchar(64)" json:"command"`
|
||||
ContainerName string `gorm:"type:varchar(64)" json:"containerName"`
|
||||
Script string `gorm:"longtext" json:"script"`
|
||||
Website string `gorm:"type:varchar(64)" json:"website"`
|
||||
|
||||
@@ -9,5 +9,6 @@ type DatabaseMysql struct {
|
||||
Username string `json:"username" gorm:"type:varchar(256);not null"`
|
||||
Password string `json:"password" gorm:"type:varchar(256);not null"`
|
||||
Permission string `json:"permission" gorm:"type:varchar(256);not null"`
|
||||
IsDelete bool `json:"isDelete" gorm:"type:varchar(64)"`
|
||||
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ type DatabasePostgresql struct {
|
||||
Format string `json:"format" gorm:"type:varchar(64);not null"`
|
||||
Username string `json:"username" gorm:"type:varchar(256);not null"`
|
||||
Password string `json:"password" gorm:"type:varchar(256);not null"`
|
||||
SuperUser bool `json:"superUser" gorm:"type:varchar(64)" `
|
||||
SuperUser bool `json:"superUser" gorm:"type:varchar(64)"`
|
||||
IsDelete bool `json:"isDelete" gorm:"type:varchar(64)"`
|
||||
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||
}
|
||||
|
||||
@@ -68,20 +68,20 @@ func (u *SettingRepo) Update(key, value string) error {
|
||||
}
|
||||
|
||||
func (u *SettingRepo) CreateMonitorBase(model model.MonitorBase) error {
|
||||
return global.DB.Create(&model).Error
|
||||
return global.MonitorDB.Create(&model).Error
|
||||
}
|
||||
func (u *SettingRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
|
||||
return global.DB.CreateInBatches(ioList, len(ioList)).Error
|
||||
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (u *SettingRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
|
||||
return global.DB.CreateInBatches(ioList, len(ioList)).Error
|
||||
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorBase(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorIO(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
|
||||
}
|
||||
func (u *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
|
||||
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type IWebsiteRepo interface {
|
||||
WithDefaultServer() DBOption
|
||||
WithDomainLike(domain string) DBOption
|
||||
WithRuntimeID(runtimeID uint) DBOption
|
||||
WithIDs(ids []uint) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
|
||||
List(opts ...DBOption) ([]model.Website, error)
|
||||
GetFirst(opts ...DBOption) (model.Website, error)
|
||||
@@ -36,9 +37,15 @@ func NewIWebsiteRepo() IWebsiteRepo {
|
||||
type WebsiteRepo struct {
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) WithAppInstallId(appInstallId uint) DBOption {
|
||||
func (w *WebsiteRepo) WithAppInstallId(appInstallID uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("app_install_id = ?", appInstallId)
|
||||
return db.Where("app_install_id = ?", appInstallID)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) WithIDs(ids []uint) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("id in (?)", ids)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ type IWebsiteDomainRepo interface {
|
||||
WithWebsiteId(websiteId uint) DBOption
|
||||
WithPort(port int) DBOption
|
||||
WithDomain(domain string) DBOption
|
||||
WithDomainLike(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)
|
||||
@@ -44,6 +45,11 @@ func (w WebsiteDomainRepo) WithDomain(domain string) DBOption {
|
||||
return db.Where("domain = ?", domain)
|
||||
}
|
||||
}
|
||||
func (w WebsiteDomainRepo) WithDomainLike(domain string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("domain like ?", "%"+domain+"%")
|
||||
}
|
||||
}
|
||||
func (w WebsiteDomainRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDomain, error) {
|
||||
var domains []model.WebsiteDomain
|
||||
db := getDb(opts...).Model(&model.WebsiteDomain{})
|
||||
|
||||
@@ -749,8 +749,10 @@ func syncAppInstallStatus(appInstall *model.AppInstall) error {
|
||||
|
||||
switch {
|
||||
case count == 0:
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(containerNames, ",")).Error()
|
||||
if appInstall.Status != constant.Error {
|
||||
appInstall.Status = constant.SyncErr
|
||||
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(containerNames, ",")).Error()
|
||||
}
|
||||
case exitedCount == total:
|
||||
appInstall.Status = constant.Stopped
|
||||
case runningCount == total:
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -37,7 +39,6 @@ import (
|
||||
"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"
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -364,6 +365,7 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
|
||||
}
|
||||
_ = websiteRepo.DeleteAll(ctx)
|
||||
_ = websiteDomainRepo.DeleteAll(ctx)
|
||||
xpack.RemoveTamper("")
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
|
||||
case constant.AppPostgresql:
|
||||
@@ -693,8 +695,8 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
|
||||
strArray[i] = strings.ToLower(fmt.Sprintf("%v", t[i]))
|
||||
}
|
||||
envParams[k] = strings.Join(strArray, ",")
|
||||
default:
|
||||
envParams[k] = t.(string)
|
||||
case map[string]interface{}:
|
||||
handleMap(t, envParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -827,7 +829,7 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var options dockerTypes.ContainerListOptions
|
||||
var options container.ListOptions
|
||||
list, err := client.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -1225,14 +1227,20 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
|
||||
return buserr.New(constant.ErrFileParse)
|
||||
}
|
||||
serviceValue := service.(map[string]interface{})
|
||||
deploy := map[string]interface{}{
|
||||
"resources": map[string]interface{}{
|
||||
"limits": map[string]interface{}{
|
||||
"cpus": "${CPUS}",
|
||||
"memory": "${MEMORY_LIMIT}",
|
||||
},
|
||||
},
|
||||
|
||||
deploy := map[string]interface{}{}
|
||||
if de, ok := serviceValue["deploy"]; ok {
|
||||
deploy = de.(map[string]interface{})
|
||||
}
|
||||
resource := map[string]interface{}{}
|
||||
if res, ok := deploy["resources"]; ok {
|
||||
resource = res.(map[string]interface{})
|
||||
}
|
||||
resource["limits"] = map[string]interface{}{
|
||||
"cpus": "${CPUS}",
|
||||
"memory": "${MEMORY_LIMIT}",
|
||||
}
|
||||
deploy["resources"] = resource
|
||||
serviceValue["deploy"] = deploy
|
||||
|
||||
ports, ok := serviceValue["ports"].([]interface{})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"strconv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -19,6 +20,7 @@ type AuthService struct{}
|
||||
|
||||
type IAuthService interface {
|
||||
CheckIsSafety(code string) (string, error)
|
||||
GetResponsePage() (string, error)
|
||||
VerifyCode(code string) (bool, error)
|
||||
Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error)
|
||||
LogOut(c *gin.Context) error
|
||||
@@ -42,7 +44,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
|
||||
if err != nil {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
if info.Password != pass || nameSetting.Value != info.Name {
|
||||
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
@@ -78,7 +80,7 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Password != pass || nameSetting.Value != info.Name {
|
||||
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
@@ -168,7 +170,7 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return setting.Value == code, nil
|
||||
return hmac.Equal([]byte(setting.Value), []byte(code)), nil
|
||||
}
|
||||
|
||||
func (u *AuthService) CheckIsSafety(code string) (string, error) {
|
||||
@@ -184,3 +186,11 @@ func (u *AuthService) CheckIsSafety(code string) (string, error) {
|
||||
}
|
||||
return "unpass", nil
|
||||
}
|
||||
|
||||
func (u *AuthService) GetResponsePage() (string, error) {
|
||||
pageCode, err := settingRepo.Get(settingRepo.WithByKey("NoAuthSetting"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return pageCode.Value, nil
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/client"
|
||||
@@ -87,7 +88,9 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
options := types.ContainerListOptions{All: true}
|
||||
options := container.ListOptions{
|
||||
All: true,
|
||||
}
|
||||
if len(req.Filters) != 0 {
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", req.Filters)
|
||||
@@ -216,7 +219,7 @@ func (u *ContainerService) List() ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
containers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -237,7 +240,7 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
list, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -376,14 +379,14 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
|
||||
container, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("create container %s successful! now check if the container is started and delete the container information if it is not.", req.Name)
|
||||
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
|
||||
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
||||
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return fmt.Errorf("create successful but start failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
@@ -410,6 +413,18 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
networkSettings := oldContainer.NetworkSettings
|
||||
bridgeNetworkSettings := networkSettings.Networks[data.Network]
|
||||
if bridgeNetworkSettings.IPAMConfig != nil {
|
||||
ipv4Address := bridgeNetworkSettings.IPAMConfig.IPv4Address
|
||||
data.Ipv4 = ipv4Address
|
||||
ipv6Address := bridgeNetworkSettings.IPAMConfig.IPv6Address
|
||||
data.Ipv6 = ipv6Address
|
||||
} else {
|
||||
data.Ipv4 = bridgeNetworkSettings.IPAddress
|
||||
}
|
||||
|
||||
data.Cmd = oldContainer.Config.Cmd
|
||||
data.OpenStdin = oldContainer.Config.OpenStdin
|
||||
data.Tty = oldContainer.Config.Tty
|
||||
@@ -442,7 +457,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||
if oldContainer.HostConfig.Memory != 0 {
|
||||
data.Memory = float64(oldContainer.HostConfig.Memory) / 1024 / 1024
|
||||
}
|
||||
data.Volumes = loadVolumeBinds(oldContainer.HostConfig.Binds)
|
||||
data.Volumes = loadVolumeBinds(oldContainer.Mounts)
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
@@ -471,7 +486,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := client.ContainerRemove(ctx, req.ContainerID, types.ContainerRemoveOptions{Force: true}); err != nil {
|
||||
if err := client.ContainerRemove(ctx, req.ContainerID, container.RemoveOptions{Force: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -482,13 +497,13 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
|
||||
}
|
||||
|
||||
global.LOG.Infof("new container info %s has been update, now start to recreate", req.Name)
|
||||
container, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
|
||||
return fmt.Errorf("update container failed, err: %v", err)
|
||||
}
|
||||
global.LOG.Infof("update container %s successful! now check if the container is started.", req.Name)
|
||||
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
|
||||
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
|
||||
return fmt.Errorf("update successful but start failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -523,18 +538,18 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{Force: true}); err != nil {
|
||||
if err := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
global.LOG.Infof("new container info %s has been update, now start to recreate", req.Name)
|
||||
container, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
|
||||
return fmt.Errorf("upgrade container failed, err: %v", err)
|
||||
}
|
||||
global.LOG.Infof("upgrade container %s successful! now check if the container is started.", req.Name)
|
||||
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
|
||||
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
|
||||
return fmt.Errorf("upgrade successful but start failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -566,7 +581,7 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
global.LOG.Infof("start container %s operation %s", item, req.Operation)
|
||||
switch req.Operation {
|
||||
case constant.ContainerOpStart:
|
||||
err = client.ContainerStart(ctx, item, types.ContainerStartOptions{})
|
||||
err = client.ContainerStart(ctx, item, container.StartOptions{})
|
||||
case constant.ContainerOpStop:
|
||||
err = client.ContainerStop(ctx, item, container.StopOptions{})
|
||||
case constant.ContainerOpRestart:
|
||||
@@ -578,7 +593,7 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
|
||||
case constant.ContainerOpUnpause:
|
||||
err = client.ContainerUnpause(ctx, item)
|
||||
case constant.ContainerOpRemove:
|
||||
err = client.ContainerRemove(ctx, item, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
||||
err = client.ContainerRemove(ctx, item, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
}
|
||||
}
|
||||
return err
|
||||
@@ -612,7 +627,7 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
||||
_ = os.Remove(file)
|
||||
}
|
||||
|
||||
if err := client.ContainerStart(ctx, containerItem.ID, types.ContainerStartOptions{}); err != nil {
|
||||
if err := client.ContainerStart(ctx, containerItem.ID, container.StartOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -623,22 +638,26 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
|
||||
if cmd.CheckIllegal(container, since, tail) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
command := fmt.Sprintf("docker logs %s", container)
|
||||
commandName := "docker"
|
||||
commandArg := []string{"logs", container}
|
||||
if containerType == "compose" {
|
||||
command = fmt.Sprintf("docker-compose -f %s logs", container)
|
||||
commandName = "docker-compose"
|
||||
commandArg = []string{"-f", container, "logs"}
|
||||
}
|
||||
if tail != "0" {
|
||||
command += " --tail " + tail
|
||||
commandArg = append(commandArg, "--tail")
|
||||
commandArg = append(commandArg, tail)
|
||||
}
|
||||
if since != "all" {
|
||||
command += " --since " + since
|
||||
commandArg = append(commandArg, "--since")
|
||||
commandArg = append(commandArg, since)
|
||||
}
|
||||
if follow {
|
||||
command += " -f"
|
||||
commandArg = append(commandArg, "-f")
|
||||
}
|
||||
command += " 2>&1"
|
||||
cmd := exec.Command("bash", "-c", command)
|
||||
if !follow {
|
||||
cmd := exec.Command(commandName, commandArg...)
|
||||
cmd.Stderr = cmd.Stdout
|
||||
stdout, _ := cmd.CombinedOutput()
|
||||
if !utf8.Valid(stdout) {
|
||||
return errors.New("invalid utf8")
|
||||
@@ -649,50 +668,53 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.Command(commandName, commandArg...)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
_ = cmd.Wait()
|
||||
}()
|
||||
var exitCh chan struct{}
|
||||
if follow {
|
||||
go func() {
|
||||
_, wsData, _ := wsConn.ReadMessage()
|
||||
if string(wsData) == "close conn" {
|
||||
exitCh <- struct{}{}
|
||||
}
|
||||
}()
|
||||
return err
|
||||
}
|
||||
cmd.Stderr = cmd.Stdout
|
||||
if err := cmd.Start(); err != nil {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
return err
|
||||
}
|
||||
exitCh := make(chan struct{})
|
||||
go func() {
|
||||
_, wsData, _ := wsConn.ReadMessage()
|
||||
if string(wsData) == "close conn" {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
exitCh <- struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return nil
|
||||
default:
|
||||
n, err := stdout.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return err
|
||||
go func() {
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return
|
||||
default:
|
||||
n, err := stdout.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
global.LOG.Errorf("read bytes from log failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
if !utf8.Valid(buffer[:n]) {
|
||||
continue
|
||||
}
|
||||
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
|
||||
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
global.LOG.Errorf("read bytes from log failed, err: %v", err)
|
||||
continue
|
||||
}
|
||||
if !utf8.Valid(buffer[:n]) {
|
||||
continue
|
||||
}
|
||||
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
|
||||
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
_ = cmd.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error) {
|
||||
@@ -735,7 +757,7 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
options := types.ContainerListOptions{All: true}
|
||||
options := container.ListOptions{All: true}
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", fmt.Sprintf("%s=%s", composeProjectLabel, req.Name))
|
||||
containers, err := cli.ContainerList(context.Background(), options)
|
||||
@@ -976,8 +998,21 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||
case "host", "none", "bridge":
|
||||
hostConf.NetworkMode = container.NetworkMode(req.Network)
|
||||
}
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
|
||||
if req.Ipv4 != "" || req.Ipv6 != "" {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{
|
||||
req.Network: {
|
||||
IPAMConfig: &network.EndpointIPAMConfig{
|
||||
IPv4Address: req.Ipv4,
|
||||
IPv6Address: req.Ipv6,
|
||||
},
|
||||
}}
|
||||
} else {
|
||||
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
|
||||
}
|
||||
} else {
|
||||
if req.Ipv4 != "" || req.Ipv6 != "" {
|
||||
return nil, nil, nil, fmt.Errorf("Please set up the network")
|
||||
}
|
||||
networkConf = network.NetworkingConfig{}
|
||||
}
|
||||
|
||||
@@ -994,10 +1029,19 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||
hostConf.MemorySwap = 0
|
||||
hostConf.PortBindings = portMap
|
||||
hostConf.Binds = []string{}
|
||||
hostConf.Mounts = []mount.Mount{}
|
||||
config.Volumes = make(map[string]struct{})
|
||||
for _, volume := range req.Volumes {
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
|
||||
if volume.Type == "volume" {
|
||||
hostConf.Mounts = append(hostConf.Mounts, mount.Mount{
|
||||
Type: mount.Type(volume.Type),
|
||||
Source: volume.SourceDir,
|
||||
Target: volume.ContainerDir,
|
||||
})
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
} else {
|
||||
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
|
||||
}
|
||||
}
|
||||
return &config, &hostConf, &networkConf, nil
|
||||
}
|
||||
@@ -1018,46 +1062,27 @@ func reCreateAfterUpdate(name string, client *client.Client, config *container.C
|
||||
global.LOG.Errorf("recreate after container update failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
if err := client.ContainerStart(ctx, oldContainer.ID, types.ContainerStartOptions{}); err != nil {
|
||||
if err := client.ContainerStart(ctx, oldContainer.ID, container.StartOptions{}); err != nil {
|
||||
global.LOG.Errorf("restart after container update failed, err: %v", err)
|
||||
}
|
||||
global.LOG.Errorf("recreate after container update successful")
|
||||
}
|
||||
|
||||
func loadVolumeBinds(binds []string) []dto.VolumeHelper {
|
||||
func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
|
||||
var datas []dto.VolumeHelper
|
||||
for _, bind := range binds {
|
||||
parts := strings.Split(bind, ":")
|
||||
var volumeItem dto.VolumeHelper
|
||||
if len(parts) > 3 {
|
||||
continue
|
||||
volumeItem.Type = string(bind.Type)
|
||||
if bind.Type == "volume" {
|
||||
volumeItem.SourceDir = bind.Name
|
||||
} else {
|
||||
volumeItem.SourceDir = bind.Source
|
||||
}
|
||||
volumeItem.SourceDir = parts[0]
|
||||
if len(parts) == 1 {
|
||||
volumeItem.ContainerDir = parts[0]
|
||||
volumeItem.ContainerDir = bind.Destination
|
||||
volumeItem.Mode = "ro"
|
||||
if bind.RW {
|
||||
volumeItem.Mode = "rw"
|
||||
}
|
||||
if len(parts) == 2 {
|
||||
switch parts[1] {
|
||||
case "r", "ro":
|
||||
volumeItem.ContainerDir = parts[0]
|
||||
volumeItem.Mode = "ro"
|
||||
case "rw":
|
||||
volumeItem.ContainerDir = parts[0]
|
||||
volumeItem.Mode = "rw"
|
||||
default:
|
||||
volumeItem.ContainerDir = parts[1]
|
||||
volumeItem.Mode = "rw"
|
||||
}
|
||||
}
|
||||
if len(parts) == 3 {
|
||||
volumeItem.ContainerDir = parts[1]
|
||||
if parts[2] == "r" {
|
||||
volumeItem.Mode = "ro"
|
||||
} else {
|
||||
volumeItem.Mode = parts[2]
|
||||
}
|
||||
}
|
||||
datas = append(datas, volumeItem)
|
||||
}
|
||||
return datas
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -20,7 +21,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
@@ -40,7 +40,7 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
options := types.ContainerListOptions{All: true}
|
||||
options := container.ListOptions{All: true}
|
||||
options.Filters = filters.NewArgs()
|
||||
options.Filters.Add("label", composeProjectLabel)
|
||||
|
||||
|
||||
@@ -265,6 +265,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
upMap["name"] = req.Name
|
||||
upMap["spec"] = spec
|
||||
upMap["script"] = req.Script
|
||||
upMap["command"] = req.Command
|
||||
upMap["container_name"] = req.ContainerName
|
||||
upMap["app_id"] = req.AppID
|
||||
upMap["website"] = req.Website
|
||||
|
||||
@@ -326,7 +326,7 @@ func handleBackupLogs(targetDir, fileName string) error {
|
||||
if len(logFiles) != 0 {
|
||||
for i := 0; i < len(logFiles); i++ {
|
||||
if !logFiles[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(itemDir, logFiles[i].Name())}, dirItem)
|
||||
_ = common.CopyFile(path.Join(itemDir, logFiles[i].Name()), dirItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,7 +335,7 @@ func handleBackupLogs(targetDir, fileName string) error {
|
||||
if len(logFiles2) != 0 {
|
||||
for i := 0; i < len(logFiles2); i++ {
|
||||
if !logFiles2[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(itemDir2, logFiles2[i].Name())}, dirItem)
|
||||
_ = common.CopyFile(path.Join(itemDir2, logFiles2[i].Name()), dirItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,7 +354,7 @@ func handleBackupLogs(targetDir, fileName string) error {
|
||||
if len(systemLogFiles) != 0 {
|
||||
for i := 0; i < len(systemLogFiles); i++ {
|
||||
if !systemLogFiles[i].IsDir() {
|
||||
_ = cpBinary([]string{path.Join(systemLogDir, systemLogFiles[i].Name())}, systemDir)
|
||||
_ = common.CopyFile(path.Join(systemLogDir, systemLogFiles[i].Name()), systemDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,7 +370,7 @@ func handleBackupLogs(targetDir, fileName string) error {
|
||||
if len(loginLogFiles) != 0 {
|
||||
for i := 0; i < len(loginLogFiles); i++ {
|
||||
if !loginLogFiles[i].IsDir() && (strings.HasPrefix(loginLogFiles[i].Name(), "secure") || strings.HasPrefix(loginLogFiles[i].Name(), "auth.log")) {
|
||||
_ = cpBinary([]string{path.Join("/var/log", loginLogFiles[i].Name())}, loginDir)
|
||||
_ = common.CopyFile(path.Join("/var/log", loginLogFiles[i].Name()), loginDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -38,7 +39,11 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||
script := cronjob.Script
|
||||
if len(cronjob.ContainerName) != 0 {
|
||||
script = fmt.Sprintf("docker exec %s %s", cronjob.ContainerName, cronjob.Script)
|
||||
command := "sh"
|
||||
if len(cronjob.Command) != 0 {
|
||||
command = cronjob.Command
|
||||
}
|
||||
script = fmt.Sprintf("docker exec %s %s -c \"%s\"", cronjob.ContainerName, command, strings.ReplaceAll(cronjob.Script, "\"", "\\\""))
|
||||
}
|
||||
err = u.handleShell(cronjob.Type, cronjob.Name, script, record.Records)
|
||||
u.removeExpiredLog(*cronjob)
|
||||
@@ -271,6 +276,21 @@ func loadClientMap(backupAccounts string) (map[string]cronjobUploadHelper, error
|
||||
if account.BackupPath != "/" {
|
||||
pathItem = strings.TrimPrefix(account.BackupPath, "/")
|
||||
}
|
||||
if target == constant.Local {
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(account.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := varMap["dir"]; !ok {
|
||||
return nil, errors.New("load local backup dir failed")
|
||||
}
|
||||
baseDir, ok := varMap["dir"].(string)
|
||||
if ok {
|
||||
pathItem = baseDir
|
||||
} else {
|
||||
return nil, fmt.Errorf("error type dir: %T", varMap["dir"])
|
||||
}
|
||||
}
|
||||
clients[target] = cronjobUploadHelper{
|
||||
client: client,
|
||||
backupPath: pathItem,
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
@@ -154,6 +156,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
|
||||
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
|
||||
|
||||
currentInfo.DiskData = loadDiskInfo()
|
||||
currentInfo.GPUData = loadGPUInfo()
|
||||
|
||||
if ioOption == "all" {
|
||||
diskInfo, _ := disk.IOCounters()
|
||||
@@ -289,3 +292,21 @@ func loadDiskInfo() []dto.DiskInfo {
|
||||
})
|
||||
return datas
|
||||
}
|
||||
|
||||
func loadGPUInfo() []dto.GPUInfo {
|
||||
list := xpack.LoadGpuInfo()
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
var data []dto.GPUInfo
|
||||
for _, gpu := range list {
|
||||
var dataItem dto.GPUInfo
|
||||
if err := copier.Copy(&dataItem, &gpu); err != nil {
|
||||
continue
|
||||
}
|
||||
dataItem.PowerUsage = dataItem.PowerDraw + " / " + dataItem.MaxPowerLimit
|
||||
dataItem.MemoryUsage = dataItem.MemUsed + " / " + dataItem.MemTotal
|
||||
data = append(data, dataItem)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -190,11 +190,13 @@ func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deleteList := databases
|
||||
for _, data := range datas {
|
||||
hasOld := false
|
||||
for _, oldData := range databases {
|
||||
if strings.EqualFold(oldData.Name, data.Name) && strings.EqualFold(oldData.MysqlName, data.MysqlName) {
|
||||
for i := 0; i < len(databases); i++ {
|
||||
if strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].MysqlName, data.MysqlName) {
|
||||
hasOld = true
|
||||
deleteList = append(deleteList[:i], deleteList[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -208,6 +210,9 @@ func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, delItem := range deleteList {
|
||||
_ = mysqlRepo.Update(delItem.ID, map[string]interface{}{"is_delete": true})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -216,11 +216,13 @@ func (u *PostgresqlService) LoadFromRemote(database string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deleteList := databases
|
||||
for _, data := range datas {
|
||||
hasOld := false
|
||||
for _, oldData := range databases {
|
||||
if strings.EqualFold(oldData.Name, data.Name) && strings.EqualFold(oldData.PostgresqlName, data.PostgresqlName) {
|
||||
for i := 0; i < len(databases); i++ {
|
||||
if strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].PostgresqlName, data.PostgresqlName) {
|
||||
hasOld = true
|
||||
deleteList = append(deleteList[:i], deleteList[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -234,6 +236,9 @@ func (u *PostgresqlService) LoadFromRemote(database string) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, delItem := range deleteList {
|
||||
_ = postgresqlRepo.Update(delItem.ID, map[string]interface{}{"is_delete": true})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -116,22 +116,32 @@ func (f *FileService) GetFileTree(op request.FileOption) ([]response.FileTree, e
|
||||
}
|
||||
|
||||
func (f *FileService) Create(op request.FileCreate) error {
|
||||
if files.IsInvalidChar(op.Path) {
|
||||
return buserr.New("ErrInvalidChar")
|
||||
}
|
||||
fo := files.NewFileOp()
|
||||
if fo.Stat(op.Path) {
|
||||
return buserr.New(constant.ErrFileIsExit)
|
||||
}
|
||||
if op.IsDir {
|
||||
return fo.CreateDir(op.Path, fs.FileMode(op.Mode))
|
||||
} else {
|
||||
if op.IsLink {
|
||||
if !fo.Stat(op.LinkPath) {
|
||||
return buserr.New(constant.ErrLinkPathNotFound)
|
||||
}
|
||||
return fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink)
|
||||
mode := op.Mode
|
||||
if mode == 0 {
|
||||
fileInfo, err := os.Stat(filepath.Dir(op.Path))
|
||||
if err == nil {
|
||||
mode = int64(fileInfo.Mode().Perm())
|
||||
} else {
|
||||
return fo.CreateFile(op.Path)
|
||||
mode = 0755
|
||||
}
|
||||
}
|
||||
if op.IsDir {
|
||||
return fo.CreateDirWithMode(op.Path, fs.FileMode(mode))
|
||||
}
|
||||
if op.IsLink {
|
||||
if !fo.Stat(op.LinkPath) {
|
||||
return buserr.New(constant.ErrLinkPathNotFound)
|
||||
}
|
||||
return fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink)
|
||||
}
|
||||
return fo.CreateFileWithMode(op.Path, fs.FileMode(mode))
|
||||
}
|
||||
|
||||
func (f *FileService) Delete(op request.FileDelete) error {
|
||||
@@ -236,6 +246,9 @@ func (f *FileService) SaveContent(edit request.FileEdit) error {
|
||||
}
|
||||
|
||||
func (f *FileService) ChangeName(req request.FileRename) error {
|
||||
if files.IsInvalidChar(req.NewName) {
|
||||
return buserr.New("ErrInvalidChar")
|
||||
}
|
||||
fo := files.NewFileOp()
|
||||
return fo.Rename(req.OldName, req.NewName)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
@@ -43,7 +45,7 @@ func NewIImageService() IImageService {
|
||||
}
|
||||
func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||
var (
|
||||
list []types.ImageSummary
|
||||
list []image.Summary
|
||||
records []dto.ImageInfo
|
||||
backDatas []dto.ImageInfo
|
||||
)
|
||||
@@ -51,11 +53,11 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
list, err = client.ImageList(context.Background(), types.ImageListOptions{})
|
||||
list, err = client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
containers, _ := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if len(req.Info) != 0 {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
@@ -104,11 +106,11 @@ func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := client.ImageList(context.Background(), types.ImageListOptions{})
|
||||
list, err := client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containers, _ := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
for _, image := range list {
|
||||
size := formatFileSize(image.Size)
|
||||
records = append(records, dto.ImageInfo{
|
||||
@@ -124,14 +126,14 @@ func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
|
||||
|
||||
func (u *ImageService) List() ([]dto.Options, error) {
|
||||
var (
|
||||
list []types.ImageSummary
|
||||
list []image.Summary
|
||||
backDatas []dto.Options
|
||||
)
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err = client.ImageList(context.Background(), types.ImageListOptions{})
|
||||
list, err = client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -153,22 +153,20 @@ func (r RecycleBinService) Clear() error {
|
||||
}
|
||||
|
||||
func getClashDir(realPath string) (string, error) {
|
||||
trimmedPath := strings.Trim(realPath, "/")
|
||||
parts := strings.Split(trimmedPath, "/")
|
||||
dir := ""
|
||||
if len(parts) > 0 {
|
||||
dir = parts[0]
|
||||
partitions, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
partitions, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, p := range partitions {
|
||||
if p.Mountpoint == "/" {
|
||||
continue
|
||||
}
|
||||
for _, p := range partitions {
|
||||
if p.Mountpoint == dir {
|
||||
if err = createClashDir(path.Join(p.Mountpoint, ".1panel_clash")); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir, nil
|
||||
if strings.HasPrefix(realPath, p.Mountpoint) {
|
||||
clashDir := path.Join(p.Mountpoint, ".1panel_clash")
|
||||
if err = createClashDir(path.Join(p.Mountpoint, ".1panel_clash")); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return clashDir, nil
|
||||
}
|
||||
}
|
||||
return constant.RecycleBinDir, createClashDir(constant.RecycleBinDir)
|
||||
|
||||
@@ -42,6 +42,7 @@ type IRuntimeService interface {
|
||||
GetNodeModules(req request.NodeModuleReq) ([]response.NodeModule, error)
|
||||
OperateNodeModules(req request.NodeModuleOperateReq) error
|
||||
SyncForRestart() error
|
||||
SyncRuntimeStatus() error
|
||||
}
|
||||
|
||||
func NewRuntimeService() IRuntimeService {
|
||||
@@ -586,3 +587,16 @@ func (r *RuntimeService) SyncForRestart() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RuntimeService) SyncRuntimeStatus() error {
|
||||
runtimes, err := runtimeRepo.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, runtime := range runtimes {
|
||||
if runtime.Type == constant.RuntimeNode {
|
||||
_ = SyncRuntimeContainerStatus(&runtime)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -181,33 +181,20 @@ func SyncRuntimeContainerStatus(runtime *model.Runtime) error {
|
||||
}
|
||||
container := containers[0]
|
||||
|
||||
interval := 10 * time.Second
|
||||
retries := 60
|
||||
for i := 0; i < retries; i++ {
|
||||
resp, err := cli.InspectContainer(container.ID)
|
||||
if err != nil {
|
||||
time.Sleep(interval)
|
||||
continue
|
||||
switch container.State {
|
||||
case "exited":
|
||||
runtime.Status = constant.RuntimeError
|
||||
case "running":
|
||||
runtime.Status = constant.RuntimeRunning
|
||||
case "paused":
|
||||
runtime.Status = constant.RuntimeStopped
|
||||
default:
|
||||
if runtime.Status != constant.RuntimeBuildIng {
|
||||
runtime.Status = constant.RuntimeStopped
|
||||
}
|
||||
if resp.State.Health != nil {
|
||||
status := strings.ToLower(resp.State.Health.Status)
|
||||
switch status {
|
||||
case "starting":
|
||||
runtime.Status = constant.RuntimeStarting
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
case "healthy":
|
||||
runtime.Status = constant.RuntimeRunning
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
return nil
|
||||
case "unhealthy":
|
||||
runtime.Status = constant.RuntimeUnhealthy
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
time.Sleep(interval)
|
||||
}
|
||||
return nil
|
||||
|
||||
return runtimeRepo.Save(runtime)
|
||||
}
|
||||
|
||||
func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
|
||||
|
||||
@@ -385,15 +385,6 @@ func updateRecoverStatus(id uint, isRecover bool, interruptStep, status, message
|
||||
}
|
||||
}
|
||||
|
||||
func cpBinary(src []string, dst string) error {
|
||||
global.LOG.Debugf(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
|
||||
stdout, err := cmd.Exec(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cp file failed, stdout: %v, err: %v", stdout, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SnapshotService) handleUnTar(sourceDir, targetDir string) error {
|
||||
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
|
||||
|
||||
@@ -43,7 +43,15 @@ func snapPanel(snap snapHelper, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel": constant.Running})
|
||||
status := constant.StatusDone
|
||||
if err := cpBinary([]string{"/usr/local/bin/1panel", "/usr/local/bin/1pctl", "/etc/systemd/system/1panel.service"}, targetDir); err != nil {
|
||||
if err := common.CopyFile("/usr/local/bin/1panel", path.Join(targetDir, "1panel")); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
|
||||
if err := common.CopyFile("/usr/local/bin/1pctl", targetDir); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
|
||||
if err := common.CopyFile("/etc/systemd/system/1panel.service", targetDir); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
snap.Status.Panel = status
|
||||
@@ -59,7 +67,7 @@ func snapDaemonJson(snap snapHelper, targetDir string) {
|
||||
return
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"daemon_json": constant.Running})
|
||||
if err := cpBinary([]string{"/etc/docker/daemon.json"}, path.Join(targetDir, "daemon.json")); err != nil {
|
||||
if err := common.CopyFile("/etc/docker/daemon.json", targetDir); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
snap.Status.DaemonJson = status
|
||||
@@ -138,7 +146,14 @@ func snapPanelData(snap snapHelper, localDir, targetDir string) {
|
||||
if strings.Contains(localDir, dataDir) {
|
||||
exclusionRules += ("." + strings.ReplaceAll(localDir, dataDir, "") + ";")
|
||||
}
|
||||
|
||||
ignoreVal, _ := settingRepo.Get(settingRepo.WithByKey("SnapshotIgnore"))
|
||||
rules := strings.Split(ignoreVal.Value, ",")
|
||||
for _, ignore := range rules {
|
||||
if len(ignore) == 0 || cmd.CheckIllegal(ignore) {
|
||||
continue
|
||||
}
|
||||
exclusionRules += ("." + strings.ReplaceAll(ignore, dataDir, "") + ";")
|
||||
}
|
||||
_ = snapshotRepo.Update(snap.SnapID, map[string]interface{}{"status": "OnSaveData"})
|
||||
sysIP, _ := settingRepo.Get(settingRepo.WithByKey("SystemIP"))
|
||||
_ = settingRepo.Update("SystemIP", "")
|
||||
@@ -154,10 +169,6 @@ func snapPanelData(snap snapHelper, localDir, targetDir string) {
|
||||
}
|
||||
|
||||
func snapCompress(snap snapHelper, rootDir string) {
|
||||
defer func() {
|
||||
global.LOG.Debugf("remove snapshot file %s", rootDir)
|
||||
_ = os.RemoveAll(rootDir)
|
||||
}()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": constant.StatusRunning})
|
||||
tmpDir := path.Join(global.CONF.System.TmpDir, "system")
|
||||
fileName := fmt.Sprintf("%s.tar.gz", path.Base(rootDir))
|
||||
@@ -178,15 +189,13 @@ func snapCompress(snap snapHelper, rootDir string) {
|
||||
snap.Status.Compress = constant.StatusDone
|
||||
snap.Status.Size = size
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": constant.StatusDone, "size": size})
|
||||
|
||||
global.LOG.Debugf("remove snapshot file %s", rootDir)
|
||||
_ = os.RemoveAll(rootDir)
|
||||
}
|
||||
|
||||
func snapUpload(snap snapHelper, accounts string, file string) {
|
||||
source := path.Join(global.CONF.System.TmpDir, "system", path.Base(file))
|
||||
defer func() {
|
||||
global.LOG.Debugf("remove snapshot file %s", source)
|
||||
_ = os.Remove(source)
|
||||
}()
|
||||
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": constant.StatusUploading})
|
||||
accountMap, err := loadClientMap(accounts)
|
||||
if err != nil {
|
||||
@@ -207,6 +216,9 @@ func snapUpload(snap snapHelper, accounts string, file string) {
|
||||
}
|
||||
snap.Status.Upload = constant.StatusDone
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": constant.StatusDone})
|
||||
|
||||
global.LOG.Debugf("remove snapshot file %s", source)
|
||||
_ = os.Remove(source)
|
||||
}
|
||||
|
||||
func handleSnapTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
@@ -216,6 +228,7 @@ func handleSnapTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
}
|
||||
}
|
||||
|
||||
exMap := make(map[string]struct{})
|
||||
exStr := ""
|
||||
excludes := strings.Split(exclusionRules, ";")
|
||||
excludes = append(excludes, "*.sock")
|
||||
@@ -223,8 +236,12 @@ func handleSnapTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
if len(exclude) == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := exMap[exclude]; ok {
|
||||
continue
|
||||
}
|
||||
exStr += " --exclude "
|
||||
exStr += exclude
|
||||
exMap[exclude] = struct{}{}
|
||||
}
|
||||
|
||||
commands := fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s -C %s .", targetDir+"/"+name, exStr, sourceDir)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -88,7 +89,7 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
|
||||
}
|
||||
|
||||
if req.IsNew || snap.InterruptStep == "1PanelBinary" {
|
||||
if err := recoverPanel(path.Join(snapFileDir, "1panel/1panel"), "/usr/local/bin/1panel"); err != nil {
|
||||
if err := recoverPanel(path.Join(snapFileDir, "1panel/1panel"), "/usr/local/bin"); err != nil {
|
||||
updateRecoverStatus(snap.ID, isRecover, "1PanelBinary", constant.StatusFailed, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -96,7 +97,7 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
|
||||
req.IsNew = true
|
||||
}
|
||||
if req.IsNew || snap.InterruptStep == "1PctlBinary" {
|
||||
if err := recoverPanel(path.Join(snapFileDir, "1panel/1pctl"), "/usr/local/bin/1pctl"); err != nil {
|
||||
if err := recoverPanel(path.Join(snapFileDir, "1panel/1pctl"), "/usr/local/bin"); err != nil {
|
||||
updateRecoverStatus(snap.ID, isRecover, "1PctlBinary", constant.StatusFailed, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -104,7 +105,7 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
|
||||
req.IsNew = true
|
||||
}
|
||||
if req.IsNew || snap.InterruptStep == "1PanelService" {
|
||||
if err := recoverPanel(path.Join(snapFileDir, "1panel/1panel.service"), "/etc/systemd/system/1panel.service"); err != nil {
|
||||
if err := recoverPanel(path.Join(snapFileDir, "1panel/1panel.service"), "/etc/systemd/system"); err != nil {
|
||||
updateRecoverStatus(snap.ID, isRecover, "1PanelService", constant.StatusFailed, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -215,7 +216,7 @@ func recoverAppData(src string) error {
|
||||
|
||||
func recoverDaemonJson(src string, fileOp files.FileOp) error {
|
||||
daemonJsonPath := "/etc/docker/daemon.json"
|
||||
_, errSrc := os.Stat(src)
|
||||
_, errSrc := os.Stat(path.Join(src, "docker/daemon.json"))
|
||||
_, errPath := os.Stat(daemonJsonPath)
|
||||
if os.IsNotExist(errSrc) && os.IsNotExist(errPath) {
|
||||
global.LOG.Debug("the daemon.json file does not exist, nothing happens.")
|
||||
@@ -235,10 +236,8 @@ func recoverPanel(src string, dst string) error {
|
||||
if _, err := os.Stat(src); err != nil {
|
||||
return fmt.Errorf("file is not found in %s, err: %v", src, err)
|
||||
}
|
||||
global.LOG.Debugf(fmt.Sprintf("\\cp -f %s %s", src, dst))
|
||||
stdout, err := cmd.Exec(fmt.Sprintf("\\cp -f %s %s", src, dst))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cp file failed, stdout: %v, err: %v", stdout, err)
|
||||
if err := common.CopyFile(src, dst); err != nil {
|
||||
return fmt.Errorf("cp file failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -177,7 +177,11 @@ func (u *SSHService) Update(req dto.SSHUpdate) error {
|
||||
},
|
||||
}
|
||||
if err := NewIFirewallService().UpdatePortRule(ruleItem); err != nil {
|
||||
global.LOG.Errorf("reset firewall rules %s -> %s failed, err: %v", req.OldValue, req.OldValue, err)
|
||||
global.LOG.Errorf("reset firewall rules %s -> %s failed, err: %v", req.OldValue, req.NewValue, err)
|
||||
}
|
||||
|
||||
if err = NewIHostService().Update(1, map[string]interface{}{"port": req.NewValue}); err != nil {
|
||||
global.LOG.Errorf("reset host port %s -> %s failed, err: %v", req.OldValue, req.NewValue, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -44,7 +43,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
|
||||
global.LOG.Infof("load latest version failed, err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if !common.CompareVersion(string(latestVersion), currentVersion.Value) {
|
||||
if !common.ComparePanelVersion(string(latestVersion), currentVersion.Value) {
|
||||
return nil, err
|
||||
}
|
||||
upgrade.LatestVersion = latestVersion
|
||||
@@ -131,13 +130,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
}
|
||||
global.LOG.Info("backup original data successful, now start to upgrade!")
|
||||
|
||||
if err := cpBinary([]string{tmpDir + "/1panel"}, "/usr/local/bin/1panel"); err != nil {
|
||||
if err := common.CopyFile(path.Join(tmpDir, "1panel"), "/usr/local/bin"); err != nil {
|
||||
global.LOG.Errorf("upgrade 1panel failed, err: %v", err)
|
||||
u.handleRollback(originalDir, 1)
|
||||
return
|
||||
}
|
||||
|
||||
if err := cpBinary([]string{tmpDir + "/1pctl"}, "/usr/local/bin/1pctl"); err != nil {
|
||||
if err := common.CopyFile(path.Join(tmpDir, "1pctl"), "/usr/local/bin"); err != nil {
|
||||
global.LOG.Errorf("upgrade 1pctl failed, err: %v", err)
|
||||
u.handleRollback(originalDir, 2)
|
||||
return
|
||||
@@ -148,7 +147,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
return
|
||||
}
|
||||
|
||||
if err := cpBinary([]string{tmpDir + "/1panel.service"}, "/etc/systemd/system/1panel.service"); err != nil {
|
||||
if err := common.CopyFile(path.Join(tmpDir, "1panel.service"), "/etc/systemd/system"); err != nil {
|
||||
global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
|
||||
u.handleRollback(originalDir, 3)
|
||||
return
|
||||
@@ -182,24 +181,23 @@ func (u *UpgradeService) handleBackup(fileOp files.FileOp, originalDir string) e
|
||||
}
|
||||
|
||||
func (u *UpgradeService) handleRollback(originalDir string, errStep int) {
|
||||
dbPath := global.CONF.System.DbPath + "/1Panel.db"
|
||||
_ = settingRepo.Update("SystemStatus", "Free")
|
||||
if err := cpBinary([]string{originalDir + "/1Panel.db"}, dbPath); err != nil {
|
||||
if err := common.CopyFile(path.Join(originalDir, "1Panel.db"), global.CONF.System.DbPath); err != nil {
|
||||
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
||||
}
|
||||
if err := cpBinary([]string{originalDir + "/1panel"}, "/usr/local/bin/1panel"); err != nil {
|
||||
if err := common.CopyFile(path.Join(originalDir, "1panel"), "/usr/local/bin"); err != nil {
|
||||
global.LOG.Errorf("rollback 1pctl failed, err: %v", err)
|
||||
}
|
||||
if errStep == 1 {
|
||||
return
|
||||
}
|
||||
if err := cpBinary([]string{originalDir + "/1pctl"}, "/usr/local/bin/1pctl"); err != nil {
|
||||
if err := common.CopyFile(path.Join(originalDir, "1pctl"), "/usr/local/bin"); err != nil {
|
||||
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
||||
}
|
||||
if errStep == 2 {
|
||||
return
|
||||
}
|
||||
if err := cpBinary([]string{originalDir + "/1panel.service"}, "/etc/systemd/system/1panel.service"); err != nil {
|
||||
if err := common.CopyFile(path.Join(originalDir, "1panel.service"), "/etc/systemd/system"); err != nil {
|
||||
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -250,19 +248,24 @@ func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
|
||||
}
|
||||
|
||||
func loadArch() (string, error) {
|
||||
switch runtime.GOARCH {
|
||||
case "amd64", "ppc64le", "s390x", "arm64":
|
||||
return runtime.GOARCH, nil
|
||||
case "arm":
|
||||
std, err := cmd.Exec("uname -m")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("std: %s, err: %s", std, err.Error())
|
||||
}
|
||||
if std == "armv7l\n" {
|
||||
return "armv7", nil
|
||||
}
|
||||
return "", fmt.Errorf("unsupported such arch: arm-%s", std)
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported such arch: %s", runtime.GOARCH)
|
||||
std, err := cmd.Exec("uname -a")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("std: %s, err: %s", std, err.Error())
|
||||
}
|
||||
if strings.Contains(std, "x86_64") {
|
||||
return "amd64", nil
|
||||
}
|
||||
if strings.Contains(std, "arm64") || strings.Contains(std, "aarch64") {
|
||||
return "arm64", nil
|
||||
}
|
||||
if strings.Contains(std, "armv7l") {
|
||||
return "armv7", nil
|
||||
}
|
||||
if strings.Contains(std, "ppc64le") {
|
||||
return "ppc64le", nil
|
||||
}
|
||||
if strings.Contains(std, "s390x") {
|
||||
return "s390x", nil
|
||||
}
|
||||
return "", fmt.Errorf("unsupported such arch: %s", std)
|
||||
}
|
||||
|
||||
+130
-83
@@ -61,6 +61,7 @@ type IWebsiteService interface {
|
||||
UpdateWebsite(req request.WebsiteUpdate) error
|
||||
DeleteWebsite(req request.WebsiteDelete) error
|
||||
GetWebsite(id uint) (response.WebsiteDTO, error)
|
||||
|
||||
CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error)
|
||||
GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error)
|
||||
DeleteWebsiteDomain(domainId uint) error
|
||||
@@ -75,10 +76,6 @@ type IWebsiteService interface {
|
||||
ChangeDefaultServer(id uint) error
|
||||
PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error)
|
||||
|
||||
GetWafConfig(req request.WebsiteWafReq) (response.WebsiteWafConfig, error)
|
||||
UpdateWafConfig(req request.WebsiteWafUpdate) error
|
||||
UpdateWafFile(req request.WebsiteWafFileUpdate) (err error)
|
||||
|
||||
GetPHPConfig(id uint) (*response.PHPConfig, error)
|
||||
UpdatePHPConfig(req request.WebsitePHPConfigUpdate) error
|
||||
UpdatePHPConfigFile(req request.WebsitePHPFileUpdate) error
|
||||
@@ -119,7 +116,16 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
|
||||
}
|
||||
opts = append(opts, commonRepo.WithOrderRuleBy(req.OrderBy, req.Order))
|
||||
if req.Name != "" {
|
||||
opts = append(opts, websiteRepo.WithDomainLike(req.Name))
|
||||
domains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithDomainLike(req.Name))
|
||||
if len(domains) > 0 {
|
||||
var websiteIds []uint
|
||||
for _, domain := range domains {
|
||||
websiteIds = append(websiteIds, domain.WebsiteID)
|
||||
}
|
||||
opts = append(opts, websiteRepo.WithIDs(websiteIds))
|
||||
} else {
|
||||
opts = append(opts, websiteRepo.WithDomainLike(req.Name))
|
||||
}
|
||||
}
|
||||
if req.WebsiteGroupID != 0 {
|
||||
opts = append(opts, websiteRepo.WithGroupID(req.WebsiteGroupID))
|
||||
@@ -173,8 +179,6 @@ func (w WebsiteService) GetWebsites() ([]response.WebsiteDTO, error) {
|
||||
}
|
||||
|
||||
func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) {
|
||||
primaryDomainArray := strings.Split(create.PrimaryDomain, ":")
|
||||
primaryDomain := primaryDomainArray[0]
|
||||
alias := create.Alias
|
||||
if common.ContainsChinese(alias) {
|
||||
alias, err = common.PunycodeEncode(alias)
|
||||
@@ -208,7 +212,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
|
||||
defaultDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate)
|
||||
website := &model.Website{
|
||||
PrimaryDomain: primaryDomain,
|
||||
PrimaryDomain: create.PrimaryDomain,
|
||||
Type: create.Type,
|
||||
Alias: alias,
|
||||
Remark: create.Remark,
|
||||
@@ -318,12 +322,16 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
case constant.RuntimeNode:
|
||||
website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err = configDefaultNginx(website, domains, appInstall, runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = createWafConfig(website, domains); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, ctx := helper.GetTxAndContext()
|
||||
defer tx.Rollback()
|
||||
if err = websiteRepo.Create(ctx, website); err != nil {
|
||||
@@ -416,17 +424,18 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := delNginxConfig(website, req.ForceDelete); err != nil {
|
||||
if err = delNginxConfig(website, req.ForceDelete); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = delWafConfig(website, req.ForceDelete); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if checkIsLinkApp(website) && req.DeleteApp {
|
||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
if !reflect.DeepEqual(model.AppInstall{}, appInstall) {
|
||||
if err := deleteAppInstall(appInstall, true, req.ForceDelete, true); err != nil && !req.ForceDelete {
|
||||
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||
if appInstall.ID > 0 {
|
||||
if err = deleteAppInstall(appInstall, true, req.ForceDelete, true); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -485,6 +494,53 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data")
|
||||
fileOp := files.NewFileOp()
|
||||
if fileOp.Stat(wafDataPath) {
|
||||
websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json")
|
||||
content, err := fileOp.GetContent(websitesConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var websitesArray []request.WafWebsite
|
||||
if content != nil {
|
||||
if err := json.Unmarshal(content, &websitesArray); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for index, wafWebsite := range websitesArray {
|
||||
if wafWebsite.Key == website.Alias {
|
||||
wafSite := request.WafWebsite{
|
||||
Key: website.Alias,
|
||||
Domains: wafWebsite.Domains,
|
||||
Host: wafWebsite.Host,
|
||||
}
|
||||
for _, domain := range domainModels {
|
||||
wafSite.Domains = append(wafSite.Domains, domain.Domain)
|
||||
if domain.Port != 80 && domain.Port != 443 {
|
||||
wafSite.Host = append(wafSite.Host, domain.Domain+":"+string(rune(domain.Port)))
|
||||
}
|
||||
}
|
||||
if len(wafSite.Host) == 0 {
|
||||
wafSite.Host = []string{}
|
||||
}
|
||||
websitesArray[index] = wafSite
|
||||
break
|
||||
}
|
||||
}
|
||||
websitesContent, err := json.Marshal(websitesArray)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return domainModels, websiteDomainRepo.BatchCreate(context.TODO(), domainModels)
|
||||
}
|
||||
|
||||
@@ -514,6 +570,7 @@ func (w WebsiteService) DeleteWebsiteDomain(domainId uint) error {
|
||||
if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID), websiteDomainRepo.WithDomain(webSiteDomain.Domain)); len(oldDomains) == 1 {
|
||||
domains = append(domains, webSiteDomain.Domain)
|
||||
}
|
||||
|
||||
if len(ports) > 0 || len(domains) > 0 {
|
||||
stringBinds := make([]string, len(ports))
|
||||
for i := 0; i < len(ports); i++ {
|
||||
@@ -524,6 +581,63 @@ func (w WebsiteService) DeleteWebsiteDomain(domainId uint) error {
|
||||
}
|
||||
}
|
||||
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data")
|
||||
fileOp := files.NewFileOp()
|
||||
if fileOp.Stat(wafDataPath) {
|
||||
websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json")
|
||||
content, err := fileOp.GetContent(websitesConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var websitesArray []request.WafWebsite
|
||||
var newWebsitesArray []request.WafWebsite
|
||||
if content != nil {
|
||||
if err := json.Unmarshal(content, &websitesArray); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, wafWebsite := range websitesArray {
|
||||
if wafWebsite.Key == website.Alias {
|
||||
wafSite := wafWebsite
|
||||
oldDomains := wafSite.Domains
|
||||
var newDomains []string
|
||||
for _, domain := range oldDomains {
|
||||
if domain == webSiteDomain.Domain {
|
||||
continue
|
||||
}
|
||||
newDomains = append(newDomains, domain)
|
||||
}
|
||||
wafSite.Domains = newDomains
|
||||
oldHostArray := wafSite.Host
|
||||
var newHostArray []string
|
||||
for _, host := range oldHostArray {
|
||||
if host == webSiteDomain.Domain+":"+string(rune(webSiteDomain.Port)) {
|
||||
continue
|
||||
}
|
||||
newHostArray = append(newHostArray, host)
|
||||
}
|
||||
wafSite.Host = newHostArray
|
||||
if len(wafSite.Host) == 0 {
|
||||
wafSite.Host = []string{}
|
||||
}
|
||||
newWebsitesArray = append(newWebsitesArray, wafSite)
|
||||
} else {
|
||||
newWebsitesArray = append(newWebsitesArray, wafWebsite)
|
||||
}
|
||||
}
|
||||
websitesContent, err := json.Marshal(newWebsitesArray)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return websiteDomainRepo.DeleteBy(context.TODO(), commonRepo.WithByID(domainId))
|
||||
}
|
||||
|
||||
@@ -843,56 +957,6 @@ func (w WebsiteService) PreInstallCheck(req request.WebsiteInstallCheckReq) ([]r
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetWafConfig(req request.WebsiteWafReq) (response.WebsiteWafConfig, error) {
|
||||
var res response.WebsiteWafConfig
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
res.Enable = true
|
||||
if req.Key != "" {
|
||||
params, err := getNginxParamsByKeys(constant.NginxScopeServer, []string{"set"}, &website)
|
||||
if err != nil {
|
||||
return res, nil
|
||||
}
|
||||
for _, param := range params {
|
||||
if param.Params[0] == req.Key {
|
||||
res.Enable = len(param.Params) > 1 && param.Params[1] == "on"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nginxFull, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
filePath := path.Join(nginxFull.SiteDir, "sites", website.Alias, "waf", "rules", req.Rule+".json")
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return res, nil
|
||||
}
|
||||
res.Content = string(content)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateWafConfig(req request.WebsiteWafUpdate) error {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateValue := "on"
|
||||
if !req.Enable {
|
||||
updateValue = "off"
|
||||
}
|
||||
return updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{
|
||||
{Name: "set", Params: []string{req.Key, updateValue}},
|
||||
}, &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateNginxConfigFile(req request.WebsiteNginxUpdate) error {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
@@ -2330,23 +2394,6 @@ func (w WebsiteService) UpdateRedirectFile(req request.NginxRedirectUpdate) (err
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateWafFile(req request.WebsiteWafFileUpdate) (err error) {
|
||||
var (
|
||||
website model.Website
|
||||
nginxInstall model.AppInstall
|
||||
)
|
||||
website, err = websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rulePath := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "waf", "rules", fmt.Sprintf("%s.json", req.Type))
|
||||
return files.NewFileOp().WriteFile(rulePath, strings.NewReader(req.Content), 0755)
|
||||
}
|
||||
|
||||
func (w WebsiteService) LoadWebsiteDirConfig(req request.WebsiteCommonReq) (*response.WebsiteDirConfig, error) {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"gopkg.in/square/go-jose.v2/json"
|
||||
)
|
||||
|
||||
type WebsiteDnsAccountService struct {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
@@ -197,7 +199,7 @@ func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website,
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileOp.CopyDir(path.Join(nginxFolder, "www", "common", "waf", "rules"), path.Join(siteFolder, "waf"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime) error {
|
||||
@@ -233,12 +235,8 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
server.UpdateServerName(serverNames)
|
||||
|
||||
siteFolder := path.Join("/www", "sites", website.Alias)
|
||||
commonFolder := path.Join("/www", "common")
|
||||
server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log")})
|
||||
server.UpdateDirective("error_log", []string{path.Join(siteFolder, "log", "error.log")})
|
||||
server.UpdateDirective("access_by_lua_file", []string{path.Join(commonFolder, "waf", "access.lua")})
|
||||
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 {
|
||||
@@ -285,6 +283,99 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
return nil
|
||||
}
|
||||
|
||||
func createWafConfig(website *model.Website, domains []model.WebsiteDomain) error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data")
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(wafDataPath) {
|
||||
return nil
|
||||
}
|
||||
websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json")
|
||||
content, err := fileOp.GetContent(websitesConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var websitesArray []request.WafWebsite
|
||||
if len(content) != 0 {
|
||||
if err := json.Unmarshal(content, &websitesArray); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
wafWebsite := request.WafWebsite{
|
||||
Key: website.Alias,
|
||||
Domains: make([]string, 0),
|
||||
Host: make([]string, 0),
|
||||
}
|
||||
|
||||
for _, domain := range domains {
|
||||
wafWebsite.Domains = append(wafWebsite.Domains, domain.Domain)
|
||||
if domain.Port != 80 && domain.Port != 443 {
|
||||
wafWebsite.Host = append(wafWebsite.Host, domain.Domain+":"+string(rune(domain.Port)))
|
||||
}
|
||||
}
|
||||
websitesArray = append(websitesArray, wafWebsite)
|
||||
websitesContent, err := json.Marshal(websitesArray)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
sitesDir = path.Join(wafDataPath, "sites")
|
||||
defaultConfigPath = path.Join(wafDataPath, "conf", "siteConfig.json")
|
||||
defaultRuleDir = path.Join(wafDataPath, "rules")
|
||||
websiteDir = path.Join(sitesDir, website.Alias)
|
||||
)
|
||||
|
||||
defaultConfigContent, err := fileOp.GetContent(defaultConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !fileOp.Stat(websiteDir) {
|
||||
if err = fileOp.CreateDir(websiteDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = fileOp.DeleteDir(websiteDir)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = fileOp.SaveFileWithByte(path.Join(websiteDir, "config.json"), defaultConfigContent, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
websiteRuleDir := path.Join(websiteDir, "rules")
|
||||
if !fileOp.Stat(websiteRuleDir) {
|
||||
if err := fileOp.CreateDir(websiteRuleDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defaultRulesName := []string{"acl", "args", "cookie", "defaultUaBlack", "defaultUrlBlack", "fileExt", "header", "methodWhite"}
|
||||
for _, ruleName := range defaultRulesName {
|
||||
srcPath := path.Join(defaultRuleDir, ruleName+".json")
|
||||
if err = fileOp.Copy(srcPath, websiteRuleDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func delNginxConfig(website model.Website, force bool) error {
|
||||
nginxApp, err := appRepo.GetFirst(appRepo.WithKey(constant.AppOpenresty))
|
||||
if err != nil {
|
||||
@@ -310,6 +401,7 @@ func delNginxConfig(website model.Website, force bool) error {
|
||||
}
|
||||
sitePath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", website.Alias)
|
||||
if fileOp.Stat(sitePath) {
|
||||
xpack.RemoveTamper(website.Alias)
|
||||
_ = fileOp.DeleteDir(sitePath)
|
||||
}
|
||||
|
||||
@@ -322,6 +414,52 @@ func delNginxConfig(website model.Website, force bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func delWafConfig(website model.Website, force bool) error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data")
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(wafDataPath) {
|
||||
return nil
|
||||
}
|
||||
websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json")
|
||||
content, err := fileOp.GetContent(websitesConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var websitesArray []request.WafWebsite
|
||||
var newWebsiteArray []request.WafWebsite
|
||||
if len(content) > 0 {
|
||||
if err = json.Unmarshal(content, &websitesArray); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, wafWebsite := range websitesArray {
|
||||
if wafWebsite.Key != website.Alias {
|
||||
newWebsiteArray = append(newWebsiteArray, wafWebsite)
|
||||
}
|
||||
}
|
||||
websitesContent, err := json.Marshal(newWebsiteArray)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = fileOp.DeleteDir(path.Join(wafDataPath, "sites", website.Alias))
|
||||
|
||||
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
|
||||
if force {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addListenAndServerName(website model.Website, ports []int, domains []string) error {
|
||||
nginxFull, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
|
||||
@@ -47,10 +47,14 @@ func WithDetail(Key string, detail interface{}, err error) BusinessError {
|
||||
}
|
||||
|
||||
func WithErr(Key string, err error) BusinessError {
|
||||
paramMap := map[string]interface{}{}
|
||||
if err != nil {
|
||||
paramMap["err"] = err
|
||||
}
|
||||
return BusinessError{
|
||||
Msg: Key,
|
||||
Detail: "",
|
||||
Err: err,
|
||||
Msg: Key,
|
||||
Map: paramMap,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ type System struct {
|
||||
Entrance string `mapstructure:"entrance"`
|
||||
IsDemo bool `mapstructure:"is_demo"`
|
||||
AppRepo string `mapstructure:"app_repo"`
|
||||
ChangeUserInfo bool `mapstructure:"change_user_info"`
|
||||
ChangeUserInfo string `mapstructure:"change_user_info"`
|
||||
OneDriveID string `mapstructure:"one_drive_id"`
|
||||
OneDriveSc string `mapstructure:"one_drive_sc"`
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ const (
|
||||
Syncing = "Syncing"
|
||||
SyncSuccess = "SyncSuccess"
|
||||
Paused = "Paused"
|
||||
SyncErr = "SyncErr"
|
||||
|
||||
ContainerPrefix = "1Panel-"
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ const (
|
||||
CodeErrDomain = 409
|
||||
CodeErrInternalServer = 500
|
||||
CodeErrHeader = 406
|
||||
|
||||
CodeErrXpack = 410
|
||||
)
|
||||
|
||||
// internal
|
||||
@@ -43,6 +45,9 @@ var (
|
||||
ErrNameIsExist = "ErrNameIsExist"
|
||||
ErrDemoEnvironment = "ErrDemoEnvironment"
|
||||
ErrCmdIllegal = "ErrCmdIllegal"
|
||||
ErrXpackNotFound = "ErrXpackNotFound"
|
||||
ErrXpackNotActive = "ErrXpackNotActive"
|
||||
ErrXpackOutOfDate = "ErrXpackOutOfDate"
|
||||
)
|
||||
|
||||
// app
|
||||
@@ -150,3 +155,11 @@ var (
|
||||
var (
|
||||
ErrNotExistUser = "ErrNotExistUser"
|
||||
)
|
||||
|
||||
// license
|
||||
var (
|
||||
ErrLicense = "ErrLicense"
|
||||
ErrLicenseCheck = "ErrLicenseCheck"
|
||||
ErrLicenseSave = "ErrLicenseSave"
|
||||
ErrLicenseSync = "ErrLicenseSync"
|
||||
)
|
||||
|
||||
@@ -13,13 +13,14 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
DB *gorm.DB
|
||||
LOG *logrus.Logger
|
||||
CONF configs.ServerConfig
|
||||
VALID *validator.Validate
|
||||
SESSION *psession.PSession
|
||||
CACHE *badger_db.Cache
|
||||
Viper *viper.Viper
|
||||
DB *gorm.DB
|
||||
MonitorDB *gorm.DB
|
||||
LOG *logrus.Logger
|
||||
CONF configs.ServerConfig
|
||||
VALID *validator.Validate
|
||||
SESSION *psession.PSession
|
||||
CACHE *badger_db.Cache
|
||||
Viper *viper.Viper
|
||||
|
||||
Cron *cron.Cron
|
||||
MonitorCronID cron.EntryID
|
||||
|
||||
@@ -74,6 +74,7 @@ ErrFileDownloadDir: "Download folder not supported"
|
||||
ErrCmdNotFound: "{{ .name}} command does not exist, please install this command on the host first"
|
||||
ErrSourcePathNotFound: "Source directory does not exist"
|
||||
ErrFavoriteExist: "This path has been collected"
|
||||
ErrInvalidChar: "Illegal characters are prohibited"
|
||||
|
||||
#website
|
||||
ErrDomainIsExist: "Domain is already exist"
|
||||
@@ -144,7 +145,7 @@ ErrNodeModulesNotFound: "The node_modules folder does not exist! Please edit the
|
||||
#setting
|
||||
ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted."
|
||||
ErrBackupCheck: "Backup account test connection failed {{ .err}}"
|
||||
ErrOSSConn: "Unable to successfully request the latest version. Please check if the server can connect to the external network environment."
|
||||
ErrOSSConn: "Unable to retrieve the latest version, please check if the server can connect to the external network."
|
||||
ErrEntrance: "Security entrance information error. Please check and try again!"
|
||||
|
||||
#tool
|
||||
@@ -166,3 +167,23 @@ CutWebsiteLogSuccess: "{{ .name }} website log cut successfully, backup path {{
|
||||
#toolbox
|
||||
ErrNotExistUser: "The current user does not exist. Please modify and retry!"
|
||||
ErrBanAction: "Setting failed, the current {{ .name }} service is unavailable, please check and try again!"
|
||||
|
||||
#waf
|
||||
ErrScope: "Modification of this configuration is not supported"
|
||||
ErrStateChange: "State modification failed"
|
||||
ErrRuleExist: "Rule is Exist"
|
||||
ErrRuleNotExist: "Rule is not Exist"
|
||||
ErrParseIP: "IP format error"
|
||||
ErrDefaultIP: "default is a reserved name, please change it to another name"
|
||||
ErrGroupInUse: "The IP group is used by the black/white list and cannot be deleted"
|
||||
ErrGroupExist: "IP group name already exists"
|
||||
ErrIPRange: "Wrong IP range"
|
||||
|
||||
#license
|
||||
ErrLicense: "License format error, please check and try again!"
|
||||
ErrLicenseCheck: "License verification failed, please check and try again!"
|
||||
ErrLicenseSave: "Failed to save license information, error {{ .err }}, please try again!"
|
||||
ErrLicenseSync: "Failed to sync license information, no license information detected in the database!"
|
||||
ErrXpackNotFound: "This section is a professional edition feature, please import the license first in Panel Settings-License interface"
|
||||
ErrXpackNotActive: "This section is a professional edition feature, please synchronize the license status first in Panel Settings-License interface"
|
||||
ErrXpackOutOfDate: "The current license has expired, please re-import the license in Panel Settings-License interface"
|
||||
|
||||
@@ -23,6 +23,7 @@ ErrTypePortRange: '連接埠範圍需要在 1-65535 之間'
|
||||
Success: "成功"
|
||||
Failed: "失敗"
|
||||
SystemRestart: "系統重啟導致任務中斷"
|
||||
ErrInvalidChar: "禁止使用非法字元"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被佔用!"
|
||||
@@ -145,7 +146,7 @@ ErrNodeModulesNotFound: "node_modules 文件夾不存在!請編輯運行環境
|
||||
#setting
|
||||
ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除"
|
||||
ErrBackupCheck: "備份帳號測試連接失敗 {{ .err}}"
|
||||
ErrOSSConn: "無法成功請求最新版本,請檢查伺服器是否能夠連接到外部網絡環境。"
|
||||
ErrOSSConn: "無法獲取最新版本,請確認伺服器是否能夠連接外部網路。"
|
||||
ErrEntrance: "安全入口信息錯誤,請檢查後重試!"
|
||||
|
||||
#tool
|
||||
@@ -167,3 +168,23 @@ CutWebsiteLogSuccess: "{{ .name }} 網站日誌切割成功,備份路徑 {{ .p
|
||||
#toolbox
|
||||
ErrNotExistUser: "當前使用者不存在,請修改後重試!"
|
||||
ErrBanAction: "設置失敗,當前 {{ .name }} 服務不可用,請檢查後重試!"
|
||||
|
||||
#waf
|
||||
ErrScope: "不支援修改此配置"
|
||||
ErrStateChange: "狀態修改失敗"
|
||||
ErrRuleExist: "規則名稱已存在"
|
||||
ErrRuleNotExist: "規則不存在"
|
||||
ErrParseIP: "IP 格式錯誤"
|
||||
ErrDefaultIP: "default 為保留名稱,請更換其他名稱"
|
||||
ErrGroupInUse: "IP 群組被黑/白名單使用,無法刪除"
|
||||
ErrGroupExist: "IP 群組名稱已存在"
|
||||
ErrIPRange: "IP 範圍錯誤"
|
||||
|
||||
#license
|
||||
ErrLicense: "許可證格式錯誤,請檢查後重試!"
|
||||
ErrLicenseCheck: "許可證校驗失敗,請檢查後重試!"
|
||||
ErrLicenseSave: "許可證信息保存失敗,錯誤 {{ .err }}, 請重試!"
|
||||
ErrLicenseSync: "許可證信息同步失敗,資料庫中未檢測到許可證信息!"
|
||||
ErrXpackNotFound: "該部分為專業版功能,請先在 面板設置-許可證 界面導入許可證"
|
||||
ErrXpackNotActive: "該部分為專業版功能,請先在 面板設置-許可證 界面同步許可證狀態"
|
||||
ErrXpackOutOfDate: "當前許可證已過期,請重新在 面板設置-許可證 界面導入許可證"
|
||||
|
||||
@@ -74,6 +74,7 @@ ErrFileDownloadDir: "不支持下载文件夹"
|
||||
ErrCmdNotFound: "{{ .name}} 命令不存在,请先在宿主机安装此命令"
|
||||
ErrSourcePathNotFound: "源目录不存在"
|
||||
ErrFavoriteExist: "已收藏此路径"
|
||||
ErrInvalidChar: "禁止使用非法字符"
|
||||
|
||||
#website
|
||||
ErrDomainIsExist: "域名已存在"
|
||||
@@ -144,7 +145,7 @@ ErrNodeModulesNotFound: "node_modules 文件夹不存在!请编辑运行环境
|
||||
#setting
|
||||
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
|
||||
ErrBackupCheck: "备份账号测试连接失败 {{ .err}}"
|
||||
ErrOSSConn: "无法成功请求最新版本,请检查服务器是否能够连接到外部网络环境。"
|
||||
ErrOSSConn: "无法获取最新版本,请确认服务器是否能够连接外部网络。"
|
||||
ErrEntrance: "安全入口信息错误,请检查后重试!"
|
||||
|
||||
#tool
|
||||
@@ -165,4 +166,25 @@ CutWebsiteLogSuccess: "{{ .name }} 网站日志切割成功,备份路径 {{ .p
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "当前用户不存在,请修改后重试!"
|
||||
ErrBanAction: "设置失败,当前 {{ .name }} 服务不可用,请检查后重试!"
|
||||
ErrBanAction: "设置失败,当前 {{ .name }} 服务不可用,请检查后重试!"
|
||||
|
||||
#waf
|
||||
ErrScope: "不支持修改此配置"
|
||||
ErrStateChange: "状态修改失败"
|
||||
ErrRuleExist: "规则名称已存在"
|
||||
ErrRuleNotExist: "规则不存在"
|
||||
ErrParseIP: "IP 格式错误"
|
||||
ErrDefaultIP: "default 为保留名称,请更换其他名称"
|
||||
ErrGroupInUse: "IP 组被黑/白名单使用,无法删除"
|
||||
ErrGroupExist: "IP 组名称已存在"
|
||||
ErrIPRange: "IP 范围错误"
|
||||
|
||||
#license
|
||||
ErrLicense: "许可证格式错误,请检查后重试!"
|
||||
ErrLicenseCheck: "许可证校验失败,请检查后重试!"
|
||||
ErrLicenseSave: "许可证信息保存失败,错误 {{ .err }},请重试!"
|
||||
ErrLicenseSync: "许可证信息同步失败,数据库中未检测到许可证信息!"
|
||||
ErrXpackNotFound: "该部分为专业版功能,请先在 面板设置-许可证 界面导入许可证"
|
||||
ErrXpackNotActive: "该部分为专业版功能,请先在 面板设置-许可证 界面同步许可证状态"
|
||||
ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可证 界面导入许可证"
|
||||
|
||||
|
||||
+37
-5
@@ -4,14 +4,13 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
@@ -36,6 +35,7 @@ func Init() {
|
||||
Colorful: false,
|
||||
},
|
||||
)
|
||||
initMonitorDB(newLogger)
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(fullPath), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
@@ -47,7 +47,7 @@ func Init() {
|
||||
_ = db.Exec("PRAGMA journal_mode = WAL;")
|
||||
sqlDB, dbError := db.DB()
|
||||
if dbError != nil {
|
||||
panic(err)
|
||||
panic(dbError)
|
||||
}
|
||||
sqlDB.SetConnMaxIdleTime(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
@@ -56,3 +56,35 @@ func Init() {
|
||||
global.DB = db
|
||||
global.LOG.Info("init db successfully")
|
||||
}
|
||||
|
||||
func initMonitorDB(newLogger logger.Interface) {
|
||||
if _, err := os.Stat(global.CONF.System.DbPath); err != nil {
|
||||
if err := os.MkdirAll(global.CONF.System.DbPath, os.ModePerm); err != nil {
|
||||
panic(fmt.Errorf("init db dir failed, err: %v", err))
|
||||
}
|
||||
}
|
||||
fullPath := path.Join(global.CONF.System.DbPath, "monitor.db")
|
||||
if _, err := os.Stat(fullPath); err != nil {
|
||||
if _, err := os.Create(fullPath); err != nil {
|
||||
panic(fmt.Errorf("init db file failed, err: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(fullPath), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
Logger: newLogger,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sqlDB, dbError := db.DB()
|
||||
if dbError != nil {
|
||||
panic(dbError)
|
||||
}
|
||||
sqlDB.SetConnMaxIdleTime(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
|
||||
global.MonitorDB = db
|
||||
global.LOG.Info("init monitor db successfully")
|
||||
}
|
||||
|
||||
+40
-15
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
@@ -57,21 +58,7 @@ func Init() {
|
||||
global.LOG.Fatalf("init service before start failed, err: %v", err)
|
||||
}
|
||||
|
||||
if global.CONF.System.ChangeUserInfo {
|
||||
if err := settingRepo.Update("UserName", common.RandStrAndNum(10)); err != nil {
|
||||
global.LOG.Fatalf("init username before start failed, err: %v", err)
|
||||
}
|
||||
pass, _ := encrypt.StringEncrypt(common.RandStrAndNum(10))
|
||||
if err := settingRepo.Update("Password", pass); err != nil {
|
||||
global.LOG.Fatalf("init password before start failed, err: %v", err)
|
||||
}
|
||||
if err := settingRepo.Update("SecurityEntrance", common.RandStrAndNum(10)); err != nil {
|
||||
global.LOG.Fatalf("init entrance before start failed, err: %v", err)
|
||||
}
|
||||
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
_, _ = cmd.Execf("%s sed -i '/CHANGE_USER_INFO=true/d' /usr/local/bin/1pctl", sudo)
|
||||
}
|
||||
handleUserInfo(global.CONF.System.ChangeUserInfo, settingRepo)
|
||||
|
||||
handleCronjobStatus()
|
||||
handleSnapStatus()
|
||||
@@ -173,3 +160,41 @@ func loadLocalDir() {
|
||||
}
|
||||
global.LOG.Errorf("error type dir: %T", varMap["dir"])
|
||||
}
|
||||
|
||||
func handleUserInfo(tags string, settingRepo repo.ISettingRepo) {
|
||||
if len(tags) == 0 {
|
||||
return
|
||||
}
|
||||
if tags == "all" {
|
||||
if err := settingRepo.Update("UserName", common.RandStrAndNum(10)); err != nil {
|
||||
global.LOG.Fatalf("init username before start failed, err: %v", err)
|
||||
}
|
||||
pass, _ := encrypt.StringEncrypt(common.RandStrAndNum(10))
|
||||
if err := settingRepo.Update("Password", pass); err != nil {
|
||||
global.LOG.Fatalf("init password before start failed, err: %v", err)
|
||||
}
|
||||
if err := settingRepo.Update("SecurityEntrance", common.RandStrAndNum(10)); err != nil {
|
||||
global.LOG.Fatalf("init entrance before start failed, err: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if strings.Contains(global.CONF.System.ChangeUserInfo, "username") {
|
||||
if err := settingRepo.Update("UserName", common.RandStrAndNum(10)); err != nil {
|
||||
global.LOG.Fatalf("init username before start failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
if strings.Contains(global.CONF.System.ChangeUserInfo, "password") {
|
||||
pass, _ := encrypt.StringEncrypt(common.RandStrAndNum(10))
|
||||
if err := settingRepo.Update("Password", pass); err != nil {
|
||||
global.LOG.Fatalf("init password before start failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
if strings.Contains(global.CONF.System.ChangeUserInfo, "entrance") {
|
||||
if err := settingRepo.Update("SecurityEntrance", common.RandStrAndNum(10)); err != nil {
|
||||
global.LOG.Fatalf("init entrance before start failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
_, _ = cmd.Execf("%s sed -i '/CHANGE_USER_INFO=%v/d' /usr/local/bin/1pctl", sudo, global.CONF.System.ChangeUserInfo)
|
||||
}
|
||||
|
||||
@@ -72,6 +72,15 @@ func Init() {
|
||||
migrations.UpdateSnapshotRecords,
|
||||
|
||||
migrations.UpdateWebDavConf,
|
||||
|
||||
migrations.AddSnapshotIgnore,
|
||||
migrations.AddDatabaseIsDelete,
|
||||
migrations.AddXpackHideMenu,
|
||||
migrations.AddCronjobCommand,
|
||||
migrations.NewMonitorDB,
|
||||
migrations.AddNoAuthSetting,
|
||||
migrations.UpdateXpackHideMenu,
|
||||
migrations.AddMenuTabsSetting,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var AddSnapshotIgnore = &gormigrate.Migration{
|
||||
ID: "20240311-add-snapshot-ignore",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Create(&model.Setting{Key: "SnapshotIgnore", Value: "*.sock"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddDatabaseIsDelete = &gormigrate.Migration{
|
||||
ID: "20240314-add-database-is-delete",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.DatabaseMysql{}, &model.DatabasePostgresql{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddXpackHideMenu = &gormigrate.Migration{
|
||||
ID: "20240328-add-xpack-hide-menu",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Create(&model.Setting{Key: "XpackHideMenu", Value: "{\"id\":\"1\",\"label\":\"/xpack\",\"isCheck\":true,\"title\":\"xpack.menu\",\"children\":[{\"id\":\"2\",\"title\":\"xpack.waf.name\",\"path\":\"/xpack/waf/dashboard\",\"label\":\"Dashboard\",\"isCheck\":true},{\"id\":\"3\",\"title\":\"xpack.tamper.tamper\",\"path\":\"/xpack/tamper\",\"label\":\"Tamper\",\"isCheck\":true},{\"id\":\"4\",\"title\":\"xpack.setting.setting\",\"path\":\"/xpack/setting\",\"label\":\"XSetting\",\"isCheck\":true}]}"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddCronjobCommand = &gormigrate.Migration{
|
||||
ID: "20240403-add-cronjob-command",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Cronjob{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var NewMonitorDB = &gormigrate.Migration{
|
||||
ID: "20240408-new-monitor-db",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
var (
|
||||
bases []model.MonitorBase
|
||||
ios []model.MonitorIO
|
||||
networks []model.MonitorNetwork
|
||||
)
|
||||
_ = tx.Find(&bases).Error
|
||||
_ = tx.Find(&ios).Error
|
||||
_ = tx.Find(&networks).Error
|
||||
|
||||
if err := global.MonitorDB.AutoMigrate(&model.MonitorBase{}, &model.MonitorIO{}, &model.MonitorNetwork{}); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = global.MonitorDB.Exec("DELETE FROM monitor_bases").Error
|
||||
_ = global.MonitorDB.Exec("DELETE FROM monitor_ios").Error
|
||||
_ = global.MonitorDB.Exec("DELETE FROM monitor_networks").Error
|
||||
|
||||
if len(bases) != 0 {
|
||||
for i := 0; i <= len(bases)/200; i++ {
|
||||
var itemData []model.MonitorBase
|
||||
if 200*(i+1) <= len(bases) {
|
||||
itemData = bases[200*i : 200*(i+1)]
|
||||
} else {
|
||||
itemData = bases[200*i:]
|
||||
}
|
||||
if len(itemData) != 0 {
|
||||
if err := global.MonitorDB.Create(&itemData).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(ios) != 0 {
|
||||
for i := 0; i <= len(ios)/200; i++ {
|
||||
var itemData []model.MonitorIO
|
||||
if 200*(i+1) <= len(ios) {
|
||||
itemData = ios[200*i : 200*(i+1)]
|
||||
} else {
|
||||
itemData = ios[200*i:]
|
||||
}
|
||||
if len(itemData) != 0 {
|
||||
if err := global.MonitorDB.Create(&itemData).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(networks) != 0 {
|
||||
for i := 0; i <= len(networks)/200; i++ {
|
||||
var itemData []model.MonitorNetwork
|
||||
if 200*(i+1) <= len(networks) {
|
||||
itemData = networks[200*i : 200*(i+1)]
|
||||
} else {
|
||||
itemData = networks[200*i:]
|
||||
}
|
||||
if len(itemData) != 0 {
|
||||
if err := global.MonitorDB.Create(&itemData).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddNoAuthSetting = &gormigrate.Migration{
|
||||
ID: "20240328-add-no-auth-setting",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Create(&model.Setting{Key: "NoAuthSetting", Value: "200"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateXpackHideMenu = &gormigrate.Migration{
|
||||
ID: "20240411-update-xpack-hide-menu",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&model.Setting{}).Where("key", "XpackHideMenu").Updates(map[string]interface{}{"value": "{\"id\":\"1\",\"label\":\"/xpack\",\"isCheck\":true,\"title\":\"xpack.menu\",\"children\":[{\"id\":\"2\",\"title\":\"xpack.waf.name\",\"path\":\"/xpack/waf/dashboard\",\"label\":\"Dashboard\",\"isCheck\":true},{\"id\":\"3\",\"title\":\"xpack.tamper.tamper\",\"path\":\"/xpack/tamper\",\"label\":\"Tamper\",\"isCheck\":true},{\"id\":\"4\",\"title\":\"xpack.gpu.gpu\",\"path\":\"/xpack/gpu\",\"label\":\"GPU\",\"isCheck\":true},{\"id\":\"5\",\"title\":\"xpack.setting.setting\",\"path\":\"/xpack/setting\",\"label\":\"XSetting\",\"isCheck\":true}]}"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddMenuTabsSetting = &gormigrate.Migration{
|
||||
ID: "20240415-add-menu-tabs-setting",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Create(&model.Setting{Key: "MenuTabs", Value: "disable"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -124,6 +124,12 @@ var AddTablePHPExtensions = &gormigrate.Migration{
|
||||
if err := tx.Create(&model.PHPExtensions{Name: "默认", Extensions: "bcmath,gd,gettext,intl,pcntl,shmop,soap,sockets,sysvsem,xmlrpc,zip"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.PHPExtensions{Name: "苹果CMS-V10", Extensions: "mysqli,pdo_mysql,zip,gd,redis,memcache,memcached"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.PHPExtensions{Name: "SeaCMS", Extensions: "mysqli,pdo_mysql,gd,curl"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"github.com/1Panel-dev/1Panel/backend/middleware"
|
||||
@@ -12,7 +15,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerfiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -21,6 +23,7 @@ var (
|
||||
|
||||
func setWebStatic(rootRouter *gin.RouterGroup) {
|
||||
rootRouter.StaticFS("/public", http.FS(web.Favicon))
|
||||
rootRouter.Static("/api/v1/images", "./uploads")
|
||||
rootRouter.Use(func(c *gin.Context) {
|
||||
c.Next()
|
||||
})
|
||||
@@ -61,6 +64,8 @@ func Routers() *gin.Engine {
|
||||
PublicGroup.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(200, "ok")
|
||||
})
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
PublicGroup.GET("/api/v1/respagecode", baseApi.GetResponsePage)
|
||||
PublicGroup.Use(gzip.Gzip(gzip.DefaultCompression))
|
||||
setWebStatic(PublicGroup)
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func Init() {
|
||||
global.CONF.System.Username = username
|
||||
global.CONF.System.Password = password
|
||||
global.CONF.System.Entrance = entrance
|
||||
global.CONF.System.ChangeUserInfo = loadChange()
|
||||
global.CONF.System.ChangeUserInfo = loadChangeInfo()
|
||||
global.Viper = v
|
||||
}
|
||||
|
||||
@@ -112,10 +112,10 @@ func loadParams(param string) string {
|
||||
return info
|
||||
}
|
||||
|
||||
func loadChange() bool {
|
||||
func loadChangeInfo() string {
|
||||
stdout, err := cmd.Exec("grep '^CHANGE_USER_INFO=' /usr/bin/1pctl | cut -d'=' -f2")
|
||||
if err != nil {
|
||||
return false
|
||||
return ""
|
||||
}
|
||||
return stdout == "true\n"
|
||||
return strings.ReplaceAll(stdout, "\n", "")
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ func commonGroups() []CommonRouter {
|
||||
&DashboardRouter{},
|
||||
&HostRouter{},
|
||||
&ContainerRouter{},
|
||||
&MonitorRouter{},
|
||||
&LogRouter{},
|
||||
&FileRouter{},
|
||||
&ToolboxRouter{},
|
||||
|
||||
@@ -35,6 +35,11 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
hostRouter.POST("/firewall/update/addr", baseApi.UpdateAddrRule)
|
||||
hostRouter.POST("/firewall/update/description", baseApi.UpdateFirewallDescription)
|
||||
|
||||
hostRouter.POST("/monitor/search", baseApi.LoadMonitor)
|
||||
hostRouter.POST("/monitor/clean", baseApi.CleanMonitor)
|
||||
hostRouter.GET("/monitor/netoptions", baseApi.GetNetworkOptions)
|
||||
hostRouter.GET("/monitor/iooptions", baseApi.GetIOOptions)
|
||||
|
||||
hostRouter.GET("/ssh/conf", baseApi.LoadSSHConf)
|
||||
hostRouter.POST("/ssh/search", baseApi.GetSSHInfo)
|
||||
hostRouter.POST("/ssh/update", baseApi.UpdateSSH)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||
"github.com/1Panel-dev/1Panel/backend/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type MonitorRouter struct{}
|
||||
|
||||
func (s *MonitorRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
monitorRouter := Router.Group("monitors").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
monitorRouter.POST("/search", baseApi.LoadMonitor)
|
||||
monitorRouter.GET("/netoptions", baseApi.GetNetworkOptions)
|
||||
monitorRouter.GET("/iooptions", baseApi.GetIOOptions)
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ func (r *RuntimeRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
groupRouter.POST("/del", baseApi.DeleteRuntime)
|
||||
groupRouter.POST("/update", baseApi.UpdateRuntime)
|
||||
groupRouter.GET("/:id", baseApi.GetRuntime)
|
||||
groupRouter.POST("/sync", baseApi.SyncStatus)
|
||||
|
||||
groupRouter.POST("/node/package", baseApi.GetNodePackageRunScript)
|
||||
groupRouter.POST("/operate", baseApi.OperateRuntime)
|
||||
|
||||
@@ -23,13 +23,13 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
settingRouter.GET("/search/available", baseApi.GetSystemAvailable)
|
||||
settingRouter.POST("/update", baseApi.UpdateSetting)
|
||||
settingRouter.GET("/interface", baseApi.LoadInterfaceAddr)
|
||||
settingRouter.POST("/menu/update", baseApi.UpdateMenu)
|
||||
settingRouter.POST("/bind/update", baseApi.UpdateBindInfo)
|
||||
settingRouter.POST("/port/update", baseApi.UpdatePort)
|
||||
settingRouter.POST("/ssl/update", baseApi.UpdateSSL)
|
||||
settingRouter.GET("/ssl/info", baseApi.LoadFromCert)
|
||||
settingRouter.POST("/ssl/download", baseApi.DownloadSSL)
|
||||
settingRouter.POST("/password/update", baseApi.UpdatePassword)
|
||||
settingRouter.POST("/monitor/clean", baseApi.CleanMonitor)
|
||||
settingRouter.POST("/mfa", baseApi.LoadMFA)
|
||||
settingRouter.POST("/mfa/bind", baseApi.MFABind)
|
||||
|
||||
|
||||
@@ -39,10 +39,6 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
groupRouter.GET("/:id/https", baseApi.GetHTTPSConfig)
|
||||
groupRouter.POST("/:id/https", baseApi.UpdateHTTPSConfig)
|
||||
|
||||
groupRouter.POST("/waf/config", baseApi.GetWebsiteWafConfig)
|
||||
groupRouter.POST("/waf/update", baseApi.UpdateWebsiteWafConfig)
|
||||
groupRouter.POST("/waf/file/update", baseApi.UpdateWebsiteWafFile)
|
||||
|
||||
groupRouter.GET("/php/config/:id", baseApi.GetWebsitePHPConfig)
|
||||
groupRouter.POST("/php/config", baseApi.UpdateWebsitePHPConfig)
|
||||
groupRouter.POST("/php/update", baseApi.UpdatePHPFile)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
)
|
||||
|
||||
type localClient struct {
|
||||
@@ -54,9 +54,8 @@ func (c localClient) Upload(src, target string) (bool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
stdout, err := cmd.Execf("\\cp -f %s %s", src, path.Join(c.dir, target))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cp file failed, stdout: %v, err: %v", stdout, err)
|
||||
if err := common.CopyFile(src, target); err != nil {
|
||||
return false, fmt.Errorf("cp file failed, err: %v", err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -73,9 +72,8 @@ func (c localClient) Download(src, target string) (bool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
stdout, err := cmd.Execf("\\cp -f %s %s", localPath, target)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cp file failed, stdout: %v, err: %v", stdout, err)
|
||||
if err := common.CopyFile(localPath, target); err != nil {
|
||||
return false, fmt.Errorf("cp file failed, err: %v", err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -120,10 +120,12 @@ func (m minIoClient) Download(src, target string) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer object.Close()
|
||||
localFile, err := os.Create(target)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer localFile.Close()
|
||||
if _, err = io.Copy(localFile, object); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
odsdk "github.com/goh-chunlin/go-onedrive/onedrive"
|
||||
@@ -321,7 +323,6 @@ func (o *oneDriveClient) upSmall(srcPath, folderID string, fileSize int64) (bool
|
||||
if err := o.client.Do(context.Background(), req, false, &response); err != nil {
|
||||
return false, fmt.Errorf("do request for list failed, err: %v", err)
|
||||
}
|
||||
fmt.Println(response)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -362,7 +363,13 @@ func (o *oneDriveClient) upBig(ctx context.Context, srcPath, folderID string, fi
|
||||
splitCount += 1
|
||||
}
|
||||
bfReader := bufio.NewReader(file)
|
||||
var fileUploadResp *UploadSessionUploadResponse
|
||||
httpClient := http.Client{
|
||||
Timeout: time.Minute * 10,
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
for splitNow := int64(0); splitNow < splitCount; splitNow++ {
|
||||
length, err := bfReader.Read(buffer)
|
||||
if err != nil {
|
||||
@@ -376,12 +383,15 @@ func (o *oneDriveClient) upBig(ctx context.Context, srcPath, folderID string, fi
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := o.client.Do(ctx, sessionFileUploadReq, false, &fileUploadResp); err != nil {
|
||||
res, err := httpClient.Do(sessionFileUploadReq)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if fileUploadResp.Id == "" {
|
||||
return false, errors.New("something went wrong. file upload incomplete. consider upload the file in a step-by-step manner")
|
||||
if res.StatusCode != 201 && res.StatusCode != 202 && res.StatusCode != 200 {
|
||||
data, _ := io.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
return false, errors.New(string(data))
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -177,7 +177,8 @@ func CheckIllegal(args ...string) bool {
|
||||
for _, arg := range args {
|
||||
if strings.Contains(arg, "&") || strings.Contains(arg, "|") || strings.Contains(arg, ";") ||
|
||||
strings.Contains(arg, "$") || strings.Contains(arg, "'") || strings.Contains(arg, "`") ||
|
||||
strings.Contains(arg, "(") || strings.Contains(arg, ")") || strings.Contains(arg, "\"") {
|
||||
strings.Contains(arg, "(") || strings.Contains(arg, ")") || strings.Contains(arg, "\"") ||
|
||||
strings.Contains(arg, "\n") || strings.Contains(arg, "\r") || strings.Contains(arg, ">") || strings.Contains(arg, "<") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"io"
|
||||
mathRand "math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
@@ -43,6 +45,35 @@ func CompareVersion(version1, version2 string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func ComparePanelVersion(version1, version2 string) bool {
|
||||
if version1 == version2 {
|
||||
return false
|
||||
}
|
||||
version1s := strings.Split(version1, ".")
|
||||
version2s := strings.Split(version2, ".")
|
||||
|
||||
if len(version2s) > len(version1s) {
|
||||
for i := 0; i < len(version2s)-len(version1s); i++ {
|
||||
version1s = append(version1s, "0")
|
||||
}
|
||||
}
|
||||
if len(version1s) > len(version2s) {
|
||||
for i := 0; i < len(version1s)-len(version2s); i++ {
|
||||
version2s = append(version2s, "0")
|
||||
}
|
||||
}
|
||||
|
||||
n := min(len(version1s), len(version2s))
|
||||
for i := 0; i < n; i++ {
|
||||
if version1s[i] == version2s[i] {
|
||||
continue
|
||||
} else {
|
||||
return version1s[i] > version2s[i]
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func extractNumbers(version string) []string {
|
||||
var numbers []string
|
||||
start := -1
|
||||
@@ -82,6 +113,36 @@ func GetSortedVersions(versions []string) []string {
|
||||
return versions
|
||||
}
|
||||
|
||||
func CopyFile(src, dst string) error {
|
||||
source, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
if path.Base(src) != path.Base(dst) {
|
||||
dst = path.Join(dst, path.Base(src))
|
||||
}
|
||||
if _, err := os.Stat(path.Dir(dst)); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
_ = os.MkdirAll(path.Dir(dst), os.ModePerm)
|
||||
}
|
||||
}
|
||||
target, err := os.OpenFile(dst+"_temp", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer target.Close()
|
||||
|
||||
if _, err = io.Copy(target, source); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = os.Rename(dst+"_temp", dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsCrossVersion(version1, version2 string) bool {
|
||||
version1s := strings.Split(version1, ".")
|
||||
version2s := strings.Split(version2, ".")
|
||||
|
||||
@@ -57,6 +57,13 @@ func (f FileOp) CreateDir(dst string, mode fs.FileMode) error {
|
||||
return f.Fs.MkdirAll(dst, mode)
|
||||
}
|
||||
|
||||
func (f FileOp) CreateDirWithMode(dst string, mode fs.FileMode) error {
|
||||
if err := f.Fs.MkdirAll(dst, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.ChmodRWithMode(dst, mode, true)
|
||||
}
|
||||
|
||||
func (f FileOp) CreateFile(dst string) error {
|
||||
if _, err := f.Fs.Create(dst); err != nil {
|
||||
return err
|
||||
@@ -64,6 +71,14 @@ func (f FileOp) CreateFile(dst string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FileOp) CreateFileWithMode(dst string, mode fs.FileMode) error {
|
||||
file, err := f.Fs.OpenFile(dst, os.O_CREATE, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return file.Close()
|
||||
}
|
||||
|
||||
func (f FileOp) LinkFile(source string, dst string, isSymlink bool) error {
|
||||
if isSymlink {
|
||||
osFs := afero.OsFs{}
|
||||
@@ -126,6 +141,21 @@ func (f FileOp) SaveFile(dst string, content string, mode fs.FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FileOp) SaveFileWithByte(dst string, content []byte, mode fs.FileMode) error {
|
||||
if !f.Stat(path.Dir(dst)) {
|
||||
_ = f.CreateDir(path.Dir(dst), mode.Perm())
|
||||
}
|
||||
file, err := f.Fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.Write(content)
|
||||
write.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FileOp) ChownR(dst string, uid string, gid string, sub bool) error {
|
||||
cmdStr := fmt.Sprintf(`chown %s:%s "%s"`, uid, gid, dst)
|
||||
if sub {
|
||||
@@ -134,7 +164,7 @@ func (f FileOp) ChownR(dst string, uid string, gid string, sub bool) error {
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
cmdStr = fmt.Sprintf("sudo %s", cmdStr)
|
||||
}
|
||||
if msg, err := cmd.ExecWithTimeOut(cmdStr, 2*time.Second); err != nil {
|
||||
if msg, err := cmd.ExecWithTimeOut(cmdStr, 10*time.Second); err != nil {
|
||||
if msg != "" {
|
||||
return errors.New(msg)
|
||||
}
|
||||
@@ -151,7 +181,24 @@ func (f FileOp) ChmodR(dst string, mode int64, sub bool) error {
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
cmdStr = fmt.Sprintf("sudo %s", cmdStr)
|
||||
}
|
||||
if msg, err := cmd.ExecWithTimeOut(cmdStr, 2*time.Second); err != nil {
|
||||
if msg, err := cmd.ExecWithTimeOut(cmdStr, 10*time.Second); err != nil {
|
||||
if msg != "" {
|
||||
return errors.New(msg)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FileOp) ChmodRWithMode(dst string, mode fs.FileMode, sub bool) error {
|
||||
cmdStr := fmt.Sprintf(`chmod %v "%s"`, fmt.Sprintf("%o", mode.Perm()), dst)
|
||||
if sub {
|
||||
cmdStr = fmt.Sprintf(`chmod -R %v "%s"`, fmt.Sprintf("%o", mode.Perm()), dst)
|
||||
}
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
cmdStr = fmt.Sprintf("sudo %s", cmdStr)
|
||||
}
|
||||
if msg, err := cmd.ExecWithTimeOut(cmdStr, 10*time.Second); err != nil {
|
||||
if msg != "" {
|
||||
return errors.New(msg)
|
||||
}
|
||||
@@ -299,7 +346,7 @@ func (f FileOp) Cut(oldPaths []string, dst, name string, cover bool) error {
|
||||
coverFlag = "-f"
|
||||
}
|
||||
|
||||
cmdStr := fmt.Sprintf(`mv %s "%s" "%s"`, coverFlag, p, dstPath)
|
||||
cmdStr := fmt.Sprintf(`mv %s '%s' '%s'`, coverFlag, p, dstPath)
|
||||
if err := cmd.ExecCmd(cmdStr); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -308,7 +355,7 @@ func (f FileOp) Cut(oldPaths []string, dst, name string, cover bool) error {
|
||||
}
|
||||
|
||||
func (f FileOp) Mv(oldPath, dstPath string) error {
|
||||
cmdStr := fmt.Sprintf(`mv "%s" "%s"`, oldPath, dstPath)
|
||||
cmdStr := fmt.Sprintf(`mv '%s' '%s'`, oldPath, dstPath)
|
||||
if err := cmd.ExecCmd(cmdStr); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -362,13 +409,13 @@ func (f FileOp) CopyAndReName(src, dst, name string, cover bool) error {
|
||||
if name != "" && !cover {
|
||||
dstPath = filepath.Join(dst, name)
|
||||
}
|
||||
return cmd.ExecCmd(fmt.Sprintf(`cp -rf "%s" "%s"`, src, dstPath))
|
||||
return cmd.ExecCmd(fmt.Sprintf(`cp -rf '%s' '%s'`, src, dstPath))
|
||||
} else {
|
||||
dstPath := filepath.Join(dst, name)
|
||||
if cover {
|
||||
dstPath = dst
|
||||
}
|
||||
return cmd.ExecCmd(fmt.Sprintf(`cp -f "%s" "%s"`, src, dstPath))
|
||||
return cmd.ExecCmd(fmt.Sprintf(`cp -f '%s' '%s'`, src, dstPath))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,12 +428,12 @@ func (f FileOp) CopyDir(src, dst string) error {
|
||||
if err = f.Fs.MkdirAll(dstDir, srcInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
return cmd.ExecCmd(fmt.Sprintf(`cp -rf "%s" "%s"`, src, dst+"/"))
|
||||
return cmd.ExecCmd(fmt.Sprintf(`cp -rf '%s' '%s'`, src, dst+"/"))
|
||||
}
|
||||
|
||||
func (f FileOp) CopyFile(src, dst string) error {
|
||||
dst = filepath.Clean(dst) + string(filepath.Separator)
|
||||
return cmd.ExecCmd(fmt.Sprintf(`cp -f "%s" "%s"`, src, dst+"/"))
|
||||
return cmd.ExecCmd(fmt.Sprintf(`cp -f '%s' '%s'`, src, dst+"/"))
|
||||
}
|
||||
|
||||
func (f FileOp) GetDirSize(path string) (float64, error) {
|
||||
|
||||
@@ -311,20 +311,22 @@ func (f *FileInfo) listChildren(option FileOption) error {
|
||||
}
|
||||
|
||||
func (f *FileInfo) getContent() error {
|
||||
if f.Size <= 10*1024*1024 {
|
||||
afs := &afero.Afero{Fs: f.Fs}
|
||||
cByte, err := afs.ReadFile(f.Path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(cByte) > 0 && DetectBinary(cByte) {
|
||||
return buserr.New(constant.ErrFileCanNotRead)
|
||||
}
|
||||
f.Content = string(cByte)
|
||||
return nil
|
||||
} else {
|
||||
if IsBlockDevice(f.FileMode) {
|
||||
return buserr.New(constant.ErrFileCanNotRead)
|
||||
}
|
||||
if f.Size > 10*1024*1024 {
|
||||
return buserr.New("ErrFileToLarge")
|
||||
}
|
||||
afs := &afero.Afero{Fs: f.Fs}
|
||||
cByte, err := afs.ReadFile(f.Path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(cByte) > 0 && DetectBinary(cByte) {
|
||||
return buserr.New(constant.ErrFileCanNotRead)
|
||||
}
|
||||
f.Content = string(cByte)
|
||||
return nil
|
||||
}
|
||||
|
||||
func DetectBinary(buf []byte) bool {
|
||||
|
||||
@@ -2,6 +2,7 @@ package files
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/spf13/afero"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -16,6 +18,10 @@ func IsSymlink(mode os.FileMode) bool {
|
||||
return mode&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
func IsBlockDevice(mode os.FileMode) bool {
|
||||
return mode&os.ModeDevice != 0 && mode&os.ModeCharDevice == 0
|
||||
}
|
||||
|
||||
func GetMimeType(path string) string {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
@@ -113,3 +119,33 @@ func ReadFileByLine(filename string, page, pageSize int) ([]string, bool, error)
|
||||
|
||||
return lines, isEndOfFile, nil
|
||||
}
|
||||
|
||||
func GetParentMode(path string) (os.FileMode, error) {
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for {
|
||||
fileInfo, err := os.Stat(absPath)
|
||||
if err == nil {
|
||||
return fileInfo.Mode() & os.ModePerm, nil
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
parentDir := filepath.Dir(absPath)
|
||||
if parentDir == absPath {
|
||||
return 0, fmt.Errorf("no existing directory found in the path: %s", path)
|
||||
}
|
||||
absPath = parentDir
|
||||
}
|
||||
}
|
||||
|
||||
func IsInvalidChar(name string) bool {
|
||||
if strings.Contains(name, "&") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ parsingloop:
|
||||
case p.curTokenIs(flag.LuaCode):
|
||||
context.IsLuaBlock = true
|
||||
context.LiteralCode = p.currentToken.Literal
|
||||
case p.curTokenIs(flag.Keyword):
|
||||
case p.curTokenIs(flag.Keyword) || p.curTokenIs(flag.QuotedString):
|
||||
s, err := p.parseStatement()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package ps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
"github.com/shirou/gopsutil/v3/process"
|
||||
)
|
||||
|
||||
func TestPs(t *testing.T) {
|
||||
processes, err := process.Processes()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, pro := range processes {
|
||||
var (
|
||||
name string
|
||||
parentID int32
|
||||
userName string
|
||||
status string
|
||||
startTime string
|
||||
numThreads int32
|
||||
numConnections int
|
||||
cpuPercent float64
|
||||
//mem string
|
||||
rss string
|
||||
ioRead string
|
||||
ioWrite string
|
||||
)
|
||||
name, _ = pro.Name()
|
||||
parentID, _ = pro.Ppid()
|
||||
userName, _ = pro.Username()
|
||||
array, err := pro.Status()
|
||||
if err == nil {
|
||||
status = array[0]
|
||||
}
|
||||
createTime, err := pro.CreateTime()
|
||||
if err == nil {
|
||||
t := time.Unix(createTime/1000, 0)
|
||||
startTime = t.Format("2006-1-2 15:04:05")
|
||||
}
|
||||
numThreads, _ = pro.NumThreads()
|
||||
connections, err := pro.Connections()
|
||||
if err == nil && len(connections) > 0 {
|
||||
numConnections = len(connections)
|
||||
}
|
||||
cpuPercent, _ = pro.CPUPercent()
|
||||
menInfo, err := pro.MemoryInfo()
|
||||
if err == nil {
|
||||
rssF := float64(menInfo.RSS) / 1048576
|
||||
rss = fmt.Sprintf("%.2f", rssF)
|
||||
}
|
||||
ioStat, err := pro.IOCounters()
|
||||
if err == nil {
|
||||
ioWrite = strconv.FormatUint(ioStat.WriteBytes, 10)
|
||||
ioRead = strconv.FormatUint(ioStat.ReadBytes, 10)
|
||||
}
|
||||
|
||||
cmdLine, err := pro.Cmdline()
|
||||
if err == nil {
|
||||
fmt.Println(cmdLine)
|
||||
}
|
||||
ss, err := pro.Terminal()
|
||||
if err == nil {
|
||||
fmt.Println(ss)
|
||||
}
|
||||
|
||||
fmt.Printf("Name: %s PId: %v ParentID: %v Username: %v status:%s startTime: %s numThreads: %v numConnections:%v cpuPercent:%v rss:%s MB IORead: %s IOWrite: %s \n",
|
||||
name, pro.Pid, parentID, userName, status, startTime, numThreads, numConnections, cpuPercent, rss, ioRead, ioWrite)
|
||||
}
|
||||
users, err := host.Users()
|
||||
if err == nil {
|
||||
fmt.Println(users)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,457 +0,0 @@
|
||||
package ssl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme/api"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
|
||||
"log"
|
||||
)
|
||||
|
||||
type AppList struct {
|
||||
Version string `json:"version"`
|
||||
Tags []Tag `json:"tags"`
|
||||
Items []AppDefine `json:"items"`
|
||||
}
|
||||
|
||||
type NewAppDefine struct {
|
||||
Name string `yaml:"name"`
|
||||
Tags []string `yaml:"tags"`
|
||||
Title string `yaml:"title"`
|
||||
Type string `yaml:"type"`
|
||||
Description string `yaml:"description"`
|
||||
AdditionalProperties AppDefine `yaml:"additionalProperties"`
|
||||
}
|
||||
|
||||
type NewAppConfig struct {
|
||||
AdditionalProperties map[string]interface{} `yaml:"additionalProperties"`
|
||||
}
|
||||
|
||||
type AppDefine struct {
|
||||
Key string `json:"key" yaml:"key"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Tags []string `json:"tags" yaml:"tags"`
|
||||
Versions []string `json:"versions" yaml:"-"`
|
||||
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh"`
|
||||
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn"`
|
||||
Type string `json:"type" yaml:"type"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"`
|
||||
Limit int `json:"limit" yaml:"limit"`
|
||||
Recommend int `json:"recommend" yaml:"recommend"`
|
||||
Website string `json:"website" yaml:"website"`
|
||||
Github string `json:"github" yaml:"github"`
|
||||
Document string `json:"document" yaml:"document"`
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Key string `json:"key" yaml:"key"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
}
|
||||
|
||||
func getTagName(key string, tags []Tag) string {
|
||||
result := "应用"
|
||||
for _, tag := range tags {
|
||||
if tag.Key == key {
|
||||
return tag.Name
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TestAppToV2(t *testing.T) {
|
||||
oldDir := "/Users/wangzhengkun/projects/github.com/1Panel-dev/appstore/apps"
|
||||
newDir := "/Users/wangzhengkun/projects/github.com/1Panel-dev/appstore/apps_new"
|
||||
listJsonDir := path.Join(oldDir, "list.json")
|
||||
fileOp := files.NewFileOp()
|
||||
content, err := fileOp.GetContent(listJsonDir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
appList := &AppList{}
|
||||
if err = json.Unmarshal(content, appList); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, appDefine := range appList.Items {
|
||||
newAppDefine := &NewAppDefine{
|
||||
Name: appDefine.Name,
|
||||
Tags: []string{getTagName(appDefine.Tags[0], appList.Tags)},
|
||||
Type: getTagName(appDefine.Tags[0], appList.Tags),
|
||||
Title: appDefine.ShortDescZh,
|
||||
Description: appDefine.ShortDescZh,
|
||||
AdditionalProperties: appDefine,
|
||||
}
|
||||
|
||||
yamlContent, err := yaml.Marshal(newAppDefine)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
oldAppDir := oldDir + "/" + appDefine.Key
|
||||
newAppDir := newDir + "/" + appDefine.Key
|
||||
if !fileOp.Stat(newAppDir) {
|
||||
if err := fileOp.CreateDir(newAppDir, 0755); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// logo
|
||||
oldLogoPath := oldAppDir + "/metadata/logo.png"
|
||||
if err := fileOp.CopyFile(oldLogoPath, newAppDir); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, version := range appDefine.Versions {
|
||||
oldVersionDir := oldAppDir + "/versions/" + version
|
||||
if err := fileOp.CopyDir(oldVersionDir, newAppDir); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
oldConfigPath := oldVersionDir + "/config.json"
|
||||
configContent, err := fileOp.GetContent(oldConfigPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(configContent, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
newConfigD := &NewAppConfig{}
|
||||
newConfigD.AdditionalProperties = result
|
||||
configYamlByte, err := yaml.Marshal(newConfigD)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
newVersionDir := newAppDir + "/" + version
|
||||
if err := fileOp.WriteFile(newVersionDir+"/data.yml", bytes.NewReader(configYamlByte), 0755); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := fileOp.WriteFile(newAppDir+"/data.yml", bytes.NewReader(yamlContent), 0755); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_ = fileOp.DeleteFile(newVersionDir + "/config.json")
|
||||
oldReadMefile := newVersionDir + "/README.md"
|
||||
_ = fileOp.Cut([]string{oldReadMefile}, newAppDir, "", false)
|
||||
_ = fileOp.DeleteFile(oldReadMefile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePrivate(t *testing.T) {
|
||||
priKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
derStream := x509.MarshalPKCS1PrivateKey(priKey)
|
||||
block := &pem.Block{
|
||||
Type: "privateKey",
|
||||
Bytes: derStream,
|
||||
}
|
||||
file, err := os.Create("private.key")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = pem.Encode(file, block); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSL(t *testing.T) {
|
||||
|
||||
priKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate private key: %v", err)
|
||||
}
|
||||
|
||||
myUser := AcmeUser{
|
||||
Email: "you2@yours.com",
|
||||
Key: priKey,
|
||||
}
|
||||
|
||||
config := lego.NewConfig(&myUser)
|
||||
//config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
config.CADirURL = "https://acme.zerossl.com/v2/DV90"
|
||||
config.UserAgent = "acm_go/0.0.1"
|
||||
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
//config.HTTPClient = httpClient
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
myUser.Registration = reg
|
||||
|
||||
//获取证书
|
||||
//certificates, err := client.Certificate.Get("https://acme-v02.api.letsencrypt.org/acme/cert/049cb98a8b3ea5a73f08dcdcf89263af8323", true)
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
//certificates, err = client.Certificate.Renew(*certificates, true, true, "")
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
|
||||
//申请证书
|
||||
ewDomain := "tuxpanel.com"
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: []string{ewDomain},
|
||||
// 证书链
|
||||
Bundle: true,
|
||||
}
|
||||
|
||||
err = client.Challenge.SetDNS01Provider(&manualDnsProvider{}, dns01.AddDNSTimeout(6*time.Minute))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
core, err := api.New(config.HTTPClient, config.UserAgent, config.CADirURL, reg.URI, priKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
order, err := core.Orders.New([]string{ewDomain})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
auth, err := core.Authorizations.Get(order.Authorizations[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
domain := challenge.GetTargetedDomain(auth)
|
||||
chlng, err := challenge.FindChallenge(challenge.DNS01, auth)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
keyAuth, err := core.GetKeyAuthorization(chlng.Token)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||
fmt.Println("fqdn", fqdn, value)
|
||||
|
||||
//
|
||||
//keyAuth, err := client.Challenge.core.GetKeyAuthorization(chlng.Token)
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
//
|
||||
|
||||
//httpProvider, err := webroot.NewHTTPProvider("/opt/1Panel/data/apps/nginx/nginx-1/www/wwwroot")
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
//
|
||||
//err = client.Challenge.SetHTTP01Provider(httpProvider)
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
//err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("43.142.178.16", "443"))
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
|
||||
certificates, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("---private---")
|
||||
fmt.Println(string(certificates.PrivateKey))
|
||||
fmt.Println("---.pem---")
|
||||
fmt.Println(string(certificates.Certificate))
|
||||
fmt.Println("---.domain---")
|
||||
fmt.Println(certificates.Domain)
|
||||
fmt.Println("---.certUrl---")
|
||||
fmt.Println(certificates.CertURL)
|
||||
fmt.Println("---.csr---")
|
||||
fmt.Println(certificates.CSR)
|
||||
fmt.Println("---.cert string---")
|
||||
fmt.Println(string(certificates.IssuerCertificate))
|
||||
|
||||
cer1, _ := pem.Decode(certificates.Certificate)
|
||||
|
||||
cert, err := x509.ParseCertificate(cer1.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(cert)
|
||||
|
||||
cer2, _ := pem.Decode(certificates.IssuerCertificate)
|
||||
|
||||
cert2, err := x509.ParseCertificate(cer2.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(cert2)
|
||||
|
||||
}
|
||||
|
||||
func generateCSR(privateKey crypto.PrivateKey, domain string) ([]byte, error) {
|
||||
// 创建证书请求的模板
|
||||
template := x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
CommonName: domain,
|
||||
},
|
||||
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
||||
}
|
||||
|
||||
// 生成 CSR
|
||||
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 将 CSR 编码为 PEM 格式
|
||||
csrPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
|
||||
|
||||
// 这里可以将 CSR 写入文件或者返回
|
||||
err = os.WriteFile("csr.pem", csrPEM, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return csrPEM, nil
|
||||
}
|
||||
|
||||
func TestZeroSSL(t *testing.T) {
|
||||
|
||||
domain := "1panel.store"
|
||||
acmeServer := "https://acme.zerossl.com/v2/DV90"
|
||||
|
||||
//acmeServer = "https://api.test4.buypass.no/acme/directory"
|
||||
//
|
||||
//acmeServer = "https://api.buypass.com/acme/directory"
|
||||
|
||||
priKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate private key: %v", err)
|
||||
}
|
||||
|
||||
user := AcmeUser{
|
||||
Email: "zhengkunwang123@sina.com",
|
||||
Key: priKey,
|
||||
}
|
||||
|
||||
//logFile, err := os.OpenFile("/opt/1panel/ssl.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
//if err != nil {
|
||||
// log.Fatalf("Failed to open log file: %v", err)
|
||||
//}
|
||||
//defer logFile.Close()
|
||||
//
|
||||
//logger := log.New(logFile, "", log.LstdFlags)
|
||||
//legoLogger.Logger = logger
|
||||
|
||||
config := lego.NewConfig(&user)
|
||||
|
||||
// 设置ACME服务器URL
|
||||
config.CADirURL = acmeServer
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
config.UserAgent = "acm_go/0.0.1"
|
||||
|
||||
// 创建ACME客户端
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// ZeroSSl
|
||||
|
||||
kid := ""
|
||||
hmacEncoded := ""
|
||||
|
||||
eabOptions := registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: true,
|
||||
Kid: kid,
|
||||
HmacEncoded: hmacEncoded,
|
||||
}
|
||||
|
||||
reg, err := client.Registration.RegisterWithExternalAccountBinding(eabOptions)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// ZeroSSl
|
||||
|
||||
//reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
//if err != nil {
|
||||
// log.Fatal(err)
|
||||
//}
|
||||
|
||||
user.Registration = reg
|
||||
|
||||
cloudflareConfig := cloudflare.NewDefaultConfig()
|
||||
cloudflareConfig.AuthEmail = ""
|
||||
cloudflareConfig.AuthKey = ""
|
||||
p, err := cloudflare.NewDNSProviderConfig(cloudflareConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// 申请证书
|
||||
pk, err := certcrypto.GeneratePrivateKey(certcrypto.EC256)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: []string{domain},
|
||||
Bundle: true,
|
||||
PrivateKey: pk,
|
||||
}
|
||||
certificates, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// 保存证书
|
||||
err = os.WriteFile("certificate.crt", certificates.Certificate, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = os.WriteFile("private.key", certificates.PrivateKey, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEABCre(t *testing.T) {
|
||||
res, err := getZeroSSLEabCredentials("zen@11.com")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%v", res)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !xpack
|
||||
|
||||
package xpack
|
||||
|
||||
func RemoveTamper(website string) {}
|
||||
|
||||
func LoadGpuInfo() []interface{} {
|
||||
return nil
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
cmdUtils "github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -43,19 +43,19 @@ var restoreCmd = &cobra.Command{
|
||||
tmpPath = path.Join(upgradeDir, tmpPath, "original")
|
||||
fmt.Printf("(0/4) 开始从 %s 目录回滚 1Panel 服务及数据... \n", tmpPath)
|
||||
|
||||
if err := cpBinary(path.Join(tmpPath, "1panel"), "/usr/local/bin/1panel"); err != nil {
|
||||
if err := common.CopyFile(path.Join(tmpPath, "1panel"), "/usr/local/bin"); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("(1/4) 1panel 二进制回滚成功")
|
||||
if err := cpBinary(path.Join(tmpPath, "1pctl"), "/usr/local/bin/1pctl"); err != nil {
|
||||
if err := common.CopyFile(path.Join(tmpPath, "1pctl"), "/usr/local/bin"); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("(2/4) 1panel 脚本回滚成功")
|
||||
if err := cpBinary(path.Join(tmpPath, "1panel.service"), "/etc/systemd/system/1panel.service"); err != nil {
|
||||
if err := common.CopyFile(path.Join(tmpPath, "1panel.service"), "/etc/systemd/system"); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("(3/4) 1panel 服务回滚成功")
|
||||
if err := cpBinary(path.Join(tmpPath, "1Panel.db"), path.Join(baseDir, "1panel", "db", "1Panel.db")); err != nil {
|
||||
if err := common.CopyFile(path.Join(tmpPath, "1Panel.db"), path.Join(baseDir, "1panel", "db")); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("(4/4) 1panel 数据回滚成功 \n\n")
|
||||
@@ -87,11 +87,3 @@ func loadRestorePath(upgradeDir string) (string, error) {
|
||||
})
|
||||
return folders[0], nil
|
||||
}
|
||||
|
||||
func cpBinary(src string, dst string) error {
|
||||
stderr, err := cmdUtils.Exec(fmt.Sprintf("\\cp -f %s %s", src, dst))
|
||||
if err != nil {
|
||||
return errors.New(stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -59,6 +59,14 @@ func getSettingByKey(db *gorm.DB, key string) string {
|
||||
return setting.Value
|
||||
}
|
||||
|
||||
type LoginLog struct{}
|
||||
|
||||
func isDefault(db *gorm.DB) bool {
|
||||
logCount := int64(0)
|
||||
_ = db.Model(&LoginLog{}).Where("status = ?", "Success").Count(&logCount).Error
|
||||
return logCount == 0
|
||||
}
|
||||
|
||||
func setSettingByKey(db *gorm.DB, key, value string) error {
|
||||
return db.Model(&setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -23,6 +25,15 @@ var userinfoCmd = &cobra.Command{
|
||||
return fmt.Errorf("init my db conn failed, err: %v \n", err)
|
||||
}
|
||||
user := getSettingByKey(db, "UserName")
|
||||
pass := "********"
|
||||
if isDefault(db) {
|
||||
encryptSetting := getSettingByKey(db, "EncryptKey")
|
||||
pass = getSettingByKey(db, "Password")
|
||||
if len(encryptSetting) == 16 {
|
||||
global.CONF.System.EncryptKey = encryptSetting
|
||||
pass, _ = encrypt.StringDecrypt(pass)
|
||||
}
|
||||
}
|
||||
port := getSettingByKey(db, "ServerPort")
|
||||
ssl := getSettingByKey(db, "SSL")
|
||||
entrance := getSettingByKey(db, "SecurityEntrance")
|
||||
@@ -38,7 +49,7 @@ var userinfoCmd = &cobra.Command{
|
||||
|
||||
fmt.Printf("面板地址: %s://%s:%s/%s \n", protocol, address, port, entrance)
|
||||
fmt.Println("面板用户: ", user)
|
||||
fmt.Println("面板密码: ", "********")
|
||||
fmt.Println("面板密码: ", pass)
|
||||
fmt.Println("提示:修改密码可执行命令:1pctl update password")
|
||||
return nil
|
||||
},
|
||||
|
||||
+213
-215
@@ -156,7 +156,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/apps/ingored": {
|
||||
"/apps/ignored": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
@@ -8097,6 +8097,62 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hosts/monitor/clean": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "清空监控数据",
|
||||
"tags": [
|
||||
"Monitor"
|
||||
],
|
||||
"summary": "Clean monitor datas",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [],
|
||||
"formatEN": "clean monitor datas",
|
||||
"formatZH": "清空监控数据",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hosts/monitor/search": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取监控数据",
|
||||
"tags": [
|
||||
"Monitor"
|
||||
],
|
||||
"summary": "Load monitor datas",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.MonitorSearch"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hosts/search": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -9158,6 +9214,28 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/runtimes/sync": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "同步运行环境状态",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Runtime"
|
||||
],
|
||||
"summary": "Sync runtime status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/runtimes/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -9901,6 +9979,46 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/menu/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "隐藏高级功能菜单",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Update system setting",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SettingUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [],
|
||||
"formatEN": "Hide advanced feature menu.",
|
||||
"formatZH": "隐藏高级功能菜单",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/mfa": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -9977,32 +10095,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/monitor/clean": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "清空监控数据",
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Clean monitor datas",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [],
|
||||
"formatEN": "clean monitor datas",
|
||||
"formatZH": "清空监控数据",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/password/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -12940,7 +13032,7 @@ const docTemplate = `{
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Load websit php conf",
|
||||
"summary": "Load website php conf",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
@@ -13918,144 +14010,6 @@ const docTemplate = `{
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/waf/config": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取网站 waf 配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website WAF"
|
||||
],
|
||||
"summary": "Load websit waf conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteWafReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.WebsiteWafConfig"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/waf/file/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新 网站 waf 配置文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website WAF"
|
||||
],
|
||||
"summary": "Update website waf file",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteWafUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_column": "id",
|
||||
"input_value": "websiteId",
|
||||
"isList": false,
|
||||
"output_column": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteId"
|
||||
],
|
||||
"formatEN": "WAF conf file update [domain]",
|
||||
"formatZH": "WAF 配置文件修改 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/waf/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新 网站 waf 配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website WAF"
|
||||
],
|
||||
"summary": "Update website waf conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteWafUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_column": "id",
|
||||
"input_value": "websiteId",
|
||||
"isList": false,
|
||||
"output_column": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteId"
|
||||
],
|
||||
"formatEN": "WAF conf update [domain]",
|
||||
"formatZH": "WAF 配置修改 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -14930,6 +14884,9 @@ const docTemplate = `{
|
||||
"backupAccounts": {
|
||||
"type": "string"
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"containerName": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -15001,6 +14958,9 @@ const docTemplate = `{
|
||||
"backupAccounts": {
|
||||
"type": "string"
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"containerName": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -15234,6 +15194,12 @@ const docTemplate = `{
|
||||
"$ref": "#/definitions/dto.DiskInfo"
|
||||
}
|
||||
},
|
||||
"gpuData": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.GPUInfo"
|
||||
}
|
||||
},
|
||||
"ioCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -15817,6 +15783,47 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.GPUInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fanSpeed": {
|
||||
"type": "string"
|
||||
},
|
||||
"gpuUtil": {
|
||||
"type": "string"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer"
|
||||
},
|
||||
"maxPowerLimit": {
|
||||
"type": "string"
|
||||
},
|
||||
"memTotal": {
|
||||
"type": "string"
|
||||
},
|
||||
"memUsed": {
|
||||
"type": "string"
|
||||
},
|
||||
"memoryUsage": {
|
||||
"type": "string"
|
||||
},
|
||||
"performanceState": {
|
||||
"type": "string"
|
||||
},
|
||||
"powerDraw": {
|
||||
"type": "string"
|
||||
},
|
||||
"powerUsage": {
|
||||
"type": "string"
|
||||
},
|
||||
"productName": {
|
||||
"type": "string"
|
||||
},
|
||||
"temperature": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.GenerateLoad": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -16324,6 +16331,34 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.MonitorSearch": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"param"
|
||||
],
|
||||
"properties": {
|
||||
"endTime": {
|
||||
"type": "string"
|
||||
},
|
||||
"info": {
|
||||
"type": "string"
|
||||
},
|
||||
"param": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"all",
|
||||
"cpu",
|
||||
"memory",
|
||||
"load",
|
||||
"io",
|
||||
"network"
|
||||
]
|
||||
},
|
||||
"startTime": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.MysqlDBCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -17800,6 +17835,9 @@ const docTemplate = `{
|
||||
"monitorStoreDays": {
|
||||
"type": "string"
|
||||
},
|
||||
"noAuthSetting": {
|
||||
"type": "string"
|
||||
},
|
||||
"ntpSite": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -17818,6 +17856,9 @@ const docTemplate = `{
|
||||
"sessionTimeout": {
|
||||
"type": "string"
|
||||
},
|
||||
"snapshotIgnore": {
|
||||
"type": "string"
|
||||
},
|
||||
"ssl": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -17841,6 +17882,9 @@ const docTemplate = `{
|
||||
},
|
||||
"weChatVars": {
|
||||
"type": "string"
|
||||
},
|
||||
"xpackHideMenu": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18087,6 +18131,9 @@ const docTemplate = `{
|
||||
},
|
||||
"sourceDir": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18918,7 +18965,6 @@ const docTemplate = `{
|
||||
"request.FileCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"mode",
|
||||
"path"
|
||||
],
|
||||
"properties": {
|
||||
@@ -20774,43 +20820,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteWafReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key",
|
||||
"rule",
|
||||
"websiteId"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"rule": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteWafUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key",
|
||||
"websiteId"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.AppDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -21490,17 +21499,6 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.WebsiteWafConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
+213
-215
@@ -149,7 +149,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/apps/ingored": {
|
||||
"/apps/ignored": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
@@ -8090,6 +8090,62 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hosts/monitor/clean": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "清空监控数据",
|
||||
"tags": [
|
||||
"Monitor"
|
||||
],
|
||||
"summary": "Clean monitor datas",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [],
|
||||
"formatEN": "clean monitor datas",
|
||||
"formatZH": "清空监控数据",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hosts/monitor/search": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取监控数据",
|
||||
"tags": [
|
||||
"Monitor"
|
||||
],
|
||||
"summary": "Load monitor datas",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.MonitorSearch"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hosts/search": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -9151,6 +9207,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/runtimes/sync": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "同步运行环境状态",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Runtime"
|
||||
],
|
||||
"summary": "Sync runtime status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/runtimes/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -9894,6 +9972,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/menu/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "隐藏高级功能菜单",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Update system setting",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SettingUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [],
|
||||
"formatEN": "Hide advanced feature menu.",
|
||||
"formatZH": "隐藏高级功能菜单",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/mfa": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -9970,32 +10088,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/monitor/clean": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "清空监控数据",
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Clean monitor datas",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [],
|
||||
"formatEN": "clean monitor datas",
|
||||
"formatZH": "清空监控数据",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/password/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -12933,7 +13025,7 @@
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Load websit php conf",
|
||||
"summary": "Load website php conf",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
@@ -13911,144 +14003,6 @@
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/waf/config": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取网站 waf 配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website WAF"
|
||||
],
|
||||
"summary": "Load websit waf conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteWafReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.WebsiteWafConfig"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/waf/file/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新 网站 waf 配置文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website WAF"
|
||||
],
|
||||
"summary": "Update website waf file",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteWafUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_column": "id",
|
||||
"input_value": "websiteId",
|
||||
"isList": false,
|
||||
"output_column": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteId"
|
||||
],
|
||||
"formatEN": "WAF conf file update [domain]",
|
||||
"formatZH": "WAF 配置文件修改 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/waf/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新 网站 waf 配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website WAF"
|
||||
],
|
||||
"summary": "Update website waf conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteWafUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_column": "id",
|
||||
"input_value": "websiteId",
|
||||
"isList": false,
|
||||
"output_column": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteId"
|
||||
],
|
||||
"formatEN": "WAF conf update [domain]",
|
||||
"formatZH": "WAF 配置修改 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -14923,6 +14877,9 @@
|
||||
"backupAccounts": {
|
||||
"type": "string"
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"containerName": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -14994,6 +14951,9 @@
|
||||
"backupAccounts": {
|
||||
"type": "string"
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"containerName": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -15227,6 +15187,12 @@
|
||||
"$ref": "#/definitions/dto.DiskInfo"
|
||||
}
|
||||
},
|
||||
"gpuData": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.GPUInfo"
|
||||
}
|
||||
},
|
||||
"ioCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -15810,6 +15776,47 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.GPUInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fanSpeed": {
|
||||
"type": "string"
|
||||
},
|
||||
"gpuUtil": {
|
||||
"type": "string"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer"
|
||||
},
|
||||
"maxPowerLimit": {
|
||||
"type": "string"
|
||||
},
|
||||
"memTotal": {
|
||||
"type": "string"
|
||||
},
|
||||
"memUsed": {
|
||||
"type": "string"
|
||||
},
|
||||
"memoryUsage": {
|
||||
"type": "string"
|
||||
},
|
||||
"performanceState": {
|
||||
"type": "string"
|
||||
},
|
||||
"powerDraw": {
|
||||
"type": "string"
|
||||
},
|
||||
"powerUsage": {
|
||||
"type": "string"
|
||||
},
|
||||
"productName": {
|
||||
"type": "string"
|
||||
},
|
||||
"temperature": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.GenerateLoad": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -16317,6 +16324,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.MonitorSearch": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"param"
|
||||
],
|
||||
"properties": {
|
||||
"endTime": {
|
||||
"type": "string"
|
||||
},
|
||||
"info": {
|
||||
"type": "string"
|
||||
},
|
||||
"param": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"all",
|
||||
"cpu",
|
||||
"memory",
|
||||
"load",
|
||||
"io",
|
||||
"network"
|
||||
]
|
||||
},
|
||||
"startTime": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.MysqlDBCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -17793,6 +17828,9 @@
|
||||
"monitorStoreDays": {
|
||||
"type": "string"
|
||||
},
|
||||
"noAuthSetting": {
|
||||
"type": "string"
|
||||
},
|
||||
"ntpSite": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -17811,6 +17849,9 @@
|
||||
"sessionTimeout": {
|
||||
"type": "string"
|
||||
},
|
||||
"snapshotIgnore": {
|
||||
"type": "string"
|
||||
},
|
||||
"ssl": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -17834,6 +17875,9 @@
|
||||
},
|
||||
"weChatVars": {
|
||||
"type": "string"
|
||||
},
|
||||
"xpackHideMenu": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18080,6 +18124,9 @@
|
||||
},
|
||||
"sourceDir": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18911,7 +18958,6 @@
|
||||
"request.FileCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"mode",
|
||||
"path"
|
||||
],
|
||||
"properties": {
|
||||
@@ -20767,43 +20813,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteWafReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key",
|
||||
"rule",
|
||||
"websiteId"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"rule": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteWafUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key",
|
||||
"websiteId"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.AppDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -21483,17 +21492,6 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.WebsiteWafConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+139
-140
@@ -584,6 +584,8 @@ definitions:
|
||||
type: string
|
||||
backupAccounts:
|
||||
type: string
|
||||
command:
|
||||
type: string
|
||||
containerName:
|
||||
type: string
|
||||
dbName:
|
||||
@@ -632,6 +634,8 @@ definitions:
|
||||
type: string
|
||||
backupAccounts:
|
||||
type: string
|
||||
command:
|
||||
type: string
|
||||
containerName:
|
||||
type: string
|
||||
dbName:
|
||||
@@ -791,6 +795,10 @@ definitions:
|
||||
items:
|
||||
$ref: '#/definitions/dto.DiskInfo'
|
||||
type: array
|
||||
gpuData:
|
||||
items:
|
||||
$ref: '#/definitions/dto.GPUInfo'
|
||||
type: array
|
||||
ioCount:
|
||||
type: integer
|
||||
ioReadBytes:
|
||||
@@ -1186,6 +1194,33 @@ definitions:
|
||||
- type
|
||||
- vars
|
||||
type: object
|
||||
dto.GPUInfo:
|
||||
properties:
|
||||
fanSpeed:
|
||||
type: string
|
||||
gpuUtil:
|
||||
type: string
|
||||
index:
|
||||
type: integer
|
||||
maxPowerLimit:
|
||||
type: string
|
||||
memTotal:
|
||||
type: string
|
||||
memUsed:
|
||||
type: string
|
||||
memoryUsage:
|
||||
type: string
|
||||
performanceState:
|
||||
type: string
|
||||
powerDraw:
|
||||
type: string
|
||||
powerUsage:
|
||||
type: string
|
||||
productName:
|
||||
type: string
|
||||
temperature:
|
||||
type: string
|
||||
type: object
|
||||
dto.GenerateLoad:
|
||||
properties:
|
||||
encryptionMode:
|
||||
@@ -1528,6 +1563,26 @@ definitions:
|
||||
- interval
|
||||
- secret
|
||||
type: object
|
||||
dto.MonitorSearch:
|
||||
properties:
|
||||
endTime:
|
||||
type: string
|
||||
info:
|
||||
type: string
|
||||
param:
|
||||
enum:
|
||||
- all
|
||||
- cpu
|
||||
- memory
|
||||
- load
|
||||
- io
|
||||
- network
|
||||
type: string
|
||||
startTime:
|
||||
type: string
|
||||
required:
|
||||
- param
|
||||
type: object
|
||||
dto.MysqlDBCreate:
|
||||
properties:
|
||||
database:
|
||||
@@ -2526,6 +2581,8 @@ definitions:
|
||||
type: string
|
||||
monitorStoreDays:
|
||||
type: string
|
||||
noAuthSetting:
|
||||
type: string
|
||||
ntpSite:
|
||||
type: string
|
||||
panelName:
|
||||
@@ -2538,6 +2595,8 @@ definitions:
|
||||
type: string
|
||||
sessionTimeout:
|
||||
type: string
|
||||
snapshotIgnore:
|
||||
type: string
|
||||
ssl:
|
||||
type: string
|
||||
sslType:
|
||||
@@ -2554,6 +2613,8 @@ definitions:
|
||||
type: string
|
||||
weChatVars:
|
||||
type: string
|
||||
xpackHideMenu:
|
||||
type: string
|
||||
type: object
|
||||
dto.SettingUpdate:
|
||||
properties:
|
||||
@@ -2715,6 +2776,8 @@ definitions:
|
||||
type: string
|
||||
sourceDir:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
type: object
|
||||
files.FileInfo:
|
||||
properties:
|
||||
@@ -3281,7 +3344,6 @@ definitions:
|
||||
sub:
|
||||
type: boolean
|
||||
required:
|
||||
- mode
|
||||
- path
|
||||
type: object
|
||||
request.FileDeCompress:
|
||||
@@ -4515,31 +4577,6 @@ definitions:
|
||||
- id
|
||||
- user
|
||||
type: object
|
||||
request.WebsiteWafReq:
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
rule:
|
||||
type: string
|
||||
websiteId:
|
||||
type: integer
|
||||
required:
|
||||
- key
|
||||
- rule
|
||||
- websiteId
|
||||
type: object
|
||||
request.WebsiteWafUpdate:
|
||||
properties:
|
||||
enable:
|
||||
type: boolean
|
||||
key:
|
||||
type: string
|
||||
websiteId:
|
||||
type: integer
|
||||
required:
|
||||
- key
|
||||
- websiteId
|
||||
type: object
|
||||
response.AppDTO:
|
||||
properties:
|
||||
createdAt:
|
||||
@@ -4988,13 +5025,6 @@ definitions:
|
||||
version:
|
||||
type: string
|
||||
type: object
|
||||
response.WebsiteWafConfig:
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
enable:
|
||||
type: boolean
|
||||
type: object
|
||||
host: localhost
|
||||
info:
|
||||
contact: {}
|
||||
@@ -5090,7 +5120,7 @@ paths:
|
||||
summary: Get app detail by id
|
||||
tags:
|
||||
- App
|
||||
/apps/ingored:
|
||||
/apps/ignored:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
@@ -10128,6 +10158,41 @@ paths:
|
||||
summary: Create group
|
||||
tags:
|
||||
- Firewall
|
||||
/hosts/monitor/clean:
|
||||
post:
|
||||
description: 清空监控数据
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Clean monitor datas
|
||||
tags:
|
||||
- Monitor
|
||||
x-panel-log:
|
||||
BeforeFunctions: []
|
||||
bodyKeys: []
|
||||
formatEN: clean monitor datas
|
||||
formatZH: 清空监控数据
|
||||
paramKeys: []
|
||||
/hosts/monitor/search:
|
||||
post:
|
||||
description: 获取监控数据
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.MonitorSearch'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Load monitor datas
|
||||
tags:
|
||||
- Monitor
|
||||
/hosts/search:
|
||||
post:
|
||||
consumes:
|
||||
@@ -10789,6 +10854,19 @@ paths:
|
||||
summary: List runtimes
|
||||
tags:
|
||||
- Runtime
|
||||
/runtimes/sync:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 同步运行环境状态
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Sync runtime status
|
||||
tags:
|
||||
- Runtime
|
||||
/runtimes/update:
|
||||
post:
|
||||
consumes:
|
||||
@@ -11261,6 +11339,32 @@ paths:
|
||||
summary: Load system address
|
||||
tags:
|
||||
- System Setting
|
||||
/settings/menu/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 隐藏高级功能菜单
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.SettingUpdate'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update system setting
|
||||
tags:
|
||||
- System Setting
|
||||
x-panel-log:
|
||||
BeforeFunctions: []
|
||||
bodyKeys: []
|
||||
formatEN: Hide advanced feature menu.
|
||||
formatZH: 隐藏高级功能菜单
|
||||
paramKeys: []
|
||||
/settings/mfa:
|
||||
post:
|
||||
consumes:
|
||||
@@ -11309,23 +11413,6 @@ paths:
|
||||
formatEN: bind mfa
|
||||
formatZH: mfa 绑定
|
||||
paramKeys: []
|
||||
/settings/monitor/clean:
|
||||
post:
|
||||
description: 清空监控数据
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Clean monitor datas
|
||||
tags:
|
||||
- System Setting
|
||||
x-panel-log:
|
||||
BeforeFunctions: []
|
||||
bodyKeys: []
|
||||
formatEN: clean monitor datas
|
||||
formatZH: 清空监控数据
|
||||
paramKeys: []
|
||||
/settings/password/update:
|
||||
post:
|
||||
consumes:
|
||||
@@ -13195,7 +13282,7 @@ paths:
|
||||
$ref: '#/definitions/response.PHPConfig'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Load websit php conf
|
||||
summary: Load website php conf
|
||||
tags:
|
||||
- Website
|
||||
/websites/php/update:
|
||||
@@ -13808,92 +13895,4 @@ paths:
|
||||
formatEN: Update website [primaryDomain]
|
||||
formatZH: 更新网站 [primaryDomain]
|
||||
paramKeys: []
|
||||
/websites/waf/config:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取网站 waf 配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.WebsiteWafReq'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.WebsiteWafConfig'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Load websit waf conf
|
||||
tags:
|
||||
- Website WAF
|
||||
/websites/waf/file/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新 网站 waf 配置文件
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.WebsiteWafUpdate'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update website waf file
|
||||
tags:
|
||||
- Website WAF
|
||||
x-panel-log:
|
||||
BeforeFunctions:
|
||||
- db: websites
|
||||
input_column: id
|
||||
input_value: websiteId
|
||||
isList: false
|
||||
output_column: primary_domain
|
||||
output_value: domain
|
||||
bodyKeys:
|
||||
- websiteId
|
||||
formatEN: WAF conf file update [domain]
|
||||
formatZH: WAF 配置文件修改 [domain]
|
||||
paramKeys: []
|
||||
/websites/waf/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新 网站 waf 配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.WebsiteWafUpdate'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update website waf conf
|
||||
tags:
|
||||
- Website WAF
|
||||
x-panel-log:
|
||||
BeforeFunctions:
|
||||
- db: websites
|
||||
input_column: id
|
||||
input_value: websiteId
|
||||
isList: false
|
||||
output_column: primary_domain
|
||||
output_value: domain
|
||||
bodyKeys:
|
||||
- websiteId
|
||||
formatEN: WAF conf update [domain]
|
||||
formatZH: WAF 配置修改 [domain]
|
||||
paramKeys: []
|
||||
swagger: "2.0"
|
||||
|
||||
@@ -16,22 +16,6 @@ server {
|
||||
access_log /www/sites/domain/log/access.log;
|
||||
error_log /www/sites/domain/log/error.log;
|
||||
|
||||
access_by_lua_file /www/common/waf/access.lua;
|
||||
set $RulePath /www/sites/domain/waf/rules;
|
||||
set $logdir /www/sites/domain/waf/log;
|
||||
set $redirect on;
|
||||
set $attackLog on;
|
||||
set $CCDeny off;
|
||||
set $urlWhiteAllow off;
|
||||
set $urlBlockDeny off;
|
||||
set $argsDeny off;
|
||||
set $postDeny off;
|
||||
set $cookieDeny off;
|
||||
set $fileExtDeny off;
|
||||
set $ipBlockDeny off;
|
||||
set $ipWhiteAllow off;
|
||||
|
||||
|
||||
location ^~ /.well-known/acme-challenge {
|
||||
allow all;
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
@@ -54,6 +54,14 @@ If you discover any security issues, please contact us through:
|
||||
- Email: support@fit2cloud.com
|
||||
- Phone: 400-052-0755
|
||||
|
||||
## Our Other Star Projects
|
||||
|
||||
- [JumpServer](https://github.com/jumpserver/jumpserver/) - A popular open-source PAM system
|
||||
- [DataEase](https://github.com/dataease/dataease/) - An open-source data visualization and analysis tool for everyone
|
||||
- [MeterSphere](https://github.com/metersphere/metersphere/) - An all-in-one open-source automated testing platform
|
||||
- [Halo](https://github.com/halo-dev/halo/) - A powerful and user-friendly open-source website building tool
|
||||
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#1Panel-dev/1Panel&Date)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user