mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-11 16:00:35 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a888d289f | ||
|
|
dbff9c02e9 | ||
|
|
45c5c5b40f | ||
|
|
aea71664ee | ||
|
|
fc65fb9323 | ||
|
|
f476823531 | ||
|
|
8222579d99 |
@@ -6,7 +6,7 @@ type WebsiteSSLSearch struct {
|
|||||||
dto.PageInfo
|
dto.PageInfo
|
||||||
AcmeAccountID string `json:"acmeAccountID"`
|
AcmeAccountID string `json:"acmeAccountID"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
OrderBy string `json:"orderBy" validate:"omitempty,oneof=created_at expire_date"`
|
OrderBy string `json:"orderBy" validate:"omitempty,oneof=created_at updated_at expire_date"`
|
||||||
Order string `json:"order" validate:"omitempty,oneof=null ascending descending"`
|
Order string `json:"order" validate:"omitempty,oneof=null ascending descending"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package repo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ type ISSLRepo interface {
|
|||||||
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
|
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
|
||||||
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
|
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
|
||||||
List(opts ...DBOption) ([]model.WebsiteSSL, error)
|
List(opts ...DBOption) ([]model.WebsiteSSL, error)
|
||||||
|
TryMarkApplying(id uint) (bool, error)
|
||||||
Create(ctx context.Context, ssl *model.WebsiteSSL) error
|
Create(ctx context.Context, ssl *model.WebsiteSSL) error
|
||||||
Save(ssl *model.WebsiteSSL) error
|
Save(ssl *model.WebsiteSSL) error
|
||||||
DeleteBy(opts ...DBOption) error
|
DeleteBy(opts ...DBOption) error
|
||||||
@@ -76,12 +79,12 @@ func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.W
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
|
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
|
||||||
var website *model.WebsiteSSL
|
var website model.WebsiteSSL
|
||||||
db := getDb(opts...).Model(&model.WebsiteSSL{})
|
db := getDb(opts...).Model(&model.WebsiteSSL{})
|
||||||
if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil {
|
if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil {
|
||||||
return website, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return website, nil
|
return &website, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {
|
func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {
|
||||||
@@ -93,14 +96,25 @@ func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {
|
|||||||
return websites, nil
|
return websites, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w WebsiteSSLRepo) TryMarkApplying(id uint) (bool, error) {
|
||||||
|
db := getDb().Model(&model.WebsiteSSL{}).
|
||||||
|
Where("id = ? AND status <> ?", id, constant.SSLApply).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"status": constant.SSLApply,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
})
|
||||||
|
if db.Error != nil {
|
||||||
|
return false, db.Error
|
||||||
|
}
|
||||||
|
return db.RowsAffected > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error {
|
func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error {
|
||||||
return getTx(ctx).Create(ssl).Error
|
return getTx(ctx).Create(ssl).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
|
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
|
||||||
return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{
|
return getDb().Save(ssl).Error
|
||||||
ID: ssl.ID,
|
|
||||||
}}).Save(&ssl).Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLRepo) SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error {
|
func (w WebsiteSSLRepo) SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error {
|
||||||
|
|||||||
@@ -788,6 +788,15 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||||||
err = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
err = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
exist, err := dockerCLi.ImageExists(image)
|
||||||
|
if err != nil {
|
||||||
|
err = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
err = buserr.WithNameAndErr("ErrDockerPullImage", "", fmt.Errorf("image %s not found", image))
|
||||||
|
return err
|
||||||
|
}
|
||||||
t.LogSuccess(i18n.GetMsgByKey("PullImage"))
|
t.LogSuccess(i18n.GetMsgByKey("PullImage"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,12 @@ func (w WebsiteAcmeAccountService) Create(create request.WebsiteAcmeAccountCreat
|
|||||||
acmeAccount.CaDirURL = create.CaDirURL
|
acmeAccount.CaDirURL = create.CaDirURL
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))
|
var client *ssl.AcmeClient
|
||||||
if err != nil {
|
if err := withLegoLogger(nil, func() error {
|
||||||
|
var err error
|
||||||
|
client, err = ssl.NewRegisterClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
privateKey, err := ssl.GetPrivateKey(client.User.GetPrivateKey(), ssl.KeyType(create.KeyType))
|
privateKey, err := ssl.GetPrivateKey(client.User.GetPrivateKey(), ssl.KeyType(create.KeyType))
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@@ -12,6 +13,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v5/certificate"
|
"github.com/go-acme/lego/v5/certificate"
|
||||||
@@ -32,11 +34,66 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/ssl"
|
"github.com/1Panel-dev/1Panel/agent/utils/ssl"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||||
|
gormv2 "gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebsiteSSLService struct {
|
type WebsiteSSLService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sslObtainTimeout = time.Hour
|
||||||
|
|
||||||
|
var legoLogMu sync.Mutex
|
||||||
|
|
||||||
|
func withLegoLogger(logger *log.Logger, fn func() error) error {
|
||||||
|
if logger == nil {
|
||||||
|
logger = log.New(io.Discard, "", log.LstdFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
legoLogMu.Lock()
|
||||||
|
defer legoLogMu.Unlock()
|
||||||
|
|
||||||
|
oldLogger := legoLogger.Default()
|
||||||
|
legoLogger.SetDefault(slog.New(slog.NewTextHandler(logger.Writer(), nil)))
|
||||||
|
defer legoLogger.SetDefault(oldLogger)
|
||||||
|
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func withLegoLoggerTimeout(logger *log.Logger, timeout time.Duration, fn func(context.Context) error) error {
|
||||||
|
return withLegoLogger(logger, func() error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
return fn(ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWebsiteSSLLegoClient(ctx context.Context, acmeAccount *model.WebsiteAcmeAccount) (*ssl.AcmeClient, error) {
|
||||||
|
client, err := ssl.NewAcmeClientWithContext(ctx, acmeAccount, getSystemProxy(acmeAccount.UseProxy))
|
||||||
|
if err == nil {
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
if !errors.Is(err, ssl.ErrAcmeAccountURLMissing) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err = ssl.NewRegisterClientWithContext(ctx, acmeAccount, getSystemProxy(acmeAccount.UseProxy))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if client.User.GetRegistration() == nil || client.User.GetRegistration().Location == "" {
|
||||||
|
return nil, ssl.ErrAcmeAccountURLMissing
|
||||||
|
}
|
||||||
|
|
||||||
|
acmeAccount.URL = client.User.GetRegistration().Location
|
||||||
|
if acmeAccount.PrivateKey == "" {
|
||||||
|
return nil, errors.New("private key can not blank")
|
||||||
|
}
|
||||||
|
if err := websiteAcmeRepo.Save(*acmeAccount); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
type IWebsiteSSLService interface {
|
type IWebsiteSSLService interface {
|
||||||
Page(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error)
|
Page(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error)
|
||||||
GetSSL(id uint) (*response.WebsiteSSLDTO, error)
|
GetSSL(id uint) (*response.WebsiteSSLDTO, error)
|
||||||
@@ -66,7 +123,7 @@ func (w WebsiteSSLService) Page(search request.WebsiteSSLSearch) (int64, []respo
|
|||||||
if search.OrderBy != "" && search.Order != "null" {
|
if search.OrderBy != "" && search.Order != "null" {
|
||||||
opts = append(opts, repo.WithOrderRuleBy(search.OrderBy, search.Order))
|
opts = append(opts, repo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||||
} else {
|
} else {
|
||||||
opts = append(opts, repo.WithOrderDesc("created_at"))
|
opts = append(opts, repo.WithOrderDesc("updated_at"))
|
||||||
}
|
}
|
||||||
if search.Domain != "" {
|
if search.Domain != "" {
|
||||||
opts = append(opts, websiteSSLRepo.WithByDomain(search.Domain))
|
opts = append(opts, websiteSSLRepo.WithByDomain(search.Domain))
|
||||||
@@ -99,7 +156,7 @@ func (w WebsiteSSLService) Search(search request.WebsiteSSLListReq) ([]response.
|
|||||||
opts []repo.DBOption
|
opts []repo.DBOption
|
||||||
result []response.WebsiteSSLDTO
|
result []response.WebsiteSSLDTO
|
||||||
)
|
)
|
||||||
opts = append(opts, repo.WithOrderDesc("created_at"))
|
opts = append(opts, repo.WithOrderDesc("updated_at"))
|
||||||
if search.AcmeAccountID != "" {
|
if search.AcmeAccountID != "" {
|
||||||
acmeAccountID, err := strconv.ParseUint(search.AcmeAccountID, 10, 64)
|
acmeAccountID, err := strconv.ParseUint(search.AcmeAccountID, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -330,19 +387,23 @@ func (w WebsiteSSLService) AutoRenewSSL(id uint) error {
|
|||||||
|
|
||||||
func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
|
func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
websiteSSL *model.WebsiteSSL
|
websiteSSL *model.WebsiteSSL
|
||||||
acmeAccount *model.WebsiteAcmeAccount
|
acmeAccount *model.WebsiteAcmeAccount
|
||||||
dnsAccount *model.WebsiteDnsAccount
|
dnsAccount *model.WebsiteDnsAccount
|
||||||
client *ssl.AcmeClient
|
logFile *os.File
|
||||||
manualClient *ssl.ManualClient
|
logger *log.Logger
|
||||||
resource certificate.Resource
|
resource certificate.Resource
|
||||||
|
httpRoot string
|
||||||
)
|
)
|
||||||
|
|
||||||
websiteSSL, err = websiteSSLRepo.GetFirst(repo.WithByID(id))
|
websiteSSL, err = websiteSSLRepo.GetFirst(repo.WithByID(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if websiteSSL.Status == constant.SSLApply {
|
||||||
|
return buserr.New("InExecuting")
|
||||||
|
}
|
||||||
acmeAccount, err = websiteAcmeRepo.GetFirst(repo.WithByID(websiteSSL.AcmeAccountID))
|
acmeAccount, err = websiteAcmeRepo.GetFirst(repo.WithByID(websiteSSL.AcmeAccountID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -352,20 +413,12 @@ func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
|
|||||||
domains = append(domains, strings.Split(websiteSSL.Domains, ",")...)
|
domains = append(domains, strings.Split(websiteSSL.Domains, ",")...)
|
||||||
}
|
}
|
||||||
if websiteSSL.Provider != constant.DnsManual {
|
if websiteSSL.Provider != constant.DnsManual {
|
||||||
client, err = ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch websiteSSL.Provider {
|
switch websiteSSL.Provider {
|
||||||
case constant.DNSAccount:
|
case constant.DNSAccount:
|
||||||
dnsAccount, err = websiteDnsRepo.GetFirst(repo.WithByID(websiteSSL.DnsAccountID))
|
dnsAccount, err = websiteDnsRepo.GetFirst(repo.WithByID(websiteSSL.DnsAccountID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, *websiteSSL); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case constant.Http:
|
case constant.Http:
|
||||||
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -379,55 +432,83 @@ func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
|
|||||||
return buserr.New("ErrWildcardDomain")
|
return buserr.New("ErrWildcardDomain")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := client.UseHTTP(path.Join(appInstall.GetPath(), "root")); err != nil {
|
httpRoot = path.Join(appInstall.GetPath(), "root")
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
websiteSSL.Status = constant.SSLApply
|
marked, err := websiteSSLRepo.TryMarkApplying(websiteSSL.ID)
|
||||||
err = websiteSSLRepo.Save(websiteSSL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !marked {
|
||||||
|
return buserr.New("InExecuting")
|
||||||
|
}
|
||||||
|
websiteSSL.Status = constant.SSLApply
|
||||||
|
|
||||||
go func() {
|
logFile, logger = newWebsiteSSLLogger(websiteSSL, autoRenew)
|
||||||
logFile, logger := newWebsiteSSLLogger(websiteSSL, autoRenew)
|
logFileOwnedByGoroutine := false
|
||||||
|
if logFile != nil {
|
||||||
|
defer func() {
|
||||||
|
if !logFileOwnedByGoroutine {
|
||||||
|
_ = logFile.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
startMsg := i18n.GetMsgWithMap("ApplySSLStart", map[string]interface{}{"domain": strings.Join(domains, ","), "type": i18n.GetMsgByKey(websiteSSL.Provider)})
|
||||||
|
if websiteSSL.Provider == constant.DNSAccount {
|
||||||
|
startMsg = startMsg + i18n.GetMsgWithMap("DNSAccountName", map[string]interface{}{"name": dnsAccount.Name, "type": dnsAccount.Type})
|
||||||
|
}
|
||||||
|
logger.Println(startMsg)
|
||||||
|
|
||||||
|
logFileOwnedByGoroutine = true
|
||||||
|
go func(logFile *os.File, logger *log.Logger) {
|
||||||
if logFile != nil {
|
if logFile != nil {
|
||||||
defer logFile.Close()
|
defer func() {
|
||||||
|
_ = logFile.Close()
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
// lego v5 switched to slog. Bridge it to the existing *log.Logger
|
|
||||||
// so the SSL apply log is still written to the per-domain file
|
|
||||||
// under SSLLogDir.
|
|
||||||
legoLogger.SetDefault(slog.New(slog.NewTextHandler(logger.Writer(), nil)))
|
|
||||||
startMsg := i18n.GetMsgWithMap("ApplySSLStart", map[string]interface{}{"domain": strings.Join(domains, ","), "type": i18n.GetMsgByKey(websiteSSL.Provider)})
|
|
||||||
if websiteSSL.Provider == constant.DNSAccount {
|
|
||||||
startMsg = startMsg + i18n.GetMsgWithMap("DNSAccountName", map[string]interface{}{"name": dnsAccount.Name, "type": dnsAccount.Type})
|
|
||||||
}
|
|
||||||
logger.Println(startMsg)
|
|
||||||
if websiteSSL.Provider != constant.DnsManual {
|
if websiteSSL.Provider != constant.DnsManual {
|
||||||
privateKey, err := ssl.GetPrivateKeyByType(websiteSSL.KeyType, websiteSSL.PrivateKey)
|
privateKey, err := ssl.GetPrivateKeyByType(websiteSSL.KeyType, websiteSSL.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(websiteSSL, err)
|
handleError(websiteSSL, logger, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if websiteSSL.IsIp {
|
err = withLegoLoggerTimeout(logger, sslObtainTimeout, func(ctx context.Context) error {
|
||||||
resource, err = client.ObtainIPSSL(domains[0], privateKey)
|
client, err := newWebsiteSSLLegoClient(ctx, acmeAccount)
|
||||||
} else {
|
if err != nil {
|
||||||
resource, err = client.ObtainSSL(domains, privateKey)
|
return err
|
||||||
}
|
}
|
||||||
|
switch websiteSSL.Provider {
|
||||||
|
case constant.DNSAccount:
|
||||||
|
if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, *websiteSSL); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case constant.Http:
|
||||||
|
if err = client.UseHTTP(httpRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if websiteSSL.IsIp {
|
||||||
|
resource, err = client.ObtainIPSSL(ctx, domains[0], privateKey)
|
||||||
|
} else {
|
||||||
|
resource, err = client.ObtainSSL(ctx, domains, privateKey)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(websiteSSL, err)
|
handleError(websiteSSL, logger, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
manualClient, err = ssl.NewCustomAcmeClient(acmeAccount, logger)
|
manualClient, err := ssl.NewCustomAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy), logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(websiteSSL, err)
|
handleError(websiteSSL, logger, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resource, err = manualClient.RequestCertificate(context.Background(), websiteSSL)
|
ctx, cancel := context.WithTimeout(context.Background(), sslObtainTimeout)
|
||||||
|
resource, err = manualClient.RequestCertificate(ctx, websiteSSL)
|
||||||
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(websiteSSL, err)
|
handleError(websiteSSL, logger, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,10 +516,9 @@ func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
|
|||||||
websiteSSL.PrivateKey = string(resource.PrivateKey)
|
websiteSSL.PrivateKey = string(resource.PrivateKey)
|
||||||
websiteSSL.Pem = string(resource.Certificate)
|
websiteSSL.Pem = string(resource.Certificate)
|
||||||
websiteSSL.CertURL = resource.CertURL
|
websiteSSL.CertURL = resource.CertURL
|
||||||
certBlock, _ := pem.Decode(resource.Certificate)
|
cert, err := parseCertificatePEM(resource.Certificate)
|
||||||
cert, err := x509.ParseCertificate(certBlock.Bytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(websiteSSL, err)
|
handleError(websiteSSL, logger, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
websiteSSL.ExpireDate = cert.NotAfter
|
websiteSSL.ExpireDate = cert.NotAfter
|
||||||
@@ -494,7 +574,7 @@ func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
|
|||||||
}
|
}
|
||||||
printSSLLog(logger, "PushSSLToNodeSuccess", nil)
|
printSSLLog(logger, "PushSSLToNodeSuccess", nil)
|
||||||
}
|
}
|
||||||
}()
|
}(logFile, logger)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -516,16 +596,24 @@ func runShellScriptFile(workDir, shell string, logger *log.Logger) error {
|
|||||||
return cmdMgr.Run("bash", file.Name())
|
return cmdMgr.Run("bash", file.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleError(websiteSSL *model.WebsiteSSL, err error) {
|
func parseCertificatePEM(certPEM []byte) (*x509.Certificate, error) {
|
||||||
|
certBlock, _ := pem.Decode(certPEM)
|
||||||
|
if certBlock == nil {
|
||||||
|
return nil, errors.New("invalid certificate PEM")
|
||||||
|
}
|
||||||
|
return x509.ParseCertificate(certBlock.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleError(websiteSSL *model.WebsiteSSL, logger *log.Logger, err error) {
|
||||||
if websiteSSL.Status == constant.SSLInit || websiteSSL.Status == constant.SSLError {
|
if websiteSSL.Status == constant.SSLInit || websiteSSL.Status == constant.SSLError {
|
||||||
websiteSSL.Status = constant.StatusError
|
websiteSSL.Status = constant.StatusError
|
||||||
} else {
|
} else {
|
||||||
websiteSSL.Status = constant.SSLApplyError
|
websiteSSL.Status = constant.SSLApplyError
|
||||||
}
|
}
|
||||||
websiteSSL.Message = err.Error()
|
websiteSSL.Message = err.Error()
|
||||||
// lego v5 uses slog; use the same global default logger to write the
|
if logger != nil {
|
||||||
// failure message to the SSL log.
|
logger.Println(i18n.GetErrMsg("ApplySSLFailed", map[string]interface{}{"domain": websiteSSL.PrimaryDomain, "detail": err.Error()}))
|
||||||
legoLogger.Default().Error(i18n.GetErrMsg("ApplySSLFailed", map[string]interface{}{"domain": websiteSSL.PrimaryDomain, "detail": err.Error()}))
|
}
|
||||||
_ = websiteSSLRepo.Save(websiteSSL)
|
_ = websiteSSLRepo.Save(websiteSSL)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,7 +622,7 @@ func (w WebsiteSSLService) GetDNSResolve(req request.WebsiteDNSReq) ([]response.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client, err := ssl.NewCustomAcmeClient(acmeAccount, nil)
|
client, err := ssl.NewCustomAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -580,7 +668,7 @@ func (w WebsiteSSLService) Delete(ids []uint) error {
|
|||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
if websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(id)); len(websites) > 0 {
|
if websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(id)); len(websites) > 0 {
|
||||||
oldSSL, _ := websiteSSLRepo.GetFirst(repo.WithByID(id))
|
oldSSL, _ := websiteSSLRepo.GetFirst(repo.WithByID(id))
|
||||||
if oldSSL.ID > 0 {
|
if oldSSL != nil && oldSSL.ID > 0 {
|
||||||
websiteSSLS = append(websiteSSLS, oldSSL.PrimaryDomain)
|
websiteSSLS = append(websiteSSLS, oldSSL.PrimaryDomain)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -608,12 +696,13 @@ func (w WebsiteSSLService) Delete(ids []uint) error {
|
|||||||
global.LOG.Errorf("Failed to get acme account for SSL revoke, err: %v", err)
|
global.LOG.Errorf("Failed to get acme account for SSL revoke, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
client, err := ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))
|
err = withLegoLogger(nil, func() error {
|
||||||
if err != nil {
|
client, err := newWebsiteSSLLegoClient(context.Background(), acmeAccount)
|
||||||
global.LOG.Errorf("Failed to create ACME client for SSL revoke, err: %v", err)
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
err = client.RevokeSSL([]byte(websiteSSL.Pem))
|
return client.RevokeSSL([]byte(websiteSSL.Pem))
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("Failed to revoke SSL for domain %s, err: %v", websiteSSL.PrimaryDomain, err)
|
global.LOG.Errorf("Failed to revoke SSL for domain %s, err: %v", websiteSSL.PrimaryDomain, err)
|
||||||
return
|
return
|
||||||
@@ -851,7 +940,13 @@ func (w WebsiteSSLService) SyncForRestart() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteSSLService) ImportMasterSSL(create model.WebsiteSSL) error {
|
func (w WebsiteSSLService) ImportMasterSSL(create model.WebsiteSSL) error {
|
||||||
websiteSSL, _ := websiteSSLRepo.GetFirst(websiteSSLRepo.WithByMasterSSLID(create.ID))
|
websiteSSL, err := websiteSSLRepo.GetFirst(websiteSSLRepo.WithByMasterSSLID(create.ID))
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, gormv2.ErrRecordNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
websiteSSL = &model.WebsiteSSL{}
|
||||||
|
}
|
||||||
websiteSSL.Status = constant.SSLReady
|
websiteSSL.Status = constant.SSLReady
|
||||||
websiteSSL.Provider = constant.FromMaster
|
websiteSSL.Provider = constant.FromMaster
|
||||||
websiteSSL.PrimaryDomain = create.PrimaryDomain
|
websiteSSL.PrimaryDomain = create.PrimaryDomain
|
||||||
@@ -865,8 +960,14 @@ func (w WebsiteSSLService) ImportMasterSSL(create model.WebsiteSSL) error {
|
|||||||
websiteSSL.Organization = create.Organization
|
websiteSSL.Organization = create.Organization
|
||||||
websiteSSL.MasterSSLID = create.ID
|
websiteSSL.MasterSSLID = create.ID
|
||||||
websiteSSL.Domains = create.Domains
|
websiteSSL.Domains = create.Domains
|
||||||
if err := websiteSSLRepo.Save(websiteSSL); err != nil {
|
if websiteSSL.ID == 0 {
|
||||||
return err
|
if err := websiteSSLRepo.Create(context.Background(), websiteSSL); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := websiteSSLRepo.Save(websiteSSL); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))
|
websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))
|
||||||
if len(websites) == 0 {
|
if len(websites) == 0 {
|
||||||
|
|||||||
+1
-16
@@ -240,7 +240,7 @@ func (c *CommandHelper) pipeContext() (context.Context, context.CancelFunc) {
|
|||||||
func (c *CommandHelper) buildPipeCommands(ctx context.Context, commands []PipeCommand) []*exec.Cmd {
|
func (c *CommandHelper) buildPipeCommands(ctx context.Context, commands []PipeCommand) []*exec.Cmd {
|
||||||
cmds := make([]*exec.Cmd, 0, len(commands))
|
cmds := make([]*exec.Cmd, 0, len(commands))
|
||||||
for _, item := range commands {
|
for _, item := range commands {
|
||||||
cmdItem := exec.CommandContext(ctx, item.Name, filterEmptyArgs(item.Args)...)
|
cmdItem := exec.CommandContext(ctx, item.Name, item.Args...)
|
||||||
cmdItem.Env = append(os.Environ(), c.env...)
|
cmdItem.Env = append(os.Environ(), c.env...)
|
||||||
cmdItem.Env = append(cmdItem.Env, item.Env...)
|
cmdItem.Env = append(cmdItem.Env, item.Env...)
|
||||||
cmdItem.Dir = c.workDir
|
cmdItem.Dir = c.workDir
|
||||||
@@ -305,7 +305,6 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
|||||||
var newContext context.Context
|
var newContext context.Context
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
var outputFile *os.File
|
var outputFile *os.File
|
||||||
arg = filterEmptyArgs(arg)
|
|
||||||
|
|
||||||
if c.timeout != 0 {
|
if c.timeout != 0 {
|
||||||
if c.context == nil {
|
if c.context == nil {
|
||||||
@@ -412,20 +411,6 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterEmptyArgs(args []string) []string {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
filtered := args[:0]
|
|
||||||
for _, arg := range args {
|
|
||||||
if arg == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
filtered = append(filtered, arg)
|
|
||||||
}
|
|
||||||
return filtered
|
|
||||||
}
|
|
||||||
|
|
||||||
func contextDone(ctx context.Context) <-chan struct{} {
|
func contextDone(ctx context.Context) <-chan struct{} {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -208,6 +208,12 @@ func (c Client) PullImageWithProcessAndOptions(task *task.Task, imageName string
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if msg, ok := progress["errorDetail"]; ok {
|
||||||
|
return fmt.Errorf("image pull failed, err: %v", msg)
|
||||||
|
}
|
||||||
|
if msg, ok := progress["error"]; ok {
|
||||||
|
return fmt.Errorf("image pull failed, err: %v", msg)
|
||||||
|
}
|
||||||
status, _ := progress["status"].(string)
|
status, _ := progress["status"].(string)
|
||||||
if status == "Downloading" || status == "Extracting" {
|
if status == "Downloading" || status == "Extracting" {
|
||||||
logProcess(progress, task)
|
logProcess(progress, task)
|
||||||
|
|||||||
+69
-53
@@ -20,8 +20,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/acme"
|
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||||
@@ -30,10 +28,9 @@ import (
|
|||||||
"github.com/go-acme/lego/v5/certcrypto/compat"
|
"github.com/go-acme/lego/v5/certcrypto/compat"
|
||||||
"github.com/go-acme/lego/v5/lego"
|
"github.com/go-acme/lego/v5/lego"
|
||||||
"github.com/go-acme/lego/v5/registration"
|
"github.com/go-acme/lego/v5/registration"
|
||||||
|
"golang.org/x/crypto/acme"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Orders = make(map[uint]*acme.Order)
|
|
||||||
|
|
||||||
// parsePrivateKeyPEM decodes a PEM-encoded private key produced by either
|
// parsePrivateKeyPEM decodes a PEM-encoded private key produced by either
|
||||||
// lego v4 (SEC1 for EC, PKCS#1 for RSA) or lego v5 (PKCS#8 for both). It tries
|
// lego v4 (SEC1 for EC, PKCS#1 for RSA) or lego v5 (PKCS#8 for both). It tries
|
||||||
// PKCS#8 first because that is what v5 and any modern tooling emits, then
|
// PKCS#8 first because that is what v5 and any modern tooling emits, then
|
||||||
@@ -139,6 +136,62 @@ func GetPrivateKey(priKey crypto.PrivateKey, keyType KeyType) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewRegisterClient(acmeAccount *model.WebsiteAcmeAccount, proxy *dto.SystemProxy) (*AcmeClient, error) {
|
func NewRegisterClient(acmeAccount *model.WebsiteAcmeAccount, proxy *dto.SystemProxy) (*AcmeClient, error) {
|
||||||
|
return NewRegisterClientWithContext(context.Background(), acmeAccount, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRegisterClientWithContext(ctx context.Context, acmeAccount *model.WebsiteAcmeAccount, proxy *dto.SystemProxy) (*AcmeClient, error) {
|
||||||
|
client, err := newAcmeClient(acmeAccount, proxy, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var reg *legoacme.ExtendedAccount
|
||||||
|
if acmeAccount.Type == "zerossl" || acmeAccount.Type == "google" || acmeAccount.Type == "freessl" || (acmeAccount.Type == "custom" && acmeAccount.UseEAB) {
|
||||||
|
if acmeAccount.Type == "zerossl" {
|
||||||
|
var res *zeroSSLRes
|
||||||
|
res, err = getZeroSSLEabCredentials(ctx, acmeAccount.Email)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if res.Success {
|
||||||
|
acmeAccount.EabKid = res.EabKid
|
||||||
|
acmeAccount.EabHmacKey = res.EabHmacKey
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("get zero ssl eab credentials failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eabOptions := registration.RegisterEABOptions{
|
||||||
|
TermsOfServiceAgreed: true,
|
||||||
|
Kid: acmeAccount.EabKid,
|
||||||
|
HmacEncoded: acmeAccount.EabHmacKey,
|
||||||
|
}
|
||||||
|
reg, err = client.Client.Registration.RegisterWithExternalAccountBinding(ctx, eabOptions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reg, err = client.Client.Registration.Register(ctx, registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.User.Registration = reg
|
||||||
|
if reg != nil {
|
||||||
|
acmeAccount.URL = reg.Location
|
||||||
|
}
|
||||||
|
if acmeAccount.PrivateKey == "" {
|
||||||
|
privateKey, err := GetPrivateKey(client.User.GetPrivateKey(), normalizeKeyType(acmeAccount.KeyType))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
acmeAccount.PrivateKey = string(privateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAcmeClient(acmeAccount *model.WebsiteAcmeAccount, proxy *dto.SystemProxy, registration *legoacme.ExtendedAccount) (*AcmeClient, error) {
|
||||||
var (
|
var (
|
||||||
priKey crypto.Signer
|
priKey crypto.Signer
|
||||||
err error
|
err error
|
||||||
@@ -165,8 +218,9 @@ func NewRegisterClient(acmeAccount *model.WebsiteAcmeAccount, proxy *dto.SystemP
|
|||||||
}
|
}
|
||||||
|
|
||||||
myUser := &AcmeUser{
|
myUser := &AcmeUser{
|
||||||
Email: acmeAccount.Email,
|
Email: acmeAccount.Email,
|
||||||
Key: priKey,
|
Registration: registration,
|
||||||
|
Key: priKey,
|
||||||
}
|
}
|
||||||
config := NewConfigWithProxy(myUser, acmeAccount.Type, acmeAccount.CaDirURL, proxy)
|
config := NewConfigWithProxy(myUser, acmeAccount.Type, acmeAccount.CaDirURL, proxy)
|
||||||
client, err := lego.NewClient(config)
|
client, err := lego.NewClient(config)
|
||||||
@@ -174,41 +228,6 @@ func NewRegisterClient(acmeAccount *model.WebsiteAcmeAccount, proxy *dto.SystemP
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
var reg *legoacme.ExtendedAccount
|
|
||||||
if acmeAccount.Type == "zerossl" || acmeAccount.Type == "google" || acmeAccount.Type == "freessl" || (acmeAccount.Type == "custom" && acmeAccount.UseEAB) {
|
|
||||||
if acmeAccount.Type == "zerossl" {
|
|
||||||
var res *zeroSSLRes
|
|
||||||
res, err = getZeroSSLEabCredentials(acmeAccount.Email)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if res.Success {
|
|
||||||
acmeAccount.EabKid = res.EabKid
|
|
||||||
acmeAccount.EabHmacKey = res.EabHmacKey
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("get zero ssl eab credentials failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eabOptions := registration.RegisterEABOptions{
|
|
||||||
TermsOfServiceAgreed: true,
|
|
||||||
Kid: acmeAccount.EabKid,
|
|
||||||
HmacEncoded: acmeAccount.EabHmacKey,
|
|
||||||
}
|
|
||||||
reg, err = client.Registration.RegisterWithExternalAccountBinding(ctx, eabOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reg, err = client.Registration.Register(ctx, registration.RegisterOptions{TermsOfServiceAgreed: true})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
myUser.Registration = reg
|
|
||||||
|
|
||||||
acmeClient := &AcmeClient{
|
acmeClient := &AcmeClient{
|
||||||
User: myUser,
|
User: myUser,
|
||||||
Client: client,
|
Client: client,
|
||||||
@@ -252,15 +271,12 @@ func NewConfigWithProxy(user registration.User, accountType, customCaURL string,
|
|||||||
}
|
}
|
||||||
// lego v5 removed lego.CertificateConfig.KeyType; the key type is now
|
// lego v5 removed lego.CertificateConfig.KeyType; the key type is now
|
||||||
// taken from the user's account for each ObtainRequest call.
|
// taken from the user's account for each ObtainRequest call.
|
||||||
return &lego.Config{
|
config := lego.NewConfig(user)
|
||||||
CADirURL: caDirURL,
|
config.CADirURL = caDirURL
|
||||||
UserAgent: "1Panel",
|
config.UserAgent = "1Panel"
|
||||||
User: user,
|
config.HTTPClient = createHTTPClientWithProxy(proxyURL, proxyUser, proxyPassword)
|
||||||
HTTPClient: createHTTPClientWithProxy(proxyURL, proxyUser, proxyPassword),
|
config.Certificate.Timeout = 60 * time.Second
|
||||||
Certificate: lego.CertificateConfig{
|
return config
|
||||||
Timeout: 60 * time.Second,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initCertPool() *x509.CertPool {
|
func initCertPool() *x509.CertPool {
|
||||||
@@ -319,20 +335,20 @@ func createHTTPClientWithProxy(proxyURL, username, password string) *http.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getZeroSSLEabCredentials(email string) (*zeroSSLRes, error) {
|
func getZeroSSLEabCredentials(ctx context.Context, email string) (*zeroSSLRes, error) {
|
||||||
baseURL := "https://api.zerossl.com/acme/eab-credentials-email"
|
baseURL := "https://api.zerossl.com/acme/eab-credentials-email"
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Add("email", email)
|
params.Add("email", email)
|
||||||
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
|
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", requestURL, nil)
|
req, err := http.NewRequestWithContext(ctx, "POST", requestURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{Timeout: 2 * time.Minute}
|
||||||
defer client.CloseIdleConnections()
|
defer client.CloseIdleConnections()
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+97
-47
@@ -7,13 +7,16 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
|
legoacme "github.com/go-acme/lego/v5/acme"
|
||||||
"github.com/go-acme/lego/v5/certificate"
|
"github.com/go-acme/lego/v5/certificate"
|
||||||
"github.com/go-acme/lego/v5/challenge/dns01"
|
"github.com/go-acme/lego/v5/challenge/dns01"
|
||||||
"github.com/go-acme/lego/v5/lego"
|
"github.com/go-acme/lego/v5/lego"
|
||||||
@@ -28,6 +31,8 @@ import (
|
|||||||
// occasionally fail propagation checks against the wrong resolver.
|
// occasionally fail propagation checks against the wrong resolver.
|
||||||
var dnsChallengeMu sync.Mutex
|
var dnsChallengeMu sync.Mutex
|
||||||
|
|
||||||
|
var ErrAcmeAccountURLMissing = errors.New("acme account url is empty")
|
||||||
|
|
||||||
type AcmeClientOption func(*AcmeClientOptions)
|
type AcmeClientOption func(*AcmeClientOptions)
|
||||||
|
|
||||||
type AcmeClientOptions struct {
|
type AcmeClientOptions struct {
|
||||||
@@ -40,18 +45,35 @@ type AcmeClient struct {
|
|||||||
User *AcmeUser
|
User *AcmeUser
|
||||||
ProxyURL string
|
ProxyURL string
|
||||||
|
|
||||||
// dnsChallengeLocked records whether this client currently holds
|
dnsChallengeConfig *dnsChallengeConfig
|
||||||
// dnsChallengeMu. It is set by UseDns and cleared by ObtainSSL/
|
}
|
||||||
// ObtainIPSSL once the DNS-01 flow finishes. UseHTTP does not touch it.
|
|
||||||
dnsChallengeLocked bool
|
type dnsChallengeConfig struct {
|
||||||
|
recursiveNameservers []string
|
||||||
|
timeout time.Duration
|
||||||
|
disableCNAME bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAcmeClient(acmeAccount *model.WebsiteAcmeAccount, systemProxy *dto.SystemProxy) (*AcmeClient, error) {
|
func NewAcmeClient(acmeAccount *model.WebsiteAcmeAccount, systemProxy *dto.SystemProxy) (*AcmeClient, error) {
|
||||||
|
return NewAcmeClientWithContext(context.Background(), acmeAccount, systemProxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAcmeClientWithContext(ctx context.Context, acmeAccount *model.WebsiteAcmeAccount, systemProxy *dto.SystemProxy) (*AcmeClient, error) {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if acmeAccount.Email == "" {
|
if acmeAccount.Email == "" {
|
||||||
return nil, errors.New("email can not blank")
|
return nil, errors.New("email can not blank")
|
||||||
}
|
}
|
||||||
|
accountURL := strings.TrimSpace(acmeAccount.URL)
|
||||||
|
if accountURL == "" {
|
||||||
|
return nil, ErrAcmeAccountURLMissing
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(acmeAccount.PrivateKey) == "" {
|
||||||
|
return nil, errors.New("private key can not blank")
|
||||||
|
}
|
||||||
|
|
||||||
client, err := NewRegisterClient(acmeAccount, systemProxy)
|
client, err := newAcmeClient(acmeAccount, systemProxy, &legoacme.ExtendedAccount{Location: accountURL})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -59,7 +81,11 @@ func NewAcmeClient(acmeAccount *model.WebsiteAcmeAccount, systemProxy *dto.Syste
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.WebsiteSSL) error {
|
func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.WebsiteSSL) error {
|
||||||
p, err := getDNSProviderConfig(dnsType, params)
|
var httpClient *http.Client
|
||||||
|
if c.Config != nil {
|
||||||
|
httpClient = c.Config.HTTPClient
|
||||||
|
}
|
||||||
|
p, err := getDNSProviderConfig(dnsType, params, httpClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -71,46 +97,20 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.Web
|
|||||||
nameservers = append(nameservers, websiteSSL.Nameserver2)
|
nameservers = append(nameservers, websiteSSL.Nameserver2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hold the global DNS-01 lock for the entire flow that follows, including
|
|
||||||
// the Obtain call. lego v5 reads dns01.DefaultClient() inside its own
|
|
||||||
// propagation-precheck loop, so the lock cannot be released right after
|
|
||||||
// SetDefaultClient/SetDNS01Provider; it has to span the whole DNS-01
|
|
||||||
// challenge. ObtainSSL / ObtainIPSSL release it once the request returns.
|
|
||||||
dnsChallengeMu.Lock()
|
|
||||||
c.dnsChallengeLocked = true
|
|
||||||
|
|
||||||
if websiteSSL.DisableCNAME {
|
|
||||||
_ = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", "true")
|
|
||||||
} else {
|
|
||||||
_ = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", "false")
|
|
||||||
}
|
|
||||||
|
|
||||||
// lego v5 removed dns01.AddRecursiveNameservers and dns01.AddDNSTimeout;
|
|
||||||
// configure them via dns01.NewClient(&dns01.Options{...}) + SetDefaultClient.
|
|
||||||
dns01.SetDefaultClient(dns01.NewClient(&dns01.Options{
|
|
||||||
RecursiveNameservers: nameservers,
|
|
||||||
Timeout: dnsTimeOut,
|
|
||||||
}))
|
|
||||||
|
|
||||||
var opts []dns01.ChallengeOption
|
var opts []dns01.ChallengeOption
|
||||||
if websiteSSL.SkipDNS {
|
if websiteSSL.SkipDNS {
|
||||||
opts = append(opts, dns01.DisableAuthoritativeNssPropagationRequirement())
|
opts = append(opts, dns01.DisableAuthoritativeNssPropagationRequirement())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.Client.Challenge.SetDNS01Provider(p, opts...); err != nil {
|
if err := c.Client.Challenge.SetDNS01Provider(p, opts...); err != nil {
|
||||||
c.releaseDNSLock()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
c.dnsChallengeConfig = &dnsChallengeConfig{
|
||||||
}
|
recursiveNameservers: append([]string(nil), nameservers...),
|
||||||
|
timeout: dnsTimeOut,
|
||||||
// releaseDNSLock releases dnsChallengeMu if it was acquired by UseDns.
|
disableCNAME: websiteSSL.DisableCNAME,
|
||||||
// Safe to call multiple times; calls after the first one are no-ops.
|
|
||||||
func (c *AcmeClient) releaseDNSLock() {
|
|
||||||
if c.dnsChallengeLocked {
|
|
||||||
c.dnsChallengeLocked = false
|
|
||||||
dnsChallengeMu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AcmeClient) UseHTTP(path string) error {
|
func (c *AcmeClient) UseHTTP(path string) error {
|
||||||
@@ -123,11 +123,14 @@ func (c *AcmeClient) UseHTTP(path string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
c.dnsChallengeConfig = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AcmeClient) ObtainSSL(domains []string, privateKey crypto.Signer) (certificate.Resource, error) {
|
func (c *AcmeClient) ObtainSSL(ctx context.Context, domains []string, privateKey crypto.Signer) (certificate.Resource, error) {
|
||||||
defer c.releaseDNSLock()
|
unlockDNSChallenge := c.lockDNSChallenge()
|
||||||
|
defer unlockDNSChallenge()
|
||||||
|
|
||||||
// lego v5 disables Common Name by default; explicitly enable it to keep
|
// lego v5 disables Common Name by default; explicitly enable it to keep
|
||||||
// the v4 behaviour, so legacy Java/router clients that still rely on the
|
// the v4 behaviour, so legacy Java/router clients that still rely on the
|
||||||
// CommonName field do not fail TLS handshake.
|
// CommonName field do not fail TLS handshake.
|
||||||
@@ -138,8 +141,6 @@ func (c *AcmeClient) ObtainSSL(domains []string, privateKey crypto.Signer) (cert
|
|||||||
EnableCommonName: true,
|
EnableCommonName: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
var certificates *certificate.Resource
|
var certificates *certificate.Resource
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -152,7 +153,9 @@ func (c *AcmeClient) ObtainSSL(domains []string, privateKey crypto.Signer) (cert
|
|||||||
if isHTTP503Error(err) && attempt < maxRetryAttempts {
|
if isHTTP503Error(err) && attempt < maxRetryAttempts {
|
||||||
global.LOG.Warnf("ACME server returned 503, retrying in %v (attempt %d/%d)",
|
global.LOG.Warnf("ACME server returned 503, retrying in %v (attempt %d/%d)",
|
||||||
retryDelayOn503, attempt, maxRetryAttempts)
|
retryDelayOn503, attempt, maxRetryAttempts)
|
||||||
time.Sleep(retryDelayOn503)
|
if err := waitForRetry(ctx, retryDelayOn503); err != nil {
|
||||||
|
return certificate.Resource{}, err
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,8 +166,10 @@ func (c *AcmeClient) ObtainSSL(domains []string, privateKey crypto.Signer) (cert
|
|||||||
return certificate.Resource{}, err
|
return certificate.Resource{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AcmeClient) ObtainIPSSL(ipAddress string, privKey crypto.Signer) (certificate.Resource, error) {
|
func (c *AcmeClient) ObtainIPSSL(ctx context.Context, ipAddress string, privKey crypto.Signer) (certificate.Resource, error) {
|
||||||
defer c.releaseDNSLock()
|
unlockDNSChallenge := c.lockDNSChallenge()
|
||||||
|
defer unlockDNSChallenge()
|
||||||
|
|
||||||
csrTemplate := &x509.CertificateRequest{
|
csrTemplate := &x509.CertificateRequest{
|
||||||
Subject: pkix.Name{
|
Subject: pkix.Name{
|
||||||
CommonName: "",
|
CommonName: "",
|
||||||
@@ -192,8 +197,6 @@ func (c *AcmeClient) ObtainIPSSL(ipAddress string, privKey crypto.Signer) (certi
|
|||||||
Bundle: true,
|
Bundle: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
var certificates *certificate.Resource
|
var certificates *certificate.Resource
|
||||||
for attempt := 1; attempt <= maxRetryAttempts; attempt++ {
|
for attempt := 1; attempt <= maxRetryAttempts; attempt++ {
|
||||||
certificates, err = c.Client.Certificate.ObtainForCSR(ctx, req)
|
certificates, err = c.Client.Certificate.ObtainForCSR(ctx, req)
|
||||||
@@ -204,7 +207,9 @@ func (c *AcmeClient) ObtainIPSSL(ipAddress string, privKey crypto.Signer) (certi
|
|||||||
if isHTTP503Error(err) && attempt < maxRetryAttempts {
|
if isHTTP503Error(err) && attempt < maxRetryAttempts {
|
||||||
global.LOG.Warnf("ACME server returned 503 for IP SSL, retrying in %v (attempt %d/%d)",
|
global.LOG.Warnf("ACME server returned 503 for IP SSL, retrying in %v (attempt %d/%d)",
|
||||||
retryDelayOn503, attempt, maxRetryAttempts)
|
retryDelayOn503, attempt, maxRetryAttempts)
|
||||||
time.Sleep(retryDelayOn503)
|
if err := waitForRetry(ctx, retryDelayOn503); err != nil {
|
||||||
|
return certificate.Resource{}, err
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,3 +222,48 @@ func (c *AcmeClient) ObtainIPSSL(ipAddress string, privKey crypto.Signer) (certi
|
|||||||
func (c *AcmeClient) RevokeSSL(pemSSL []byte) error {
|
func (c *AcmeClient) RevokeSSL(pemSSL []byte) error {
|
||||||
return c.Client.Certificate.Revoke(context.Background(), pemSSL)
|
return c.Client.Certificate.Revoke(context.Background(), pemSSL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *AcmeClient) lockDNSChallenge() func() {
|
||||||
|
if c.dnsChallengeConfig == nil {
|
||||||
|
return func() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsChallengeMu.Lock()
|
||||||
|
|
||||||
|
oldCNAME, hadCNAME := os.LookupEnv("LEGO_DISABLE_CNAME_SUPPORT")
|
||||||
|
if c.dnsChallengeConfig.disableCNAME {
|
||||||
|
_ = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", "true")
|
||||||
|
} else {
|
||||||
|
_ = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", "false")
|
||||||
|
}
|
||||||
|
|
||||||
|
previousClient := dns01.DefaultClient()
|
||||||
|
// lego v5 removed dns01.AddRecursiveNameservers and dns01.AddDNSTimeout;
|
||||||
|
// configure them via dns01.NewClient(&dns01.Options{...}) + SetDefaultClient.
|
||||||
|
dns01.SetDefaultClient(dns01.NewClient(&dns01.Options{
|
||||||
|
RecursiveNameservers: c.dnsChallengeConfig.recursiveNameservers,
|
||||||
|
Timeout: c.dnsChallengeConfig.timeout,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
dns01.SetDefaultClient(previousClient)
|
||||||
|
if hadCNAME {
|
||||||
|
_ = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", oldCNAME)
|
||||||
|
} else {
|
||||||
|
_ = os.Unsetenv("LEGO_DISABLE_CNAME_SUPPORT")
|
||||||
|
}
|
||||||
|
dnsChallengeMu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForRetry(ctx context.Context, delay time.Duration) error {
|
||||||
|
timer := time.NewTimer(delay)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-timer.C:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package ssl
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v5/challenge"
|
"github.com/go-acme/lego/v5/challenge"
|
||||||
@@ -95,7 +97,7 @@ var (
|
|||||||
manualDnsTimeout = 10 * time.Minute
|
manualDnsTimeout = 10 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, error) {
|
func getDNSProviderConfig(dnsType DnsType, params string, httpClient *http.Client) (challenge.Provider, error) {
|
||||||
var (
|
var (
|
||||||
param DNSParam
|
param DNSParam
|
||||||
p challenge.Provider
|
p challenge.Provider
|
||||||
@@ -106,7 +108,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
}
|
}
|
||||||
switch dnsType {
|
switch dnsType {
|
||||||
case AliYun:
|
case AliYun:
|
||||||
config := alidns.NewDefaultConfig()
|
config := newDNSProviderConfig(alidns.NewDefaultConfig(), httpClient)
|
||||||
config.SecretKey = param.SecretKey
|
config.SecretKey = param.SecretKey
|
||||||
config.APIKey = param.AccessKey
|
config.APIKey = param.AccessKey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -114,7 +116,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = alidns.NewDNSProviderConfig(config)
|
p, err = alidns.NewDNSProviderConfig(config)
|
||||||
case AliESA:
|
case AliESA:
|
||||||
config := aliesa.NewDefaultConfig()
|
config := newDNSProviderConfig(aliesa.NewDefaultConfig(), httpClient)
|
||||||
config.SecretKey = param.SecretKey
|
config.SecretKey = param.SecretKey
|
||||||
config.APIKey = param.AccessKey
|
config.APIKey = param.AccessKey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -122,7 +124,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = aliesa.NewDNSProviderConfig(config)
|
p, err = aliesa.NewDNSProviderConfig(config)
|
||||||
case AWSRoute53:
|
case AWSRoute53:
|
||||||
config := route53.NewDefaultConfig()
|
config := newDNSProviderConfig(route53.NewDefaultConfig(), httpClient)
|
||||||
config.AccessKeyID = param.AccessKey
|
config.AccessKeyID = param.AccessKey
|
||||||
config.SecretAccessKey = param.SecretKey
|
config.SecretAccessKey = param.SecretKey
|
||||||
config.Region = param.Region
|
config.Region = param.Region
|
||||||
@@ -135,7 +137,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = route53.NewDNSProviderConfig(config)
|
p, err = route53.NewDNSProviderConfig(config)
|
||||||
case CloudFlare:
|
case CloudFlare:
|
||||||
config := cloudflare.NewDefaultConfig()
|
config := newDNSProviderConfig(cloudflare.NewDefaultConfig(), httpClient)
|
||||||
config.AuthEmail = param.Email
|
config.AuthEmail = param.Email
|
||||||
config.AuthToken = param.APIkey
|
config.AuthToken = param.APIkey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -143,7 +145,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = cloudflare.NewDNSProviderConfig(config)
|
p, err = cloudflare.NewDNSProviderConfig(config)
|
||||||
case CloudDns:
|
case CloudDns:
|
||||||
config := clouddns.NewDefaultConfig()
|
config := newDNSProviderConfig(clouddns.NewDefaultConfig(), httpClient)
|
||||||
config.ClientID = param.ClientID
|
config.ClientID = param.ClientID
|
||||||
config.Email = param.Email
|
config.Email = param.Email
|
||||||
config.Password = param.Password
|
config.Password = param.Password
|
||||||
@@ -152,7 +154,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = clouddns.NewDNSProviderConfig(config)
|
p, err = clouddns.NewDNSProviderConfig(config)
|
||||||
case NameCheap:
|
case NameCheap:
|
||||||
config := namecheap.NewDefaultConfig()
|
config := newDNSProviderConfig(namecheap.NewDefaultConfig(), httpClient)
|
||||||
config.APIKey = param.APIkey
|
config.APIKey = param.APIkey
|
||||||
config.APIUser = param.APIUser
|
config.APIUser = param.APIUser
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -160,14 +162,14 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = namecheap.NewDNSProviderConfig(config)
|
p, err = namecheap.NewDNSProviderConfig(config)
|
||||||
case NameSilo:
|
case NameSilo:
|
||||||
config := namesilo.NewDefaultConfig()
|
config := newDNSProviderConfig(namesilo.NewDefaultConfig(), httpClient)
|
||||||
config.APIKey = param.APIkey
|
config.APIKey = param.APIkey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
config.PollingInterval = pollingInterval
|
config.PollingInterval = pollingInterval
|
||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = namesilo.NewDNSProviderConfig(config)
|
p, err = namesilo.NewDNSProviderConfig(config)
|
||||||
case Godaddy:
|
case Godaddy:
|
||||||
config := godaddy.NewDefaultConfig()
|
config := newDNSProviderConfig(godaddy.NewDefaultConfig(), httpClient)
|
||||||
config.APIKey = param.APIkey
|
config.APIKey = param.APIkey
|
||||||
config.APISecret = param.APISecret
|
config.APISecret = param.APISecret
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -175,7 +177,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = godaddy.NewDNSProviderConfig(config)
|
p, err = godaddy.NewDNSProviderConfig(config)
|
||||||
case NameCom:
|
case NameCom:
|
||||||
config := namedotcom.NewDefaultConfig()
|
config := newDNSProviderConfig(namedotcom.NewDefaultConfig(), httpClient)
|
||||||
config.APIToken = param.Token
|
config.APIToken = param.Token
|
||||||
config.Username = param.APIUser
|
config.Username = param.APIUser
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -183,7 +185,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = namedotcom.NewDNSProviderConfig(config)
|
p, err = namedotcom.NewDNSProviderConfig(config)
|
||||||
case TencentCloud:
|
case TencentCloud:
|
||||||
config := tencentcloud.NewDefaultConfig()
|
config := newDNSProviderConfig(tencentcloud.NewDefaultConfig(), httpClient)
|
||||||
config.SecretID = param.SecretID
|
config.SecretID = param.SecretID
|
||||||
config.SecretKey = param.SecretKey
|
config.SecretKey = param.SecretKey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -191,14 +193,14 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = tencentcloud.NewDNSProviderConfig(config)
|
p, err = tencentcloud.NewDNSProviderConfig(config)
|
||||||
case RainYun:
|
case RainYun:
|
||||||
config := rainyun.NewDefaultConfig()
|
config := newDNSProviderConfig(rainyun.NewDefaultConfig(), httpClient)
|
||||||
config.APIKey = param.APIkey
|
config.APIKey = param.APIkey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
config.PollingInterval = pollingInterval
|
config.PollingInterval = pollingInterval
|
||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = rainyun.NewDNSProviderConfig(config)
|
p, err = rainyun.NewDNSProviderConfig(config)
|
||||||
case Volcengine:
|
case Volcengine:
|
||||||
config := volcengine.NewDefaultConfig()
|
config := newDNSProviderConfig(volcengine.NewDefaultConfig(), httpClient)
|
||||||
config.SecretKey = param.SecretKey
|
config.SecretKey = param.SecretKey
|
||||||
config.AccessKey = param.AccessKey
|
config.AccessKey = param.AccessKey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -206,7 +208,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = volcengine.NewDNSProviderConfig(config)
|
p, err = volcengine.NewDNSProviderConfig(config)
|
||||||
case HuaweiCloud:
|
case HuaweiCloud:
|
||||||
config := huaweicloud.NewDefaultConfig()
|
config := newDNSProviderConfig(huaweicloud.NewDefaultConfig(), httpClient)
|
||||||
config.AccessKeyID = param.AccessKey
|
config.AccessKeyID = param.AccessKey
|
||||||
config.SecretAccessKey = param.SecretKey
|
config.SecretAccessKey = param.SecretKey
|
||||||
config.Region = param.Region
|
config.Region = param.Region
|
||||||
@@ -218,19 +220,19 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = int32(ttl)
|
config.TTL = int32(ttl)
|
||||||
p, err = huaweicloud.NewDNSProviderConfig(config)
|
p, err = huaweicloud.NewDNSProviderConfig(config)
|
||||||
case FreeMyIP:
|
case FreeMyIP:
|
||||||
config := freemyip.NewDefaultConfig()
|
config := newDNSProviderConfig(freemyip.NewDefaultConfig(), httpClient)
|
||||||
config.Token = param.Token
|
config.Token = param.Token
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
config.PollingInterval = pollingInterval
|
config.PollingInterval = pollingInterval
|
||||||
p, err = freemyip.NewDNSProviderConfig(config)
|
p, err = freemyip.NewDNSProviderConfig(config)
|
||||||
case Vercel:
|
case Vercel:
|
||||||
config := vercel.NewDefaultConfig()
|
config := newDNSProviderConfig(vercel.NewDefaultConfig(), httpClient)
|
||||||
config.AuthToken = param.Token
|
config.AuthToken = param.Token
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
config.PollingInterval = pollingInterval
|
config.PollingInterval = pollingInterval
|
||||||
p, err = vercel.NewDNSProviderConfig(config)
|
p, err = vercel.NewDNSProviderConfig(config)
|
||||||
case Spaceship:
|
case Spaceship:
|
||||||
config := spaceship.NewDefaultConfig()
|
config := newDNSProviderConfig(spaceship.NewDefaultConfig(), httpClient)
|
||||||
config.APIKey = param.APIkey
|
config.APIKey = param.APIkey
|
||||||
config.APISecret = param.APISecret
|
config.APISecret = param.APISecret
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -238,7 +240,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = spaceship.NewDNSProviderConfig(config)
|
p, err = spaceship.NewDNSProviderConfig(config)
|
||||||
case WestCN:
|
case WestCN:
|
||||||
config := westcn.NewDefaultConfig()
|
config := newDNSProviderConfig(westcn.NewDefaultConfig(), httpClient)
|
||||||
config.Username = param.Username
|
config.Username = param.Username
|
||||||
config.Password = param.Password
|
config.Password = param.Password
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -246,7 +248,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = westcn.NewDNSProviderConfig(config)
|
p, err = westcn.NewDNSProviderConfig(config)
|
||||||
case ClouDNS:
|
case ClouDNS:
|
||||||
config := cloudns.NewDefaultConfig()
|
config := newDNSProviderConfig(cloudns.NewDefaultConfig(), httpClient)
|
||||||
config.AuthID = param.AuthID
|
config.AuthID = param.AuthID
|
||||||
config.SubAuthID = param.SubAuthID
|
config.SubAuthID = param.SubAuthID
|
||||||
config.AuthPassword = param.AuthPassword
|
config.AuthPassword = param.AuthPassword
|
||||||
@@ -255,7 +257,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = cloudns.NewDNSProviderConfig(config)
|
p, err = cloudns.NewDNSProviderConfig(config)
|
||||||
case RegRu:
|
case RegRu:
|
||||||
config := regru.NewDefaultConfig()
|
config := newDNSProviderConfig(regru.NewDefaultConfig(), httpClient)
|
||||||
config.Username = param.Username
|
config.Username = param.Username
|
||||||
config.Password = param.Password
|
config.Password = param.Password
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -263,14 +265,14 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = regru.NewDNSProviderConfig(config)
|
p, err = regru.NewDNSProviderConfig(config)
|
||||||
case Dynu:
|
case Dynu:
|
||||||
config := dynu.NewDefaultConfig()
|
config := newDNSProviderConfig(dynu.NewDefaultConfig(), httpClient)
|
||||||
config.APIKey = param.APIkey
|
config.APIKey = param.APIkey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
config.PollingInterval = pollingInterval
|
config.PollingInterval = pollingInterval
|
||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = dynu.NewDNSProviderConfig(config)
|
p, err = dynu.NewDNSProviderConfig(config)
|
||||||
case Dynadot:
|
case Dynadot:
|
||||||
config := dynadot.NewDefaultConfig()
|
config := newDNSProviderConfig(dynadot.NewDefaultConfig(), httpClient)
|
||||||
config.APIKey = param.APIkey
|
config.APIKey = param.APIkey
|
||||||
config.APISecret = param.APISecret
|
config.APISecret = param.APISecret
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -278,7 +280,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = dynadot.NewDNSProviderConfig(config)
|
p, err = dynadot.NewDNSProviderConfig(config)
|
||||||
case BaiduCloud:
|
case BaiduCloud:
|
||||||
config := baiducloud.NewDefaultConfig()
|
config := newDNSProviderConfig(baiducloud.NewDefaultConfig(), httpClient)
|
||||||
config.AccessKeyID = param.AccessKey
|
config.AccessKeyID = param.AccessKey
|
||||||
config.SecretAccessKey = param.SecretKey
|
config.SecretAccessKey = param.SecretKey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -286,7 +288,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = baiducloud.NewDNSProviderConfig(config)
|
p, err = baiducloud.NewDNSProviderConfig(config)
|
||||||
case Ovh:
|
case Ovh:
|
||||||
config := ovh.NewDefaultConfig()
|
config := newDNSProviderConfig(ovh.NewDefaultConfig(), httpClient)
|
||||||
config.APIEndpoint = param.Endpoint
|
config.APIEndpoint = param.Endpoint
|
||||||
config.AccessToken = param.AccessToken
|
config.AccessToken = param.AccessToken
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -294,12 +296,12 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = ovh.NewDNSProviderConfig(config)
|
p, err = ovh.NewDNSProviderConfig(config)
|
||||||
case AcmeDNS:
|
case AcmeDNS:
|
||||||
config := acmedns.NewDefaultConfig()
|
config := newDNSProviderConfig(acmedns.NewDefaultConfig(), httpClient)
|
||||||
config.APIBase = param.Endpoint
|
config.APIBase = param.Endpoint
|
||||||
config.StorageBaseURL = param.BaseURL
|
config.StorageBaseURL = param.BaseURL
|
||||||
p, err = acmedns.NewDNSProviderConfig(config)
|
p, err = acmedns.NewDNSProviderConfig(config)
|
||||||
case PorkBun:
|
case PorkBun:
|
||||||
config := porkbun.NewDefaultConfig()
|
config := newDNSProviderConfig(porkbun.NewDefaultConfig(), httpClient)
|
||||||
config.APIKey = param.APIkey
|
config.APIKey = param.APIkey
|
||||||
config.SecretAPIKey = param.SecretKey
|
config.SecretAPIKey = param.SecretKey
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -307,7 +309,7 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
config.TTL = ttl
|
config.TTL = ttl
|
||||||
p, err = porkbun.NewDNSProviderConfig(config)
|
p, err = porkbun.NewDNSProviderConfig(config)
|
||||||
case Technitium:
|
case Technitium:
|
||||||
config := technitium.NewDefaultConfig()
|
config := newDNSProviderConfig(technitium.NewDefaultConfig(), httpClient)
|
||||||
config.BaseURL = param.BaseURL
|
config.BaseURL = param.BaseURL
|
||||||
config.APIToken = param.Token
|
config.APIToken = param.Token
|
||||||
config.PropagationTimeout = propagationTimeout
|
config.PropagationTimeout = propagationTimeout
|
||||||
@@ -331,3 +333,27 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
|||||||
}
|
}
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newDNSProviderConfig[T any](config *T, httpClient *http.Client) *T {
|
||||||
|
setDNSProviderHTTPClient(config, httpClient)
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDNSProviderHTTPClient(config any, httpClient *http.Client) {
|
||||||
|
if config == nil || httpClient == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
value := reflect.ValueOf(config)
|
||||||
|
if value.Kind() != reflect.Ptr || value.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
field := value.Elem().FieldByName("HTTPClient")
|
||||||
|
if !field.IsValid() || !field.CanSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httpClientType := reflect.TypeOf((*http.Client)(nil))
|
||||||
|
if field.Type() != httpClientType {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(httpClient))
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,14 +10,17 @@ import (
|
|||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/go-acme/lego/v5/certificate"
|
"github.com/go-acme/lego/v5/certificate"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"golang.org/x/crypto/acme"
|
"golang.org/x/crypto/acme"
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ManualClient struct {
|
type ManualClient struct {
|
||||||
@@ -30,7 +33,34 @@ type RequestCertRequest struct {
|
|||||||
WebsiteSSL *model.WebsiteSSL
|
WebsiteSSL *model.WebsiteSSL
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCustomAcmeClient(acmeAccount *model.WebsiteAcmeAccount, logger *log.Logger) (*ManualClient, error) {
|
var manualOrders sync.Map
|
||||||
|
|
||||||
|
func loadManualOrder(sslID uint) (*acme.Order, bool) {
|
||||||
|
value, ok := manualOrders.Load(sslID)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
order, ok := value.(*acme.Order)
|
||||||
|
return order, ok && order != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeManualOrder(sslID uint, order *acme.Order) {
|
||||||
|
if order == nil {
|
||||||
|
manualOrders.Delete(sslID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
manualOrders.Store(sslID, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteManualOrder(sslID uint, order *acme.Order) {
|
||||||
|
if order == nil {
|
||||||
|
manualOrders.Delete(sslID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
manualOrders.CompareAndDelete(sslID, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomAcmeClient(acmeAccount *model.WebsiteAcmeAccount, proxy *dto.SystemProxy, logger *log.Logger) (*ManualClient, error) {
|
||||||
key, err := parsePrivateKeyPEM([]byte(acmeAccount.PrivateKey))
|
key, err := parsePrivateKeyPEM([]byte(acmeAccount.PrivateKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -38,9 +68,20 @@ func NewCustomAcmeClient(acmeAccount *model.WebsiteAcmeAccount, logger *log.Logg
|
|||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.Default()
|
logger = log.Default()
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
|
proxyURL string
|
||||||
|
proxyUser string
|
||||||
|
proxyPassword string
|
||||||
|
)
|
||||||
|
if proxy != nil {
|
||||||
|
proxyURL = fmt.Sprintf("%s://%s:%s", proxy.Type, proxy.URL, proxy.Port)
|
||||||
|
proxyUser = proxy.User
|
||||||
|
proxyPassword = proxy.Password
|
||||||
|
}
|
||||||
|
|
||||||
client := &acme.Client{
|
client := &acme.Client{
|
||||||
Key: key,
|
Key: key,
|
||||||
|
HTTPClient: createHTTPClientWithProxy(proxyURL, proxyUser, proxyPassword),
|
||||||
DirectoryURL: getCaDirURL(acmeAccount.Type, acmeAccount.CaDirURL),
|
DirectoryURL: getCaDirURL(acmeAccount.Type, acmeAccount.CaDirURL),
|
||||||
}
|
}
|
||||||
return &ManualClient{
|
return &ManualClient{
|
||||||
@@ -61,7 +102,7 @@ func (c *ManualClient) GetDNSResolve(ctx context.Context, websiteSSL *model.Webs
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Check if we have an existing valid order for this SSL
|
// Check if we have an existing valid order for this SSL
|
||||||
existingOrder, exists := Orders[websiteSSL.ID]
|
existingOrder, exists := loadManualOrder(websiteSSL.ID)
|
||||||
if exists && existingOrder != nil {
|
if exists && existingOrder != nil {
|
||||||
// Verify the order is still valid (not expired and still pending)
|
// Verify the order is still valid (not expired and still pending)
|
||||||
// If Expires is zero, order is still valid (ACME doesn't always set expiry immediately)
|
// If Expires is zero, order is still valid (ACME doesn't always set expiry immediately)
|
||||||
@@ -75,7 +116,7 @@ func (c *ManualClient) GetDNSResolve(ctx context.Context, websiteSSL *model.Webs
|
|||||||
// If extraction failed, fall through to create a new order
|
// If extraction failed, fall through to create a new order
|
||||||
}
|
}
|
||||||
// Existing order is expired or invalid, remove it
|
// Existing order is expired or invalid, remove it
|
||||||
delete(Orders, websiteSSL.ID)
|
deleteManualOrder(websiteSSL.ID, existingOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new order
|
// Create a new order
|
||||||
@@ -83,7 +124,7 @@ func (c *ManualClient) GetDNSResolve(ctx context.Context, websiteSSL *model.Webs
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
Orders[websiteSSL.ID] = order
|
storeManualOrder(websiteSSL.ID, order)
|
||||||
|
|
||||||
return c.extractDNSChallenges(ctx, order)
|
return c.extractDNSChallenges(ctx, order)
|
||||||
}
|
}
|
||||||
@@ -178,6 +219,9 @@ func (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string,
|
|||||||
c.logger.Printf("[INFO] [%s] acme: Checking TXT record %s", domain, expectedRecord)
|
c.logger.Printf("[INFO] [%s] acme: Checking TXT record %s", domain, expectedRecord)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.logger.Printf("[INFO] [%s] acme: Checking DNS record propagation.", domain)
|
c.logger.Printf("[INFO] [%s] acme: Checking DNS record propagation.", domain)
|
||||||
var currentRecords map[string][]string
|
var currentRecords map[string][]string
|
||||||
var queryErr error
|
var queryErr error
|
||||||
@@ -218,7 +262,9 @@ func (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string,
|
|||||||
c.logger.Printf("[INFO] [%s] TXT record mismatch for %s: expected %s, got %v", domain, domain, expectedRecord, providedRecords)
|
c.logger.Printf("[INFO] [%s] TXT record mismatch for %s: expected %s, got %v", domain, domain, expectedRecord, providedRecords)
|
||||||
return fmt.Errorf("TXT record mismatch for %s: expected %s, got %v", domain, expectedRecord, providedRecords)
|
return fmt.Errorf("TXT record mismatch for %s: expected %s, got %v", domain, expectedRecord, providedRecords)
|
||||||
}
|
}
|
||||||
time.Sleep(pollingInterval)
|
if err := waitForRetry(ctx, pollingInterval); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.client.Accept(ctx, dnsChallenge)
|
_, err = c.client.Accept(ctx, dnsChallenge)
|
||||||
@@ -226,7 +272,9 @@ func (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string,
|
|||||||
return fmt.Errorf("failed to accept challenge: %v", err)
|
return fmt.Errorf("failed to accept challenge: %v", err)
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
time.Sleep(pollingInterval)
|
if err := waitForRetry(ctx, pollingInterval); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
authz, err = c.client.GetAuthorization(ctx, authzURL)
|
authz, err = c.client.GetAuthorization(ctx, authzURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get authorization while polling: %v", err)
|
return fmt.Errorf("failed to get authorization while polling: %v", err)
|
||||||
@@ -299,11 +347,11 @@ func (c *ManualClient) RequestCertificate(ctx context.Context, websiteSSL *model
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
order, ok := Orders[websiteSSL.ID]
|
order, ok := loadManualOrder(websiteSSL.ID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("order not found")
|
return res, fmt.Errorf("order not found")
|
||||||
}
|
}
|
||||||
defer delete(Orders, websiteSSL.ID)
|
defer deleteManualOrder(websiteSSL.ID, order)
|
||||||
|
|
||||||
for _, authzURL := range order.AuthzURLs {
|
for _, authzURL := range order.AuthzURLs {
|
||||||
if err := c.handleAuthorization(ctx, authzURL, getNameservers(*websiteSSL)); err != nil {
|
if err := c.handleAuthorization(ctx, authzURL, getNameservers(*websiteSSL)); err != nil {
|
||||||
|
|||||||
+1
-16
@@ -219,7 +219,7 @@ func (c *CommandHelper) pipeContext() (context.Context, context.CancelFunc) {
|
|||||||
func (c *CommandHelper) buildPipeCommands(ctx context.Context, commands []PipeCommand) []*exec.Cmd {
|
func (c *CommandHelper) buildPipeCommands(ctx context.Context, commands []PipeCommand) []*exec.Cmd {
|
||||||
cmds := make([]*exec.Cmd, 0, len(commands))
|
cmds := make([]*exec.Cmd, 0, len(commands))
|
||||||
for _, item := range commands {
|
for _, item := range commands {
|
||||||
cmdItem := exec.CommandContext(ctx, item.Name, filterEmptyArgs(item.Args)...)
|
cmdItem := exec.CommandContext(ctx, item.Name, item.Args...)
|
||||||
cmdItem.Env = append(os.Environ(), c.env...)
|
cmdItem.Env = append(os.Environ(), c.env...)
|
||||||
cmdItem.Env = append(cmdItem.Env, item.Env...)
|
cmdItem.Env = append(cmdItem.Env, item.Env...)
|
||||||
cmdItem.Dir = c.workDir
|
cmdItem.Dir = c.workDir
|
||||||
@@ -284,7 +284,6 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
|||||||
var newContext context.Context
|
var newContext context.Context
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
var outputFile *os.File
|
var outputFile *os.File
|
||||||
arg = filterEmptyArgs(arg)
|
|
||||||
|
|
||||||
if c.timeout != 0 {
|
if c.timeout != 0 {
|
||||||
if c.context == nil {
|
if c.context == nil {
|
||||||
@@ -379,20 +378,6 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterEmptyArgs(args []string) []string {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
filtered := args[:0]
|
|
||||||
for _, arg := range args {
|
|
||||||
if arg == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
filtered = append(filtered, arg)
|
|
||||||
}
|
|
||||||
return filtered
|
|
||||||
}
|
|
||||||
|
|
||||||
func contextDone(ctx context.Context) <-chan struct{} {
|
func contextDone(ctx context.Context) <-chan struct{} {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -56,10 +56,6 @@ export const listAppNodes = () => {
|
|||||||
return http.get<Array<Setting.NodeAppItem>>(`/core/xpack/nodes/apps/update`, {}, { timeout: TimeoutEnum.T_60S });
|
return http.get<Array<Setting.NodeAppItem>>(`/core/xpack/nodes/apps/update`, {}, { timeout: TimeoutEnum.T_60S });
|
||||||
};
|
};
|
||||||
|
|
||||||
// enterprise
|
|
||||||
export const loadNodeByUser = () => {
|
|
||||||
return http.get<Array<Setting.NodeItem>>(`/core/enterprise/users/nodes`);
|
|
||||||
};
|
|
||||||
export const uploadEnterpriseLicense = (params: FormData) => {
|
export const uploadEnterpriseLicense = (params: FormData) => {
|
||||||
return http.upload('/core/enterprise/licenses/upload', params);
|
return http.upload('/core/enterprise/licenses/upload', params);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export const getSSL = (id: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const obtainSSL = (req: Website.SSLObtain) => {
|
export const obtainSSL = (req: Website.SSLObtain) => {
|
||||||
return http.post<any>(`/websites/ssl/obtain`, req);
|
return http.post<any>(`/websites/ssl/obtain`, req, TimeoutEnum.T_10M);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateSSL = (req: Website.SSLUpdate) => {
|
export const updateSSL = (req: Website.SSLUpdate) => {
|
||||||
|
|||||||
@@ -171,10 +171,11 @@ const closeRightClick = () => {
|
|||||||
const disabled = computed(() => {
|
const disabled = computed(() => {
|
||||||
return function (btn: any) {
|
return function (btn: any) {
|
||||||
let permissionDisabled = false;
|
let permissionDisabled = false;
|
||||||
|
const permissionOptions = { nodeAdmin: btn.nodeAdmin === true };
|
||||||
if (btn.permission === true) {
|
if (btn.permission === true) {
|
||||||
permissionDisabled = !hasManagePermissionAccess();
|
permissionDisabled = !hasManagePermissionAccess(undefined, permissionOptions);
|
||||||
} else if (btn.permission !== undefined) {
|
} else if (btn.permission !== undefined) {
|
||||||
permissionDisabled = !hasPermissionAccess(btn.permission);
|
permissionDisabled = !hasPermissionAccess(btn.permission, permissionOptions);
|
||||||
}
|
}
|
||||||
const buttonDisabled =
|
const buttonDisabled =
|
||||||
typeof btn.disabled === 'function' ? btn.disabled(rightClick.value.currentRow) : btn.disabled;
|
typeof btn.disabled === 'function' ? btn.disabled(rightClick.value.currentRow) : btn.disabled;
|
||||||
|
|||||||
@@ -206,10 +206,11 @@ const getMoreButtons = (row: any) => {
|
|||||||
|
|
||||||
const isButtonDisabled = (button: FuTableOperationButton, row: any) => {
|
const isButtonDisabled = (button: FuTableOperationButton, row: any) => {
|
||||||
let permissionDisabled = false;
|
let permissionDisabled = false;
|
||||||
|
const permissionOptions = { nodeAdmin: button.nodeAdmin === true };
|
||||||
if (button.permission === true) {
|
if (button.permission === true) {
|
||||||
permissionDisabled = !hasManagePermissionAccess();
|
permissionDisabled = !hasManagePermissionAccess(undefined, permissionOptions);
|
||||||
} else if (button.permission !== undefined) {
|
} else if (button.permission !== undefined) {
|
||||||
permissionDisabled = !hasPermissionAccess(button.permission);
|
permissionDisabled = !hasPermissionAccess(button.permission, permissionOptions);
|
||||||
}
|
}
|
||||||
return permissionDisabled || Boolean(resolveMaybeFn(button.disabled ?? false, row));
|
return permissionDisabled || Boolean(resolveMaybeFn(button.disabled ?? false, row));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export interface FuTableOperationButton {
|
|||||||
click?: (row: any) => void;
|
click?: (row: any) => void;
|
||||||
disabled?: boolean | ((row: any) => boolean);
|
disabled?: boolean | ((row: any) => boolean);
|
||||||
permission?: true | PermissionBindingValue;
|
permission?: true | PermissionBindingValue;
|
||||||
|
nodeAdmin?: boolean;
|
||||||
show?: boolean | ((row: any) => boolean);
|
show?: boolean | ((row: any) => boolean);
|
||||||
type?: string;
|
type?: string;
|
||||||
icon?: any;
|
icon?: any;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const handleChange = (value) => {
|
|||||||
const search = async () => {
|
const search = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await listNodes('all');
|
const res = await listNodes('all');
|
||||||
nodes.value = res.data || [];
|
nodes.value = res || [];
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,61 @@ function initChart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const grid = props.option.grid || { left: '7%', right: '7%', bottom: '20%' };
|
const grid = props.option.grid || { left: '7%', right: '7%', bottom: '20%' };
|
||||||
|
const defaultTooltip = {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: function (datas: any) {
|
||||||
|
let res = datas[0].name + '<br/>';
|
||||||
|
switch (props.option.formatStr) {
|
||||||
|
case 'KB/s':
|
||||||
|
for (const item of datas) {
|
||||||
|
res +=
|
||||||
|
item.marker +
|
||||||
|
' ' +
|
||||||
|
item.seriesName +
|
||||||
|
i18n.global.t('commons.colon') +
|
||||||
|
computeSizeFromKBs(item.data) +
|
||||||
|
'<br/>';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'KB':
|
||||||
|
for (const item of datas) {
|
||||||
|
res +=
|
||||||
|
item.marker +
|
||||||
|
' ' +
|
||||||
|
item.seriesName +
|
||||||
|
i18n.global.t('commons.colon') +
|
||||||
|
computeSizeFromKB(item.data) +
|
||||||
|
'<br/>';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'MB':
|
||||||
|
for (const item of datas) {
|
||||||
|
res +=
|
||||||
|
item.marker +
|
||||||
|
' ' +
|
||||||
|
item.seriesName +
|
||||||
|
i18n.global.t('commons.colon') +
|
||||||
|
computeSizeFromMB(item.data) +
|
||||||
|
'<br/>';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
for (const item of datas) {
|
||||||
|
res +=
|
||||||
|
item.marker +
|
||||||
|
' ' +
|
||||||
|
item.seriesName +
|
||||||
|
i18n.global.t('commons.colon') +
|
||||||
|
item.data +
|
||||||
|
props.option.formatStr +
|
||||||
|
'<br/>';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const tooltip = props.option.tooltip || defaultTooltip;
|
||||||
const option = {
|
const option = {
|
||||||
title: [
|
title: [
|
||||||
{
|
{
|
||||||
@@ -158,59 +213,10 @@ function initChart() {
|
|||||||
],
|
],
|
||||||
zlevel: 1,
|
zlevel: 1,
|
||||||
z: 1,
|
z: 1,
|
||||||
tooltip: props.option.tooltip || {
|
tooltip: {
|
||||||
trigger: 'axis',
|
appendToBody: true,
|
||||||
formatter: function (datas: any) {
|
...tooltip,
|
||||||
let res = datas[0].name + '<br/>';
|
extraCssText: `${tooltip.extraCssText || ''}; z-index: 3000;`,
|
||||||
switch (props.option.formatStr) {
|
|
||||||
case 'KB/s':
|
|
||||||
for (const item of datas) {
|
|
||||||
res +=
|
|
||||||
item.marker +
|
|
||||||
' ' +
|
|
||||||
item.seriesName +
|
|
||||||
i18n.global.t('commons.colon') +
|
|
||||||
computeSizeFromKBs(item.data) +
|
|
||||||
'<br/>';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'KB':
|
|
||||||
for (const item of datas) {
|
|
||||||
res +=
|
|
||||||
item.marker +
|
|
||||||
' ' +
|
|
||||||
item.seriesName +
|
|
||||||
i18n.global.t('commons.colon') +
|
|
||||||
computeSizeFromKB(item.data) +
|
|
||||||
'<br/>';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'MB':
|
|
||||||
for (const item of datas) {
|
|
||||||
res +=
|
|
||||||
item.marker +
|
|
||||||
' ' +
|
|
||||||
item.seriesName +
|
|
||||||
i18n.global.t('commons.colon') +
|
|
||||||
computeSizeFromMB(item.data) +
|
|
||||||
'<br/>';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
for (const item of datas) {
|
|
||||||
res +=
|
|
||||||
item.marker +
|
|
||||||
' ' +
|
|
||||||
item.seriesName +
|
|
||||||
i18n.global.t('commons.colon') +
|
|
||||||
item.data +
|
|
||||||
props.option.formatStr +
|
|
||||||
'<br/>';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
grid,
|
grid,
|
||||||
legend: itemSelect || {
|
legend: itemSelect || {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { App, Directive } from 'vue';
|
import { App, Directive } from 'vue';
|
||||||
import integerInput from './modules/integer';
|
import integerInput from './modules/integer';
|
||||||
import permission from './modules/permission';
|
import permission, { nodeAdminDirective } from './modules/permission';
|
||||||
|
|
||||||
const directivesList: { [key: string]: Directive } = {
|
const directivesList: { [key: string]: Directive } = {
|
||||||
'integer-input': integerInput,
|
'integer-input': integerInput,
|
||||||
|
'node-admin': nodeAdminDirective,
|
||||||
permission,
|
permission,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const PERMISSION_DISABLED_ATTR = 'data-permission-disabled';
|
|||||||
const PERMISSION_POINTER_EVENTS_ATTR = 'data-permission-pointer-events';
|
const PERMISSION_POINTER_EVENTS_ATTR = 'data-permission-pointer-events';
|
||||||
const PERMISSION_NATIVE_DISABLED_ATTR = 'data-permission-native-disabled';
|
const PERMISSION_NATIVE_DISABLED_ATTR = 'data-permission-native-disabled';
|
||||||
const PERMISSION_TABINDEX_ATTR = 'data-permission-tabindex';
|
const PERMISSION_TABINDEX_ATTR = 'data-permission-tabindex';
|
||||||
|
const NODE_ADMIN_PERMISSION_ATTR = 'data-node-admin-permission';
|
||||||
|
|
||||||
const getDisableTargets = (el: HTMLElement) => {
|
const getDisableTargets = (el: HTMLElement) => {
|
||||||
const targets = [el, ...Array.from(el.querySelectorAll<HTMLElement>('button, input, select, textarea'))];
|
const targets = [el, ...Array.from(el.querySelectorAll<HTMLElement>('button, input, select, textarea'))];
|
||||||
@@ -102,11 +103,16 @@ const getPermissionMode = (binding: DirectiveBinding<PermissionBindingValue>): P
|
|||||||
return binding.arg === 'view' ? 'view' : 'manage';
|
return binding.arg === 'view' ? 'view' : 'manage';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasNodeAdminDirective = (el: HTMLElement, vnode: VNode) => {
|
||||||
|
return el.hasAttribute(NODE_ADMIN_PERMISSION_ATTR) || !!vnode.dirs?.some((item) => item.dir === nodeAdminDirective);
|
||||||
|
};
|
||||||
|
|
||||||
const applyPermission = (el: HTMLElement, binding: DirectiveBinding<PermissionBindingValue>, vnode: VNode) => {
|
const applyPermission = (el: HTMLElement, binding: DirectiveBinding<PermissionBindingValue>, vnode: VNode) => {
|
||||||
|
const options = { nodeAdmin: hasNodeAdminDirective(el, vnode) };
|
||||||
const disabled =
|
const disabled =
|
||||||
getPermissionMode(binding) === 'view'
|
getPermissionMode(binding) === 'view'
|
||||||
? !hasPermissionAccess(binding.value)
|
? !hasPermissionAccess(binding.value, options)
|
||||||
: !hasManagePermissionAccess(binding.value);
|
: !hasManagePermissionAccess(binding.value, options);
|
||||||
const controller = getComponentPermissionController(vnode);
|
const controller = getComponentPermissionController(vnode);
|
||||||
|
|
||||||
if (controller?.setPermissionDisabled) {
|
if (controller?.setPermissionDisabled) {
|
||||||
@@ -130,4 +136,16 @@ const permissionDirective: Directive<HTMLElement, PermissionBindingValue> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const nodeAdminDirective: Directive<HTMLElement, boolean | undefined> = {
|
||||||
|
mounted(el) {
|
||||||
|
el.setAttribute(NODE_ADMIN_PERMISSION_ATTR, 'true');
|
||||||
|
},
|
||||||
|
updated(el) {
|
||||||
|
el.setAttribute(NODE_ADMIN_PERMISSION_ATTR, 'true');
|
||||||
|
},
|
||||||
|
unmounted(el) {
|
||||||
|
el.removeAttribute(NODE_ADMIN_PERMISSION_ATTR);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default permissionDirective;
|
export default permissionDirective;
|
||||||
|
|||||||
@@ -2054,11 +2054,13 @@ const message = {
|
|||||||
ai: 'AI',
|
ai: 'AI',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'AI Proxy',
|
ai_proxy: 'AI Proxy',
|
||||||
|
ai_benchmark: 'Benchmark',
|
||||||
groups: 'Group',
|
groups: 'Group',
|
||||||
hosts: 'Host',
|
hosts: 'Host',
|
||||||
apps: 'App',
|
apps: 'App',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: 'Website',
|
websites: 'Website',
|
||||||
|
monitor: 'Website Monitor',
|
||||||
containers: 'Container',
|
containers: 'Container',
|
||||||
files: 'File',
|
files: 'File',
|
||||||
runtimes: 'Runtime',
|
runtimes: 'Runtime',
|
||||||
@@ -2077,6 +2079,7 @@ const message = {
|
|||||||
commands: 'Quick Commands',
|
commands: 'Quick Commands',
|
||||||
opsReport: 'Ops Report',
|
opsReport: 'Ops Report',
|
||||||
users: 'User Management',
|
users: 'User Management',
|
||||||
|
roles: 'Role',
|
||||||
auth: 'Login Authentication',
|
auth: 'Login Authentication',
|
||||||
alert: 'Alert Notifications',
|
alert: 'Alert Notifications',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2109,11 +2109,13 @@ const message = {
|
|||||||
ai: 'IA',
|
ai: 'IA',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'Proxy de IA',
|
ai_proxy: 'Proxy de IA',
|
||||||
|
ai_benchmark: 'Benchmark',
|
||||||
groups: 'Grupo',
|
groups: 'Grupo',
|
||||||
hosts: 'Host',
|
hosts: 'Host',
|
||||||
apps: 'Aplicación',
|
apps: 'Aplicación',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: 'Sitio web',
|
websites: 'Sitio web',
|
||||||
|
monitor: 'Monitoreo de sitios web',
|
||||||
containers: 'Contenedor',
|
containers: 'Contenedor',
|
||||||
files: 'Archivo',
|
files: 'Archivo',
|
||||||
runtimes: 'Runtime',
|
runtimes: 'Runtime',
|
||||||
@@ -2132,6 +2134,7 @@ const message = {
|
|||||||
commands: 'Comandos rápidos',
|
commands: 'Comandos rápidos',
|
||||||
opsReport: 'Informe de operaciones',
|
opsReport: 'Informe de operaciones',
|
||||||
users: 'Gestión de usuarios',
|
users: 'Gestión de usuarios',
|
||||||
|
roles: 'Rol',
|
||||||
auth: 'Autenticación de inicio de sesión',
|
auth: 'Autenticación de inicio de sesión',
|
||||||
alert: 'Notificaciones de alerta',
|
alert: 'Notificaciones de alerta',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2080,11 +2080,13 @@ const message = {
|
|||||||
ai: 'AI',
|
ai: 'AI',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'AIプロキシ',
|
ai_proxy: 'AIプロキシ',
|
||||||
|
ai_benchmark: 'ベンチマーク',
|
||||||
groups: 'グループ',
|
groups: 'グループ',
|
||||||
hosts: 'ホスト',
|
hosts: 'ホスト',
|
||||||
apps: 'アプリケーション',
|
apps: 'アプリケーション',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: 'ウェブサイト',
|
websites: 'ウェブサイト',
|
||||||
|
monitor: 'ウェブサイト監視',
|
||||||
containers: 'コンテナ',
|
containers: 'コンテナ',
|
||||||
files: 'ファイル管理',
|
files: 'ファイル管理',
|
||||||
runtimes: 'ランタイム',
|
runtimes: 'ランタイム',
|
||||||
@@ -2103,6 +2105,7 @@ const message = {
|
|||||||
commands: 'クイックコマンド',
|
commands: 'クイックコマンド',
|
||||||
opsReport: '運用レポート',
|
opsReport: '運用レポート',
|
||||||
users: 'ユーザー管理',
|
users: 'ユーザー管理',
|
||||||
|
roles: 'ロール',
|
||||||
auth: 'ログイン認証',
|
auth: 'ログイン認証',
|
||||||
alert: 'アラート通知',
|
alert: 'アラート通知',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2038,11 +2038,13 @@ const message = {
|
|||||||
ai: 'AI',
|
ai: 'AI',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'AI 프록시',
|
ai_proxy: 'AI 프록시',
|
||||||
|
ai_benchmark: '벤치마크',
|
||||||
groups: '그룹',
|
groups: '그룹',
|
||||||
hosts: '호스트',
|
hosts: '호스트',
|
||||||
apps: '애플리케이션',
|
apps: '애플리케이션',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: '웹사이트',
|
websites: '웹사이트',
|
||||||
|
monitor: '웹사이트 모니터링',
|
||||||
containers: '컨테이너',
|
containers: '컨테이너',
|
||||||
files: '파일 관리',
|
files: '파일 관리',
|
||||||
runtimes: '실행 환경',
|
runtimes: '실행 환경',
|
||||||
@@ -2061,6 +2063,7 @@ const message = {
|
|||||||
commands: '빠른 명령',
|
commands: '빠른 명령',
|
||||||
opsReport: '운영 보고서',
|
opsReport: '운영 보고서',
|
||||||
users: '사용자 관리',
|
users: '사용자 관리',
|
||||||
|
roles: '역할',
|
||||||
auth: '로그인 인증',
|
auth: '로그인 인증',
|
||||||
alert: '경고 알림',
|
alert: '경고 알림',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2107,11 +2107,13 @@ const message = {
|
|||||||
ai: 'AI',
|
ai: 'AI',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'Proksi AI',
|
ai_proxy: 'Proksi AI',
|
||||||
|
ai_benchmark: 'Penanda Aras',
|
||||||
groups: 'Kumpulan',
|
groups: 'Kumpulan',
|
||||||
hosts: 'Hos',
|
hosts: 'Hos',
|
||||||
apps: 'Aplikasi',
|
apps: 'Aplikasi',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: 'Laman Web',
|
websites: 'Laman Web',
|
||||||
|
monitor: 'Pemantauan Laman Web',
|
||||||
containers: 'Kontena',
|
containers: 'Kontena',
|
||||||
files: 'Pengurusan Fail',
|
files: 'Pengurusan Fail',
|
||||||
runtimes: 'Persekitaran Jalankan',
|
runtimes: 'Persekitaran Jalankan',
|
||||||
@@ -2130,6 +2132,7 @@ const message = {
|
|||||||
commands: 'Perintah Pantas',
|
commands: 'Perintah Pantas',
|
||||||
opsReport: 'Laporan Operasi',
|
opsReport: 'Laporan Operasi',
|
||||||
users: 'Pengurusan pengguna',
|
users: 'Pengurusan pengguna',
|
||||||
|
roles: 'Peranan',
|
||||||
auth: 'Pengesahan log masuk',
|
auth: 'Pengesahan log masuk',
|
||||||
alert: 'Pemberitahuan amaran',
|
alert: 'Pemberitahuan amaran',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2221,11 +2221,13 @@ const message = {
|
|||||||
ai: 'AI',
|
ai: 'AI',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'Proxy de IA',
|
ai_proxy: 'Proxy de IA',
|
||||||
|
ai_benchmark: 'Benchmark',
|
||||||
groups: 'Grupos',
|
groups: 'Grupos',
|
||||||
hosts: 'Hosts',
|
hosts: 'Hosts',
|
||||||
apps: 'Aplicativos',
|
apps: 'Aplicativos',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: 'Sites',
|
websites: 'Sites',
|
||||||
|
monitor: 'Monitoramento de Sites',
|
||||||
containers: 'Contêineres',
|
containers: 'Contêineres',
|
||||||
files: 'Gerenciamento de Arquivos',
|
files: 'Gerenciamento de Arquivos',
|
||||||
runtimes: 'Ambientes de Execução',
|
runtimes: 'Ambientes de Execução',
|
||||||
@@ -2244,6 +2246,7 @@ const message = {
|
|||||||
commands: 'Comandos Rápidos',
|
commands: 'Comandos Rápidos',
|
||||||
opsReport: 'Relatório de Operações',
|
opsReport: 'Relatório de Operações',
|
||||||
users: 'Gerenciamento de usuários',
|
users: 'Gerenciamento de usuários',
|
||||||
|
roles: 'Função',
|
||||||
auth: 'Autenticação de login',
|
auth: 'Autenticação de login',
|
||||||
alert: 'Notificações de alerta',
|
alert: 'Notificações de alerta',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2094,11 +2094,13 @@ const message = {
|
|||||||
ai: 'AI',
|
ai: 'AI',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'AI Proxy',
|
ai_proxy: 'AI Proxy',
|
||||||
|
ai_benchmark: 'Бенчмарк',
|
||||||
groups: 'Группы',
|
groups: 'Группы',
|
||||||
hosts: 'Хосты',
|
hosts: 'Хосты',
|
||||||
apps: 'Приложения',
|
apps: 'Приложения',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: 'Вебсайты',
|
websites: 'Вебсайты',
|
||||||
|
monitor: 'Мониторинг веб-сайтов',
|
||||||
containers: 'Контейнеры',
|
containers: 'Контейнеры',
|
||||||
files: 'Управление файлами',
|
files: 'Управление файлами',
|
||||||
runtimes: 'Среды выполнения',
|
runtimes: 'Среды выполнения',
|
||||||
@@ -2117,6 +2119,7 @@ const message = {
|
|||||||
commands: 'Быстрые команды',
|
commands: 'Быстрые команды',
|
||||||
opsReport: 'Операционный отчет',
|
opsReport: 'Операционный отчет',
|
||||||
users: 'Управление пользователями',
|
users: 'Управление пользователями',
|
||||||
|
roles: 'Роль',
|
||||||
auth: 'Аутентификация входа',
|
auth: 'Аутентификация входа',
|
||||||
alert: 'Уведомления о тревогах',
|
alert: 'Уведомления о тревогах',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2103,11 +2103,13 @@ const message = {
|
|||||||
ai: 'AI',
|
ai: 'AI',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'AI Proxy',
|
ai_proxy: 'AI Proxy',
|
||||||
|
ai_benchmark: 'Benchmark',
|
||||||
groups: 'Grup',
|
groups: 'Grup',
|
||||||
hosts: 'Ana Bilgisayar',
|
hosts: 'Ana Bilgisayar',
|
||||||
apps: 'Uygulama',
|
apps: 'Uygulama',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: 'Website',
|
websites: 'Website',
|
||||||
|
monitor: 'Website İzleme',
|
||||||
containers: 'Konteyner',
|
containers: 'Konteyner',
|
||||||
files: 'Dosya',
|
files: 'Dosya',
|
||||||
runtimes: 'Çalışma Zamanı',
|
runtimes: 'Çalışma Zamanı',
|
||||||
@@ -2126,6 +2128,7 @@ const message = {
|
|||||||
commands: 'Hızlı Komutlar',
|
commands: 'Hızlı Komutlar',
|
||||||
opsReport: 'Operasyon Raporu',
|
opsReport: 'Operasyon Raporu',
|
||||||
users: 'Kullanıcı yönetimi',
|
users: 'Kullanıcı yönetimi',
|
||||||
|
roles: 'Rol',
|
||||||
auth: 'Giriş kimlik doğrulaması',
|
auth: 'Giriş kimlik doğrulaması',
|
||||||
alert: 'Uyarı bildirimleri',
|
alert: 'Uyarı bildirimleri',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1950,11 +1950,13 @@ const message = {
|
|||||||
ai: 'AI',
|
ai: 'AI',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'AI 網關',
|
ai_proxy: 'AI 網關',
|
||||||
|
ai_benchmark: '基準測試',
|
||||||
groups: '分組',
|
groups: '分組',
|
||||||
hosts: '主機',
|
hosts: '主機',
|
||||||
apps: '應用',
|
apps: '應用',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: '網站',
|
websites: '網站',
|
||||||
|
monitor: '網站監控',
|
||||||
containers: '容器',
|
containers: '容器',
|
||||||
files: '檔案管理',
|
files: '檔案管理',
|
||||||
runtimes: '執行環境',
|
runtimes: '執行環境',
|
||||||
@@ -1973,6 +1975,7 @@ const message = {
|
|||||||
commands: '快速指令',
|
commands: '快速指令',
|
||||||
opsReport: '運維報表',
|
opsReport: '運維報表',
|
||||||
users: '使用者管理',
|
users: '使用者管理',
|
||||||
|
roles: '角色',
|
||||||
auth: '登入認證',
|
auth: '登入認證',
|
||||||
alert: '告警通知',
|
alert: '告警通知',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1934,11 +1934,13 @@ const message = {
|
|||||||
ai: 'AI',
|
ai: 'AI',
|
||||||
skills_hub: 'Skills Hub',
|
skills_hub: 'Skills Hub',
|
||||||
ai_proxy: 'AI 网关',
|
ai_proxy: 'AI 网关',
|
||||||
|
ai_benchmark: '基准测试',
|
||||||
groups: '分组',
|
groups: '分组',
|
||||||
hosts: '主机',
|
hosts: '主机',
|
||||||
apps: '应用',
|
apps: '应用',
|
||||||
openresty: 'OpenResty',
|
openresty: 'OpenResty',
|
||||||
websites: '网站',
|
websites: '网站',
|
||||||
|
monitor: '网站监控',
|
||||||
containers: '容器',
|
containers: '容器',
|
||||||
files: '文件管理',
|
files: '文件管理',
|
||||||
runtimes: '运行环境',
|
runtimes: '运行环境',
|
||||||
@@ -1957,6 +1959,7 @@ const message = {
|
|||||||
commands: '快速命令',
|
commands: '快速命令',
|
||||||
opsReport: '运维报表',
|
opsReport: '运维报表',
|
||||||
users: '用户管理',
|
users: '用户管理',
|
||||||
|
roles: '角色',
|
||||||
auth: '登陆认证',
|
auth: '登陆认证',
|
||||||
alert: '告警通知',
|
alert: '告警通知',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Setting } from '@/api/interface/setting';
|
import { Setting } from '@/api/interface/setting';
|
||||||
import { listNodeOptions, loadNodeByUser } from '@/api/modules/setting';
|
import { listNodeOptions } from '@/api/modules/setting';
|
||||||
import { GlobalStore } from '@/store';
|
import { GlobalStore } from '@/store';
|
||||||
|
|
||||||
export const changeToLocal = async () => {
|
export const changeToLocal = async () => {
|
||||||
@@ -23,15 +23,9 @@ export const changeToLocal = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function listNodes(type: string): Promise<Array<Setting.NodeItem>> {
|
export async function listNodes(type: string): Promise<Array<Setting.NodeItem>> {
|
||||||
const globalStore = GlobalStore();
|
|
||||||
try {
|
try {
|
||||||
if (globalStore.isAdmin) {
|
const res = await listNodeOptions(type);
|
||||||
const res = await listNodeOptions(type);
|
return res.data || [];
|
||||||
return res.data || [];
|
|
||||||
} else {
|
|
||||||
const res = await loadNodeByUser();
|
|
||||||
return res.data || [];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { normalizeToManageCode } from '@/utils/permission-codes';
|
|||||||
|
|
||||||
export type PermissionBindingValue = string | string[] | undefined;
|
export type PermissionBindingValue = string | string[] | undefined;
|
||||||
export type PermissionMode = 'manage' | 'view';
|
export type PermissionMode = 'manage' | 'view';
|
||||||
|
export type PermissionAccessOptions = {
|
||||||
|
nodeAdmin?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
const getRoutePermission = (): PermissionBindingValue => {
|
const getRoutePermission = (): PermissionBindingValue => {
|
||||||
const route = router.currentRoute.value;
|
const route = router.currentRoute.value;
|
||||||
@@ -46,8 +49,15 @@ export const toPermissionList = (value: PermissionBindingValue) => {
|
|||||||
return routePermission ? [routePermission] : [];
|
return routePermission ? [routePermission] : [];
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasPermissionAccessByMode = (mode: PermissionMode, value?: PermissionBindingValue) => {
|
const hasPermissionAccessByMode = (
|
||||||
|
mode: PermissionMode,
|
||||||
|
value?: PermissionBindingValue,
|
||||||
|
options: PermissionAccessOptions = {},
|
||||||
|
) => {
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
if (options.nodeAdmin && globalStore.isNodeAdmin) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const permissions = toPermissionList(value);
|
const permissions = toPermissionList(value);
|
||||||
const normalizedPermissions = mode === 'manage' ? permissions.map(toManagePermission).filter(Boolean) : permissions;
|
const normalizedPermissions = mode === 'manage' ? permissions.map(toManagePermission).filter(Boolean) : permissions;
|
||||||
if (normalizedPermissions.length === 0) {
|
if (normalizedPermissions.length === 0) {
|
||||||
@@ -56,10 +66,10 @@ const hasPermissionAccessByMode = (mode: PermissionMode, value?: PermissionBindi
|
|||||||
return normalizedPermissions.some((permission) => globalStore.hasPermission(permission));
|
return normalizedPermissions.some((permission) => globalStore.hasPermission(permission));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const hasManagePermissionAccess = (value?: PermissionBindingValue) => {
|
export const hasManagePermissionAccess = (value?: PermissionBindingValue, options?: PermissionAccessOptions) => {
|
||||||
return hasPermissionAccessByMode('manage', value);
|
return hasPermissionAccessByMode('manage', value, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const hasPermissionAccess = (value?: PermissionBindingValue) => {
|
export const hasPermissionAccess = (value?: PermissionBindingValue, options?: PermissionAccessOptions) => {
|
||||||
return hasPermissionAccessByMode('view', value);
|
return hasPermissionAccessByMode('view', value, options);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,15 +2,16 @@
|
|||||||
<div>
|
<div>
|
||||||
<LayoutContent v-loading="loading" v-if="!isRecordShow" :title="$t('menu.cronjob')">
|
<LayoutContent v-loading="loading" v-if="!isRecordShow" :title="$t('menu.cronjob')">
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="onOpenDialog('')">
|
<el-button v-permission v-node-admin type="primary" @click="onOpenDialog('')">
|
||||||
{{ $t('commons.button.create') }}
|
{{ $t('commons.button.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission @click="onOpenGroupDialog()">
|
<el-button v-permission v-node-admin @click="onOpenGroupDialog()">
|
||||||
{{ $t('commons.table.group') }}
|
{{ $t('commons.table.group') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button-group>
|
<el-button-group>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
plain
|
plain
|
||||||
:disabled="selects.length === 0"
|
:disabled="selects.length === 0"
|
||||||
@click="onBatchChangeStatus('enable')"
|
@click="onBatchChangeStatus('enable')"
|
||||||
@@ -19,22 +20,23 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
plain
|
plain
|
||||||
:disabled="selects.length === 0"
|
:disabled="selects.length === 0"
|
||||||
@click="onBatchChangeStatus('disable')"
|
@click="onBatchChangeStatus('disable')"
|
||||||
>
|
>
|
||||||
{{ $t('commons.button.disable') }}
|
{{ $t('commons.button.disable') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission plain :disabled="selects.length === 0" @click="onDelete(null)">
|
<el-button v-permission v-node-admin plain :disabled="selects.length === 0" @click="onDelete(null)">
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
|
|
||||||
<el-button-group>
|
<el-button-group>
|
||||||
<el-button v-permission @click="onImport">
|
<el-button v-permission v-node-admin @click="onImport">
|
||||||
{{ $t('commons.button.import') }}
|
{{ $t('commons.button.import') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission :disabled="selects.length === 0" @click="onExport">
|
<el-button v-permission v-node-admin :disabled="selects.length === 0" @click="onExport">
|
||||||
{{ $t('commons.button.export') }}
|
{{ $t('commons.button.export') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
@@ -80,7 +82,12 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('commons.table.group')" min-width="120" prop="group">
|
<el-table-column :label="$t('commons.table.group')" min-width="120" prop="group">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<fu-select-rw-switch v-permission v-model="row.groupID" @change="updateGroup(row)">
|
<fu-select-rw-switch
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
v-model="row.groupID"
|
||||||
|
@change="updateGroup(row)"
|
||||||
|
>
|
||||||
<template #read>
|
<template #read>
|
||||||
{{ row.groupBelong === 'Default' ? $t('commons.table.default') : row.groupBelong }}
|
{{ row.groupBelong === 'Default' ? $t('commons.table.default') : row.groupBelong }}
|
||||||
</template>
|
</template>
|
||||||
@@ -99,6 +106,7 @@
|
|||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<Status
|
<Status
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
v-if="row.status === 'Enable'"
|
v-if="row.status === 'Enable'"
|
||||||
@click="onChangeStatus(row.id, 'disable')"
|
@click="onChangeStatus(row.id, 'disable')"
|
||||||
:status="row.status"
|
:status="row.status"
|
||||||
@@ -106,6 +114,7 @@
|
|||||||
/>
|
/>
|
||||||
<Status
|
<Status
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
v-if="row.status === 'Disable'"
|
v-if="row.status === 'Disable'"
|
||||||
@click="onChangeStatus(row.id, 'enable')"
|
@click="onChangeStatus(row.id, 'enable')"
|
||||||
:status="row.status"
|
:status="row.status"
|
||||||
@@ -139,6 +148,7 @@
|
|||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
v-if="hasBackup(row.type)"
|
v-if="hasBackup(row.type)"
|
||||||
@click="loadBackups(row)"
|
@click="loadBackups(row)"
|
||||||
plain
|
plain
|
||||||
@@ -511,6 +521,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.handle'),
|
label: i18n.global.t('commons.button.handle'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Cronjob.CronjobInfo) => {
|
click: (row: Cronjob.CronjobInfo) => {
|
||||||
onHandle(row);
|
onHandle(row);
|
||||||
},
|
},
|
||||||
@@ -527,6 +538,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Cronjob.CronjobInfo) => {
|
click: (row: Cronjob.CronjobInfo) => {
|
||||||
onOpenDialog(row.id + '');
|
onOpenDialog(row.id + '');
|
||||||
},
|
},
|
||||||
@@ -534,6 +546,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Cronjob.CronjobInfo) => {
|
click: (row: Cronjob.CronjobInfo) => {
|
||||||
onDelete(row);
|
onDelete(row);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
:disabled="dialogData.rowData.status === 'Pending'"
|
:disabled="dialogData.rowData.status === 'Pending'"
|
||||||
@click="onHandle(dialogData.rowData)"
|
@click="onHandle(dialogData.rowData)"
|
||||||
link
|
link
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
v-if="dialogData.rowData.status === 'Enable'"
|
v-if="dialogData.rowData.status === 'Enable'"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onChangeStatus(dialogData.rowData.id, 'disable')"
|
@click="onChangeStatus(dialogData.rowData.id, 'disable')"
|
||||||
link
|
link
|
||||||
>
|
>
|
||||||
@@ -53,13 +55,21 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
v-if="dialogData.rowData.status === 'Disable'"
|
v-if="dialogData.rowData.status === 'Disable'"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onChangeStatus(dialogData.rowData.id, 'enable')"
|
@click="onChangeStatus(dialogData.rowData.id, 'enable')"
|
||||||
link
|
link
|
||||||
>
|
>
|
||||||
{{ $t('commons.button.enable') }}
|
{{ $t('commons.button.enable') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission :disabled="!hasRecords" type="primary" @click="onClean" link>
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
:disabled="!hasRecords"
|
||||||
|
type="primary"
|
||||||
|
@click="onClean"
|
||||||
|
link
|
||||||
|
>
|
||||||
{{ $t('commons.button.clean') }}
|
{{ $t('commons.button.clean') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<LayoutContent v-loading="loading" :title="$t('logs.login')">
|
<LayoutContent v-loading="loading" :title="$t('logs.login')">
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="onOpenDialog('create')">
|
<el-button v-permission v-node-admin type="primary" @click="onOpenDialog('create')">
|
||||||
{{ $t('commons.button.create') }}
|
{{ $t('commons.button.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-dropdown @command="handleSyncOp" class="mr-2.5">
|
<el-dropdown @command="handleSyncOp" class="mr-2.5">
|
||||||
@@ -12,23 +12,33 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<fu-dropdown-item v-permission command="sync">
|
<fu-dropdown-item v-permission v-node-admin command="sync">
|
||||||
{{ $t('cronjob.library.syncNow') }}
|
{{ $t('cronjob.library.syncNow') }}
|
||||||
</fu-dropdown-item>
|
</fu-dropdown-item>
|
||||||
<fu-dropdown-item v-if="scriptSync === 'Disable'" v-permission command="turnOnSync">
|
<fu-dropdown-item
|
||||||
|
v-if="scriptSync === 'Disable'"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
command="turnOnSync"
|
||||||
|
>
|
||||||
{{ $t('cronjob.library.turnOnSync') }}
|
{{ $t('cronjob.library.turnOnSync') }}
|
||||||
</fu-dropdown-item>
|
</fu-dropdown-item>
|
||||||
<fu-dropdown-item v-if="scriptSync === 'Enable'" v-permission command="turnOffSync">
|
<fu-dropdown-item
|
||||||
|
v-if="scriptSync === 'Enable'"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
command="turnOffSync"
|
||||||
|
>
|
||||||
{{ $t('cronjob.library.turnOffSync') }}
|
{{ $t('cronjob.library.turnOffSync') }}
|
||||||
</fu-dropdown-item>
|
</fu-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
|
|
||||||
<el-button v-permission type="primary" plain @click="onOpenGroupDialog()">
|
<el-button v-permission v-node-admin type="primary" plain @click="onOpenGroupDialog()">
|
||||||
{{ $t('commons.table.group') }}
|
{{ $t('commons.table.group') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission plain :disabled="selects.length === 0" @click="onDelete(null)">
|
<el-button v-permission v-node-admin plain :disabled="selects.length === 0" @click="onDelete(null)">
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -326,6 +336,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.clone'),
|
label: i18n.global.t('commons.button.clone'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
disabled: (row: any) => {
|
disabled: (row: any) => {
|
||||||
return !row.isSystem;
|
return !row.isSystem;
|
||||||
},
|
},
|
||||||
@@ -341,6 +352,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
disabled: (row: any) => {
|
disabled: (row: any) => {
|
||||||
return row.isSystem;
|
return row.isSystem;
|
||||||
},
|
},
|
||||||
@@ -351,6 +363,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
disabled: (row: any) => {
|
disabled: (row: any) => {
|
||||||
return row.isSystem;
|
return row.isSystem;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -48,7 +48,13 @@
|
|||||||
diskInfo.mountPoint == ''
|
diskInfo.mountPoint == ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<el-button v-permission type="primary" size="small" @click="handlePartition(diskInfo)">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handlePartition(diskInfo)"
|
||||||
|
>
|
||||||
{{ $t('disk.handlePartition') }}
|
{{ $t('disk.handlePartition') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,6 +100,7 @@
|
|||||||
<el-text type="info" v-if="scope === 'system'">{{ $t('disk.cannotOperate') }}</el-text>
|
<el-text type="info" v-if="scope === 'system'">{{ $t('disk.cannotOperate') }}</el-text>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
type="primary"
|
type="primary"
|
||||||
link
|
link
|
||||||
v-else-if="row.mountPoint != ''"
|
v-else-if="row.mountPoint != ''"
|
||||||
@@ -101,7 +108,7 @@
|
|||||||
>
|
>
|
||||||
{{ $t('disk.unmount') }}
|
{{ $t('disk.unmount') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission type="primary" link v-else @click="mount(row)">
|
<el-button v-permission v-node-admin type="primary" link v-else @click="mount(row)">
|
||||||
{{ $t('disk.mount') }}
|
{{ $t('disk.mount') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -147,6 +154,7 @@
|
|||||||
<el-text type="info" v-if="scope === 'system'">{{ $t('disk.cannotOperate') }}</el-text>
|
<el-text type="info" v-if="scope === 'system'">{{ $t('disk.cannotOperate') }}</el-text>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
type="primary"
|
type="primary"
|
||||||
link
|
link
|
||||||
v-else-if="row.mountPoint != ''"
|
v-else-if="row.mountPoint != ''"
|
||||||
@@ -154,7 +162,7 @@
|
|||||||
>
|
>
|
||||||
{{ $t('disk.unmount') }}
|
{{ $t('disk.unmount') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission type="primary" link v-else @click="mount(row)">
|
<el-button v-permission v-node-admin type="primary" link v-else @click="mount(row)">
|
||||||
{{ $t('disk.mount') }}
|
{{ $t('disk.mount') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -30,20 +30,25 @@
|
|||||||
<el-form-item :label="$t('disk.mountPoint')" prop="mountPoint">
|
<el-form-item :label="$t('disk.mountPoint')" prop="mountPoint">
|
||||||
<el-input v-model="form.mountPoint">
|
<el-input v-model="form.mountPoint">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-button icon="Folder" v-permission @click="fileRef.acceptParams({ dir: true })" />
|
<el-button
|
||||||
|
icon="Folder"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="fileRef.acceptParams({ dir: true })"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('disk.autoMount')" prop="autoMount">
|
<el-form-item :label="$t('disk.autoMount')" prop="autoMount">
|
||||||
<el-switch v-permission v-model="form.autoMount" />
|
<el-switch v-permission v-node-admin v-model="form.autoMount" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('disk.noFail')" prop="noFail" v-if="form.autoMount">
|
<el-form-item :label="$t('disk.noFail')" prop="noFail" v-if="form.autoMount">
|
||||||
<el-switch v-permission v-model="form.noFail" />
|
<el-switch v-permission v-node-admin v-model="form.noFail" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission type="primary" @click="submit" :disabled="loading">
|
<el-button v-permission v-node-admin type="primary" @click="submit" :disabled="loading">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -94,6 +94,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
class="w-full sm:w-auto !ml-0"
|
class="w-full sm:w-auto !ml-0"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
:disabled="selected.length === 0"
|
:disabled="selected.length === 0"
|
||||||
:loading="deleteLoading"
|
:loading="deleteLoading"
|
||||||
@click="deleteSelected(null)"
|
@click="deleteSelected(null)"
|
||||||
@@ -152,7 +153,13 @@
|
|||||||
<el-button link type="primary" @click.stop="openHistoryRecord(row)">
|
<el-button link type="primary" @click.stop="openHistoryRecord(row)">
|
||||||
{{ $t('commons.button.view') }}
|
{{ $t('commons.button.view') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission link type="primary" @click.stop="deleteSelected(row)">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click.stop="deleteSelected(row)"
|
||||||
|
>
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -184,6 +191,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
v-if="canRestoreSelected"
|
v-if="canRestoreSelected"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
type="primary"
|
type="primary"
|
||||||
:loading="restoreLoading"
|
:loading="restoreLoading"
|
||||||
@click="confirmRestoreSelected"
|
@click="confirmRestoreSelected"
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<div class="flex justify-start items-center gap-x-4 card-action">
|
<div class="flex justify-start items-center gap-x-4 card-action">
|
||||||
<el-text class="cursor-pointer" @click="handleReset">{{ $t('commons.button.reset') }}</el-text>
|
<el-text class="cursor-pointer" @click="handleReset">{{ $t('commons.button.reset') }}</el-text>
|
||||||
<el-divider direction="vertical" class="!mx-0" />
|
<el-divider direction="vertical" class="!mx-0" />
|
||||||
<el-text v-permission class="cursor-pointer ml-0" @click="saveContent()">
|
<el-text v-permission v-node-admin class="cursor-pointer ml-0" @click="saveContent()">
|
||||||
{{ $t('commons.button.save') }}
|
{{ $t('commons.button.save') }}
|
||||||
</el-text>
|
</el-text>
|
||||||
<el-divider direction="vertical" class="!mx-0" />
|
<el-divider direction="vertical" class="!mx-0" />
|
||||||
@@ -127,11 +127,11 @@
|
|||||||
</el-text>
|
</el-text>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<fu-dropdown-item v-permission command="dir" class="!px-2">
|
<fu-dropdown-item v-permission v-node-admin command="dir" class="!px-2">
|
||||||
<svg-icon class="!w-5 !h-5" iconName="p-file-folder"></svg-icon>
|
<svg-icon class="!w-5 !h-5" iconName="p-file-folder"></svg-icon>
|
||||||
{{ $t('file.dir') }}
|
{{ $t('file.dir') }}
|
||||||
</fu-dropdown-item>
|
</fu-dropdown-item>
|
||||||
<fu-dropdown-item v-permission command="file" class="!px-2">
|
<fu-dropdown-item v-permission v-node-admin command="file" class="!px-2">
|
||||||
<svg-icon class="!w-5 !h-5" iconName="p-file-normal"></svg-icon>
|
<svg-icon class="!w-5 !h-5" iconName="p-file-normal"></svg-icon>
|
||||||
{{ $t('menu.files') }}
|
{{ $t('menu.files') }}
|
||||||
</fu-dropdown-item>
|
</fu-dropdown-item>
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: any) => {
|
click: (row: any) => {
|
||||||
singleDel(row.id);
|
singleDel(row.id);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -222,7 +222,14 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
<el-button v-permission class="max-w-20" plain type="primary" @click="openAiSearchDrawer">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
class="max-w-20"
|
||||||
|
plain
|
||||||
|
type="primary"
|
||||||
|
@click="openAiSearchDrawer"
|
||||||
|
>
|
||||||
{{ $t('file.aiSearch') }}
|
{{ $t('file.aiSearch') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -246,11 +253,11 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<fu-dropdown-item v-permission command="dir">
|
<fu-dropdown-item v-permission v-node-admin command="dir">
|
||||||
<svg-icon iconName="p-file-folder"></svg-icon>
|
<svg-icon iconName="p-file-folder"></svg-icon>
|
||||||
{{ $t('file.dir') }}
|
{{ $t('file.dir') }}
|
||||||
</fu-dropdown-item>
|
</fu-dropdown-item>
|
||||||
<fu-dropdown-item v-permission command="file">
|
<fu-dropdown-item v-permission v-node-admin command="file">
|
||||||
<svg-icon iconName="p-file-normal"></svg-icon>
|
<svg-icon iconName="p-file-normal"></svg-icon>
|
||||||
{{ $t('menu.files') }}
|
{{ $t('menu.files') }}
|
||||||
</fu-dropdown-item>
|
</fu-dropdown-item>
|
||||||
@@ -264,11 +271,11 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<fu-dropdown-item v-permission @click="openUpload">
|
<fu-dropdown-item v-permission v-node-admin @click="openUpload">
|
||||||
<el-icon><ElUpload /></el-icon>
|
<el-icon><ElUpload /></el-icon>
|
||||||
{{ $t('commons.button.upload') }}
|
{{ $t('commons.button.upload') }}
|
||||||
</fu-dropdown-item>
|
</fu-dropdown-item>
|
||||||
<fu-dropdown-item v-permission @click="openWget">
|
<fu-dropdown-item v-permission v-node-admin @click="openWget">
|
||||||
<el-icon><ElDownload /></el-icon>
|
<el-icon><ElDownload /></el-icon>
|
||||||
{{ $t('file.remoteFile') }}
|
{{ $t('file.remoteFile') }}
|
||||||
</fu-dropdown-item>
|
</fu-dropdown-item>
|
||||||
@@ -437,6 +444,7 @@
|
|||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<fu-dropdown-item
|
<fu-dropdown-item
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
v-for="btn in moreButtons"
|
v-for="btn in moreButtons"
|
||||||
:key="btn.label"
|
:key="btn.label"
|
||||||
@click="btn.action"
|
@click="btn.action"
|
||||||
@@ -453,6 +461,7 @@
|
|||||||
<template v-for="btn in visibleButtons" :key="btn.label">
|
<template v-for="btn in visibleButtons" :key="btn.label">
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
plain
|
plain
|
||||||
@click="btn.action"
|
@click="btn.action"
|
||||||
:disabled="selects.length === 0"
|
:disabled="selects.length === 0"
|
||||||
@@ -469,6 +478,7 @@
|
|||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<fu-dropdown-item
|
<fu-dropdown-item
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
v-for="btn in moreButtons"
|
v-for="btn in moreButtons"
|
||||||
:key="btn.label"
|
:key="btn.label"
|
||||||
@click="btn.action"
|
@click="btn.action"
|
||||||
@@ -555,6 +565,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
v-if="row.shareCode"
|
v-if="row.shareCode"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -566,6 +577,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
v-if="row.favoriteID > 0"
|
v-if="row.favoriteID > 0"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
link
|
link
|
||||||
type="warning"
|
type="warning"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -576,6 +588,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
v-if="hoveredRowPath === row.path"
|
v-if="hoveredRowPath === row.path"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
link
|
link
|
||||||
icon="Star"
|
icon="Star"
|
||||||
@click="addToFavorite(row)"
|
@click="addToFavorite(row)"
|
||||||
@@ -587,7 +600,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('file.mode')" prop="mode" width="80">
|
<el-table-column :label="$t('file.mode')" prop="mode" width="80">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link v-permission underline="never" @click="openMode(row)">
|
<el-link v-permission v-node-admin underline="never" @click="openMode(row)">
|
||||||
{{ row.mode }}
|
{{ row.mode }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
@@ -599,7 +612,7 @@
|
|||||||
width="200"
|
width="200"
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link v-permission underline="never" @click="openChown(row)">
|
<el-link v-permission v-node-admin underline="never" @click="openChown(row)">
|
||||||
{{ row.user ? row.user : '-' }} ({{ row.uid }}) /
|
{{ row.user ? row.user : '-' }} ({{ row.uid }}) /
|
||||||
{{ row.group ? row.group : '-' }} ({{ row.gid }})
|
{{ row.group ? row.group : '-' }} ({{ row.gid }})
|
||||||
</el-link>
|
</el-link>
|
||||||
@@ -1981,16 +1994,19 @@ const beforeButtons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.copy'),
|
label: i18n.global.t('commons.button.copy'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => openMoveBtn('copy', row),
|
click: (row: File.File) => openMoveBtn('copy', row),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('file.move'),
|
label: i18n.global.t('file.move'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => openMoveBtn('cut', row),
|
click: (row: File.File) => openMoveBtn('cut', row),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('file.paste'),
|
label: i18n.global.t('file.paste'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: openPaste,
|
click: openPaste,
|
||||||
disabled: () => {
|
disabled: () => {
|
||||||
return !moveOpen.value;
|
return !moveOpen.value;
|
||||||
@@ -1999,6 +2015,7 @@ const beforeButtons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.compress'),
|
label: i18n.global.t('file.compress'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => {
|
click: (row: File.File) => {
|
||||||
openCompress([row]);
|
openCompress([row]);
|
||||||
},
|
},
|
||||||
@@ -2013,6 +2030,7 @@ const beforeButtons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.editPermissions'),
|
label: i18n.global.t('file.editPermissions'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => {
|
click: (row: File.File) => {
|
||||||
openBatchRole([row]);
|
openBatchRole([row]);
|
||||||
},
|
},
|
||||||
@@ -2021,6 +2039,7 @@ const beforeButtons = [
|
|||||||
label: i18n.global.t('file.setRemark'),
|
label: i18n.global.t('file.setRemark'),
|
||||||
hideOnRemarkBlackList: true,
|
hideOnRemarkBlackList: true,
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => {
|
click: (row: File.File) => {
|
||||||
openRemark(row);
|
openRemark(row);
|
||||||
},
|
},
|
||||||
@@ -2030,6 +2049,7 @@ const afterButtons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
disabled: (row: File.File) => {
|
disabled: (row: File.File) => {
|
||||||
return row.name == '.1panel_clash';
|
return row.name == '.1panel_clash';
|
||||||
},
|
},
|
||||||
@@ -2043,6 +2063,7 @@ const afterButtons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.addFavoriteAction'),
|
label: i18n.global.t('file.addFavoriteAction'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => {
|
click: (row: File.File) => {
|
||||||
addToFavorite(row);
|
addToFavorite(row);
|
||||||
},
|
},
|
||||||
@@ -2051,6 +2072,7 @@ const afterButtons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.removeFavoriteAction'),
|
label: i18n.global.t('file.removeFavoriteAction'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => {
|
click: (row: File.File) => {
|
||||||
remove(row?.favoriteID);
|
remove(row?.favoriteID);
|
||||||
},
|
},
|
||||||
@@ -2059,6 +2081,7 @@ const afterButtons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.shareFile'),
|
label: i18n.global.t('file.shareFile'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: openShareFile,
|
click: openShareFile,
|
||||||
show: (row: File.File) => {
|
show: (row: File.File) => {
|
||||||
return !row?.isDir && !row?.shareCode;
|
return !row?.isDir && !row?.shareCode;
|
||||||
@@ -2067,6 +2090,7 @@ const afterButtons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.shareCancel'),
|
label: i18n.global.t('file.shareCancel'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => {
|
click: (row: File.File) => {
|
||||||
removeShareByPath(row.path);
|
removeShareByPath(row.path);
|
||||||
},
|
},
|
||||||
@@ -2077,6 +2101,7 @@ const afterButtons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.convert'),
|
label: i18n.global.t('file.convert'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => {
|
click: (row: File.File) => {
|
||||||
openConvert(row);
|
openConvert(row);
|
||||||
},
|
},
|
||||||
@@ -2099,6 +2124,7 @@ const rightBtnRename = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.rename'),
|
label: i18n.global.t('file.rename'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => {
|
click: (row: File.File) => {
|
||||||
openRename(row, 'right');
|
openRename(row, 'right');
|
||||||
},
|
},
|
||||||
@@ -2108,6 +2134,7 @@ const moreBtnRename = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.rename'),
|
label: i18n.global.t('file.rename'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.File) => {
|
click: (row: File.File) => {
|
||||||
openRename(row, 'more');
|
openRename(row, 'more');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,18 +2,35 @@
|
|||||||
<DrawerPro v-model="open" :header="$t('file.recycleBin')" @close="handleClose" size="large">
|
<DrawerPro v-model="open" :header="$t('file.recycleBin')" @close="handleClose" size="large">
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex space-x-4">
|
<div class="flex space-x-4">
|
||||||
<el-button v-permission @click="clear" type="primary" :disabled="data == null || data.length == 0">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="clear"
|
||||||
|
type="primary"
|
||||||
|
:disabled="data == null || data.length == 0"
|
||||||
|
>
|
||||||
{{ $t('file.clearRecycleBin') }}
|
{{ $t('file.clearRecycleBin') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission @click="patchDelete" :disabled="data == null || selects.length == 0">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="patchDelete"
|
||||||
|
:disabled="data == null || selects.length == 0"
|
||||||
|
>
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission @click="patchReduce" :disabled="data == null || selects.length == 0">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="patchReduce"
|
||||||
|
:disabled="data == null || selects.length == 0"
|
||||||
|
>
|
||||||
{{ $t('file.reduce') }}
|
{{ $t('file.reduce') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-form-item :label="$t('file.fileRecycleBin')">
|
<el-form-item :label="$t('file.fileRecycleBin')">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
v-model="status"
|
v-model="status"
|
||||||
active-value="Enable"
|
active-value="Enable"
|
||||||
inactive-value="Disable"
|
inactive-value="Disable"
|
||||||
@@ -188,11 +205,13 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.reduce'),
|
label: i18n.global.t('file.reduce'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: rdFile,
|
click: rdFile,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: singleDel,
|
click: singleDel,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('file.shareClose'),
|
label: i18n.global.t('file.shareClose'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: File.FileShareInfo) => {
|
click: (row: File.FileShareInfo) => {
|
||||||
closeShare(row.path);
|
closeShare(row.path);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -63,13 +63,13 @@
|
|||||||
<el-button :disabled="loading" @click="handleClose">
|
<el-button :disabled="loading" @click="handleClose">
|
||||||
{{ $t('commons.button.cancel') }}
|
{{ $t('commons.button.cancel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission v-if="shareInfo" :disabled="loading" @click="cancelShare">
|
<el-button v-permission v-node-admin v-if="shareInfo" :disabled="loading" @click="cancelShare">
|
||||||
{{ $t('file.shareClose') }}
|
{{ $t('file.shareClose') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="shareInfo" :disabled="loading" @click="copyLink">
|
<el-button v-if="shareInfo" :disabled="loading" @click="copyLink">
|
||||||
{{ $t('file.shareCopyLink') }}
|
{{ $t('file.shareCopyLink') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission type="primary" :disabled="loading" @click="generate">
|
<el-button v-permission v-node-admin type="primary" :disabled="loading" @click="generate">
|
||||||
{{ shareInfo ? $t('file.shareRegenerate') : $t('file.shareGenerate') }}
|
{{ shareInfo ? $t('file.shareRegenerate') : $t('file.shareGenerate') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -36,16 +36,22 @@
|
|||||||
<el-alert type="info" :closable="false" :title="loadPrompt()" />
|
<el-alert type="info" :closable="false" :title="loadPrompt()" />
|
||||||
</template>
|
</template>
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="onOpenDialog('create')">
|
<el-button v-permission v-node-admin type="primary" @click="onOpenDialog('create')">
|
||||||
{{ $t('firewall.create') }}
|
{{ $t('firewall.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="isBind" v-permission plain @click="onUnBind">
|
<el-button v-if="isBind" v-permission v-node-admin plain @click="onUnBind">
|
||||||
{{ $t('commons.button.unbind') }}
|
{{ $t('commons.button.unbind') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="!isBind" v-permission plain @click="onBind">
|
<el-button v-if="!isBind" v-permission v-node-admin plain @click="onBind">
|
||||||
{{ $t('commons.button.bind') }}
|
{{ $t('commons.button.bind') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission @click="onDelete(null)" plain :disabled="selects.length === 0">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onDelete(null)"
|
||||||
|
plain
|
||||||
|
:disabled="selects.length === 0"
|
||||||
|
>
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -118,6 +124,7 @@
|
|||||||
<fu-input-rw-switch
|
<fu-input-rw-switch
|
||||||
v-model="row.description"
|
v-model="row.description"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@enter="onChange(row)"
|
@enter="onChange(row)"
|
||||||
@blur="onChange(row)"
|
@blur="onChange(row)"
|
||||||
/>
|
/>
|
||||||
@@ -339,6 +346,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Host.IptablesRules) => {
|
click: (row: Host.IptablesRules) => {
|
||||||
onDelete(row);
|
onDelete(row);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,17 +19,23 @@
|
|||||||
|
|
||||||
<LayoutContent :title="$t('firewall.forwardRule', 2)" :class="{ mask: !isActive }">
|
<LayoutContent :title="$t('firewall.forwardRule', 2)" :class="{ mask: !isActive }">
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="onOpenDialog('create')">
|
<el-button v-permission v-node-admin type="primary" @click="onOpenDialog('create')">
|
||||||
{{ $t('commons.button.create') }}
|
{{ $t('commons.button.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission @click="onDelete(null)" plain :disabled="selects.length === 0">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onDelete(null)"
|
||||||
|
plain
|
||||||
|
:disabled="selects.length === 0"
|
||||||
|
>
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button-group>
|
<el-button-group>
|
||||||
<el-button v-permission @click="onImport">
|
<el-button v-permission v-node-admin @click="onImport">
|
||||||
{{ $t('commons.button.import') }}
|
{{ $t('commons.button.import') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission :disabled="selects.length === 0" @click="onExport">
|
<el-button v-permission v-node-admin :disabled="selects.length === 0" @click="onExport">
|
||||||
{{ $t('commons.button.export') }}
|
{{ $t('commons.button.export') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
@@ -255,6 +261,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Host.RuleForward) => {
|
click: (row: Host.RuleForward) => {
|
||||||
onOpenDialog('edit', row);
|
onOpenDialog('edit', row);
|
||||||
},
|
},
|
||||||
@@ -262,6 +269,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Host.RuleForward) => {
|
click: (row: Host.RuleForward) => {
|
||||||
onDelete(row);
|
onDelete(row);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,17 +29,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="onOpenDialog('create')">
|
<el-button v-permission v-node-admin type="primary" @click="onOpenDialog('create')">
|
||||||
{{ $t('commons.button.create') }}
|
{{ $t('commons.button.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission @click="onDelete(null)" plain :disabled="selects.length === 0">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onDelete(null)"
|
||||||
|
plain
|
||||||
|
:disabled="selects.length === 0"
|
||||||
|
>
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button-group>
|
<el-button-group>
|
||||||
<el-button v-permission @click="onImport">
|
<el-button v-permission v-node-admin @click="onImport">
|
||||||
{{ $t('commons.button.import') }}
|
{{ $t('commons.button.import') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission :disabled="selects.length === 0" @click="onExport">
|
<el-button v-permission v-node-admin :disabled="selects.length === 0" @click="onExport">
|
||||||
{{ $t('commons.button.export') }}
|
{{ $t('commons.button.export') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
@@ -75,6 +81,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
v-if="row.strategy === 'accept'"
|
v-if="row.strategy === 'accept'"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onChangeStatus(row, 'drop')"
|
@click="onChangeStatus(row, 'drop')"
|
||||||
link
|
link
|
||||||
type="success"
|
type="success"
|
||||||
@@ -86,6 +93,7 @@
|
|||||||
link
|
link
|
||||||
type="danger"
|
type="danger"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onChangeStatus(row, 'accept')"
|
@click="onChangeStatus(row, 'accept')"
|
||||||
>
|
>
|
||||||
{{ $t('firewall.deny') }}
|
{{ $t('firewall.deny') }}
|
||||||
@@ -102,6 +110,7 @@
|
|||||||
<fu-input-rw-switch
|
<fu-input-rw-switch
|
||||||
v-model="row.description"
|
v-model="row.description"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@enter="onChange(row)"
|
@enter="onChange(row)"
|
||||||
@blur="onChange(row)"
|
@blur="onChange(row)"
|
||||||
/>
|
/>
|
||||||
@@ -323,6 +332,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Host.RuleIP) => {
|
click: (row: Host.RuleIP) => {
|
||||||
onOpenDialog('edit', row);
|
onOpenDialog('edit', row);
|
||||||
},
|
},
|
||||||
@@ -330,6 +340,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Host.RuleIP) => {
|
click: (row: Host.RuleIP) => {
|
||||||
onDelete(row);
|
onDelete(row);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -43,17 +43,23 @@
|
|||||||
</el-alert>
|
</el-alert>
|
||||||
</template>
|
</template>
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="onOpenDialog('create')">
|
<el-button v-permission v-node-admin type="primary" @click="onOpenDialog('create')">
|
||||||
{{ $t('commons.button.create') }}
|
{{ $t('commons.button.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission @click="onDelete(null)" plain :disabled="selects.length === 0">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onDelete(null)"
|
||||||
|
plain
|
||||||
|
:disabled="selects.length === 0"
|
||||||
|
>
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button-group>
|
<el-button-group>
|
||||||
<el-button v-permission @click="onImport">
|
<el-button v-permission v-node-admin @click="onImport">
|
||||||
{{ $t('commons.button.import') }}
|
{{ $t('commons.button.import') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission :disabled="selects.length === 0" @click="onExport">
|
<el-button v-permission v-node-admin :disabled="selects.length === 0" @click="onExport">
|
||||||
{{ $t('commons.button.export') }}
|
{{ $t('commons.button.export') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
@@ -140,6 +146,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
v-if="row.strategy === 'accept'"
|
v-if="row.strategy === 'accept'"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onChangeStatus(row, 'drop')"
|
@click="onChangeStatus(row, 'drop')"
|
||||||
link
|
link
|
||||||
type="success"
|
type="success"
|
||||||
@@ -151,6 +158,7 @@
|
|||||||
link
|
link
|
||||||
type="danger"
|
type="danger"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onChangeStatus(row, 'accept')"
|
@click="onChangeStatus(row, 'accept')"
|
||||||
>
|
>
|
||||||
{{ $t('firewall.drop') }}
|
{{ $t('firewall.drop') }}
|
||||||
@@ -173,6 +181,7 @@
|
|||||||
<fu-input-rw-switch
|
<fu-input-rw-switch
|
||||||
v-model="row.description"
|
v-model="row.description"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@enter="onChange(row)"
|
@enter="onChange(row)"
|
||||||
@blur="onChange(row)"
|
@blur="onChange(row)"
|
||||||
/>
|
/>
|
||||||
@@ -656,6 +665,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Host.RulePort) => {
|
click: (row: Host.RulePort) => {
|
||||||
onOpenDialog('edit', row);
|
onOpenDialog('edit', row);
|
||||||
},
|
},
|
||||||
@@ -663,6 +673,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Host.RuleInfo) => {
|
click: (row: Host.RuleInfo) => {
|
||||||
onDelete(row);
|
onDelete(row);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<template v-if="baseInfo.name !== 'iptables'">
|
<template v-if="baseInfo.name !== 'iptables'">
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
type="primary"
|
type="primary"
|
||||||
v-if="baseInfo.isActive"
|
v-if="baseInfo.isActive"
|
||||||
@click="onOperate('stop')"
|
@click="onOperate('stop')"
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
type="primary"
|
type="primary"
|
||||||
v-if="!baseInfo.isActive"
|
v-if="!baseInfo.isActive"
|
||||||
@click="onOperate('start')"
|
@click="onOperate('start')"
|
||||||
@@ -29,29 +31,43 @@
|
|||||||
{{ $t('commons.button.start') }}
|
{{ $t('commons.button.start') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission type="primary" @click="onOperate('restart')" link>
|
<el-button v-permission v-node-admin type="primary" @click="onOperate('restart')" link>
|
||||||
{{ $t('commons.button.restart') }}
|
{{ $t('commons.button.restart') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="!baseInfo.isInit || (props.currentTab === 'forward' && !baseInfo.isBind)">
|
<template v-if="!baseInfo.isInit || (props.currentTab === 'forward' && !baseInfo.isBind)">
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission type="primary" link @click="onInit">
|
<el-button v-permission v-node-admin type="primary" link @click="onInit">
|
||||||
{{ $t('commons.button.init') }}
|
{{ $t('commons.button.init') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="baseInfo.name === 'iptables' && baseInfo.isInit && props.currentTab == 'base'">
|
<template v-if="baseInfo.name === 'iptables' && baseInfo.isInit && props.currentTab == 'base'">
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-if="baseInfo.isBind" v-permission type="primary" link @click="onUnBind">
|
<el-button
|
||||||
|
v-if="baseInfo.isBind"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="onUnBind"
|
||||||
|
>
|
||||||
{{ $t('commons.button.unbind') }}
|
{{ $t('commons.button.unbind') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="!baseInfo.isBind" v-permission type="primary" link @click="onBind">
|
<el-button
|
||||||
|
v-if="!baseInfo.isBind"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="onBind"
|
||||||
|
>
|
||||||
{{ $t('commons.button.bind') }}
|
{{ $t('commons.button.bind') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="props.currentTab == 'base'">
|
<template v-if="props.currentTab == 'base'">
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button link type="primary" v-permission @click="onOpenWhiteList" plain>
|
<el-button link type="primary" v-permission v-node-admin @click="onOpenWhiteList" plain>
|
||||||
{{ $t('firewall.portWhiteList') }}
|
{{ $t('firewall.portWhiteList') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -61,6 +77,7 @@
|
|||||||
<el-button type="primary" link>{{ $t('firewall.noPing') }}</el-button>
|
<el-button type="primary" link>{{ $t('firewall.noPing') }}</el-button>
|
||||||
<el-switch
|
<el-switch
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
size="small"
|
size="small"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
inactive-value="Disable"
|
inactive-value="Disable"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
<el-form-item :label="$t('monitor.enableMonitor')" prop="monitorStatus">
|
<el-form-item :label="$t('monitor.enableMonitor')" prop="monitorStatus">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@change="onSaveStatus"
|
@change="onSaveStatus"
|
||||||
v-model="form.monitorStatus"
|
v-model="form.monitorStatus"
|
||||||
active-value="Enable"
|
active-value="Enable"
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
<el-form-item :label="$t('monitor.storeDays')" prop="monitorStoreDays">
|
<el-form-item :label="$t('monitor.storeDays')" prop="monitorStoreDays">
|
||||||
<el-input disabled v-model="form.monitorStoreDays">
|
<el-input disabled v-model="form.monitorStoreDays">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeStoreDays" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeStoreDays" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
<el-form-item :label="$t('monitor.interval')" prop="monitorIntervalItem">
|
<el-form-item :label="$t('monitor.interval')" prop="monitorIntervalItem">
|
||||||
<el-input disabled v-model="form.monitorIntervalItem">
|
<el-input disabled v-model="form.monitorIntervalItem">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeInterval" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeInterval" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
<el-form-item :label="$t('monitor.defaultNetwork')">
|
<el-form-item :label="$t('monitor.defaultNetwork')">
|
||||||
<el-input disabled v-model="form.defaultNetwork">
|
<el-input disabled v-model="form.defaultNetwork">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeNetwork" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeNetwork" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -48,7 +49,7 @@
|
|||||||
<el-form-item :label="$t('monitor.defaultIO')">
|
<el-form-item :label="$t('monitor.defaultIO')">
|
||||||
<el-input disabled v-model="form.defaultIO">
|
<el-input disabled v-model="form.defaultIO">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeIO" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeIO" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
<span class="input-help">{{ $t('monitor.defaultIOHelper') }}</span>
|
<span class="input-help">{{ $t('monitor.defaultIOHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button v-permission @click="onClean()" icon="Delete">
|
<el-button v-permission v-node-admin @click="onClean()" icon="Delete">
|
||||||
{{ $t('monitor.cleanMonitor') }}
|
{{ $t('monitor.cleanMonitor') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="mt-2"><el-alert type="info" :title="$t('ssh.sshAlert')" :closable="false" /></div>
|
<div class="mt-2"><el-alert type="info" :title="$t('ssh.sshAlert')" :closable="false" /></div>
|
||||||
</template>
|
</template>
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="onExport">
|
<el-button v-permission v-node-admin type="primary" @click="onExport">
|
||||||
{{ $t('commons.button.export') }}
|
{{ $t('commons.button.export') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<el-button @click="open = false" :disabled="loading">
|
<el-button @click="open = false" :disabled="loading">
|
||||||
{{ $t('commons.button.cancel') }}
|
{{ $t('commons.button.cancel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission type="primary" @click="onSubmitExport" :disabled="loading">
|
<el-button v-permission v-node-admin type="primary" @click="onSubmitExport" :disabled="loading">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.disConn'),
|
label: i18n.global.t('commons.button.disConn'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: function (row: any) {
|
click: function (row: any) {
|
||||||
stop(row.PID);
|
stop(row.PID);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<el-alert :closable="false">{{ $t('ssh.pubKeyHelper', [currentUser]) }}</el-alert>
|
<el-alert :closable="false">{{ $t('ssh.pubKeyHelper', [currentUser]) }}</el-alert>
|
||||||
</div>
|
</div>
|
||||||
<el-button v-permission type="primary" plain @click="onOpenDialog('create')">
|
<el-button v-permission v-node-admin type="primary" plain @click="onOpenDialog('create')">
|
||||||
{{ $t('commons.button.create') }}
|
{{ $t('commons.button.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission plain @click="onSync()">
|
<el-button v-permission v-node-admin plain @click="onSync()">
|
||||||
{{ $t('commons.button.sync') }}
|
{{ $t('commons.button.sync') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission plain :disabled="selects.length === 0" @click="onDelete(null)">
|
<el-button v-permission v-node-admin plain :disabled="selects.length === 0" @click="onDelete(null)">
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<ComplexTable
|
<ComplexTable
|
||||||
@@ -262,6 +262,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Host.RootCertInfo) => {
|
click: (row: Host.RootCertInfo) => {
|
||||||
onOpenDialog('edit', row);
|
onOpenDialog('edit', row);
|
||||||
},
|
},
|
||||||
@@ -276,6 +277,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Host.RootCertInfo) => {
|
click: (row: Host.RootCertInfo) => {
|
||||||
onDelete(row);
|
onDelete(row);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,14 +10,28 @@
|
|||||||
<Status class="mt-0.5" :status="form.isActive ? 'enable' : 'disable'" :msg="form.message" />
|
<Status class="mt-0.5" :status="form.isActive ? 'enable' : 'disable'" :msg="form.message" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-0.5">
|
<div class="mt-0.5">
|
||||||
<el-button v-if="form.isActive" type="primary" v-permission @click="onOperate('stop')" link>
|
<el-button
|
||||||
|
v-if="form.isActive"
|
||||||
|
type="primary"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onOperate('stop')"
|
||||||
|
link
|
||||||
|
>
|
||||||
{{ $t('commons.button.stop') }}
|
{{ $t('commons.button.stop') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="!form.isActive" type="primary" v-permission @click="onOperate('start')" link>
|
<el-button
|
||||||
|
v-if="!form.isActive"
|
||||||
|
type="primary"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onOperate('start')"
|
||||||
|
link
|
||||||
|
>
|
||||||
{{ $t('commons.button.start') }}
|
{{ $t('commons.button.start') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission type="primary" @click="onOperate('restart')" link>
|
<el-button v-permission v-node-admin type="primary" @click="onOperate('restart')" link>
|
||||||
{{ $t('commons.button.restart') }}
|
{{ $t('commons.button.restart') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
@@ -26,6 +40,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-switch
|
<el-switch
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
size="small"
|
size="small"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
inactive-value="disable"
|
inactive-value="disable"
|
||||||
@@ -45,8 +60,8 @@
|
|||||||
<el-radio-button value="all">{{ $t('database.allConf') }}</el-radio-button>
|
<el-radio-button value="all">{{ $t('database.allConf') }}</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
|
|
||||||
<el-button v-permission @click="onOpenDrawer">{{ $t('ssh.pubkey') }}</el-button>
|
<el-button v-permission v-node-admin @click="onOpenDrawer">{{ $t('ssh.pubkey') }}</el-button>
|
||||||
<el-button v-permission @click="onOpenAuthKeys">{{ $t('ssh.authKeys') }}</el-button>
|
<el-button v-permission v-node-admin @click="onOpenAuthKeys">{{ $t('ssh.authKeys') }}</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template #main>
|
<template #main>
|
||||||
<el-row class="mt-10" v-if="confShowType === 'base'">
|
<el-row class="mt-10" v-if="confShowType === 'base'">
|
||||||
@@ -55,7 +70,7 @@
|
|||||||
<el-form-item :label="$t('ssh.port')" prop="port">
|
<el-form-item :label="$t('ssh.port')" prop="port">
|
||||||
<el-input disabled v-model="form.port">
|
<el-input disabled v-model="form.port">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangePort" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangePort" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -65,7 +80,7 @@
|
|||||||
<el-form-item :label="$t('ssh.listenAddress')" prop="listenAddress">
|
<el-form-item :label="$t('ssh.listenAddress')" prop="listenAddress">
|
||||||
<el-input disabled v-model="form.listenAddressItem">
|
<el-input disabled v-model="form.listenAddressItem">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeAddress" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeAddress" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -75,7 +90,7 @@
|
|||||||
<el-form-item :label="$t('ssh.permitRootLogin')" prop="permitRootLoginItem">
|
<el-form-item :label="$t('ssh.permitRootLogin')" prop="permitRootLoginItem">
|
||||||
<el-input disabled v-model="form.permitRootLoginItem">
|
<el-input disabled v-model="form.permitRootLoginItem">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeRoot" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeRoot" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -85,6 +100,7 @@
|
|||||||
<el-form-item :label="$t('ssh.passwordAuthentication')" prop="passwordAuthentication">
|
<el-form-item :label="$t('ssh.passwordAuthentication')" prop="passwordAuthentication">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
active-value="yes"
|
active-value="yes"
|
||||||
inactive-value="no"
|
inactive-value="no"
|
||||||
@change="onSave(formRef, 'PasswordAuthentication', form.passwordAuthentication)"
|
@change="onSave(formRef, 'PasswordAuthentication', form.passwordAuthentication)"
|
||||||
@@ -95,6 +111,7 @@
|
|||||||
<el-form-item :label="$t('ssh.pubkeyAuthentication')" prop="pubkeyAuthentication">
|
<el-form-item :label="$t('ssh.pubkeyAuthentication')" prop="pubkeyAuthentication">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
active-value="yes"
|
active-value="yes"
|
||||||
inactive-value="no"
|
inactive-value="no"
|
||||||
@change="onSave(formRef, 'PubkeyAuthentication', form.pubkeyAuthentication)"
|
@change="onSave(formRef, 'PubkeyAuthentication', form.pubkeyAuthentication)"
|
||||||
@@ -105,6 +122,7 @@
|
|||||||
<el-form-item :label="$t('ssh.useDNS')" prop="useDNS">
|
<el-form-item :label="$t('ssh.useDNS')" prop="useDNS">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
active-value="yes"
|
active-value="yes"
|
||||||
inactive-value="no"
|
inactive-value="no"
|
||||||
@change="onSave(formRef, 'UseDNS', form.useDNS)"
|
@change="onSave(formRef, 'UseDNS', form.useDNS)"
|
||||||
@@ -134,7 +152,14 @@
|
|||||||
mode="nginx"
|
mode="nginx"
|
||||||
placeholder="# The SSH configuration file does not exist or is empty (/etc/ssh/sshd_config)"
|
placeholder="# The SSH configuration file does not exist or is empty (/etc/ssh/sshd_config)"
|
||||||
></CodemirrorPro>
|
></CodemirrorPro>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSaveFile" class="mt-2.5">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
:disabled="loading"
|
||||||
|
type="primary"
|
||||||
|
@click="onSaveFile"
|
||||||
|
class="mt-2.5"
|
||||||
|
>
|
||||||
{{ $t('commons.button.save') }}
|
{{ $t('commons.button.save') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,9 +16,11 @@
|
|||||||
<el-option :label="$t('logs.detail.apps')" value="apps" />
|
<el-option :label="$t('logs.detail.apps')" value="apps" />
|
||||||
<el-option :label="$t('logs.detail.openresty')" value="openresty" />
|
<el-option :label="$t('logs.detail.openresty')" value="openresty" />
|
||||||
<el-option :label="$t('logs.detail.websites')" value="websites" />
|
<el-option :label="$t('logs.detail.websites')" value="websites" />
|
||||||
|
<el-option :label="$t('logs.detail.monitor')" value="monitor" />
|
||||||
<el-option :label="$t('logs.detail.runtimes')" value="runtimes" />
|
<el-option :label="$t('logs.detail.runtimes')" value="runtimes" />
|
||||||
<el-option :label="$t('logs.detail.ai')" value="ai" />
|
<el-option :label="$t('logs.detail.ai')" value="ai" />
|
||||||
<el-option :label="$t('logs.detail.ai_proxy')" value="ai-proxy" />
|
<el-option :label="$t('logs.detail.ai_proxy')" value="ai-proxy" />
|
||||||
|
<el-option :label="$t('logs.detail.ai_benchmark')" value="ai_benchmark" />
|
||||||
<el-option :label="$t('logs.detail.skills_hub')" value="skills-hub" />
|
<el-option :label="$t('logs.detail.skills_hub')" value="skills-hub" />
|
||||||
<el-option :label="$t('logs.detail.databases')" value="databases" />
|
<el-option :label="$t('logs.detail.databases')" value="databases" />
|
||||||
<el-option :label="$t('logs.detail.containers')" value="containers" />
|
<el-option :label="$t('logs.detail.containers')" value="containers" />
|
||||||
@@ -37,6 +39,7 @@
|
|||||||
<el-option :label="$t('logs.detail.alert')" value="alert" />
|
<el-option :label="$t('logs.detail.alert')" value="alert" />
|
||||||
<el-option :label="$t('logs.detail.backups')" value="backups" />
|
<el-option :label="$t('logs.detail.backups')" value="backups" />
|
||||||
<el-option :label="$t('logs.detail.groups')" value="groups" />
|
<el-option :label="$t('logs.detail.groups')" value="groups" />
|
||||||
|
<el-option :label="$t('logs.detail.roles')" value="roles" />
|
||||||
<el-option :label="$t('logs.detail.commands')" value="commands" />
|
<el-option :label="$t('logs.detail.commands')" value="commands" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-select v-model="searchStatus" @change="search()" clearable class="p-w-200">
|
<el-select v-model="searchStatus" @change="search()" clearable class="p-w-200">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
class="bg-white shadow-lg relative z-10 border border-gray-200 flex overflow-hidden"
|
class="bg-white shadow-lg relative z-10 border border-gray-200 flex overflow-hidden"
|
||||||
id="login-container"
|
id="login-container"
|
||||||
>
|
>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 items-stretch w-full">
|
<div class="grid items-stretch w-full" :style="loginGridStyle">
|
||||||
<div v-if="showLogo" class="flex justify-center" :style="{ height: containerHeight }">
|
<div v-if="showLogo" class="flex justify-center" :style="{ height: containerHeight }">
|
||||||
<img
|
<img
|
||||||
v-show="imgLoaded"
|
v-show="imgLoaded"
|
||||||
@@ -144,9 +144,12 @@ const { width } = useWindowSize();
|
|||||||
const showLogo = computed(() => width.value >= FIXED_WIDTH);
|
const showLogo = computed(() => width.value >= FIXED_WIDTH);
|
||||||
const containerWidth = computed(() => `${FIXED_WIDTH}px`);
|
const containerWidth = computed(() => `${FIXED_WIDTH}px`);
|
||||||
const containerHeight = computed(() => `${FIXED_HEIGHT}px`);
|
const containerHeight = computed(() => `${FIXED_HEIGHT}px`);
|
||||||
|
const loginGridStyle = computed(() => ({
|
||||||
|
gridTemplateColumns: showLogo.value ? 'repeat(2, minmax(0, 1fr))' : 'minmax(0, 1fr)',
|
||||||
|
}));
|
||||||
const loginFormClass = computed(() => {
|
const loginFormClass = computed(() => {
|
||||||
return showLogo.value
|
return showLogo.value
|
||||||
? 'hidden md:flex items-center justify-center p-4'
|
? 'flex items-center justify-center p-4 min-w-0'
|
||||||
: 'flex items-center justify-center p-4 w-full';
|
: 'flex items-center justify-center p-4 w-full min-w-0';
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -20,11 +20,18 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #leftToolBar v-if="clamStatus.isExist">
|
<template #leftToolBar v-if="clamStatus.isExist">
|
||||||
<el-button v-permission type="primary" :disabled="!clamStatus.isRunning" @click="onOpenDialog('add')">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
type="primary"
|
||||||
|
:disabled="!clamStatus.isRunning"
|
||||||
|
@click="onOpenDialog('add')"
|
||||||
|
>
|
||||||
{{ $t('toolbox.clam.clamCreate') }}
|
{{ $t('toolbox.clam.clamCreate') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
plain
|
plain
|
||||||
:disabled="selects.length === 0 || !clamStatus.isRunning"
|
:disabled="selects.length === 0 || !clamStatus.isRunning"
|
||||||
@click="onDelete(null)"
|
@click="onDelete(null)"
|
||||||
@@ -145,6 +152,7 @@
|
|||||||
<fu-input-rw-switch
|
<fu-input-rw-switch
|
||||||
v-model="row.description"
|
v-model="row.description"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@enter="onChange(row)"
|
@enter="onChange(row)"
|
||||||
@blur="onChange(row)"
|
@blur="onChange(row)"
|
||||||
/>
|
/>
|
||||||
@@ -344,6 +352,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.handle'),
|
label: i18n.global.t('commons.button.handle'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: async (row: Toolbox.ClamInfo) => {
|
click: async (row: Toolbox.ClamInfo) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await handleClamScan(row.id)
|
await handleClamScan(row.id)
|
||||||
@@ -360,6 +369,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Toolbox.ClamInfo) => {
|
click: (row: Toolbox.ClamInfo) => {
|
||||||
onOpenDialog('edit', row);
|
onOpenDialog('edit', row);
|
||||||
},
|
},
|
||||||
@@ -373,6 +383,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Toolbox.ClamInfo) => {
|
click: (row: Toolbox.ClamInfo) => {
|
||||||
onDelete(row);
|
onDelete(row);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,7 +19,12 @@
|
|||||||
<el-form-item :label="$t('toolbox.clam.scanDir')" prop="path">
|
<el-form-item :label="$t('toolbox.clam.scanDir')" prop="path">
|
||||||
<el-input v-model="dialogData.rowData!.path">
|
<el-input v-model="dialogData.rowData!.path">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-button icon="Folder" v-permission @click="scanDirRef.acceptParams({ dir: true })" />
|
<el-button
|
||||||
|
icon="Folder"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="scanDirRef.acceptParams({ dir: true })"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -41,6 +46,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
icon="Folder"
|
icon="Folder"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="infectedDirRef.acceptParams({ dir: true })"
|
@click="infectedDirRef.acceptParams({ dir: true })"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -211,7 +217,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -30,11 +30,24 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
|
|
||||||
<span class="mt-0.5">
|
<span class="mt-0.5">
|
||||||
<el-button type="primary" v-permission @click="onHandle(dialogData.rowData)" link>
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onHandle(dialogData.rowData)"
|
||||||
|
link
|
||||||
|
>
|
||||||
{{ $t('commons.button.handle') }}
|
{{ $t('commons.button.handle') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission :disabled="!hasRecords" type="primary" @click="onClean" link>
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
:disabled="!hasRecords"
|
||||||
|
type="primary"
|
||||||
|
@click="onClean"
|
||||||
|
link
|
||||||
|
>
|
||||||
{{ $t('commons.button.clean') }}
|
{{ $t('commons.button.clean') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
:disabled="!canUpdate()"
|
:disabled="!canUpdate()"
|
||||||
:placeholder="$t('commons.msg.noneData')"
|
:placeholder="$t('commons.msg.noneData')"
|
||||||
></CodemirrorPro>
|
></CodemirrorPro>
|
||||||
<el-button type="primary" class="mt-5" v-if="canUpdate()" v-permission @click="onSave">
|
<el-button type="primary" class="mt-5" v-if="canUpdate()" v-permission v-node-admin @click="onSave">
|
||||||
{{ $t('commons.button.save') }}
|
{{ $t('commons.button.save') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
v-if="!data.isActive"
|
v-if="!data.isActive"
|
||||||
link
|
link
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onOperate('ClamAV', 'start')"
|
@click="onOperate('ClamAV', 'start')"
|
||||||
>
|
>
|
||||||
{{ $t('commons.operate.start') }}
|
{{ $t('commons.operate.start') }}
|
||||||
@@ -23,16 +24,23 @@
|
|||||||
v-if="data.isActive"
|
v-if="data.isActive"
|
||||||
link
|
link
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onOperate('ClamAV', 'stop')"
|
@click="onOperate('ClamAV', 'stop')"
|
||||||
>
|
>
|
||||||
{{ $t('commons.operate.stop') }}
|
{{ $t('commons.operate.stop') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button type="primary" link v-permission @click="onOperate('ClamAV', 'restart')">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onOperate('ClamAV', 'restart')"
|
||||||
|
>
|
||||||
{{ $t('commons.button.restart') }}
|
{{ $t('commons.button.restart') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission type="primary" link @click="setting">
|
<el-button v-permission v-node-admin type="primary" link @click="setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
@@ -56,6 +64,7 @@
|
|||||||
v-if="!data.freshIsActive"
|
v-if="!data.freshIsActive"
|
||||||
link
|
link
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onOperate('FreshClam', 'start')"
|
@click="onOperate('FreshClam', 'start')"
|
||||||
>
|
>
|
||||||
{{ $t('commons.operate.start') }}
|
{{ $t('commons.operate.start') }}
|
||||||
@@ -65,12 +74,19 @@
|
|||||||
v-if="data.freshIsActive"
|
v-if="data.freshIsActive"
|
||||||
link
|
link
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@click="onOperate('FreshClam', 'stop')"
|
@click="onOperate('FreshClam', 'stop')"
|
||||||
>
|
>
|
||||||
{{ $t('commons.operate.stop') }}
|
{{ $t('commons.operate.stop') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button type="primary" link v-permission @click="onOperate('FreshClam', 'restart')">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onOperate('FreshClam', 'restart')"
|
||||||
|
>
|
||||||
{{ $t('commons.button.restart') }}
|
{{ $t('commons.button.restart') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div v-loading="loading">
|
<div v-loading="loading">
|
||||||
<LayoutContent :title="$t('setting.diskClean')" :divider="true">
|
<LayoutContent :title="$t('setting.diskClean')" :divider="true">
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="scanData">
|
<el-button v-permission v-node-admin type="primary" @click="scanData">
|
||||||
{{ $t('clean.scan') }}
|
{{ $t('clean.scan') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -79,6 +79,7 @@
|
|||||||
<el-text class="clean_title">{{ $t('clean.totalScan') }} {{ computeSize(totalSize) }}</el-text>
|
<el-text class="clean_title">{{ $t('clean.totalScan') }} {{ computeSize(totalSize) }}</el-text>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
type="primary"
|
type="primary"
|
||||||
class="-mt-2 ml-20"
|
class="-mt-2 ml-20"
|
||||||
:disabled="selectSize <= 0"
|
:disabled="selectSize <= 0"
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<el-button :disabled="loading" @click="onTest()">
|
<el-button :disabled="loading" @click="onTest()">
|
||||||
{{ $t('toolbox.device.dnsCheck') }}
|
{{ $t('toolbox.device.dnsCheck') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSave()">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSave()">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -9,7 +9,13 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSaveHostname(formRef)">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
:disabled="loading"
|
||||||
|
type="primary"
|
||||||
|
@click="onSaveHostname(formRef)"
|
||||||
|
>
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-button v-permission class="ml-3 mt-2" @click="handleHostsAdd()">
|
<el-button v-permission v-node-admin class="ml-3 mt-2" @click="handleHostsAdd()">
|
||||||
{{ $t('commons.button.add') }}
|
{{ $t('commons.button.add') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSave()">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSave()">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<el-form-item label="DNS" prop="dnsItem">
|
<el-form-item label="DNS" prop="dnsItem">
|
||||||
<el-input disabled v-model="form.dnsItem">
|
<el-input disabled v-model="form.dnsItem">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeDNS" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeDNS" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<el-form-item label="Hosts" prop="hosts">
|
<el-form-item label="Hosts" prop="hosts">
|
||||||
<el-input disabled v-model="form.hostItem">
|
<el-input disabled v-model="form.hostItem">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeHost" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeHost" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
<el-form-item label="Swap" prop="swap">
|
<el-form-item label="Swap" prop="swap">
|
||||||
<el-input disabled v-model="form.swapItem">
|
<el-input disabled v-model="form.swapItem">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeSwap" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeSwap" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<el-form-item :label="$t('toolbox.device.hostname')" prop="hostname">
|
<el-form-item :label="$t('toolbox.device.hostname')" prop="hostname">
|
||||||
<el-input disabled v-model="form.hostname">
|
<el-input disabled v-model="form.hostname">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeHostname" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeHostname" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
<el-form-item :label="$t('toolbox.device.passwd')" prop="passwd">
|
<el-form-item :label="$t('toolbox.device.passwd')" prop="passwd">
|
||||||
<el-input disabled v-model="form.passwd" type="password">
|
<el-input disabled v-model="form.passwd" type="password">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangePasswd" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangePasswd" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
<el-form-item :label="$t('toolbox.device.syncSite')" prop="ntp">
|
<el-form-item :label="$t('toolbox.device.syncSite')" prop="ntp">
|
||||||
<el-input disabled v-model="form.ntp">
|
<el-input disabled v-model="form.ntp">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeNtp" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeNtp" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
<el-form-item :label="$t('toolbox.device.timeZone')" prop="timeZone">
|
<el-form-item :label="$t('toolbox.device.timeZone')" prop="timeZone">
|
||||||
<el-input disabled v-model="form.timeZone">
|
<el-input disabled v-model="form.timeZone">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeTimeZone" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangeTimeZone" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -77,7 +77,13 @@
|
|||||||
<el-form-item :label="$t('toolbox.device.localTime')" prop="localTime">
|
<el-form-item :label="$t('toolbox.device.localTime')" prop="localTime">
|
||||||
<el-input disabled v-model="form.localTime">
|
<el-input disabled v-model="form.localTime">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeLocalTime" icon="Refresh" width="150px">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onChangeLocalTime"
|
||||||
|
icon="Refresh"
|
||||||
|
width="150px"
|
||||||
|
>
|
||||||
{{ $t('commons.button.sync') }}
|
{{ $t('commons.button.sync') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,13 +3,34 @@
|
|||||||
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
|
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
|
||||||
<el-form-item :label="$t('toolbox.device.syncSite')" prop="ntpSite" :rules="Rules.domain">
|
<el-form-item :label="$t('toolbox.device.syncSite')" prop="ntpSite" :rules="Rules.domain">
|
||||||
<el-input v-model="form.ntpSite" />
|
<el-input v-model="form.ntpSite" />
|
||||||
<el-button type="primary" link class="tagClass" v-permission @click="form.ntpSite = 'pool.ntp.org'">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
class="tagClass"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="form.ntpSite = 'pool.ntp.org'"
|
||||||
|
>
|
||||||
{{ $t('commons.table.default') }}
|
{{ $t('commons.table.default') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" link class="tagClass" v-permission @click="form.ntpSite = 'ntp.aliyun.com'">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
class="tagClass"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="form.ntpSite = 'ntp.aliyun.com'"
|
||||||
|
>
|
||||||
{{ $t('toolbox.device.ntpALi') }}
|
{{ $t('toolbox.device.ntpALi') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" link class="tagClass" v-permission @click="form.ntpSite = 'time.google.com'">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
class="tagClass"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="form.ntpSite = 'time.google.com'"
|
||||||
|
>
|
||||||
{{ $t('toolbox.device.ntpGoogle') }}
|
{{ $t('toolbox.device.ntpGoogle') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -17,7 +38,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSyncTime(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSyncTime(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -23,7 +23,13 @@
|
|||||||
<el-button :disabled="loading" @click="passwordVisible = false">
|
<el-button :disabled="loading" @click="passwordVisible = false">
|
||||||
{{ $t('commons.button.cancel') }}
|
{{ $t('commons.button.cancel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="submitChangePassword(formRef)">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
:disabled="loading"
|
||||||
|
type="primary"
|
||||||
|
@click="submitChangePassword(formRef)"
|
||||||
|
>
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column min-width="70">
|
<el-table-column min-width="70">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button v-permission link type="primary" @click="onSave(scope.row)">
|
<el-button v-permission v-node-admin link type="primary" @click="onSave(scope.row)">
|
||||||
{{ $t('commons.button.save') }}
|
{{ $t('commons.button.save') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSave(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSave(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSave(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSave(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSave(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSave(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -9,14 +9,28 @@
|
|||||||
<el-tag>{{ form.version }}</el-tag>
|
<el-tag>{{ form.version }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-0.5">
|
<div class="mt-0.5">
|
||||||
<el-button v-if="form.isActive" v-permission type="primary" @click="onOperate('stop')" link>
|
<el-button
|
||||||
|
v-if="form.isActive"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
type="primary"
|
||||||
|
@click="onOperate('stop')"
|
||||||
|
link
|
||||||
|
>
|
||||||
{{ $t('commons.button.stop') }}
|
{{ $t('commons.button.stop') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="!form.isActive" v-permission type="primary" @click="onOperate('start')" link>
|
<el-button
|
||||||
|
v-if="!form.isActive"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
type="primary"
|
||||||
|
@click="onOperate('start')"
|
||||||
|
link
|
||||||
|
>
|
||||||
{{ $t('commons.button.start') }}
|
{{ $t('commons.button.start') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission type="primary" @click="onOperate('restart')" link>
|
<el-button v-permission v-node-admin type="primary" @click="onOperate('restart')" link>
|
||||||
{{ $t('commons.button.restart') }}
|
{{ $t('commons.button.restart') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
@@ -25,6 +39,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-switch
|
<el-switch
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
size="small"
|
size="small"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
inactive-value="disable"
|
inactive-value="disable"
|
||||||
@@ -52,6 +67,7 @@
|
|||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
:disabled="!form.isActive"
|
:disabled="!form.isActive"
|
||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
@@ -61,6 +77,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
:disabled="!form.isActive"
|
:disabled="!form.isActive"
|
||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
@@ -81,7 +98,7 @@
|
|||||||
<el-form-item :label="$t('toolbox.fail2ban.sshPort')" prop="port">
|
<el-form-item :label="$t('toolbox.fail2ban.sshPort')" prop="port">
|
||||||
<el-input disabled v-model="form.port">
|
<el-input disabled v-model="form.port">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangePort" icon="Setting">
|
<el-button v-permission v-node-admin @click="onChangePort" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -91,7 +108,12 @@
|
|||||||
<el-form-item :label="$t('toolbox.fail2ban.maxRetry')" prop="maxRetry">
|
<el-form-item :label="$t('toolbox.fail2ban.maxRetry')" prop="maxRetry">
|
||||||
<el-input disabled v-model="form.maxRetry">
|
<el-input disabled v-model="form.maxRetry">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeMaxRetry" icon="Setting">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onChangeMaxRetry"
|
||||||
|
icon="Setting"
|
||||||
|
>
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -100,7 +122,12 @@
|
|||||||
<el-form-item :label="$t('toolbox.fail2ban.banTime')" prop="banTime">
|
<el-form-item :label="$t('toolbox.fail2ban.banTime')" prop="banTime">
|
||||||
<el-input disabled v-model="form.banTimeItem">
|
<el-input disabled v-model="form.banTimeItem">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeBanTime" icon="Setting">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onChangeBanTime"
|
||||||
|
icon="Setting"
|
||||||
|
>
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -110,7 +137,12 @@
|
|||||||
<el-form-item :label="$t('toolbox.fail2ban.findTime')" prop="findTime">
|
<el-form-item :label="$t('toolbox.fail2ban.findTime')" prop="findTime">
|
||||||
<el-input disabled v-model="form.findTimeItem">
|
<el-input disabled v-model="form.findTimeItem">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeFindTime" icon="Setting">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onChangeFindTime"
|
||||||
|
icon="Setting"
|
||||||
|
>
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -119,7 +151,12 @@
|
|||||||
<el-form-item :label="$t('toolbox.fail2ban.banAction')" prop="banAction">
|
<el-form-item :label="$t('toolbox.fail2ban.banAction')" prop="banAction">
|
||||||
<el-input disabled v-model="form.banAction">
|
<el-input disabled v-model="form.banAction">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeBanAction" icon="Setting">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onChangeBanAction"
|
||||||
|
icon="Setting"
|
||||||
|
>
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -128,7 +165,12 @@
|
|||||||
<el-form-item :label="$t('toolbox.fail2ban.logPath')" prop="logPath">
|
<el-form-item :label="$t('toolbox.fail2ban.logPath')" prop="logPath">
|
||||||
<el-input disabled v-model="form.logPath">
|
<el-input disabled v-model="form.logPath">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="onChangeLogPath" icon="Setting">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="onChangeLogPath"
|
||||||
|
icon="Setting"
|
||||||
|
>
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -145,7 +187,14 @@
|
|||||||
v-model="fail2banConf"
|
v-model="fail2banConf"
|
||||||
:heightDiff="460"
|
:heightDiff="460"
|
||||||
></CodemirrorPro>
|
></CodemirrorPro>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSaveFile" class="mt-2.5">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
:disabled="loading"
|
||||||
|
type="primary"
|
||||||
|
@click="onSaveFile"
|
||||||
|
class="mt-2.5"
|
||||||
|
>
|
||||||
{{ $t('commons.button.save') }}
|
{{ $t('commons.button.save') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSave(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSave(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<el-form-item :label="$t('toolbox.fail2ban.logPath')" prop="logPath" :rules="Rules.requiredInput">
|
<el-form-item :label="$t('toolbox.fail2ban.logPath')" prop="logPath" :rules="Rules.requiredInput">
|
||||||
<el-input v-model="form.logPath">
|
<el-input v-model="form.logPath">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-button v-permission icon="Folder" @click="fileRef.acceptParams({})" />
|
<el-button v-permission v-node-admin icon="Folder" @click="fileRef.acceptParams({})" />
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<span class="input-help">{{ $t('toolbox.fail2ban.logPathHelper') }}</span>
|
<span class="input-help">{{ $t('toolbox.fail2ban.logPathHelper') }}</span>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSave(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSave(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSave(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSave(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSave(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSave(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -8,14 +8,28 @@
|
|||||||
<Status class="mt-0.5" :status="form.isActive ? 'enable' : 'disable'" />
|
<Status class="mt-0.5" :status="form.isActive ? 'enable' : 'disable'" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-0.5">
|
<div class="mt-0.5">
|
||||||
<el-button v-if="form.isActive" v-permission type="primary" @click="onOperate('stop')" link>
|
<el-button
|
||||||
|
v-if="form.isActive"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
type="primary"
|
||||||
|
@click="onOperate('stop')"
|
||||||
|
link
|
||||||
|
>
|
||||||
{{ $t('commons.button.stop') }}
|
{{ $t('commons.button.stop') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="!form.isActive" v-permission type="primary" @click="onOperate('start')" link>
|
<el-button
|
||||||
|
v-if="!form.isActive"
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
type="primary"
|
||||||
|
@click="onOperate('start')"
|
||||||
|
link
|
||||||
|
>
|
||||||
{{ $t('commons.button.start') }}
|
{{ $t('commons.button.start') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission type="primary" @click="onOperate('restart')" link>
|
<el-button v-permission v-node-admin type="primary" @click="onOperate('restart')" link>
|
||||||
{{ $t('commons.button.restart') }}
|
{{ $t('commons.button.restart') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,13 +49,13 @@
|
|||||||
</el-alert>
|
</el-alert>
|
||||||
</template>
|
</template>
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="onOpenDialog('add')">
|
<el-button v-permission v-node-admin type="primary" @click="onOpenDialog('add')">
|
||||||
{{ $t('commons.button.add') }}
|
{{ $t('commons.button.add') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission @click="onSync()">
|
<el-button v-permission v-node-admin @click="onSync()">
|
||||||
{{ $t('commons.button.sync') }}
|
{{ $t('commons.button.sync') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission plain :disabled="selects.length === 0" @click="onDelete(null)">
|
<el-button v-permission v-node-admin plain :disabled="selects.length === 0" @click="onDelete(null)">
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -126,6 +140,7 @@
|
|||||||
<fu-input-rw-switch
|
<fu-input-rw-switch
|
||||||
v-model="row.description"
|
v-model="row.description"
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
@enter="onChange(row)"
|
@enter="onChange(row)"
|
||||||
@blur="onChange(row)"
|
@blur="onChange(row)"
|
||||||
/>
|
/>
|
||||||
@@ -337,6 +352,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
disabled: (row: Toolbox.FtpInfo) => {
|
disabled: (row: Toolbox.FtpInfo) => {
|
||||||
return row.status === 'deleted';
|
return row.status === 'deleted';
|
||||||
},
|
},
|
||||||
@@ -356,6 +372,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: (row: Toolbox.FtpInfo) => {
|
click: (row: Toolbox.FtpInfo) => {
|
||||||
onDelete(row);
|
onDelete(row);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<el-form-item :label="$t('commons.login.password')" prop="password">
|
<el-form-item :label="$t('commons.login.password')" prop="password">
|
||||||
<el-input type="password" clearable v-model="dialogData.rowData!.password" show-password>
|
<el-input type="password" clearable v-model="dialogData.rowData!.password" show-password>
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button v-permission @click="random">
|
<el-button v-permission v-node-admin @click="random">
|
||||||
{{ $t('commons.button.random') }}
|
{{ $t('commons.button.random') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -22,7 +22,12 @@
|
|||||||
<el-form-item :label="$t('file.root')" prop="path">
|
<el-form-item :label="$t('file.root')" prop="path">
|
||||||
<el-input v-model="dialogData.rowData!.path">
|
<el-input v-model="dialogData.rowData!.path">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-button v-permission icon="Folder" @click="fileRef.acceptParams({ dir: true })" />
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
icon="Folder"
|
||||||
|
@click="fileRef.acceptParams({ dir: true })"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<span class="input-help">{{ $t('toolbox.ftp.dirHelper') }}</span>
|
<span class="input-help">{{ $t('toolbox.ftp.dirHelper') }}</span>
|
||||||
@@ -34,7 +39,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
<el-button v-permission v-node-admin :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
<RouterButton :buttons="buttons">
|
<RouterButton :buttons="buttons">
|
||||||
<template #route-button>
|
<template #route-button>
|
||||||
<div class="router-button">
|
<div class="router-button">
|
||||||
<el-button v-permission link type="primary" @click="onRestart('1panel')">
|
<el-button v-permission v-node-admin link type="primary" @click="onRestart('1panel')">
|
||||||
{{ $t('home.restart_1panel') }}
|
{{ $t('home.restart_1panel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission link type="primary" @click="onRestart('system')">
|
<el-button v-permission v-node-admin link type="primary" @click="onRestart('system')">
|
||||||
{{ $t('home.restart_system') }}
|
{{ $t('home.restart_system') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<span class="input-help">{{ $t('tool.supervisor.serviceNameHelper') }}</span>
|
<span class="input-help">{{ $t('tool.supervisor.serviceNameHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button v-permission type="primary" @click="submit(initForm)" :disabled="loading">
|
<el-button v-permission v-node-admin type="primary" @click="submit(initForm)" :disabled="loading">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div v-loading="loading">
|
<div v-loading="loading">
|
||||||
<CodemirrorPro class="mt-5" v-model="content" :heightDiff="375"></CodemirrorPro>
|
<CodemirrorPro class="mt-5" v-model="content" :heightDiff="375"></CodemirrorPro>
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<el-button v-permission type="primary" @click="submit()" :disabled="loading">
|
<el-button v-permission v-node-admin type="primary" @click="submit()" :disabled="loading">
|
||||||
{{ $t('commons.button.save') }}
|
{{ $t('commons.button.save') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,7 +22,12 @@
|
|||||||
<el-form-item :label="$t('tool.supervisor.dir')" prop="dir">
|
<el-form-item :label="$t('tool.supervisor.dir')" prop="dir">
|
||||||
<el-input v-model.trim="process.dir">
|
<el-input v-model.trim="process.dir">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-button v-permission icon="Folder" @click="fileRef.acceptParams({ dir: true })" />
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
icon="Folder"
|
||||||
|
@click="fileRef.acceptParams({ dir: true })"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -47,7 +52,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission type="primary" @click="submit(processForm)" :disabled="loading">
|
<el-button v-permission v-node-admin type="primary" @click="submit(processForm)" :disabled="loading">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span>
|
<span>
|
||||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button v-permission type="primary" :disabled="loading" @click="submit()">
|
<el-button v-permission v-node-admin type="primary" :disabled="loading" @click="submit()">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="showTable" #leftToolBar>
|
<template v-if="showTable" #leftToolBar>
|
||||||
<el-button v-permission type="primary" @click="openCreate" :disabled="showStopped">
|
<el-button v-permission v-node-admin type="primary" @click="openCreate" :disabled="showStopped">
|
||||||
{{ $t('commons.button.create') }}
|
{{ $t('commons.button.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -75,17 +75,25 @@
|
|||||||
<div v-if="row.status && row.status.length > 0 && row.hasLoad">
|
<div v-if="row.status && row.status.length > 0 && row.hasLoad">
|
||||||
<Status
|
<Status
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
v-if="checkStatus(row.status) === 'RUNNING'"
|
v-if="checkStatus(row.status) === 'RUNNING'"
|
||||||
status="running"
|
status="running"
|
||||||
@click="operate('stop', row.name)"
|
@click="operate('stop', row.name)"
|
||||||
/>
|
/>
|
||||||
<Status
|
<Status
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
v-else-if="checkStatus(row.status) === 'WARNING'"
|
v-else-if="checkStatus(row.status) === 'WARNING'"
|
||||||
status="unhealthy"
|
status="unhealthy"
|
||||||
@click="operate('restart', row.name)"
|
@click="operate('restart', row.name)"
|
||||||
/>
|
/>
|
||||||
<Status v-else v-permission status="stopped" @click="operate('start', row.name)" />
|
<Status
|
||||||
|
v-else
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
status="stopped"
|
||||||
|
@click="operate('start', row.name)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!row.hasLoad">
|
<div v-if="!row.hasLoad">
|
||||||
<el-button link loading></el-button>
|
<el-button link loading></el-button>
|
||||||
@@ -342,6 +350,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: function (row: HostTool.SupervisorProcess) {
|
click: function (row: HostTool.SupervisorProcess) {
|
||||||
edit(row);
|
edit(row);
|
||||||
},
|
},
|
||||||
@@ -349,6 +358,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('website.sourceFile'),
|
label: i18n.global.t('website.sourceFile'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: function (row: HostTool.SupervisorProcess) {
|
click: function (row: HostTool.SupervisorProcess) {
|
||||||
getFile(row.name, 'config');
|
getFile(row.name, 'config');
|
||||||
},
|
},
|
||||||
@@ -362,6 +372,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.restart'),
|
label: i18n.global.t('commons.button.restart'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: function (row: HostTool.SupervisorProcess) {
|
click: function (row: HostTool.SupervisorProcess) {
|
||||||
operate('restart', row.name);
|
operate('restart', row.name);
|
||||||
},
|
},
|
||||||
@@ -369,6 +380,7 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
permission: true,
|
permission: true,
|
||||||
|
nodeAdmin: true,
|
||||||
click: function (row: HostTool.SupervisorProcess) {
|
click: function (row: HostTool.SupervisorProcess) {
|
||||||
operate('delete', row.name);
|
operate('delete', row.name);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,7 +22,13 @@
|
|||||||
:height-diff="300"
|
:height-diff="300"
|
||||||
>
|
>
|
||||||
<template #button>
|
<template #button>
|
||||||
<el-button v-permission @click="cleanLog" icon="Delete" :disabled="hasContent === false">
|
<el-button
|
||||||
|
v-permission
|
||||||
|
v-node-admin
|
||||||
|
@click="cleanLog"
|
||||||
|
icon="Delete"
|
||||||
|
:disabled="hasContent === false"
|
||||||
|
>
|
||||||
{{ $t('commons.button.clean') }}
|
{{ $t('commons.button.clean') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
<div class="mt-0.5" v-if="!data.init">
|
<div class="mt-0.5" v-if="!data.init">
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
type="primary"
|
type="primary"
|
||||||
v-if="data.status != 'running'"
|
v-if="data.status != 'running'"
|
||||||
link
|
link
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-permission
|
v-permission
|
||||||
|
v-node-admin
|
||||||
type="primary"
|
type="primary"
|
||||||
v-if="data.status == 'running'"
|
v-if="data.status == 'running'"
|
||||||
link
|
link
|
||||||
@@ -28,16 +30,16 @@
|
|||||||
{{ $t('commons.operate.stop') }}
|
{{ $t('commons.operate.stop') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission type="primary" link @click="onOperate('restart')">
|
<el-button v-permission v-node-admin type="primary" link @click="onOperate('restart')">
|
||||||
{{ $t('commons.button.restart') }}
|
{{ $t('commons.button.restart') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button v-permission type="primary" link @click="setting">
|
<el-button v-permission v-node-admin type="primary" link @click="setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-0.5" v-else>
|
<div class="mt-0.5" v-else>
|
||||||
<el-button v-permission type="primary" link @click="init">
|
<el-button v-permission v-node-admin type="primary" link @click="init">
|
||||||
{{ $t('commons.button.init') }}
|
{{ $t('commons.button.init') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<el-button @click="handleClose()" :disabled="loading">
|
<el-button @click="handleClose()" :disabled="loading">
|
||||||
{{ $t('commons.button.cancel') }}
|
{{ $t('commons.button.cancel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission type="primary" @click="openSubmit(initForm)" :disabled="loading">
|
<el-button v-permission v-node-admin type="primary" @click="openSubmit(initForm)" :disabled="loading">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -231,8 +231,8 @@ let selects = ref<any>([]);
|
|||||||
const columns = ref([]);
|
const columns = ref([]);
|
||||||
const req = reactive({
|
const req = reactive({
|
||||||
domain: '',
|
domain: '',
|
||||||
orderBy: 'expire_date',
|
orderBy: 'updated_at',
|
||||||
order: 'ascending',
|
order: 'descending',
|
||||||
});
|
});
|
||||||
|
|
||||||
const routerButton = [
|
const routerButton = [
|
||||||
@@ -325,7 +325,7 @@ const onDownload = (ssl: Website.SSLDTO) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const changeSort = ({ order }) => {
|
const changeSort = ({ order }) => {
|
||||||
req.orderBy = 'expire_date';
|
req.orderBy = order ? 'expire_date' : 'updated_at';
|
||||||
req.order = order || 'descending';
|
req.order = order || 'descending';
|
||||||
search();
|
search();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user