mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 08:00:27 +00:00
Compare commits
13
Commits
v2.1.5
...
v1.10.13-lts
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
001c896eed | ||
|
|
1cf3a0143c | ||
|
|
f04be521e5 | ||
|
|
2873632a40 | ||
|
|
075879b5b5 | ||
|
|
9b12930376 | ||
|
|
3339ba9bad | ||
|
|
68922676fc | ||
|
|
dcf2afb4a6 | ||
|
|
16a35141dd | ||
|
|
f6c334bdad | ||
|
|
cbb119117b | ||
|
|
cae0a6118f |
@@ -155,7 +155,7 @@ func (b *BaseApi) GetIgnoredApp(c *gin.Context) {
|
||||
// @Success 200 {object} model.AppInstall
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/install [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[{"input_column":"name","input_value":"name","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"}
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"安装应用 [name]","formatEN":"Install app [name]"}
|
||||
func (b *BaseApi) InstallApp(c *gin.Context) {
|
||||
var req request.AppInstallCreate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -336,8 +337,12 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
mode := info.Mode()
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
uid, gid := -1, -1
|
||||
if ok {
|
||||
uid, gid = int(stat.Uid), int(stat.Gid)
|
||||
}
|
||||
|
||||
success := 0
|
||||
failures := make(buserr.MultiErr)
|
||||
@@ -351,6 +356,7 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
global.LOG.Error(e)
|
||||
continue
|
||||
}
|
||||
_ = os.Chown(dstDir, uid, gid)
|
||||
}
|
||||
tmpFilename := dstFilename + ".tmp"
|
||||
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
|
||||
@@ -378,6 +384,9 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||
} else {
|
||||
_ = os.Chmod(dstFilename, mode)
|
||||
}
|
||||
if uid != -1 && gid != -1 {
|
||||
_ = os.Chown(dstFilename, uid, gid)
|
||||
}
|
||||
success++
|
||||
}
|
||||
if success == 0 {
|
||||
@@ -603,9 +612,17 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int,
|
||||
if mode == 0 {
|
||||
mode = 0755
|
||||
}
|
||||
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
|
||||
if err = op.CreateDir(dstDir, mode); err != nil {
|
||||
return err
|
||||
uid, gid := -1, -1
|
||||
if info, err := os.Stat(dstDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = op.CreateDir(dstDir, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
if ok {
|
||||
uid, gid = int(stat.Uid), int(stat.Gid)
|
||||
}
|
||||
}
|
||||
dstFileName := filepath.Join(dstDir, fileName)
|
||||
@@ -615,6 +632,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int,
|
||||
} else {
|
||||
mode = 0644
|
||||
}
|
||||
|
||||
if overwrite {
|
||||
_ = os.Remove(dstFileName)
|
||||
}
|
||||
@@ -635,6 +653,9 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int,
|
||||
}
|
||||
_ = os.Remove(chunkPath)
|
||||
}
|
||||
if uid != -1 && gid != -1 {
|
||||
_ = os.Chown(dstFileName, uid, gid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -145,6 +145,9 @@ func (a AppService) GetAppTags() ([]response.TagDTO, error) {
|
||||
|
||||
func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
||||
var appDTO response.AppDTO
|
||||
if key == "postgres" {
|
||||
key = "postgresql"
|
||||
}
|
||||
app, err := appRepo.GetFirst(appRepo.WithKey(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -17,15 +17,20 @@ import (
|
||||
|
||||
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) error {
|
||||
var apps []model.AppInstall
|
||||
if cronjob.AppID == "all" {
|
||||
if strings.Contains(cronjob.AppID, "all") {
|
||||
apps, _ = appInstallRepo.ListBy()
|
||||
} else {
|
||||
itemID, _ := strconv.Atoi(cronjob.AppID)
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
|
||||
appIds := strings.Split(cronjob.AppID, ",")
|
||||
var idItems []uint
|
||||
for i := 0; i < len(appIds); i++ {
|
||||
itemID, _ := strconv.Atoi(appIds[i])
|
||||
idItems = append(idItems, uint(itemID))
|
||||
}
|
||||
appItems, err := appInstallRepo.ListBy(commonRepo.WithIdsIn(idItems))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apps = append(apps, app)
|
||||
apps = appItems
|
||||
}
|
||||
accountMap, err := loadClientMap(cronjob.BackupAccounts)
|
||||
if err != nil {
|
||||
@@ -232,7 +237,7 @@ type databaseHelper struct {
|
||||
|
||||
func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
|
||||
var dbs []databaseHelper
|
||||
if cronjob.DBName == "all" {
|
||||
if strings.Contains(cronjob.DBName, "all") {
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
mysqlItems, _ := mysqlRepo.List()
|
||||
for _, mysql := range mysqlItems {
|
||||
@@ -254,36 +259,42 @@ func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
|
||||
}
|
||||
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,
|
||||
})
|
||||
|
||||
dbNames := strings.Split(cronjob.DBName, ",")
|
||||
for _, name := range dbNames {
|
||||
itemID, _ := strconv.Atoi(name)
|
||||
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" {
|
||||
if strings.Contains(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)
|
||||
websites := strings.Split(cronjob.Website, ",")
|
||||
var idItems []uint
|
||||
for i := 0; i < len(websites); i++ {
|
||||
itemID, _ := strconv.Atoi(websites[i])
|
||||
idItems = append(idItems, uint(itemID))
|
||||
}
|
||||
weblist, _ = websiteRepo.List(commonRepo.WithIdsIn(idItems))
|
||||
return weblist
|
||||
}
|
||||
|
||||
|
||||
@@ -910,6 +910,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
websiteSSL.Provider = constant.Manual
|
||||
websiteSSL.PrivateKey = privateKey
|
||||
websiteSSL.Pem = certificate
|
||||
websiteSSL.Status = constant.SSLReady
|
||||
|
||||
res.SSL = websiteSSL
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -96,10 +97,11 @@ func OperationLog() gin.HandlerFunc {
|
||||
if funcs.InputValue == key {
|
||||
var names []string
|
||||
if funcs.IsList {
|
||||
sql := fmt.Sprintf("SELECT %s FROM %s where %s in (?);", funcs.OutputColumn, funcs.DB, funcs.InputColumn)
|
||||
_ = global.DB.Raw(sql, value).Scan(&names)
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE %s in (?)", funcs.OutputColumn, funcs.DB, funcs.InputColumn)
|
||||
_ = global.DB.Raw(query, value).Scan(&names)
|
||||
} else {
|
||||
_ = global.DB.Raw(fmt.Sprintf("select %s from %s where %s = ?;", funcs.OutputColumn, funcs.DB, funcs.InputColumn), value).Scan(&names)
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE %s = ?", funcs.OutputColumn, funcs.DB, funcs.InputColumn)
|
||||
_ = global.DB.Raw(query, value).Scan(&names)
|
||||
}
|
||||
formatMap[funcs.OutputValue] = strings.Join(names, ",")
|
||||
break
|
||||
@@ -109,12 +111,21 @@ func OperationLog() gin.HandlerFunc {
|
||||
}
|
||||
for key, value := range formatMap {
|
||||
if strings.Contains(operationDic.FormatEN, "["+key+"]") {
|
||||
if arrays, ok := value.([]string); ok {
|
||||
operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(arrays, ",")))
|
||||
operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(arrays, ",")))
|
||||
} else {
|
||||
t := reflect.TypeOf(value)
|
||||
if t.Kind() != reflect.Array && t.Kind() != reflect.Slice {
|
||||
operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, "["+key+"]", fmt.Sprintf("[%v]", value))
|
||||
operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, "["+key+"]", fmt.Sprintf("[%v]", value))
|
||||
} else {
|
||||
val := reflect.ValueOf(value)
|
||||
length := val.Len()
|
||||
|
||||
var elements []string
|
||||
for i := 0; i < length; i++ {
|
||||
element := val.Index(i).Interface().(string)
|
||||
elements = append(elements, element)
|
||||
}
|
||||
operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(elements, ",")))
|
||||
operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(elements, ",")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func init() {}
|
||||
|
||||
type dumpOption struct {
|
||||
isData bool
|
||||
|
||||
tables []string
|
||||
isAllTable bool
|
||||
isDropTable bool
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
type DumpOption func(*dumpOption)
|
||||
|
||||
func WithDropTable() DumpOption {
|
||||
return func(option *dumpOption) {
|
||||
option.isDropTable = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithData() DumpOption {
|
||||
return func(option *dumpOption) {
|
||||
option.isData = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithWriter(writer io.Writer) DumpOption {
|
||||
return func(option *dumpOption) {
|
||||
option.writer = writer
|
||||
}
|
||||
}
|
||||
|
||||
func Dump(dns string, opts ...DumpOption) error {
|
||||
start := time.Now()
|
||||
global.LOG.Infof("dump start at %s\n", start.Format(constant.DateTimeLayout))
|
||||
defer func() {
|
||||
end := time.Now()
|
||||
global.LOG.Infof("dump end at %s, cost %s\n", end.Format(constant.DateTimeLayout), end.Sub(start))
|
||||
}()
|
||||
|
||||
var err error
|
||||
|
||||
var o dumpOption
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&o)
|
||||
}
|
||||
|
||||
if len(o.tables) == 0 {
|
||||
o.isAllTable = true
|
||||
}
|
||||
|
||||
if o.writer == nil {
|
||||
o.writer = os.Stdout
|
||||
}
|
||||
|
||||
buf := bufio.NewWriter(o.writer)
|
||||
defer buf.Flush()
|
||||
|
||||
itemFile, lineNumber := "", 0
|
||||
|
||||
itemFile += "-- ----------------------------\n"
|
||||
itemFile += "-- MySQL Database Dump\n"
|
||||
itemFile += "-- Start Time: " + start.Format(constant.DateTimeLayout) + "\n"
|
||||
itemFile += "-- ----------------------------\n\n\n"
|
||||
|
||||
db, err := sql.Open("mysql", dns)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("open mysql db failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
dbName, err := getDBNameFromDNS(dns)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get db name from dns failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
_, err = db.Exec(fmt.Sprintf("USE `%s`", dbName))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("exec `use %s` failed, err: %v", dbName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
var tables []string
|
||||
if o.isAllTable {
|
||||
tmp, err := getAllTables(db)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get all tables failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
tables = tmp
|
||||
} else {
|
||||
tables = o.tables
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
if o.isDropTable {
|
||||
itemFile += fmt.Sprintf("DROP TABLE IF EXISTS `%s`;\n", table)
|
||||
}
|
||||
|
||||
itemFile += "-- ----------------------------\n"
|
||||
itemFile += fmt.Sprintf("-- Table structure for %s\n", table)
|
||||
itemFile += "-- ----------------------------\n"
|
||||
|
||||
createTableSQL, err := getCreateTableSQL(db, table)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get create table sql failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
itemFile += createTableSQL
|
||||
itemFile += ";\n\n\n\n"
|
||||
|
||||
if o.isData {
|
||||
itemFile += "-- ----------------------------\n"
|
||||
itemFile += fmt.Sprintf("-- Records of %s\n", table)
|
||||
itemFile += "-- ----------------------------\n"
|
||||
|
||||
lineRows, err := db.Query(fmt.Sprintf("SELECT * FROM `%s`", table))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("exec `select * from %s` failed, err: %v", table, err)
|
||||
return err
|
||||
}
|
||||
defer lineRows.Close()
|
||||
|
||||
var columns []string
|
||||
columns, err = lineRows.Columns()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get columes failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
columnTypes, err := lineRows.ColumnTypes()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get colume types failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
for lineRows.Next() {
|
||||
row := make([]interface{}, len(columns))
|
||||
rowPointers := make([]interface{}, len(columns))
|
||||
for i := range columns {
|
||||
rowPointers[i] = &row[i]
|
||||
}
|
||||
if err = lineRows.Scan(rowPointers...); err != nil {
|
||||
global.LOG.Errorf("scan row data failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
ssql := loadDataSql(row, columnTypes, table)
|
||||
if len(ssql) != 0 {
|
||||
itemFile += ssql
|
||||
lineNumber++
|
||||
}
|
||||
if lineNumber > 500 {
|
||||
_, _ = buf.WriteString(itemFile)
|
||||
itemFile = ""
|
||||
lineNumber = 0
|
||||
}
|
||||
}
|
||||
|
||||
itemFile += "\n\n"
|
||||
}
|
||||
}
|
||||
|
||||
itemFile += "-- ----------------------------\n"
|
||||
itemFile += "-- Dumped by mysqldump\n"
|
||||
itemFile += "-- Cost Time: " + time.Since(start).String() + "\n"
|
||||
itemFile += "-- ----------------------------\n"
|
||||
|
||||
_, _ = buf.WriteString(itemFile)
|
||||
_ = buf.Flush()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCreateTableSQL(db *sql.DB, table string) (string, error) {
|
||||
var createTableSQL string
|
||||
err := db.QueryRow(fmt.Sprintf("SHOW CREATE TABLE `%s`", table)).Scan(&table, &createTableSQL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
createTableSQL = strings.Replace(createTableSQL, "CREATE TABLE", "CREATE TABLE IF NOT EXISTS", 1)
|
||||
return createTableSQL, nil
|
||||
}
|
||||
|
||||
func getAllTables(db *sql.DB) ([]string, error) {
|
||||
var tables []string
|
||||
rows, err := db.Query("SHOW TABLES")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var table string
|
||||
err = rows.Scan(&table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tables = append(tables, table)
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
func loadDataSql(row []interface{}, columnTypes []*sql.ColumnType, table string) string {
|
||||
ssql := "INSERT INTO `" + table + "` VALUES ("
|
||||
for i, col := range row {
|
||||
if col == nil {
|
||||
ssql += "NULL"
|
||||
} else {
|
||||
Type := columnTypes[i].DatabaseTypeName()
|
||||
Type = strings.Replace(Type, "UNSIGNED", "", -1)
|
||||
Type = strings.Replace(Type, " ", "", -1)
|
||||
switch Type {
|
||||
case "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "INTEGER", "BIGINT":
|
||||
if bs, ok := col.([]byte); ok {
|
||||
ssql += string(bs)
|
||||
} else {
|
||||
ssql += fmt.Sprintf("%d", col)
|
||||
}
|
||||
case "FLOAT", "DOUBLE":
|
||||
if bs, ok := col.([]byte); ok {
|
||||
ssql += string(bs)
|
||||
} else {
|
||||
ssql += fmt.Sprintf("%f", col)
|
||||
}
|
||||
case "DECIMAL", "DEC":
|
||||
ssql += fmt.Sprintf("%s", col)
|
||||
|
||||
case "DATE":
|
||||
t, ok := col.(time.Time)
|
||||
if !ok {
|
||||
global.LOG.Errorf("the DATE type conversion failed, err value: %v", col)
|
||||
return ""
|
||||
}
|
||||
ssql += fmt.Sprintf("'%s'", t.Format("2006-01-02"))
|
||||
case "DATETIME":
|
||||
t, ok := col.(time.Time)
|
||||
if !ok {
|
||||
global.LOG.Errorf("the DATETIME type conversion failed, err value: %v", col)
|
||||
return ""
|
||||
}
|
||||
ssql += fmt.Sprintf("'%s'", t.Format(constant.DateTimeLayout))
|
||||
case "TIMESTAMP":
|
||||
t, ok := col.(time.Time)
|
||||
if !ok {
|
||||
global.LOG.Errorf("the TIMESTAMP type conversion failed, err value: %v", col)
|
||||
return ""
|
||||
}
|
||||
ssql += fmt.Sprintf("'%s'", t.Format(constant.DateTimeLayout))
|
||||
case "TIME":
|
||||
t, ok := col.([]byte)
|
||||
if !ok {
|
||||
global.LOG.Errorf("the TIME type conversion failed, err value: %v", col)
|
||||
return ""
|
||||
}
|
||||
ssql += fmt.Sprintf("'%s'", string(t))
|
||||
case "YEAR":
|
||||
t, ok := col.([]byte)
|
||||
if !ok {
|
||||
global.LOG.Errorf("the YEAR type conversion failed, err value: %v", col)
|
||||
return ""
|
||||
}
|
||||
ssql += string(t)
|
||||
case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT":
|
||||
ssql += fmt.Sprintf("'%s'", strings.Replace(fmt.Sprintf("%s", col), "'", "''", -1))
|
||||
case "BIT", "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB":
|
||||
ssql += fmt.Sprintf("0x%X", col)
|
||||
case "ENUM", "SET":
|
||||
ssql += fmt.Sprintf("'%s'", col)
|
||||
case "BOOL", "BOOLEAN":
|
||||
if col.(bool) {
|
||||
ssql += "true"
|
||||
} else {
|
||||
ssql += "false"
|
||||
}
|
||||
case "JSON":
|
||||
ssql += fmt.Sprintf("'%s'", col)
|
||||
default:
|
||||
global.LOG.Errorf("unsupported colume type: %s", Type)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
if i < len(row)-1 {
|
||||
ssql += ","
|
||||
}
|
||||
}
|
||||
ssql += ");\n"
|
||||
return ssql
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
)
|
||||
|
||||
type sourceOption struct {
|
||||
dryRun bool
|
||||
mergeInsert int
|
||||
debug bool
|
||||
}
|
||||
type SourceOption func(*sourceOption)
|
||||
|
||||
func WithMergeInsert(size int) SourceOption {
|
||||
return func(o *sourceOption) {
|
||||
o.mergeInsert = size
|
||||
}
|
||||
}
|
||||
|
||||
type dbWrapper struct {
|
||||
DB *sql.DB
|
||||
debug bool
|
||||
dryRun bool
|
||||
}
|
||||
|
||||
func newDBWrapper(db *sql.DB, dryRun, debug bool) *dbWrapper {
|
||||
|
||||
return &dbWrapper{
|
||||
DB: db,
|
||||
dryRun: dryRun,
|
||||
debug: debug,
|
||||
}
|
||||
}
|
||||
|
||||
func (db *dbWrapper) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
if db.debug {
|
||||
global.LOG.Debugf("query %s", query)
|
||||
}
|
||||
|
||||
if db.dryRun {
|
||||
return nil, nil
|
||||
}
|
||||
return db.DB.Exec(query, args...)
|
||||
}
|
||||
|
||||
func Source(dns string, reader io.Reader, opts ...SourceOption) error {
|
||||
start := time.Now()
|
||||
global.LOG.Infof("source start at %s", start.Format(constant.DateTimeLayout))
|
||||
defer func() {
|
||||
end := time.Now()
|
||||
global.LOG.Infof("source end at %s, cost %s", end.Format(constant.DateTimeLayout), end.Sub(start))
|
||||
}()
|
||||
|
||||
var err error
|
||||
var db *sql.DB
|
||||
var o sourceOption
|
||||
for _, opt := range opts {
|
||||
opt(&o)
|
||||
}
|
||||
|
||||
dbName, err := getDBNameFromDNS(dns)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get db name from dns failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
db, err = sql.Open("mysql", dns)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("open mysql db failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
dbWrapper := newDBWrapper(db, o.dryRun, o.debug)
|
||||
|
||||
_, err = dbWrapper.Exec(fmt.Sprintf("USE `%s`;", dbName))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("exec `use %s` failed, err: %v", dbName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
db.SetConnMaxLifetime(3600)
|
||||
|
||||
r := bufio.NewReader(reader)
|
||||
_, err = dbWrapper.Exec("SET autocommit=0;")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("exec `set autocommit=0` failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
line, err := readLine(r)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
global.LOG.Errorf("read sql failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
ssql, err := trim(line)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("trim sql failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
afterInsertSql := ""
|
||||
if o.mergeInsert > 1 && strings.HasPrefix(ssql, "INSERT INTO") {
|
||||
var insertSQLs []string
|
||||
insertSQLs = append(insertSQLs, ssql)
|
||||
for i := 0; i < o.mergeInsert-1; i++ {
|
||||
line, err := readLine(r)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
ssql2, err := trim(line)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("trim merge insert sql failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
if strings.HasPrefix(ssql2, "INSERT INTO") {
|
||||
insertSQLs = append(insertSQLs, ssql2)
|
||||
continue
|
||||
}
|
||||
afterInsertSql = ssql2
|
||||
break
|
||||
}
|
||||
ssql, err = mergeInsert(insertSQLs)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do merge insert failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = dbWrapper.Exec(ssql)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("exec sql failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
if len(afterInsertSql) != 0 {
|
||||
_, err = dbWrapper.Exec(afterInsertSql)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("exec sql failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = dbWrapper.Exec("COMMIT;")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("exec `commit` failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = dbWrapper.Exec("SET autocommit=1;")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("exec `autocommit=1` failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeInsert(insertSQLs []string) (string, error) {
|
||||
if len(insertSQLs) == 0 {
|
||||
return "", errors.New("no input provided")
|
||||
}
|
||||
builder := strings.Builder{}
|
||||
sql1 := insertSQLs[0]
|
||||
sql1 = strings.TrimSuffix(sql1, ";")
|
||||
builder.WriteString(sql1)
|
||||
for i, insertSQL := range insertSQLs[1:] {
|
||||
if i < len(insertSQLs)-1 {
|
||||
builder.WriteString(",")
|
||||
}
|
||||
|
||||
valuesIdx := strings.Index(insertSQL, "VALUES")
|
||||
if valuesIdx == -1 {
|
||||
return "", errors.New("invalid SQL: missing VALUES keyword")
|
||||
}
|
||||
sqln := insertSQL[valuesIdx:]
|
||||
sqln = strings.TrimPrefix(sqln, "VALUES")
|
||||
sqln = strings.TrimSuffix(sqln, ";")
|
||||
builder.WriteString(sqln)
|
||||
|
||||
}
|
||||
builder.WriteString(";")
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
func trim(s string) (string, error) {
|
||||
s = strings.TrimLeft(s, "\n")
|
||||
s = strings.TrimSpace(s)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func getDBNameFromDNS(dns string) (string, error) {
|
||||
ss1 := strings.Split(dns, "/")
|
||||
if len(ss1) == 2 {
|
||||
ss2 := strings.Split(ss1[1], "?")
|
||||
if len(ss2) == 2 {
|
||||
return ss2[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("dns error: %s", dns)
|
||||
}
|
||||
|
||||
func readLine(r *bufio.Reader) (string, error) {
|
||||
lineItem, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return lineItem, err
|
||||
}
|
||||
global.LOG.Errorf("read merge insert sql failed, err: %v", err)
|
||||
return "", err
|
||||
}
|
||||
if strings.HasSuffix(lineItem, ";\n") {
|
||||
return lineItem, nil
|
||||
}
|
||||
lineAppend, err := readLine(r)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return lineItem, err
|
||||
}
|
||||
global.LOG.Errorf("read merge insert sql failed, err: %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return lineItem + lineAppend, nil
|
||||
}
|
||||
+43
-22
@@ -1,5 +1,4 @@
|
||||
// Code generated by swaggo/swag. DO NOT EDIT.
|
||||
|
||||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
@@ -216,28 +215,12 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [
|
||||
{
|
||||
"db": "app_installs",
|
||||
"input_column": "name",
|
||||
"input_value": "name",
|
||||
"isList": false,
|
||||
"output_column": "app_id",
|
||||
"output_value": "appId"
|
||||
},
|
||||
{
|
||||
"db": "apps",
|
||||
"info": "appId",
|
||||
"isList": false,
|
||||
"output_column": "key",
|
||||
"output_value": "appKey"
|
||||
}
|
||||
],
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"name"
|
||||
],
|
||||
"formatEN": "Install app [appKey]-[name]",
|
||||
"formatZH": "安装应用 [appKey]-[name]",
|
||||
"formatEN": "Install app [name]",
|
||||
"formatZH": "安装应用 [name]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@@ -11486,7 +11469,7 @@ const docTemplate = `{
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SearchWithPage"
|
||||
"$ref": "#/definitions/dto.SearchClamWithPage"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -19321,6 +19304,42 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SearchClamWithPage": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"order",
|
||||
"orderBy",
|
||||
"page",
|
||||
"pageSize"
|
||||
],
|
||||
"properties": {
|
||||
"info": {
|
||||
"type": "string"
|
||||
},
|
||||
"order": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"null",
|
||||
"ascending",
|
||||
"descending"
|
||||
]
|
||||
},
|
||||
"orderBy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"status",
|
||||
"created_at"
|
||||
]
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"pageSize": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SearchForTree": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -23457,6 +23476,8 @@ var SwaggerInfo = &swag.Spec{
|
||||
Description: "开源Linux面板",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -209,28 +209,12 @@
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [
|
||||
{
|
||||
"db": "app_installs",
|
||||
"input_column": "name",
|
||||
"input_value": "name",
|
||||
"isList": false,
|
||||
"output_column": "app_id",
|
||||
"output_value": "appId"
|
||||
},
|
||||
{
|
||||
"db": "apps",
|
||||
"info": "appId",
|
||||
"isList": false,
|
||||
"output_column": "key",
|
||||
"output_value": "appKey"
|
||||
}
|
||||
],
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"name"
|
||||
],
|
||||
"formatEN": "Install app [appKey]-[name]",
|
||||
"formatZH": "安装应用 [appKey]-[name]",
|
||||
"formatEN": "Install app [name]",
|
||||
"formatZH": "安装应用 [name]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@@ -11479,7 +11463,7 @@
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SearchWithPage"
|
||||
"$ref": "#/definitions/dto.SearchClamWithPage"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -19314,6 +19298,42 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SearchClamWithPage": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"order",
|
||||
"orderBy",
|
||||
"page",
|
||||
"pageSize"
|
||||
],
|
||||
"properties": {
|
||||
"info": {
|
||||
"type": "string"
|
||||
},
|
||||
"order": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"null",
|
||||
"ascending",
|
||||
"descending"
|
||||
]
|
||||
},
|
||||
"orderBy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"status",
|
||||
"created_at"
|
||||
]
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"pageSize": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SearchForTree": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -2748,6 +2748,32 @@ definitions:
|
||||
- ssl
|
||||
- sslType
|
||||
type: object
|
||||
dto.SearchClamWithPage:
|
||||
properties:
|
||||
info:
|
||||
type: string
|
||||
order:
|
||||
enum:
|
||||
- "null"
|
||||
- ascending
|
||||
- descending
|
||||
type: string
|
||||
orderBy:
|
||||
enum:
|
||||
- name
|
||||
- status
|
||||
- created_at
|
||||
type: string
|
||||
page:
|
||||
type: integer
|
||||
pageSize:
|
||||
type: integer
|
||||
required:
|
||||
- order
|
||||
- orderBy
|
||||
- page
|
||||
- pageSize
|
||||
type: object
|
||||
dto.SearchForTree:
|
||||
properties:
|
||||
info:
|
||||
@@ -5633,22 +5659,11 @@ paths:
|
||||
tags:
|
||||
- App
|
||||
x-panel-log:
|
||||
BeforeFunctions:
|
||||
- db: app_installs
|
||||
input_column: name
|
||||
input_value: name
|
||||
isList: false
|
||||
output_column: app_id
|
||||
output_value: appId
|
||||
- db: apps
|
||||
info: appId
|
||||
isList: false
|
||||
output_column: key
|
||||
output_value: appKey
|
||||
BeforeFunctions: []
|
||||
bodyKeys:
|
||||
- name
|
||||
formatEN: Install app [appKey]-[name]
|
||||
formatZH: 安装应用 [appKey]-[name]
|
||||
formatEN: Install app [name]
|
||||
formatZH: 安装应用 [name]
|
||||
paramKeys: []
|
||||
/apps/installed/check:
|
||||
post:
|
||||
@@ -12769,7 +12784,7 @@ paths:
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.SearchWithPage'
|
||||
$ref: '#/definitions/dto.SearchClamWithPage'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
|
||||
@@ -24,6 +24,9 @@ export namespace Cronjob {
|
||||
backupAccounts: string;
|
||||
defaultDownload: string;
|
||||
backupAccountList: Array<string>;
|
||||
appIdList: Array<string>;
|
||||
websiteList: Array<string>;
|
||||
dbNameList: Array<string>;
|
||||
retainCopies: number;
|
||||
status: string;
|
||||
secret: string;
|
||||
|
||||
@@ -69,6 +69,7 @@ const message = {
|
||||
createNewFolder: 'Create new folder',
|
||||
createNewFile: 'Create new file',
|
||||
helpDoc: 'Help Document',
|
||||
unbind: 'UnBind',
|
||||
},
|
||||
search: {
|
||||
timeStart: 'Time start',
|
||||
|
||||
@@ -68,6 +68,7 @@ const message = {
|
||||
createNewFolder: '新建資料夾',
|
||||
createNewFile: '新建檔案',
|
||||
helpDoc: '幫助文档',
|
||||
unbind: '解綁',
|
||||
},
|
||||
search: {
|
||||
timeStart: '開始時間',
|
||||
|
||||
@@ -68,6 +68,7 @@ const message = {
|
||||
createNewFolder: '新建文件夹',
|
||||
createNewFile: '新建文件',
|
||||
helpDoc: '帮助文档',
|
||||
unbind: '解绑',
|
||||
},
|
||||
search: {
|
||||
timeStart: '开始时间',
|
||||
|
||||
@@ -147,6 +147,12 @@ const openInstall = () => {
|
||||
case 'node':
|
||||
router.push({ path: '/websites/runtimes/node' });
|
||||
break;
|
||||
case 'java':
|
||||
router.push({ path: '/websites/runtimes/java' });
|
||||
break;
|
||||
case 'go':
|
||||
router.push({ path: '/websites/runtimes/go' });
|
||||
break;
|
||||
default:
|
||||
const params = {
|
||||
app: app.value,
|
||||
|
||||
@@ -186,9 +186,9 @@
|
||||
<el-form-item
|
||||
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'cutWebsiteLog'"
|
||||
:label="dialogData.rowData!.type === 'website' ? $t('cronjob.website'):$t('website.website')"
|
||||
prop="website"
|
||||
prop="websiteList"
|
||||
>
|
||||
<el-select class="selectClass" v-model="dialogData.rowData!.website">
|
||||
<el-select class="selectClass" multiple v-model="dialogData.rowData!.websiteList">
|
||||
<el-option
|
||||
:disabled="websiteOptions.length === 0"
|
||||
:label="$t('commons.table.all')"
|
||||
@@ -212,8 +212,8 @@
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="dialogData.rowData!.type === 'app'">
|
||||
<el-form-item :label="$t('cronjob.app')" prop="appID">
|
||||
<el-select class="selectClass" clearable v-model="dialogData.rowData!.appID">
|
||||
<el-form-item :label="$t('cronjob.app')" prop="appIdList">
|
||||
<el-select class="selectClass" multiple clearable v-model="dialogData.rowData!.appIdList">
|
||||
<el-option
|
||||
:disabled="appOptions.length === 0"
|
||||
:label="$t('commons.table.all')"
|
||||
@@ -239,8 +239,8 @@
|
||||
<el-radio value="postgresql">PostgreSQL</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('cronjob.database')" prop="dbName">
|
||||
<el-select class="selectClass" clearable v-model="dialogData.rowData!.dbName">
|
||||
<el-form-item :label="$t('cronjob.database')" prop="dbNameList">
|
||||
<el-select class="selectClass" multiple clearable v-model="dialogData.rowData!.dbNameList">
|
||||
<el-option
|
||||
:disabled="dbInfo.dbs.length === 0"
|
||||
:label="$t('commons.table.all')"
|
||||
@@ -378,6 +378,7 @@ import { listContainer } from '@/api/modules/container';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import { ListAppInstalled } from '@/api/modules/app';
|
||||
import { loadDefaultSpec, specOptions, transObjToSpec, transSpecToObj, weekOptions } from './../helper';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
interface DialogProps {
|
||||
@@ -385,6 +386,7 @@ interface DialogProps {
|
||||
rowData?: Cronjob.CronjobInfo;
|
||||
getTableList?: () => Promise<any>;
|
||||
}
|
||||
|
||||
const title = ref<string>('');
|
||||
const drawerVisible = ref(false);
|
||||
const dialogData = ref<DialogProps>({
|
||||
@@ -408,6 +410,15 @@ const acceptParams = (params: DialogProps): void => {
|
||||
if (dialogData.value.rowData.backupAccounts) {
|
||||
dialogData.value.rowData.backupAccountList = dialogData.value.rowData.backupAccounts.split(',');
|
||||
}
|
||||
if (dialogData.value.rowData.appID) {
|
||||
dialogData.value.rowData.appIdList = dialogData.value.rowData.appID.split(',');
|
||||
}
|
||||
if (dialogData.value.rowData.website) {
|
||||
dialogData.value.rowData.websiteList = dialogData.value.rowData.website.split(',');
|
||||
}
|
||||
if (dialogData.value.rowData.dbName) {
|
||||
dialogData.value.rowData.dbNameList = dialogData.value.rowData.dbName.split(',');
|
||||
}
|
||||
dialogData.value.rowData!.command = dialogData.value.rowData!.command || 'sh';
|
||||
dialogData.value.rowData!.isCustom =
|
||||
dialogData.value.rowData!.command !== 'sh' &&
|
||||
@@ -718,6 +729,17 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
specs.push(itemSpec);
|
||||
}
|
||||
dialogData.value.rowData.backupAccounts = dialogData.value.rowData.backupAccountList.join(',');
|
||||
|
||||
if (dialogData.value.rowData.appIdList) {
|
||||
dialogData.value.rowData.appID = dialogData.value.rowData.appIdList.join(',');
|
||||
}
|
||||
if (dialogData.value.rowData.websiteList) {
|
||||
dialogData.value.rowData.website = dialogData.value.rowData.websiteList.join(',');
|
||||
}
|
||||
if (dialogData.value.rowData.dbNameList) {
|
||||
dialogData.value.rowData.dbName = dialogData.value.rowData.dbNameList.join(',');
|
||||
}
|
||||
|
||||
dialogData.value.rowData.spec = specs.join(',');
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
@@ -750,40 +772,49 @@ defineExpose({
|
||||
.specClass {
|
||||
width: 20% !important;
|
||||
margin-left: 20px;
|
||||
|
||||
.append {
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
.specClass {
|
||||
width: 100% !important;
|
||||
margin-top: 20px;
|
||||
margin-left: 0;
|
||||
|
||||
.append {
|
||||
width: 43px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.specTypeClass {
|
||||
width: 22% !important;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
.specTypeClass {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.selectClass {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tagClass {
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.logText {
|
||||
line-height: 22px;
|
||||
font-size: 12px;
|
||||
|
||||
.link {
|
||||
font-size: 12px;
|
||||
margin-top: -3px;
|
||||
|
||||
@@ -74,7 +74,7 @@ const submit = async () => {
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
emit('search');
|
||||
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
dialogVisible.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -163,7 +163,7 @@ const buttons = [
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
label: i18n.global.t('commons.button.unbind'),
|
||||
click: (row: Database.DatabaseInfo) => {
|
||||
onDelete(row);
|
||||
},
|
||||
|
||||
@@ -74,7 +74,7 @@ const submit = async () => {
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
emit('search');
|
||||
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
dialogVisible.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -163,7 +163,7 @@ const buttons = [
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
label: i18n.global.t('commons.button.unbind'),
|
||||
click: (row: Database.DatabaseInfo) => {
|
||||
onDelete(row);
|
||||
},
|
||||
|
||||
@@ -74,7 +74,7 @@ const submit = async () => {
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
emit('search');
|
||||
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
dialogVisible.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -163,7 +163,7 @@ const buttons = [
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
label: i18n.global.t('commons.button.unbind'),
|
||||
click: (row: Database.DatabaseInfo) => {
|
||||
onDelete(row);
|
||||
},
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
link
|
||||
small
|
||||
@click="getDirSize(row, $index)"
|
||||
:loading="btnLoading == $index"
|
||||
:loading="btnLoading.includes($index)"
|
||||
>
|
||||
<span v-if="row.dirSize == undefined">
|
||||
{{ $t('file.calculate') }}
|
||||
@@ -387,7 +387,7 @@ const initData = () => ({
|
||||
});
|
||||
let req = reactive(initData());
|
||||
let loading = ref(false);
|
||||
let btnLoading = ref(-1);
|
||||
let btnLoading = ref([]);
|
||||
const paths = ref<FilePaths[]>([]);
|
||||
let pathWidth = ref(0);
|
||||
const history: string[] = [];
|
||||
@@ -638,7 +638,7 @@ const getDirSize = async (row: any, index: number) => {
|
||||
const req = {
|
||||
path: row.path,
|
||||
};
|
||||
btnLoading.value = index;
|
||||
btnLoading.value.push(index);
|
||||
await ComputeDirSize(req)
|
||||
.then(async (res) => {
|
||||
let newData = [...data.value];
|
||||
@@ -646,7 +646,7 @@ const getDirSize = async (row: any, index: number) => {
|
||||
data.value = newData;
|
||||
})
|
||||
.finally(() => {
|
||||
btnLoading.value = -1;
|
||||
btnLoading.value = btnLoading.value.filter((item) => item !== index);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ const verifySpec = (rule: any, value: any, callback: any) => {
|
||||
!Number.isInteger(item.second) ||
|
||||
!Number.isInteger(item.week)
|
||||
) {
|
||||
callback(new Error(i18n.global.t('cronjob.specErr')));
|
||||
callback(new Error(i18n.global.t('toolbox.clam.specErr')));
|
||||
return;
|
||||
}
|
||||
switch (item.specType) {
|
||||
@@ -215,7 +215,7 @@ const verifySpec = (rule: any, value: any, callback: any) => {
|
||||
item.minute < 0 ||
|
||||
item.minute > 59
|
||||
) {
|
||||
callback(new Error(i18n.global.t('cronjob.specErr')));
|
||||
callback(new Error(i18n.global.t('toolbox.clam.specErr')));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -228,7 +228,7 @@ const verifySpec = (rule: any, value: any, callback: any) => {
|
||||
item.minute < 0 ||
|
||||
item.minute > 59
|
||||
) {
|
||||
callback(new Error(i18n.global.t('cronjob.specErr')));
|
||||
callback(new Error(i18n.global.t('toolbox.clam.specErr')));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -241,36 +241,36 @@ const verifySpec = (rule: any, value: any, callback: any) => {
|
||||
item.minute < 0 ||
|
||||
item.minute > 59
|
||||
) {
|
||||
callback(new Error(i18n.global.t('cronjob.specErr')));
|
||||
callback(new Error(i18n.global.t('toolbox.clam.specErr')));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'perDay':
|
||||
if (item.hour < 0 || item.hour > 23 || item.minute < 0 || item.minute > 59) {
|
||||
callback(new Error(i18n.global.t('cronjob.specErr')));
|
||||
callback(new Error(i18n.global.t('toolbox.clam.specErr')));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'perNHour':
|
||||
if (item.hour < 0 || item.hour > 8784 || item.minute < 0 || item.minute > 59) {
|
||||
callback(new Error(i18n.global.t('cronjob.specErr')));
|
||||
callback(new Error(i18n.global.t('toolbox.clam.specErr')));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'perHour':
|
||||
if (item.minute < 0 || item.minute > 59) {
|
||||
callback(new Error(i18n.global.t('cronjob.specErr')));
|
||||
callback(new Error(i18n.global.t('toolbox.clam.specErr')));
|
||||
return;
|
||||
}
|
||||
case 'perNMinute':
|
||||
if (item.minute < 0 || item.minute > 527040) {
|
||||
callback(new Error(i18n.global.t('cronjob.specErr')));
|
||||
callback(new Error(i18n.global.t('toolbox.clam.specErr')));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'perNSecond':
|
||||
if (item.second < 0 || item.second > 31622400) {
|
||||
callback(new Error(i18n.global.t('cronjob.specErr')));
|
||||
callback(new Error(i18n.global.t('toolbox.clam.specErr')));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user