From 2d525e9680bcd34ee769ffb199049854dbcb5265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Thu, 8 Jan 2026 21:26:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E8=AF=81=E4=B9=A6?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/ace/wire_gen.go | 2 +- internal/biz/setting.go | 2 +- internal/data/setting.go | 18 +++++++ internal/http/request/setting.go | 2 + internal/job/cert_renew.go | 29 +++++++----- internal/service/cli.go | 26 +++++++---- internal/service/setting.go | 67 +++++++++++++++++++++++++-- web/src/views/setting/IndexView.vue | 20 ++++++-- web/src/views/setting/SettingBase.vue | 7 --- web/src/views/setting/SettingSafe.vue | 54 +++++++++++++++++---- 10 files changed, 183 insertions(+), 44 deletions(-) diff --git a/cmd/ace/wire_gen.go b/cmd/ace/wire_gen.go index 634c61b1..f4297b79 100644 --- a/cmd/ace/wire_gen.go +++ b/cmd/ace/wire_gen.go @@ -114,7 +114,7 @@ func initWeb() (*app.Web, error) { fileService := service.NewFileService(locale, taskRepo) monitorRepo := data.NewMonitorRepo(db, settingRepo) monitorService := service.NewMonitorService(settingRepo, monitorRepo) - settingService := service.NewSettingService(settingRepo) + settingService := service.NewSettingService(locale, db, settingRepo, certRepo, certAccountRepo) systemctlService := service.NewSystemctlService(locale) toolboxSystemService := service.NewToolboxSystemService(locale) toolboxBenchmarkService := service.NewToolboxBenchmarkService(locale) diff --git a/internal/biz/setting.go b/internal/biz/setting.go index 700e544a..bb2cf2c1 100644 --- a/internal/biz/setting.go +++ b/internal/biz/setting.go @@ -9,7 +9,6 @@ import ( type SettingKey string const ( - SettingKeyIP SettingKey = "ip" SettingKeyName SettingKey = "name" SettingKeyVersion SettingKey = "version" SettingKeyChannel SettingKey = "channel" @@ -21,6 +20,7 @@ const ( SettingKeyOfflineMode SettingKey = "offline_mode" SettingKeyAutoUpdate SettingKey = "auto_update" SettingKeyWebserver SettingKey = "webserver" + SettingKeyPublicIPs SettingKey = "public_ips" SettingHiddenMenu SettingKey = "hidden_menu" SettingKeyCustomLogo SettingKey = "custom_logo" ) diff --git a/internal/data/setting.go b/internal/data/setting.go index 6cb14132..ee9bb695 100644 --- a/internal/data/setting.go +++ b/internal/data/setting.go @@ -221,6 +221,14 @@ func (r *settingRepo) GetPanel() (*request.SettingPanel, error) { if err != nil { return nil, err } + ip, err := r.Get(biz.SettingKeyPublicIPs) + if err != nil { + return nil, err + } + publicIP := make([]string, 0) + if err = json.Unmarshal([]byte(ip), &publicIP); err != nil { + return nil, err + } crt, err := io.Read(filepath.Join(app.Root, "panel/storage/cert.pem")) if err != nil { @@ -247,6 +255,8 @@ func (r *settingRepo) GetPanel() (*request.SettingPanel, error) { BackupPath: backupPath, Port: r.conf.HTTP.Port, HTTPS: r.conf.HTTP.TLS, + ACME: r.conf.HTTP.ACME, + PublicIP: publicIP, Cert: crt, Key: key, }, nil @@ -271,6 +281,13 @@ func (r *settingRepo) UpdatePanel(req *request.SettingPanel) (bool, error) { if err := r.Set(biz.SettingKeyBackupPath, req.BackupPath); err != nil { return false, err } + publicIPBytes, err := json.Marshal(req.PublicIP) + if err != nil { + return false, err + } + if err = r.Set(biz.SettingKeyPublicIPs, string(publicIPBytes)); err != nil { + return false, err + } // 下面是需要需要重启的设置 // 面板HTTPS @@ -326,6 +343,7 @@ func (r *settingRepo) UpdatePanel(req *request.SettingPanel) (bool, error) { conf.HTTP.Port = req.Port conf.HTTP.Entrance = req.Entrance conf.HTTP.TLS = req.HTTPS + conf.HTTP.ACME = req.ACME conf.HTTP.IPHeader = req.IPHeader conf.HTTP.BindDomain = req.BindDomain conf.HTTP.BindIP = req.BindIP diff --git a/internal/http/request/setting.go b/internal/http/request/setting.go index 5f5a7155..87324c3a 100644 --- a/internal/http/request/setting.go +++ b/internal/http/request/setting.go @@ -19,6 +19,8 @@ type SettingPanel struct { BackupPath string `json:"backup_path" validate:"required"` Port uint `json:"port" validate:"required|min:1|max:65535"` HTTPS bool `json:"https"` + ACME bool `json:"acme"` + PublicIP []string `json:"public_ip"` Cert string `json:"cert" validate:"required"` Key string `json:"key" validate:"required"` } diff --git a/internal/job/cert_renew.go b/internal/job/cert_renew.go index 9c41982d..6fa8b2e7 100644 --- a/internal/job/cert_renew.go +++ b/internal/job/cert_renew.go @@ -1,13 +1,14 @@ package job import ( + "encoding/json" "log/slog" "path/filepath" "time" + "github.com/acepanel/panel/internal/http/request" "github.com/acepanel/panel/pkg/config" - "github.com/acepanel/panel/pkg/io" - "github.com/acepanel/panel/pkg/systemctl" + "github.com/acepanel/panel/pkg/tools" "gorm.io/gorm" "github.com/acepanel/panel/internal/app" @@ -80,11 +81,16 @@ func (r *CertRenew) Run() { return } - ip, err := r.settingRepo.Get(biz.SettingKeyIP) - if err != nil || ip == "" { + ip, err := r.settingRepo.Get(biz.SettingKeyPublicIPs) + if err != nil { r.log.Warn("[CertRenew] failed to get panel IP", slog.Any("err", err)) return } + var ips []string + if err = json.Unmarshal([]byte(ip), &ips); err != nil || len(ips) == 0 { + r.log.Warn("[CertRenew] panel public IPs not set", slog.Any("err", err)) + return + } var user biz.User if err = r.db.First(&user).Error; err != nil { @@ -96,23 +102,22 @@ func (r *CertRenew) Run() { r.log.Warn("[CertRenew] failed to get panel ACME account", slog.Any("err", err)) return } - crt, key, err := r.certRepo.ObtainPanel(account, []string{ip}) + crt, key, err := r.certRepo.ObtainPanel(account, ips) if err != nil { r.log.Warn("[CertRenew] failed to obtain ACME cert", slog.Any("err", err)) return } - if err = io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), string(crt), 0644); err != nil { - r.log.Warn("[CertRenew] failed to write panel cert", slog.Any("err", err)) - return - } - if err = io.Write(filepath.Join(app.Root, "panel/storage/cert.key"), string(key), 0644); err != nil { - r.log.Warn("[CertRenew] failed to write panel cert key", slog.Any("err", err)) + if err = r.settingRepo.UpdateCert(&request.SettingCert{ + Cert: string(crt), + Key: string(key), + }); err != nil { + r.log.Warn("[CertRenew] failed to update panel cert", slog.Any("err", err)) return } r.log.Info("[CertRenew] panel cert renewed successfully") - _ = systemctl.Restart("panel") + tools.RestartPanel() } } diff --git a/internal/service/cli.go b/internal/service/cli.go index dfe83963..612e9fa5 100644 --- a/internal/service/cli.go +++ b/internal/service/cli.go @@ -3,6 +3,7 @@ package service import ( "bufio" "context" + "encoding/json" "errors" "fmt" "math/rand/v2" @@ -361,9 +362,13 @@ func (s *CliService) HTTPSGenerate(ctx context.Context, cmd *cli.Command) error } if s.conf.HTTP.ACME { - ip, err := s.settingRepo.Get(biz.SettingKeyIP) - if err != nil || ip == "" { - return errors.New(s.t.Get("Please set the panel IP in settings first for ACME certificate generation: %v", err)) + ip, err := s.settingRepo.Get(biz.SettingKeyPublicIPs) + if err != nil { + return err + } + var ips []string + if err = json.Unmarshal([]byte(ip), &ips); err != nil || len(ips) == 0 { + return errors.New(s.t.Get("Please set the panel IP in settings first for ACME certificate generation")) } var user biz.User @@ -374,10 +379,11 @@ func (s *CliService) HTTPSGenerate(ctx context.Context, cmd *cli.Command) error if err != nil { return errors.New(s.t.Get("Failed to get ACME account: %v", err)) } - crt, key, err = s.certRepo.ObtainPanel(account, []string{ip}) + crt, key, err = s.certRepo.ObtainPanel(account, ips) if err != nil { return errors.New(s.t.Get("Failed to obtain ACME certificate: %v", err)) } + 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 { @@ -889,21 +895,25 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error { return errors.New(s.t.Get("Already initialized")) } - ip := "" + ips := make([]string, 0) acme := false rv6, err := tools.GetPublicIPv6() if err == nil { - ip = rv6 + ips = append(ips, rv6) acme = true } rv4, err := tools.GetPublicIPv4() if err == nil { - ip = rv4 + ips = append(ips, rv4) acme = true } + ip, err := json.Marshal(ips) + if err != nil { + ip = []byte("[]") + } settings := []biz.Setting{ - {Key: biz.SettingKeyIP, Value: ip}, + {Key: biz.SettingKeyPublicIPs, Value: string(ip)}, {Key: biz.SettingKeyName, Value: "AcePanel"}, {Key: biz.SettingKeyChannel, Value: "stable"}, {Key: biz.SettingKeyVersion, Value: app.Version}, diff --git a/internal/service/setting.go b/internal/service/setting.go index 184a2312..8c03f73b 100644 --- a/internal/service/setting.go +++ b/internal/service/setting.go @@ -1,20 +1,33 @@ package service import ( + "encoding/json" "net/http" + "github.com/leonelquinteros/gotext" + "github.com/libtnb/chix" + "gorm.io/gorm" + "github.com/acepanel/panel/internal/biz" "github.com/acepanel/panel/internal/http/request" "github.com/acepanel/panel/pkg/tools" ) type SettingService struct { - settingRepo biz.SettingRepo + t *gotext.Locale + db *gorm.DB + settingRepo biz.SettingRepo + certRepo biz.CertRepo + certAccountRepo biz.CertAccountRepo } -func NewSettingService(setting biz.SettingRepo) *SettingService { +func NewSettingService(t *gotext.Locale, db *gorm.DB, setting biz.SettingRepo, cert biz.CertRepo, certAccount biz.CertAccountRepo) *SettingService { return &SettingService{ - settingRepo: setting, + t: t, + db: db, + settingRepo: setting, + certRepo: cert, + certAccountRepo: certAccount, } } @@ -45,6 +58,54 @@ func (s *SettingService) Update(w http.ResponseWriter, r *http.Request) { tools.RestartPanel() } + Success(w, chix.M{ + "restart": restart, + }) +} + +func (s *SettingService) ObtainCert(w http.ResponseWriter, r *http.Request) { + ip, err := s.settingRepo.Get(biz.SettingKeyPublicIPs) + if err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + var ips []string + if err = json.Unmarshal([]byte(ip), &ips); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + if len(ips) == 0 { + Error(w, http.StatusBadRequest, s.t.Get("please set public ips first")) + return + } + + var user biz.User + if err = s.db.First(&user).Error; err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + account, err := s.certAccountRepo.GetDefault(user.ID) + if err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + crt, key, err := s.certRepo.ObtainPanel(account, ips) + if err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + if err = s.settingRepo.UpdateCert(&request.SettingCert{ + Cert: string(crt), + Key: string(key), + }); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + tools.RestartPanel() + Success(w, nil) } diff --git a/web/src/views/setting/IndexView.vue b/web/src/views/setting/IndexView.vue index 7308e514..3f3c7543 100644 --- a/web/src/views/setting/IndexView.vue +++ b/web/src/views/setting/IndexView.vue @@ -36,6 +36,7 @@ const { data: model } = useRequest(setting.list, { backup_path: '', https: false, acme: false, + public_ip: [], cert: '', key: '' } @@ -45,14 +46,25 @@ const handleSave = () => { if (model.value.entrance.trim() === '') { model.value.entrance = '/' } - useRequest(setting.update(model.value)).onSuccess(() => { + useRequest(setting.update(model.value)).onSuccess(({ data }) => { window.$message.success($gettext('Saved successfully')) + + // 更新语言设置 if (model.value.locale !== themeStore.locale) { themeStore.setLocale(model.value.locale) - window.$message.info($gettext('Panel is restarting, page will refresh in 3 seconds')) + } + + // 如果需要重启,则自动刷新页面 + if (data.restart) { + window.$message.info($gettext('Panel is restarting, page will refresh in 5 seconds')) setTimeout(() => { - window.location.reload() - }, 3000) + const protocol = model.value.https ? 'https:' : 'http:' + const hostname = window.location.hostname + const port = model.value.port + const entrance = model.value.entrance || '/' + // 构建新的 URL + window.location.href = `${protocol}//${hostname}:${port}${entrance}` + }, 5000) } }) } diff --git a/web/src/views/setting/SettingBase.vue b/web/src/views/setting/SettingBase.vue index a4ed57ce..8372d1cb 100644 --- a/web/src/views/setting/SettingBase.vue +++ b/web/src/views/setting/SettingBase.vue @@ -29,13 +29,6 @@ const channels = [ - + + + - + - + - + - +