Compare commits

..
11 Commits
572 changed files with 11721 additions and 108265 deletions
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '20.2'
node-version: '18.14'
- name: Build Web
run: |
cd frontend && npm install && npm run build:pro
@@ -24,7 +24,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.21.x'
go-version: '1.20.x'
- name: Build Release
uses: goreleaser/goreleaser-action@v4
with:
+7 -9
View File
@@ -21,6 +21,13 @@ build/1panel
*.iml
*.ipr
# NetBeans
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
# Test binary, built with `go test -c`
*.test
@@ -37,15 +44,6 @@ cmd/server/web/monacoeditorwork
cmd/server/web/index.html
frontend/auto-imports.d.ts
frontend/components.d.ts
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
.history/
dist/
+7 -7
View File
@@ -11,13 +11,13 @@
------------------------------
1Panel 是新一代的 Linux 服务器运维管理面板。
1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。1Panel 的功能和优势包括:
- **高效管理**:用户可以通过 Web 图形界面轻松管理 Linux 服务器,实现主机监控、文件管理、数据库管理、容器管理等功能;
- **快速建站**:深度集成开源建站软件 WordPress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等操作一键搞定;
- **应用商店**:精选上架各类高质量的开源工具和应用软件,协助用户轻松安装并升级;
- **安全可靠**:基于容器管理并部署应用,实现最小的漏洞暴露面,同时提供防火墙和日志审计等功能;
- **一键备份**:支持一键备份和恢复,用户可以将数据备份到各类云端存储介质,永不丢失。
- **快速建站**:深度集成 Wordpress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等一键搞定;
- **高效管理**:通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等;
- **安全可靠**:基于容器来管理和部署应用,最小漏洞暴露面,提供防火墙和日志审计等功能;
- **一键备份**:支持一键备份和恢复,备份数据到各类云端存储,永不丢失;
- **应用商店**:精选各类高质量开源工具和应用软件,助力您轻松安装和升级。
## UI 展示
@@ -63,7 +63,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
## License
Copyright (c) 2014-2024 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
+1 -1
View File
@@ -16,6 +16,6 @@ All security bugs should be reported to the contact as below:
- wanghe@fit2cloud.com
- zhengkun@fit2cloud.com
- support@fit2cloud.com
- +86 400-052-0755
- 400-052-0755
Thanks for your support!
+5
View File
@@ -6,6 +6,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/gin-gonic/gin"
)
@@ -71,6 +72,10 @@ func (b *BaseApi) CheckAppInstalled(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
checkData, err := appInstallService.CheckExist(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+17 -62
View File
@@ -49,17 +49,6 @@ func (b *BaseApi) CreateBackup(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Backup Account
// @Summary Refresh OneDrive token
// @Description 刷新 OneDrive token
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/backup/refresh/onedrive [post]
func (b *BaseApi) RefreshOneDriveToken(c *gin.Context) {
backupService.Run()
helper.SuccessWithData(c, nil)
}
// @Tags Backup Account
// @Summary List buckets
// @Description 获取 bucket 列表
@@ -102,16 +91,16 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
// @Summary Load OneDrive info
// @Description 获取 OneDrive 信息
// @Accept json
// @Success 200 {object} dto.OneDriveInfo
// @Success 200 string clientID
// @Security ApiKeyAuth
// @Router /settings/backup/onedrive [get]
func (b *BaseApi) LoadOneDriveInfo(c *gin.Context) {
data, err := backupService.LoadOneDriveInfo()
clientID, err := backupService.LoadOneDriveInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
helper.SuccessWithData(c, clientID)
}
// @Tags Backup Account
@@ -162,32 +151,6 @@ func (b *BaseApi) SearchBackupRecords(c *gin.Context) {
})
}
// @Tags Backup Account
// @Summary Page backup records by cronjob
// @Description 通过计划任务获取备份记录列表分页
// @Accept json
// @Param request body dto.RecordSearchByCronjob true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/backup/record/search/bycronjob [post]
func (b *BaseApi) SearchBackupRecordsByCronjob(c *gin.Context) {
var req dto.RecordSearchByCronjob
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := backupService.SearchRecordsByCronjobWithPage(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags Backup Account
// @Summary Download backup record
// @Description 下载备份记录
@@ -302,7 +265,12 @@ func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
return
}
data := backupService.ListFiles(req)
data, err := backupService.ListFiles(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
@@ -313,7 +281,7 @@ func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
// @Param request body dto.CommonBackup true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/backup/backup [post]
// @Router /settings/backup/ [post]
// @x-panel-log {"bodyKeys":["type","name","detailName"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"备份 [type] 数据 [name][detailName]","formatEN":"backup [type] data [name][detailName]"}
func (b *BaseApi) Backup(c *gin.Context) {
var req dto.CommonBackup
@@ -332,11 +300,6 @@ func (b *BaseApi) Backup(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case constant.AppPostgresql:
if err := backupService.PostgresqlBackup(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case "website":
if err := backupService.WebsiteBackup(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -366,23 +329,20 @@ func (b *BaseApi) Recover(c *gin.Context) {
return
}
downloadPath, err := backupService.DownloadRecord(dto.DownloadRecord{Source: req.Source, FileDir: path.Dir(req.File), FileName: path.Base(req.File)})
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("download file failed, err: %v", err))
return
if req.Source != "LOCAL" {
downloadPath, err := backupService.DownloadRecord(dto.DownloadRecord{Source: req.Source, FileDir: path.Dir(req.File), FileName: path.Base(req.File)})
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("download file failed, err: %v", err))
return
}
req.File = downloadPath
}
req.File = downloadPath
switch req.Type {
case "mysql", "mariadb":
if err := backupService.MysqlRecover(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case constant.AppPostgresql:
if err := backupService.PostgresqlRecover(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case "website":
if err := backupService.WebsiteRecover(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -423,11 +383,6 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case constant.AppPostgresql:
if err := backupService.PostgresqlRecoverByUpload(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case "app":
if err := backupService.AppRecover(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+6 -2
View File
@@ -95,13 +95,17 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
// @Param request body dto.OperateByID true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /cronjobs/records/log [post]
// @Router /cronjob/record/log [post]
func (b *BaseApi) LoadRecordLog(c *gin.Context) {
var req dto.OperateByID
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
content := cronjobService.LoadRecordLog(req)
content, err := cronjobService.LoadRecordLog(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, content)
}
-16
View File
@@ -8,22 +8,6 @@ import (
"github.com/gin-gonic/gin"
)
// @Tags Dashboard
// @Summary Load os info
// @Description 获取服务器基础数据
// @Accept json
// @Success 200 {object} dto.OsInfo
// @Security ApiKeyAuth
// @Router /dashboard/base/os [get]
func (b *BaseApi) LoadDashboardOsInfo(c *gin.Context) {
data, err := dashboardService.LoadOsInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Load dashboard base info
// @Description 获取首页基础数据
-47
View File
@@ -1,8 +1,6 @@
package v1
import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -23,14 +21,6 @@ func (b *BaseApi) CreateDatabase(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.SSL {
key, _ := base64.StdEncoding.DecodeString(req.ClientKey)
req.ClientKey = string(key)
cert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
req.ClientCert = string(cert)
ca, _ := base64.StdEncoding.DecodeString(req.RootCert)
req.RootCert = string(ca)
}
if err := databaseService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -53,14 +43,6 @@ func (b *BaseApi) CheckDatabase(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.SSL {
clientKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)
req.ClientKey = string(clientKey)
clientCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
req.ClientCert = string(clientCert)
rootCert, _ := base64.StdEncoding.DecodeString(req.RootCert)
req.RootCert = string(rootCert)
}
helper.SuccessWithData(c, databaseService.CheckDatabase(req))
}
@@ -112,27 +94,6 @@ func (b *BaseApi) ListDatabase(c *gin.Context) {
helper.SuccessWithData(c, list)
}
// @Tags Database
// @Summary List databases
// @Description 获取数据库列表
// @Success 200 {array} dto.DatabaseItem
// @Security ApiKeyAuth
// @Router /databases/db/item/:type [get]
func (b *BaseApi) LoadDatabaseItems(c *gin.Context) {
dbType, err := helper.GetStrParamByKey(c, "type")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
list, err := databaseService.LoadItems(dbType)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Database
// @Summary Get databases
// @Description 获取远程数据库
@@ -212,14 +173,6 @@ func (b *BaseApi) UpdateDatabase(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.SSL {
cKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)
req.ClientKey = string(cKey)
cCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
req.ClientCert = string(cCert)
ca, _ := base64.StdEncoding.DecodeString(req.RootCert)
req.RootCert = string(ca)
}
if err := databaseService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
-75
View File
@@ -1,75 +0,0 @@
package v1
import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags Database Common
// @Summary Load base info
// @Description 获取数据库基础信息
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {object} dto.DBBaseInfo
// @Security ApiKeyAuth
// @Router /databases/common/info [post]
func (b *BaseApi) LoadDBBaseInfo(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := dbCommonService.LoadBaseInfo(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Database Common
// @Summary Load Database conf
// @Description 获取数据库配置文件
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/common/load/file [post]
func (b *BaseApi) LoadDBFile(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
content, err := dbCommonService.LoadDatabaseFile(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, content)
}
// @Tags Database Common
// @Summary Update conf by upload file
// @Description 上传替换配置文件
// @Accept json
// @Param request body dto.DBConfUpdateByFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/common/update/conf [post]
// @x-panel-log {"bodyKeys":["type","database"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 [type] 数据库 [database] 配置信息","formatEN":"update the [type] [database] database configuration information"}
func (b *BaseApi) UpdateDBConfByFile(c *gin.Context) {
var req dto.DBConfUpdateByFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := dbCommonService.UpdateConfByFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
+69 -31
View File
@@ -41,37 +41,6 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Bind user of mysql database
// @Description 绑定 mysql 数据库用户
// @Accept json
// @Param request body dto.BindUser true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/bind [post]
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 mysql 数据库名 [database] [username]","formatEN":"bind mysql database [database] [username]"}
func (b *BaseApi) BindUser(c *gin.Context) {
var req dto.BindUser
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(password)
}
if err := mysqlService.BindUser(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Update mysql database description
// @Description 更新 mysql 数据库库描述信息
@@ -169,6 +138,29 @@ func (b *BaseApi) UpdateMysqlVariables(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Update mysql conf by upload file
// @Description 上传替换 mysql 配置文件
// @Accept json
// @Param request body dto.MysqlConfUpdateByFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/conffile/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 mysql 数据库配置信息","formatEN":"update the mysql database configuration information"}
func (b *BaseApi) UpdateMysqlConfByFile(c *gin.Context) {
var req dto.MysqlConfUpdateByFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := mysqlService.UpdateConfByFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Page mysql databases
// @Description 获取 mysql 数据库列表分页
@@ -281,6 +273,51 @@ func (b *BaseApi) DeleteMysql(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Load mysql base info
// @Description 获取 mysql 基础信息
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {object} dto.DBBaseInfo
// @Security ApiKeyAuth
// @Router /databases/baseinfo [post]
func (b *BaseApi) LoadBaseinfo(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := mysqlService.LoadBaseInfo(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Database
// @Summary Load Database file
// @Description 获取数据库文件
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/load/file [post]
func (b *BaseApi) LoadDatabaseFile(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
content, err := mysqlService.LoadDatabaseFile(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, content)
}
// @Tags Database Mysql
// @Summary Load mysql remote access
// @Description 获取 mysql 远程访问权限
@@ -294,6 +331,7 @@ func (b *BaseApi) LoadRemoteAccess(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
isRemote, err := mysqlService.LoadRemoteAccess(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
-233
View File
@@ -1,233 +0,0 @@
package v1
import (
"context"
"encoding/base64"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags Database Postgresql
// @Summary Create postgresql database
// @Description 创建 postgresql 数据库
// @Accept json
// @Param request body dto.PostgresqlDBCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 postgresql 数据库 [name]","formatEN":"create postgresql database [name]"}
func (b *BaseApi) CreatePostgresql(c *gin.Context) {
var req dto.PostgresqlDBCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(password)
}
if _, err := postgresqlService.Create(context.Background(), req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Bind postgresql user
// @Description 绑定 postgresql 数据库用户
// @Accept json
// @Param request body dto.PostgresqlBindUser true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/bind [post]
// @x-panel-log {"bodyKeys":["name", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 postgresql 数据库 [name] 用户 [username]","formatEN":"bind postgresql database [name] user [username]"}
func (b *BaseApi) BindPostgresqlUser(c *gin.Context) {
var req dto.PostgresqlBindUser
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := postgresqlService.BindUser(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Update postgresql database description
// @Description 更新 postgresql 数据库库描述信息
// @Accept json
// @Param request body dto.UpdateDescription true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/description [post]
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"postgresql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the postgresql database [name] is modified => [description]"}
func (b *BaseApi) UpdatePostgresqlDescription(c *gin.Context) {
var req dto.UpdateDescription
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := postgresqlService.UpdateDescription(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Change postgresql privileges
// @Description 修改 postgresql 用户权限
// @Accept json
// @Param request body dto.ChangeDBInfo true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/privileges [post]
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新数据库 [database] 用户 [username] 权限","formatEN":"Update [user] privileges of database [database]"}
func (b *BaseApi) ChangePostgresqlPrivileges(c *gin.Context) {
var req dto.PostgresqlPrivileges
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := postgresqlService.ChangePrivileges(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Change postgresql password
// @Description 修改 postgresql 密码
// @Accept json
// @Param request body dto.ChangeDBInfo true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/password [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
func (b *BaseApi) ChangePostgresqlPassword(c *gin.Context) {
var req dto.ChangeDBInfo
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Value) != 0 {
value, err := base64.StdEncoding.DecodeString(req.Value)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Value = string(value)
}
if err := postgresqlService.ChangePassword(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Page postgresql databases
// @Description 获取 postgresql 数据库列表分页
// @Accept json
// @Param request body dto.PostgresqlDBSearch true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /databases/pg/search [post]
func (b *BaseApi) SearchPostgresql(c *gin.Context) {
var req dto.PostgresqlDBSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := postgresqlService.SearchWithPage(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags Database Postgresql
// @Summary Load postgresql database from remote
// @Description 从服务器获取
// @Accept json
// @Param request body dto.PostgresqlLoadDB true "request"
// @Security ApiKeyAuth
// @Router /databases/pg/:database/load [post]
func (b *BaseApi) LoadPostgresqlDBFromRemote(c *gin.Context) {
database, err := helper.GetStrParamByKey(c, "database")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := postgresqlService.LoadFromRemote(database); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Check before delete postgresql database
// @Description Postgresql 数据库删除前检查
// @Accept json
// @Param request body dto.PostgresqlDBDeleteCheck true "request"
// @Success 200 {array} string
// @Security ApiKeyAuth
// @Router /databases/pg/del/check [post]
func (b *BaseApi) DeleteCheckPostgresql(c *gin.Context) {
var req dto.PostgresqlDBDeleteCheck
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
apps, err := postgresqlService.DeleteCheck(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, apps)
}
// @Tags Database Postgresql
// @Summary Delete postgresql database
// @Description 删除 postgresql 数据库
// @Accept json
// @Param request body dto.PostgresqlDBDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"删除 postgresql 数据库 [name]","formatEN":"delete postgresql database [name]"}
func (b *BaseApi) DeletePostgresql(c *gin.Context) {
var req dto.PostgresqlDBDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
tx, ctx := helper.GetTxAndContext()
if err := postgresqlService.Delete(ctx, req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
tx.Rollback()
return
}
tx.Commit()
helper.SuccessWithData(c, nil)
}
+46
View File
@@ -1,11 +1,15 @@
package v1
import (
"bufio"
"encoding/base64"
"fmt"
"os"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"github.com/gin-gonic/gin"
)
@@ -157,3 +161,45 @@ func (b *BaseApi) RedisBackupList(c *gin.Context) {
Total: total,
})
}
// @Tags Database Redis
// @Summary Update redis conf by file
// @Description 上传更新 redis 配置信息
// @Accept json
// @Param request body dto.RedisConfUpdateByFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/redis/conffile/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 redis 数据库配置信息","formatEN":"update the redis database configuration information"}
func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) {
var req dto.RedisConfUpdateByFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
redisInfo, err := redisService.LoadConf()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisInfo.Name)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(req.File)
write.Flush()
if req.RestartNow {
composeDir := fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)
if _, err := compose.Restart(composeDir); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
}
helper.SuccessWithData(c, nil)
}
-236
View File
@@ -1,236 +0,0 @@
package v1
import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags Device
// @Summary Load device base info
// @Description 获取设备基础信息
// @Success 200 {object} dto.DeviceBaseInfo
// @Security ApiKeyAuth
// @Router /toolbox/device/base [post]
func (b *BaseApi) LoadDeviceBaseInfo(c *gin.Context) {
data, err := deviceService.LoadBaseInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Device
// @Summary list time zone options
// @Description 获取系统可用时区选项
// @Accept json
// @Success 200 {Array} string
// @Security ApiKeyAuth
// @Router /toolbox/device/zone/options [get]
func (b *BaseApi) LoadTimeOption(c *gin.Context) {
list, err := deviceService.LoadTimeZone()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Device
// @Summary load conf
// @Description 获取系统配置文件
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/conf [post]
func (b *BaseApi) LoadDeviceConf(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list, err := deviceService.LoadConf(req.Name)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Device
// @Summary Update device conf by file
// @Description 通过文件修改配置
// @Accept json
// @Param request body dto.UpdateByNameAndFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/byconf [post]
func (b *BaseApi) UpdateDeviceByFile(c *gin.Context) {
var req dto.UpdateByNameAndFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.UpdateByConf(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device
// @Description 修改系统参数
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/conf [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改主机参数 [key] => [value]","formatEN":"update device conf [key] => [value]"}
func (b *BaseApi) UpdateDeviceConf(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.Update(req.Key, req.Value); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device hosts
// @Description 修改系统 hosts
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/host [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改主机 Host [key] => [value]","formatEN":"update device host [key] => [value]"}
func (b *BaseApi) UpdateDeviceHost(c *gin.Context) {
var req []dto.HostHelper
if err := helper.CheckBind(&req, c); err != nil {
return
}
if err := deviceService.UpdateHosts(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device passwd
// @Description 修改系统密码
// @Accept json
// @Param request body dto.ChangePasswd true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/passwd [post]
func (b *BaseApi) UpdateDevicePasswd(c *gin.Context) {
var req dto.ChangePasswd
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Passwd) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Passwd)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Passwd = string(password)
}
if err := deviceService.UpdatePasswd(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device swap
// @Description 修改系统 Swap
// @Accept json
// @Param request body dto.SwapHelper true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/swap [post]
// @x-panel-log {"bodyKeys":["operate","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机 swap [path]","formatEN":"[operate] device swap [path]"}
func (b *BaseApi) UpdateDeviceSwap(c *gin.Context) {
var req dto.SwapHelper
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.UpdateSwap(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Check device DNS conf
// @Description 检查系统 DNS 配置可用性
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/check/dns [post]
func (b *BaseApi) CheckDNS(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := deviceService.CheckDNS(req.Key, req.Value)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Device
// @Summary Scan system
// @Description 扫描系统垃圾文件
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/scan [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"扫描系统垃圾文件","formatEN":"scan System Junk Files"}
func (b *BaseApi) ScanSystem(c *gin.Context) {
helper.SuccessWithData(c, deviceService.Scan())
}
// @Tags Device
// @Summary Clean system
// @Description 清理系统垃圾文件
// @Accept json
// @Param request body []dto.Clean true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理系统垃圾文件","formatEN":"Clean system junk files"}
func (b *BaseApi) SystemClean(c *gin.Context) {
var req []dto.Clean
if err := helper.CheckBind(&req, c); err != nil {
return
}
deviceService.Clean(req)
helper.SuccessWithData(c, nil)
}
+1 -24
View File
@@ -83,7 +83,7 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
// @Param request body dto.LogOption true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/logoption/update [post]
// @Router /containers/daemonjson/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"}
func (b *BaseApi) UpdateLogOption(c *gin.Context) {
var req dto.LogOption
@@ -99,29 +99,6 @@ func (b *BaseApi) UpdateLogOption(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Container Docker
// @Summary Update docker daemon.json ipv6 option
// @Description 修改 docker ipv6 配置
// @Accept json
// @Param request body dto.LogOption true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/ipv6option/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json ipv6 配置","formatEN":"Updated the docker daemon.json ipv6 option"}
func (b *BaseApi) UpdateIpv6Option(c *gin.Context) {
var req dto.Ipv6Option
if err := helper.CheckBind(&req, c); err != nil {
return
}
if err := dockerService.UpdateIpv6Option(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Container Docker
// @Summary Update docker daemon.json by upload file
// @Description 上传替换 docker 配置文件
+5 -13
View File
@@ -21,11 +21,9 @@ var (
imageService = service.NewIImageService()
dockerService = service.NewIDockerService()
dbCommonService = service.NewIDBCommonService()
mysqlService = service.NewIMysqlService()
postgresqlService = service.NewIPostgresqlService()
databaseService = service.NewIDatabaseService()
redisService = service.NewIRedisService()
mysqlService = service.NewIMysqlService()
databaseService = service.NewIDatabaseService()
redisService = service.NewIRedisService()
cronjobService = service.NewICronjobService()
@@ -35,9 +33,6 @@ var (
sshService = service.NewISSHService()
firewallService = service.NewIFirewallService()
deviceService = service.NewIDeviceService()
fail2banService = service.NewIFail2BanService()
settingService = service.NewISettingService()
backupService = service.NewIBackupService()
@@ -54,14 +49,11 @@ var (
snapshotService = service.NewISnapshotService()
upgradeService = service.NewIUpgradeService()
runtimeService = service.NewRuntimeService()
processService = service.NewIProcessService()
phpExtensionsService = service.NewIPHPExtensionsService()
runtimeService = service.NewRuntimeService()
processService = service.NewIProcessService()
hostToolService = service.NewIHostToolService()
recycleBinService = service.NewIRecycleBinService()
favoriteService = service.NewIFavoriteService()
websiteCAService = service.NewIWebsiteCAService()
)
-153
View File
@@ -1,153 +0,0 @@
package v1
import (
"os"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags Fail2ban
// @Summary Load fail2ban base info
// @Description 获取 Fail2ban 基础信息
// @Success 200 {object} dto.Fail2BanBaseInfo
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/base [get]
func (b *BaseApi) LoadFail2BanBaseInfo(c *gin.Context) {
data, err := fail2banService.LoadBaseInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Fail2ban
// @Summary Page fail2ban ip list
// @Description 获取 Fail2ban ip
// @Accept json
// @Param request body dto.Fail2BanSearch true "request"
// @Success 200 {Array} string
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/search [post]
func (b *BaseApi) SearchFail2Ban(c *gin.Context) {
var req dto.Fail2BanSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list, err := fail2banService.Search(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Fail2ban
// @Summary Operate fail2ban
// @Description 修改 Fail2ban 状态
// @Accept json
// @Param request body dto.Operate true "request"
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/operate [post]
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] Fail2ban","formatEN":"[operation] Fail2ban"}
func (b *BaseApi) OperateFail2Ban(c *gin.Context) {
var req dto.Operate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fail2banService.Operate(req.Operation); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Fail2ban
// @Summary Operate sshd of fail2ban
// @Description 配置 sshd
// @Accept json
// @Param request body dto.Operate true "request"
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/operate/sshd [post]
func (b *BaseApi) OperateSSHD(c *gin.Context) {
var req dto.Fail2BanSet
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fail2banService.OperateSSHD(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Fail2ban
// @Summary Update fail2ban conf
// @Description 修改 Fail2ban 配置
// @Accept json
// @Param request body dto.Fail2BanUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/update [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 Fail2ban 配置 [key] => [value]","formatEN":"update fail2ban conf [key] => [value]"}
func (b *BaseApi) UpdateFail2BanConf(c *gin.Context) {
var req dto.Fail2BanUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fail2banService.UpdateConf(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Fail2ban
// @Summary Load fail2ban conf
// @Description 获取 fail2ban 配置文件
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/load/conf [get]
func (b *BaseApi) LoadFail2BanConf(c *gin.Context) {
path := "/etc/fail2ban/jail.local"
file, err := os.ReadFile(path)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, string(file))
}
// @Tags Fail2ban
// @Summary Update fail2ban conf by file
// @Description 通过文件修改 fail2ban 配置
// @Accept json
// @Param request body dto.UpdateByFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/update/byconf [post]
func (b *BaseApi) UpdateFail2BanConfByFile(c *gin.Context) {
var req dto.UpdateByFile
if err := helper.CheckBind(&req, c); err != nil {
return
}
if err := fail2banService.UpdateConfByFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
+10 -4
View File
@@ -422,9 +422,11 @@ func (b *BaseApi) MoveFile(c *gin.Context) {
// @Summary Download file
// @Description 下载文件
// @Accept json
// @Param request body request.FileDownload true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/download [get]
// @Router /files/download [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
func (b *BaseApi) Download(c *gin.Context) {
filePath := c.Query("path")
file, err := os.Open(filePath)
@@ -683,7 +685,7 @@ func (b *BaseApi) Keys(c *gin.Context) {
// @Tags File
// @Summary Read file by Line
// @Description 按行读取日志文件
// @Description 按行读取文件
// @Param request body request.FileReadByLineReq true "request"
// @Success 200
// @Security ApiKeyAuth
@@ -693,11 +695,15 @@ func (b *BaseApi) ReadFileByLine(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := fileService.ReadLogByLine(req)
lines, end, err := files.ReadFileByLine(req.Path, req.Page, req.PageSize)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
res := response.FileLineContent{
End: end,
}
res.Path = req.Path
res.Content = strings.Join(lines, "\n")
helper.SuccessWithData(c, res)
}
+2 -18
View File
@@ -35,24 +35,8 @@ func (b *BaseApi) SearchImage(c *gin.Context) {
}
// @Tags Container Image
// @Summary List all images
// @Description 获取所有镜像列表
// @Produce json
// @Success 200 {array} dto.ImageInfo
// @Security ApiKeyAuth
// @Router /containers/image/all [get]
func (b *BaseApi) ListAllImage(c *gin.Context) {
list, err := imageService.ListAll()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Container Image
// @Summary load images options
// @Description 获取镜像名称列表
// @Summary List images
// @Description 获取镜像列表
// @Produce json
// @Success 200 {array} dto.Options
// @Security ApiKeyAuth
+5 -20
View File
@@ -12,7 +12,7 @@ import (
// @Description 获取 OpenResty 配置信息
// @Success 200 {object} response.FileInfo
// @Security ApiKeyAuth
// @Router /openresty [get]
// @Router /openResty [get]
func (b *BaseApi) GetNginx(c *gin.Context) {
fileInfo, err := nginxService.GetNginxConfig()
if err != nil {
@@ -29,7 +29,7 @@ func (b *BaseApi) GetNginx(c *gin.Context) {
// @Param request body request.NginxScopeReq true "request"
// @Success 200 {array} response.NginxParam
// @Security ApiKeyAuth
// @Router /openresty/scope [post]
// @Router /openResty/scope [post]
func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
var req request.NginxScopeReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -51,7 +51,7 @@ func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
// @Param request body request.NginxConfigUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /openresty/update [post]
// @Router /openResty/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":"更新 nginx 配置 [domain]","formatEN":"Update nginx conf [domain]"}
func (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) {
var req request.NginxConfigUpdate
@@ -70,7 +70,7 @@ func (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) {
// @Description 获取 OpenResty 状态信息
// @Success 200 {object} response.NginxStatus
// @Security ApiKeyAuth
// @Router /openresty/status [get]
// @Router /openResty/status [get]
func (b *BaseApi) GetNginxStatus(c *gin.Context) {
res, err := nginxService.GetStatus()
if err != nil {
@@ -87,7 +87,7 @@ func (b *BaseApi) GetNginxStatus(c *gin.Context) {
// @Param request body request.NginxConfigFileUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /openresty/file [post]
// @Router /openResty/file [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 nginx 配置","formatEN":"Update nginx conf"}
func (b *BaseApi) UpdateNginxFile(c *gin.Context) {
var req request.NginxConfigFileUpdate
@@ -101,18 +101,3 @@ func (b *BaseApi) UpdateNginxFile(c *gin.Context) {
}
helper.SuccessWithData(c, nil)
}
// @Tags OpenResty
// @Summary Clear OpenResty proxy cache
// @Description 清理 OpenResty 代理缓存
// @Success 200
// @Security ApiKeyAuth
// @Router /openresty/clear [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理 Openresty 代理缓存","formatEN":"Clear nginx proxy cache"}
func (b *BaseApi) ClearNginxProxyCache(c *gin.Context) {
if err := nginxService.ClearProxyCache(); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
-103
View File
@@ -1,103 +0,0 @@
package v1
import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags PHP Extensions
// @Summary Page Extensions
// @Description Page Extensions
// @Accept json
// @Param request body request.PHPExtensionsSearch true "request"
// @Success 200 {array} response.PHPExtensionsDTO
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/search [post]
func (b *BaseApi) PagePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.All {
list, err := phpExtensionsService.List()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
} else {
total, list, err := phpExtensionsService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: list,
})
}
}
// @Tags PHP Extensions
// @Summary Create Extensions
// @Description Create Extensions
// @Accept json
// @Param request body request.PHPExtensionsCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions [post]
func (b *BaseApi) CreatePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := phpExtensionsService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags PHP Extensions
// @Summary Update Extensions
// @Description Update Extensions
// @Accept json
// @Param request body request.PHPExtensionsUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/update [post]
func (b *BaseApi) UpdatePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := phpExtensionsService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags PHP Extensions
// @Summary Delete Extensions
// @Description Delete Extensions
// @Accept json
// @Param request body request.PHPExtensionsDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/del [post]
func (b *BaseApi) DeletePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := phpExtensionsService.Delete(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
-16
View File
@@ -68,19 +68,3 @@ func (b *BaseApi) ClearRecycleBinFile(c *gin.Context) {
}
helper.SuccessWithOutData(c)
}
// @Tags File
// @Summary Get RecycleBin status
// @Description 获取回收站状态
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /files/recycle/status [get]
func (b *BaseApi) GetRecycleStatus(c *gin.Context) {
settingInfo, err := settingService.GetSettingInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, settingInfo.FileRecycleBin)
}
+2 -3
View File
@@ -46,12 +46,11 @@ func (b *BaseApi) CreateRuntime(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
ssl, err := runtimeService.Create(req)
if err != nil {
if err := runtimeService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, ssl)
helper.SuccessWithOutData(c)
}
// @Tags Website
+68
View File
@@ -217,6 +217,43 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load time zone options
// @Description 加载系统可用时区
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/time/option [get]
func (b *BaseApi) LoadTimeZone(c *gin.Context) {
zones, err := settingService.LoadTimeZone()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, zones)
}
// @Tags System Setting
// @Summary Sync system time
// @Description 系统时间同步
// @Accept json
// @Param request body dto.SyncTime true "request"
// @Success 200 {string} ntime
// @Security ApiKeyAuth
// @Router /settings/time/sync [post]
// @x-panel-log {"bodyKeys":["ntpSite"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"系统时间同步[ntpSite]","formatEN":"sync system time [ntpSite]"}
func (b *BaseApi) SyncTime(c *gin.Context) {
var req dto.SyncTime
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := settingService.SyncTime(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load local backup dir
// @Description 获取安装根目录
@@ -251,6 +288,37 @@ func (b *BaseApi) CleanMonitor(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Scan system
// @Description 扫描系统垃圾文件
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/scan [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"扫描系统垃圾文件","formatEN":"scan System Junk Files"}
func (b *BaseApi) ScanSystem(c *gin.Context) {
helper.SuccessWithData(c, settingService.SystemScan())
}
// @Tags System Setting
// @Summary System clean
// @Description 清理系统垃圾文件
// @Accept json
// @Param request body []dto.Clean true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理系统垃圾文件","formatEN":"Clean system junk files"}
func (b *BaseApi) SystemClean(c *gin.Context) {
var req []dto.Clean
if err := helper.CheckBind(&req, c); err != nil {
return
}
settingService.SystemClean(req)
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load mfa info
// @Description 获取 mfa 信息
+22
View File
@@ -131,6 +131,28 @@ func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Tags SSH
// @Summary Analysis host SSH logs
// @Description 分析 SSH 登录日志
// @Accept json
// @Param request body dto.SearchForAnalysis true "request"
// @Success 200 {array} dto.SSHLogAnalysis
// @Security ApiKeyAuth
// @Router /host/ssh/log/analysis [post]
func (b *BaseApi) AnalysisLog(c *gin.Context) {
var req dto.SearchForAnalysis
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := sshService.AnalysisLog(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags SSH
// @Summary Load host SSH logs
// @Description 获取 SSH 登录日志
+3 -2
View File
@@ -55,6 +55,7 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
return
}
defer client.Close()
sws, err := terminal.NewLogicSshWsSession(cols, rows, true, connInfo.Client, wsConn)
if wshandleError(wsConn, err) {
return
@@ -105,7 +106,7 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
defer killBash(redisConf.ContainerName, commands, pidMap)
defer slave.Close()
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
if wshandleError(wsConn, err) {
return
}
@@ -173,7 +174,7 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
defer killBash(containerID, command, pidMap)
defer slave.Close()
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true)
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
if wshandleError(wsConn, err) {
return
}
+1 -1
View File
@@ -52,7 +52,7 @@ func (b *BaseApi) GetNotesByVersion(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/upgrade [post]
// @x-panel-log {"bodyKeys":["version"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新系统 => [version]","formatEN":"upgrade system => [version]"}
// @x-panel-log {"bodyKeys":["version"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新系统 => [version]","formatEN":"upgrade service => [version]"}
func (b *BaseApi) Upgrade(c *gin.Context) {
var req dto.Upgrade
if err := helper.CheckBindAndValidate(&req, c); err != nil {
-144
View File
@@ -1,144 +0,0 @@
package v1
import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags Website CA
// @Summary Page website ca
// @Description 获取网站 ca 列表分页
// @Accept json
// @Param request body request.WebsiteCASearch true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /websites/ca/search [post]
func (b *BaseApi) PageWebsiteCA(c *gin.Context) {
var req request.WebsiteCASearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, cas, err := websiteCAService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: cas,
})
}
// @Tags Website CA
// @Summary Create website ca
// @Description 创建网站 ca
// @Accept json
// @Param request body request.WebsiteCACreate true "request"
// @Success 200 {object} request.WebsiteCACreate
// @Security ApiKeyAuth
// @Router /websites/ca [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建网站 ca [name]","formatEN":"Create website ca [name]"}
func (b *BaseApi) CreateWebsiteCA(c *gin.Context) {
var req request.WebsiteCACreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := websiteCAService.Create(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Website CA
// @Summary Get website ca
// @Description 获取网站 ca
// @Accept json
// @Param id path int true "id"
// @Success 200 {object} response.WebsiteCADTO
// @Security ApiKeyAuth
// @Router /websites/ca/{id} [get]
func (b *BaseApi) GetWebsiteCA(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
return
}
res, err := websiteCAService.GetCA(id)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Website CA
// @Summary Delete website ca
// @Description 删除网站 ca
// @Accept json
// @Param request body request.WebsiteCommonReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_cas","output_column":"name","output_value":"name"}],"formatZH":"删除网站 ca [name]","formatEN":"Delete website ca [name]"}
func (b *BaseApi) DeleteWebsiteCA(c *gin.Context) {
var req request.WebsiteCommonReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteCAService.Delete(req.ID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags Website CA
// @Summary Obtain SSL
// @Description 自签 SSL 证书
// @Accept json
// @Param request body request.WebsiteCAObtain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/obtain [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_cas","output_column":"name","output_value":"name"}],"formatZH":"自签 SSL 证书 [name]","formatEN":"Obtain SSL [name]"}
func (b *BaseApi) ObtainWebsiteCA(c *gin.Context) {
var req request.WebsiteCAObtain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if _, err := websiteCAService.ObtainSSL(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags Website CA
// @Summary Obtain SSL
// @Description 续签 SSL 证书
// @Accept json
// @Param request body request.WebsiteCAObtain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/renew [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_cas","output_column":"name","output_value":"name"}],"formatZH":"自签 SSL 证书 [name]","formatEN":"Obtain SSL [name]"}
func (b *BaseApi) RenewWebsiteCA(c *gin.Context) {
var req request.WebsiteCARenew
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if _, err := websiteCAService.ObtainSSL(request.WebsiteCAObtain{
SSLID: req.SSLID,
Renew: true,
Unit: "year",
Time: 1,
}); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
+13 -45
View File
@@ -1,10 +1,7 @@
package v1
import (
"net/http"
"net/url"
"reflect"
"strconv"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -69,24 +66,24 @@ func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
}
// @Tags Website SSL
// @Summary Apply ssl
// @Description 申请证书
// @Summary Reset website ssl
// @Description 重置网站 ssl
// @Accept json
// @Param request body request.WebsiteSSLApply true "request"
// @Param request body request.WebsiteSSLRenew true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/obtain [post]
// @x-panel-log {"bodyKeys":["ID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ID","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"申请证书 [domain]","formatEN":"apply ssl [domain]"}
func (b *BaseApi) ApplyWebsiteSSL(c *gin.Context) {
var req request.WebsiteSSLApply
// @Router /websites/ssl/renew [post]
// @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"SSLId","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"重置 ssl [domain]","formatEN":"Renew ssl [domain]"}
func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) {
var req request.WebsiteSSLRenew
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteSSLService.ObtainSSL(req); err != nil {
if err := websiteSSLService.Renew(req.SSLID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
helper.SuccessWithData(c, nil)
}
// @Tags Website SSL
@@ -114,17 +111,17 @@ func (b *BaseApi) GetDNSResolve(c *gin.Context) {
// @Summary Delete website ssl
// @Description 删除网站 ssl
// @Accept json
// @Param request body request.WebsiteBatchDelReq true "request"
// @Param request body request.WebsiteResourceReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
var req request.WebsiteBatchDelReq
var req request.WebsiteResourceReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteSSLService.Delete(req.IDs); err != nil {
if err := websiteSSLService.Delete(req.ID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@@ -216,32 +213,3 @@ func (b *BaseApi) UploadWebsiteSSL(c *gin.Context) {
}
helper.SuccessWithData(c, nil)
}
// @Tags Website SSL
// @Summary Download SSL file
// @Description 下载证书文件
// @Accept json
// @Param request body request.WebsiteResourceReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/download [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"下载证书文件 [domain]","formatEN":"download ssl file [domain]"}
func (b *BaseApi) DownloadWebsiteSSL(c *gin.Context) {
var req request.WebsiteResourceReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
file, err := websiteSSLService.DownloadFile(req.ID)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
info, err := file.Stat()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
}
-6
View File
@@ -13,18 +13,12 @@ type AppDatabase struct {
type AuthParam struct {
RootPassword string `json:"PANEL_DB_ROOT_PASSWORD"`
RootUser string `json:"PANEL_DB_ROOT_USER"`
}
type RedisAuthParam struct {
RootPassword string `json:"PANEL_REDIS_ROOT_PASSWORD"`
}
type MinioAuthParam struct {
RootPassword string `json:"PANEL_MINIO_ROOT_PASSWORD"`
RootUser string `json:"PANEL_MINIO_ROOT_USER"`
}
type ContainerExec struct {
ContainerName string `json:"containerName"`
DbParam AppDatabase `json:"dbParam"`
+4 -16
View File
@@ -21,24 +21,18 @@ type BackupInfo struct {
Vars string `json:"vars"`
}
type OneDriveInfo struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RedirectUri string `json:"redirect_uri"`
}
type BackupSearchFile struct {
Type string `json:"type" validate:"required"`
}
type CommonBackup struct {
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
Name string `json:"name"`
DetailName string `json:"detailName"`
}
type CommonRecover struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
Name string `json:"name"`
DetailName string `json:"detailName"`
File string `json:"file"`
@@ -51,11 +45,6 @@ type RecordSearch struct {
DetailName string `json:"detailName"`
}
type RecordSearchByCronjob struct {
PageInfo
CronjobID uint `json:"cronjobID" validate:"required"`
}
type BackupRecords struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
@@ -63,11 +52,10 @@ type BackupRecords struct {
BackupType string `json:"backupType"`
FileDir string `json:"fileDir"`
FileName string `json:"fileName"`
Size int64 `json:"size"`
}
type DownloadRecord struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"`
FileDir string `json:"fileDir" validate:"required"`
FileName string `json:"fileName" validate:"required"`
}
-2
View File
@@ -2,8 +2,6 @@ package dto
type SearchCommandWithPage struct {
SearchWithPage
OrderBy string `json:"orderBy"`
Order string `json:"order"`
GroupID uint `json:"groupID"`
Info string `json:"info"`
}
-9
View File
@@ -41,15 +41,6 @@ type DeleteByName struct {
Name string `json:"name" validate:"required"`
}
type UpdateByFile struct {
File string `json:"file"`
}
type UpdateByNameAndFile struct {
Name string `json:"name"`
File string `json:"file"`
}
type OperationWithNameAndType struct {
Name string `json:"name"`
Type string `json:"type" validate:"required"`
+14 -29
View File
@@ -4,12 +4,11 @@ import "time"
type PageContainer struct {
PageInfo
Name string `json:"name"`
State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
Filters string `json:"filters"`
ExcludeAppStore bool `json:"excludeAppStore"`
Name string `json:"name"`
State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
Filters string `json:"filters"`
}
type InspectReq struct {
@@ -31,15 +30,11 @@ type ContainerInfo struct {
IsFromApp bool `json:"isFromApp"`
IsFromCompose bool `json:"isFromCompose"`
AppName string `json:"appName"`
AppInstallName string `json:"appInstallName"`
Websites []string `json:"websites"`
}
type ResourceLimit struct {
CPU int `json:"cpu"`
Memory uint64 `json:"memory"`
CPU int `json:"cpu"`
Memory int `json:"memory"`
}
type ContainerOperate struct {
@@ -57,7 +52,6 @@ type ContainerOperate struct {
CPUShares int64 `json:"cpuShares"`
NanoCPUs float64 `json:"nanoCPUs"`
Memory float64 `json:"memory"`
Privileged bool `json:"privileged"`
AutoRemove bool `json:"autoRemove"`
Volumes []VolumeHelper `json:"volumes"`
Labels []string `json:"labels"`
@@ -141,21 +135,13 @@ type Network struct {
Attachable bool `json:"attachable"`
}
type NetworkCreate struct {
Name string `json:"name" validate:"required"`
Driver string `json:"driver" validate:"required"`
Options []string `json:"options"`
Ipv4 bool `json:"ipv4"`
Subnet string `json:"subnet"`
Gateway string `json:"gateway"`
IPRange string `json:"ipRange"`
AuxAddress []SettingUpdate `json:"auxAddress"`
Ipv6 bool `json:"ipv6"`
SubnetV6 string `json:"subnetV6"`
GatewayV6 string `json:"gatewayV6"`
IPRangeV6 string `json:"ipRangeV6"`
AuxAddressV6 []SettingUpdate `json:"auxAddressV6"`
Labels []string `json:"labels"`
Name string `json:"name" validate:"required"`
Driver string `json:"driver" validate:"required"`
Options []string `json:"options"`
Subnet string `json:"subnet"`
Gateway string `json:"gateway"`
IPRange string `json:"ipRange"`
Labels []string `json:"labels"`
}
type Volume struct {
@@ -173,7 +159,6 @@ type VolumeCreate struct {
}
type BatchDelete struct {
Force bool `json:"force"`
Names []string `json:"names" validate:"required"`
}
+43 -33
View File
@@ -3,43 +3,49 @@ package dto
import "time"
type CronjobCreate struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
Spec string `json:"spec" validate:"required"`
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
SpecType string `json:"specType" validate:"required"`
Week int `json:"week" validate:"number,max=6,min=0"`
Day int `json:"day" validate:"number"`
Hour int `json:"hour" validate:"number"`
Minute int `json:"minute" validate:"number"`
Second int `json:"second" validate:"number"`
Script string `json:"script"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBType string `json:"dbType"`
DBName string `json:"dbName"`
URL string `json:"url"`
SourceDir string `json:"sourceDir"`
BackupAccounts string `json:"backupAccounts"`
DefaultDownload string `json:"defaultDownload"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
KeepLocal bool `json:"keepLocal"`
TargetDirID int `json:"targetDirID"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
}
type CronjobUpdate struct {
ID uint `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Spec string `json:"spec" validate:"required"`
ID uint `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
SpecType string `json:"specType" validate:"required"`
Week int `json:"week" validate:"number,max=6,min=0"`
Day int `json:"day" validate:"number"`
Hour int `json:"hour" validate:"number"`
Minute int `json:"minute" validate:"number"`
Second int `json:"second" validate:"number"`
Script string `json:"script"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBType string `json:"dbType"`
DBName string `json:"dbName"`
URL string `json:"url"`
SourceDir string `json:"sourceDir"`
BackupAccounts string `json:"backupAccounts"`
DefaultDownload string `json:"defaultDownload"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
KeepLocal bool `json:"keepLocal"`
TargetDirID int `json:"targetDirID"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
}
type CronjobUpdateStatus struct {
@@ -53,7 +59,6 @@ type CronjobDownload struct {
}
type CronjobClean struct {
IsDelete bool `json:"isDelete"`
CleanData bool `json:"cleanData"`
CronjobID uint `json:"cronjobID" validate:"required"`
}
@@ -64,23 +69,28 @@ type CronjobBatchDelete struct {
}
type CronjobInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Spec string `json:"spec"`
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
SpecType string `json:"specType"`
Week int `json:"week"`
Day int `json:"day"`
Hour int `json:"hour"`
Minute int `json:"minute"`
Second int `json:"second"`
Script string `json:"script"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBType string `json:"dbType"`
DBName string `json:"dbName"`
URL string `json:"url"`
SourceDir string `json:"sourceDir"`
BackupAccounts string `json:"backupAccounts"`
DefaultDownload string `json:"defaultDownload"`
RetainCopies int `json:"retainCopies"`
Script string `json:"script"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBName string `json:"dbName"`
URL string `json:"url"`
SourceDir string `json:"sourceDir"`
KeepLocal bool `json:"keepLocal"`
TargetDir string `json:"targetDir"`
TargetDirID int `json:"targetDirID"`
RetainCopies int `json:"retainCopies"`
LastRecordTime string `json:"lastRecordTime"`
Status string `json:"status"`
+1 -14
View File
@@ -24,14 +24,6 @@ type DashboardBase struct {
CurrentInfo DashboardCurrent `json:"currentInfo"`
}
type OsInfo struct {
OS string `json:"os"`
Platform string `json:"platform"`
PlatformFamily string `json:"platformFamily"`
KernelArch string `json:"kernelArch"`
KernelVersion string `json:"kernelVersion"`
}
type DashboardCurrent struct {
Uptime uint64 `json:"uptime"`
TimeSinceUptime string `json:"timeSinceUptime"`
@@ -51,12 +43,7 @@ type DashboardCurrent struct {
MemoryTotal uint64 `json:"memoryTotal"`
MemoryAvailable uint64 `json:"memoryAvailable"`
MemoryUsed uint64 `json:"memoryUsed"`
MemoryUsedPercent float64 `json:"memoryUsedPercent"`
SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
SwapMemoryAvailable uint64 `json:"swapMemoryAvailable"`
SwapMemoryUsed uint64 `json:"swapMemoryUsed"`
SwapMemoryUsedPercent float64 `json:"swapMemoryUsedPercent"`
MemoryUsedPercent float64 `json:"MemoryUsedPercent"`
IOReadBytes uint64 `json:"ioReadBytes"`
IOWriteBytes uint64 `json:"ioWriteBytes"`
+45 -83
View File
@@ -2,27 +2,6 @@ package dto
import "time"
// common
type DBConfUpdateByFile struct {
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis"`
Database string `json:"database" validate:"required"`
File string `json:"file"`
}
type ChangeDBInfo struct {
ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql"`
Database string `json:"database" validate:"required"`
Value string `json:"value" validate:"required"`
}
type DBBaseInfo struct {
Name string `json:"name"`
ContainerName string `json:"containerName"`
Port int64 `json:"port"`
}
// mysql
type MysqlDBSearch struct {
PageInfo
Info string `json:"info"`
@@ -64,14 +43,6 @@ type MysqlDBCreate struct {
Description string `json:"description"`
}
type BindUser struct {
Database string `json:"database" validate:"required"`
DB string `json:"db" validate:"required"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Permission string `json:"permission" validate:"required"`
}
type MysqlLoadDB struct {
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
@@ -162,6 +133,25 @@ type MysqlVariablesUpdateHelper struct {
Param string `json:"param"`
Value interface{} `json:"value"`
}
type MysqlConfUpdateByFile struct {
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
File string `json:"file"`
}
type ChangeDBInfo struct {
ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
Value string `json:"value" validate:"required"`
}
type DBBaseInfo struct {
Name string `json:"name"`
ContainerName string `json:"containerName"`
Port int64 `json:"port"`
}
// redis
type ChangeRedisPass struct {
@@ -237,24 +227,17 @@ type DatabaseSearch struct {
}
type DatabaseInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name" validate:"max=256"`
From string `json:"from"`
Type string `json:"type"`
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Description string `json:"description"`
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name" validate:"max=256"`
From string `json:"from"`
Type string `json:"type"`
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
}
type DatabaseOption struct {
@@ -266,47 +249,26 @@ type DatabaseOption struct {
Address string `json:"address"`
}
type DatabaseItem struct {
ID uint `json:"id"`
From string `json:"from"`
Database string `json:"database"`
Name string `json:"name"`
}
type DatabaseCreate struct {
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Description string `json:"description"`
}
type DatabaseUpdate struct {
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Description string `json:"description"`
}
-85
View File
@@ -1,85 +0,0 @@
package dto
import "time"
type PostgresqlDBSearch struct {
PageInfo
Info string `json:"info"`
Database string `json:"database" validate:"required"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
}
type PostgresqlDBInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name"`
From string `json:"from"`
PostgresqlName string `json:"postgresqlName"`
Format string `json:"format"`
Username string `json:"username"`
Password string `json:"password"`
SuperUser bool `json:"superUser"`
BackupCount int `json:"backupCount"`
Description string `json:"description"`
}
type PostgresqlOption struct {
ID uint `json:"id"`
From string `json:"from"`
Type string `json:"type"`
Database string `json:"database"`
Name string `json:"name"`
}
type PostgresqlDBCreate struct {
Name string `json:"name" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Database string `json:"database" validate:"required"`
Format string `json:"format"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SuperUser bool `json:"superUser"`
Description string `json:"description"`
}
type PostgresqlBindUser struct {
Name string `json:"name" validate:"required"`
Database string `json:"database" validate:"required"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SuperUser bool `json:"superUser"`
}
type PostgresqlPrivileges struct {
Name string `json:"name" validate:"required"`
Database string `json:"database" validate:"required"`
Username string `json:"username" validate:"required"`
SuperUser bool `json:"superUser"`
}
type PostgresqlLoadDB struct {
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=postgresql"`
Database string `json:"database" validate:"required"`
}
type PostgresqlDBDeleteCheck struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required,oneof=postgresql"`
Database string `json:"database" validate:"required"`
}
type PostgresqlDBDelete struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required,oneof=postgresql"`
Database string `json:"database" validate:"required"`
ForceDelete bool `json:"forceDelete"`
DeleteBackup bool `json:"deleteBackup"`
}
type PostgresqlConfUpdateByFile struct {
Type string `json:"type" validate:"required,oneof=postgresql mariadb"`
Database string `json:"database" validate:"required"`
File string `json:"file"`
}
-41
View File
@@ -1,41 +0,0 @@
package dto
type DeviceBaseInfo struct {
DNS []string `json:"dns"`
Hosts []HostHelper `json:"hosts"`
Hostname string `json:"hostname"`
TimeZone string `json:"timeZone"`
LocalTime string `json:"localTime"`
Ntp string `json:"ntp"`
User string `json:"user"`
SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
SwapMemoryAvailable uint64 `json:"swapMemoryAvailable"`
SwapMemoryUsed uint64 `json:"swapMemoryUsed"`
MaxSize uint64 `json:"maxSize"`
SwapDetails []SwapHelper `json:"swapDetails"`
}
type HostHelper struct {
IP string `json:"ip"`
Host string `json:"host"`
}
type SwapHelper struct {
Path string `json:"path" validate:"required"`
Size uint64 `json:"size"`
Used string `json:"used"`
IsNew bool `json:"isNew"`
}
type TimeZoneOptions struct {
From string `json:"from"`
Zones []string `json:"zones"`
}
type ChangePasswd struct {
User string `json:"user"`
Passwd string `json:"passwd"`
}
-11
View File
@@ -14,11 +14,6 @@ type DaemonJsonConf struct {
IPTables bool `json:"iptables"`
CgroupDriver string `json:"cgroupDriver"`
Ipv6 bool `json:"ipv6"`
FixedCidrV6 string `json:"fixedCidrV6"`
Ip6Tables bool `json:"ip6Tables"`
Experimental bool `json:"experimental"`
LogMaxSize string `json:"logMaxSize"`
LogMaxFile string `json:"logMaxFile"`
}
@@ -28,12 +23,6 @@ type LogOption struct {
LogMaxFile string `json:"logMaxFile"`
}
type Ipv6Option struct {
FixedCidrV6 string `json:"fixedCidrV6"`
Ip6Tables bool `json:"ip6Tables" validate:"required"`
Experimental bool `json:"experimental"`
}
type DockerOperation struct {
Operation string `json:"operation" validate:"required,oneof=start restart stop"`
}
-29
View File
@@ -1,29 +0,0 @@
package dto
type Fail2BanBaseInfo struct {
IsEnable bool `json:"isEnable"`
IsActive bool `json:"isActive"`
IsExist bool `json:"isExist"`
Version string `json:"version"`
Port int `json:"port"`
MaxRetry int `json:"maxRetry"`
BanTime string `json:"banTime"`
FindTime string `json:"findTime"`
BanAction string `json:"banAction"`
LogPath string `json:"logPath"`
}
type Fail2BanSearch struct {
Status string `json:"status" validate:"required,oneof=banned ignore"`
}
type Fail2BanUpdate struct {
Key string `json:"key" validate:"required,oneof=port bantime findtime maxretry banaction logpath port"`
Value string `json:"value"`
}
type Fail2BanSet struct {
IPs []string `json:"ips"`
Operate string `json:"operate" validate:"required,oneof=banned ignore"`
}
+1 -1
View File
@@ -16,7 +16,7 @@ type RuleSearch struct {
}
type FirewallOperation struct {
Operation string `json:"operation" validate:"required,oneof=start stop restart disablePing enablePing"`
Operation string `json:"operation" validate:"required,oneof=start stop disablePing enablePing"`
}
type PortRuleOperate struct {
+1
View File
@@ -27,6 +27,7 @@ type ImagePull struct {
}
type ImageTag struct {
RepoID uint `json:"repoID"`
SourceID string `json:"sourceID" validate:"required"`
TargetName string `json:"targetName" validate:"required"`
}
-2
View File
@@ -32,7 +32,6 @@ type AppContainerConfig struct {
EditCompose bool `json:"editCompose"`
DockerCompose string `json:"dockerCompose"`
HostMode bool `json:"hostMode"`
PullImage bool `json:"pullImage"`
}
type AppInstalledSearch struct {
@@ -68,7 +67,6 @@ type AppInstalledOperate struct {
DeleteBackup bool `json:"deleteBackup"`
DeleteDB bool `json:"deleteDB"`
Backup bool `json:"backup"`
PullImage bool `json:"pullImage"`
}
type AppInstalledUpdate struct {
+5 -8
View File
@@ -64,7 +64,7 @@ type FileDeCompress struct {
type FileEdit struct {
Path string `json:"path" validate:"required"`
Content string `json:"content"`
Content string `json:"content" validate:"required"`
}
type FileRename struct {
@@ -77,10 +77,9 @@ type FilePathCheck struct {
}
type FileWget struct {
Url string `json:"url" validate:"required"`
Path string `json:"path" validate:"required"`
Name string `json:"name" validate:"required"`
IgnoreCertificate bool `json:"ignoreCertificate"`
Url string `json:"url" validate:"required"`
Path string `json:"path" validate:"required"`
Name string `json:"name" validate:"required"`
}
type FileMove struct {
@@ -119,11 +118,9 @@ type FileRoleUpdate struct {
}
type FileReadByLineReq struct {
Path string `json:"path" validate:"required"`
Page int `json:"page" validate:"required"`
PageSize int `json:"pageSize" validate:"required"`
Type string `json:"type" validate:"required"`
ID uint `json:"ID"`
Name string `json:"name"`
}
type FileExistReq struct {
-22
View File
@@ -1,22 +0,0 @@
package request
import "github.com/1Panel-dev/1Panel/backend/app/dto"
type PHPExtensionsSearch struct {
dto.PageInfo
All bool `json:"all"`
}
type PHPExtensionsCreate struct {
Name string `json:"name" validate:"required"`
Extensions string `json:"extensions" validate:"required"`
}
type PHPExtensionsUpdate struct {
ID uint `json:"id" validate:"required"`
Extensions string `json:"extensions" validate:"required"`
}
type PHPExtensionsDelete struct {
ID uint `json:"id" validate:"required"`
}
+1 -1
View File
@@ -210,7 +210,7 @@ type WebsiteRedirectReq struct {
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 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"`
}
type WebsiteCommonReq struct {
+3 -58
View File
@@ -14,12 +14,6 @@ type WebsiteSSLCreate struct {
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
DnsAccountID uint `json:"dnsAccountId"`
AutoRenew bool `json:"autoRenew"`
KeyType string `json:"keyType"`
Apply bool `json:"apply"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
ID uint `json:"id"`
Description string `json:"description"`
}
type WebsiteDNSReq struct {
@@ -31,17 +25,8 @@ type WebsiteSSLRenew struct {
SSLID uint `json:"SSLId" validate:"required"`
}
type WebsiteSSLApply struct {
ID uint `json:"ID" validate:"required"`
SkipDNSCheck bool `json:"SkipDNSCheck"`
}
type WebsiteAcmeAccountCreate struct {
Email string `json:"email" validate:"required"`
Type string `json:"type" validate:"required,oneof=letsencrypt zerossl buypass google"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
EabKid string `json:"eabKid"`
EabHmacKey string `json:"eabHmacKey"`
Email string `json:"email" validate:"required"`
}
type WebsiteDnsAccountCreate struct {
@@ -61,14 +46,9 @@ type WebsiteResourceReq struct {
ID uint `json:"id" validate:"required"`
}
type WebsiteBatchDelReq struct {
IDs []uint `json:"ids" validate:"required"`
}
type WebsiteSSLUpdate struct {
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
Description string `json:"description"`
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
}
type WebsiteSSLUpload struct {
@@ -77,39 +57,4 @@ type WebsiteSSLUpload struct {
PrivateKeyPath string `json:"privateKeyPath"`
CertificatePath string `json:"certificatePath"`
Type string `json:"type" validate:"required,oneof=paste local"`
SSLID uint `json:"sslID"`
Description string `json:"description"`
}
type WebsiteCASearch struct {
dto.PageInfo
}
type WebsiteCACreate struct {
CommonName string `json:"commonName" validate:"required"`
Country string `json:"country" validate:"required"`
Organization string `json:"organization" validate:"required"`
OrganizationUint string `json:"organizationUint"`
Name string `json:"name" validate:"required"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
Province string `json:"province" `
City string `json:"city"`
}
type WebsiteCAObtain struct {
ID uint `json:"id" validate:"required"`
Domains string `json:"domains" validate:"required"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
Time int `json:"time" validate:"required"`
Unit string `json:"unit" validate:"required"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
AutoRenew bool `json:"autoRenew"`
Renew bool `json:"renew"`
SSLID uint `json:"sslID"`
Description string `json:"description"`
}
type WebsiteCARenew struct {
SSLID uint `json:"SSLID" validate:"required"`
}
+4 -7
View File
@@ -1,10 +1,9 @@
package response
import (
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
)
@@ -73,11 +72,9 @@ type AppInstalledDTO struct {
}
type DatabaseConn struct {
Username string `json:"username"`
Password string `json:"password"`
ContainerName string `json:"containerName"`
ServiceName string `json:"serviceName"`
Port int64 `json:"port"`
Password string `json:"password"`
ServiceName string `json:"serviceName"`
Port int64 `json:"port"`
}
type AppService struct {
@@ -1,7 +0,0 @@
package response
import "github.com/1Panel-dev/1Panel/backend/app/model"
type PHPExtensionsDTO struct {
model.PHPExtensions
}
-6
View File
@@ -13,12 +13,6 @@ type WebsiteDTO struct {
RuntimeName string `json:"runtimeName"`
}
type WebsiteOption struct {
ID uint `json:"id"`
PrimaryDomain string `json:"primaryDomain"`
Alias string `json:"alias"`
}
type WebsitePreInstallCheck struct {
Name string `json:"name"`
Status string `json:"status"`
-11
View File
@@ -4,7 +4,6 @@ import "github.com/1Panel-dev/1Panel/backend/app/model"
type WebsiteSSLDTO struct {
model.WebsiteSSL
LogPath string `json:"logPath"`
}
type WebsiteDNSRes struct {
@@ -22,13 +21,3 @@ type WebsiteDnsAccountDTO struct {
model.WebsiteDnsAccount
Authorization map[string]string `json:"authorization"`
}
type WebsiteCADTO struct {
model.WebsiteCA
CommonName string `json:"commonName" `
Country string `json:"country"`
Organization string `json:"organization"`
OrganizationUint string `json:"organizationUint"`
Province string `json:"province" `
City string `json:"city"`
}
+16 -21
View File
@@ -3,11 +3,10 @@ package dto
import "time"
type SettingInfo struct {
UserName string `json:"userName"`
Email string `json:"email"`
SystemIP string `json:"systemIP"`
SystemVersion string `json:"systemVersion"`
DockerSockPath string `json:"dockerSockPath"`
UserName string `json:"userName"`
Email string `json:"email"`
SystemIP string `json:"systemIP"`
SystemVersion string `json:"systemVersion"`
SessionTimeout string `json:"sessionTimeout"`
LocalTime string `json:"localTime"`
@@ -50,8 +49,6 @@ type SettingInfo struct {
AppStoreVersion string `json:"appStoreVersion"`
AppStoreLastModified string `json:"appStoreLastModified"`
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
FileRecycleBin string `json:"fileRecycleBin"`
}
type SettingUpdate struct {
@@ -60,7 +57,7 @@ type SettingUpdate struct {
}
type SSLUpdate struct {
SSLType string `json:"sslType" validate:"required,oneof=self select import import-paste import-local"`
SSLType string `json:"sslType"`
Domain string `json:"domain"`
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
Cert string `json:"cert"`
@@ -99,10 +96,9 @@ type SnapshotStatus struct {
}
type SnapshotCreate struct {
ID uint `json:"id"`
From string `json:"from" validate:"required"`
DefaultDownload string `json:"defaultDownload" validate:"required"`
Description string `json:"description" validate:"max=256"`
ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive"`
Description string `json:"description" validate:"max=256"`
}
type SnapshotRecover struct {
IsNew bool `json:"isNew"`
@@ -115,15 +111,14 @@ type SnapshotImport struct {
Description string `json:"description" validate:"max=256"`
}
type SnapshotInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Description string `json:"description" validate:"max=256"`
From string `json:"from"`
DefaultDownload string `json:"defaultDownload"`
Status string `json:"status"`
Message string `json:"message"`
CreatedAt time.Time `json:"createdAt"`
Version string `json:"version"`
ID uint `json:"id"`
Name string `json:"name"`
Description string `json:"description" validate:"max=256"`
From string `json:"from"`
Status string `json:"status"`
Message string `json:"message"`
CreatedAt time.Time `json:"createdAt"`
Version string `json:"version"`
InterruptStep string `json:"interruptStep"`
RecoverStatus string `json:"recoverStatus"`
+20
View File
@@ -44,6 +44,26 @@ type SSHLog struct {
FailedCount int `json:"failedCount"`
}
type SearchForAnalysis struct {
PageInfo
OrderBy string `json:"orderBy" validate:"required,oneof=Success Failed"`
}
type AnalysisRes struct {
Total int64 `json:"total"`
Items []SSHLogAnalysis `json:"items"`
SuccessfulCount int `json:"successfulCount"`
FailedCount int `json:"failedCount"`
}
type SSHLogAnalysis struct {
Address string `json:"address"`
Area string `json:"area"`
SuccessfulCount int `json:"successfulCount"`
FailedCount int `json:"failedCount"`
Status string `json:"status"`
}
type SSHHistory struct {
Date time.Time `json:"date"`
DateStr string `json:"dateStr"`
-17
View File
@@ -1,11 +1,5 @@
package model
import (
"github.com/1Panel-dev/1Panel/backend/constant"
"path/filepath"
"strings"
)
type App struct {
BaseModel
Name string `json:"name" gorm:"type:varchar(64);not null"`
@@ -30,14 +24,3 @@ type App struct {
TagsKey []string `json:"tags" yaml:"tags" gorm:"-"`
AppTags []AppTag `json:"-" gorm:"-:migration"`
}
func (i *App) IsLocalApp() bool {
return i.Resource == constant.ResourceLocal
}
func (i *App) GetAppResourcePath() string {
if i.IsLocalApp() {
//这里要去掉本地应用的local前缀
return filepath.Join(constant.LocalAppResourceDir, strings.TrimPrefix(i.Key, "local"))
}
return filepath.Join(constant.RemoteAppResourceDir, i.Key)
}
-2
View File
@@ -12,8 +12,6 @@ type BackupAccount struct {
type BackupRecord struct {
BaseModel
From string `gorm:"type:varchar(64)" json:"from"`
CronjobID uint `gorm:"type:decimal" json:"cronjobID"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Name string `gorm:"type:varchar(64);not null" json:"name"`
DetailName string `gorm:"type:varchar(256)" json:"detailName"`
+15 -14
View File
@@ -5,31 +5,32 @@ import "time"
type Cronjob struct {
BaseModel
Name string `gorm:"type:varchar(64);not null" json:"name"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Spec string `gorm:"type:varchar(64);not null" json:"spec"`
Name string `gorm:"type:varchar(64);not null;unique" json:"name"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
SpecType string `gorm:"type:varchar(64);not null" json:"specType"`
Spec string `gorm:"type:varchar(64);not null" json:"spec"`
Week uint64 `gorm:"type:decimal" json:"week"`
Day uint64 `gorm:"type:decimal" json:"day"`
Hour uint64 `gorm:"type:decimal" json:"hour"`
Minute uint64 `gorm:"type:decimal" json:"minute"`
Second uint64 `gorm:"type:decimal" json:"second"`
ContainerName string `gorm:"type:varchar(64)" json:"containerName"`
Script string `gorm:"longtext" json:"script"`
Website string `gorm:"type:varchar(64)" json:"website"`
AppID string `gorm:"type:varchar(64)" json:"appID"`
DBType string `gorm:"type:varchar(64)" json:"dbType"`
DBName string `gorm:"type:varchar(64)" json:"dbName"`
URL string `gorm:"type:varchar(256)" json:"url"`
SourceDir string `gorm:"type:varchar(256)" json:"sourceDir"`
ExclusionRules string `gorm:"longtext" json:"exclusionRules"`
// 已废弃
KeepLocal bool `gorm:"type:varchar(64)" json:"keepLocal"`
TargetDirID uint64 `gorm:"type:decimal" json:"targetDirID"`
KeepLocal bool `gorm:"type:varchar(64)" json:"keepLocal"`
TargetDirID uint64 `gorm:"type:decimal" json:"targetDirID"`
RetainCopies uint64 `gorm:"type:decimal" json:"retainCopies"`
BackupAccounts string `gorm:"type:varchar(64)" json:"backupAccounts"`
DefaultDownload string `gorm:"type:varchar(64)" json:"defaultDownload"`
RetainCopies uint64 `gorm:"type:decimal" json:"retainCopies"`
Status string `gorm:"type:varchar(64)" json:"status"`
EntryIDs string `gorm:"type:varchar(64)" json:"entryIDs"`
Records []JobRecords `json:"records"`
Status string `gorm:"type:varchar(64)" json:"status"`
EntryID uint64 `gorm:"type:decimal" json:"entryID"`
Records []JobRecords `json:"records"`
}
type JobRecords struct {
+1 -8
View File
@@ -11,12 +11,5 @@ type Database struct {
Port uint `json:"port" gorm:"type:decimal;not null"`
Username string `json:"username" gorm:"type:varchar(64)"`
Password string `json:"password" gorm:"type:varchar(64)"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert" gorm:"type:longText"`
ClientKey string `json:"clientKey" gorm:"type:longText"`
ClientCert string `json:"clientCert" gorm:"type:longText"`
SkipVerify bool `json:"skipVerify"`
Description string `json:"description" gorm:"type:varchar(256);"`
Description string `json:"description" gorm:"type:varchar(256);"`
}
-13
View File
@@ -1,13 +0,0 @@
package model
type DatabasePostgresql struct {
BaseModel
Name string `json:"name" gorm:"type:varchar(256);not null"`
From string `json:"from" gorm:"type:varchar(256);not null;default:local"`
PostgresqlName string `json:"postgresqlName" gorm:"type:varchar(64);not null"`
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)" `
Description string `json:"description" gorm:"type:varchar(256);"`
}
-7
View File
@@ -1,7 +0,0 @@
package model
type PHPExtensions struct {
BaseModel
Name string ` json:"name" gorm:"not null"`
Extensions string `json:"extensions" gorm:"not null"`
}
+6 -7
View File
@@ -2,13 +2,12 @@ package model
type Snapshot struct {
BaseModel
Name string `json:"name" gorm:"type:varchar(64);not null;unique"`
Description string `json:"description" gorm:"type:varchar(256)"`
From string `json:"from"`
DefaultDownload string `json:"defaultDownload" gorm:"type:varchar(64)"`
Status string `json:"status" gorm:"type:varchar(64)"`
Message string `json:"message" gorm:"type:varchar(256)"`
Version string `json:"version" gorm:"type:varchar(256)"`
Name string `json:"name" gorm:"type:varchar(64);not null;unique"`
Description string `json:"description" gorm:"type:varchar(256)"`
From string `json:"from"`
Status string `json:"status" gorm:"type:varchar(64)"`
Message string `json:"message" gorm:"type:varchar(256)"`
Version string `json:"version" gorm:"type:varchar(256)"`
InterruptStep string `json:"interruptStep" gorm:"type:varchar(64)"`
RecoverStatus string `json:"recoverStatus" gorm:"type:varchar(64)"`
+3 -7
View File
@@ -2,13 +2,9 @@ package model
type WebsiteAcmeAccount struct {
BaseModel
Email string `gorm:"not null" json:"email"`
URL string `gorm:"not null" json:"url"`
PrivateKey string `gorm:"not null" json:"-"`
Type string `gorm:"not null;default:letsencrypt" json:"type"`
EabKid string `gorm:"default:null;" json:"eabKid"`
EabHmacKey string `gorm:"default:null" json:"eabHmacKey"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
Email string `gorm:"type:varchar(256);not null" json:"email"`
URL string `gorm:"type:varchar(256);not null" json:"url"`
PrivateKey string `gorm:"type:longtext;not null" json:"-"`
}
func (w WebsiteAcmeAccount) TableName() string {
-9
View File
@@ -1,9 +0,0 @@
package model
type WebsiteCA struct {
BaseModel
CSR string `gorm:"not null;" json:"csr"`
Name string `gorm:"not null;" json:"name"`
PrivateKey string `gorm:"not null" json:"privateKey"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
}
+1 -18
View File
@@ -1,11 +1,6 @@
package model
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/constant"
"path"
"time"
)
import "time"
type WebsiteSSL struct {
BaseModel
@@ -19,26 +14,14 @@ type WebsiteSSL struct {
Organization string `gorm:"type:varchar(64);not null" json:"organization"`
DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"`
AcmeAccountID uint `gorm:"type:integer;not null" json:"acmeAccountId"`
CaID uint `gorm:"type:integer;not null;default:0" json:"caId"`
AutoRenew bool `gorm:"type:varchar(64);not null" json:"autoRenew"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
Status string `gorm:"not null;default:ready" json:"status"`
Message string `json:"message"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
PushDir bool `gorm:"not null;default:0" json:"pushDir"`
Dir string `json:"dir"`
Description string `json:"description"`
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
Websites []Website `json:"websites" gorm:"-:migration"`
}
func (w WebsiteSSL) TableName() string {
return "website_ssls"
}
func (w WebsiteSSL) GetLogPath() string {
return path.Join(constant.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", w.PrimaryDomain, w.ID))
}
-10
View File
@@ -17,7 +17,6 @@ type IAppRepo interface {
OrderByRecommend() DBOption
GetRecommend() DBOption
WithResource(resource string) DBOption
WithLikeName(name string) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.App, error)
GetFirst(opts ...DBOption) (model.App, error)
GetBy(opts ...DBOption) ([]model.App, error)
@@ -32,15 +31,6 @@ func NewIAppRepo() IAppRepo {
return &AppRepo{}
}
func (a AppRepo) WithLikeName(name string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(name) == 0 {
return g
}
return g.Where("name like ? or short_desc_zh like ? or short_desc_en like ?", "%"+name+"%", "%"+name+"%", "%"+name+"%")
}
}
func (a AppRepo) WithKey(key string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("key = ?", key)
+1 -5
View File
@@ -3,7 +3,6 @@ package repo
import (
"context"
"encoding/json"
"github.com/1Panel-dev/1Panel/backend/constant"
"gorm.io/gorm/clause"
@@ -171,7 +170,6 @@ type RootInfo struct {
Env string `json:"env"`
Key string `json:"key"`
Version string `json:"version"`
AppPath string `json:"app_path"`
}
func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error) {
@@ -207,7 +205,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
if ok {
info.Password = password
}
case "mongodb", constant.AppPostgresql:
case "mongodb", "postgresql":
user, ok := envMap["PANEL_DB_ROOT_USER"].(string)
if ok {
info.UserName = user
@@ -232,7 +230,5 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
info.Param = appInstall.Param
info.Version = appInstall.Version
info.Key = app.Key
appInstall.App = app
info.AppPath = appInstall.GetAppPath()
return &info, nil
}
-13
View File
@@ -2,7 +2,6 @@ package repo
import (
"context"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"gorm.io/gorm"
@@ -20,11 +19,9 @@ type IBackupRepo interface {
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
DeleteRecord(ctx context.Context, opts ...DBOption) error
UpdateRecord(record *model.BackupRecord) error
WithByDetailName(detailName string) DBOption
WithByFileName(fileName string) DBOption
WithByType(backupType string) DBOption
WithByCronID(cronjobID uint) DBOption
}
func NewIBackupRepo() IBackupRepo {
@@ -108,10 +105,6 @@ func (u *BackupRepo) CreateRecord(record *model.BackupRecord) error {
return global.DB.Create(record).Error
}
func (u *BackupRepo) UpdateRecord(record *model.BackupRecord) error {
return global.DB.Save(record).Error
}
func (u *BackupRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.BackupAccount{}).Where("id = ?", id).Updates(vars).Error
}
@@ -127,9 +120,3 @@ func (u *BackupRepo) Delete(opts ...DBOption) error {
func (u *BackupRepo) DeleteRecord(ctx context.Context, opts ...DBOption) error {
return getTx(ctx, opts...).Delete(&model.BackupRecord{}).Error
}
func (u *BackupRepo) WithByCronID(cronjobID uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("cronjob_id = ?", cronjobID)
}
}
+3 -15
View File
@@ -20,8 +20,7 @@ type ICronjobRepo interface {
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
Create(cronjob *model.Cronjob) error
WithByJobID(id int) DBOption
WithByDbName(name string) DBOption
WithByDefaultDownload(account string) DBOption
WithByBackupID(id uint) DBOption
WithByRecordDropID(id int) DBOption
WithByRecordFile(file string) DBOption
Save(id uint, cronjob model.Cronjob) error
@@ -29,7 +28,6 @@ type ICronjobRepo interface {
Delete(opts ...DBOption) error
DeleteRecord(opts ...DBOption) error
StartRecords(cronjobID uint, fromLocal bool, targetPath string) model.JobRecords
UpdateRecords(id uint, vars map[string]interface{}) error
EndRecords(record model.JobRecords, status, message, records string)
PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error)
}
@@ -118,15 +116,9 @@ func (c *CronjobRepo) WithByJobID(id int) DBOption {
}
}
func (c *CronjobRepo) WithByDbName(name string) DBOption {
func (c *CronjobRepo) WithByBackupID(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("db_name = ?", name)
}
}
func (c *CronjobRepo) WithByDefaultDownload(account string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("default_download = ?", account)
return g.Where("target_dir_id = ?", id)
}
}
@@ -172,10 +164,6 @@ func (u *CronjobRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.Cronjob{}).Where("id = ?", id).Updates(vars).Error
}
func (u *CronjobRepo) UpdateRecords(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.JobRecords{}).Where("id = ?", id).Updates(vars).Error
}
func (u *CronjobRepo) Delete(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
-123
View File
@@ -1,123 +0,0 @@
package repo
import (
"context"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"gorm.io/gorm"
)
type PostgresqlRepo struct{}
type IPostgresqlRepo interface {
Get(opts ...DBOption) (model.DatabasePostgresql, error)
WithByPostgresqlName(postgresqlName string) DBOption
WithByFrom(from string) DBOption
List(opts ...DBOption) ([]model.DatabasePostgresql, error)
Page(limit, offset int, opts ...DBOption) (int64, []model.DatabasePostgresql, error)
Create(ctx context.Context, postgresql *model.DatabasePostgresql) error
Delete(ctx context.Context, opts ...DBOption) error
Update(id uint, vars map[string]interface{}) error
DeleteLocal(ctx context.Context) error
}
func NewIPostgresqlRepo() IPostgresqlRepo {
return &PostgresqlRepo{}
}
func (u *PostgresqlRepo) Get(opts ...DBOption) (model.DatabasePostgresql, error) {
var postgresql model.DatabasePostgresql
db := global.DB
for _, opt := range opts {
db = opt(db)
}
if err := db.First(&postgresql).Error; err != nil {
return postgresql, err
}
pass, err := encrypt.StringDecrypt(postgresql.Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresql.Name, err)
}
postgresql.Password = pass
return postgresql, err
}
func (u *PostgresqlRepo) List(opts ...DBOption) ([]model.DatabasePostgresql, error) {
var postgresqls []model.DatabasePostgresql
db := global.DB.Model(&model.DatabasePostgresql{})
for _, opt := range opts {
db = opt(db)
}
if err := db.Find(&postgresqls).Error; err != nil {
return postgresqls, err
}
for i := 0; i < len(postgresqls); i++ {
pass, err := encrypt.StringDecrypt(postgresqls[i].Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresqls[i].Name, err)
}
postgresqls[i].Password = pass
}
return postgresqls, nil
}
func (u *PostgresqlRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabasePostgresql, error) {
var postgresqls []model.DatabasePostgresql
db := global.DB.Model(&model.DatabasePostgresql{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
if err := db.Limit(size).Offset(size * (page - 1)).Find(&postgresqls).Error; err != nil {
return count, postgresqls, err
}
for i := 0; i < len(postgresqls); i++ {
pass, err := encrypt.StringDecrypt(postgresqls[i].Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresqls[i].Name, err)
}
postgresqls[i].Password = pass
}
return count, postgresqls, nil
}
func (u *PostgresqlRepo) Create(ctx context.Context, postgresql *model.DatabasePostgresql) error {
pass, err := encrypt.StringEncrypt(postgresql.Password)
if err != nil {
return fmt.Errorf("decrypt database db %s password failed, err: %v", postgresql.Name, err)
}
postgresql.Password = pass
return getTx(ctx).Create(postgresql).Error
}
func (u *PostgresqlRepo) Delete(ctx context.Context, opts ...DBOption) error {
return getTx(ctx, opts...).Delete(&model.DatabasePostgresql{}).Error
}
func (u *PostgresqlRepo) DeleteLocal(ctx context.Context) error {
return getTx(ctx).Where("`from` = ?", "local").Delete(&model.DatabasePostgresql{}).Error
}
func (u *PostgresqlRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.DatabasePostgresql{}).Where("id = ?", id).Updates(vars).Error
}
func (u *PostgresqlRepo) WithByPostgresqlName(postgresqlName string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("postgresql_name = ?", postgresqlName)
}
}
func (u *PostgresqlRepo) WithByFrom(from string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(from) != 0 {
return g.Where("`from` = ?", from)
}
return g
}
}
-59
View File
@@ -1,59 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/backend/app/model"
)
type PHPExtensionsRepo struct {
}
type IPHPExtensionsRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error)
Save(extension *model.PHPExtensions) error
Create(extension *model.PHPExtensions) error
GetFirst(opts ...DBOption) (model.PHPExtensions, error)
DeleteBy(opts ...DBOption) error
List() ([]model.PHPExtensions, error)
}
func NewIPHPExtensionsRepo() IPHPExtensionsRepo {
return &PHPExtensionsRepo{}
}
func (p *PHPExtensionsRepo) Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error) {
var (
phpExtensions []model.PHPExtensions
)
db := getDb(opts...).Model(&model.PHPExtensions{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&phpExtensions).Error
return count, phpExtensions, err
}
func (p *PHPExtensionsRepo) List() ([]model.PHPExtensions, error) {
var (
phpExtensions []model.PHPExtensions
)
err := getDb().Model(&model.PHPExtensions{}).Find(&phpExtensions).Error
return phpExtensions, err
}
func (p *PHPExtensionsRepo) Save(extension *model.PHPExtensions) error {
return getDb().Save(&extension).Error
}
func (p *PHPExtensionsRepo) Create(extension *model.PHPExtensions) error {
return getDb().Create(&extension).Error
}
func (p *PHPExtensionsRepo) GetFirst(opts ...DBOption) (model.PHPExtensions, error) {
var extension model.PHPExtensions
db := getDb(opts...).Model(&model.PHPExtensions{})
err := db.First(&extension).Error
return extension, err
}
func (p *PHPExtensionsRepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.PHPExtensions{}).Error
}
+1 -1
View File
@@ -95,7 +95,7 @@ func (w *WebsiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Web
func (w *WebsiteRepo) List(opts ...DBOption) ([]model.Website, error) {
var websites []model.Website
err := getDb(opts...).Model(&model.Website{}).Preload("Domains").Preload("WebsiteSSL").Find(&websites).Error
err := getDb(opts...).Model(&model.Website{}).Preload("WebsiteSSL").Find(&websites).Error
return websites, err
}
-6
View File
@@ -12,7 +12,6 @@ type IAcmeAccountRepo interface {
Save(account model.WebsiteAcmeAccount) error
DeleteBy(opts ...DBOption) error
WithEmail(email string) DBOption
WithType(acType string) DBOption
}
func NewIAcmeAccountRepo() IAcmeAccountRepo {
@@ -27,11 +26,6 @@ func (w *WebsiteAcmeAccountRepo) WithEmail(email string) DBOption {
return db.Where("email = ?", email)
}
}
func (w *WebsiteAcmeAccountRepo) WithType(acType string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("type = ?", acType)
}
}
func (w *WebsiteAcmeAccountRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteAcmeAccount, error) {
var accounts []model.WebsiteAcmeAccount
-54
View File
@@ -1,54 +0,0 @@
package repo
import (
"context"
"github.com/1Panel-dev/1Panel/backend/app/model"
)
type WebsiteCARepo struct {
}
func NewIWebsiteCARepo() IWebsiteCARepo {
return &WebsiteCARepo{}
}
type IWebsiteCARepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteCA, error)
GetFirst(opts ...DBOption) (model.WebsiteCA, error)
List(opts ...DBOption) ([]model.WebsiteCA, error)
Create(ctx context.Context, ca *model.WebsiteCA) error
DeleteBy(opts ...DBOption) error
}
func (w WebsiteCARepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteCA, error) {
var caList []model.WebsiteCA
db := getDb(opts...).Model(&model.WebsiteCA{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&caList).Error
return count, caList, err
}
func (w WebsiteCARepo) GetFirst(opts ...DBOption) (model.WebsiteCA, error) {
var ca model.WebsiteCA
db := getDb(opts...).Model(&model.WebsiteCA{})
if err := db.First(&ca).Error; err != nil {
return ca, err
}
return ca, nil
}
func (w WebsiteCARepo) List(opts ...DBOption) ([]model.WebsiteCA, error) {
var caList []model.WebsiteCA
db := getDb(opts...).Model(&model.WebsiteCA{})
err := db.Find(&caList).Error
return caList, err
}
func (w WebsiteCARepo) Create(ctx context.Context, ca *model.WebsiteCA) error {
return getTx(ctx).Create(ca).Error
}
func (w WebsiteCARepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.WebsiteCA{}).Error
}
+8 -15
View File
@@ -14,12 +14,11 @@ type ISSLRepo interface {
WithByAlias(alias string) DBOption
WithByAcmeAccountId(acmeAccountId uint) DBOption
WithByDnsAccountId(dnsAccountId uint) DBOption
WithByCAID(caID uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
GetFirst(opts ...DBOption) (model.WebsiteSSL, error)
List(opts ...DBOption) ([]model.WebsiteSSL, error)
Create(ctx context.Context, ssl *model.WebsiteSSL) error
Save(ssl *model.WebsiteSSL) error
Save(ssl model.WebsiteSSL) error
DeleteBy(opts ...DBOption) error
}
@@ -44,25 +43,19 @@ func (w WebsiteSSLRepo) WithByDnsAccountId(dnsAccountId uint) DBOption {
}
}
func (w WebsiteSSLRepo) WithByCAID(caID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("ca_id = ?", caID)
}
}
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) {
var sslList []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Preload("AcmeAccount").Preload("DnsAccount").Preload("Websites").Find(&sslList).Error
err := db.Limit(size).Offset(size * (page - 1)).Preload("AcmeAccount").Preload("Websites").Find(&sslList).Error
return count, sslList, err
}
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
var website *model.WebsiteSSL
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (model.WebsiteSSL, error) {
var website model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil {
if err := db.Preload("AcmeAccount").First(&website).Error; err != nil {
return website, err
}
return website, nil
@@ -71,7 +64,7 @@ func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {
var websites []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
if err := db.Preload("AcmeAccount").Preload("DnsAccount").Find(&websites).Error; err != nil {
if err := db.Preload("AcmeAccount").Find(&websites).Error; err != nil {
return websites, err
}
return websites, nil
@@ -81,7 +74,7 @@ func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error
return getTx(ctx).Create(ssl).Error
}
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
func (w WebsiteSSLRepo) Save(ssl model.WebsiteSSL) error {
return getDb().Save(&ssl).Error
}
+14 -15
View File
@@ -8,7 +8,7 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"path"
"reflect"
"strconv"
"strings"
@@ -53,7 +53,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
var opts []repo.DBOption
opts = append(opts, appRepo.OrderByRecommend())
if req.Name != "" {
opts = append(opts, appRepo.WithLikeName(req.Name))
opts = append(opts, commonRepo.WithLikeName(req.Name))
}
if req.Type != "" {
opts = append(opts, appRepo.WithType(req.Type))
@@ -61,7 +61,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
if req.Recommend {
opts = append(opts, appRepo.GetRecommend())
}
if req.Resource != "" && req.Resource != "all" {
if req.Resource != "" {
opts = append(opts, appRepo.WithResource(req.Resource))
}
if len(req.Tags) != 0 {
@@ -173,8 +173,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
return appDetailDTO, err
}
fileOp := files.NewFileOp()
versionPath := filepath.Join(app.GetAppResourcePath(), detail.Version)
versionPath := path.Join(constant.AppResourceDir, app.Resource, app.Key, detail.Version)
if !fileOp.Stat(versionPath) || detail.Update {
if err = downloadApp(app, detail, nil); err != nil {
return appDetailDTO, err
@@ -182,8 +181,8 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
}
switch app.Type {
case constant.RuntimePHP:
buildPath := filepath.Join(versionPath, "build")
paramsPath := filepath.Join(buildPath, "config.json")
buildPath := path.Join(versionPath, "build")
paramsPath := path.Join(buildPath, "config.json")
if !fileOp.Stat(paramsPath) {
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
}
@@ -196,7 +195,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
return appDetailDTO, err
}
appDetailDTO.Params = paramMap
composePath := filepath.Join(buildPath, "docker-compose.yml")
composePath := path.Join(buildPath, "docker-compose.yml")
if !fileOp.Stat(composePath) {
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
}
@@ -224,7 +223,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
}
if appDetailDTO.DockerCompose == "" {
filename := filepath.Base(appDetailDTO.DownloadUrl)
filename := path.Base(appDetailDTO.DownloadUrl)
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml")
composeRes, err := http.Get(dockerComposeUrl)
if err != nil {
@@ -397,7 +396,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
}
index++
}
if app.Limit == 0 && appInstall.Name != serviceName && len(servicesMap) == 1 {
if app.Limit == 0 && appInstall.Name != serviceName {
servicesMap[appInstall.Name] = servicesMap[serviceName]
delete(servicesMap, serviceName)
serviceName = appInstall.Name
@@ -459,7 +458,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
if err = runScript(appInstall, "init"); err != nil {
return
}
upApp(appInstall, req.PullImage)
upApp(appInstall)
}()
go updateToolApp(appInstall)
return
@@ -490,7 +489,7 @@ func (a AppService) SyncAppListFromLocal() {
}
for _, dirEntry := range dirEntries {
if dirEntry.IsDir() {
appDir := filepath.Join(localAppDir, dirEntry.Name())
appDir := path.Join(localAppDir, dirEntry.Name())
appDirEntries, err := os.ReadDir(appDir)
if err != nil {
global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppDirNull", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
@@ -508,7 +507,7 @@ func (a AppService) SyncAppListFromLocal() {
Version: appDirEntry.Name(),
Status: constant.AppNormal,
}
versionDir := filepath.Join(appDir, appDirEntry.Name())
versionDir := path.Join(appDir, appDirEntry.Name())
if err = handleLocalAppDetail(versionDir, &appDetail); err != nil {
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()}))
continue
@@ -743,7 +742,7 @@ func getAppFromRepo(downloadPath string) error {
downloadUrl := downloadPath
global.LOG.Infof("[AppStore] download file from %s", downloadUrl)
fileOp := files.NewFileOp()
packagePath := filepath.Join(constant.ResourceDir, filepath.Base(downloadUrl))
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
return err
}
@@ -761,7 +760,7 @@ func getAppList() (*dto.AppList, error) {
if err := getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.System.AppRepo, global.CONF.System.Mode)); err != nil {
return nil, err
}
listFile := filepath.Join(constant.ResourceDir, "1panel.json")
listFile := path.Join(constant.ResourceDir, "1panel.json")
content, err := os.ReadFile(listFile)
if err != nil {
return nil, err
+87 -96
View File
@@ -8,10 +8,11 @@ import (
"os"
"path"
"reflect"
"sort"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"gopkg.in/yaml.v3"
@@ -150,7 +151,7 @@ func (a *AppInstallService) CheckExist(req request.AppInstalledInfo) (*response.
if reflect.DeepEqual(appInstall, model.AppInstall{}) {
return res, nil
}
if err = syncAppInstallStatus(&appInstall); err != nil {
if err = syncByID(appInstall.ID); err != nil {
return nil, err
}
@@ -182,11 +183,9 @@ func (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (resp
if err != nil {
return data, nil
}
data.Username = app.UserName
data.Password = app.Password
data.ServiceName = app.ServiceName
data.Port = app.Port
data.ContainerName = app.ContainerName
return data, nil
}
@@ -240,30 +239,28 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
if err != nil {
return handleErr(install, err, out)
}
return syncAppInstallStatus(&install)
return syncByID(install.ID)
case constant.Stop:
out, err := compose.Stop(dockerComposePath)
if err != nil {
return handleErr(install, err, out)
}
return syncAppInstallStatus(&install)
return syncByID(install.ID)
case constant.Restart:
out, err := compose.Restart(dockerComposePath)
if err != nil {
return handleErr(install, err, out)
}
return syncAppInstallStatus(&install)
return syncByID(install.ID)
case constant.Delete:
if err := deleteAppInstall(install, req.DeleteBackup, req.ForceDelete, req.DeleteDB); err != nil && !req.ForceDelete {
return err
}
return nil
case constant.Sync:
return syncAppInstallStatus(&install)
return syncByID(install.ID)
case constant.Upgrade:
return upgradeInstall(install.ID, req.DetailId, req.Backup, req.PullImage)
case constant.Reload:
return opNginx(install.ContainerName, constant.NginxReload)
return upgradeInstall(install.ID, req.DetailId, req.Backup)
default:
return errors.New("operate not support")
}
@@ -411,7 +408,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
return err
}
for _, i := range allList {
if i.Status == constant.Installing || i.Status == constant.Upgrading || i.Status == constant.Rebuilding {
if i.Status == constant.Installing || i.Status == constant.Upgrading {
if systemInit {
i.Status = constant.Error
i.Message = "1Panel restart causes the task to terminate"
@@ -420,7 +417,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
continue
}
if !systemInit {
if err = syncAppInstallStatus(&i); err != nil {
if err := syncByID(i.ID); err != nil {
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
}
}
@@ -431,9 +428,6 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {
var res []response.AppService
if DatabaseKeys[key] > 0 {
if key == constant.AppPostgres {
key = constant.AppPostgresql
}
dbs, _ := databaseRepo.GetList(commonRepo.WithByType(key))
if len(dbs) == 0 {
return res, nil
@@ -511,9 +505,6 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
})
}
}
sort.Slice(versions, func(i, j int) bool {
return common.CompareVersion(versions[i].Version, versions[j].Version)
})
return versions, nil
}
@@ -527,7 +518,7 @@ func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
return nil
}
if err := updateInstallInfoInDB(req.Key, req.Name, "port", strconv.FormatInt(req.Port, 10)); err != nil {
if err := updateInstallInfoInDB(req.Key, req.Name, "port", true, strconv.FormatInt(req.Port, 10)); err != nil {
return nil
}
@@ -639,11 +630,10 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
for _, form := range appForm.FormFields {
if v, ok := envs[form.EnvKey]; ok {
appParam := response.AppParam{
Edit: false,
Key: form.EnvKey,
Rule: form.Rule,
Type: form.Type,
Multiple: form.Multiple,
Edit: false,
Key: form.EnvKey,
Rule: form.Rule,
Type: form.Type,
}
if form.Edit {
appParam.Edit = true
@@ -655,20 +645,10 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(v.(string)))
appParam.ShowValue = appInstall.Name
} else if form.Type == "select" {
if form.Multiple {
if v == "" {
appParam.Value = []string{}
} else {
if str, ok := v.(string); ok {
appParam.Value = strings.Split(str, ",")
}
}
} else {
for _, fv := range form.Values {
if fv.Value == v {
appParam.ShowValue = fv.Label
break
}
for _, fv := range form.Values {
if fv.Value == v {
appParam.ShowValue = fv.Label
break
}
}
appParam.Values = form.Values
@@ -676,15 +656,14 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
params = append(params, appParam)
} else {
params = append(params, response.AppParam{
Edit: form.Edit,
Key: form.EnvKey,
Rule: form.Rule,
Type: form.Type,
LabelZh: form.LabelZh,
LabelEn: form.LabelEn,
Value: form.Default,
Values: form.Values,
Multiple: form.Multiple,
Edit: form.Edit,
Key: form.EnvKey,
Rule: form.Rule,
Type: form.Type,
LabelZh: form.LabelZh,
LabelEn: form.LabelEn,
Value: form.Default,
Values: form.Values,
})
}
}
@@ -700,11 +679,15 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
return &res, nil
}
func syncAppInstallStatus(appInstall *model.AppInstall) error {
if appInstall.Status == constant.Installing || appInstall.Status == constant.Rebuilding || appInstall.Status == constant.Upgrading {
func syncByID(installID uint) error {
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
if err != nil {
return err
}
if appInstall.Status == constant.Installing {
return nil
}
containerNames, err := getContainerNames(*appInstall)
containerNames, err := getContainerNames(appInstall)
if err != nil {
return err
}
@@ -716,67 +699,75 @@ func syncAppInstallStatus(appInstall *model.AppInstall) error {
if err != nil {
return err
}
var (
runningCount int
exitedCount int
pausedCount int
exitedContainerNames []string
total = len(containerNames)
count = len(containers)
errorContainers []string
notFoundContainers []string
runningContainers []string
exitedContainers []string
)
foundNames := make(map[string]bool)
for _, con := range containers {
foundNames[con.Names[0]] = true
switch con.State {
for _, n := range containers {
switch n.State {
case "exited":
exitedContainerNames = append(exitedContainerNames, strings.TrimPrefix(con.Names[0], "/"))
exitedCount++
exitedContainers = append(exitedContainers, n.Names[0])
case "running":
runningCount++
case "paused":
pausedCount++
runningContainers = append(runningContainers, n.Names[0])
default:
errorContainers = append(errorContainers, n.Names[0])
}
}
var notFoundNames []string
for _, name := range containerNames {
if !foundNames["/"+name] {
notFoundNames = append(notFoundNames, name)
exist := false
for _, container := range containers {
if common.ExistWithStrArray(name, container.Names) {
exist = true
break
}
}
if !exist {
notFoundContainers = append(notFoundContainers, name)
}
}
switch {
case count == 0:
containerCount := len(containers)
errCount := len(errorContainers)
notFoundCount := len(notFoundContainers)
existedCount := len(exitedContainers)
normalCount := len(containerNames)
runningCount := len(runningContainers)
if containerCount == 0 {
appInstall.Status = constant.Error
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(containerNames, ",")).Error()
case exitedCount == total:
appInstall.Status = constant.Stopped
case runningCount == total:
appInstall.Message = i18n.GetMsgWithMap("ErrContainerNotFound", map[string]interface{}{"name": strings.Join(containerNames, ",")})
return appInstallRepo.Save(context.Background(), &appInstall)
}
if errCount == 0 && existedCount == 0 && notFoundCount == 0 {
appInstall.Status = constant.Running
case pausedCount == total:
appInstall.Status = constant.Paused
default:
var msg string
if exitedCount > 0 {
msg = buserr.WithName("ErrContainerMsg", strings.Join(exitedContainerNames, ",")).Error()
}
if len(notFoundNames) > 0 {
msg += buserr.WithName("ErrContainerNotFound", strings.Join(notFoundNames, ",")).Error()
}
if msg == "" {
msg = buserr.New("ErrAppWarn").Error()
}
appInstall.Message = msg
return appInstallRepo.Save(context.Background(), &appInstall)
}
if existedCount == normalCount {
appInstall.Status = constant.Stopped
return appInstallRepo.Save(context.Background(), &appInstall)
}
if errCount == normalCount {
appInstall.Status = constant.Error
}
if runningCount < normalCount {
appInstall.Status = constant.UnHealthy
}
_ = appInstallRepo.Save(context.Background(), appInstall)
return nil
var errMsg string
if errCount > 0 {
errMsg += i18n.GetMsgWithMap("ErrContainerMsg", map[string]interface{}{"name": strings.Join(errorContainers, ",")})
}
if notFoundCount > 0 {
errMsg += i18n.GetMsgWithMap("ErrContainerNotFound", map[string]interface{}{"name": strings.Join(notFoundContainers, ",")})
}
appInstall.Message = errMsg
return appInstallRepo.Save(context.Background(), &appInstall)
}
func updateInstallInfoInDB(appKey, appName, param string, value interface{}) error {
func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value interface{}) error {
if param != "password" && param != "port" && param != "user-password" {
return nil
}
@@ -784,7 +775,7 @@ func updateInstallInfoInDB(appKey, appName, param string, value interface{}) err
if err != nil {
return nil
}
envPath := fmt.Sprintf("%s/%s/.env", appInstall.AppPath, appInstall.Name)
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, appKey, appInstall.Name)
lineBytes, err := os.ReadFile(envPath)
if err != nil {
return err
@@ -793,7 +784,7 @@ func updateInstallInfoInDB(appKey, appName, param string, value interface{}) err
envKey := ""
switch param {
case "password":
if appKey == "mysql" || appKey == "mariadb" || appKey == "postgresql" {
if appKey == "mysql" || appKey == "mariadb" {
envKey = "PANEL_DB_ROOT_PASSWORD="
} else {
envKey = "PANEL_REDIS_ROOT_PASSWORD="
+147 -260
View File
@@ -110,22 +110,17 @@ func checkPortExist(port int) error {
}
var DatabaseKeys = map[string]uint{
constant.AppMysql: 3306,
constant.AppMariaDB: 3306,
constant.AppPostgresql: 5432,
constant.AppPostgres: 5432,
constant.AppMongodb: 27017,
constant.AppRedis: 6379,
constant.AppMemcached: 11211,
}
var ToolKeys = map[string]uint{
"minio": 9001,
"mysql": 3306,
"mariadb": 3306,
"postgresql": 5432,
"mongodb": 27017,
"redis": 6379,
"memcached": 11211,
}
func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
var dbConfig dto.AppDatabase
if DatabaseKeys[app.Key] > 0 {
if app.Type == "runtime" && DatabaseKeys[app.Key] > 0 {
database := &model.Database{
AppInstallID: appInstall.ID,
Name: appInstall.Name,
@@ -155,9 +150,17 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
}
switch app.Key {
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb:
case "mysql", "mariadb", "postgresql", "mongodb":
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
if password != "" {
authParam := dto.AuthParam{
RootPassword: password.(string),
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
database.Password = password.(string)
if app.Key == "mysql" || app.Key == "mariadb" {
database.Username = "root"
@@ -165,19 +168,9 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
if rootUser, ok := params["PANEL_DB_ROOT_USER"]; ok {
database.Username = rootUser.(string)
}
authParam := dto.AuthParam{
RootPassword: password.(string),
RootUser: database.Username,
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
case constant.AppRedis:
case "redis":
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
if password != "" {
authParam := dto.RedisAuthParam{
@@ -196,23 +189,6 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
return err
}
}
if ToolKeys[app.Key] > 0 {
if app.Key == "minio" {
authParam := dto.MinioAuthParam{}
if password, ok := params["PANEL_MINIO_ROOT_PASSWORD"]; ok {
authParam.RootPassword = password.(string)
}
if rootUser, ok := params["PANEL_MINIO_ROOT_USER"]; ok {
authParam.RootUser = rootUser.(string)
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
if app.Type == "website" || app.Type == "tool" {
paramByte, err := json.Marshal(params)
if err != nil {
@@ -234,55 +210,28 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
}
var resourceId uint
if dbConfig.DbName != "" && dbConfig.DbUser != "" && dbConfig.Password != "" {
switch database.Type {
case constant.AppPostgresql, constant.AppPostgres:
iPostgresqlRepo := repo.NewIPostgresqlRepo()
oldPostgresqlDb, _ := iPostgresqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iPostgresqlRepo.WithByFrom(constant.ResourceLocal))
resourceId = oldPostgresqlDb.ID
if oldPostgresqlDb.ID > 0 {
if oldPostgresqlDb.Username != dbConfig.DbUser || oldPostgresqlDb.Password != dbConfig.Password {
return buserr.New(constant.ErrDbUserNotValid)
}
} else {
var createPostgresql dto.PostgresqlDBCreate
createPostgresql.Name = dbConfig.DbName
createPostgresql.Username = dbConfig.DbUser
createPostgresql.Database = database.Name
createPostgresql.Format = "UTF8"
createPostgresql.Password = dbConfig.Password
createPostgresql.From = database.From
createPostgresql.SuperUser = true
pgdb, err := NewIPostgresqlService().Create(ctx, createPostgresql)
if err != nil {
return err
}
resourceId = pgdb.ID
iMysqlRepo := repo.NewIMysqlRepo()
oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iMysqlRepo.WithByFrom(constant.ResourceLocal))
resourceId = oldMysqlDb.ID
if oldMysqlDb.ID > 0 {
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
return buserr.New(constant.ErrDbUserNotValid)
}
case constant.AppMysql, constant.AppMariaDB:
iMysqlRepo := repo.NewIMysqlRepo()
oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iMysqlRepo.WithByFrom(constant.ResourceLocal))
resourceId = oldMysqlDb.ID
if oldMysqlDb.ID > 0 {
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
return buserr.New(constant.ErrDbUserNotValid)
}
} else {
var createMysql dto.MysqlDBCreate
createMysql.Name = dbConfig.DbName
createMysql.Username = dbConfig.DbUser
createMysql.Database = database.Name
createMysql.Format = "utf8mb4"
createMysql.Permission = "%"
createMysql.Password = dbConfig.Password
createMysql.From = database.From
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
if err != nil {
return err
}
resourceId = mysqldb.ID
} else {
var createMysql dto.MysqlDBCreate
createMysql.Name = dbConfig.DbName
createMysql.Username = dbConfig.DbUser
createMysql.Database = database.Name
createMysql.Format = "utf8mb4"
createMysql.Permission = "%"
createMysql.Password = dbConfig.Password
createMysql.From = database.From
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
if err != nil {
return err
}
resourceId = mysqldb.ID
}
}
var installResource model.AppInstallResource
installResource.ResourceId = resourceId
@@ -354,8 +303,8 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
websiteApp, _ := appRepo.GetFirst(commonRepo.WithByID(websiteAppInstall.AppId))
if websiteApp.Type == constant.RuntimePHP {
go func() {
_, _ = compose.Down(websiteAppInstall.GetComposePath())
_ = op.DeleteDir(websiteAppInstall.GetPath())
_, _ = compose.Down(install.GetComposePath())
_ = op.DeleteDir(install.GetPath())
}()
_ = appInstallRepo.Delete(ctx, websiteAppInstall)
}
@@ -366,8 +315,6 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
_ = websiteDomainRepo.DeleteAll(ctx)
case constant.AppMysql, constant.AppMariaDB:
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
case constant.AppPostgresql:
_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
@@ -389,56 +336,38 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
}
func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool, deleteBackup bool) error {
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
if len(resources) == 0 {
return nil
}
for _, re := range resources {
if deleteDB {
switch re.Key {
case constant.AppMysql, constant.AppMariaDB:
mysqlService := NewIMysqlService()
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
continue
}
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
ID: database.ID,
ForceDelete: forceDelete,
DeleteBackup: deleteBackup,
Type: re.Key,
Database: database.MysqlName,
}); err != nil && !forceDelete {
return err
}
case constant.AppPostgresql:
pgsqlService := NewIPostgresqlService()
database, _ := postgresqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabasePostgresql{}) {
continue
}
if err := pgsqlService.Delete(ctx, dto.PostgresqlDBDelete{
ID: database.ID,
ForceDelete: forceDelete,
DeleteBackup: deleteBackup,
Type: re.Key,
Database: database.PostgresqlName,
}); err != nil {
return err
}
mysqlService := NewIMysqlService()
if (re.Key == constant.AppMysql || re.Key == constant.AppMariaDB) && deleteDB {
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
continue
}
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
ID: database.ID,
ForceDelete: forceDelete,
DeleteBackup: deleteBackup,
Type: re.Key,
Database: database.MysqlName,
}); err != nil && !forceDelete {
return err
}
}
}
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
}
func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
func upgradeInstall(installId uint, detailId uint, backup bool) error {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
if err != nil {
return err
}
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailID))
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailId))
if err != nil {
return err
}
@@ -454,40 +383,23 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
}
}
var upErr error
defer func() {
if upErr != nil {
if backup {
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal}); err != nil {
global.LOG.Errorf("recover app [%s] [%s] failed %v", install.App.Key, install.Name, err)
}
}
install.Status = constant.UpgradeErr
install.Message = upErr.Error()
_ = appInstallRepo.Save(context.Background(), &install)
}
}()
fileOp := files.NewFileOp()
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
if install.App.Resource == constant.AppResourceRemote {
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
return
}
if detail.DockerCompose == "" {
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
if err != nil {
upErr = err
return
}
detail.DockerCompose = string(composeDetail)
_ = appDetailRepo.Update(context.Background(), detail)
}
go func() {
_, _ = http.Get(detail.DownloadCallBackUrl)
}()
}
if install.App.Resource == constant.AppResourceLocal {
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
}
@@ -497,6 +409,7 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
if stdout != nil {
global.LOG.Infof("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout))
}
fileOp := files.NewFileOp()
sourceScripts := path.Join(detailDir, "scripts")
if fileOp.Stat(sourceScripts) {
dstScripts := path.Join(install.GetPath(), "scripts")
@@ -506,6 +419,16 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
_, _ = scriptCmd.CombinedOutput()
}
if detail.DockerCompose == "" {
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
if err != nil {
upErr = err
return
}
detail.DockerCompose = string(composeDetail)
_ = appDetailRepo.Update(context.Background(), detail)
}
composeMap := make(map[string]interface{})
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
return
@@ -556,7 +479,25 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
install.DockerCompose = string(composeByte)
install.Version = detail.Version
install.AppDetailId = detailID
install.AppDetailId = detailId
images, err := getImages(install)
if err != nil {
upErr = err
return
}
dockerCli, err := composeV2.NewClient()
if err != nil {
upErr = err
return
}
for _, image := range images {
if err = dockerCli.PullImage(image, true); err != nil {
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
return
}
}
if out, err := compose.Down(install.GetComposePath()); err != nil {
if out != "" {
@@ -575,31 +516,6 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
return
}
content, err := fileOp.GetContent(install.GetEnvPath())
if err != nil {
upErr = err
return
}
if pullImage {
images, err := composeV2.GetDockerComposeImages(install.Name, content, []byte(detail.DockerCompose))
if err != nil {
upErr = err
return
}
dockerCli, err := composeV2.NewClient()
if err != nil {
upErr = err
return
}
for _, image := range images {
if err = dockerCli.PullImage(image, true); err != nil {
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
return
}
}
}
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
return
}
@@ -644,6 +560,26 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
return containerNames, nil
}
func getImages(install model.AppInstall) ([]string, error) {
envStr, err := coverEnvJsonToStr(install.Env)
if err != nil {
return nil, err
}
project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)
if err != nil {
return nil, err
}
imagesMap := make(map[string]struct{})
for _, service := range project.AllServices() {
imagesMap[service.Image] = struct{}{}
}
var images []string
for k := range imagesMap {
images = append(images, k)
}
return images, nil
}
func coverEnvJsonToStr(envJson string) (string, error) {
envMap := make(map[string]interface{})
_ = json.Unmarshal([]byte(envJson), &envMap)
@@ -687,12 +623,6 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
envParams[k] = strconv.Itoa(int(t))
case int:
envParams[k] = strconv.Itoa(t)
case []interface{}:
strArray := make([]string, len(t))
for i := range t {
strArray[i] = strings.ToLower(fmt.Sprintf("%v", t[i]))
}
envParams[k] = strings.Join(strArray, ",")
default:
envParams[k] = t.(string)
}
@@ -700,12 +630,8 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
}
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall) (err error) {
if app.IsLocalApp() {
//本地应用,不去官网下载
return nil
}
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
appDownloadDir := app.GetAppResourcePath()
appDownloadDir := path.Join(appResourceDir, app.Key)
appVersionDir := path.Join(appDownloadDir, appDetail.Version)
fileOp := files.NewFileOp()
if !appDetail.Update && fileOp.Stat(appVersionDir) {
@@ -847,36 +773,39 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
return false, nil
}
func upApp(appInstall *model.AppInstall, pullImages bool) {
func upApp(appInstall *model.AppInstall) {
upProject := func(appInstall *model.AppInstall) (err error) {
var (
out string
errMsg string
)
if pullImages && appInstall.App.Type != "php" {
out, err = compose.Pull(appInstall.GetComposePath())
if err == nil {
var (
out string
errMsg string
)
if appInstall.App.Type != "php" {
out, err = compose.Pull(appInstall.GetComposePath())
if err != nil {
if out != "" {
if strings.Contains(out, "no such host") {
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
}
if strings.Contains(out, "timeout") {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
}
appInstall.Message = errMsg + out
}
return err
}
}
out, err = compose.Up(appInstall.GetComposePath())
if err != nil {
if out != "" {
if strings.Contains(out, "no such host") {
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
}
if strings.Contains(out, "timeout") {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
}
appInstall.Message = errMsg + out
}
return err
}
return
} else {
return
}
out, err = compose.Up(appInstall.GetComposePath())
if err != nil {
if out != "" {
appInstall.Message = errMsg + out
}
return err
}
return
}
if err := upProject(appInstall); err != nil {
appInstall.Status = constant.Error
@@ -899,12 +828,11 @@ func rebuildApp(appInstall model.AppInstall) error {
_ = handleErr(appInstall, err, out)
return
}
out, err = compose.Up(appInstall.GetComposePath())
out, err = compose.Up(dockerComposePath)
if err != nil {
_ = handleErr(appInstall, err, out)
return
}
appInstall.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &appInstall)
}()
@@ -969,66 +897,29 @@ func handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error {
fileOp := files.NewFileOp()
dockerComposePath := path.Join(versionDir, "docker-compose.yml")
if !fileOp.Stat(dockerComposePath) {
return buserr.WithName(constant.ErrFileNotFound, "docker-compose.yml")
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "docker-compose.yml"}))
}
dockerComposeByte, _ := fileOp.GetContent(dockerComposePath)
if dockerComposeByte == nil {
return buserr.WithName(constant.ErrFileParseApp, "docker-compose.yml")
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "docker-compose.yml"}))
}
appDetail.DockerCompose = string(dockerComposeByte)
paramPath := path.Join(versionDir, "data.yml")
if !fileOp.Stat(paramPath) {
return buserr.WithName(constant.ErrFileNotFound, "data.yml")
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
}
paramByte, _ := fileOp.GetContent(paramPath)
if paramByte == nil {
return buserr.WithName(constant.ErrFileNotFound, "data.yml")
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
}
appParamConfig := dto.LocalAppParam{}
if err := yaml.Unmarshal(paramByte, &appParamConfig); err != nil {
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
}
dataJson, err := json.Marshal(appParamConfig.AppParams)
if err != nil {
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
}
var appParam dto.AppForm
if err = json.Unmarshal(dataJson, &appParam); err != nil {
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
}
for _, formField := range appParam.FormFields {
if strings.Contains(formField.EnvKey, " ") {
return buserr.WithName(constant.ErrAppParamKey, formField.EnvKey)
}
}
var dataMap map[string]interface{}
err = yaml.Unmarshal(paramByte, &dataMap)
if err != nil {
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
}
additionalProperties, ok := dataMap["additionalProperties"].(map[string]interface{})
if !ok {
return buserr.WithName(constant.ErrAppParamKey, "additionalProperties")
}
formFieldsInterface, ok := additionalProperties["formFields"]
if ok {
formFields, ok := formFieldsInterface.([]interface{})
if !ok {
return buserr.WithName(constant.ErrAppParamKey, "formFields")
}
for _, item := range formFields {
field := item.(map[string]interface{})
for key, value := range field {
if value == nil {
return buserr.WithName(constant.ErrAppParamKey, key)
}
}
}
}
appDetail.Params = string(dataJson)
return nil
}
@@ -1037,22 +928,22 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
fileOp := files.NewFileOp()
configYamlPath := path.Join(appDir, "data.yml")
if !fileOp.Stat(configYamlPath) {
err = buserr.WithName(constant.ErrFileNotFound, "data.yml")
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
return
}
iconPath := path.Join(appDir, "logo.png")
if !fileOp.Stat(iconPath) {
err = buserr.WithName(constant.ErrFileNotFound, "logo.png")
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "logo.png"}))
return
}
configYamlByte, err := fileOp.GetContent(configYamlPath)
if err != nil {
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
return
}
localAppDefine := dto.LocalAppAppDefine{}
if err = yaml.Unmarshal(configYamlByte, &localAppDefine); err != nil {
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
return
}
app = &localAppDefine.AppProperty
@@ -1080,8 +971,8 @@ func handleErr(install model.AppInstall, err error, out string) error {
if out != "" {
install.Message = out
reErr = errors.New(out)
install.Status = constant.Error
}
install.Status = constant.Error
_ = appInstallRepo.Save(context.Background(), &install)
return reErr
}
@@ -1092,10 +983,6 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
if updated && (installed.App.Type == "php" || installed.Status == constant.Installing || (installed.App.Key == constant.AppMysql && installed.Version == "5.6.51")) {
continue
}
if err := syncAppInstallStatus(&installed); err != nil {
global.LOG.Error("sync app install status error : ", err)
}
installDTO := response.AppInstalledDTO{
AppInstall: installed,
Path: installed.GetPath(),
@@ -1110,7 +997,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
}
var versions []string
for _, detail := range details {
if detail.IgnoreUpgrade || installed.Version == "latest" {
if detail.IgnoreUpgrade {
continue
}
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
@@ -1265,7 +1152,7 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
if portExist {
allowHost := "127.0.0.1"
if req.Advanced && req.AllowPort {
allowHost = ""
allowHost = "0.0.0.0"
}
params[constant.HostIP] = allowHost
}
@@ -1277,7 +1164,7 @@ func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig
config := request.AppContainerConfig{}
if hostIp, ok := envs[constant.HostIP]; ok {
config.AllowPort = hostIp.(string) != "127.0.0.1"
config.AllowPort = hostIp.(string) == "0.0.0.0"
} else {
config.AllowPort = true
}
+2 -10
View File
@@ -109,10 +109,6 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
if err != nil {
return nil, err
}
httpsSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
if err != nil {
return nil, err
}
lifeTime, err := strconv.Atoi(setting.Value)
if err != nil {
return nil, err
@@ -133,7 +129,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
sessionUser, err := global.SESSION.Get(sID)
if err != nil {
sID = uuid.New().String()
c.SetCookie(constant.SessionName, sID, 0, "", "", httpsSetting.Value == "enable", true)
c.SetCookie(constant.SessionName, sID, 0, "", "", false, false)
err := global.SESSION.Set(sID, sessionUser, lifeTime)
if err != nil {
return nil, err
@@ -148,13 +144,9 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
}
func (u *AuthService) LogOut(c *gin.Context) error {
httpsSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
if err != nil {
return err
}
sID, _ := c.Cookie(constant.SessionName)
if sID != "" {
c.SetCookie(constant.SessionName, sID, -1, "", "", httpsSetting.Value == "enable", true)
c.SetCookie(constant.SessionName, sID, -1, "", "", false, false)
err := global.SESSION.Delete(sID)
if err != nil {
return err
+86 -212
View File
@@ -6,12 +6,12 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"sort"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
@@ -19,7 +19,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage/client"
fileUtils "github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
@@ -30,8 +29,7 @@ type BackupService struct{}
type IBackupService interface {
List() ([]dto.BackupInfo, error)
SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)
SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error)
LoadOneDriveInfo() (dto.OneDriveInfo, error)
LoadOneDriveInfo() (string, error)
DownloadRecord(info dto.DownloadRecord) (string, error)
Create(backupDto dto.BackupOperate) error
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
@@ -40,14 +38,11 @@ type IBackupService interface {
BatchDeleteRecord(ids []uint) error
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
ListFiles(req dto.BackupSearchFile) []string
ListFiles(req dto.BackupSearchFile) ([]string, error)
MysqlBackup(db dto.CommonBackup) error
PostgresqlBackup(db dto.CommonBackup) error
MysqlRecover(db dto.CommonRecover) error
PostgresqlRecover(db dto.CommonRecover) error
MysqlRecoverByUpload(req dto.CommonRecover) error
PostgresqlRecoverByUpload(req dto.CommonRecover) error
RedisBackup() error
RedisRecover(db dto.CommonRecover) error
@@ -57,8 +52,6 @@ type IBackupService interface {
AppBackup(db dto.CommonBackup) error
AppRecover(req dto.CommonRecover) error
Run()
}
func NewIBackupService() IBackupService {
@@ -76,7 +69,6 @@ func (u *BackupService) List() ([]dto.BackupInfo, error) {
dtobas = append(dtobas, u.loadByType("COS", ops))
dtobas = append(dtobas, u.loadByType("KODO", ops))
dtobas = append(dtobas, u.loadByType("OneDrive", ops))
dtobas = append(dtobas, u.loadByType("WebDAV", ops))
return dtobas, err
}
@@ -88,72 +80,32 @@ func (u *BackupService) SearchRecordsWithPage(search dto.RecordSearch) (int64, [
commonRepo.WithByType(search.Type),
backupRepo.WithByDetailName(search.DetailName),
)
if err != nil {
return 0, nil, err
var dtobas []dto.BackupRecords
for _, group := range records {
var item dto.BackupRecords
if err := copier.Copy(&item, &group); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtobas = append(dtobas, item)
}
datas, err := u.loadRecordSize(records)
sort.Slice(datas, func(i, j int) bool {
return datas[i].CreatedAt.After(datas[j].CreatedAt)
})
return total, datas, err
return total, dtobas, err
}
func (u *BackupService) SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error) {
total, records, err := backupRepo.PageRecord(
search.Page, search.PageSize,
commonRepo.WithOrderBy("created_at desc"),
backupRepo.WithByCronID(search.CronjobID),
)
func (u *BackupService) LoadOneDriveInfo() (string, error) {
OneDriveID, err := settingRepo.Get(settingRepo.WithByKey("OneDriveID"))
if err != nil {
return 0, nil, err
return "", err
}
datas, err := u.loadRecordSize(records)
sort.Slice(datas, func(i, j int) bool {
return datas[i].CreatedAt.After(datas[j].CreatedAt)
})
return total, datas, err
}
type loadSizeHelper struct {
isOk bool
backupPath string
client cloud_storage.CloudStorageClient
}
func (u *BackupService) LoadOneDriveInfo() (dto.OneDriveInfo, error) {
var data dto.OneDriveInfo
data.RedirectUri = constant.OneDriveRedirectURI
clientID, err := settingRepo.Get(settingRepo.WithByKey("OneDriveID"))
idItem, err := base64.StdEncoding.DecodeString(OneDriveID.Value)
if err != nil {
return data, err
return "", err
}
idItem, err := base64.StdEncoding.DecodeString(clientID.Value)
if err != nil {
return data, err
}
data.ClientID = string(idItem)
clientSecret, err := settingRepo.Get(settingRepo.WithByKey("OneDriveSc"))
if err != nil {
return data, err
}
secretItem, err := base64.StdEncoding.DecodeString(clientSecret.Value)
if err != nil {
return data, err
}
data.ClientSecret = string(secretItem)
return data, err
return string(idItem), err
}
func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error) {
if info.Source == "LOCAL" {
localDir, err := loadLocalDir()
if err != nil {
return "", err
}
return path.Join(localDir, info.FileDir, info.FileName), nil
return info.FileDir + "/" + info.FileName, nil
}
backup, _ := backupRepo.Get(commonRepo.WithByType(info.Source))
if backup.ID == 0 {
@@ -165,7 +117,7 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
}
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp, constant.WebDAV:
case constant.Sftp:
varMap["username"] = backup.AccessKey
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
@@ -186,7 +138,9 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
}
srcPath := fmt.Sprintf("%s/%s", info.FileDir, info.FileName)
if len(backup.BackupPath) != 0 {
srcPath = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), srcPath)
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
srcPath = itemPath + srcPath
}
if exist, _ := backClient.Exist(srcPath); exist {
isOK, err := backClient.Download(srcPath, targetPath)
@@ -212,13 +166,11 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
}
}
if req.Type != "LOCAL" {
if _, err := u.checkBackupConn(&backup); err != nil {
isOk, err := u.checkBackupConn(&backup)
if err != nil || !isOk {
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
}
}
if backup.Type == constant.OneDrive {
StartRefreshOneDriveToken()
}
if err := backupRepo.Create(&backup); err != nil {
return err
}
@@ -231,7 +183,7 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
return nil, err
}
switch backupDto.Type {
case constant.Sftp, constant.WebDAV:
case constant.Sftp:
varMap["username"] = backupDto.AccessKey
varMap["password"] = backupDto.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
@@ -246,14 +198,7 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
}
func (u *BackupService) Delete(id uint) error {
backup, _ := backupRepo.Get(commonRepo.WithByID(id))
if backup.ID == 0 {
return constant.ErrRecordNotFound
}
if backup.Type == constant.OneDrive {
global.Cron.Remove(global.OneDriveCronID)
}
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDefaultDownload(backup.Type))
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByBackupID(id))
if len(cronjobs) != 0 {
return buserr.New(constant.ErrBackupInUsed)
}
@@ -266,18 +211,22 @@ func (u *BackupService) BatchDeleteRecord(ids []uint) error {
return err
}
for _, record := range records {
backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
if err != nil {
global.LOG.Errorf("load backup account %s info from db failed, err: %v", record.Source, err)
continue
}
client, err := u.NewClient(&backupAccount)
if err != nil {
global.LOG.Errorf("new client for backup account %s failed, err: %v", record.Source, err)
continue
}
if _, err = client.Delete(path.Join(record.FileDir, record.FileName)); err != nil {
global.LOG.Errorf("remove file %s from %s failed, err: %v", path.Join(record.FileDir, record.FileName), record.Source, err)
if record.Source == "LOCAL" {
if err := os.Remove(record.FileDir + "/" + record.FileName); err != nil {
global.LOG.Errorf("remove file %s failed, err: %v", record.FileDir+record.FileName, err)
}
} else {
backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
if err != nil {
return err
}
client, err := u.NewClient(&backupAccount)
if err != nil {
return err
}
if _, err = client.Delete(record.FileDir + record.FileName); err != nil {
global.LOG.Errorf("remove file %s from %s failed, err: %v", record.FileDir+record.FileName, record.Source, err)
}
}
}
return backupRepo.DeleteRecord(context.Background(), commonRepo.WithIdsIn(ids))
@@ -330,29 +279,27 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
if backup.Type == "LOCAL" {
if dir, ok := varMap["dir"]; ok {
if dirStr, isStr := dir.(string); isStr {
if strings.HasSuffix(dirStr, "/") && dirStr != "/" {
if strings.HasSuffix(dirStr, "/") {
dirStr = dirStr[:strings.LastIndex(dirStr, "/")]
}
if err := copyDir(oldDir, dirStr); err != nil {
_ = backupRepo.Update(req.ID, (map[string]interface{}{"vars": oldVars}))
return err
}
global.CONF.System.Backup = dirStr
}
}
}
return nil
}
func (u *BackupService) ListFiles(req dto.BackupSearchFile) []string {
var datas []string
func (u *BackupService) ListFiles(req dto.BackupSearchFile) ([]string, error) {
backup, err := backupRepo.Get(backupRepo.WithByType(req.Type))
if err != nil {
return datas
return nil, err
}
client, err := u.NewClient(&backup)
if err != nil {
return datas
return nil, err
}
prefix := "system_snapshot"
if len(backup.BackupPath) != 0 {
@@ -360,15 +307,15 @@ func (u *BackupService) ListFiles(req dto.BackupSearchFile) []string {
}
files, err := client.ListObjects(prefix)
if err != nil {
global.LOG.Debugf("load files from %s failed, err: %v", req.Type, err)
return datas
return nil, err
}
var datas []string
for _, file := range files {
if len(file) != 0 {
datas = append(datas, path.Base(file))
}
}
return datas
return datas, nil
}
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
@@ -376,9 +323,12 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
return nil, err
}
if backup.Type == "LOCAL" {
return nil, errors.New("not support")
}
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp, constant.WebDAV:
case constant.Sftp:
varMap["username"] = backup.AccessKey
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
@@ -403,15 +353,6 @@ func (u *BackupService) loadByType(accountType string, accounts []model.BackupAc
if err := copier.Copy(&item, &account); err != nil {
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
}
if account.Type == constant.OneDrive {
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(item.Vars), &varMap); err != nil {
return dto.BackupInfo{Type: accountType}
}
delete(varMap, "refresh_token")
itemVars, _ := json.Marshal(varMap)
item.Vars = string(itemVars)
}
return item
}
}
@@ -423,68 +364,48 @@ func (u *BackupService) loadAccessToken(backup *model.BackupAccount) error {
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
return fmt.Errorf("unmarshal backup vars failed, err: %v", err)
}
token, refreshToken, err := client.RefreshToken("authorization_code", varMap)
data := url.Values{}
data.Set("client_id", global.CONF.System.OneDriveID)
data.Set("client_secret", global.CONF.System.OneDriveSc)
data.Set("grant_type", "authorization_code")
data.Set("code", varMap["code"].(string))
data.Set("redirect_uri", constant.OneDriveRedirectURI)
client := &http.Client{}
req, err := http.NewRequest("POST", "https://login.microsoftonline.com/common/oauth2/v2.0/token", strings.NewReader(data.Encode()))
if err != nil {
return err
return fmt.Errorf("new http post client for access token failed, err: %v", err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("request for access token failed, err: %v", err)
}
delete(varMap, "code")
backup.Credential = token
varMap["refresh_status"] = constant.StatusSuccess
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
varMap["refresh_token"] = refreshToken
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("read data from response body failed, err: %v", err)
}
defer resp.Body.Close()
token := map[string]interface{}{}
if err := json.Unmarshal(respBody, &token); err != nil {
return fmt.Errorf("unmarshal data from response body failed, err: %v", err)
}
accessToken, ok := token["refresh_token"].(string)
if !ok {
return errors.New("no such access token in response")
}
itemVars, err := json.Marshal(varMap)
if err != nil {
return fmt.Errorf("json marshal var map failed, err: %v", err)
}
backup.Credential = accessToken
backup.Vars = string(itemVars)
return nil
}
func (u *BackupService) loadRecordSize(records []model.BackupRecord) ([]dto.BackupRecords, error) {
var datas []dto.BackupRecords
clientMap := make(map[string]loadSizeHelper)
var wg sync.WaitGroup
for i := 0; i < len(records); i++ {
var item dto.BackupRecords
if err := copier.Copy(&item, &records[i]); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
itemPath := path.Join(records[i].FileDir, records[i].FileName)
if _, ok := clientMap[records[i].Source]; !ok {
backup, err := backupRepo.Get(commonRepo.WithByType(records[i].Source))
if err != nil {
global.LOG.Errorf("load backup model %s from db failed, err: %v", records[i].Source, err)
clientMap[records[i].Source] = loadSizeHelper{}
datas = append(datas, item)
continue
}
client, err := u.NewClient(&backup)
if err != nil {
global.LOG.Errorf("load backup client %s from db failed, err: %v", records[i].Source, err)
clientMap[records[i].Source] = loadSizeHelper{}
datas = append(datas, item)
continue
}
item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
datas = append(datas, item)
clientMap[records[i].Source] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client, isOk: true}
continue
}
if clientMap[records[i].Source].isOk {
wg.Add(1)
go func(index int) {
item.Size, _ = clientMap[records[index].Source].client.Size(path.Join(clientMap[records[index].Source].backupPath, itemPath))
datas = append(datas, item)
wg.Done()
}(i)
} else {
datas = append(datas, item)
}
}
wg.Wait()
return datas, nil
}
func loadLocalDir() (string, error) {
backup, err := backupRepo.Get(commonRepo.WithByType("LOCAL"))
if err != nil {
@@ -566,50 +487,3 @@ func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, erro
targetPath := strings.TrimPrefix(path.Join(backup.BackupPath, "test/1panel"), "/")
return client.Upload(fileItem, targetPath)
}
func StartRefreshOneDriveToken() {
service := NewIBackupService()
oneDriveCronID, err := global.Cron.AddJob("0 * * * *", service)
if err != nil {
global.LOG.Errorf("can not add OneDrive corn job: %s", err.Error())
return
}
global.OneDriveCronID = oneDriveCronID
}
func (u *BackupService) Run() {
var backupItem model.BackupAccount
_ = global.DB.Where("`type` = ?", "OneDrive").First(&backupItem)
if backupItem.ID == 0 {
return
}
if len(backupItem.Credential) == 0 {
global.LOG.Error("OneDrive configuration lacks token information, please rebind.")
return
}
global.LOG.Info("start to refresh token of OneDrive ...")
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backupItem.Vars), &varMap); err != nil {
global.LOG.Errorf("Failed to refresh OneDrive token, please retry, err: %v", err)
return
}
token, refreshToken, err := client.RefreshToken("refresh_token", varMap)
varMap["refresh_status"] = constant.StatusSuccess
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
if err != nil {
varMap["refresh_status"] = constant.StatusFailed
varMap["refresh_msg"] = err.Error()
global.LOG.Errorf("Failed to refresh OneDrive token, please retry, err: %v", err)
return
}
varMap["refresh_token"] = refreshToken
varsItem, _ := json.Marshal(varMap)
_ = global.DB.Model(&model.BackupAccount{}).
Where("id = ?", backupItem.ID).
Updates(map[string]interface{}{
"credential": token,
"vars": varsItem,
}).Error
global.LOG.Info("Successfully refreshed OneDrive token.")
}
+9 -39
View File
@@ -10,13 +10,10 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/pkg/errors"
@@ -36,10 +33,10 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) error {
return err
}
timeNow := time.Now().Format("20060102150405")
itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName)
backupDir := path.Join(localDir, itemDir)
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", req.Name, req.DetailName))
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow)
if err := handleAppBackup(&install, backupDir, fileName); err != nil {
return err
}
@@ -50,7 +47,7 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) error {
DetailName: req.DetailName,
Source: "LOCAL",
BackupType: "LOCAL",
FileDir: itemDir,
FileDir: backupDir,
FileName: fileName,
}
@@ -73,7 +70,7 @@ func (u *BackupService) AppRecover(req dto.CommonRecover) error {
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return buserr.WithName("ErrFileNotFound", req.File)
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
}
if _, err := compose.Down(install.GetComposePath()); err != nil {
return err
@@ -109,21 +106,12 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
for _, resource := range resources {
switch resource.Key {
case constant.AppMysql, constant.AppMariaDB:
if resource.Key == "mysql" || resource.Key == "mariadb" {
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
if err != nil {
return err
}
if err := handleMysqlBackup(db.MysqlName, resource.Key, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
return err
}
case constant.AppPostgresql:
db, err := postgresqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
if err != nil {
return err
}
if err := handlePostgresqlBackup(db.PostgresqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
if err := handleMysqlBackup(db.MysqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
return err
}
}
@@ -202,21 +190,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
return err
}
}
switch database.Type {
case constant.AppPostgresql:
db, err := postgresqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
if err != nil {
return err
}
if err := handlePostgresqlRecover(dto.CommonRecover{
Name: database.Name,
DetailName: db.Name,
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
}, true); err != nil {
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
return err
}
case constant.AppMysql, constant.AppMariaDB:
if database.Type == "mysql" || database.Type == "mariadb" {
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
if err != nil {
return err
@@ -260,7 +234,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
_ = fileOp.DeleteDir(backPath)
if len(newEnvFile) != 0 {
envPath := fmt.Sprintf("%s/%s/.env", install.GetAppPath(), install.Name)
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, install.App.Key, install.Name)
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
@@ -304,10 +278,6 @@ func reCreateDB(dbID uint, database model.Database, oldEnv string) (*model.Datab
Password: oldPassword,
Permission: "%",
})
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDbName(fmt.Sprintf("%v", dbID)))
for _, job := range cronjobs {
_ = cronjobRepo.Update(job.ID, map[string]interface{}{"db_name": fmt.Sprintf("%v", createDB.ID)})
}
if err != nil {
return nil, envMap, err
}
+9 -20
View File
@@ -8,14 +8,12 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
"github.com/pkg/errors"
)
func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
@@ -25,11 +23,10 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
}
timeNow := time.Now().Format("20060102150405")
itemDir := fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName)
targetDir := path.Join(localDir, itemDir)
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
targetDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName))
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow)
if err := handleMysqlBackup(req.Name, req.Type, req.DetailName, targetDir, fileName); err != nil {
if err := handleMysqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil {
return err
}
@@ -39,7 +36,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
DetailName: req.DetailName,
Source: "LOCAL",
BackupType: "LOCAL",
FileDir: itemDir,
FileDir: targetDir,
FileName: fileName,
}
if err := backupRepo.CreateRecord(record); err != nil {
@@ -100,20 +97,18 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
return nil
}
func handleMysqlBackup(database, dbType, dbName, targetDir, fileName string) error {
func handleMysqlBackup(database, dbName, targetDir, fileName string) error {
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName), mysqlRepo.WithByMysqlName(database))
if err != nil {
return err
}
cli, version, err := LoadMysqlClientByFrom(database)
cli, _, err := LoadMysqlClientByFrom(database)
if err != nil {
return err
}
backupInfo := client.BackupInfo{
Name: dbName,
Type: dbType,
Version: version,
Format: dbInfo.Format,
TargetDir: targetDir,
FileName: fileName,
@@ -130,13 +125,13 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
isOk := false
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return buserr.WithName("ErrFileNotFound", req.File)
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
}
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(req.DetailName), mysqlRepo.WithByMysqlName(req.Name))
if err != nil {
return err
}
cli, version, err := LoadMysqlClientByFrom(req.Name)
cli, _, err := LoadMysqlClientByFrom(req.Name)
if err != nil {
return err
}
@@ -145,8 +140,6 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format("20060102150405")))
if err := cli.Backup(client.BackupInfo{
Name: req.DetailName,
Type: req.Type,
Version: version,
Format: dbInfo.Format,
TargetDir: path.Dir(rollbackFile),
FileName: path.Base(rollbackFile),
@@ -160,8 +153,6 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
global.LOG.Info("recover failed, start to rollback now")
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
Type: req.Type,
Version: version,
Format: dbInfo.Format,
SourceFile: rollbackFile,
@@ -178,8 +169,6 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
}
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
Type: req.Type,
Version: version,
Format: dbInfo.Format,
SourceFile: req.File,
-177
View File
@@ -1,177 +0,0 @@
package service
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/utils/common"
pgclient "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
)
func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
localDir, err := loadLocalDir()
if err != nil {
return err
}
timeNow := time.Now().Format("20060102150405")
itemDir := fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName)
targetDir := path.Join(localDir, itemDir)
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
if err := handlePostgresqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil {
return err
}
record := &model.BackupRecord{
Type: req.Type,
Name: req.Name,
DetailName: req.DetailName,
Source: "LOCAL",
BackupType: "LOCAL",
FileDir: itemDir,
FileName: fileName,
}
if err := backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
}
return nil
}
func (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {
if err := handlePostgresqlRecover(req, false); err != nil {
return err
}
return nil
}
func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
file := req.File
fileName := path.Base(req.File)
if strings.HasSuffix(fileName, ".tar.gz") {
fileNameItem := time.Now().Format("20060102150405")
dstDir := fmt.Sprintf("%s/%s", path.Dir(req.File), fileNameItem)
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dstDir, os.ModePerm); err != nil {
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
}
}
if err := handleUnTar(req.File, dstDir); err != nil {
_ = os.RemoveAll(dstDir)
return err
}
global.LOG.Infof("decompress file %s successful, now start to check test.sql is exist", req.File)
hasTestSql := false
_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if !info.IsDir() && info.Name() == "test.sql" {
hasTestSql = true
file = path
fileName = "test.sql"
}
return nil
})
if !hasTestSql {
_ = os.RemoveAll(dstDir)
return fmt.Errorf("no such file named test.sql in %s", fileName)
}
defer func() {
_ = os.RemoveAll(dstDir)
}()
}
req.File = path.Dir(file) + "/" + fileName
if err := handlePostgresqlRecover(req, false); err != nil {
return err
}
global.LOG.Info("recover from uploads successful!")
return nil
}
func handlePostgresqlBackup(database, dbName, targetDir, fileName string) error {
cli, err := LoadPostgresqlClientByFrom(database)
if err != nil {
return err
}
defer cli.Close()
backupInfo := pgclient.BackupInfo{
Name: dbName,
TargetDir: targetDir,
FileName: fileName,
Timeout: 300,
}
if err := cli.Backup(backupInfo); err != nil {
return err
}
return nil
}
func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
isOk := false
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return buserr.WithName("ErrFileNotFound", req.File)
}
dbInfo, err := postgresqlRepo.Get(commonRepo.WithByName(req.DetailName), postgresqlRepo.WithByPostgresqlName(req.Name))
if err != nil {
return err
}
cli, err := LoadPostgresqlClientByFrom(req.Name)
if err != nil {
return err
}
defer cli.Close()
if !isRollback {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format("20060102150405")))
if err := cli.Backup(client.BackupInfo{
Name: req.DetailName,
TargetDir: path.Dir(rollbackFile),
FileName: path.Base(rollbackFile),
Timeout: 300,
}); err != nil {
return fmt.Errorf("backup postgresql db %s for rollback before recover failed, err: %v", req.DetailName, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
SourceFile: rollbackFile,
Timeout: 300,
}); err != nil {
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
}
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
SourceFile: req.File,
Username: dbInfo.Username,
Timeout: 300,
}); err != nil {
return err
}
isOk = true
return nil
}
+4 -6
View File
@@ -14,7 +14,6 @@ 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/compose"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/pkg/errors"
@@ -35,7 +34,7 @@ func (u *BackupService) RedisBackup() error {
}
global.LOG.Infof("appendonly in redis conf is %s", appendonly)
timeNow := time.Now().Format("20060102150405") + common.RandStrAndNum(5)
timeNow := time.Now().Format("20060102150405")
fileName := fmt.Sprintf("%s.rdb", timeNow)
if appendonly == "yes" {
if strings.HasPrefix(redisInfo.Version, "6.") {
@@ -44,8 +43,7 @@ func (u *BackupService) RedisBackup() error {
fileName = fmt.Sprintf("%s.tar.gz", timeNow)
}
}
itemDir := fmt.Sprintf("database/redis/%s", redisInfo.Name)
backupDir := path.Join(localDir, itemDir)
backupDir := path.Join(localDir, fmt.Sprintf("database/redis/%s", redisInfo.Name))
if err := handleRedisBackup(redisInfo, backupDir, fileName); err != nil {
return err
}
@@ -53,7 +51,7 @@ func (u *BackupService) RedisBackup() error {
Type: "redis",
Source: "LOCAL",
BackupType: "LOCAL",
FileDir: itemDir,
FileDir: backupDir,
FileName: fileName,
}
if err := backupRepo.CreateRecord(record); err != nil {
@@ -113,7 +111,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) err
func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback bool) error {
fileOp := files.NewFileOp()
if !fileOp.Stat(recoverFile) {
return buserr.WithName("ErrFileNotFound", recoverFile)
return fmt.Errorf("%s file is not exist", recoverFile)
}
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
+19 -22
View File
@@ -15,7 +15,6 @@ 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/compose"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/pkg/errors"
@@ -26,15 +25,14 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
if err != nil {
return err
}
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(req.Name))
if err != nil {
return err
}
timeNow := time.Now().Format("20060102150405")
itemDir := fmt.Sprintf("website/%s", req.Name)
backupDir := path.Join(localDir, itemDir)
fileName := fmt.Sprintf("%s_%s.tar.gz", website.PrimaryDomain, timeNow+common.RandStrAndNum(5))
backupDir := path.Join(localDir, fmt.Sprintf("website/%s", req.Name))
fileName := fmt.Sprintf("%s_%s.tar.gz", website.PrimaryDomain, timeNow)
if err := handleWebsiteBackup(&website, backupDir, fileName); err != nil {
return err
}
@@ -42,10 +40,10 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
record := &model.BackupRecord{
Type: "website",
Name: website.PrimaryDomain,
DetailName: req.DetailName,
DetailName: "",
Source: "LOCAL",
BackupType: "LOCAL",
FileDir: itemDir,
FileDir: backupDir,
FileName: fileName,
}
if err := backupRepo.CreateRecord(record); err != nil {
@@ -56,14 +54,14 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
}
func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return buserr.WithName("ErrFileNotFound", req.File)
}
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(req.Name))
if err != nil {
return err
}
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
}
global.LOG.Infof("recover website %s from backup file %s", req.Name, req.File)
if err := handleWebsiteRecover(&website, req.File, false); err != nil {
return err
@@ -81,6 +79,15 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
_ = os.RemoveAll(tmpPath)
}()
temPathWithName := tmpPath + "/" + website.Alias
if !fileOp.Stat(tmpPath+"/website.json") || !fileOp.Stat(temPathWithName+".conf") || !fileOp.Stat(temPathWithName+".web.tar.gz") {
return buserr.WithDetail(constant.ErrBackupExist, ".conf or .web.tar.gz", nil)
}
if website.Type == constant.Deployment {
if !fileOp.Stat(temPathWithName + ".app.tar.gz") {
return buserr.WithDetail(constant.ErrBackupExist, ".app.tar.gz", nil)
}
}
var oldWebsite model.Website
websiteJson, err := os.ReadFile(tmpPath + "/website.json")
if err != nil {
@@ -94,16 +101,6 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
return err
}
temPathWithName := tmpPath + "/" + website.Alias
if !fileOp.Stat(tmpPath+"/website.json") || !fileOp.Stat(temPathWithName+".conf") || !fileOp.Stat(temPathWithName+".web.tar.gz") {
return buserr.WithDetail(constant.ErrBackupExist, ".conf or .web.tar.gz", nil)
}
if website.Type == constant.Deployment {
if !fileOp.Stat(temPathWithName + ".app.tar.gz") {
return buserr.WithDetail(constant.ErrBackupExist, ".app.tar.gz", nil)
}
}
isOk := false
if !isRollback {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format("20060102150405")))
+4 -4
View File
@@ -23,7 +23,7 @@ func NewICommandService() ICommandService {
}
func (u *CommandService) List() ([]dto.CommandInfo, error) {
commands, err := commandRepo.GetList(commonRepo.WithOrderBy("name"))
commands, err := commandRepo.GetList()
if err != nil {
return nil, constant.ErrRecordNotFound
}
@@ -39,7 +39,7 @@ func (u *CommandService) List() ([]dto.CommandInfo, error) {
}
func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) {
cmdList, err := commandRepo.GetList(commonRepo.WithOrderBy("name"))
cmdList, err := commandRepo.GetList()
if err != nil {
return nil, err
}
@@ -65,11 +65,11 @@ func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) {
}
func (u *CommandService) SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error) {
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID))
if err != nil {
return 0, nil, err
}
groups, _ := groupRepo.GetList(commonRepo.WithByType("command"), commonRepo.WithOrderBy("name"))
groups, _ := groupRepo.GetList(commonRepo.WithByType("command"))
var dtoCommands []dto.CommandInfo
for _, command := range commands {
var item dto.CommandInfo
+25 -98
View File
@@ -2,12 +2,12 @@ package service
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
"sort"
"strconv"
@@ -15,9 +15,6 @@ import (
"sync"
"syscall"
"time"
"unicode/utf8"
"github.com/pkg/errors"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
@@ -30,7 +27,6 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/gorilla/websocket"
@@ -92,21 +88,10 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
options.Filters = filters.NewArgs()
options.Filters.Add("label", req.Filters)
}
containers, err := client.ContainerList(context.Background(), options)
list, err = client.ContainerList(context.Background(), options)
if err != nil {
return 0, nil, err
}
if req.ExcludeAppStore {
for _, item := range containers {
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
continue
}
list = append(list, item)
}
} else {
list = containers
}
if len(req.Name) != 0 {
length, count := len(list), 0
for count < length {
@@ -175,8 +160,8 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
IsFromApp = true
}
ports := loadContainerPort(item.Ports)
info := dto.ContainerInfo{
ports := simplifyPort(item.Ports)
backDatas[i] = dto.ContainerInfo{
ContainerID: item.ID,
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
Name: item.Names[0][1:],
@@ -188,16 +173,6 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
IsFromApp: IsFromApp,
IsFromCompose: IsFromCompose,
}
install, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(info.Name))
if install.ID > 0 {
info.AppInstallName = install.Name
info.AppName = install.App.Name
websites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(install.ID))
for _, website := range websites {
info.Websites = append(info.Websites, website.PrimaryDomain)
}
}
backDatas[i] = info
if item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {
networks := make([]string, 0, len(item.NetworkSettings.Networks))
for key := range item.NetworkSettings.Networks {
@@ -337,7 +312,7 @@ func (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {
data := dto.ResourceLimit{
CPU: cpuCounts,
Memory: memoryInfo.Total,
Memory: int(memoryInfo.Total),
}
return &data, nil
}
@@ -433,9 +408,8 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
}
}
data.AutoRemove = oldContainer.HostConfig.AutoRemove
data.Privileged = oldContainer.HostConfig.Privileged
data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts
data.RestartPolicy = string(oldContainer.HostConfig.RestartPolicy.Name)
data.RestartPolicy = oldContainer.HostConfig.RestartPolicy.Name
if oldContainer.HostConfig.NanoCPUs != 0 {
data.NanoCPUs = float64(oldContainer.HostConfig.NanoCPUs) / 1000000000
}
@@ -467,7 +441,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
if !req.ForcePull {
return err
}
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
}
}
@@ -510,7 +484,7 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
if !req.ForcePull {
return err
}
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
}
}
config := oldContainer.Config
@@ -640,9 +614,6 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
cmd := exec.Command("bash", "-c", command)
if !follow {
stdout, _ := cmd.CombinedOutput()
if !utf8.Valid(stdout) {
return errors.New("invalid utf8")
}
if err := wsConn.WriteMessage(websocket.TextMessage, stdout); err != nil {
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
}
@@ -684,9 +655,6 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
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
@@ -723,6 +691,7 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
if cache, ok := stats.MemoryStats.Stats["cache"]; ok {
data.Cache = float64(cache) / 1024 / 1024
}
data.Memory = data.Memory - data.Cache
data.NetworkRX, data.NetworkTX = calculateNetwork(stats.Networks)
data.ShotTime = stats.Read
return &data, nil
@@ -730,15 +699,18 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) string {
filePath := ""
if req.Type == "compose-detail" {
cli, err := docker.NewDockerClient()
switch req.Type {
case "image-pull", "image-push", "image-build", "compose-create":
filePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
case "compose-detail":
client, err := docker.NewDockerClient()
if err != nil {
return ""
}
options := types.ContainerListOptions{All: true}
options.Filters = filters.NewArgs()
options.Filters.Add("label", fmt.Sprintf("%s=%s", composeProjectLabel, req.Name))
containers, err := cli.ContainerList(context.Background(), options)
containers, err := client.ContainerList(context.Background(), options)
if err != nil {
return ""
}
@@ -753,6 +725,9 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
break
}
}
if req.Type == "compose-create" {
filePath = path.Join(path.Dir(filePath), "compose.log")
}
}
if _, err := os.Stat(filePath); err != nil {
return ""
@@ -790,7 +765,7 @@ func calculateCPUPercentUnix(stats *types.StatsJSON) float64 {
}
func calculateMemPercentUnix(memStats types.MemoryStats) float64 {
memPercent := 0.0
memUsage := float64(memStats.Usage)
memUsage := float64(memStats.Usage - memStats.Stats["cache"])
memLimit := float64(memStats.Limit)
if memUsage > 0.0 && memLimit > 0.0 {
memPercent = (memUsage / memLimit) * 100.0
@@ -836,25 +811,7 @@ func checkImageExist(client *client.Client, image string) bool {
}
func pullImages(ctx context.Context, client *client.Client, image string) error {
options := types.ImagePullOptions{}
repos, _ := imageRepoRepo.List()
if len(repos) != 0 {
for _, repo := range repos {
if strings.HasPrefix(image, repo.DownloadUrl) && repo.Auth {
authConfig := registry.AuthConfig{
Username: repo.Username,
Password: repo.Password,
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return err
}
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
options.RegistryAuth = authStr
}
}
}
out, err := client.ImagePull(ctx, image, options)
out, err := client.ImagePull(ctx, image, types.ImagePullOptions{})
if err != nil {
return err
}
@@ -894,7 +851,6 @@ func loadCpuAndMem(client *client.Client, container string) dto.ContainerListSta
data.MemoryCache = stats.MemoryStats.Stats["cache"]
data.MemoryUsage = stats.MemoryStats.Usage
data.MemoryLimit = stats.MemoryStats.Limit
data.MemoryPercent = calculateMemPercentUnix(stats.MemoryStats)
return data
}
@@ -981,11 +937,10 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
networkConf = network.NetworkingConfig{}
}
hostConf.Privileged = req.Privileged
hostConf.AutoRemove = req.AutoRemove
hostConf.CPUShares = req.CPUShares
hostConf.PublishAllPorts = req.PublishAllPorts
hostConf.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(req.RestartPolicy)}
hostConf.RestartPolicy = container.RestartPolicy{Name: req.RestartPolicy}
if req.RestartPolicy == "on-failure" {
hostConf.RestartPolicy.MaximumRetryCount = 5
}
@@ -1063,22 +1018,6 @@ func loadVolumeBinds(binds []string) []dto.VolumeHelper {
return datas
}
func loadContainerPort(ports []types.Port) []string {
var (
ipv4Ports []types.Port
ipv6Ports []types.Port
)
for _, port := range ports {
if strings.Contains(port.IP, ":") {
ipv6Ports = append(ipv6Ports, port)
} else {
ipv4Ports = append(ipv4Ports, port)
}
}
list1 := simplifyPort(ipv4Ports)
list2 := simplifyPort(ipv6Ports)
return append(list1, list2...)
}
func simplifyPort(ports []types.Port) []string {
var datas []string
if len(ports) == 0 {
@@ -1109,19 +1048,13 @@ func simplifyPort(ports []types.Port) []string {
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", start.IP, start.PublicPort, start.PrivatePort, start.Type)
}
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
} else {
itemPortStr := fmt.Sprintf("%s:%v-%v/%s", start.IP, start.PrivatePort, ports[i-1].PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v-%v->%v-%v/%s", start.IP, start.PublicPort, ports[i-1].PublicPort, start.PrivatePort, ports[i-1].PrivatePort, start.Type)
}
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
}
start = ports[i]
}
@@ -1131,19 +1064,13 @@ func simplifyPort(ports []types.Port) []string {
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", start.IP, start.PublicPort, start.PrivatePort, start.Type)
}
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
} else {
itemPortStr := fmt.Sprintf("%s:%v-%v/%s", start.IP, start.PrivatePort, ports[i].PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v-%v->%v-%v/%s", start.IP, start.PublicPort, ports[i].PublicPort, start.PrivatePort, ports[i].PrivatePort, start.Type)
}
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
}
}
}
+3 -26
View File
@@ -216,8 +216,7 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
if cmd.CheckIllegal(req.Name, req.Path) {
return buserr.New(constant.ErrCmdIllegal)
}
oldFile, err := os.ReadFile(req.Path)
if err != nil {
if _, err := os.Stat(req.Path); err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
}
file, err := os.OpenFile(req.Path, os.O_WRONLY|os.O_TRUNC, 0640)
@@ -231,16 +230,10 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.Path)
if stdout, err := compose.Down(req.Path); err != nil {
if err := recreateCompose(string(oldFile), req.Path); err != nil {
return fmt.Errorf("update failed when handle compose down, err: %s, recreate failed: %v", string(stdout), err)
}
return fmt.Errorf("update failed when handle compose down, err: %s", string(stdout))
return errors.New(string(stdout))
}
if stdout, err := compose.Up(req.Path); err != nil {
if err := recreateCompose(string(oldFile), req.Path); err != nil {
return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err)
}
return fmt.Errorf("update failed when handle compose up, err: %s", string(stdout))
return errors.New(string(stdout))
}
return nil
@@ -268,19 +261,3 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
}
return nil
}
func recreateCompose(content, path string) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(content)
write.Flush()
if stdout, err := compose.Up(path); err != nil {
return errors.New(string(stdout))
}
return nil
}
+17 -45
View File
@@ -116,57 +116,29 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
return err
}
var (
ipams []network.IPAMConfig
enableV6 bool
ipam network.IPAMConfig
hasConf bool
)
if req.Ipv4 {
var itemIpam network.IPAMConfig
if len(req.AuxAddress) != 0 {
itemIpam.AuxAddress = make(map[string]string)
}
if len(req.Subnet) != 0 {
itemIpam.Subnet = req.Subnet
}
if len(req.Gateway) != 0 {
itemIpam.Gateway = req.Gateway
}
if len(req.IPRange) != 0 {
itemIpam.IPRange = req.IPRange
}
for _, addr := range req.AuxAddress {
itemIpam.AuxAddress[addr.Key] = addr.Value
}
ipams = append(ipams, itemIpam)
if len(req.Subnet) != 0 {
ipam.Subnet = req.Subnet
hasConf = true
}
if req.Ipv6 {
enableV6 = true
var itemIpam network.IPAMConfig
if len(req.AuxAddress) != 0 {
itemIpam.AuxAddress = make(map[string]string)
}
if len(req.SubnetV6) != 0 {
itemIpam.Subnet = req.SubnetV6
}
if len(req.GatewayV6) != 0 {
itemIpam.Gateway = req.GatewayV6
}
if len(req.IPRangeV6) != 0 {
itemIpam.IPRange = req.IPRangeV6
}
for _, addr := range req.AuxAddressV6 {
itemIpam.AuxAddress[addr.Key] = addr.Value
}
ipams = append(ipams, itemIpam)
if len(req.Gateway) != 0 {
ipam.Gateway = req.Gateway
hasConf = true
}
if len(req.IPRange) != 0 {
ipam.IPRange = req.IPRange
hasConf = true
}
options := types.NetworkCreate{
EnableIPv6: enableV6,
Driver: req.Driver,
Options: stringsToMap(req.Options),
Labels: stringsToMap(req.Labels),
Driver: req.Driver,
Options: stringsToMap(req.Options),
Labels: stringsToMap(req.Labels),
}
if len(ipams) != 0 {
options.IPAM = &network.IPAM{Config: ipams}
if hasConf {
options.IPAM = &network.IPAM{Config: []network.IPAMConfig{ipam}}
}
if _, err := client.NetworkCreate(context.TODO(), req.Name, options); err != nil {
return err
+3 -9
View File
@@ -9,7 +9,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/volume"
@@ -52,20 +51,15 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
records = list.Volumes[start:end]
}
nyc, _ := time.LoadLocation(common.LoadTimeZone())
for _, item := range records {
tag := make([]string, 0)
for _, val := range item.Labels {
tag = append(tag, val)
}
var createTime time.Time
if strings.Contains(item.CreatedAt, "Z") {
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05Z", item.CreatedAt, nyc)
} else if strings.Contains(item.CreatedAt, "+") {
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05+08:00", item.CreatedAt, nyc)
} else {
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05", item.CreatedAt, nyc)
if len(item.CreatedAt) > 19 {
item.CreatedAt = item.CreatedAt[0:19]
}
createTime, _ := time.Parse("2006-01-02T15:04:05", item.CreatedAt)
data = append(data, dto.Volume{
CreatedAt: createTime,
Name: item.Name,
+86 -74
View File
@@ -5,12 +5,11 @@ import (
"fmt"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/jinzhu/copier"
@@ -29,10 +28,10 @@ type ICronjobService interface {
UpdateStatus(id uint, status string) error
Delete(req dto.CronjobBatchDelete) error
Download(down dto.CronjobDownload) (string, error)
StartJob(cronjob *model.Cronjob) (string, error)
StartJob(cronjob *model.Cronjob) (int, error)
CleanRecord(req dto.CronjobClean) error
LoadRecordLog(req dto.OperateByID) string
LoadRecordLog(req dto.OperateByID) (string, error)
}
func NewICronjobService() ICronjobService {
@@ -47,6 +46,14 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
if err := copier.Copy(&item, &cronjob); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if hasBackup(item.Type) {
backup, _ := backupRepo.Get(commonRepo.WithByID(uint(item.TargetDirID)))
if len(backup.Type) != 0 {
item.TargetDir = backup.Type
}
} else {
item.TargetDir = "-"
}
record, _ := cronjobRepo.RecordFirst(cronjob.ID)
if record.ID != 0 {
item.LastRecordTime = record.StartTime.Format("2006-01-02 15:04:05")
@@ -76,19 +83,19 @@ func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interfac
return total, dtoCronjobs, err
}
func (u *CronjobService) LoadRecordLog(req dto.OperateByID) string {
func (u *CronjobService) LoadRecordLog(req dto.OperateByID) (string, error) {
record, err := cronjobRepo.GetRecord(commonRepo.WithByID(req.ID))
if err != nil {
return ""
return "", err
}
if _, err := os.Stat(record.Records); err != nil {
return ""
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile(record.Records)
if err != nil {
return ""
return "", err
}
return string(content)
return string(content), nil
}
func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
@@ -96,23 +103,24 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
if err != nil {
return err
}
if req.CleanData {
if hasBackup(cronjob.Type) {
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if req.CleanData && hasBackup(cronjob.Type) {
cronjob.RetainCopies = 0
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
if err != nil {
return err
}
if backup.Type != "LOCAL" {
localDir, err := loadLocalDir()
if err != nil {
return err
}
cronjob.RetainCopies = 0
u.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{})
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
return err
}
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
} else {
u.removeExpiredLog(cronjob)
}
}
if req.IsDelete {
records, _ := backupRepo.ListRecord(backupRepo.WithByCronID(cronjob.ID))
for _, records := range records {
records.CronjobID = 0
_ = backupRepo.UpdateRecord(&records)
u.HandleRmExpired(backup.Type, backup.BackupPath, "", &cronjob, nil)
}
}
delRecords, err := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(req.CronjobID)))
@@ -176,40 +184,29 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
cronjob.Status = constant.StatusEnable
cronjob.Spec = loadSpec(cronjob)
global.LOG.Infof("create cronjob %s successful, spec: %s", cronjob.Name, cronjob.Spec)
spec := cronjob.Spec
entryIDs, err := u.StartJob(&cronjob)
entryID, err := u.StartJob(&cronjob)
if err != nil {
return err
}
cronjob.Spec = spec
cronjob.EntryIDs = entryIDs
cronjob.EntryID = uint64(entryID)
if err := cronjobRepo.Create(&cronjob); err != nil {
return err
}
return nil
}
func (u *CronjobService) StartJob(cronjob *model.Cronjob) (string, error) {
if len(cronjob.EntryIDs) != 0 {
ids := strings.Split(cronjob.EntryIDs, ",")
for _, id := range ids {
idItem, _ := strconv.Atoi(id)
global.Cron.Remove(cron.EntryID(idItem))
}
func (u *CronjobService) StartJob(cronjob *model.Cronjob) (int, error) {
if cronjob.EntryID != 0 {
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
}
specs := strings.Split(cronjob.Spec, ",")
var ids []string
for _, spec := range specs {
cronjob.Spec = spec
entryID, err := u.AddCronJob(cronjob)
if err != nil {
return "", err
}
ids = append(ids, fmt.Sprintf("%v", entryID))
entryID, err := u.AddCronJob(cronjob)
if err != nil {
return 0, err
}
return strings.Join(ids, ","), nil
return entryID, nil
}
func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
@@ -218,13 +215,9 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
if cronjob.ID == 0 {
return errors.New("find cronjob in db failed")
}
ids := strings.Split(cronjob.EntryIDs, ",")
for _, id := range ids {
idItem, _ := strconv.Atoi(id)
global.Cron.Remove(cron.EntryID(idItem))
}
global.LOG.Infof("stop cronjob entryID: %s", cronjob.EntryIDs)
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData, IsDelete: true}); err != nil {
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData}); err != nil {
return err
}
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {
@@ -245,37 +238,37 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
return constant.ErrRecordNotFound
}
upMap := make(map[string]interface{})
cronjob.EntryIDs = cronModel.EntryIDs
cronjob.EntryID = cronModel.EntryID
cronjob.Type = cronModel.Type
spec := cronjob.Spec
cronjob.Spec = loadSpec(cronjob)
if cronModel.Status == constant.StatusEnable {
newEntryIDs, err := u.StartJob(&cronjob)
newEntryID, err := u.StartJob(&cronjob)
if err != nil {
return err
}
upMap["entry_ids"] = newEntryIDs
upMap["entry_id"] = newEntryID
} else {
ids := strings.Split(cronjob.EntryIDs, ",")
for _, id := range ids {
idItem, _ := strconv.Atoi(id)
global.Cron.Remove(cron.EntryID(idItem))
}
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
}
upMap["name"] = req.Name
upMap["spec"] = spec
upMap["spec"] = cronjob.Spec
upMap["script"] = req.Script
upMap["container_name"] = req.ContainerName
upMap["spec_type"] = req.SpecType
upMap["week"] = req.Week
upMap["day"] = req.Day
upMap["hour"] = req.Hour
upMap["minute"] = req.Minute
upMap["second"] = req.Second
upMap["app_id"] = req.AppID
upMap["website"] = req.Website
upMap["exclusion_rules"] = req.ExclusionRules
upMap["db_type"] = req.DBType
upMap["db_name"] = req.DBName
upMap["url"] = req.URL
upMap["source_dir"] = req.SourceDir
upMap["backup_accounts"] = req.BackupAccounts
upMap["default_download"] = req.DefaultDownload
upMap["keep_local"] = req.KeepLocal
upMap["target_dir_id"] = req.TargetDirID
upMap["retain_copies"] = req.RetainCopies
return cronjobRepo.Update(id, upMap)
}
@@ -286,23 +279,19 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
return errors.WithMessage(constant.ErrRecordNotFound, "record not found")
}
var (
entryIDs string
err error
entryID int
err error
)
if status == constant.StatusEnable {
entryIDs, err = u.StartJob(&cronjob)
entryID, err = u.StartJob(&cronjob)
if err != nil {
return err
}
} else {
ids := strings.Split(cronjob.EntryIDs, ",")
for _, id := range ids {
idItem, _ := strconv.Atoi(id)
global.Cron.Remove(cron.EntryID(idItem))
}
global.LOG.Infof("stop cronjob entryID: %s", cronjob.EntryIDs)
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
}
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_ids": entryIDs})
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_id": entryID})
}
func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
@@ -338,3 +327,26 @@ func mkdirAndWriteFile(cronjob *model.Cronjob, startTime time.Time, msg []byte)
write.Flush()
return path, nil
}
func loadSpec(cronjob model.Cronjob) string {
switch cronjob.SpecType {
case "perMonth":
return fmt.Sprintf("%v %v %v * *", cronjob.Minute, cronjob.Hour, cronjob.Day)
case "perWeek":
return fmt.Sprintf("%v %v * * %v", cronjob.Minute, cronjob.Hour, cronjob.Week)
case "perNDay":
return fmt.Sprintf("%v %v */%v * *", cronjob.Minute, cronjob.Hour, cronjob.Day)
case "perDay":
return fmt.Sprintf("%v %v * * *", cronjob.Minute, cronjob.Hour)
case "perNHour":
return fmt.Sprintf("%v */%v * * *", cronjob.Minute, cronjob.Hour)
case "perHour":
return fmt.Sprintf("%v * * * *", cronjob.Minute)
case "perNMinute":
return fmt.Sprintf("@every %vm", cronjob.Minute)
case "perNSecond":
return fmt.Sprintf("@every %vs", cronjob.Second)
default:
return ""
}
}
-386
View File
@@ -1,386 +0,0 @@
package service
import (
"fmt"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
)
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) error {
var apps []model.AppInstall
if cronjob.AppID == "all" {
apps, _ = appInstallRepo.ListBy()
} else {
itemID, _ := (strconv.Atoi(cronjob.AppID))
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
if err != nil {
return err
}
apps = append(apps, app)
}
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if err != nil {
return err
}
for _, app := range apps {
var record model.BackupRecord
record.From = "cronjob"
record.Type = "app"
record.CronjobID = cronjob.ID
record.Name = app.App.Key
record.DetailName = app.Name
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format("20060102150405")+common.RandStrAndNum(5))
if err := handleAppBackup(&app, backupDir, record.FileName); err != nil {
return err
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
}
return nil
}
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Time) error {
webs := loadWebsForJob(cronjob)
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if err != nil {
return err
}
for _, web := range webs {
var record model.BackupRecord
record.From = "cronjob"
record.Type = "website"
record.CronjobID = cronjob.ID
record.Name = web.PrimaryDomain
record.DetailName = web.Alias
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s", web.PrimaryDomain))
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.PrimaryDomain, startTime.Format("20060102150405")+common.RandStrAndNum(5))
if err := handleWebsiteBackup(&web, backupDir, record.FileName); err != nil {
return err
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
}
return nil
}
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Time) error {
dbs := loadDbsForJob(cronjob)
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if err != nil {
return err
}
for _, dbInfo := range dbs {
var record model.BackupRecord
record.From = "cronjob"
record.Type = dbInfo.DBType
record.CronjobID = cronjob.ID
record.Name = dbInfo.Database
record.DetailName = dbInfo.Name
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405")+common.RandStrAndNum(5))
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
if err := handleMysqlBackup(dbInfo.Database, dbInfo.DBType, dbInfo.Name, backupDir, record.FileName); err != nil {
return err
}
} else {
if err := handlePostgresqlBackup(dbInfo.Database, dbInfo.Name, backupDir, record.FileName); err != nil {
return err
}
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
}
return nil
}
func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.Time) error {
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if err != nil {
return err
}
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405")+common.RandStrAndNum(5))
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name))
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
return err
}
var record model.BackupRecord
record.From = "cronjob"
record.Type = "directory"
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, fileName))
if err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
record.FileName = fileName
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
return nil
}
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time) error {
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if err != nil {
return err
}
nameItem := startTime.Format("20060102150405") + common.RandStrAndNum(5)
fileName := fmt.Sprintf("system_log_%s.tar.gz", nameItem)
backupDir := path.Join(global.CONF.System.TmpDir, "log", nameItem)
if err := handleBackupLogs(backupDir, fileName); err != nil {
return err
}
var record model.BackupRecord
record.From = "cronjob"
record.Type = "log"
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(path.Dir(backupDir), fileName))
if err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
record.FileName = fileName
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
return nil
}
func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, startTime time.Time, logPath string) error {
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if err != nil {
return err
}
var record model.BackupRecord
record.From = "cronjob"
record.Type = "directory"
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
record.FileDir = "system_snapshot"
req := dto.SnapshotCreate{
From: record.BackupType,
DefaultDownload: cronjob.DefaultDownload,
}
name, err := NewISnapshotService().HandleSnapshot(true, logPath, req, startTime.Format("20060102150405")+common.RandStrAndNum(5))
if err != nil {
return err
}
record.FileName = name + ".tar.gz"
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
return nil
}
type databaseHelper struct {
DBType string
Database string
Name string
}
func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
var dbs []databaseHelper
if cronjob.DBName == "all" {
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
mysqlItems, _ := mysqlRepo.List()
for _, mysql := range mysqlItems {
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: mysql.MysqlName,
Name: mysql.Name,
})
}
} else {
pgItems, _ := postgresqlRepo.List()
for _, pg := range pgItems {
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: pg.PostgresqlName,
Name: pg.Name,
})
}
}
return dbs
}
itemID, _ := (strconv.Atoi(cronjob.DBName))
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
mysqlItem, _ := mysqlRepo.Get(commonRepo.WithByID(uint(itemID)))
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: mysqlItem.MysqlName,
Name: mysqlItem.Name,
})
} else {
pgItem, _ := postgresqlRepo.Get(commonRepo.WithByID(uint(itemID)))
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: pgItem.PostgresqlName,
Name: pgItem.Name,
})
}
return dbs
}
func loadWebsForJob(cronjob model.Cronjob) []model.Website {
var weblist []model.Website
if cronjob.Website == "all" {
weblist, _ = websiteRepo.List()
return weblist
}
itemID, _ := (strconv.Atoi(cronjob.Website))
webItem, _ := websiteRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
if webItem.ID != 0 {
weblist = append(weblist, webItem)
}
return weblist
}
func loadRecordPath(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper) (string, string) {
source := accountMap[fmt.Sprintf("%v", cronjob.DefaultDownload)].backType
targets := strings.Split(cronjob.BackupAccounts, ",")
var itemAccounts []string
for _, target := range targets {
if len(target) == 0 {
continue
}
if len(accountMap[target].backType) != 0 {
itemAccounts = append(itemAccounts, accountMap[target].backType)
}
}
backupType := strings.Join(itemAccounts, ",")
return source, backupType
}
func handleBackupLogs(targetDir, fileName string) error {
websites, err := websiteRepo.List()
if err != nil {
return err
}
if len(websites) != 0 {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
webItem := path.Join(nginxInstall.GetPath(), "www/sites")
for _, website := range websites {
dirItem := path.Join(targetDir, "website", website.Alias)
if _, err := os.Stat(dirItem); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dirItem, os.ModePerm); err != nil {
return err
}
}
itemDir := path.Join(webItem, website.Alias, "log")
logFiles, _ := os.ReadDir(itemDir)
if len(logFiles) != 0 {
for i := 0; i < len(logFiles); i++ {
if !logFiles[i].IsDir() {
_ = cpBinary([]string{path.Join(itemDir, logFiles[i].Name())}, dirItem)
}
}
}
itemDir2 := path.Join(global.CONF.System.Backup, "log/website", website.Alias)
logFiles2, _ := os.ReadDir(itemDir2)
if len(logFiles2) != 0 {
for i := 0; i < len(logFiles2); i++ {
if !logFiles2[i].IsDir() {
_ = cpBinary([]string{path.Join(itemDir2, logFiles2[i].Name())}, dirItem)
}
}
}
}
global.LOG.Debug("backup website log successful!")
}
systemLogDir := path.Join(global.CONF.System.BaseDir, "1panel/log")
systemDir := path.Join(targetDir, "system")
if _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(systemDir, os.ModePerm); err != nil {
return err
}
}
systemLogFiles, _ := os.ReadDir(systemLogDir)
if len(systemLogFiles) != 0 {
for i := 0; i < len(systemLogFiles); i++ {
if !systemLogFiles[i].IsDir() {
_ = cpBinary([]string{path.Join(systemLogDir, systemLogFiles[i].Name())}, systemDir)
}
}
}
global.LOG.Debug("backup system log successful!")
loginLogFiles, _ := os.ReadDir("/var/log")
loginDir := path.Join(targetDir, "login")
if _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(loginDir, os.ModePerm); err != nil {
return err
}
}
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)
}
}
}
global.LOG.Debug("backup ssh log successful!")
if err := handleTar(targetDir, path.Dir(targetDir), fileName, ""); err != nil {
return err
}
defer func() {
_ = os.RemoveAll(targetDir)
}()
return nil
}
+529 -194
View File
@@ -5,14 +5,13 @@ import (
"fmt"
"os"
"path"
"strconv"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
@@ -34,58 +33,48 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
if len(cronjob.Script) == 0 {
return
}
record.Records = u.generateLogsPath(*cronjob, record.StartTime)
_ = 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)
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("docker exec %s %s", cronjob.ContainerName, cronjob.Script))
} else {
message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
}
err = u.handleShell(cronjob.Type, cronjob.Name, script, record.Records)
u.removeExpiredLog(*cronjob)
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
case "snapshot":
messageItem := ""
messageItem, record.File, err = u.handleSnapshot(cronjob, record.StartTime)
message = []byte(messageItem)
case "curl":
if len(cronjob.URL) == 0 {
return
}
record.Records = u.generateLogsPath(*cronjob, record.StartTime)
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL), record.Records)
u.removeExpiredLog(*cronjob)
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL))
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
case "ntp":
err = u.handleNtpSync()
u.removeExpiredLog(*cronjob)
case "cutWebsiteLog":
var messageItem []string
messageItem, record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
message = []byte(strings.Join(messageItem, "\n"))
case "clean":
messageItem := ""
messageItem, err = u.handleSystemClean()
message = []byte(messageItem)
u.removeExpiredLog(*cronjob)
case "website":
err = u.handleWebsite(*cronjob, record.StartTime)
case "app":
err = u.handleApp(*cronjob, record.StartTime)
case "database":
err = u.handleDatabase(*cronjob, record.StartTime)
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
case "website", "database", "app":
record.File, err = u.handleBackup(cronjob, record.StartTime)
case "directory":
if len(cronjob.SourceDir) == 0 {
return
}
err = u.handleDirectory(*cronjob, record.StartTime)
record.File, err = u.handleBackup(cronjob, record.StartTime)
case "cutWebsiteLog":
record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
if err != nil {
global.LOG.Errorf("cut website log file failed, err: %v", err)
}
case "clean":
messageItem := ""
messageItem, err = u.handleSystemClean()
message = []byte(messageItem)
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
case "log":
err = u.handleSystemLog(*cronjob, record.StartTime)
case "snapshot":
record.Records = u.generateLogsPath(*cronjob, record.StartTime)
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
err = u.handleSnapshot(*cronjob, record.StartTime, record.Records)
record.File, err = u.handleSystemLog(*cronjob, record.StartTime)
}
if err != nil {
if len(message) != 0 {
record.Records, _ = mkdirAndWriteFile(cronjob, record.StartTime, message)
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
return
}
if len(message) != 0 {
@@ -98,17 +87,18 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
}()
}
func (u *CronjobService) handleShell(cronType, cornName, script, logPath string) error {
func (u *CronjobService) handleShell(cronType, cornName, script string) ([]byte, error) {
handleDir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronType, cornName)
if _, err := os.Stat(handleDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(handleDir, os.ModePerm); err != nil {
return err
return nil, err
}
}
if err := cmd.ExecCronjobWithTimeOut(script, handleDir, logPath, 24*time.Hour); err != nil {
return err
stdout, err := cmd.ExecCronjobWithTimeOut(script, handleDir, 24*time.Hour)
if err != nil {
return []byte(stdout), err
}
return nil
return []byte(stdout), nil
}
func (u *CronjobService) handleNtpSync() error {
@@ -126,6 +116,102 @@ func (u *CronjobService) handleNtpSync() error {
return nil
}
func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
if err != nil {
return "", err
}
localDir, err := loadLocalDir()
if err != nil {
return "", err
}
global.LOG.Infof("start to backup %s %s to %s", cronjob.Type, cronjob.Name, backup.Type)
switch cronjob.Type {
case "database":
paths, err := u.handleDatabase(*cronjob, backup, startTime)
return strings.Join(paths, ","), err
case "app":
paths, err := u.handleApp(*cronjob, backup, startTime)
return strings.Join(paths, ","), err
case "website":
paths, err := u.handleWebsite(*cronjob, backup, startTime)
return strings.Join(paths, ","), err
default:
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405"))
backupDir := path.Join(localDir, fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name))
itemFileDir := fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name)
global.LOG.Infof("handle tar %s to %s", backupDir, fileName)
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
return "", err
}
var client cloud_storage.CloudStorageClient
if backup.Type != "LOCAL" {
if !cronjob.KeepLocal {
defer func() {
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, fileName))
}()
}
client, err = NewIBackupService().NewClient(&backup)
if err != nil {
return "", err
}
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
}
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
return "", err
}
}
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, cronjob, client)
if backup.Type == "LOCAL" || cronjob.KeepLocal {
return fmt.Sprintf("%s/%s", backupDir, fileName), nil
} else {
return fmt.Sprintf("%s/%s", itemFileDir, fileName), nil
}
}
}
func (u *CronjobService) HandleRmExpired(backType, backupPath, localDir string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc"))
if len(records) <= int(cronjob.RetainCopies) {
return
}
for i := int(cronjob.RetainCopies); i < len(records); i++ {
if len(records[i].File) != 0 {
files := strings.Split(records[i].File, ",")
for _, file := range files {
_ = os.Remove(file)
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
if backType == "LOCAL" {
continue
}
fileItem := file
if cronjob.KeepLocal {
if len(backupPath) != 0 {
itemPath := strings.TrimPrefix(backupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
fileItem = itemPath + strings.TrimPrefix(file, localDir+"/")
} else {
fileItem = strings.TrimPrefix(file, localDir+"/")
}
}
if cronjob.Type == "snapshot" {
_ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(path.Base(fileItem), ".tar.gz")))
}
_, _ = backClient.Delete(fileItem)
}
}
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
_ = os.Remove(records[i].Records)
}
}
func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
@@ -183,190 +269,439 @@ func handleUnTar(sourceFile, targetDir string) error {
return nil
}
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) ([]string, string, error) {
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
var paths []string
localDir, err := loadLocalDir()
if err != nil {
return paths, err
}
var dbs []model.DatabaseMysql
if cronjob.DBName == "all" {
dbs, err = mysqlRepo.List()
if err != nil {
return paths, err
}
} else {
itemID, _ := (strconv.Atoi(cronjob.DBName))
dbs, err = mysqlRepo.List(commonRepo.WithByID(uint(itemID)))
if err != nil {
return paths, err
}
}
var client cloud_storage.CloudStorageClient
if backup.Type != "LOCAL" {
client, err = NewIBackupService().NewClient(&backup)
if err != nil {
return paths, err
}
}
for _, dbInfo := range dbs {
var record model.BackupRecord
database, _ := databaseRepo.Get(commonRepo.WithByName(dbInfo.MysqlName))
record.Type = database.Type
record.Source = "LOCAL"
record.BackupType = backup.Type
record.Name = dbInfo.MysqlName
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", database.Type, record.Name, dbInfo.Name))
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405"))
if err = handleMysqlBackup(dbInfo.MysqlName, dbInfo.Name, backupDir, record.FileName); err != nil {
return paths, err
}
record.DetailName = dbInfo.Name
record.FileDir = backupDir
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
record.Source = backup.Type
record.FileDir = itemFileDir
}
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return paths, err
}
if backup.Type != "LOCAL" {
if !cronjob.KeepLocal {
defer func() {
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
}()
}
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
}
}
if backup.Type == "LOCAL" || cronjob.KeepLocal {
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
} else {
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
}
}
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
return paths, nil
}
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) (string, error) {
var (
websites []string
err error
filePaths []string
msgs []string
)
websites := loadWebsForJob(*cronjob)
if cronjob.Website == "all" {
websites, _ = NewIWebsiteService().GetWebsiteOptions()
if len(websites) == 0 {
return "", nil
}
} else {
websites = append(websites, cronjob.Website)
}
nginx, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return msgs, "", nil
return "", nil
}
baseDir := path.Join(nginx.GetPath(), "www", "sites")
fileOp := files.NewFileOp()
for _, website := range websites {
websiteLogDir := path.Join(baseDir, website.Alias, "log")
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.Alias)
if !fileOp.Stat(dstLogDir) {
_ = os.MkdirAll(dstLogDir, 0755)
}
var wg sync.WaitGroup
wg.Add(len(websites))
for _, websiteName := range websites {
name := websiteName
go func() {
website, _ := websiteRepo.GetFirst(websiteRepo.WithDomain(name))
if website.ID == 0 {
wg.Done()
return
}
websiteLogDir := path.Join(baseDir, website.Alias, "log")
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.Alias)
if !fileOp.Stat(dstLogDir) {
_ = os.MkdirAll(dstLogDir, 0755)
}
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
dstFilePath := path.Join(dstLogDir, dstName)
filePaths = append(filePaths, dstFilePath)
if err = backupLogFile(dstFilePath, websiteLogDir, fileOp); err != nil {
websiteErr := buserr.WithNameAndErr("ErrCutWebsiteLog", website.PrimaryDomain, err)
err = websiteErr
msgs = append(msgs, websiteErr.Error())
global.LOG.Error(websiteErr.Error())
continue
} else {
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
}
msg := i18n.GetMsgWithMap("CutWebsiteLogSuccess", map[string]interface{}{"name": website.PrimaryDomain, "path": dstFilePath})
global.LOG.Infof(msg)
msgs = append(msgs, msg)
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
filePaths = append(filePaths, path.Join(dstLogDir, dstName))
if err = fileOp.Compress([]string{srcAccessLogPath, srcErrorLogPath}, dstLogDir, dstName, files.Gz); err != nil {
global.LOG.Errorf("There was an error in compressing the website[%s] access.log, err: %v", website.PrimaryDomain, err)
} else {
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
}
global.LOG.Infof("The website[%s] log file was successfully rotated in the directory [%s]", website.PrimaryDomain, dstLogDir)
var record model.BackupRecord
record.Type = "cutWebsiteLog"
record.Name = cronjob.Website
record.Source = "LOCAL"
record.BackupType = "LOCAL"
record.FileDir = dstLogDir
record.FileName = dstName
if err = backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
}
wg.Done()
}()
}
u.removeExpiredLog(*cronjob)
return msgs, strings.Join(filePaths, ","), err
wg.Wait()
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
return strings.Join(filePaths, ","), nil
}
func backupLogFile(dstFilePath, websiteLogDir string, fileOp files.FileOp) error {
if err := cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, websiteLogDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil {
dstDir := path.Dir(dstFilePath)
if err = fileOp.Copy(path.Join(websiteLogDir, "access.log"), dstDir); err != nil {
return err
}
if err = fileOp.Copy(path.Join(websiteLogDir, "error.log"), dstDir); err != nil {
return err
}
if err = cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, dstDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil {
return err
}
_ = fileOp.DeleteFile(path.Join(dstDir, "access.log"))
_ = fileOp.DeleteFile(path.Join(dstDir, "error.log"))
return nil
func (u *CronjobService) handleApp(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
var paths []string
localDir, err := loadLocalDir()
if err != nil {
return paths, err
}
return nil
var applist []model.AppInstall
if cronjob.AppID == "all" {
applist, err = appInstallRepo.ListBy()
if err != nil {
return paths, err
}
} else {
itemID, _ := (strconv.Atoi(cronjob.AppID))
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
if err != nil {
return paths, err
}
applist = append(applist, app)
}
var client cloud_storage.CloudStorageClient
if backup.Type != "LOCAL" {
client, err = NewIBackupService().NewClient(&backup)
if err != nil {
return paths, err
}
}
for _, app := range applist {
var record model.BackupRecord
record.Type = "app"
record.Name = app.App.Key
record.DetailName = app.Name
record.Source = "LOCAL"
record.BackupType = backup.Type
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
record.FileDir = backupDir
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
record.Source = backup.Type
record.FileDir = strings.TrimPrefix(backupDir, localDir+"/")
}
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format("20060102150405"))
if err := handleAppBackup(&app, backupDir, record.FileName); err != nil {
return paths, err
}
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return paths, err
}
if backup.Type != "LOCAL" {
if !cronjob.KeepLocal {
defer func() {
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
}()
}
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
}
}
if backup.Type == "LOCAL" || cronjob.KeepLocal {
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
} else {
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
}
}
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
return paths, nil
}
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
var paths []string
localDir, err := loadLocalDir()
if err != nil {
return paths, err
}
var weblist []string
if cronjob.Website == "all" {
weblist, err = NewIWebsiteService().GetWebsiteOptions()
if err != nil {
return paths, err
}
} else {
weblist = append(weblist, cronjob.Website)
}
var client cloud_storage.CloudStorageClient
if backup.Type != "LOCAL" {
client, err = NewIBackupService().NewClient(&backup)
if err != nil {
return paths, err
}
}
for _, websiteItem := range weblist {
var record model.BackupRecord
record.Type = "website"
record.Name = cronjob.Website
record.Source = "LOCAL"
record.BackupType = backup.Type
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(websiteItem))
if err != nil {
return paths, err
}
backupDir := path.Join(localDir, fmt.Sprintf("website/%s", website.PrimaryDomain))
record.FileDir = backupDir
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
record.Source = backup.Type
record.FileDir = strings.TrimPrefix(backupDir, localDir+"/")
}
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
if err := handleWebsiteBackup(&website, backupDir, record.FileName); err != nil {
return paths, err
}
record.Name = website.PrimaryDomain
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return paths, err
}
if backup.Type != "LOCAL" {
if !cronjob.KeepLocal {
defer func() {
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
}()
}
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
}
}
if backup.Type == "LOCAL" || cronjob.KeepLocal {
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
} else {
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
}
}
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
return paths, nil
}
func (u *CronjobService) handleSnapshot(cronjob *model.Cronjob, startTime time.Time) (string, string, error) {
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
if err != nil {
return "", "", err
}
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
return "", "", err
}
req := dto.SnapshotCreate{
From: backup.Type,
}
message, name, err := NewISnapshotService().HandleSnapshot(true, req, startTime.Format("20060102150405"))
if err != nil {
return message, "", err
}
path := path.Join(strings.TrimPrefix(backup.BackupPath, "/"), "system_snapshot", name+".tar.gz")
u.HandleRmExpired(backup.Type, backup.BackupPath, "", cronjob, client)
return message, path, nil
}
func (u *CronjobService) handleSystemClean() (string, error) {
return NewIDeviceService().CleanForCronjob()
return NewISettingService().SystemCleanForCronjob()
}
func loadClientMap(backupAccounts string) (map[string]cronjobUploadHelper, error) {
clients := make(map[string]cronjobUploadHelper)
accounts, err := backupRepo.List()
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time) (string, error) {
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
if err != nil {
return nil, err
return "", err
}
targets := strings.Split(backupAccounts, ",")
for _, target := range targets {
if len(target) == 0 {
continue
pathItem := path.Join(global.CONF.System.BaseDir, "1panel/tmp/log", startTime.Format("20060102150405"))
websites, err := websiteRepo.List()
if err != nil {
return "", err
}
if len(websites) != 0 {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return "", err
}
for _, account := range accounts {
if target == account.Type {
client, err := NewIBackupService().NewClient(&account)
if err != nil {
return nil, err
webItem := path.Join(nginxInstall.GetPath(), "www/sites")
for _, website := range websites {
dirItem := path.Join(pathItem, "website", website.Alias)
if _, err := os.Stat(dirItem); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dirItem, os.ModePerm); err != nil {
return "", err
}
pathItem := account.BackupPath
if account.BackupPath != "/" {
pathItem = strings.TrimPrefix(account.BackupPath, "/")
}
itemDir := path.Join(webItem, website.Alias, "log")
logFiles, _ := os.ReadDir(itemDir)
if len(logFiles) != 0 {
for i := 0; i < len(logFiles); i++ {
if !logFiles[i].IsDir() {
_ = cpBinary([]string{path.Join(itemDir, logFiles[i].Name())}, dirItem)
}
}
clients[target] = cronjobUploadHelper{
client: client,
backupPath: pathItem,
backType: account.Type,
}
itemDir2 := path.Join(global.CONF.System.Backup, "log/website", website.Alias)
logFiles2, _ := os.ReadDir(itemDir2)
if len(logFiles2) != 0 {
for i := 0; i < len(logFiles2); i++ {
if !logFiles2[i].IsDir() {
_ = cpBinary([]string{path.Join(itemDir2, logFiles2[i].Name())}, dirItem)
}
}
}
}
global.LOG.Debug("backup website log successful!")
}
return clients, nil
}
type cronjobUploadHelper struct {
backupPath string
backType string
client cloud_storage.CloudStorageClient
}
systemLogDir := path.Join(global.CONF.System.BaseDir, "1panel/log")
systemDir := path.Join(pathItem, "system")
if _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(systemDir, os.ModePerm); err != nil {
return "", err
}
}
systemLogFiles, _ := os.ReadDir(systemLogDir)
if len(systemLogFiles) != 0 {
for i := 0; i < len(systemLogFiles); i++ {
if !systemLogFiles[i].IsDir() {
_ = cpBinary([]string{path.Join(systemLogDir, systemLogFiles[i].Name())}, systemDir)
}
}
}
global.LOG.Debug("backup system log successful!")
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper, file string) (string, error) {
loginLogFiles, _ := os.ReadDir("/var/log")
loginDir := path.Join(pathItem, "login")
if _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(loginDir, os.ModePerm); err != nil {
return "", err
}
}
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)
}
}
}
global.LOG.Debug("backup ssh log successful!")
fileName := fmt.Sprintf("system_log_%s.tar.gz", startTime.Format("20060102150405"))
targetDir := path.Dir(pathItem)
if err := handleTar(pathItem, targetDir, fileName, ""); err != nil {
return "", err
}
defer func() {
_ = os.Remove(file)
os.RemoveAll(pathItem)
os.RemoveAll(path.Join(targetDir, fileName))
}()
accounts := strings.Split(cronjob.BackupAccounts, ",")
cloudSrc := strings.TrimPrefix(file, global.CONF.System.TmpDir+"/")
for _, account := range accounts {
if len(account) != 0 {
global.LOG.Debugf("start upload file to %s, dir: %s", account, path.Join(accountMap[account].backupPath, cloudSrc))
if _, err := accountMap[account].client.Upload(file, path.Join(accountMap[account].backupPath, cloudSrc)); err != nil {
return "", err
}
global.LOG.Debugf("upload successful!")
}
}
return cloudSrc, nil
}
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper, record model.BackupRecord) {
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
var opts []repo.DBOption
opts = append(opts, commonRepo.WithByFrom("cronjob"))
opts = append(opts, backupRepo.WithByCronID(cronjob.ID))
opts = append(opts, commonRepo.WithOrderBy("created_at desc"))
if record.ID != 0 {
opts = append(opts, backupRepo.WithByType(record.Type))
opts = append(opts, commonRepo.WithByName(record.Name))
opts = append(opts, backupRepo.WithByDetailName(record.DetailName))
}
records, _ := backupRepo.ListRecord(opts...)
if len(records) <= int(cronjob.RetainCopies) {
return
}
for i := int(cronjob.RetainCopies); i < len(records); i++ {
accounts := strings.Split(cronjob.BackupAccounts, ",")
if cronjob.Type == "snapshot" {
for _, account := range accounts {
if len(account) != 0 {
_, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName))
}
}
_ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(records[i].FileName, ".tar.gz")))
} else {
for _, account := range accounts {
if len(account) != 0 {
_, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName))
}
}
}
_ = backupRepo.DeleteRecord(context.Background(), commonRepo.WithByID(records[i].ID))
}
}
func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc"))
if len(records) <= int(cronjob.RetainCopies) {
return
}
for i := int(cronjob.RetainCopies); i < len(records); i++ {
if len(records[i].File) != 0 {
files := strings.Split(records[i].File, ",")
for _, file := range files {
_ = os.Remove(file)
}
}
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
_ = os.Remove(records[i].Records)
}
}
func (u *CronjobService) generateLogsPath(cronjob model.Cronjob, startTime time.Time) string {
dir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronjob.Type, cronjob.Name)
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
_ = os.MkdirAll(dir, os.ModePerm)
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
return "", err
}
path := fmt.Sprintf("%s/%s.log", dir, startTime.Format("20060102150405"))
return path
targetPath := "log/" + fileName
if len(backup.BackupPath) != 0 {
targetPath = strings.TrimPrefix(backup.BackupPath, "/") + "/log/" + fileName
}
if _, err = client.Upload(path.Join(targetDir, fileName), targetPath); err != nil {
return "", err
}
u.HandleRmExpired(backup.Type, backup.BackupPath, "", &cronjob, client)
return targetPath, nil
}
func hasBackup(cronjobType string) bool {
+19 -80
View File
@@ -3,9 +3,7 @@ package service
import (
"encoding/json"
"fmt"
"sort"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -22,7 +20,6 @@ import (
type DashboardService struct{}
type IDashboardService interface {
LoadOsInfo() (*dto.OsInfo, error)
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
@@ -50,27 +47,6 @@ func (u *DashboardService) Restart(operation string) error {
return nil
}
func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
var baseInfo dto.OsInfo
hostInfo, err := host.Info()
if err != nil {
return nil, err
}
baseInfo.OS = hostInfo.OS
baseInfo.Platform = hostInfo.Platform
baseInfo.PlatformFamily = hostInfo.PlatformFamily
baseInfo.KernelArch = hostInfo.KernelArch
baseInfo.KernelVersion = hostInfo.KernelVersion
if baseInfo.KernelArch == "armv7l" {
baseInfo.KernelArch = "armv7"
}
if baseInfo.KernelArch == "x86_64" {
baseInfo.KernelArch = "amd64"
}
return &baseInfo, nil
}
func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error) {
var baseInfo dto.DashboardBase
hostInfo, err := host.Info()
@@ -147,12 +123,6 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory()
currentInfo.SwapMemoryTotal = swapInfo.Total
currentInfo.SwapMemoryAvailable = swapInfo.Free
currentInfo.SwapMemoryUsed = swapInfo.Used
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
currentInfo.DiskData = loadDiskInfo()
if ioOption == "all" {
@@ -203,12 +173,9 @@ type diskInfo struct {
func loadDiskInfo() []dto.DiskInfo {
var datas []dto.DiskInfo
stdout, err := cmd.ExecWithTimeOut("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev", 2*time.Second)
stdout, err := cmd.Exec("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")
if err != nil {
stdout, err = cmd.ExecWithTimeOut("df -lhT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev", 1*time.Second)
if err != nil {
return datas
}
return datas
}
lines := strings.Split(stdout, "\n")
@@ -240,52 +207,24 @@ func loadDiskInfo() []dto.DiskInfo {
mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: fields[6]})
}
var (
wg sync.WaitGroup
mu sync.Mutex
)
wg.Add(len(mounts))
for i := 0; i < len(mounts); i++ {
go func(timeoutCh <-chan time.Time, mount diskInfo) {
defer wg.Done()
var itemData dto.DiskInfo
itemData.Path = mount.Mount
itemData.Type = mount.Type
itemData.Device = mount.Device
select {
case <-timeoutCh:
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: timeout", mount.Mount)
default:
state, err := disk.Usage(mount.Mount)
if err != nil {
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, err)
return
}
itemData.Total = state.Total
itemData.Free = state.Free
itemData.Used = state.Used
itemData.UsedPercent = state.UsedPercent
itemData.InodesTotal = state.InodesTotal
itemData.InodesUsed = state.InodesUsed
itemData.InodesFree = state.InodesFree
itemData.InodesUsedPercent = state.InodesUsedPercent
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
}
}(time.After(5*time.Second), mounts[i])
state, err := disk.Usage(mounts[i].Mount)
if err != nil {
continue
}
var itemData dto.DiskInfo
itemData.Path = mounts[i].Mount
itemData.Type = mounts[i].Type
itemData.Device = mounts[i].Device
itemData.Total = state.Total
itemData.Free = state.Free
itemData.Used = state.Used
itemData.UsedPercent = state.UsedPercent
itemData.InodesTotal = state.InodesTotal
itemData.InodesUsed = state.InodesUsed
itemData.InodesFree = state.InodesFree
itemData.InodesUsedPercent = state.InodesUsedPercent
datas = append(datas, itemData)
}
wg.Wait()
sort.Slice(datas, func(i, j int) bool {
return datas[i].Path < datas[j].Path
})
return datas
}
+30 -138
View File
@@ -6,9 +6,6 @@ import (
"os"
"path"
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
client2 "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -31,7 +28,6 @@ type IDatabaseService interface {
DeleteCheck(id uint) ([]string, error)
Delete(req dto.DatabaseDelete) error
List(dbType string) ([]dto.DatabaseOption, error)
LoadItems(dbType string) ([]dto.DatabaseItem, error)
}
func NewIDatabaseService() IDatabaseService {
@@ -81,66 +77,18 @@ func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {
return datas, err
}
func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
var datas []dto.DatabaseItem
for _, db := range dbs {
if dbType == "postgresql" {
items, _ := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(db.Name))
for _, item := range items {
var dItem dto.DatabaseItem
if err := copier.Copy(&dItem, &item); err != nil {
continue
}
dItem.Database = db.Name
datas = append(datas, dItem)
}
} else {
items, _ := mysqlRepo.List(mysqlRepo.WithByMysqlName(db.Name))
for _, item := range items {
var dItem dto.DatabaseItem
if err := copier.Copy(&dItem, &item); err != nil {
continue
}
dItem.Database = db.Name
datas = append(datas, dItem)
}
}
}
return datas, err
}
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
switch req.Type {
case constant.AppPostgresql:
_, err := postgresql.NewPostgresqlClient(client2.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
})
return err == nil
case "mysql", "mariadb":
_, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
SSL: req.SSL,
RootCert: req.RootCert,
ClientKey: req.ClientKey,
ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify,
Timeout: 6,
})
return err == nil
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
}); err != nil {
return false
}
return false
return true
}
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
@@ -151,39 +99,16 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
}
return constant.ErrRecordExist
}
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
}); err != nil {
return err
}
case "mysql", "mariadb":
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
SSL: req.SSL,
RootCert: req.RootCert,
ClientKey: req.ClientKey,
ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify,
Timeout: 6,
}); err != nil {
return err
}
default:
return errors.New("database type not supported")
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
}); err != nil {
return err
}
if err := copier.Copy(&db, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
@@ -233,51 +158,23 @@ func (u *DatabaseService) Delete(req dto.DatabaseDelete) error {
return err
}
if db.From != "local" {
if db.Type == "mysql" || db.Type == "mariadb" {
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {
return err
}
} else {
if err := postgresqlRepo.Delete(context.Background(), postgresqlRepo.WithByPostgresqlName(db.Name)); err != nil && !req.ForceDelete {
return err
}
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {
return err
}
}
return nil
}
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 300,
}); err != nil {
return err
}
case "mysql", "mariadb":
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
SSL: req.SSL,
RootCert: req.RootCert,
ClientKey: req.ClientKey,
ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify,
Timeout: 300,
}); err != nil {
return err
}
default:
return errors.New("database type not supported")
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 300,
}); err != nil {
return err
}
pass, err := encrypt.StringEncrypt(req.Password)
@@ -293,10 +190,5 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
upMap["username"] = req.Username
upMap["password"] = pass
upMap["description"] = req.Description
upMap["ssl"] = req.SSL
upMap["client_key"] = req.ClientKey
upMap["client_cert"] = req.ClientCert
upMap["root_cert"] = req.RootCert
upMap["skip_verify"] = req.SkipVerify
return databaseRepo.Update(req.ID, upMap)
}
-94
View File
@@ -1,94 +0,0 @@
package service
import (
"bufio"
"fmt"
"os"
"path"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
)
type DBCommonService struct{}
type IDBCommonService interface {
LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
UpdateConfByFile(req dto.DBConfUpdateByFile) error
}
func NewIDBCommonService() IDBCommonService {
return &DBCommonService{}
}
func (u *DBCommonService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {
var data dto.DBBaseInfo
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return nil, err
}
data.ContainerName = app.ContainerName
data.Name = app.Name
data.Port = int64(app.Port)
return &data, nil
}
func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
filePath := ""
switch req.Type {
case "mysql-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
case "mariadb-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
case "postgresql-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
case "redis-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
case "mysql-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
case "mariadb-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
}
if _, err := os.Stat(filePath); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(content), nil
}
func (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return err
}
path := ""
switch req.Type {
case constant.AppMariaDB, constant.AppMysql:
path = fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
case constant.AppPostgresql:
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", constant.AppInstallDir, req.Type, app.Name)
case constant.AppRedis:
path = fmt.Sprintf("%s/%s/%s/conf/redis.conf", constant.AppInstallDir, req.Type, app.Name)
}
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(req.File)
write.Flush()
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
return err
}
return nil
}
+64 -44
View File
@@ -1,6 +1,7 @@
package service
import (
"bufio"
"context"
"encoding/json"
"fmt"
@@ -34,18 +35,21 @@ type IMysqlService interface {
SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error)
ListDBOption() ([]dto.MysqlOption, error)
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
BindUser(req dto.BindUser) error
LoadFromRemote(req dto.MysqlLoadDB) error
ChangeAccess(info dto.ChangeDBInfo) error
ChangePassword(info dto.ChangeDBInfo) error
UpdateVariables(req dto.MysqlVariablesUpdate) error
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
UpdateDescription(req dto.UpdateDescription) error
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
Delete(ctx context.Context, req dto.MysqlDBDelete) error
LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)
LoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error)
LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)
LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error)
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
}
func NewIMysqlService() IMysqlService {
@@ -140,42 +144,6 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
return &createItem, nil
}
func (u *MysqlService) BindUser(req dto.BindUser) error {
dbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(req.Database), commonRepo.WithByName(req.DB))
if err != nil {
return err
}
cli, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
if err := cli.CreateUser(client.CreateInfo{
Name: dbItem.Name,
Format: dbItem.Format,
Username: req.Username,
Password: req.Password,
Permission: req.Permission,
Version: version,
Timeout: 300,
}, false); err != nil {
return err
}
pass, err := encrypt.StringEncrypt(req.Password)
if err != nil {
return fmt.Errorf("decrypt database db password failed, err: %v", err)
}
if err := mysqlRepo.Update(dbItem.ID, map[string]interface{}{
"username": req.Username,
"password": pass,
"permission": req.Permission,
}); err != nil {
return err
}
return nil
}
func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
client, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
@@ -342,7 +310,7 @@ func (u *MysqlService) ChangePassword(req dto.ChangeDBInfo) error {
}
global.LOG.Infof("start to update mysql password used by app %s-%s", appModel.Key, appInstall.Name)
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", req.Value); err != nil {
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, req.Value); err != nil {
return err
}
}
@@ -355,7 +323,7 @@ func (u *MysqlService) ChangePassword(req dto.ChangeDBInfo) error {
return nil
}
if err := updateInstallInfoInDB(req.Type, req.Database, "password", req.Value); err != nil {
if err := updateInstallInfoInDB(req.Type, req.Database, "password", false, req.Value); err != nil {
return err
}
return nil
@@ -401,6 +369,26 @@ func (u *MysqlService) ChangeAccess(req dto.ChangeDBInfo) error {
return nil
}
func (u *MysqlService) UpdateConfByFile(req dto.MysqlConfUpdateByFile) error {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return err
}
path := fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(req.File)
write.Flush()
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
return err
}
return nil
}
func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
@@ -446,6 +434,19 @@ func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
return nil
}
func (u *MysqlService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {
var data dto.DBBaseInfo
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return nil, err
}
data.ContainerName = app.ContainerName
data.Name = app.Name
data.Port = int64(app.Port)
return &data, nil
}
func (u *MysqlService) LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error) {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
@@ -527,6 +528,30 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
return &info, nil
}
func (u *MysqlService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
filePath := ""
switch req.Type {
case "mysql-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
case "mariadb-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
case "redis-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
case "mysql-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
case "mariadb-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
}
if _, err := os.Stat(filePath); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(content), nil
}
func executeSqlForMaps(containerName, password, command string) (map[string]string, error) {
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
stdout, err := cmd.CombinedOutput()
@@ -616,11 +641,6 @@ func LoadMysqlClientByFrom(database string) (mysql.MysqlClient, string, error) {
dbInfo.Port = databaseItem.Port
dbInfo.Username = databaseItem.Username
dbInfo.Password = databaseItem.Password
dbInfo.SSL = databaseItem.SSL
dbInfo.ClientKey = databaseItem.ClientKey
dbInfo.ClientCert = databaseItem.ClientCert
dbInfo.RootCert = databaseItem.RootCert
dbInfo.SkipVerify = databaseItem.SkipVerify
version = databaseItem.Version
} else {
-413
View File
@@ -1,413 +0,0 @@
package service
import (
"context"
"fmt"
"os"
"path"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
"github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
type PostgresqlService struct{}
type IPostgresqlService interface {
SearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error)
ListDBOption() ([]dto.PostgresqlOption, error)
BindUser(req dto.PostgresqlBindUser) error
Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error)
LoadFromRemote(database string) error
ChangePrivileges(req dto.PostgresqlPrivileges) error
ChangePassword(info dto.ChangeDBInfo) error
UpdateDescription(req dto.UpdateDescription) error
DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error)
Delete(ctx context.Context, req dto.PostgresqlDBDelete) error
}
func NewIPostgresqlService() IPostgresqlService {
return &PostgresqlService{}
}
func (u *PostgresqlService) SearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error) {
total, postgresqls, err := postgresqlRepo.Page(search.Page, search.PageSize,
postgresqlRepo.WithByPostgresqlName(search.Database),
commonRepo.WithLikeName(search.Info),
commonRepo.WithOrderRuleBy(search.OrderBy, search.Order),
)
var dtoPostgresqls []dto.PostgresqlDBInfo
for _, pg := range postgresqls {
var item dto.PostgresqlDBInfo
if err := copier.Copy(&item, &pg); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtoPostgresqls = append(dtoPostgresqls, item)
}
return total, dtoPostgresqls, err
}
func (u *PostgresqlService) ListDBOption() ([]dto.PostgresqlOption, error) {
postgresqls, err := postgresqlRepo.List()
if err != nil {
return nil, err
}
databases, err := databaseRepo.GetList(databaseRepo.WithTypeList("postgresql,mariadb"))
if err != nil {
return nil, err
}
var dbs []dto.PostgresqlOption
for _, pg := range postgresqls {
var item dto.PostgresqlOption
if err := copier.Copy(&item, &pg); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
item.Database = pg.PostgresqlName
for _, database := range databases {
if database.Name == item.Database {
item.Type = database.Type
}
}
dbs = append(dbs, item)
}
return dbs, err
}
func (u *PostgresqlService) BindUser(req dto.PostgresqlBindUser) error {
if cmd.CheckIllegal(req.Name, req.Username, req.Password) {
return buserr.New(constant.ErrCmdIllegal)
}
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
if err != nil {
return err
}
pgClient, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return err
}
if err := pgClient.CreateUser(client.CreateInfo{
Name: req.Name,
Username: req.Username,
Password: req.Password,
SuperUser: req.SuperUser,
Timeout: 300,
}, false); err != nil {
return err
}
pass, err := encrypt.StringEncrypt(req.Password)
if err != nil {
return fmt.Errorf("decrypt database db password failed, err: %v", err)
}
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
"username": req.Username,
"password": pass,
"super_user": req.SuperUser,
}); err != nil {
return err
}
return nil
}
func (u *PostgresqlService) Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error) {
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format) {
return nil, buserr.New(constant.ErrCmdIllegal)
}
pgsql, _ := postgresqlRepo.Get(commonRepo.WithByName(req.Name), postgresqlRepo.WithByPostgresqlName(req.Database), databaseRepo.WithByFrom(req.From))
if pgsql.ID != 0 {
return nil, constant.ErrRecordExist
}
var createItem model.DatabasePostgresql
if err := copier.Copy(&createItem, &req); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if req.From == "local" && req.Username == "root" {
return nil, errors.New("Cannot set root as user name")
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return nil, err
}
defer cli.Close()
createItem.PostgresqlName = req.Database
defer cli.Close()
if err := cli.Create(client.CreateInfo{
Name: req.Name,
Username: req.Username,
Password: req.Password,
SuperUser: req.SuperUser,
Timeout: 300,
}); err != nil {
return nil, err
}
global.LOG.Infof("create database %s successful!", req.Name)
if err := postgresqlRepo.Create(ctx, &createItem); err != nil {
return nil, err
}
return &createItem, nil
}
func LoadPostgresqlClientByFrom(database string) (postgresql.PostgresqlClient, error) {
var (
dbInfo client.DBInfo
err error
)
dbInfo.Timeout = 300
databaseItem, err := databaseRepo.Get(commonRepo.WithByName(database))
if err != nil {
return nil, err
}
dbInfo.From = databaseItem.From
dbInfo.Database = database
if dbInfo.From != "local" {
dbInfo.Address = databaseItem.Address
dbInfo.Port = databaseItem.Port
dbInfo.Username = databaseItem.Username
dbInfo.Password = databaseItem.Password
} else {
app, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database)
if err != nil {
return nil, err
}
dbInfo.From = "local"
dbInfo.Address = app.ContainerName
dbInfo.Username = app.UserName
dbInfo.Password = app.Password
dbInfo.Port = uint(app.Port)
}
cli, err := postgresql.NewPostgresqlClient(dbInfo)
if err != nil {
return nil, err
}
return cli, nil
}
func (u *PostgresqlService) LoadFromRemote(database string) error {
client, err := LoadPostgresqlClientByFrom(database)
if err != nil {
return err
}
defer client.Close()
databases, err := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(database))
if err != nil {
return err
}
datas, err := client.SyncDB()
if err != nil {
return err
}
for _, data := range datas {
hasOld := false
for _, oldData := range databases {
if strings.EqualFold(oldData.Name, data.Name) && strings.EqualFold(oldData.PostgresqlName, data.PostgresqlName) {
hasOld = true
break
}
}
if !hasOld {
var createItem model.DatabasePostgresql
if err := copier.Copy(&createItem, &data); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if err := postgresqlRepo.Create(context.Background(), &createItem); err != nil {
return err
}
}
}
return nil
}
func (u *PostgresqlService) UpdateDescription(req dto.UpdateDescription) error {
return postgresqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
}
func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error) {
var appInUsed []string
db, err := postgresqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil {
return appInUsed, err
}
if db.From == "local" {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return appInUsed, err
}
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
appInUsed = append(appInUsed, appInstall.Name)
}
}
} else {
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
appInUsed = append(appInUsed, appInstall.Name)
}
}
}
return appInUsed, nil
}
func (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDelete) error {
db, err := postgresqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil && !req.ForceDelete {
return err
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
if err := cli.Delete(client.DeleteInfo{
Name: db.Name,
Username: db.Username,
ForceDelete: req.ForceDelete,
Timeout: 300,
}); err != nil && !req.ForceDelete {
return err
}
if req.DeleteBackup {
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
}
localDir, err := loadLocalDir()
if err != nil && !req.ForceDelete {
return err
}
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, db.PostgresqlName, db.Name))
if _, err := os.Stat(backupDir); err == nil {
_ = os.RemoveAll(backupDir)
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(req.Type), commonRepo.WithByName(req.Database), backupRepo.WithByDetailName(db.Name))
global.LOG.Infof("delete database %s-%s backups successful", req.Database, db.Name)
}
_ = postgresqlRepo.Delete(ctx, commonRepo.WithByID(db.ID))
return nil
}
func (u *PostgresqlService) ChangePrivileges(req dto.PostgresqlPrivileges) error {
if cmd.CheckIllegal(req.Database, req.Username) {
return buserr.New(constant.ErrCmdIllegal)
}
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
if err != nil {
return err
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
if err := cli.ChangePrivileges(client.Privileges{Username: req.Username, SuperUser: req.SuperUser, Timeout: 300}); err != nil {
return err
}
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
"super_user": req.SuperUser,
}); err != nil {
return err
}
return nil
}
func (u *PostgresqlService) ChangePassword(req dto.ChangeDBInfo) error {
if cmd.CheckIllegal(req.Value) {
return buserr.New(constant.ErrCmdIllegal)
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
var (
postgresqlData model.DatabasePostgresql
passwordInfo client.PasswordChangeInfo
)
passwordInfo.Password = req.Value
passwordInfo.Timeout = 300
if req.ID != 0 {
postgresqlData, err = postgresqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
passwordInfo.Username = postgresqlData.Username
} else {
dbItem, err := databaseRepo.Get(commonRepo.WithByType(req.Type), commonRepo.WithByFrom(req.From))
if err != nil {
return err
}
passwordInfo.Username = dbItem.Username
}
if err := cli.ChangePassword(passwordInfo); err != nil {
return err
}
if req.ID != 0 {
var appRess []model.AppInstallResource
if req.From == "local" {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return err
}
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(postgresqlData.ID))
} else {
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(postgresqlData.ID))
}
for _, appRes := range appRess {
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
if err != nil {
return err
}
appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
if err != nil {
return err
}
global.LOG.Infof("start to update postgresql password used by app %s-%s", appModel.Key, appInstall.Name)
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", req.Value); err != nil {
return err
}
}
global.LOG.Info("execute password change sql successful")
pass, err := encrypt.StringEncrypt(req.Value)
if err != nil {
return fmt.Errorf("decrypt database db password failed, err: %v", err)
}
_ = postgresqlRepo.Update(postgresqlData.ID, map[string]interface{}{"password": pass})
return nil
}
if err := updateInstallInfoInDB(req.Type, req.Database, "password", req.Value); err != nil {
return err
}
return nil
}
+6 -25
View File
@@ -44,7 +44,7 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
confs = append(confs, redisConfig{key: "timeout", value: req.Timeout})
confs = append(confs, redisConfig{key: "maxclients", value: req.Maxclients})
confs = append(confs, redisConfig{key: "maxmemory", value: req.Maxmemory})
if err := confSet(redisInfo.Name, "", confs); err != nil {
if err := confSet(redisInfo.Name, confs); err != nil {
return err
}
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)); err != nil {
@@ -55,10 +55,10 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
}
func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
if err := updateInstallInfoInDB("redis", "", "password", req.Value); err != nil {
if err := updateInstallInfoInDB("redis", "", "password", true, req.Value); err != nil {
return err
}
if err := updateInstallInfoInDB("redis-commander", "", "password", req.Value); err != nil {
if err := updateInstallInfoInDB("redis-commander", "", "password", true, req.Value); err != nil {
return err
}
@@ -78,7 +78,7 @@ func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate)
confs = append(confs, redisConfig{key: "appendonly", value: req.Appendonly})
confs = append(confs, redisConfig{key: "appendfsync", value: req.Appendfsync})
}
if err := confSet(redisInfo.Name, req.Type, confs); err != nil {
if err := confSet(redisInfo.Name, confs); err != nil {
return err
}
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)); err != nil {
@@ -213,7 +213,7 @@ type redisConfig struct {
value string
}
func confSet(redisName string, updateType string, changeConf []redisConfig) error {
func confSet(redisName string, changeConf []redisConfig) error {
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisName)
lineBytes, err := os.ReadFile(path)
if err != nil {
@@ -221,38 +221,19 @@ func confSet(redisName string, updateType string, changeConf []redisConfig) erro
}
files := strings.Split(string(lineBytes), "\n")
startIndex, endIndex, emptyLine := 0, 0, 0
startIndex, endIndex := 0, 0
var newFiles []string
for i := 0; i < len(files); i++ {
if files[i] == "# Redis configuration rewrite by 1Panel" {
startIndex = i
newFiles = append(newFiles, files[i])
continue
}
if files[i] == "# End Redis configuration rewrite by 1Panel" {
endIndex = i
break
}
if startIndex == 0 && strings.HasPrefix(files[i], "save ") {
newFiles = append(newFiles, "# "+files[i])
continue
}
if startIndex != 0 && endIndex == 0 && (len(files[i]) == 0 || (updateType == "rbd" && strings.HasPrefix(files[i], "save "))) {
emptyLine++
continue
}
newFiles = append(newFiles, files[i])
}
endIndex = endIndex - emptyLine
for _, item := range changeConf {
if item.key == "save" {
saveVal := strings.Split(item.value, ",")
for i := 0; i < len(saveVal); i++ {
newFiles = append(newFiles, "save "+saveVal[i])
}
continue
}
isExist := false
for i := startIndex; i < endIndex; i++ {
if strings.HasPrefix(newFiles[i], item.key) || strings.HasPrefix(newFiles[i], "# "+item.key) {
-431
View File
@@ -1,431 +0,0 @@
package service
import (
"bufio"
"errors"
"fmt"
"net"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/shirou/gopsutil/v3/mem"
)
const defaultDNSPath = "/etc/resolv.conf"
const defaultHostPath = "/etc/hosts"
const defaultFstab = "/etc/fstab"
type DeviceService struct{}
type IDeviceService interface {
LoadBaseInfo() (dto.DeviceBaseInfo, error)
Update(key, value string) error
UpdateHosts(req []dto.HostHelper) error
UpdatePasswd(req dto.ChangePasswd) error
UpdateSwap(req dto.SwapHelper) error
UpdateByConf(req dto.UpdateByNameAndFile) error
LoadTimeZone() ([]string, error)
CheckDNS(key, value string) (bool, error)
LoadConf(name string) (string, error)
Scan() dto.CleanData
Clean(req []dto.Clean)
CleanForCronjob() (string, error)
}
func NewIDeviceService() IDeviceService {
return &DeviceService{}
}
func (u *DeviceService) LoadBaseInfo() (dto.DeviceBaseInfo, error) {
var baseInfo dto.DeviceBaseInfo
baseInfo.LocalTime = time.Now().Format("2006-01-02 15:04:05 MST -0700")
baseInfo.TimeZone = common.LoadTimeZoneByCmd()
baseInfo.DNS = loadDNS()
baseInfo.Hosts = loadHosts()
baseInfo.Hostname = loadHostname()
baseInfo.User = loadUser()
ntp, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
if err == nil {
baseInfo.Ntp = ntp.Value
}
swapInfo, err := mem.SwapMemory()
if err != nil {
return baseInfo, err
}
baseInfo.SwapMemoryTotal = swapInfo.Total
baseInfo.SwapMemoryAvailable = swapInfo.Free
baseInfo.SwapMemoryUsed = swapInfo.Used
if baseInfo.SwapMemoryTotal != 0 {
baseInfo.SwapDetails = loadSwap()
}
disks := loadDiskInfo()
for _, item := range disks {
baseInfo.MaxSize += item.Free
}
return baseInfo, nil
}
func (u *DeviceService) LoadTimeZone() ([]string, error) {
std, err := cmd.Exec("timedatectl list-timezones")
if err != nil {
return []string{}, err
}
return strings.Split(std, "\n"), nil
}
func (u *DeviceService) CheckDNS(key, value string) (bool, error) {
content, err := os.ReadFile(defaultDNSPath)
if err != nil {
return false, err
}
defer func() { _ = u.UpdateByConf(dto.UpdateByNameAndFile{Name: "DNS", File: string(content)}) }()
if key == "form" {
if err := u.Update("DNS", value); err != nil {
return false, err
}
} else {
if err := u.UpdateByConf(dto.UpdateByNameAndFile{Name: "DNS", File: value}); err != nil {
return false, err
}
}
conn, err := net.DialTimeout("ip4:icmp", "www.baidu.com", time.Second*2)
if err != nil {
return false, err
}
defer conn.Close()
return true, nil
}
func (u *DeviceService) Update(key, value string) error {
switch key {
case "TimeZone":
if cmd.CheckIllegal(value) {
return buserr.New(constant.ErrCmdIllegal)
}
if err := ntp.UpdateSystemTimeZone(value); err != nil {
return err
}
go func() {
_, err := cmd.Exec("systemctl restart 1panel.service")
if err != nil {
global.LOG.Errorf("restart system for new time zone failed, err: %v", err)
}
}()
case "DNS":
if err := updateDNS(strings.Split(value, ",")); err != nil {
return err
}
case "Hostname":
if cmd.CheckIllegal(value) {
return buserr.New(constant.ErrCmdIllegal)
}
std, err := cmd.Execf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value)
if err != nil {
return errors.New(std)
}
case "Ntp", "LocalTime":
if cmd.CheckIllegal(value) {
return buserr.New(constant.ErrCmdIllegal)
}
ntpValue := value
if key == "LocalTime" {
ntpItem, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
if err != nil {
return err
}
ntpValue = ntpItem.Value
} else {
if err := settingRepo.Update("NtpSite", ntpValue); err != nil {
return err
}
}
ntime, err := ntp.GetRemoteTime(ntpValue)
if err != nil {
return err
}
ts := ntime.Format("2006-01-02 15:04:05")
if err := ntp.UpdateSystemTime(ts); err != nil {
return err
}
default:
return fmt.Errorf("not support such key %s", key)
}
return nil
}
func (u *DeviceService) UpdateHosts(req []dto.HostHelper) error {
conf, err := os.ReadFile(defaultHostPath)
if err != nil {
return fmt.Errorf("read namesever conf of %s failed, err: %v", defaultHostPath, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 2 {
newFile += line + "\n"
continue
}
for index, item := range req {
if item.IP == parts[0] && item.Host == strings.Join(parts[1:], " ") {
newFile += line + "\n"
req = append(req[:index], req[index+1:]...)
break
}
}
}
for _, item := range req {
newFile += fmt.Sprintf("%s %s \n", item.IP, item.Host)
}
file, err := os.OpenFile(defaultHostPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}
func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
if cmd.CheckIllegal(req.User, req.Passwd) {
return buserr.New(constant.ErrCmdIllegal)
}
std, err := cmd.Execf("%s echo '%s:%s' | %s chpasswd", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd())
if err != nil {
if strings.Contains(err.Error(), "does not exist") {
return buserr.New(constant.ErrNotExistUser)
}
return errors.New(std)
}
return nil
}
func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
if cmd.CheckIllegal(req.Path) {
return buserr.New(constant.ErrCmdIllegal)
}
if !req.IsNew {
std, err := cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle swapoff %s failed, err: %s", req.Path, std)
}
}
if req.Size == 0 {
if req.Path == path.Join(global.CONF.System.BaseDir, ".1panel_swap") {
_ = os.Remove(path.Join(global.CONF.System.BaseDir, ".1panel_swap"))
}
return operateSwapWithFile(true, req)
}
std1, err := cmd.Execf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std1)
}
std2, err := cmd.Execf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std2)
}
std3, err := cmd.Execf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
_, _ = cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std3)
}
return operateSwapWithFile(false, req)
}
func (u *DeviceService) LoadConf(name string) (string, error) {
pathItem := ""
switch name {
case "DNS":
pathItem = defaultDNSPath
case "Hosts":
pathItem = defaultHostPath
default:
return "", fmt.Errorf("not support such name %s", name)
}
if _, err := os.Stat(pathItem); err != nil {
return "", err
}
content, err := os.ReadFile(pathItem)
if err != nil {
return "", err
}
return string(content), nil
}
func (u *DeviceService) UpdateByConf(req dto.UpdateByNameAndFile) error {
if req.Name != "DNS" && req.Name != "Hosts" {
return fmt.Errorf("not support such name %s", req.Name)
}
path := defaultDNSPath
if req.Name == "Hosts" {
path = defaultHostPath
}
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(req.File)
write.Flush()
return nil
}
func loadDNS() []string {
var list []string
dnsConf, err := os.ReadFile(defaultDNSPath)
if err == nil {
lines := strings.Split(string(dnsConf), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "nameserver ") {
list = append(list, strings.TrimPrefix(line, "nameserver "))
}
}
}
return list
}
func updateDNS(list []string) error {
conf, err := os.ReadFile(defaultDNSPath)
if err != nil {
return fmt.Errorf("read nameserver conf of %s failed, err: %v", defaultDNSPath, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 2 || parts[0] != "nameserver" {
newFile += line + "\n"
continue
}
itemNs := strings.Join(parts[1:], " ")
for index, item := range list {
if item == itemNs {
newFile += line + "\n"
list = append(list[:index], list[index+1:]...)
break
}
}
}
for _, item := range list {
if len(item) != 0 {
newFile += fmt.Sprintf("nameserver %s \n", item)
}
}
file, err := os.OpenFile(defaultDNSPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}
func loadHosts() []dto.HostHelper {
var list []dto.HostHelper
hostConf, err := os.ReadFile(defaultHostPath)
if err == nil {
lines := strings.Split(string(hostConf), "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 2 || strings.HasPrefix(strings.TrimPrefix(line, " "), "#") {
continue
}
list = append(list, dto.HostHelper{IP: parts[0], Host: strings.Join(parts[1:], " ")})
}
}
return list
}
func loadHostname() string {
std, err := cmd.Exec("hostname")
if err != nil {
return ""
}
return strings.ReplaceAll(std, "\n", "")
}
func loadUser() string {
std, err := cmd.Exec("whoami")
if err != nil {
return ""
}
return strings.ReplaceAll(std, "\n", "")
}
func loadSwap() []dto.SwapHelper {
var data []dto.SwapHelper
std, err := cmd.Execf("%s swapon --summary", cmd.SudoHandleCmd())
if err != nil {
return data
}
lines := strings.Split(std, "\n")
for index, line := range lines {
if index == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 5 {
continue
}
sizeItem, _ := strconv.Atoi(parts[2])
data = append(data, dto.SwapHelper{Path: parts[0], Size: uint64(sizeItem), Used: parts[3]})
}
return data
}
func operateSwapWithFile(delete bool, req dto.SwapHelper) error {
conf, err := os.ReadFile(defaultFstab)
if err != nil {
return fmt.Errorf("read file %s failed, err: %v", defaultFstab, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) == 6 && parts[0] == req.Path {
continue
}
newFile += line + "\n"
}
if !delete {
newFile += fmt.Sprintf("%s swap swap defaults 0 0\n", req.Path)
}
file, err := os.OpenFile(defaultFstab, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}

Some files were not shown because too many files have changed in this diff Show More