mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: merge from dev (#7216)
This commit is contained in:
parent
943cf2f9f9
commit
9b0916f0ed
@ -11,6 +11,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
@ -338,7 +339,11 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
|||||||
mode := info.Mode()
|
mode := info.Mode()
|
||||||
|
|
||||||
fileOp := files.NewFileOp()
|
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
|
success := 0
|
||||||
failures := make(buserr.MultiErr)
|
failures := make(buserr.MultiErr)
|
||||||
for _, file := range uploadFiles {
|
for _, file := range uploadFiles {
|
||||||
@ -351,6 +356,7 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
|||||||
global.LOG.Error(e)
|
global.LOG.Error(e)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
_ = os.Chown(dstDir, uid, gid)
|
||||||
}
|
}
|
||||||
tmpFilename := dstFilename + ".tmp"
|
tmpFilename := dstFilename + ".tmp"
|
||||||
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
|
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
|
||||||
@ -378,6 +384,9 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
|||||||
} else {
|
} else {
|
||||||
_ = os.Chmod(dstFilename, mode)
|
_ = os.Chmod(dstFilename, mode)
|
||||||
}
|
}
|
||||||
|
if uid != -1 && gid != -1 {
|
||||||
|
_ = os.Chown(dstFilename, uid, gid)
|
||||||
|
}
|
||||||
success++
|
success++
|
||||||
}
|
}
|
||||||
if success == 0 {
|
if success == 0 {
|
||||||
|
@ -10,6 +10,7 @@ type CommonBackup struct {
|
|||||||
DetailName string `json:"detailName"`
|
DetailName string `json:"detailName"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
TaskID string `json:"taskID"`
|
TaskID string `json:"taskID"`
|
||||||
|
FileName string `json:"fileName"`
|
||||||
}
|
}
|
||||||
type CommonRecover struct {
|
type CommonRecover struct {
|
||||||
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
||||||
|
@ -36,10 +36,11 @@ type FileWgetRes struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FileLineContent struct {
|
type FileLineContent struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
End bool `json:"end"`
|
End bool `json:"end"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Total int `json:"total"`
|
Total int `json:"total"`
|
||||||
|
Lines []string `json:"lines"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileExist struct {
|
type FileExist struct {
|
||||||
|
@ -16,8 +16,10 @@ type IBackupRepo interface {
|
|||||||
CreateRecord(record *model.BackupRecord) error
|
CreateRecord(record *model.BackupRecord) error
|
||||||
DeleteRecord(ctx context.Context, opts ...DBOption) error
|
DeleteRecord(ctx context.Context, opts ...DBOption) error
|
||||||
UpdateRecord(record *model.BackupRecord) error
|
UpdateRecord(record *model.BackupRecord) error
|
||||||
|
WithByDetailName(detailName string) DBOption
|
||||||
WithByFileName(fileName string) DBOption
|
WithByFileName(fileName string) DBOption
|
||||||
WithByCronID(cronjobID uint) DBOption
|
WithByCronID(cronjobID uint) DBOption
|
||||||
|
WithFileNameStartWith(filePrefix string) DBOption
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIBackupRepo() IBackupRepo {
|
func NewIBackupRepo() IBackupRepo {
|
||||||
@ -55,6 +57,21 @@ func (u *BackupRepo) WithByFileName(fileName string) DBOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *BackupRepo) WithByDetailName(detailName string) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
if len(detailName) == 0 {
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
return g.Where("detail_name = ?", detailName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *BackupRepo) WithFileNameStartWith(filePrefix string) DBOption {
|
||||||
|
return func(g *gorm.DB) *gorm.DB {
|
||||||
|
return g.Where("file_name LIKE ?", filePrefix+"%")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (u *BackupRepo) CreateRecord(record *model.BackupRecord) error {
|
func (u *BackupRepo) CreateRecord(record *model.BackupRecord) error {
|
||||||
return global.DB.Create(record).Error
|
return global.DB.Create(record).Error
|
||||||
}
|
}
|
||||||
|
@ -161,6 +161,9 @@ func (a AppService) GetAppTags() ([]response.TagDTO, error) {
|
|||||||
|
|
||||||
func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
||||||
var appDTO response.AppDTO
|
var appDTO response.AppDTO
|
||||||
|
if key == "postgres" {
|
||||||
|
key = "postgresql"
|
||||||
|
}
|
||||||
app, err := appRepo.GetFirst(appRepo.WithKey(key))
|
app, err := appRepo.GetFirst(appRepo.WithKey(key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -571,6 +571,12 @@ func (a *AppInstallService) GetUpdateVersions(req request.AppUpdateVersion) ([]d
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return versions, err
|
return versions, err
|
||||||
}
|
}
|
||||||
|
if app.Key == constant.AppMysql {
|
||||||
|
majorVersion := getMajorVersion(install.Version)
|
||||||
|
if !strings.HasPrefix(detail.Version, majorVersion) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
versions = append(versions, dto.AppVersion{
|
versions = append(versions, dto.AppVersion{
|
||||||
Version: detail.Version,
|
Version: detail.Version,
|
||||||
DetailId: detail.ID,
|
DetailId: detail.ID,
|
||||||
@ -740,7 +746,24 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if form.Type == "apps" {
|
||||||
|
if m, ok := form.Child.(map[string]interface{}); ok {
|
||||||
|
result := make(map[string]string)
|
||||||
|
for key, value := range m {
|
||||||
|
if strVal, ok := value.(string); ok {
|
||||||
|
result[key] = strVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if envKey, ok := result["envKey"]; ok {
|
||||||
|
serviceName := envs[envKey]
|
||||||
|
if serviceName != nil {
|
||||||
|
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(serviceName.(string)))
|
||||||
|
appParam.ShowValue = appInstall.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params = append(params, appParam)
|
params = append(params, appParam)
|
||||||
} else {
|
} else {
|
||||||
params = append(params, response.AppParam{
|
params = append(params, response.AppParam{
|
||||||
|
@ -199,17 +199,18 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
|||||||
}
|
}
|
||||||
case constant.AppRedis:
|
case constant.AppRedis:
|
||||||
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
|
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
|
||||||
if password != "" {
|
authParam := dto.RedisAuthParam{
|
||||||
authParam := dto.RedisAuthParam{
|
RootPassword: "",
|
||||||
RootPassword: password.(string),
|
|
||||||
}
|
|
||||||
authByte, err := json.Marshal(authParam)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
appInstall.Param = string(authByte)
|
|
||||||
}
|
}
|
||||||
database.Password = password.(string)
|
if password != "" {
|
||||||
|
authParam.RootPassword = password.(string)
|
||||||
|
database.Password = password.(string)
|
||||||
|
}
|
||||||
|
authByte, err := json.Marshal(authParam)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
appInstall.Param = string(authByte)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return databaseRepo.Create(ctx, database)
|
return databaseRepo.Create(ctx, database)
|
||||||
@ -558,11 +559,23 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||||||
)
|
)
|
||||||
backUpApp := func(t *task.Task) error {
|
backUpApp := func(t *task.Task) error {
|
||||||
if req.Backup {
|
if req.Backup {
|
||||||
backupRecord, err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name})
|
backupService := NewIBackupService()
|
||||||
if err != nil {
|
fileName := fmt.Sprintf("upgrade_backup_%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||||
|
backupRecord, err := backupService.AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name, FileName: fileName})
|
||||||
|
if err == nil {
|
||||||
|
backups, _ := backupService.ListAppRecords(install.App.Key, install.Name, "upgrade_backup")
|
||||||
|
if len(backups) > 3 {
|
||||||
|
backupsToDelete := backups[:len(backups)-3]
|
||||||
|
var deleteIDs []uint
|
||||||
|
for _, backup := range backupsToDelete {
|
||||||
|
deleteIDs = append(deleteIDs, backup.ID)
|
||||||
|
}
|
||||||
|
_ = backupService.BatchDeleteRecord(deleteIDs)
|
||||||
|
}
|
||||||
|
backupFile = path.Join(global.CONF.System.Backup, backupRecord.FileDir, backupRecord.FileName)
|
||||||
|
} else {
|
||||||
return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
|
return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
|
||||||
}
|
}
|
||||||
backupFile = path.Join(global.CONF.System.Backup, backupRecord.FileDir, backupRecord.FileName)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1390,7 +1403,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, installed := range appInstallList {
|
for _, installed := range appInstallList {
|
||||||
if updated && (installed.App.Type == "php" || installed.Status == constant.Installing || (installed.App.Key == constant.AppMysql && installed.Version == "5.6.51")) {
|
if updated && ignoreUpdate(installed) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if sync && !doNotNeedSync(installed) {
|
if sync && !doNotNeedSync(installed) {
|
||||||
@ -1691,3 +1704,28 @@ func isHostModel(dockerCompose string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMajorVersion(version string) string {
|
||||||
|
parts := strings.Split(version, ".")
|
||||||
|
if len(parts) >= 2 {
|
||||||
|
return parts[0] + "." + parts[1]
|
||||||
|
}
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
|
func ignoreUpdate(installed model.AppInstall) bool {
|
||||||
|
if installed.App.Type == "php" || installed.Status == constant.Installing {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if installed.App.Key == constant.AppMysql {
|
||||||
|
majorVersion := getMajorVersion(installed.Version)
|
||||||
|
appDetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID))
|
||||||
|
for _, appDetail := range appDetails {
|
||||||
|
if strings.HasPrefix(appDetail.Version, majorVersion) && common.CompareVersion(appDetail.Version, installed.Version) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -36,6 +36,7 @@ type IBackupService interface {
|
|||||||
DownloadRecord(info dto.DownloadRecord) (string, error)
|
DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||||
DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error
|
DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error
|
||||||
BatchDeleteRecord(ids []uint) error
|
BatchDeleteRecord(ids []uint) error
|
||||||
|
ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error)
|
||||||
|
|
||||||
ListFiles(req dto.OperateByID) []string
|
ListFiles(req dto.OperateByID) []string
|
||||||
|
|
||||||
@ -194,6 +195,20 @@ func (u *BackupService) BatchDeleteRecord(ids []uint) error {
|
|||||||
return backupRepo.DeleteRecord(context.Background(), commonRepo.WithByIDs(ids))
|
return backupRepo.DeleteRecord(context.Background(), commonRepo.WithByIDs(ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *BackupService) ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error) {
|
||||||
|
records, err := backupRepo.ListRecord(
|
||||||
|
commonRepo.WithOrderBy("created_at asc"),
|
||||||
|
commonRepo.WithByName(name),
|
||||||
|
commonRepo.WithByType("app"),
|
||||||
|
backupRepo.WithFileNameStartWith(fileName),
|
||||||
|
backupRepo.WithByDetailName(detailName),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return records, err
|
||||||
|
}
|
||||||
|
|
||||||
func (u *BackupService) ListFiles(req dto.OperateByID) []string {
|
func (u *BackupService) ListFiles(req dto.OperateByID) []string {
|
||||||
var datas []string
|
var datas []string
|
||||||
account, client, err := NewBackupClientWithID(req.ID)
|
account, client, err := NewBackupClientWithID(req.ID)
|
||||||
|
@ -38,7 +38,10 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, er
|
|||||||
itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName)
|
itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName)
|
||||||
backupDir := path.Join(global.CONF.System.Backup, itemDir)
|
backupDir := path.Join(global.CONF.System.Backup, itemDir)
|
||||||
|
|
||||||
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
fileName := req.FileName
|
||||||
|
if req.FileName == "" {
|
||||||
|
fileName = fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||||
|
}
|
||||||
|
|
||||||
backupApp := func() (*model.BackupRecord, error) {
|
backupApp := func() (*model.BackupRecord, error) {
|
||||||
if err = handleAppBackup(&install, nil, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
if err = handleAppBackup(&install, nil, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
||||||
|
@ -1087,6 +1087,29 @@ func calculateNetwork(network map[string]container.NetworkStats) (float64, float
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkImageExist(client *client.Client, imageItem string) bool {
|
func checkImageExist(client *client.Client, imageItem string) bool {
|
||||||
|
if client == nil {
|
||||||
|
var err error
|
||||||
|
client, err = docker.NewDockerClient()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
images, err := client.ImageList(context.Background(), image.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, img := range images {
|
||||||
|
for _, tag := range img.RepoTags {
|
||||||
|
if tag == imageItem || tag == imageItem+":latest" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkImage(client *client.Client, imageItem string) bool {
|
||||||
images, err := client.ImageList(context.Background(), image.ListOptions{})
|
images, err := client.ImageList(context.Background(), image.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
@ -467,11 +467,19 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if req.Latest && req.Page == 1 && len(lines) < 1000 && total > 1 {
|
||||||
|
preLines, _, _, err := files.ReadFileByLine(logFilePath, total-1, req.PageSize, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lines = append(preLines, lines...)
|
||||||
|
}
|
||||||
res := &response.FileLineContent{
|
res := &response.FileLineContent{
|
||||||
Content: strings.Join(lines, "\n"),
|
Content: strings.Join(lines, "\n"),
|
||||||
End: isEndOfFile,
|
End: isEndOfFile,
|
||||||
Path: logFilePath,
|
Path: logFilePath,
|
||||||
Total: total,
|
Total: total,
|
||||||
|
Lines: lines,
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
@ -378,6 +378,9 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
switch runtime.Type {
|
switch runtime.Type {
|
||||||
case constant.RuntimePHP:
|
case constant.RuntimePHP:
|
||||||
if runtime.Resource == constant.ResourceAppstore {
|
if runtime.Resource == constant.ResourceAppstore {
|
||||||
|
if !checkImageExist(nil, runtime.Image) {
|
||||||
|
return buserr.WithName("ErrImageNotExist", runtime.Name)
|
||||||
|
}
|
||||||
website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port)
|
website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port)
|
||||||
} else {
|
} else {
|
||||||
website.ProxyType = create.ProxyType
|
website.ProxyType = create.ProxyType
|
||||||
@ -1085,6 +1088,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
|||||||
websiteSSL.Provider = constant.Manual
|
websiteSSL.Provider = constant.Manual
|
||||||
websiteSSL.PrivateKey = privateKey
|
websiteSSL.PrivateKey = privateKey
|
||||||
websiteSSL.Pem = certificate
|
websiteSSL.Pem = certificate
|
||||||
|
websiteSSL.Status = constant.SSLReady
|
||||||
|
|
||||||
res.SSL = websiteSSL
|
res.SSL = websiteSSL
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,11 @@ location ^~ /test {
|
|||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header REMOTE-HOST $remote_addr;
|
proxy_set_header REMOTE-HOST $remote_addr;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection $http_connection;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
add_header X-Cache $upstream_cache_status;
|
add_header X-Cache $upstream_cache_status;
|
||||||
|
add_header Cache-Control no-cache;
|
||||||
|
proxy_ssl_server_name off;
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,7 @@ ErrDomainFormat: "{{ .name }} domain format error"
|
|||||||
ErrDefaultAlias: "default is a reserved code name, please use another code name"
|
ErrDefaultAlias: "default is a reserved code name, please use another code name"
|
||||||
ErrParentWebsite: "You need to delete the subsite(s) {{ .name }} first"
|
ErrParentWebsite: "You need to delete the subsite(s) {{ .name }} first"
|
||||||
ErrBuildDirNotFound: "Build directory does not exist"
|
ErrBuildDirNotFound: "Build directory does not exist"
|
||||||
|
ErrImageNotExist: "Running environment {{.name}} image does not exist, please re-edit the running environment"
|
||||||
|
|
||||||
#ssl
|
#ssl
|
||||||
ErrSSLCannotDelete: "The certificate {{ .name }} is being used by the website and cannot be removed"
|
ErrSSLCannotDelete: "The certificate {{ .name }} is being used by the website and cannot be removed"
|
||||||
|
@ -104,6 +104,7 @@ ErrDomainFormat: "{{ .name }} 域名格式不正確"
|
|||||||
ErrDefaultAlias: "default 為保留代號,請使用其他代號"
|
ErrDefaultAlias: "default 為保留代號,請使用其他代號"
|
||||||
ErrParentWebsite: "需要先刪除子網站 {{ .name }}"
|
ErrParentWebsite: "需要先刪除子網站 {{ .name }}"
|
||||||
ErrBuildDirNotFound: "編譯目錄不存在"
|
ErrBuildDirNotFound: "編譯目錄不存在"
|
||||||
|
ErrImageNotExist: "執行環境 {{.name}} 鏡像不存在,請重新編輯執行環境"
|
||||||
|
|
||||||
#ssl
|
#ssl
|
||||||
ErrSSLCannotDelete: "{{ .name }} 證書正在被網站使用,無法刪除"
|
ErrSSLCannotDelete: "{{ .name }} 證書正在被網站使用,無法刪除"
|
||||||
|
@ -103,6 +103,7 @@ ErrDomainFormat: "{{ .name }} 域名格式不正确"
|
|||||||
ErrDefaultAlias: "default 为保留代号,请使用其他代号"
|
ErrDefaultAlias: "default 为保留代号,请使用其他代号"
|
||||||
ErrParentWebsite: "需要先删除子网站 {{ .name }}"
|
ErrParentWebsite: "需要先删除子网站 {{ .name }}"
|
||||||
ErrBuildDirNotFound: "构建目录不存在"
|
ErrBuildDirNotFound: "构建目录不存在"
|
||||||
|
ErrImageNotExist: "运行环境 {{.name}} 镜像不存在,请重新编辑运行环境"
|
||||||
|
|
||||||
#ssl
|
#ssl
|
||||||
ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除"
|
ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除"
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"crypto"
|
"crypto"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
@ -296,8 +297,12 @@ func (c *AcmeClient) GetDNSResolve(domains []string) (map[string]Resolve, error)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
|
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
fqdn := challengeInfo.FQDN
|
||||||
|
if strings.HasPrefix(domain, "*.") && strings.Contains(fqdn, "*.") {
|
||||||
|
fqdn = strings.Replace(fqdn, "*.", "", 1)
|
||||||
|
}
|
||||||
resolves[domain] = Resolve{
|
resolves[domain] = Resolve{
|
||||||
Key: challengeInfo.FQDN,
|
Key: fqdn,
|
||||||
Value: challengeInfo.Value,
|
Value: challengeInfo.Value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div v-loading="isLoading">
|
||||||
<div v-if="defaultButton">
|
<div v-if="defaultButton">
|
||||||
<el-checkbox border v-model="tailLog" class="float-left" @change="changeTail(false)">
|
<el-checkbox border v-model="tailLog" class="float-left" @change="changeTail(false)">
|
||||||
{{ $t('commons.button.watch') }}
|
{{ $t('commons.button.watch') }}
|
||||||
@ -84,10 +84,11 @@ let timer: NodeJS.Timer | null = null;
|
|||||||
const tailLog = ref(false);
|
const tailLog = ref(false);
|
||||||
const content = ref('');
|
const content = ref('');
|
||||||
const end = ref(false);
|
const end = ref(false);
|
||||||
const lastContent = ref('');
|
|
||||||
const scrollerElement = ref<HTMLElement | null>(null);
|
const scrollerElement = ref<HTMLElement | null>(null);
|
||||||
const minPage = ref(1);
|
const minPage = ref(1);
|
||||||
const maxPage = ref(1);
|
const maxPage = ref(1);
|
||||||
|
const logs = ref([]);
|
||||||
|
const isLoading = ref(false);
|
||||||
|
|
||||||
const readReq = reactive({
|
const readReq = reactive({
|
||||||
id: 0,
|
id: 0,
|
||||||
@ -124,7 +125,12 @@ const stopSignals = [
|
|||||||
'image push successful!',
|
'image push successful!',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const lastLogs = ref([]);
|
||||||
|
|
||||||
const getContent = (pre: boolean) => {
|
const getContent = (pre: boolean) => {
|
||||||
|
if (isLoading.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
emit('update:isReading', true);
|
emit('update:isReading', true);
|
||||||
readReq.id = props.config.id;
|
readReq.id = props.config.id;
|
||||||
readReq.type = props.config.type;
|
readReq.type = props.config.type;
|
||||||
@ -132,38 +138,52 @@ const getContent = (pre: boolean) => {
|
|||||||
if (readReq.page < 1) {
|
if (readReq.page < 1) {
|
||||||
readReq.page = 1;
|
readReq.page = 1;
|
||||||
}
|
}
|
||||||
|
isLoading.value = true;
|
||||||
ReadByLine(readReq).then((res) => {
|
ReadByLine(readReq).then((res) => {
|
||||||
if (!end.value && res.data.end) {
|
if (!end.value && res.data.end) {
|
||||||
lastContent.value = content.value;
|
lastLogs.value = [...logs.value];
|
||||||
}
|
}
|
||||||
|
|
||||||
res.data.content = res.data.content.replace(/\\u(\w{4})/g, function (match, grp) {
|
|
||||||
return String.fromCharCode(parseInt(grp, 16));
|
|
||||||
});
|
|
||||||
data.value = res.data;
|
data.value = res.data;
|
||||||
if (res.data.content != '') {
|
if (res.data.lines && res.data.lines.length > 0) {
|
||||||
if (stopSignals.some((signal) => res.data.content.endsWith(signal))) {
|
res.data.lines = res.data.lines.map((line) =>
|
||||||
|
line.replace(/\\u(\w{4})/g, function (match, grp) {
|
||||||
|
return String.fromCharCode(parseInt(grp, 16));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const newLogs = res.data.lines;
|
||||||
|
if (newLogs.length === readReq.pageSize && readReq.page < res.data.total) {
|
||||||
|
readReq.page++;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
readReq.type == 'php' &&
|
||||||
|
logs.value.length > 0 &&
|
||||||
|
newLogs.length > 0 &&
|
||||||
|
newLogs[newLogs.length - 1] === logs.value[logs.value.length - 1]
|
||||||
|
) {
|
||||||
|
isLoading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stopSignals.some((signal) => newLogs[newLogs.length - 1].endsWith(signal))) {
|
||||||
onCloseLog();
|
onCloseLog();
|
||||||
}
|
}
|
||||||
if (end.value) {
|
if (end.value) {
|
||||||
if (lastContent.value == '') {
|
if ((logs.value.length = 0)) {
|
||||||
content.value = res.data.content;
|
logs.value = newLogs;
|
||||||
} else {
|
} else {
|
||||||
content.value = pre
|
logs.value = pre ? [...newLogs, ...lastLogs.value] : [...lastLogs.value, ...newLogs];
|
||||||
? res.data.content + '\n' + lastContent.value
|
|
||||||
: lastContent.value + '\n' + res.data.content;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (content.value == '') {
|
if ((logs.value.length = 0)) {
|
||||||
content.value = res.data.content;
|
logs.value = newLogs;
|
||||||
} else {
|
} else {
|
||||||
content.value = pre
|
logs.value = pre ? [...newLogs, ...logs.value] : [...logs.value, ...newLogs];
|
||||||
? res.data.content + '\n' + content.value
|
|
||||||
: content.value + '\n' + res.data.content;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end.value = res.data.end;
|
end.value = res.data.end;
|
||||||
|
content.value = logs.value.join('\n');
|
||||||
emit('update:hasContent', content.value !== '');
|
emit('update:hasContent', content.value !== '');
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (pre) {
|
if (pre) {
|
||||||
@ -181,16 +201,52 @@ const getContent = (pre: boolean) => {
|
|||||||
maxPage.value = res.data.total;
|
maxPage.value = res.data.total;
|
||||||
minPage.value = res.data.total;
|
minPage.value = res.data.total;
|
||||||
}
|
}
|
||||||
|
if (logs.value && logs.value.length > 3000) {
|
||||||
|
logs.value.splice(0, readReq.pageSize);
|
||||||
|
if (minPage.value > 1) {
|
||||||
|
minPage.value--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function throttle<T extends (...args: any[]) => any>(func: T, limit: number): (...args: Parameters<T>) => void {
|
||||||
|
let inThrottle: boolean;
|
||||||
|
let lastFunc: ReturnType<typeof setTimeout>;
|
||||||
|
let lastRan: number;
|
||||||
|
return function (this: any, ...args: Parameters<T>) {
|
||||||
|
if (!inThrottle) {
|
||||||
|
func.apply(this, args);
|
||||||
|
lastRan = Date.now();
|
||||||
|
inThrottle = true;
|
||||||
|
setTimeout(() => (inThrottle = false), limit);
|
||||||
|
} else {
|
||||||
|
clearTimeout(lastFunc);
|
||||||
|
lastFunc = setTimeout(() => {
|
||||||
|
if (Date.now() - lastRan >= limit) {
|
||||||
|
func.apply(this, args);
|
||||||
|
lastRan = Date.now();
|
||||||
|
}
|
||||||
|
}, limit - (Date.now() - lastRan));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const throttledGetContent = throttle(getContent, 3000);
|
||||||
|
|
||||||
|
const search = () => {
|
||||||
|
throttledGetContent(false);
|
||||||
|
};
|
||||||
|
|
||||||
const changeTail = (fromOutSide: boolean) => {
|
const changeTail = (fromOutSide: boolean) => {
|
||||||
if (fromOutSide) {
|
if (fromOutSide) {
|
||||||
tailLog.value = !tailLog.value;
|
tailLog.value = !tailLog.value;
|
||||||
}
|
}
|
||||||
if (tailLog.value) {
|
if (tailLog.value) {
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
getContent(false);
|
search();
|
||||||
}, 1000 * 3);
|
}, 1000 * 3);
|
||||||
} else {
|
} else {
|
||||||
onCloseLog();
|
onCloseLog();
|
||||||
@ -208,6 +264,7 @@ const onCloseLog = async () => {
|
|||||||
tailLog.value = false;
|
tailLog.value = false;
|
||||||
clearInterval(Number(timer));
|
clearInterval(Number(timer));
|
||||||
timer = null;
|
timer = null;
|
||||||
|
isLoading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
function isScrolledToBottom(element: HTMLElement): boolean {
|
function isScrolledToBottom(element: HTMLElement): boolean {
|
||||||
@ -228,7 +285,7 @@ const init = () => {
|
|||||||
changeTail(false);
|
changeTail(false);
|
||||||
}
|
}
|
||||||
readReq.latest = true;
|
readReq.latest = true;
|
||||||
getContent(false);
|
search();
|
||||||
|
|
||||||
nextTick(() => {});
|
nextTick(() => {});
|
||||||
};
|
};
|
||||||
@ -242,9 +299,14 @@ const initCodemirror = () => {
|
|||||||
if (editorRef.value) {
|
if (editorRef.value) {
|
||||||
scrollerElement.value = editorRef.value.$el as HTMLElement;
|
scrollerElement.value = editorRef.value.$el as HTMLElement;
|
||||||
scrollerElement.value.addEventListener('scroll', function () {
|
scrollerElement.value.addEventListener('scroll', function () {
|
||||||
|
if (tailLog.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isScrolledToBottom(scrollerElement.value)) {
|
if (isScrolledToBottom(scrollerElement.value)) {
|
||||||
readReq.page = maxPage.value;
|
if (maxPage.value > 1) {
|
||||||
getContent(false);
|
readReq.page = maxPage.value;
|
||||||
|
}
|
||||||
|
search();
|
||||||
}
|
}
|
||||||
if (isScrolledToTop(scrollerElement.value)) {
|
if (isScrolledToTop(scrollerElement.value)) {
|
||||||
readReq.page = minPage.value - 1;
|
readReq.page = minPage.value - 1;
|
||||||
@ -252,7 +314,7 @@ const initCodemirror = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
minPage.value = readReq.page;
|
minPage.value = readReq.page;
|
||||||
getContent(true);
|
throttledGetContent(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
|
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
|
||||||
|
@ -86,6 +86,7 @@ onMounted(() => {
|
|||||||
background-color: var(--panel-button-active) !important;
|
background-color: var(--panel-button-active) !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
border: 2px solid transparent !important;
|
border: 2px solid transparent !important;
|
||||||
|
color: var(--el-text-color-regular) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-radio-button__original-radio:checked + .el-radio-button__inner {
|
.el-radio-button__original-radio:checked + .el-radio-button__inner {
|
||||||
|
@ -1941,6 +1941,8 @@ const message = {
|
|||||||
'Using a custom docker-compose.yml file may cause the application upgrade to fail. If it is not necessary, do not check it',
|
'Using a custom docker-compose.yml file may cause the application upgrade to fail. If it is not necessary, do not check it',
|
||||||
diffHelper:
|
diffHelper:
|
||||||
'The left side is the old version, the right side is the new version. After editing, click to save the custom version',
|
'The left side is the old version, the right side is the new version. After editing, click to save the custom version',
|
||||||
|
pullImage: 'Pull Image',
|
||||||
|
pullImageHelper: 'Execute docker pull to pull the image before the application starts',
|
||||||
deleteImage: 'Delete Image',
|
deleteImage: 'Delete Image',
|
||||||
deleteImageHelper: 'Delete the image related to the application. The task will not stop if deletion fails',
|
deleteImageHelper: 'Delete the image related to the application. The task will not stop if deletion fails',
|
||||||
requireMemory: 'Memory',
|
requireMemory: 'Memory',
|
||||||
|
@ -1804,6 +1804,8 @@ const message = {
|
|||||||
useCustom: '自訂 docker-compose.yml',
|
useCustom: '自訂 docker-compose.yml',
|
||||||
useCustomHelper: '使用自訂 docker-compose.yml 文件,可能會導致應用程式升級失敗,如無必要,請勿勾選',
|
useCustomHelper: '使用自訂 docker-compose.yml 文件,可能會導致應用程式升級失敗,如無必要,請勿勾選',
|
||||||
diffHelper: '左側為舊版本,右側為新版,編輯之後點擊使用自訂版本保存',
|
diffHelper: '左側為舊版本,右側為新版,編輯之後點擊使用自訂版本保存',
|
||||||
|
pullImage: '拉取鏡像',
|
||||||
|
pullImageHelper: '在應用啟動之前執行 docker pull 來拉取鏡像',
|
||||||
deleteImage: '刪除鏡像',
|
deleteImage: '刪除鏡像',
|
||||||
deleteImageHelper: '刪除應用相關鏡像,刪除失敗任務不會終止',
|
deleteImageHelper: '刪除應用相關鏡像,刪除失敗任務不會終止',
|
||||||
requireMemory: '最低內存',
|
requireMemory: '最低內存',
|
||||||
|
@ -1802,6 +1802,8 @@ const message = {
|
|||||||
useCustom: '自定义 docker-compose.yml',
|
useCustom: '自定义 docker-compose.yml',
|
||||||
useCustomHelper: '使用自定义 docker-compose.yml 文件,可能会导致应用升级失败,如无必要,请勿勾选',
|
useCustomHelper: '使用自定义 docker-compose.yml 文件,可能会导致应用升级失败,如无必要,请勿勾选',
|
||||||
diffHelper: '左侧为旧版本,右侧为新版,编辑之后点击使用自定义版本保存',
|
diffHelper: '左侧为旧版本,右侧为新版,编辑之后点击使用自定义版本保存',
|
||||||
|
pullImage: '拉取镜像',
|
||||||
|
pullImageHelper: '在应用启动之前执行 docker pull 来拉取镜像',
|
||||||
deleteImage: '删除镜像',
|
deleteImage: '删除镜像',
|
||||||
deleteImageHelper: '删除应用相关镜像,删除失败任务不会终止',
|
deleteImageHelper: '删除应用相关镜像,删除失败任务不会终止',
|
||||||
requireMemory: '内存需求',
|
requireMemory: '内存需求',
|
||||||
|
@ -348,15 +348,16 @@ html {
|
|||||||
|
|
||||||
.el-input-group__append {
|
.el-input-group__append {
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
&:hover {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
background-color: var(--el-color-primary-light-9) !important;
|
|
||||||
}
|
|
||||||
background-color: #ffffff !important;
|
background-color: #ffffff !important;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
box-shadow: 0 1px 0 0 var(--el-input-border-color) inset, 0 -1px 0 0 var(--el-input-border-color) inset,
|
box-shadow: 0 1px 0 0 var(--el-input-border-color) inset, 0 -1px 0 0 var(--el-input-border-color) inset,
|
||||||
-1px 0 0 0 var(--el-input-border-color) inset;
|
-1px 0 0 0 var(--el-input-border-color) inset;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-button {
|
.tag-button {
|
||||||
|
@ -80,10 +80,6 @@ html.dark {
|
|||||||
border: 1px solid var(--panel-border-color);
|
border: 1px solid var(--panel-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
// * wangEditor
|
|
||||||
--w-e-textarea-bg-color: #1b1b1b;
|
|
||||||
--w-e-textarea-color: #eeeeee;
|
|
||||||
|
|
||||||
.md-editor-dark {
|
.md-editor-dark {
|
||||||
--md-bk-color: #111417;
|
--md-bk-color: #111417;
|
||||||
}
|
}
|
||||||
@ -251,6 +247,8 @@ html.dark {
|
|||||||
color: rgb(174, 166, 153);
|
color: rgb(174, 166, 153);
|
||||||
}
|
}
|
||||||
.el-dialog {
|
.el-dialog {
|
||||||
|
border: 1px solid var(--panel-border-color);
|
||||||
|
|
||||||
.el-dialog__header {
|
.el-dialog__header {
|
||||||
border-bottom: var(--panel-border);
|
border-bottom: var(--panel-border);
|
||||||
color: #999999;
|
color: #999999;
|
||||||
@ -258,7 +256,6 @@ html.dark {
|
|||||||
color: var(--el-menu-text-color);
|
color: var(--el-menu-text-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
border: 1px solid var(--panel-border-color);
|
|
||||||
}
|
}
|
||||||
.el-tabs__item {
|
.el-tabs__item {
|
||||||
color: #999999;
|
color: #999999;
|
||||||
@ -330,15 +327,16 @@ html.dark {
|
|||||||
|
|
||||||
.el-input-group__append {
|
.el-input-group__append {
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
&:hover {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
background-color: var(--el-color-primary-light-9) !important;
|
|
||||||
}
|
|
||||||
background-color: #212426 !important;
|
background-color: #212426 !important;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
box-shadow: 0 1px 0 0 var(--el-input-border-color) inset, 0 -1px 0 0 var(--el-input-border-color) inset,
|
box-shadow: 0 1px 0 0 var(--el-input-border-color) inset, 0 -1px 0 0 var(--el-input-border-color) inset,
|
||||||
-1px 0 0 0 var(--el-input-border-color) inset;
|
-1px 0 0 0 var(--el-input-border-color) inset;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-collapse-item__header {
|
.el-collapse-item__header {
|
||||||
|
@ -174,12 +174,13 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-dialog {
|
.el-dialog {
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
.el-dialog__header {
|
.el-dialog__header {
|
||||||
.el-dialog__title {
|
.el-dialog__title {
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
border-radius: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-box {
|
.row-box {
|
||||||
|
@ -95,8 +95,8 @@
|
|||||||
<span class="input-help">{{ $t('app.gpuConfigHelper') }}</span>
|
<span class="input-help">{{ $t('app.gpuConfigHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item pro="pullImage">
|
<el-form-item pro="pullImage">
|
||||||
<el-checkbox v-model="req.pullImage" :label="$t('container.forcePull')" size="large" />
|
<el-checkbox v-model="req.pullImage" :label="$t('app.pullImage')" size="large" />
|
||||||
<span class="input-help">{{ $t('container.forcePullHelper') }}</span>
|
<span class="input-help">{{ $t('app.pullImageHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="editCompose">
|
<el-form-item prop="editCompose">
|
||||||
<el-checkbox v-model="req.editCompose" :label="$t('app.editCompose')" size="large" />
|
<el-checkbox v-model="req.editCompose" :label="$t('app.editCompose')" size="large" />
|
||||||
|
@ -31,7 +31,11 @@
|
|||||||
<el-alert :title="$t('app.updateHelper')" type="warning" :closable="false" class="common-prompt" />
|
<el-alert :title="$t('app.updateHelper')" type="warning" :closable="false" class="common-prompt" />
|
||||||
<el-form @submit.prevent ref="paramForm" :model="paramModel" label-position="top" :rules="rules">
|
<el-form @submit.prevent ref="paramForm" :model="paramModel" label-position="top" :rules="rules">
|
||||||
<div v-for="(p, index) in params" :key="index">
|
<div v-for="(p, index) in params" :key="index">
|
||||||
<el-form-item :prop="p.key" :label="getLabel(p)">
|
<el-form-item
|
||||||
|
:prop="p.key"
|
||||||
|
:label="getLabel(p)"
|
||||||
|
v-if="p.showValue == undefined || p.showValue == ''"
|
||||||
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-if="p.type == 'number'"
|
v-if="p.type == 'number'"
|
||||||
type="number"
|
type="number"
|
||||||
@ -53,6 +57,9 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
<el-input v-else v-model.trim="paramModel.params[p.key]" :disabled="!p.edit"></el-input>
|
<el-input v-else v-model.trim="paramModel.params[p.key]" :disabled="!p.edit"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :prop="p.key" :label="getLabel(p)" v-else>
|
||||||
|
<el-input v-model.trim="p.showValue" :disabled="!p.edit"></el-input>
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item prop="advanced">
|
<el-form-item prop="advanced">
|
||||||
<el-checkbox v-model="paramModel.advanced" :label="$t('app.advanced')" size="large" />
|
<el-checkbox v-model="paramModel.advanced" :label="$t('app.advanced')" size="large" />
|
||||||
|
@ -50,8 +50,8 @@
|
|||||||
</span>
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item pro="pullImage" v-if="operateReq.operate === 'upgrade'">
|
<el-form-item pro="pullImage" v-if="operateReq.operate === 'upgrade'">
|
||||||
<el-checkbox v-model="operateReq.pullImage" :label="$t('container.forcePull')" size="large" />
|
<el-checkbox v-model="operateReq.pullImage" :label="$t('app.pullImage')" size="large" />
|
||||||
<span class="input-help">{{ $t('container.forcePullHelper') }}</span>
|
<span class="input-help">{{ $t('app.pullImageHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<DrawerPro v-model="open" :header="$t('file.info')" :back="handleClose" size="large">
|
<el-drawer v-model="open" width="30%" :close-on-click-modal="false" :close-on-press-escape="false">
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader :header="$t('file.info')" :back="handleClose" />
|
||||||
|
</template>
|
||||||
<el-descriptions :column="1" border>
|
<el-descriptions :column="1" border>
|
||||||
<el-descriptions-item :label="$t('file.fileName')">{{ data.name }}</el-descriptions-item>
|
<el-descriptions-item label-class-name="detail-label" :label="$t('file.fileName')">
|
||||||
<el-descriptions-item :label="$t('commons.table.type')">{{ data.type }}</el-descriptions-item>
|
{{ data.name }}
|
||||||
<el-descriptions-item :label="$t('file.path')">{{ data.path }}</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('file.size')">
|
<el-descriptions-item
|
||||||
|
label-class-name="detail-label"
|
||||||
|
:label="$t('commons.table.type')"
|
||||||
|
v-if="data.type != ''"
|
||||||
|
>
|
||||||
|
{{ data.type }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item class-name="detail-content" label-class-name="detail-label" :label="$t('file.path')">
|
||||||
|
{{ data.path }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label-class-name="detail-label" :label="$t('file.size')">
|
||||||
<span v-if="data.isDir">
|
<span v-if="data.isDir">
|
||||||
<el-button type="primary" link small @click="getDirSize(data)">
|
<el-button type="primary" link small @click="getDirSize(data)" :loading="loading">
|
||||||
<span v-if="data.dirSize == undefined">
|
<span v-if="data.dirSize == undefined">
|
||||||
{{ $t('file.calculate') }}
|
{{ $t('file.calculate') }}
|
||||||
</span>
|
</span>
|
||||||
@ -15,14 +28,20 @@
|
|||||||
</span>
|
</span>
|
||||||
<span v-else>{{ computeSize(data.size) }}</span>
|
<span v-else>{{ computeSize(data.size) }}</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('file.role')">{{ data.mode }}</el-descriptions-item>
|
<el-descriptions-item label-class-name="detail-label" :label="$t('file.role')">
|
||||||
<el-descriptions-item :label="$t('commons.table.user')">{{ data.user }}</el-descriptions-item>
|
{{ data.mode }}
|
||||||
<el-descriptions-item :label="$t('file.group')">{{ data.group }}</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('commons.table.updatedAt')">
|
<el-descriptions-item label-class-name="detail-label" :label="$t('commons.table.user')">
|
||||||
|
{{ data.user }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label-class-name="detail-label" :label="$t('file.group')">
|
||||||
|
{{ data.group }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label-class-name="detail-label" :label="$t('commons.table.updatedAt')">
|
||||||
{{ dateFormatSimple(data.modTime) }}
|
{{ dateFormatSimple(data.modTime) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</DrawerPro>
|
</el-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@ -30,6 +49,7 @@ import { ComputeDirSize, GetFileContent } from '@/api/modules/files';
|
|||||||
import { computeSize } from '@/utils/util';
|
import { computeSize } from '@/utils/util';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { dateFormatSimple } from '@/utils/util';
|
import { dateFormatSimple } from '@/utils/util';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
|
|
||||||
interface InfoProps {
|
interface InfoProps {
|
||||||
path: string;
|
path: string;
|
||||||
@ -72,3 +92,14 @@ defineExpose({
|
|||||||
acceptParams,
|
acceptParams,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
:deep(.detail-label) {
|
||||||
|
min-width: 100px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.detail-content) {
|
||||||
|
max-width: 295px;
|
||||||
|
word-break: break-all;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -420,18 +420,18 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-captcha {
|
.login-captcha {
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
:deep(.el-input__wrapper) {
|
:deep(.el-input__wrapper) {
|
||||||
background: none !important;
|
background: none !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
border-bottom: 1px solid #dcdfe6;
|
border-bottom: 1px solid #dcdfe6;
|
||||||
}
|
}
|
||||||
margin-top: 10px;
|
|
||||||
.el-input {
|
.el-input {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 45%;
|
width: 45%;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
|
@ -86,6 +86,7 @@ const acceptParams = async (website: Website.WebsiteDTO) => {
|
|||||||
deleteBackup: false,
|
deleteBackup: false,
|
||||||
forceDelete: false,
|
forceDelete: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
subSites.value = '';
|
subSites.value = '';
|
||||||
if (website.childSites && website.childSites.length > 0) {
|
if (website.childSites && website.childSites.length > 0) {
|
||||||
subSites.value = website.childSites.join(',');
|
subSites.value = website.childSites.join(',');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user