From 16f31120f53a18eead0536956b7764129c0260ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Thu, 15 May 2025 04:06:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=AF=81=E4=B9=A6=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/setting.go | 5 +++-- internal/biz/website.go | 1 + internal/data/setting.go | 27 ++++++++++++++++++++--- internal/data/website.go | 38 ++++++++++++++++++++++++++++++++ internal/http/request/setting.go | 9 ++++++-- internal/http/request/website.go | 6 +++++ internal/route/http.go | 2 ++ internal/service/setting.go | 24 +++++++++++++++++--- internal/service/website.go | 16 ++++++++++++++ 9 files changed, 118 insertions(+), 10 deletions(-) diff --git a/internal/biz/setting.go b/internal/biz/setting.go index cbeac4ad..c7a916c5 100644 --- a/internal/biz/setting.go +++ b/internal/biz/setting.go @@ -37,6 +37,7 @@ type SettingRepo interface { Set(key SettingKey, value string) error SetSlice(key SettingKey, value []string) error Delete(key SettingKey) error - GetPanelSetting() (*request.PanelSetting, error) - UpdatePanelSetting(req *request.PanelSetting) (bool, error) + GetPanel() (*request.SettingPanel, error) + UpdatePanel(req *request.SettingPanel) (bool, error) + UpdateCert(req *request.SettingCert) error } diff --git a/internal/biz/website.go b/internal/biz/website.go index c69b089a..8c48f84f 100644 --- a/internal/biz/website.go +++ b/internal/biz/website.go @@ -35,5 +35,6 @@ type WebsiteRepo interface { UpdateRemark(id uint, remark string) error ResetConfig(id uint) error UpdateStatus(id uint, status bool) error + UpdateCert(req *request.WebsiteUpdateCert) error ObtainCert(ctx context.Context, id uint) error } diff --git a/internal/data/setting.go b/internal/data/setting.go index 96cbb03b..4019d765 100644 --- a/internal/data/setting.go +++ b/internal/data/setting.go @@ -197,7 +197,7 @@ func (r *settingRepo) Delete(key biz.SettingKey) error { return nil } -func (r *settingRepo) GetPanelSetting() (*request.PanelSetting, error) { +func (r *settingRepo) GetPanel() (*request.SettingPanel, error) { name, err := r.Get(biz.SettingKeyName) if err != nil { return nil, err @@ -232,7 +232,7 @@ func (r *settingRepo) GetPanelSetting() (*request.PanelSetting, error) { return nil, err } - return &request.PanelSetting{ + return &request.SettingPanel{ Name: name, Channel: channel, Locale: r.conf.String("app.locale"), @@ -252,7 +252,7 @@ func (r *settingRepo) GetPanelSetting() (*request.PanelSetting, error) { }, nil } -func (r *settingRepo) UpdatePanelSetting(req *request.PanelSetting) (bool, error) { +func (r *settingRepo) UpdatePanel(req *request.SettingPanel) (bool, error) { if err := r.Set(biz.SettingKeyName, req.Name); err != nil { return false, err } @@ -350,3 +350,24 @@ func (r *settingRepo) UpdatePanelSetting(req *request.PanelSetting) (bool, error return restartFlag, nil } + +func (r *settingRepo) UpdateCert(req *request.SettingCert) error { + if r.task.HasRunningTask() { + return errors.New(r.t.Get("background task is running, modifying some settings is prohibited, please try again later")) + } + if _, err := cert.ParseCert(req.Cert); err != nil { + return errors.New(r.t.Get("failed to parse certificate: %v", err)) + } + if _, err := cert.ParseKey(req.Key); err != nil { + 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 { + return err + } + if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.key"), req.Key, 0644); err != nil { + return err + } + + return nil +} diff --git a/internal/data/website.go b/internal/data/website.go index 5c892e79..795a7235 100644 --- a/internal/data/website.go +++ b/internal/data/website.go @@ -469,6 +469,12 @@ func (r *websiteRepo) Update(req *request.WebsiteUpdate) error { } website.Https = req.HTTPS if req.HTTPS { + if _, err = cert.ParseCert(req.SSLCertificate); err != nil { + return errors.New(r.t.Get("failed to parse certificate: %v", err)) + } + if _, err = cert.ParseKey(req.SSLCertificateKey); err != nil { + return errors.New(r.t.Get("failed to parse private key: %v", err)) + } if err = p.SetHTTPS(certPath, keyPath); err != nil { return err } @@ -746,6 +752,38 @@ func (r *websiteRepo) UpdateStatus(id uint, status bool) error { return nil } +func (r *websiteRepo) UpdateCert(req *request.WebsiteUpdateCert) error { + website := new(biz.Website) + if err := r.db.Where("name", req.Name).First(&website).Error; err != nil { + return err + } + + if _, err := cert.ParseCert(req.Cert); err != nil { + return errors.New(r.t.Get("failed to parse certificate: %v", err)) + } + if _, err := cert.ParseKey(req.Key); err != nil { + return errors.New(r.t.Get("failed to parse private key: %v", err)) + } + + certPath := filepath.Join(app.Root, "server/vhost/cert", website.Name+".pem") + keyPath := filepath.Join(app.Root, "server/vhost/cert", website.Name+".key") + if err := io.Write(certPath, req.Cert, 0644); err != nil { + return err + } + if err := io.Write(keyPath, req.Key, 0644); err != nil { + return err + } + + if website.Https { + if err := systemctl.Reload("nginx"); err != nil { + _, err = shell.Execf("nginx -t") + return err + } + } + + return nil +} + func (r *websiteRepo) ObtainCert(ctx context.Context, id uint) error { website, err := r.Get(id) if err != nil { diff --git a/internal/http/request/setting.go b/internal/http/request/setting.go index 6ae1b0b4..cf735f6f 100644 --- a/internal/http/request/setting.go +++ b/internal/http/request/setting.go @@ -2,7 +2,7 @@ package request import "net/http" -type PanelSetting struct { +type SettingPanel struct { Name string `json:"name" validate:"required"` Channel string `json:"channel" validate:"required|in:stable,beta"` Locale string `json:"locale" validate:"required"` @@ -22,10 +22,15 @@ type PanelSetting struct { Key string `json:"key" validate:"required"` } -func (r *PanelSetting) Rules(_ *http.Request) map[string]string { +func (r *SettingPanel) Rules(_ *http.Request) map[string]string { return map[string]string{ "BindDomain.*": "required", "BindIP.*": "required|ip", "BindUA.*": "required", } } + +type SettingCert struct { + Cert string `json:"cert" validate:"required"` + Key string `json:"key" validate:"required"` +} diff --git a/internal/http/request/website.go b/internal/http/request/website.go index 7954f657..99caad0c 100644 --- a/internal/http/request/website.go +++ b/internal/http/request/website.go @@ -55,3 +55,9 @@ type WebsiteUpdateStatus struct { ID uint `json:"id" form:"id" validate:"required|exists:websites,id"` Status bool `json:"status" form:"status"` } + +type WebsiteUpdateCert struct { + Name uint `json:"name" validate:"required|exists:websites,name"` + Cert string `json:"cert" validate:"required"` + Key string `json:"key" validate:"required"` +} diff --git a/internal/route/http.go b/internal/route/http.go index e6d73db7..b18fbcbd 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -168,6 +168,7 @@ func (route *Http) Register(r *chi.Mux) { r.Post("/{id}/update_remark", route.website.UpdateRemark) r.Post("/{id}/reset_config", route.website.ResetConfig) r.Post("/{id}/status", route.website.UpdateStatus) + r.Post("/{id}/cert", route.website.UpdateCert) r.Post("/{id}/obtain_cert", route.website.ObtainCert) }) @@ -366,6 +367,7 @@ func (route *Http) Register(r *chi.Mux) { r.Route("/setting", func(r chi.Router) { r.Get("/", route.setting.Get) r.Post("/", route.setting.Update) + r.Post("/cert", route.setting.UpdateCert) }) r.Route("/systemctl", func(r chi.Router) { diff --git a/internal/service/setting.go b/internal/service/setting.go index f6f94e08..62a96495 100644 --- a/internal/service/setting.go +++ b/internal/service/setting.go @@ -19,7 +19,7 @@ func NewSettingService(setting biz.SettingRepo) *SettingService { } func (s *SettingService) Get(w http.ResponseWriter, r *http.Request) { - setting, err := s.settingRepo.GetPanelSetting() + setting, err := s.settingRepo.GetPanel() if err != nil { Error(w, http.StatusInternalServerError, "%v", err) return @@ -29,14 +29,14 @@ func (s *SettingService) Get(w http.ResponseWriter, r *http.Request) { } func (s *SettingService) Update(w http.ResponseWriter, r *http.Request) { - req, err := Bind[request.PanelSetting](r) + req, err := Bind[request.SettingPanel](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) return } restart := false - if restart, err = s.settingRepo.UpdatePanelSetting(req); err != nil { + if restart, err = s.settingRepo.UpdatePanel(req); err != nil { Error(w, http.StatusInternalServerError, "%v", err) return } @@ -47,3 +47,21 @@ func (s *SettingService) Update(w http.ResponseWriter, r *http.Request) { Success(w, nil) } + +// UpdateCert 用于自动化工具更新证书 +func (s *SettingService) UpdateCert(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.SettingCert](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + if err = s.settingRepo.UpdateCert(req); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + tools.RestartPanel() + + Success(w, nil) +} diff --git a/internal/service/website.go b/internal/service/website.go index 59a4fce9..2f2daa37 100644 --- a/internal/service/website.go +++ b/internal/service/website.go @@ -212,6 +212,22 @@ func (s *WebsiteService) UpdateStatus(w http.ResponseWriter, r *http.Request) { Success(w, nil) } +// UpdateCert 用于自动化工具更新证书 +func (s *WebsiteService) UpdateCert(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.WebsiteUpdateCert](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + if err = s.websiteRepo.UpdateCert(req); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) +} + func (s *WebsiteService) ObtainCert(w http.ResponseWriter, r *http.Request) { req, err := Bind[request.ID](r) if err != nil {