mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
feat: 面板设置过期跳转功能实现
This commit is contained in:
parent
49f0fb10a8
commit
12dc630c89
@ -104,7 +104,22 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := cronjobService.Save(id, req); err != nil {
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["name"] = req.Name
|
||||
upMap["script"] = req.Script
|
||||
upMap["specType"] = req.SpecType
|
||||
upMap["week"] = req.Week
|
||||
upMap["day"] = req.Day
|
||||
upMap["hour"] = req.Hour
|
||||
upMap["minute"] = req.Minute
|
||||
upMap["website"] = req.Website
|
||||
upMap["exclusionRules"] = req.ExclusionRules
|
||||
upMap["database"] = req.Database
|
||||
upMap["url"] = req.URL
|
||||
upMap["sourceDir"] = req.SourceDir
|
||||
upMap["targetDirID"] = req.TargetDirID
|
||||
upMap["retainCopies"] = req.RetainCopies
|
||||
if err := cronjobService.Update(id, req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
@ -57,6 +57,24 @@ func (b *BaseApi) UpdatePassword(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
|
||||
var req dto.PasswordUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.HandlePasswordExpired(c, req.OldPassword, req.NewPassword); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) SyncTime(c *gin.Context) {
|
||||
var timeLayoutStr = "2006-01-02 15:04:05"
|
||||
|
||||
|
@ -18,7 +18,7 @@ type CronjobCreate struct {
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainDays int `json:"retainDays" validate:"number,min=1"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
}
|
||||
|
||||
type CronjobUpdate struct {
|
||||
@ -36,7 +36,7 @@ type CronjobUpdate struct {
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainDays int `json:"retainDays" validate:"number,min=1"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
}
|
||||
|
||||
type CronjobUpdateStatus struct {
|
||||
@ -71,9 +71,10 @@ type CronjobInfo struct {
|
||||
SourceDir string `json:"sourceDir"`
|
||||
TargetDir string `json:"targetDir"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainDays int `json:"retainDays"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
|
||||
Status string `json:"status"`
|
||||
LastRecrodTime string `json:"lastRecrodTime"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type SearchRecord struct {
|
||||
|
@ -13,7 +13,7 @@ type SettingInfo struct {
|
||||
|
||||
ServerPort int `json:"serverPort"`
|
||||
SecurityEntrance string `json:"securityEntrance"`
|
||||
PasswordTimeOut string `json:"passwordTimeOut"`
|
||||
ExpirationTime string `json:"expirationTime"`
|
||||
ComplexityVerification string `json:"complexityVerification"`
|
||||
MFAStatus string `json:"mfaStatus"`
|
||||
MFASecret string `json:"mfaSecret"`
|
||||
|
@ -21,7 +21,7 @@ type Cronjob struct {
|
||||
SourceDir string `gorm:"type:varchar(256)" json:"sourceDir"`
|
||||
TargetDirID uint64 `gorm:"type:decimal" json:"targetDirID"`
|
||||
ExclusionRules string `gorm:"longtext" json:"exclusionRules"`
|
||||
RetainDays uint64 `gorm:"type:decimal" json:"retainDays"`
|
||||
RetainCopies uint64 `gorm:"type:decimal" json:"retainCopies"`
|
||||
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
EntryID uint64 `gorm:"type:decimal" json:"entryID"`
|
||||
|
@ -14,6 +14,7 @@ type CronjobRepo struct{}
|
||||
type ICronjobRepo interface {
|
||||
Get(opts ...DBOption) (model.Cronjob, error)
|
||||
GetRecord(opts ...DBOption) (model.JobRecords, error)
|
||||
RecordFirst(id uint) (model.JobRecords, error)
|
||||
ListRecord(opts ...DBOption) ([]model.JobRecords, error)
|
||||
List(opts ...DBOption) ([]model.Cronjob, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
|
||||
@ -84,6 +85,12 @@ func (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cro
|
||||
return count, cronjobs, err
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) RecordFirst(id uint) (model.JobRecords, error) {
|
||||
var record model.JobRecords
|
||||
err := global.DB.Order("created_at desc").First(&record).Error
|
||||
return record, err
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error) {
|
||||
var cronjobs []model.JobRecords
|
||||
db := global.DB.Model(&model.JobRecords{})
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/1Panel-dev/1Panel/utils/cloud_storage"
|
||||
"github.com/jinzhu/copier"
|
||||
@ -18,6 +19,7 @@ type IBackupService interface {
|
||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
BatchDelete(ids []uint) error
|
||||
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
|
||||
}
|
||||
|
||||
func NewIBackupService() IBackupService {
|
||||
@ -81,3 +83,28 @@ func (u *BackupService) BatchDelete(ids []uint) error {
|
||||
func (u *BackupService) Update(id uint, upMap map[string]interface{}) error {
|
||||
return backupRepo.Update(id, upMap)
|
||||
}
|
||||
|
||||
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
varMap["type"] = backup.Type
|
||||
if backup.Type == "LOCAL" {
|
||||
return nil, errors.New("not support")
|
||||
}
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp:
|
||||
varMap["password"] = backup.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo:
|
||||
varMap["secretKey"] = backup.Credential
|
||||
}
|
||||
|
||||
backClient, err := cloud_storage.NewCloudStorageClient(varMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return backClient, nil
|
||||
}
|
||||
|
@ -2,13 +2,9 @@ package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -22,11 +18,6 @@ import (
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
errRecord = "errRecord"
|
||||
errHandle = "errHandle"
|
||||
)
|
||||
|
||||
type CronjobService struct{}
|
||||
|
||||
type ICronjobService interface {
|
||||
@ -34,7 +25,7 @@ type ICronjobService interface {
|
||||
SearchRecords(search dto.SearchRecord) (int64, interface{}, error)
|
||||
Create(cronjobDto dto.CronjobCreate) error
|
||||
HandleOnce(id uint) error
|
||||
Save(id uint, req dto.CronjobUpdate) error
|
||||
Update(id uint, req dto.CronjobUpdate) error
|
||||
UpdateStatus(id uint, status string) error
|
||||
Delete(ids []uint) error
|
||||
}
|
||||
@ -59,6 +50,12 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
|
||||
} else {
|
||||
item.TargetDir = "-"
|
||||
}
|
||||
record, _ := cronjobRepo.RecordFirst(cronjob.ID)
|
||||
if record.ID != 0 {
|
||||
item.LastRecrodTime = record.StartTime.Format("2006-01-02 15:04:05")
|
||||
} else {
|
||||
item.LastRecrodTime = "-"
|
||||
}
|
||||
dtoCronjobs = append(dtoCronjobs, item)
|
||||
}
|
||||
return total, dtoCronjobs, err
|
||||
@ -184,15 +181,8 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob) error {
|
||||
|
||||
func (u *CronjobService) Delete(ids []uint) error {
|
||||
if len(ids) == 1 {
|
||||
cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(ids[0]))
|
||||
if cronjob.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(ids[0])))
|
||||
|
||||
if err := os.RemoveAll(fmt.Sprintf("%s/%s/%s-%v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID)); err != nil {
|
||||
global.LOG.Errorf("rm file %s/%s/%s-%v failed, err: %v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID, err)
|
||||
if err := u.HandleDelete(ids[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
return cronjobRepo.Delete(commonRepo.WithByID(ids[0]))
|
||||
}
|
||||
@ -201,24 +191,37 @@ func (u *CronjobService) Delete(ids []uint) error {
|
||||
return err
|
||||
}
|
||||
for i := range cronjobs {
|
||||
global.Cron.Remove(cron.EntryID(cronjobs[i].EntryID))
|
||||
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(cronjobs[i].ID)))
|
||||
if err := os.RemoveAll(fmt.Sprintf("%s/%s/%s-%v", constant.TaskDir, cronjobs[i].Type, cronjobs[i].Name, cronjobs[i].ID)); err != nil {
|
||||
global.LOG.Errorf("rm file %s/%s/%s-%v failed, err: %v", constant.TaskDir, cronjobs[i].Type, cronjobs[i].Name, cronjobs[i].ID, err)
|
||||
}
|
||||
_ = u.HandleDelete(ids[i])
|
||||
}
|
||||
return cronjobRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||
}
|
||||
|
||||
func (u *CronjobService) Save(id uint, req dto.CronjobUpdate) error {
|
||||
func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
||||
var cronjob model.Cronjob
|
||||
if err := copier.Copy(&cronjob, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
cronjob.Spec = loadSpec(cronjob)
|
||||
if err := u.StartJob(&cronjob); err != nil {
|
||||
return err
|
||||
}
|
||||
return cronjobRepo.Save(id, cronjob)
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["name"] = req.Name
|
||||
upMap["script"] = req.Script
|
||||
upMap["spec_type"] = req.SpecType
|
||||
upMap["week"] = req.Week
|
||||
upMap["day"] = req.Day
|
||||
upMap["hour"] = req.Hour
|
||||
upMap["minute"] = req.Minute
|
||||
upMap["website"] = req.Website
|
||||
upMap["exclusion_rules"] = req.ExclusionRules
|
||||
upMap["database"] = req.Database
|
||||
upMap["url"] = req.URL
|
||||
upMap["source_dir"] = req.SourceDir
|
||||
upMap["target_dir_id"] = req.TargetDirID
|
||||
upMap["retain_days"] = req.RetainCopies
|
||||
return cronjobRepo.Update(id, upMap)
|
||||
}
|
||||
|
||||
func (u *CronjobService) UpdateStatus(id uint, status string) error {
|
||||
@ -248,55 +251,6 @@ func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
|
||||
return int(entryID), nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
var (
|
||||
message []byte
|
||||
err error
|
||||
)
|
||||
record := cronjobRepo.StartRecords(cronjob.ID, "")
|
||||
switch cronjob.Type {
|
||||
case "shell":
|
||||
cmd := exec.Command(cronjob.Script)
|
||||
message, err = cmd.CombinedOutput()
|
||||
case "website":
|
||||
message, err = tarWithExclude(cronjob, record.StartTime)
|
||||
case "database":
|
||||
message, err = tarWithExclude(cronjob, record.StartTime)
|
||||
case "directory":
|
||||
if len(cronjob.SourceDir) == 0 {
|
||||
return
|
||||
}
|
||||
message, err = tarWithExclude(cronjob, record.StartTime)
|
||||
case "curl":
|
||||
if len(cronjob.URL) == 0 {
|
||||
return
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client := &http.Client{Timeout: 1 * time.Second, Transport: tr}
|
||||
request, _ := http.NewRequest("GET", cronjob.URL, nil)
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
record.Records = errHandle
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
message, _ = ioutil.ReadAll(response.Body)
|
||||
}
|
||||
if err != nil {
|
||||
record.Records = errHandle
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
|
||||
return
|
||||
}
|
||||
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, message)
|
||||
if err != nil {
|
||||
record.Records = errRecord
|
||||
global.LOG.Errorf("save file %s failed, err: %v", record.Records, err)
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||
}
|
||||
|
||||
func mkdirAndWriteFile(cronjob *model.Cronjob, startTime time.Time, msg []byte) (string, error) {
|
||||
dir := fmt.Sprintf("%s%s/%s-%v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID)
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
@ -317,143 +271,6 @@ func mkdirAndWriteFile(cronjob *model.Cronjob, startTime time.Time, msg []byte)
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func tarWithExclude(cronjob *model.Cronjob, startTime time.Time) ([]byte, error) {
|
||||
varMaps, targetdir, err := loadTargetInfo(cronjob)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load target dir failed, err: %v", err)
|
||||
}
|
||||
|
||||
exStr := []string{}
|
||||
name := ""
|
||||
if cronjob.Type == "database" {
|
||||
exStr = append(exStr, "-zvPf")
|
||||
name = fmt.Sprintf("%s/%s.gz", targetdir, startTime.Format("20060102150405"))
|
||||
exStr = append(exStr, name)
|
||||
} else {
|
||||
exStr = append(exStr, "-zcvPf")
|
||||
name = fmt.Sprintf("%s/%s.tar.gz", targetdir, startTime.Format("20060102150405"))
|
||||
exStr = append(exStr, name)
|
||||
excludes := strings.Split(cronjob.ExclusionRules, ";")
|
||||
for _, exclude := range excludes {
|
||||
exStr = append(exStr, "--exclude")
|
||||
exStr = append(exStr, exclude)
|
||||
}
|
||||
}
|
||||
exStr = append(exStr, cronjob.SourceDir)
|
||||
cmd := exec.Command("tar", exStr...)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tar zcPf failed, err: %v", err)
|
||||
}
|
||||
|
||||
var backClient cloud_storage.CloudStorageClient
|
||||
if varMaps["type"] != "LOCAL" {
|
||||
backClient, err = cloud_storage.NewCloudStorageClient(varMaps)
|
||||
if err != nil {
|
||||
return stdout, fmt.Errorf("new cloud storage client failed, err: %v", err)
|
||||
}
|
||||
isOK, err := backClient.Upload(name, strings.Replace(name, constant.TmpDir, "", -1))
|
||||
if !isOK {
|
||||
return nil, fmt.Errorf("cloud storage upload failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
if backType, ok := varMaps["type"].(string); ok {
|
||||
rmExpiredRecords(backType, targetdir, cronjob, backClient)
|
||||
}
|
||||
return stdout, nil
|
||||
}
|
||||
|
||||
func loadTargetInfo(cronjob *model.Cronjob) (map[string]interface{}, string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
dir := ""
|
||||
varMap["type"] = backup.Type
|
||||
if backup.Type != "LOCAL" {
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp:
|
||||
varMap["password"] = backup.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo:
|
||||
varMap["secretKey"] = backup.Credential
|
||||
}
|
||||
dir = fmt.Sprintf("%s%s/%s", constant.TmpDir, cronjob.Type, cronjob.Name)
|
||||
} else {
|
||||
if _, ok := varMap["dir"]; !ok {
|
||||
return nil, "", errors.New("load local backup dir failed")
|
||||
}
|
||||
dir = fmt.Sprintf("%v/%s/%s", varMap["dir"], cronjob.Type, cronjob.Name)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("mkdir %s failed, err: %v", dir, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return varMap, dir, nil
|
||||
}
|
||||
|
||||
func rmExpiredRecords(backType, path string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
|
||||
timeNow := time.Now()
|
||||
timeZero := time.Date(timeNow.Year(), timeNow.Month(), timeNow.Day(), 0, 0, 0, 0, timeNow.Location())
|
||||
timeStart := timeZero.AddDate(0, 0, -int(cronjob.RetainDays)+1)
|
||||
var timePrefixs []string
|
||||
for i := 0; i < int(cronjob.RetainDays); i++ {
|
||||
timePrefixs = append(timePrefixs, timeZero.AddDate(0, 0, i).Format("20060102"))
|
||||
}
|
||||
if backType != "LOCAL" {
|
||||
dir := fmt.Sprintf("%s/%s/", cronjob.Type, cronjob.Name)
|
||||
currentObjs, err := backClient.ListObjects(dir)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("list bucket object %s failed, err: %v", dir, err)
|
||||
return
|
||||
}
|
||||
for _, obj := range currentObjs {
|
||||
objKey, ok := obj.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
objKey = strings.ReplaceAll(objKey, dir, "")
|
||||
isOk := false
|
||||
for _, pre := range timePrefixs {
|
||||
if strings.HasPrefix(objKey, pre) {
|
||||
isOk = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isOk {
|
||||
_, _ = backClient.Delete(objKey)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
files, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("read dir %s failed, err: %v", path, err)
|
||||
return
|
||||
}
|
||||
for _, file := range files {
|
||||
isOk := false
|
||||
for _, pre := range timePrefixs {
|
||||
if strings.HasPrefix(file.Name(), pre) {
|
||||
isOk = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isOk {
|
||||
_ = os.Remove(path + "/" + file.Name())
|
||||
}
|
||||
}
|
||||
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), cronjobRepo.WithByStartDate(timeStart))
|
||||
}
|
||||
|
||||
func loadSpec(cronjob model.Cronjob) string {
|
||||
switch cronjob.SpecType {
|
||||
case "perMonth":
|
||||
|
192
backend/app/service/cronjob_helper.go
Normal file
192
backend/app/service/cronjob_helper.go
Normal file
@ -0,0 +1,192 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/1Panel-dev/1Panel/global"
|
||||
"github.com/1Panel-dev/1Panel/utils/cloud_storage"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
var (
|
||||
message []byte
|
||||
err error
|
||||
)
|
||||
record := cronjobRepo.StartRecords(cronjob.ID, "")
|
||||
switch cronjob.Type {
|
||||
case "shell":
|
||||
cmd := exec.Command(cronjob.Script)
|
||||
message, err = cmd.CombinedOutput()
|
||||
case "website":
|
||||
message, err = u.HandleBackup(cronjob, record.StartTime)
|
||||
case "database":
|
||||
message, err = u.HandleBackup(cronjob, record.StartTime)
|
||||
case "directory":
|
||||
if len(cronjob.SourceDir) == 0 {
|
||||
return
|
||||
}
|
||||
message, err = u.HandleBackup(cronjob, record.StartTime)
|
||||
case "curl":
|
||||
if len(cronjob.URL) == 0 {
|
||||
return
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client := &http.Client{Timeout: 1 * time.Second, Transport: tr}
|
||||
request, _ := http.NewRequest("GET", cronjob.URL, nil)
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
||||
}
|
||||
defer response.Body.Close()
|
||||
message, _ = ioutil.ReadAll(response.Body)
|
||||
}
|
||||
if err != nil {
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
||||
return
|
||||
}
|
||||
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, message)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("save file %s failed, err: %v", record.Records, err)
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Time) ([]byte, error) {
|
||||
var stdout []byte
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commonDir := fmt.Sprintf("%s/%s/", cronjob.Type, cronjob.Name)
|
||||
name := fmt.Sprintf("%s.gz", startTime.Format("20060102150405"))
|
||||
if cronjob.Type != "database" {
|
||||
name = fmt.Sprintf("%s.tar.gz", startTime.Format("20060102150405"))
|
||||
}
|
||||
if backup.Type == "LOCAL" {
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := varMap["dir"]; !ok {
|
||||
return nil, errors.New("load local backup dir failed")
|
||||
}
|
||||
baseDir := varMap["dir"].(string)
|
||||
if _, err := os.Stat(baseDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(baseDir, os.ModePerm); err != nil {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mkdir %s failed, err: %v", baseDir, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
stdout, err = handleTar(cronjob.SourceDir, fmt.Sprintf("%s/%s", baseDir, commonDir), name, cronjob.ExclusionRules)
|
||||
if err != nil {
|
||||
return stdout, err
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, fmt.Sprintf("%s/%s", baseDir, commonDir), cronjob, nil)
|
||||
return stdout, nil
|
||||
}
|
||||
targetDir := constant.TmpDir + commonDir
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cronjob.Type != "database" {
|
||||
stdout, err = handleTar(cronjob.SourceDir, targetDir, name, cronjob.ExclusionRules)
|
||||
if err != nil {
|
||||
return stdout, err
|
||||
}
|
||||
}
|
||||
if _, err = client.Upload(targetDir+name, commonDir+name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.HandleRmExpired(backup.Type, commonDir+name, cronjob, client)
|
||||
return stdout, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleDelete(id uint) error {
|
||||
cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(id))
|
||||
if cronjob.ID == 0 {
|
||||
return errors.New("find cronjob in db failed")
|
||||
}
|
||||
commonDir := fmt.Sprintf("%s/%s/", cronjob.Type, cronjob.Name)
|
||||
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(id)))
|
||||
|
||||
if err := os.RemoveAll(fmt.Sprintf("%s/%s-%v", constant.TaskDir, commonDir, cronjob.ID)); err != nil {
|
||||
global.LOG.Errorf("rm file %s/%s-%v failed, err: %v", constant.TaskDir, commonDir, cronjob.ID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) HandleRmExpired(backType, path string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
|
||||
if backType != "LOCAL" {
|
||||
commonDir := fmt.Sprintf("%s/%s/", cronjob.Type, cronjob.Name)
|
||||
currentObjs, err := backClient.ListObjects(commonDir)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("list bucket object %s failed, err: %v", commonDir, err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(currentObjs)-int(cronjob.RetainCopies); i++ {
|
||||
_, _ = backClient.Delete(currentObjs[i].(string))
|
||||
}
|
||||
return
|
||||
}
|
||||
files, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("read dir %s failed, err: %v", path, err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(files)-int(cronjob.RetainCopies); i++ {
|
||||
_ = os.Remove(path + "/" + files[i].Name())
|
||||
}
|
||||
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)))
|
||||
if len(records) > int(cronjob.RetainCopies) {
|
||||
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
||||
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(records[i].ID)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleTar(sourceDir, targetDir, name, exclusionRules string) ([]byte, error) {
|
||||
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
exStr := []string{}
|
||||
exStr = append(exStr, "zcvf")
|
||||
exStr = append(exStr, targetDir+name)
|
||||
excludes := strings.Split(exclusionRules, ";")
|
||||
for _, exclude := range excludes {
|
||||
if len(exclude) == 0 {
|
||||
continue
|
||||
}
|
||||
exStr = append(exStr, "--exclude")
|
||||
exStr = append(exStr, exclude)
|
||||
}
|
||||
if len(strings.Split(sourceDir, "/")) > 3 {
|
||||
exStr = append(exStr, "-C")
|
||||
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
|
||||
aheadDir := strings.ReplaceAll(sourceDir, itemDir, "")
|
||||
exStr = append(exStr, aheadDir)
|
||||
exStr = append(exStr, itemDir)
|
||||
} else {
|
||||
exStr = append(exStr, sourceDir)
|
||||
}
|
||||
cmd := exec.Command("tar", exStr...)
|
||||
return (cmd.CombinedOutput())
|
||||
}
|
@ -18,6 +18,7 @@ type ISettingService interface {
|
||||
GetSettingInfo() (*dto.SettingInfo, error)
|
||||
Update(c *gin.Context, key, value string) error
|
||||
UpdatePassword(c *gin.Context, old, new string) error
|
||||
HandlePasswordExpired(c *gin.Context, old, new string) error
|
||||
}
|
||||
|
||||
func NewISettingService() ISettingService {
|
||||
@ -47,13 +48,20 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
|
||||
}
|
||||
|
||||
func (u *SettingService) Update(c *gin.Context, key, value string) error {
|
||||
if key == "ExpirationDays" {
|
||||
timeout, _ := strconv.Atoi(value)
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006.01.02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
c.SetCookie(constant.PasswordExpiredName, encrypt.Md5(time.Now().Format("20060102150405")), 86400*timeout, "", "", false, false)
|
||||
}
|
||||
if err := settingRepo.Update(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
return settingRepo.Update(key, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
|
||||
func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error {
|
||||
setting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -70,15 +78,32 @@ func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
|
||||
if err := settingRepo.Update("Password", newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
sID, _ := c.Cookie(constant.SessionName)
|
||||
if sID != "" {
|
||||
c.SetCookie(constant.SessionName, sID, -1, "", "", false, false)
|
||||
err := global.SESSION.Delete(sID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expiredSetting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeout, _ := strconv.Atoi(expiredSetting.Value)
|
||||
c.SetCookie(constant.PasswordExpiredName, encrypt.Md5(time.Now().Format("20060102150405")), 86400*timeout, "", "", false, false)
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006.01.02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return constant.ErrInitialPassword
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
|
||||
if err := u.HandlePasswordExpired(c, old, new); err != nil {
|
||||
return err
|
||||
}
|
||||
sID, _ := c.Cookie(constant.SessionName)
|
||||
if sID != "" {
|
||||
c.SetCookie(constant.SessionName, sID, -1, "", "", false, false)
|
||||
err := global.SESSION.Delete(sID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ const (
|
||||
CodeErrUnSafety = 402
|
||||
CodeErrForbidden = 403
|
||||
CodeErrNotFound = 404
|
||||
CodePasswordExpired = 405
|
||||
CodeErrInternalServer = 500
|
||||
CodeErrHeader = 406
|
||||
)
|
||||
@ -32,10 +33,11 @@ var (
|
||||
|
||||
// api
|
||||
var (
|
||||
ErrTypeInternalServer = "ErrInternalServer"
|
||||
ErrTypeInvalidParams = "ErrInvalidParams"
|
||||
ErrTypeToken = "ErrToken"
|
||||
ErrTypeTokenTimeOut = "ErrTokenTimeOut"
|
||||
ErrTypeNotLogin = "ErrNotLogin"
|
||||
ErrTypeNotSafety = "ErrNotSafety"
|
||||
ErrTypeInternalServer = "ErrInternalServer"
|
||||
ErrTypeInvalidParams = "ErrInvalidParams"
|
||||
ErrTypeToken = "ErrToken"
|
||||
ErrTypeTokenTimeOut = "ErrTokenTimeOut"
|
||||
ErrTypeNotLogin = "ErrNotLogin"
|
||||
ErrTypePasswordExpired = "ErrPasswordExpired"
|
||||
ErrTypeNotSafety = "ErrNotSafety"
|
||||
)
|
||||
|
@ -9,4 +9,6 @@ const (
|
||||
JWTSigningKey = "1panelKey"
|
||||
JWTBufferTime = 86400
|
||||
JWTIssuer = "1Panel"
|
||||
|
||||
PasswordExpiredName = "expired"
|
||||
)
|
||||
|
@ -10,4 +10,5 @@ ErrRecordNotFound: "Records not found: {{ .detail }}"
|
||||
ErrStructTransform: "Type conversion failure: {{ .detail }}"
|
||||
ErrNotLogin: "User is not Login: {{ .detail }}"
|
||||
ErrNotSafety: "The login status of the current user is unsafe: {{ .detail }}"
|
||||
ErrPasswordExpired: "The current password has expired: {{ .detail }}"
|
||||
ErrNotSupportType: "The system does not support the current type: {{ .detail }}"
|
@ -10,4 +10,5 @@ ErrRecordNotFound: "记录未能找到: {{ .detail }}"
|
||||
ErrStructTransform: "类型转换失败: {{ .detail }}"
|
||||
ErrNotLogin: "用户未登录: {{ .detail }}"
|
||||
ErrNotSafety: "当前用户登录状态不安全: {{ .detail }}"
|
||||
ErrPasswordExpired: "当前密码已过期: {{ .detail }}"
|
||||
ErrNotSupportType: "系统暂不支持当前类型: {{ .detail }}"
|
@ -54,7 +54,7 @@ var AddTableSetting = &gormigrate.Migration{
|
||||
if err := tx.Create(&model.Setting{Key: "UserName", Value: "admin"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "Password", Value: "5WYEZ4XcitdomVvAyimt9WwJwBJJSbTTHncZoqyOraQ="}).Error; err != nil {
|
||||
if err := tx.Create(&model.Setting{Key: "Password", Value: "Sr2qOhssQNg8rGRvqyWhsBDJx+tV5VfLEZXdbax//dA="}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "Email", Value: ""}).Error; err != nil {
|
||||
@ -84,7 +84,10 @@ var AddTableSetting = &gormigrate.Migration{
|
||||
if err := tx.Create(&model.Setting{Key: "SecurityEntrance", Value: "onepanel"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "PasswordTimeOut", Value: time.Now().AddDate(0, 0, 10).Format("2016.01.02 15:04:05")}).Error; err != nil {
|
||||
if err := tx.Create(&model.Setting{Key: "ExpirationTime", Value: time.Now().AddDate(0, 0, 10).Format("2006.01.02 15:04:05")}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "ExpirationDays", Value: "10"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "ComplexityVerification", Value: "enable"}).Error; err != nil {
|
||||
|
18
backend/middleware/password_expired.go
Normal file
18
backend/middleware/password_expired.go
Normal file
@ -0,0 +1,18 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PasswordExpired() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
_, err := c.Cookie(constant.PasswordExpiredName)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, nil)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ type OperationLogRouter struct{}
|
||||
|
||||
func (s *OperationLogRouter) InitOperationLogRouter(Router *gin.RouterGroup) {
|
||||
operationRouter := Router.Group("operations")
|
||||
operationRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
operationRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.PasswordExpired())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
operationRouter.POST("", baseApi.GetOperationList)
|
||||
|
@ -10,8 +10,15 @@ import (
|
||||
type BackupRouter struct{}
|
||||
|
||||
func (s *BackupRouter) InitBackupRouter(Router *gin.RouterGroup) {
|
||||
baRouter := Router.Group("backups").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := Router.Group("backups").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
|
||||
baRouter := Router.Group("backups").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
withRecordRouter := Router.Group("backups").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired()).
|
||||
Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
baRouter.GET("/search", baseApi.ListBackup)
|
||||
|
@ -10,8 +10,15 @@ import (
|
||||
type CommandRouter struct{}
|
||||
|
||||
func (s *CommandRouter) InitCommandRouter(Router *gin.RouterGroup) {
|
||||
cmdRouter := Router.Group("commands").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := Router.Group("commands").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
|
||||
cmdRouter := Router.Group("commands").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
withRecordRouter := Router.Group("commands").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired()).
|
||||
Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
withRecordRouter.POST("", baseApi.CreateCommand)
|
||||
|
@ -10,8 +10,15 @@ import (
|
||||
type CronjobRouter struct{}
|
||||
|
||||
func (s *CronjobRouter) InitCronjobRouter(Router *gin.RouterGroup) {
|
||||
cmdRouter := Router.Group("cronjobs").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := Router.Group("cronjobs").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
|
||||
cmdRouter := Router.Group("cronjobs").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
withRecordRouter := Router.Group("cronjobs").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired()).
|
||||
Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
withRecordRouter.POST("", baseApi.CreateCronjob)
|
||||
|
@ -11,7 +11,7 @@ type FileRouter struct {
|
||||
|
||||
func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
|
||||
fileRouter := Router.Group("files")
|
||||
fileRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
fileRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.PasswordExpired())
|
||||
//withRecordRouter := fileRouter.Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
|
@ -10,8 +10,15 @@ import (
|
||||
type GroupRouter struct{}
|
||||
|
||||
func (s *GroupRouter) InitGroupRouter(Router *gin.RouterGroup) {
|
||||
groupRouter := Router.Group("groups").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := Router.Group("groups").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
|
||||
groupRouter := Router.Group("groups").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
withRecordRouter := Router.Group("groups").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired()).
|
||||
Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
withRecordRouter.POST("", baseApi.CreateGroup)
|
||||
|
@ -10,8 +10,15 @@ import (
|
||||
type HostRouter struct{}
|
||||
|
||||
func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
|
||||
hostRouter := Router.Group("hosts").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := Router.Group("hosts").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
|
||||
hostRouter := Router.Group("hosts").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
withRecordRouter := Router.Group("hosts").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired()).
|
||||
Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
withRecordRouter.POST("", baseApi.CreateHost)
|
||||
|
@ -10,7 +10,10 @@ import (
|
||||
type MonitorRouter struct{}
|
||||
|
||||
func (s *MonitorRouter) InitMonitorRouter(Router *gin.RouterGroup) {
|
||||
monitorRouter := Router.Group("monitors").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
monitorRouter := Router.Group("monitors").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
monitorRouter.POST("/search", baseApi.LoadMonitor)
|
||||
|
@ -9,11 +9,20 @@ import (
|
||||
type SettingRouter struct{}
|
||||
|
||||
func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
|
||||
settingRouter := Router.Group("settings").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := Router.Group("settings").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
|
||||
baseRouter := Router.Group("settings")
|
||||
settingRouter := Router.Group("settings").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
withRecordRouter := Router.Group("settings").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired()).
|
||||
Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
settingRouter.POST("/search", baseApi.GetSettingInfo)
|
||||
baseRouter.POST("/search", baseApi.GetSettingInfo)
|
||||
baseRouter.PUT("/expired/handle", baseApi.HandlePasswordExpired)
|
||||
withRecordRouter.PUT("", baseApi.UpdateSetting)
|
||||
settingRouter.PUT("/password", baseApi.UpdatePassword)
|
||||
settingRouter.POST("/time/sync", baseApi.SyncTime)
|
||||
|
@ -10,7 +10,10 @@ import (
|
||||
type TerminalRouter struct{}
|
||||
|
||||
func (s *TerminalRouter) InitTerminalRouter(Router *gin.RouterGroup) {
|
||||
terminalRouter := Router.Group("terminals").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
terminalRouter := Router.Group("terminals").
|
||||
Use(middleware.JwtAuth()).
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
terminalRouter.GET("", baseApi.WsSsh)
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
|
||||
func TestStringEncrypt(t *testing.T) {
|
||||
viper.Init()
|
||||
p, err := StringEncrypt("Songliu123++")
|
||||
p, err := StringEncrypt("1Panel@2022")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -18,7 +18,7 @@ func TestStringEncrypt(t *testing.T) {
|
||||
|
||||
func TestStringDecrypt(t *testing.T) {
|
||||
viper.Init()
|
||||
p, err := StringDecrypt("Jmg4EUACGznt3dEQTJ+0ZRxwLaVNsNg7R5RcZ0V7ElQ=")
|
||||
p, err := StringDecrypt("dXn5bVtea+KVLDrLJlpnPIJNfW8TAMmqX1QNMdSGp88=")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -2,13 +2,14 @@ package files
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/afero"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
type FileInfo struct {
|
||||
@ -92,7 +93,7 @@ func (f *FileInfo) listChildren(dir, showHidden bool, page, pageSize int) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.ItemTotal = len(files)
|
||||
f.ItemTotal = 0
|
||||
|
||||
var items []*FileInfo
|
||||
for _, df := range files {
|
||||
@ -106,6 +107,7 @@ func (f *FileInfo) listChildren(dir, showHidden bool, page, pageSize int) error
|
||||
if !showHidden && IsHidden(name) {
|
||||
continue
|
||||
}
|
||||
f.ItemTotal++
|
||||
|
||||
isSymlink, isInvalidLink := false, false
|
||||
if IsSymlink(df.Mode()) {
|
||||
|
@ -55,6 +55,10 @@ class RequestHttp {
|
||||
});
|
||||
return data;
|
||||
}
|
||||
if (data.code == ResultEnum.EXPIRED) {
|
||||
router.push({ name: 'Expired' });
|
||||
return data;
|
||||
}
|
||||
if (data.code && data.code !== ResultEnum.SUCCESS) {
|
||||
ElMessage.error(data.msg);
|
||||
return Promise.reject(data);
|
||||
|
@ -19,7 +19,7 @@ export namespace Cronjob {
|
||||
sourceDir: string;
|
||||
targetDirID: number;
|
||||
targetDir: string;
|
||||
retainDays: number;
|
||||
retainCopies: number;
|
||||
status: string;
|
||||
}
|
||||
export interface CronjobCreate {
|
||||
@ -38,7 +38,7 @@ export namespace Cronjob {
|
||||
url: string;
|
||||
sourceDir: string;
|
||||
targetDirID: number;
|
||||
retainDays: number;
|
||||
retainCopies: number;
|
||||
}
|
||||
export interface CronjobUpdate {
|
||||
id: number;
|
||||
@ -55,7 +55,7 @@ export namespace Cronjob {
|
||||
url: string;
|
||||
sourceDir: string;
|
||||
targetDirID: number;
|
||||
retainDays: number;
|
||||
retainCopies: number;
|
||||
}
|
||||
export interface UpdateStatus {
|
||||
id: number;
|
||||
|
@ -13,7 +13,7 @@ export namespace Setting {
|
||||
|
||||
serverPort: number;
|
||||
securityEntrance: string;
|
||||
passwordTimeOut: string;
|
||||
expirationTime: string;
|
||||
complexityVerification: string;
|
||||
mfaStatus: string;
|
||||
mfaSecret: string;
|
||||
|
@ -13,6 +13,10 @@ export const updatePassword = (param: Setting.PasswordUpdate) => {
|
||||
return http.put(`/settings/password`, param);
|
||||
};
|
||||
|
||||
export const handleExpired = (param: Setting.PasswordUpdate) => {
|
||||
return http.put(`/settings/expired/handle`, param);
|
||||
};
|
||||
|
||||
export const syncTime = () => {
|
||||
return http.post(`/settings/time/sync`, {});
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ export enum ResultEnum {
|
||||
OVERDUE = 401,
|
||||
UNSAFETY = 402,
|
||||
FORBIDDEN = 403,
|
||||
EXPIRED = 405,
|
||||
TIMEOUT = 100000,
|
||||
TYPE = 'success',
|
||||
}
|
||||
|
@ -151,6 +151,8 @@ export default {
|
||||
taskType: 'Task type',
|
||||
shell: 'shell',
|
||||
website: 'website',
|
||||
rulesHelper: 'Compression exclusion rules (with; Is a delimiter), for example: \n*.log; *.sql',
|
||||
lastRecrodTime: 'Last execution time',
|
||||
failedFilter: 'Failed Task Filtering',
|
||||
all: 'all',
|
||||
database: 'database',
|
||||
@ -165,7 +167,7 @@ export default {
|
||||
exclusionRules: 'Exclusive rule',
|
||||
url: 'URL Address',
|
||||
target: 'Target',
|
||||
retainDays: 'Retain days',
|
||||
retainCopies: 'Retain copies',
|
||||
cronSpecRule: 'Please enter a correct lifecycle',
|
||||
perMonth: 'Per monthly',
|
||||
perWeek: 'Per week',
|
||||
@ -339,6 +341,7 @@ export default {
|
||||
retryPassword: 'Confirm password',
|
||||
|
||||
backup: 'Backup',
|
||||
noTypeForCreate: 'No backup type is currently created',
|
||||
serverDisk: 'Server disks',
|
||||
OSS: 'Ali OSS',
|
||||
S3: 'Amazon S3',
|
||||
@ -359,7 +362,8 @@ export default {
|
||||
safeEntrance: 'Security entrance',
|
||||
safeEntranceHelper:
|
||||
'Panel management portal. You can log in to the panel only through a specified security portal, for example: onepanel',
|
||||
passwordTimeout: 'Expiration Time',
|
||||
expirationTime: 'Expiration Time',
|
||||
expiredHelper: 'The current password has expired. Please change the password again.',
|
||||
timeoutHelper:
|
||||
'[ {0} days ] The panel password is about to expire. After the expiration, you need to reset the password',
|
||||
complexity: 'Complexity verification',
|
||||
|
@ -148,7 +148,9 @@ export default {
|
||||
taskType: '任务类型',
|
||||
shell: 'Shell 脚本',
|
||||
website: '备份网站',
|
||||
rulesHelper: '压缩排除规则(以 ; 号为分隔符),例如: \n*.log;*.sql',
|
||||
failedFilter: '失败任务过滤',
|
||||
lastRecrodTime: '上次执行时间',
|
||||
all: '所有',
|
||||
database: '备份数据库',
|
||||
missBackupAccount: '未能找到备份账号',
|
||||
@ -162,7 +164,7 @@ export default {
|
||||
exclusionRules: '排除规则',
|
||||
url: 'URL 地址',
|
||||
target: '备份到',
|
||||
retainDays: '保留天数',
|
||||
retainCopies: '保留份数',
|
||||
cronSpecRule: '请输入正确的执行周期',
|
||||
perMonth: '每月',
|
||||
perWeek: '每周',
|
||||
@ -335,6 +337,7 @@ export default {
|
||||
retryPassword: '确认密码',
|
||||
|
||||
backup: '备份',
|
||||
noTypeForCreate: '当前无可创建备份类型',
|
||||
serverDisk: '服务器磁盘',
|
||||
OSS: '阿里云 OSS',
|
||||
S3: '亚马逊 S3 云存储',
|
||||
@ -353,7 +356,8 @@ export default {
|
||||
portHelper: '建议端口范围8888 - 65535,注意:有安全组的服务器请提前在安全组放行新端口',
|
||||
safeEntrance: '安全入口',
|
||||
safeEntranceHelper: '面板管理入口,设置后只能通过指定安全入口登录面板,如: onepanel',
|
||||
passwordTimeout: '密码过期时间',
|
||||
expirationTime: '密码过期时间',
|
||||
expiredHelper: '当前密码已过期,请重新修改密码:',
|
||||
timeoutHelper: '【 {0} 天后 】面板密码即将过期,过期后需要重新设置密码',
|
||||
complexity: '密码复杂度验证',
|
||||
complexityHelper: '密码必须满足密码长度大于 8 位且包含字母、数字及特殊字符',
|
||||
|
@ -19,6 +19,16 @@ const settingRouter = {
|
||||
key: 'Setting',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/expired',
|
||||
name: 'Expired',
|
||||
hidden: true,
|
||||
component: () => import('@/views/setting/expired.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
key: 'Expired',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -55,7 +55,12 @@
|
||||
{{ $t('cronjob.handle') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('cronjob.retainDays')" prop="retainDays" />
|
||||
<el-table-column :label="$t('cronjob.retainCopies')" prop="retainCopies" />
|
||||
<el-table-column :label="$t('cronjob.lastRecrodTime')" prop="lastRecrodTime">
|
||||
<template #default="{ row }">
|
||||
{{ row.lastRecrodTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('cronjob.target')" prop="targetDir">
|
||||
<template #default="{ row }">
|
||||
{{ loadBackupName(row.targetDir) }}
|
||||
@ -88,13 +93,13 @@ const switchState = ref<boolean>(false);
|
||||
const data = ref();
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const logSearch = reactive({
|
||||
page: 1,
|
||||
pageSize: 5,
|
||||
pageSize: 10,
|
||||
});
|
||||
const weekOptions = [
|
||||
{ label: i18n.global.t('cronjob.monday'), value: 1 },
|
||||
@ -133,7 +138,7 @@ const onOpenDialog = async (
|
||||
day: 1,
|
||||
hour: 2,
|
||||
minute: 3,
|
||||
retainDays: 7,
|
||||
retainCopies: 7,
|
||||
},
|
||||
) => {
|
||||
let params = {
|
||||
@ -195,7 +200,7 @@ const buttons = [
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.log'),
|
||||
label: i18n.global.t('commons.button.view'),
|
||||
icon: 'Clock',
|
||||
click: (row: Cronjob.CronjobInfo) => {
|
||||
onOpenRecordDialog(row);
|
||||
|
@ -108,8 +108,8 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isBackup()" :label="$t('cronjob.retainDays')" prop="retainDays">
|
||||
<el-input-number :min="1" :max="30" v-model.number="dialogData.rowData!.retainDays"></el-input-number>
|
||||
<el-form-item v-if="isBackup()" :label="$t('cronjob.retainCopies')" prop="retainCopies">
|
||||
<el-input-number :min="1" :max="30" v-model.number="dialogData.rowData!.retainCopies"></el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="dialogData.rowData!.type === 'curl'" :label="$t('cronjob.url') + 'URL'" prop="url">
|
||||
@ -124,6 +124,7 @@
|
||||
<el-input
|
||||
style="width: 100%"
|
||||
type="textarea"
|
||||
:placeholder="$t('cronjob.rulesHelper')"
|
||||
:autosize="{ minRows: 3, maxRows: 6 }"
|
||||
clearable
|
||||
v-model="dialogData.rowData!.exclusionRules"
|
||||
@ -265,7 +266,7 @@ const rules = reactive({
|
||||
url: [Rules.requiredInput],
|
||||
sourceDir: [Rules.requiredSelect],
|
||||
targetDirID: [Rules.requiredSelect, Rules.number],
|
||||
retainDays: [Rules.number],
|
||||
retainCopies: [Rules.number],
|
||||
});
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
|
@ -138,8 +138,8 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="isBackup()">
|
||||
<el-form-item :label="$t('cronjob.retainDays')">
|
||||
{{ dialogData.rowData!.retainDays }}
|
||||
<el-form-item :label="$t('cronjob.retainCopies')">
|
||||
{{ dialogData.rowData!.retainCopies }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="dialogData.rowData!.type === 'curl'">
|
||||
@ -152,9 +152,12 @@
|
||||
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'"
|
||||
>
|
||||
<el-form-item :label="$t('cronjob.exclusionRules')">
|
||||
<div v-for="item in dialogData.rowData!.exclusionRules.split(';')" :key="item">
|
||||
<el-tag>{{ item }}</el-tag>
|
||||
<div v-if="dialogData.rowData!.exclusionRules">
|
||||
<div v-for="item in dialogData.rowData!.exclusionRules.split(';')" :key="item">
|
||||
<el-tag>{{ item }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -191,30 +194,32 @@
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item :label="$t('commons.table.records')">
|
||||
<span
|
||||
style="color: red"
|
||||
v-if="currentRecord?.records! === 'errRecord' || currentRecord?.records! === 'errHandle'|| currentRecord?.records! === 'noRecord'"
|
||||
>
|
||||
<span style="color: red" v-if="currentRecord?.status! === 'Failed'">
|
||||
{{ currentRecord?.message }}
|
||||
</span>
|
||||
<el-popover
|
||||
v-else
|
||||
placement="right"
|
||||
:width="600"
|
||||
trigger="click"
|
||||
style="white-space: pre-wrap"
|
||||
>
|
||||
<div style="margin-left: 20px; max-height: 400px; overflow: auto">
|
||||
<span style="white-space: pre-wrap">
|
||||
{{ currentRecordDetail }}
|
||||
</span>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button type="primary" link @click="loadRecord(currentRecord?.records!)">
|
||||
{{ $t('commons.button.expand') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<div v-else>
|
||||
<el-popover
|
||||
placement="right"
|
||||
:width="600"
|
||||
trigger="click"
|
||||
style="white-space: pre-wrap"
|
||||
>
|
||||
<div style="margin-left: 20px; max-height: 400px; overflow: auto">
|
||||
<span style="white-space: pre-wrap">
|
||||
{{ currentRecordDetail }}
|
||||
</span>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="loadRecord(currentRecord?.records!)"
|
||||
>
|
||||
{{ $t('commons.button.expand') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
132
frontend/src/views/setting/expired.vue
Normal file
132
frontend/src/views/setting/expired.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card style="margin-top: 20px">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span style="font-size: 14px; font-weight: 500">当前密码已过期,请重新修改密码:</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-row>
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="10">
|
||||
<el-form
|
||||
:model="passForm"
|
||||
ref="passFormRef"
|
||||
:rules="passRules"
|
||||
label-position="left"
|
||||
label-width="160px"
|
||||
>
|
||||
<el-form-item :label="$t('setting.oldPassword')" prop="oldPassword">
|
||||
<el-input type="password" show-password clearable v-model="passForm.oldPassword" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="settingForm.complexityVerification === 'disable'"
|
||||
:label="$t('setting.newPassword')"
|
||||
prop="newPassword"
|
||||
>
|
||||
<el-input type="password" show-password clearable v-model="passForm.newPassword" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="settingForm.complexityVerification === 'enable'"
|
||||
:label="$t('setting.newPassword')"
|
||||
prop="newPasswordComplexity"
|
||||
>
|
||||
<el-input
|
||||
type="password"
|
||||
show-password
|
||||
clearable
|
||||
v-model="passForm.newPasswordComplexity"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('setting.retryPassword')" prop="retryPassword">
|
||||
<el-input type="password" show-password clearable v-model="passForm.retryPassword" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="submitChangePassword(passFormRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive } from 'vue';
|
||||
import { Setting } from '@/api/interface/setting';
|
||||
import { getSettingInfo, handleExpired } from '@/api/modules/setting';
|
||||
import { ElForm, ElMessage } from 'element-plus';
|
||||
import i18n from '@/lang';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import router from '@/routers';
|
||||
let settingForm = reactive<Setting.SettingInfo>({
|
||||
userName: '',
|
||||
password: '',
|
||||
email: '',
|
||||
sessionTimeout: 86400,
|
||||
localTime: '',
|
||||
panelName: '',
|
||||
theme: '',
|
||||
language: '',
|
||||
serverPort: 8888,
|
||||
securityEntrance: '',
|
||||
expirationTime: '',
|
||||
complexityVerification: 'enable',
|
||||
mfaStatus: '',
|
||||
mfaSecret: '',
|
||||
monitorStatus: '',
|
||||
monitorStoreDays: 30,
|
||||
messageType: '',
|
||||
emailVars: '',
|
||||
weChatVars: '',
|
||||
dingVars: '',
|
||||
});
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const passFormRef = ref<FormInstance>();
|
||||
const passRules = reactive({
|
||||
oldPassword: [Rules.requiredInput],
|
||||
newPassword: [
|
||||
Rules.requiredInput,
|
||||
{ min: 6, message: i18n.global.t('commons.rule.commonPassword'), trigger: 'blur' },
|
||||
],
|
||||
newPasswordComplexity: [Rules.requiredInput, Rules.password],
|
||||
retryPassword: [Rules.requiredInput, { validator: checkPassword, trigger: 'blur' }],
|
||||
});
|
||||
const passForm = reactive({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
newPasswordComplexity: '',
|
||||
retryPassword: '',
|
||||
});
|
||||
|
||||
function checkPassword(rule: any, value: any, callback: any) {
|
||||
let password =
|
||||
settingForm.complexityVerification === 'disable' ? passForm.newPassword : passForm.newPasswordComplexity;
|
||||
if (password !== passForm.retryPassword) {
|
||||
return callback(new Error(i18n.global.t('commons.rule.rePassword')));
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
const submitChangePassword = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
let password =
|
||||
settingForm.complexityVerification === 'disable' ? passForm.newPassword : passForm.newPasswordComplexity;
|
||||
await handleExpired({ oldPassword: passForm.oldPassword, newPassword: password });
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
router.push({ name: 'home' });
|
||||
});
|
||||
};
|
||||
const search = async () => {
|
||||
const res = await getSettingInfo();
|
||||
settingForm = res.data;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
</script>
|
@ -56,7 +56,7 @@ let form = ref<Setting.SettingInfo>({
|
||||
language: '',
|
||||
serverPort: 8888,
|
||||
securityEntrance: '',
|
||||
passwordTimeOut: '',
|
||||
expirationTime: '',
|
||||
complexityVerification: '',
|
||||
mfaStatus: '',
|
||||
mfaSecret: '',
|
||||
|
@ -198,6 +198,10 @@ const search = async () => {
|
||||
|
||||
const onCreate = () => {
|
||||
loadOption();
|
||||
if (!typeOptions.value || typeOptions.value.length === 0) {
|
||||
ElMessage.info(i18n.global.t('setting.noTypeForCreate'));
|
||||
return;
|
||||
}
|
||||
operation.value = 'create';
|
||||
form.id = 0;
|
||||
form.type = typeOptions.value[0].value;
|
||||
|
@ -63,11 +63,11 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('setting.passwordTimeout')"
|
||||
prop="settingInfo.passwordTimeOut"
|
||||
:label="$t('setting.expirationTime')"
|
||||
prop="settingInfo.expirationTime"
|
||||
:rules="Rules.requiredInput"
|
||||
>
|
||||
<el-input disabled v-model="form.settingInfo.passwordTimeOut">
|
||||
<el-input disabled v-model="form.settingInfo.expirationTime">
|
||||
<template #append>
|
||||
<el-button @click="timeoutVisiable = true" icon="Collection">
|
||||
{{ $t('commons.button.set') }}
|
||||
@ -184,7 +184,7 @@ const form = withDefaults(defineProps<Props>(), {
|
||||
settingInfo: {
|
||||
serverPort: '',
|
||||
securityEntrance: '',
|
||||
passwordTimeOut: '',
|
||||
ExpirationTime: '',
|
||||
complexityVerification: '',
|
||||
mfaStatus: '',
|
||||
mfaSecret: '',
|
||||
@ -237,14 +237,14 @@ const submitTimeout = async (formEl: FormInstance | undefined) => {
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
let time = new Date(new Date().getTime() + 3600 * 1000 * 24 * timeoutForm.days);
|
||||
await updateSetting({ key: 'PasswordTimeOut', value: dateFromat(0, 0, time) });
|
||||
form.settingInfo.passwordTimeOut = dateFromat(0, 0, time);
|
||||
await updateSetting({ key: 'ExpirationTime', value: dateFromat(0, 0, time) });
|
||||
form.settingInfo.ExpirationTime = dateFromat(0, 0, time);
|
||||
timeoutVisiable.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
function loadTimeOut() {
|
||||
let staytimeGap = new Date(form.settingInfo.passwordTimeOut).getTime() - new Date().getTime();
|
||||
let staytimeGap = new Date(form.settingInfo.ExpirationTime).getTime() - new Date().getTime();
|
||||
return Math.floor(staytimeGap / (3600 * 1000 * 24));
|
||||
}
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user