mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 07:57:21 +08:00
fix: 优化权限
This commit is contained in:
@@ -55,7 +55,7 @@ func (s *App) SaveConfig(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = io.Write(fmt.Sprintf("%s/server/nginx/conf/nginx.conf", app.Root), req.Config, 0644); err != nil {
|
||||
if err = io.Write(fmt.Sprintf("%s/server/nginx/conf/nginx.conf", app.Root), req.Config, 0600); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
conf = regexp.MustCompile(`listen\s+(\d+);`).ReplaceAllString(conf, "listen "+cast.ToString(req.Port)+";")
|
||||
if err = io.Write(fmt.Sprintf("%s/sites/phpmyadmin/config/nginx.conf", app.Root), conf, 0644); err != nil {
|
||||
if err = io.Write(fmt.Sprintf("%s/sites/phpmyadmin/config/nginx.conf", app.Root), conf, 0600); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = io.Write(fmt.Sprintf("%s/sites/phpmyadmin/config/nginx.conf", app.Root), req.Config, 0644); err != nil {
|
||||
if err = io.Write(fmt.Sprintf("%s/sites/phpmyadmin/config/nginx.conf", app.Root), req.Config, 0600); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ type Cert struct {
|
||||
DNSID uint `gorm:"not null;default:0" json:"dns_id"` // 关联的 DNS ID
|
||||
Type string `gorm:"not null;default:''" json:"type"` // 证书类型 (P256, P384, 2048, 3072, 4096)
|
||||
Domains []string `gorm:"not null;default:'[]';serializer:json" json:"domains"`
|
||||
AutoRenew bool `gorm:"not null;default:false" json:"auto_renew"` // 自动续签
|
||||
AutoRenewal bool `gorm:"not null;default:false" json:"auto_renewal"` // 自动续签
|
||||
RenewalInfo mholtacme.RenewalInfo `gorm:"not null;default:'{}';serializer:json" json:"renewal_info"` // 续签信息
|
||||
CertURL string `gorm:"not null;default:''" json:"cert_url"` // 证书 URL (续签时使用)
|
||||
Cert string `gorm:"not null;default:''" json:"cert"` // 证书内容
|
||||
|
||||
@@ -49,19 +49,20 @@ func (r *certRepo) List(page, limit uint) ([]*types.CertList, int64, error) {
|
||||
list := make([]*types.CertList, 0)
|
||||
for cert := range slices.Values(certs) {
|
||||
item := &types.CertList{
|
||||
ID: cert.ID,
|
||||
AccountID: cert.AccountID,
|
||||
WebsiteID: cert.WebsiteID,
|
||||
DNSID: cert.DNSID,
|
||||
Type: cert.Type,
|
||||
Domains: cert.Domains,
|
||||
AutoRenew: cert.AutoRenew,
|
||||
Cert: cert.Cert,
|
||||
Key: cert.Key,
|
||||
CertURL: cert.CertURL,
|
||||
Script: cert.Script,
|
||||
CreatedAt: cert.CreatedAt,
|
||||
UpdatedAt: cert.UpdatedAt,
|
||||
ID: cert.ID,
|
||||
AccountID: cert.AccountID,
|
||||
WebsiteID: cert.WebsiteID,
|
||||
DNSID: cert.DNSID,
|
||||
Type: cert.Type,
|
||||
Domains: cert.Domains,
|
||||
AutoRenewal: cert.AutoRenewal,
|
||||
NextRenewal: cert.RenewalInfo.SelectedTime,
|
||||
Cert: cert.Cert,
|
||||
Key: cert.Key,
|
||||
CertURL: cert.CertURL,
|
||||
Script: cert.Script,
|
||||
CreatedAt: cert.CreatedAt,
|
||||
UpdatedAt: cert.UpdatedAt,
|
||||
}
|
||||
if decode, err := pkgcert.ParseCert(cert.Cert); err == nil {
|
||||
item.NotBefore = decode.NotBefore
|
||||
@@ -112,12 +113,12 @@ func (r *certRepo) Upload(req *request.CertUpload) (*biz.Cert, error) {
|
||||
|
||||
func (r *certRepo) Create(req *request.CertCreate) (*biz.Cert, error) {
|
||||
cert := &biz.Cert{
|
||||
AccountID: req.AccountID,
|
||||
WebsiteID: req.WebsiteID,
|
||||
DNSID: req.DNSID,
|
||||
Type: req.Type,
|
||||
Domains: req.Domains,
|
||||
AutoRenew: req.AutoRenew,
|
||||
AccountID: req.AccountID,
|
||||
WebsiteID: req.WebsiteID,
|
||||
DNSID: req.DNSID,
|
||||
Type: req.Type,
|
||||
Domains: req.Domains,
|
||||
AutoRenewal: req.AutoRenewal,
|
||||
}
|
||||
if err := r.db.Create(cert).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -130,21 +131,21 @@ func (r *certRepo) Update(req *request.CertUpdate) error {
|
||||
if err == nil && req.Type == "upload" {
|
||||
req.Domains = info.DNSNames
|
||||
}
|
||||
if req.Type == "upload" && req.AutoRenew {
|
||||
return errors.New(r.t.Get("upload certificate cannot be set to auto renew"))
|
||||
if req.Type == "upload" && req.AutoRenewal {
|
||||
return errors.New(r.t.Get("upload certificate cannot be set to auto renewal"))
|
||||
}
|
||||
|
||||
return r.db.Model(&biz.Cert{}).Where("id = ?", req.ID).Select("*").Updates(&biz.Cert{
|
||||
ID: req.ID,
|
||||
AccountID: req.AccountID,
|
||||
WebsiteID: req.WebsiteID,
|
||||
DNSID: req.DNSID,
|
||||
Type: req.Type,
|
||||
Cert: req.Cert,
|
||||
Key: req.Key,
|
||||
Script: req.Script,
|
||||
Domains: req.Domains,
|
||||
AutoRenew: req.AutoRenew,
|
||||
ID: req.ID,
|
||||
AccountID: req.AccountID,
|
||||
WebsiteID: req.WebsiteID,
|
||||
DNSID: req.DNSID,
|
||||
Type: req.Type,
|
||||
Cert: req.Cert,
|
||||
Key: req.Key,
|
||||
Script: req.Script,
|
||||
Domains: req.Domains,
|
||||
AutoRenewal: req.AutoRenewal,
|
||||
}).Error
|
||||
}
|
||||
|
||||
@@ -403,10 +404,10 @@ func (r *certRepo) Deploy(ID, WebsiteID uint) error {
|
||||
if err = r.db.Where("id", WebsiteID).First(website).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err = io.Write(fmt.Sprintf("%s/sites/%s/config/fullchain.pem", app.Root, website.Name), cert.Cert, 0644); err != nil {
|
||||
if err = io.Write(fmt.Sprintf("%s/sites/%s/config/fullchain.pem", app.Root, website.Name), cert.Cert, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = io.Write(fmt.Sprintf("%s/sites/%s/config/private.key", app.Root, website.Name), cert.Key, 0644); err != nil {
|
||||
if err = io.Write(fmt.Sprintf("%s/sites/%s/config/private.key", app.Root, website.Name), cert.Key, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = systemctl.Reload("nginx"); err != nil {
|
||||
|
||||
@@ -318,10 +318,10 @@ func (r *settingRepo) UpdatePanel(req *request.SettingPanel) (bool, error) {
|
||||
if _, err := cert.ParseKey(req.Key); err != nil {
|
||||
return false, errors.New(r.t.Get("failed to parse private key: %v", err))
|
||||
}
|
||||
if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), req.Cert, 0644); err != nil {
|
||||
if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), req.Cert, 0600); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.key"), req.Key, 0644); err != nil {
|
||||
if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.key"), req.Key, 0600); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -387,10 +387,10 @@ func (r *settingRepo) UpdateCert(req *request.SettingCert) error {
|
||||
return errors.New(r.t.Get("failed to parse private key: %v", err))
|
||||
}
|
||||
|
||||
if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), req.Cert, 0644); err != nil {
|
||||
if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), req.Cert, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.key"), req.Key, 0644); err != nil {
|
||||
if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.key"), req.Key, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -248,14 +248,14 @@ func (r *websiteRepo) Create(req *request.WebsiteCreate) (*biz.Website, error) {
|
||||
}
|
||||
|
||||
// 创建配置文件目录
|
||||
if err = os.MkdirAll(filepath.Join(app.Root, "sites", req.Name, "config", "site"), 0644); err != nil {
|
||||
if err = os.MkdirAll(filepath.Join(app.Root, "sites", req.Name, "config", "site"), 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = os.MkdirAll(filepath.Join(app.Root, "sites", req.Name, "config", "shared"), 0644); err != nil {
|
||||
if err = os.MkdirAll(filepath.Join(app.Root, "sites", req.Name, "config", "shared"), 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 创建日志目录
|
||||
if err = os.MkdirAll(filepath.Join(app.Root, "sites", req.Name, "log"), 0644); err != nil {
|
||||
if err = os.MkdirAll(filepath.Join(app.Root, "sites", req.Name, "log"), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -381,20 +381,36 @@ location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", req.Name, "config", "fullchain.pem"), "", 0644); err != nil {
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", req.Name, "config", "fullchain.pem"), "", 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", req.Name, "config", "private.key"), "", 0644); err != nil {
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", req.Name, "config", "private.key"), "", 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 设置目录权限
|
||||
// sites/site_name 0755 root
|
||||
// sites/site_name/config 0600 root
|
||||
// sites/site_name/log 644 www
|
||||
// sites/site_name/public 0755 www
|
||||
if err = io.Chmod(filepath.Join(app.Root, "sites", req.Name), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = io.Chmod(req.Path, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = io.Chown(req.Path, "www", "www"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = io.Chmod(filepath.Join(app.Root, "sites", req.Name, "log"), 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = io.Chown(filepath.Join(app.Root, "sites", req.Name, "log"), "www", "www"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = io.Chmod(filepath.Join(app.Root, "sites", req.Name, "config"), 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// PHP 网站默认开启防跨站
|
||||
if req.Type == "php" {
|
||||
@@ -482,10 +498,10 @@ func (r *websiteRepo) Update(req *request.WebsiteUpdate) error {
|
||||
// SSL
|
||||
certPath := filepath.Join(app.Root, "sites", website.Name, "config", "fullchain.pem")
|
||||
keyPath := filepath.Join(app.Root, "sites", website.Name, "config", "private.key")
|
||||
if err = io.Write(certPath, req.SSLCert, 0644); err != nil {
|
||||
if err = io.Write(certPath, req.SSLCert, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = io.Write(keyPath, req.SSLKey, 0644); err != nil {
|
||||
if err = io.Write(keyPath, req.SSLKey, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
website.SSL = req.SSL
|
||||
@@ -665,15 +681,15 @@ func (r *websiteRepo) ResetConfig(id uint) error {
|
||||
if err = vhost.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", website.Name, "config", "fullchain.pem"), "", 0644); err != nil {
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", website.Name, "config", "fullchain.pem"), "", 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", website.Name, "config", "private.key"), "", 0644); err != nil {
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", website.Name, "config", "private.key"), "", 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
// PHP 网站默认伪静态
|
||||
if website.Type == biz.WebsiteTypePHP {
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", website.Name, "config", "site", "010-rewrite.conf"), "", 0644); err != nil {
|
||||
if err = io.Write(filepath.Join(app.Root, "sites", website.Name, "config", "site", "010-rewrite.conf"), "", 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -727,10 +743,10 @@ func (r *websiteRepo) UpdateCert(req *request.WebsiteUpdateCert) error {
|
||||
|
||||
certPath := filepath.Join(app.Root, "sites", website.Name, "config", "fullchain.pem")
|
||||
keyPath := filepath.Join(app.Root, "sites", website.Name, "config", "private.key")
|
||||
if err := io.Write(certPath, req.Cert, 0644); err != nil {
|
||||
if err := io.Write(certPath, req.Cert, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := io.Write(keyPath, req.Key, 0644); err != nil {
|
||||
if err := io.Write(keyPath, req.Key, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -759,11 +775,11 @@ func (r *websiteRepo) ObtainCert(ctx context.Context, id uint) error {
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
newCert, err = r.cert.Create(&request.CertCreate{
|
||||
Type: string(acme.KeyEC256),
|
||||
Domains: website.Domains,
|
||||
AutoRenew: true,
|
||||
AccountID: account.ID,
|
||||
WebsiteID: website.ID,
|
||||
Type: string(acme.KeyEC256),
|
||||
Domains: website.Domains,
|
||||
AutoRenewal: true,
|
||||
AccountID: account.ID,
|
||||
WebsiteID: website.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -6,25 +6,25 @@ type CertUpload struct {
|
||||
}
|
||||
|
||||
type CertCreate struct {
|
||||
Type string `form:"type" json:"type" validate:"required|in:P256,P384,2048,3072,4096"`
|
||||
Domains []string `form:"domains" json:"domains" validate:"required|isSlice"`
|
||||
AutoRenew bool `form:"auto_renew" json:"auto_renew"`
|
||||
AccountID uint `form:"account_id" json:"account_id"`
|
||||
DNSID uint `form:"dns_id" json:"dns_id"`
|
||||
WebsiteID uint `form:"website_id" json:"website_id"`
|
||||
Type string `form:"type" json:"type" validate:"required|in:P256,P384,2048,3072,4096"`
|
||||
Domains []string `form:"domains" json:"domains" validate:"required|isSlice"`
|
||||
AutoRenewal bool `form:"auto_renewal" json:"auto_renewal"`
|
||||
AccountID uint `form:"account_id" json:"account_id"`
|
||||
DNSID uint `form:"dns_id" json:"dns_id"`
|
||||
WebsiteID uint `form:"website_id" json:"website_id"`
|
||||
}
|
||||
|
||||
type CertUpdate struct {
|
||||
ID uint `form:"id" json:"id" validate:"required|exists:certs,id"`
|
||||
Type string `form:"type" json:"type" validate:"required|in:P256,P384,2048,3072,4096,upload"`
|
||||
Domains []string `form:"domains" json:"domains" validate:"required|isSlice"`
|
||||
Cert string `form:"cert" json:"cert"`
|
||||
Key string `form:"key" json:"key"`
|
||||
Script string `form:"script" json:"script"`
|
||||
AutoRenew bool `form:"auto_renew" json:"auto_renew"`
|
||||
AccountID uint `form:"account_id" json:"account_id"`
|
||||
DNSID uint `form:"dns_id" json:"dns_id"`
|
||||
WebsiteID uint `form:"website_id" json:"website_id"`
|
||||
ID uint `form:"id" json:"id" validate:"required|exists:certs,id"`
|
||||
Type string `form:"type" json:"type" validate:"required|in:P256,P384,2048,3072,4096,upload"`
|
||||
Domains []string `form:"domains" json:"domains" validate:"required|isSlice"`
|
||||
Cert string `form:"cert" json:"cert"`
|
||||
Key string `form:"key" json:"key"`
|
||||
Script string `form:"script" json:"script"`
|
||||
AutoRenewal bool `form:"auto_renewal" json:"auto_renewal"`
|
||||
AccountID uint `form:"account_id" json:"account_id"`
|
||||
DNSID uint `form:"dns_id" json:"dns_id"`
|
||||
WebsiteID uint `form:"website_id" json:"website_id"`
|
||||
}
|
||||
|
||||
type CertDeploy struct {
|
||||
|
||||
@@ -50,7 +50,7 @@ func (r *CertRenew) Run() {
|
||||
|
||||
for _, cert := range certs {
|
||||
// 跳过上传类型或未开启自动续签的证书
|
||||
if cert.Type == "upload" || !cert.AutoRenew {
|
||||
if cert.Type == "upload" || !cert.AutoRenewal {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -392,10 +392,10 @@ func (s *CliService) HTTPSGenerate(ctx context.Context, cmd *cli.Command) error
|
||||
fmt.Println(s.t.Get("Successfully obtained ACME certificate"))
|
||||
}
|
||||
|
||||
if err = io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), string(crt), 0644); err != nil {
|
||||
if err = io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), string(crt), 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = io.Write(filepath.Join(app.Root, "panel/storage/cert.key"), string(key), 0644); err != nil {
|
||||
if err = io.Write(filepath.Join(app.Root, "panel/storage/cert.key"), string(key), 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ func (s *panelSolver) writeNginxConfig() error {
|
||||
}
|
||||
conf.WriteString("}\n")
|
||||
|
||||
if err := os.WriteFile(s.conf, []byte(conf.String()), 0644); err != nil {
|
||||
if err := os.WriteFile(s.conf, []byte(conf.String()), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write nginx config %q: %w", s.conf, err)
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ func (s *panelSolver) CleanUp(ctx context.Context, _ acme.Challenge) error {
|
||||
}
|
||||
|
||||
// 清理 nginx 配置
|
||||
if err := os.WriteFile(s.conf, []byte(""), 0644); err != nil {
|
||||
if err := os.WriteFile(s.conf, []byte(""), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write to nginx config %q: %w", s.conf, err)
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ func (s httpSolver) Present(_ context.Context, challenge acme.Challenge) error {
|
||||
}
|
||||
`, challenge.HTTP01ResourcePath(), challenge.KeyAuthorization)
|
||||
|
||||
file, err := os.OpenFile(s.conf, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
file, err := os.OpenFile(s.conf, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open nginx config %q: %w", s.conf, err)
|
||||
}
|
||||
@@ -204,7 +204,7 @@ func (s httpSolver) CleanUp(_ context.Context, challenge acme.Challenge) error {
|
||||
`, challenge.HTTP01ResourcePath(), challenge.KeyAuthorization)
|
||||
|
||||
newConf := strings.ReplaceAll(string(conf), target, "")
|
||||
if err = os.WriteFile(s.conf, []byte(newConf), 0644); err != nil {
|
||||
if err = os.WriteFile(s.conf, []byte(newConf), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write to nginx config %q: %w", s.conf, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,22 +3,23 @@ package types
|
||||
import "time"
|
||||
|
||||
type CertList struct {
|
||||
ID uint `json:"id"`
|
||||
AccountID uint `json:"account_id"`
|
||||
WebsiteID uint `json:"website_id"`
|
||||
DNSID uint `json:"dns_id"`
|
||||
Type string `json:"type"`
|
||||
Domains []string `json:"domains"`
|
||||
AutoRenew bool `json:"auto_renew"`
|
||||
Cert string `json:"cert"`
|
||||
Key string `json:"key"`
|
||||
CertURL string `json:"cert_url"`
|
||||
Script string `json:"script"`
|
||||
NotBefore time.Time `json:"not_before"`
|
||||
NotAfter time.Time `json:"not_after"`
|
||||
Issuer string `json:"issuer"`
|
||||
OCSPServer []string `json:"ocsp_server"`
|
||||
DNSNames []string `json:"dns_names"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID uint `json:"id"`
|
||||
AccountID uint `json:"account_id"`
|
||||
WebsiteID uint `json:"website_id"`
|
||||
DNSID uint `json:"dns_id"`
|
||||
Type string `json:"type"`
|
||||
Domains []string `json:"domains"`
|
||||
AutoRenewal bool `json:"auto_renewal"`
|
||||
NextRenewal time.Time `json:"next_renewal"`
|
||||
Cert string `json:"cert"`
|
||||
Key string `json:"key"`
|
||||
CertURL string `json:"cert_url"`
|
||||
Script string `json:"script"`
|
||||
NotBefore time.Time `json:"not_before"`
|
||||
NotAfter time.Time `json:"not_after"`
|
||||
Issuer string `json:"issuer"`
|
||||
OCSPServer []string `json:"ocsp_server"`
|
||||
DNSNames []string `json:"dns_names"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func writeProxyFiles(siteDir string, proxies []types.Proxy) error {
|
||||
filePath := filepath.Join(siteDir, fileName)
|
||||
|
||||
content := generateProxyConfig(proxy)
|
||||
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(filePath, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write proxy config: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -355,7 +355,7 @@ func writeBalancerFiles(sharedDir string, upstreams []types.Upstream) error {
|
||||
filePath := filepath.Join(sharedDir, fileName)
|
||||
|
||||
content := generateBalancerConfig(upstream)
|
||||
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(filePath, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write balancer config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func writeRedirectFiles(siteDir string, redirects []types.Redirect) error {
|
||||
filePath := filepath.Join(siteDir, fileName)
|
||||
|
||||
content := generateRedirectConfig(redirect)
|
||||
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(filePath, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write redirect config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ type baseVhost struct {
|
||||
config *Config
|
||||
vhost *VirtualHost
|
||||
configDir string // 配置目录
|
||||
siteName string // 网站名
|
||||
}
|
||||
|
||||
// newBaseVhost 创建基础虚拟主机实例
|
||||
@@ -39,6 +40,7 @@ func newBaseVhost(configDir string) (*baseVhost, error) {
|
||||
|
||||
v := &baseVhost{
|
||||
configDir: configDir,
|
||||
siteName: filepath.Base(filepath.Dir(configDir)),
|
||||
}
|
||||
|
||||
// 加载配置
|
||||
@@ -56,7 +58,8 @@ func newBaseVhost(configDir string) (*baseVhost, error) {
|
||||
|
||||
// 如果没有配置文件,使用默认配置
|
||||
if config == nil {
|
||||
config, err = ParseString(DefaultVhostConf)
|
||||
defaultConf := strings.ReplaceAll(DefaultVhostConf, "/opt/ace/sites/default", fmt.Sprintf("/opt/ace/sites/%s", v.siteName))
|
||||
config, err = ParseString(defaultConf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse default config: %w", err)
|
||||
}
|
||||
@@ -121,7 +124,7 @@ func (v *baseVhost) SetEnable(enable bool) error {
|
||||
// 禁用时,保存当前根目录
|
||||
currentRoot := v.Root()
|
||||
if currentRoot != "" && currentRoot != DisablePagePath {
|
||||
if err := os.WriteFile(filepath.Join(v.configDir, "root.saved"), []byte(currentRoot), 0644); err != nil {
|
||||
if err := os.WriteFile(filepath.Join(v.configDir, "root.saved"), []byte(currentRoot), 0600); err != nil {
|
||||
return fmt.Errorf("failed to save current root: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -329,7 +332,7 @@ func (v *baseVhost) SetErrorLog(errorLog string) error {
|
||||
func (v *baseVhost) Save() error {
|
||||
configFile := filepath.Join(v.configDir, "apache.conf")
|
||||
content := v.config.Export()
|
||||
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(configFile, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to save config file: %w", err)
|
||||
}
|
||||
|
||||
@@ -338,7 +341,8 @@ func (v *baseVhost) Save() error {
|
||||
|
||||
func (v *baseVhost) Reset() error {
|
||||
// 重置配置为默认值
|
||||
config, err := ParseString(DefaultVhostConf)
|
||||
defaultConf := strings.ReplaceAll(DefaultVhostConf, "/opt/ace/sites/default", fmt.Sprintf("/opt/ace/sites/%s", v.siteName))
|
||||
config, err := ParseString(defaultConf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to reset config: %w", err)
|
||||
}
|
||||
@@ -362,7 +366,7 @@ func (v *baseVhost) Config(name string, typ string) string {
|
||||
|
||||
func (v *baseVhost) SetConfig(name string, typ string, content string) error {
|
||||
conf := filepath.Join(v.configDir, typ, name)
|
||||
if err := os.WriteFile(conf, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(conf, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -18,17 +18,9 @@ type Parser struct {
|
||||
cfgPath string // 配置文件路径
|
||||
}
|
||||
|
||||
func NewParser(website ...string) (*Parser, error) {
|
||||
str := DefaultConf
|
||||
cfgPath := ""
|
||||
if len(website) != 0 && website[0] != "" {
|
||||
cfgPath = fmt.Sprintf("/opt/ace/sites/%s/config/nginx.conf", website[0])
|
||||
if cfg, err := os.ReadFile(cfgPath); err == nil {
|
||||
str = string(cfg)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// NewParser 使用网站名创建解析器,将默认配置中的 default 替换为实际网站名
|
||||
func NewParser(siteName string) (*Parser, error) {
|
||||
str := strings.ReplaceAll(DefaultConf, "/opt/ace/sites/default", fmt.Sprintf("/opt/ace/sites/%s", siteName))
|
||||
|
||||
p := parser.NewStringParser(str, parser.WithSkipIncludeParsingErr(), parser.WithSkipValidDirectivesErr())
|
||||
cfg, err := p.Parse()
|
||||
@@ -36,7 +28,7 @@ func NewParser(website ...string) (*Parser, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Parser{cfg: cfg, cfgPath: cfgPath}, nil
|
||||
return &Parser{cfg: cfg, cfgPath: ""}, nil
|
||||
}
|
||||
|
||||
// NewParserFromFile 从指定文件路径创建解析器
|
||||
@@ -201,7 +193,7 @@ func (p *Parser) Dump() string {
|
||||
func (p *Parser) Save() error {
|
||||
p.sortDirectives(p.cfg.Directives, order)
|
||||
content := p.Dump() + "\n"
|
||||
if err := os.WriteFile(p.cfgPath, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(p.cfgPath, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to save config file: %w", err)
|
||||
}
|
||||
|
||||
@@ -214,7 +206,7 @@ func (p *Parser) SetConfigPath(path string) {
|
||||
}
|
||||
|
||||
func (p *Parser) sortDirectives(directives []config.IDirective, orderIndex map[string]int) {
|
||||
slices.SortFunc(directives, func(a config.IDirective, b config.IDirective) int {
|
||||
slices.SortStableFunc(directives, func(a config.IDirective, b config.IDirective) int {
|
||||
// 块指令(如 server、location)应该排在普通指令(如 include)后面
|
||||
aIsBlock := a.GetBlock() != nil && len(a.GetBlock().GetDirectives()) > 0
|
||||
bIsBlock := b.GetBlock() != nil && len(b.GetBlock().GetDirectives()) > 0
|
||||
@@ -226,11 +218,8 @@ func (p *Parser) sortDirectives(directives []config.IDirective, orderIndex map[s
|
||||
return -1 // b 是块指令,排在前面
|
||||
}
|
||||
|
||||
// 同类指令,按 order 排序
|
||||
if orderIndex[a.GetName()] != orderIndex[b.GetName()] {
|
||||
return orderIndex[a.GetName()] - orderIndex[b.GetName()]
|
||||
}
|
||||
return slices.Compare(p.parameters2Slices(a.GetParameters()), p.parameters2Slices(b.GetParameters()))
|
||||
// 同类指令,按 order 排序;相同名称的指令保持原有顺序
|
||||
return orderIndex[a.GetName()] - orderIndex[b.GetName()]
|
||||
})
|
||||
|
||||
for _, directive := range directives {
|
||||
|
||||
@@ -167,7 +167,7 @@ func writeProxyFiles(siteDir string, proxies []types.Proxy) error {
|
||||
filePath := filepath.Join(siteDir, fileName)
|
||||
|
||||
content := generateProxyConfig(proxy)
|
||||
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(filePath, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write proxy config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ func writeRedirectFiles(siteDir string, redirects []types.Redirect) error {
|
||||
filePath := filepath.Join(siteDir, fileName)
|
||||
|
||||
content := generateRedirectConfig(redirect)
|
||||
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(filePath, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write redirect config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ func writeUpstreamFiles(sharedDir string, upstreams []types.Upstream) error {
|
||||
filePath := filepath.Join(sharedDir, fileName)
|
||||
|
||||
content := generateUpstreamConfig(upstream)
|
||||
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(filePath, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write upstream config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ type ProxyVhost struct {
|
||||
type baseVhost struct {
|
||||
parser *Parser
|
||||
configDir string // 配置目录
|
||||
siteName string // 网站名
|
||||
}
|
||||
|
||||
// newBaseVhost 创建基础虚拟主机实例
|
||||
@@ -41,6 +42,7 @@ func newBaseVhost(configDir string) (*baseVhost, error) {
|
||||
|
||||
v := &baseVhost{
|
||||
configDir: configDir,
|
||||
siteName: filepath.Base(filepath.Dir(configDir)),
|
||||
}
|
||||
|
||||
// 加载配置
|
||||
@@ -58,8 +60,7 @@ func newBaseVhost(configDir string) (*baseVhost, error) {
|
||||
|
||||
// 如果没有配置文件,使用默认配置
|
||||
if parser == nil {
|
||||
// 使用空字符串创建默认配置,而不尝试读取文件
|
||||
parser, err = NewParser("")
|
||||
parser, err = NewParser(v.siteName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load default config: %w", err)
|
||||
}
|
||||
@@ -123,7 +124,7 @@ func (v *baseVhost) SetEnable(enable bool) error {
|
||||
// 禁用时,保存当前根目录
|
||||
currentRoot := v.Root()
|
||||
if currentRoot != "" && currentRoot != DisablePagePath {
|
||||
if err := os.WriteFile(filepath.Join(v.configDir, "root.saved"), []byte(currentRoot), 0644); err != nil {
|
||||
if err := os.WriteFile(filepath.Join(v.configDir, "root.saved"), []byte(currentRoot), 0600); err != nil {
|
||||
return fmt.Errorf("failed to save current root: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -160,15 +161,38 @@ func (v *baseVhost) Listen() []types.Listen {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result []types.Listen
|
||||
// 使用 map 合并相同地址的 listen 指令
|
||||
// nginx 中 ssl 和 quic 需要分开写
|
||||
listenMap := make(map[string]types.Listen)
|
||||
var order []string // 保持顺序
|
||||
|
||||
for _, dir := range directives {
|
||||
l := v.parser.parameters2Slices(dir.GetParameters())
|
||||
listen := types.Listen{Address: l[0], Args: []string{}}
|
||||
for i := 1; i < len(l); i++ {
|
||||
listen.Args = append(listen.Args, l[i])
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
address := l[0]
|
||||
|
||||
result = append(result, listen)
|
||||
if existing, ok := listenMap[address]; ok {
|
||||
// 合并 args
|
||||
for i := 1; i < len(l); i++ {
|
||||
if !slices.Contains(existing.Args, l[i]) {
|
||||
existing.Args = append(existing.Args, l[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
listen := types.Listen{Address: address, Args: []string{}}
|
||||
for i := 1; i < len(l); i++ {
|
||||
listen.Args = append(listen.Args, l[i])
|
||||
}
|
||||
listenMap[address] = listen
|
||||
order = append(order, address)
|
||||
}
|
||||
}
|
||||
|
||||
var result []types.Listen
|
||||
for _, addr := range order {
|
||||
result = append(result, listenMap[addr])
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -177,12 +201,39 @@ func (v *baseVhost) Listen() []types.Listen {
|
||||
func (v *baseVhost) SetListen(listens []types.Listen) error {
|
||||
var directives []*config.Directive
|
||||
for _, l := range listens {
|
||||
listen := []string{l.Address}
|
||||
listen = append(listen, l.Args...)
|
||||
directives = append(directives, &config.Directive{
|
||||
Name: "listen",
|
||||
Parameters: v.parser.slices2Parameters(listen),
|
||||
})
|
||||
hasSSL := slices.Contains(l.Args, "ssl")
|
||||
hasQUIC := slices.Contains(l.Args, "quic")
|
||||
|
||||
// nginx 中 ssl 和 quic 不能在同一个 listen 指令中
|
||||
// 需要分成两行:listen 443 ssl; 和 listen 443 quic;
|
||||
if hasSSL && hasQUIC {
|
||||
// 生成 ssl 行(包含除 quic 外的所有参数)
|
||||
sslArgs := []string{l.Address}
|
||||
for _, arg := range l.Args {
|
||||
if arg != "quic" {
|
||||
sslArgs = append(sslArgs, arg)
|
||||
}
|
||||
}
|
||||
directives = append(directives, &config.Directive{
|
||||
Name: "listen",
|
||||
Parameters: v.parser.slices2Parameters(sslArgs),
|
||||
})
|
||||
|
||||
// 生成 quic 行
|
||||
quicArgs := []string{l.Address, "quic"}
|
||||
directives = append(directives, &config.Directive{
|
||||
Name: "listen",
|
||||
Parameters: v.parser.slices2Parameters(quicArgs),
|
||||
})
|
||||
} else {
|
||||
// 普通情况,直接生成一行
|
||||
listen := []string{l.Address}
|
||||
listen = append(listen, l.Args...)
|
||||
directives = append(directives, &config.Directive{
|
||||
Name: "listen",
|
||||
Parameters: v.parser.slices2Parameters(listen),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_ = v.parser.Clear("server.listen")
|
||||
@@ -337,7 +388,7 @@ func (v *baseVhost) Save() error {
|
||||
|
||||
func (v *baseVhost) Reset() error {
|
||||
// 重置配置为默认值
|
||||
parser, err := NewParser("")
|
||||
parser, err := NewParser(v.siteName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to reset config: %w", err)
|
||||
}
|
||||
@@ -362,7 +413,7 @@ func (v *baseVhost) Config(name string, typ string) string {
|
||||
|
||||
func (v *baseVhost) SetConfig(name string, typ string, content string) error {
|
||||
conf := filepath.Join(v.configDir, typ, name)
|
||||
if err := os.WriteFile(conf, []byte(content), 0644); err != nil {
|
||||
if err := os.WriteFile(conf, []byte(content), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -118,6 +118,41 @@ func (s *VhostTestSuite) TestListenWithHTTP3() {
|
||||
s.Equal("quic", got[0].Args[0])
|
||||
}
|
||||
|
||||
func (s *VhostTestSuite) TestListenWithSSLAndQUIC() {
|
||||
// 测试 ssl 和 quic 同时存在时,应该分成两行 listen 指令
|
||||
// 但读取时应该合并为一个 Listen 对象
|
||||
listens := []types.Listen{
|
||||
{Address: "80"},
|
||||
{Address: "443", Args: []string{"ssl", "quic"}},
|
||||
}
|
||||
s.NoError(s.vhost.SetListen(listens))
|
||||
|
||||
// 保存后验证顺序
|
||||
s.NoError(s.vhost.Save())
|
||||
|
||||
// 验证生成的配置中 ssl 和 quic 是分开的
|
||||
dump := s.vhost.parser.Dump()
|
||||
s.Contains(dump, "listen 443 ssl;")
|
||||
s.Contains(dump, "listen 443 quic;")
|
||||
// 确保没有 "listen 443 ssl quic;" 这样的行
|
||||
s.NotContains(dump, "listen 443 ssl quic;")
|
||||
|
||||
// 验证顺序:80 应该在 443 前面
|
||||
idx80 := strings.Index(dump, "listen 80;")
|
||||
idx443 := strings.Index(dump, "listen 443")
|
||||
s.Greater(idx443, idx80, "listen 80 should come before listen 443")
|
||||
|
||||
// 读取时应该合并为一个 Listen 对象
|
||||
got := s.vhost.Listen()
|
||||
s.Len(got, 2) // 80 和 443
|
||||
// 验证顺序
|
||||
s.Equal("80", got[0].Address)
|
||||
s.Equal("443", got[1].Address)
|
||||
// 验证 443 的 args
|
||||
s.Contains(got[1].Args, "ssl")
|
||||
s.Contains(got[1].Args, "quic")
|
||||
}
|
||||
|
||||
func (s *VhostTestSuite) TestSSL() {
|
||||
s.False(s.vhost.SSL())
|
||||
s.Nil(s.vhost.SSLConfig())
|
||||
|
||||
@@ -38,7 +38,7 @@ const updateModel = ref<any>({
|
||||
dns_id: null,
|
||||
account_id: null,
|
||||
website_id: null,
|
||||
auto_renew: true,
|
||||
auto_renewal: true,
|
||||
cert: '',
|
||||
key: '',
|
||||
script: ''
|
||||
@@ -115,7 +115,7 @@ const columns: any = [
|
||||
{
|
||||
title: $gettext('Associated Account'),
|
||||
key: 'account_id',
|
||||
minWidth: 200,
|
||||
minWidth: 240,
|
||||
resizable: true,
|
||||
ellipsis: { tooltip: true },
|
||||
render(row: any) {
|
||||
@@ -128,7 +128,7 @@ const columns: any = [
|
||||
{
|
||||
title: $gettext('Issuer'),
|
||||
key: 'issuer',
|
||||
width: 150,
|
||||
width: 120,
|
||||
ellipsis: { tooltip: true },
|
||||
render(row: any) {
|
||||
return row.issuer == '' ? $gettext('None') : row.issuer
|
||||
@@ -144,35 +144,25 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'OCSP',
|
||||
key: 'ocsp_server',
|
||||
title: $gettext('Next Renewal Time'),
|
||||
key: 'next_renewal',
|
||||
minWidth: 200,
|
||||
resizable: true,
|
||||
render(row: any) {
|
||||
if (row.ocsp_server == null || row.ocsp_server.length == 0) {
|
||||
return h(NTag, null, { default: () => $gettext('None') })
|
||||
}
|
||||
return h(NFlex, null, {
|
||||
default: () =>
|
||||
row.ocsp_server.map((server: any) =>
|
||||
h(NTag, null, {
|
||||
default: () => server
|
||||
})
|
||||
)
|
||||
})
|
||||
return row.next_renewal == 0 ? $gettext('None') : formatDateTime(row.next_renewal)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Auto Renew'),
|
||||
key: 'auto_renew',
|
||||
title: $gettext('Auto Renewal'),
|
||||
key: 'auto_renewal',
|
||||
width: 140,
|
||||
resizable: true,
|
||||
render(row: any) {
|
||||
return h(NSwitch, {
|
||||
size: 'small',
|
||||
rubberBand: false,
|
||||
value: row.auto_renew,
|
||||
onUpdateValue: () => handleAutoRenewUpdate(row)
|
||||
value: row.auto_renewal,
|
||||
onUpdateValue: () => handleAutoRenewalUpdate(row)
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -241,7 +231,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Renew')
|
||||
default: () => $gettext('Renewal')
|
||||
}
|
||||
)
|
||||
: null,
|
||||
@@ -276,7 +266,7 @@ const columns: any = [
|
||||
updateModel.value.dns_id = row.dns_id == 0 ? null : row.dns_id
|
||||
updateModel.value.account_id = row.account_id == 0 ? null : row.account_id
|
||||
updateModel.value.website_id = row.website_id == 0 ? null : row.website_id
|
||||
updateModel.value.auto_renew = row.auto_renew
|
||||
updateModel.value.auto_renewal = row.auto_renewal
|
||||
updateModel.value.cert = row.cert
|
||||
updateModel.value.key = row.key
|
||||
updateModel.value.script = row.script
|
||||
@@ -340,7 +330,7 @@ const handleUpdateCert = () => {
|
||||
updateModel.value.dns_id = null
|
||||
updateModel.value.account_id = null
|
||||
updateModel.value.website_id = null
|
||||
updateModel.value.auto_renew = true
|
||||
updateModel.value.auto_renewal = true
|
||||
updateModel.value.cert = ''
|
||||
updateModel.value.key = ''
|
||||
updateModel.value.script = ''
|
||||
@@ -348,13 +338,13 @@ const handleUpdateCert = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleAutoRenewUpdate = (row: any) => {
|
||||
const handleAutoRenewalUpdate = (row: any) => {
|
||||
updateModel.value.domains = row.domains
|
||||
updateModel.value.type = row.type
|
||||
updateModel.value.dns_id = row.dns_id == 0 ? null : row.dns_id
|
||||
updateModel.value.account_id = row.account_id == 0 ? null : row.account_id
|
||||
updateModel.value.website_id = row.website_id == 0 ? null : row.website_id
|
||||
updateModel.value.auto_renew = !row.auto_renew
|
||||
updateModel.value.auto_renewal = !row.auto_renewal
|
||||
updateModel.value.cert = row.cert
|
||||
updateModel.value.key = row.key
|
||||
updateModel.value.script = row.script
|
||||
@@ -369,7 +359,7 @@ const handleAutoRenewUpdate = (row: any) => {
|
||||
updateModel.value.dns_id = null
|
||||
updateModel.value.account_id = null
|
||||
updateModel.value.website_id = null
|
||||
updateModel.value.auto_renew = true
|
||||
updateModel.value.auto_renewal = true
|
||||
updateModel.value.cert = ''
|
||||
updateModel.value.key = ''
|
||||
updateModel.value.script = ''
|
||||
|
||||
@@ -33,7 +33,7 @@ const model = ref<any>({
|
||||
type: 'P256',
|
||||
account_id: null,
|
||||
website_id: null,
|
||||
auto_renew: true
|
||||
auto_renewal: true
|
||||
})
|
||||
|
||||
const handleCreateCert = () => {
|
||||
@@ -46,7 +46,7 @@ const handleCreateCert = () => {
|
||||
model.value.type = 'P256'
|
||||
model.value.account_id = null
|
||||
model.value.website_id = null
|
||||
model.value.auto_renew = true
|
||||
model.value.auto_renewal = true
|
||||
window.$message.success($gettext('Created successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -95,15 +95,15 @@ const certOptions = computed(() => {
|
||||
const selectedCert = ref(null)
|
||||
|
||||
const handleSave = () => {
|
||||
// 如果没有任何监听地址设置了https,则自动添加443
|
||||
if (setting.value.https && !setting.value.listens.some((item: any) => item.https)) {
|
||||
// 如果开启了ssl但没有任何监听地址设置了ssl,则自动添加443
|
||||
if (setting.value.ssl && !setting.value.listens.some((item: any) => item.args?.includes('ssl'))) {
|
||||
setting.value.listens.push({
|
||||
address: '443',
|
||||
args: ['ssl', 'quic']
|
||||
})
|
||||
}
|
||||
// 如果关闭了https,自动禁用所有https和quic
|
||||
if (!setting.value.https) {
|
||||
// 如果关闭了ssl,自动禁用所有ssl和quic
|
||||
if (!setting.value.ssl) {
|
||||
setting.value.listens = setting.value.listens.filter((item: any) => item.address !== '443') // 443直接删掉
|
||||
setting.value.listens.forEach((item: any) => {
|
||||
item.args = []
|
||||
@@ -304,19 +304,11 @@ const removeProxy = (index: number) => {
|
||||
// 处理 Proxy Pass 变化,自动更新 Host
|
||||
const handleProxyPassChange = (proxy: any, value: string) => {
|
||||
proxy.pass = value
|
||||
// 如果 host 是 $host 或为空,则自动提取
|
||||
if (!proxy.host || proxy.host === '$host') {
|
||||
const extracted = extractHostFromUrl(value)
|
||||
// 只有当提取到的不是 IP 地址时才设置
|
||||
if (
|
||||
extracted &&
|
||||
!/^\d+\.\d+\.\d+\.\d+(:\d+)?$/.test(extracted) &&
|
||||
!/^localhost(:\d+)?$/i.test(extracted)
|
||||
) {
|
||||
proxy.host = extracted
|
||||
} else {
|
||||
proxy.host = '$host'
|
||||
}
|
||||
const extracted = extractHostFromUrl(value)
|
||||
if (extracted !== '') {
|
||||
proxy.host = extracted
|
||||
} else {
|
||||
proxy.host = '$host'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user