diff --git a/cmd/ace/wire_gen.go b/cmd/ace/wire_gen.go index 8de64138..634c61b1 100644 --- a/cmd/ace/wire_gen.go +++ b/cmd/ace/wire_gen.go @@ -151,7 +151,7 @@ func initWeb() (*app.Web, error) { return nil, err } gormigrate := bootstrap.NewMigrate(db) - jobs := job.NewJobs(db, logger, settingRepo, certRepo, backupRepo, cacheRepo, taskRepo) + jobs := job.NewJobs(config, db, logger, settingRepo, certRepo, certAccountRepo, backupRepo, cacheRepo, taskRepo) cron, err := bootstrap.NewCron(config, logger, jobs) if err != nil { return nil, err diff --git a/cmd/cli/wire_gen.go b/cmd/cli/wire_gen.go index 0c012b2f..38e2c589 100644 --- a/cmd/cli/wire_gen.go +++ b/cmd/cli/wire_gen.go @@ -68,7 +68,7 @@ func initCli() (*app.Cli, error) { certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger) websiteRepo := data.NewWebsiteRepo(locale, db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo, settingRepo) backupRepo := data.NewBackupRepo(locale, db, settingRepo, websiteRepo) - cliService := service.NewCliService(locale, config, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo) + cliService := service.NewCliService(locale, config, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo, certRepo, certAccountRepo) cli := route.NewCli(locale, cliService) command := bootstrap.NewCli(locale, cli) gormigrate := bootstrap.NewMigrate(db) diff --git a/internal/biz/cert.go b/internal/biz/cert.go index 94239490..841d8395 100644 --- a/internal/biz/cert.go +++ b/internal/biz/cert.go @@ -38,6 +38,7 @@ type CertRepo interface { Delete(id uint) error ObtainAuto(id uint) (*acme.Certificate, error) ObtainManual(id uint) (*acme.Certificate, error) + ObtainPanel(account *CertAccount, ips []string) ([]byte, []byte, error) ObtainSelfSigned(id uint) error Renew(id uint) (*acme.Certificate, error) ManualDNS(id uint) ([]acme.DNSRecord, error) diff --git a/internal/biz/setting.go b/internal/biz/setting.go index 85b4cd82..700e544a 100644 --- a/internal/biz/setting.go +++ b/internal/biz/setting.go @@ -9,6 +9,7 @@ import ( type SettingKey string const ( + SettingKeyIP SettingKey = "ip" SettingKeyName SettingKey = "name" SettingKeyVersion SettingKey = "version" SettingKeyChannel SettingKey = "channel" diff --git a/internal/data/cert.go b/internal/data/cert.go index ab45e3b5..83b7f273 100644 --- a/internal/data/cert.go +++ b/internal/data/cert.go @@ -6,6 +6,7 @@ import ( "fmt" "log/slog" "os" + "path/filepath" "slices" "strings" "time" @@ -233,6 +234,21 @@ func (r *certRepo) ObtainManual(id uint) (*acme.Certificate, error) { return &ssl, nil } +func (r *certRepo) ObtainPanel(account *biz.CertAccount, ips []string) ([]byte, []byte, error) { + client, err := acme.NewPrivateKeyAccount(account.Email, account.PrivateKey, acme.CALetsEncrypt, nil, r.log) + if err != nil { + return nil, nil, err + } + client.UsePanel(ips, filepath.Join(app.Root, "server/nginx/conf/acme.conf")) + + ssl, err := client.ObtainCertificate(context.Background(), ips, acme.KeyEC256) + if err != nil { + return nil, nil, err + } + + return ssl.ChainPEM, ssl.PrivateKey, nil +} + func (r *certRepo) ObtainSelfSigned(id uint) error { cert, err := r.Get(id) if err != nil { diff --git a/internal/job/cert_renew.go b/internal/job/cert_renew.go index c2c84dbf..9c41982d 100644 --- a/internal/job/cert_renew.go +++ b/internal/job/cert_renew.go @@ -2,8 +2,12 @@ package job import ( "log/slog" + "path/filepath" "time" + "github.com/acepanel/panel/pkg/config" + "github.com/acepanel/panel/pkg/io" + "github.com/acepanel/panel/pkg/systemctl" "gorm.io/gorm" "github.com/acepanel/panel/internal/app" @@ -13,16 +17,22 @@ import ( // CertRenew 证书续签 type CertRenew struct { - db *gorm.DB - log *slog.Logger - certRepo biz.CertRepo + conf *config.Config + db *gorm.DB + log *slog.Logger + settingRepo biz.SettingRepo + certRepo biz.CertRepo + certAccountRepo biz.CertAccountRepo } -func NewCertRenew(db *gorm.DB, log *slog.Logger, cert biz.CertRepo) *CertRenew { +func NewCertRenew(conf *config.Config, db *gorm.DB, log *slog.Logger, setting biz.SettingRepo, cert biz.CertRepo, certAccount biz.CertAccountRepo) *CertRenew { return &CertRenew{ - db: db, - log: log, - certRepo: cert, + conf: conf, + db: db, + log: log, + settingRepo: setting, + certRepo: cert, + certAccountRepo: certAccount, } } @@ -57,4 +67,52 @@ func (r *CertRenew) Run() { r.log.Warn("[CertRenew] failed to renew cert", slog.Any("err", err)) } } + + // 面板证书续签 + if r.conf.HTTP.ACME { + decode, err := pkgcert.ParseCert(filepath.Join(app.Root, "panel/storage/cert.pem")) + if err != nil { + r.log.Warn("[CertRenew] failed to parse panel cert", slog.Any("err", err)) + return + } + // 结束时间大于 2 天不续签 + if time.Until(decode.NotAfter) > 24*2*time.Hour { + return + } + + ip, err := r.settingRepo.Get(biz.SettingKeyIP) + if err != nil || ip == "" { + r.log.Warn("[CertRenew] failed to get panel IP", slog.Any("err", err)) + return + } + + var user biz.User + if err = r.db.First(&user).Error; err != nil { + r.log.Warn("[CertRenew] failed to get a panel user", slog.Any("err", err)) + return + } + account, err := r.certAccountRepo.GetDefault(user.ID) + if err != nil { + r.log.Warn("[CertRenew] failed to get panel ACME account", slog.Any("err", err)) + return + } + crt, key, err := r.certRepo.ObtainPanel(account, []string{ip}) + 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)) + return + } + + r.log.Info("[CertRenew] panel cert renewed successfully") + _ = systemctl.Restart("panel") + } + } diff --git a/internal/job/job.go b/internal/job/job.go index ce7bfba9..99e1e5d6 100644 --- a/internal/job/job.go +++ b/internal/job/job.go @@ -3,6 +3,7 @@ package job import ( "log/slog" + "github.com/acepanel/panel/pkg/config" "github.com/google/wire" "github.com/robfig/cron/v3" "gorm.io/gorm" @@ -13,24 +14,28 @@ import ( var ProviderSet = wire.NewSet(NewJobs) type Jobs struct { - db *gorm.DB - log *slog.Logger - setting biz.SettingRepo - cert biz.CertRepo - backup biz.BackupRepo - cache biz.CacheRepo - task biz.TaskRepo + conf *config.Config + db *gorm.DB + log *slog.Logger + setting biz.SettingRepo + cert biz.CertRepo + certAccount biz.CertAccountRepo + backup biz.BackupRepo + cache biz.CacheRepo + task biz.TaskRepo } -func NewJobs(db *gorm.DB, log *slog.Logger, setting biz.SettingRepo, cert biz.CertRepo, backup biz.BackupRepo, cache biz.CacheRepo, task biz.TaskRepo) *Jobs { +func NewJobs(conf *config.Config, db *gorm.DB, log *slog.Logger, setting biz.SettingRepo, cert biz.CertRepo, certAccount biz.CertAccountRepo, backup biz.BackupRepo, cache biz.CacheRepo, task biz.TaskRepo) *Jobs { return &Jobs{ - db: db, - log: log, - setting: setting, - cert: cert, - backup: backup, - cache: cache, - task: task, + conf: conf, + db: db, + log: log, + setting: setting, + cert: cert, + certAccount: certAccount, + backup: backup, + cache: cache, + task: task, } } @@ -38,10 +43,9 @@ func (r *Jobs) Register(c *cron.Cron) error { if _, err := c.AddJob("* * * * *", NewMonitoring(r.db, r.log, r.setting)); err != nil { return err } - if _, err := c.AddJob("0 4 * * *", NewCertRenew(r.db, r.log, r.cert)); err != nil { + if _, err := c.AddJob("0 4 * * *", NewCertRenew(r.conf, r.db, r.log, r.setting, r.cert, r.certAccount)); err != nil { return err } - if _, err := c.AddJob("0 2 * * *", NewPanelTask(r.db, r.log, r.backup, r.cache, r.task, r.setting)); err != nil { return err } diff --git a/internal/service/cli.go b/internal/service/cli.go index ef8a9862..2653a1aa 100644 --- a/internal/service/cli.go +++ b/internal/service/cli.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/acepanel/panel/pkg/cert" "github.com/leonelquinteros/gotext" "github.com/libtnb/utils/collect" "github.com/libtnb/utils/hash" @@ -23,7 +24,6 @@ import ( "github.com/acepanel/panel/internal/biz" "github.com/acepanel/panel/internal/http/request" "github.com/acepanel/panel/pkg/api" - "github.com/acepanel/panel/pkg/cert" "github.com/acepanel/panel/pkg/config" "github.com/acepanel/panel/pkg/firewall" "github.com/acepanel/panel/pkg/io" @@ -46,10 +46,12 @@ type CliService struct { backupRepo biz.BackupRepo websiteRepo biz.WebsiteRepo databaseServerRepo biz.DatabaseServerRepo + certRepo biz.CertRepo + certAccountRepo biz.CertAccountRepo hash hash.Hasher } -func NewCliService(t *gotext.Locale, conf *config.Config, db *gorm.DB, appRepo biz.AppRepo, cache biz.CacheRepo, user biz.UserRepo, setting biz.SettingRepo, backup biz.BackupRepo, website biz.WebsiteRepo, databaseServer biz.DatabaseServerRepo) *CliService { +func NewCliService(t *gotext.Locale, conf *config.Config, db *gorm.DB, appRepo biz.AppRepo, cache biz.CacheRepo, user biz.UserRepo, setting biz.SettingRepo, backup biz.BackupRepo, website biz.WebsiteRepo, databaseServer biz.DatabaseServerRepo, cert biz.CertRepo, certAccount biz.CertAccountRepo) *CliService { return &CliService{ hr: `+----------------------------------------------------`, api: api.NewAPI(app.Version, app.Locale), @@ -63,6 +65,8 @@ func NewCliService(t *gotext.Locale, conf *config.Config, db *gorm.DB, appRepo b backupRepo: backup, websiteRepo: website, databaseServerRepo: databaseServer, + certRepo: cert, + certAccountRepo: certAccount, hash: hash.NewArgon2id(), } } @@ -218,9 +222,8 @@ func (s *CliService) UserName(ctx context.Context, cmd *cli.Command) error { if err := s.db.Where("username", oldUsername).First(user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.New(s.t.Get("User not exists")) - } else { - return errors.New(s.t.Get("Failed to get user: %v", err)) } + return errors.New(s.t.Get("Failed to get user: %v", err)) } user.Username = newUsername @@ -246,9 +249,8 @@ func (s *CliService) UserPassword(ctx context.Context, cmd *cli.Command) error { if err := s.db.Where("username", username).First(user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.New(s.t.Get("User not exists")) - } else { - return errors.New(s.t.Get("Failed to get user: %v", err)) } + return errors.New(s.t.Get("Failed to get user: %v", err)) } hashed, err := s.hash.Make(password) @@ -274,9 +276,8 @@ func (s *CliService) UserTwoFA(ctx context.Context, cmd *cli.Command) error { if err := s.db.Where("username", username).First(user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.New(s.t.Get("User not exists")) - } else { - return errors.New(s.t.Get("Failed to get user: %v", err)) } + return errors.New(s.t.Get("Failed to get user: %v", err)) } // 已开启,关闭2FA @@ -359,6 +360,26 @@ func (s *CliService) HTTPSGenerate(ctx context.Context, cmd *cli.Command) error return err } + 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)) + } + + var user biz.User + if err := s.db.First(&user).Error; err != nil { + return errors.New(s.t.Get("Failed to get a panel user: %v", err)) + } + account, err := s.certAccountRepo.GetDefault(user.ID) + if err != nil { + return errors.New(s.t.Get("Failed to get ACME account: %v", err)) + } + crt, key, err = s.certRepo.ObtainPanel(account, names) + if err != nil { + return errors.New(s.t.Get("Failed to obtain ACME certificate: %v", err)) + } + } + if err = io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), string(crt), 0644); err != nil { return err } @@ -558,7 +579,7 @@ func (s *CliService) DatabaseAddServer(ctx context.Context, cmd *cli.Command) er Type: cmd.String("type"), Name: cmd.String("name"), Host: cmd.String("host"), - Port: uint(cmd.Uint("port")), + Port: cmd.Uint("port"), Username: cmd.String("username"), Password: cmd.String("password"), Remark: cmd.String("remark"), @@ -646,7 +667,7 @@ func (s *CliService) BackupClear(ctx context.Context, cmd *cli.Command) error { fmt.Println(s.t.Get("|-Cleaning type: %s", cmd.String("type"))) fmt.Println(s.t.Get("|-Cleaning target: %s", cmd.String("file"))) fmt.Println(s.t.Get("|-Keep count: %d", cmd.Int("save"))) - if err = s.backupRepo.ClearExpired(path, cmd.String("file"), int(cmd.Int("save"))); err != nil { + if err = s.backupRepo.ClearExpired(path, cmd.String("file"), cmd.Int("save")); err != nil { return errors.New(s.t.Get("Cleaning failed: %v", err)) } fmt.Println(s.hr) @@ -694,7 +715,7 @@ func (s *CliService) CutoffClear(ctx context.Context, cmd *cli.Command) error { fmt.Println(s.t.Get("|-Cleaning type: %s", cmd.String("type"))) fmt.Println(s.t.Get("|-Cleaning target: %s", cmd.String("file"))) fmt.Println(s.t.Get("|-Keep count: %d", cmd.Int("save"))) - if err := s.backupRepo.ClearExpired(path, cmd.String("file"), int(cmd.Int("save"))); err != nil { + if err := s.backupRepo.ClearExpired(path, cmd.String("file"), cmd.Int("save")); err != nil { return err } fmt.Println(s.hr) @@ -868,19 +889,33 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error { return errors.New(s.t.Get("Already initialized")) } + ip := "" + acme := false + rv6, err := tools.GetPublicIPv6() + if err == nil { + ip = rv6 + acme = true + } + rv4, err := tools.GetPublicIPv4() + if err == nil { + ip = rv4 + acme = true + } + settings := []biz.Setting{ + {Key: biz.SettingKeyIP, Value: ip}, {Key: biz.SettingKeyName, Value: "AcePanel"}, {Key: biz.SettingKeyChannel, Value: "stable"}, {Key: biz.SettingKeyVersion, Value: app.Version}, {Key: biz.SettingKeyMonitor, Value: "true"}, {Key: biz.SettingKeyMonitorDays, Value: "30"}, {Key: biz.SettingKeyBackupPath, Value: filepath.Join(app.Root, "backup")}, - {Key: biz.SettingKeyWebsitePath, Value: filepath.Join(app.Root, "wwwroot")}, + {Key: biz.SettingKeyWebsitePath, Value: filepath.Join(app.Root, "sites")}, {Key: biz.SettingKeyOfflineMode, Value: "false"}, {Key: biz.SettingKeyAutoUpdate, Value: "true"}, {Key: biz.SettingHiddenMenu, Value: "[]"}, } - if err := s.db.Create(&settings).Error; err != nil { + if err = s.db.Create(&settings).Error; err != nil { return errors.New(s.t.Get("Initialization failed: %v", err)) } @@ -894,10 +929,6 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error { return errors.New(s.t.Get("Initialization failed: %v", err)) } - if err = s.HTTPSGenerate(ctx, cmd); err != nil { - return errors.New(s.t.Get("Initialization failed: %v", err)) - } - conf, err := config.Load() if err != nil { return err @@ -907,6 +938,7 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error { conf.App.APIEndpoint = "api.acepanel.net" conf.App.DownloadEndpoint = "dl.acepanel.net" conf.HTTP.Entrance = "/" + str.Random(6) + conf.HTTP.ACME = acme // 随机默认端口 checkPort: @@ -930,5 +962,11 @@ checkPort: return err } + s.conf = conf // 更新配置,否则后续签发证书不会使用ACME + + if err = s.HTTPSGenerate(ctx, cmd); err != nil { + return errors.New(s.t.Get("Initialization failed: %v", err)) + } + return nil } diff --git a/pkg/acme/client.go b/pkg/acme/client.go index 7ad07836..8211f8f3 100644 --- a/pkg/acme/client.go +++ b/pkg/acme/client.go @@ -52,7 +52,6 @@ func (c *Client) UseManualDns(check ...bool) { // UseHTTP 使用 HTTP 验证 // conf nginx 配置文件路径 -// path 验证文件存放路径 func (c *Client) UseHTTP(conf string) { c.zClient.ChallengeSolvers = map[string]acmez.Solver{ acme.ChallengeTypeHTTP01: httpSolver{ @@ -61,6 +60,18 @@ func (c *Client) UseHTTP(conf string) { } } +// UsePanel 使用面板 HTTP 验证 +// ip 外网访问 IP 地址 +// conf nginx 配置文件路径 +func (c *Client) UsePanel(ip []string, conf string) { + c.zClient.ChallengeSolvers = map[string]acmez.Solver{ + acme.ChallengeTypeHTTP01: &panelSolver{ + ip: ip, + conf: conf, + }, + } +} + // ObtainCertificate 签发 SSL 证书 func (c *Client) ObtainCertificate(ctx context.Context, domains []string, keyType KeyType) (Certificate, error) { certPrivateKey, err := generatePrivateKey(keyType) diff --git a/pkg/acme/solvers.go b/pkg/acme/solvers.go index c5a956e9..87c79013 100644 --- a/pkg/acme/solvers.go +++ b/pkg/acme/solvers.go @@ -2,7 +2,9 @@ package acme import ( "context" + "errors" "fmt" + "net/http" "os" "strings" "time" @@ -20,10 +22,106 @@ import ( "github.com/mholt/acmez/v3/acme" "golang.org/x/net/publicsuffix" + pkgos "github.com/acepanel/panel/pkg/os" "github.com/acepanel/panel/pkg/shell" "github.com/acepanel/panel/pkg/systemctl" ) +type panelSolver struct { + ip []string + conf string + server *http.Server +} + +func (s *panelSolver) Present(_ context.Context, challenge acme.Challenge) error { + // 如果 80 端口没有被占用,则直接起一个内置的 HTTP 服务器验证 + if !pkgos.TCPPortInUse(80) { + return s.presentPanel(challenge) + } + + conf := fmt.Sprintf(`server { + listen 80; + server_name %s; + location = %s { + default_type text/plain; + return 200 %q; + } +} +`, s.ip, challenge.HTTP01ResourcePath(), challenge.KeyAuthorization) + + f, err := os.OpenFile(s.conf, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("failed to open nginx config %q: %w", s.conf, err) + } + defer func(f *os.File) { _ = f.Close() }(f) + + if _, err = f.Write([]byte(conf)); err != nil { + return fmt.Errorf("failed to write to nginx config %q: %w", s.conf, err) + } + + if err = systemctl.Reload("nginx"); err != nil { + _, err = shell.Execf("nginx -t") + return fmt.Errorf("failed to reload nginx: %w", err) + } + + return nil +} + +func (s *panelSolver) presentPanel(challenge acme.Challenge) error { + mux := http.NewServeMux() + mux.HandleFunc(challenge.HTTP01ResourcePath(), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + _, _ = w.Write([]byte(challenge.KeyAuthorization)) + }) + + s.server = &http.Server{ + Addr: ":80", + Handler: mux, + } + + errChan := make(chan error, 1) + go func() { + if err := s.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + errChan <- err + } + close(errChan) + }() + + // 等待一小段时间确保服务器启动成功 + select { + case err := <-errChan: + return fmt.Errorf("failed to start HTTP server: %w", err) + case <-time.After(100 * time.Millisecond): + return nil + } +} + +// CleanUp cleans up the HTTP server if it is the last one to finish. +func (s *panelSolver) CleanUp(ctx context.Context, _ acme.Challenge) error { + // 如果启动了内置 HTTP 服务器,则关闭它 + if s.server != nil { + shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + if err := s.server.Shutdown(shutdownCtx); err != nil { + return fmt.Errorf("failed to shutdown HTTP server: %w", err) + } + s.server = nil + return nil + } + + // 否则清理 nginx 配置 + if err := os.WriteFile(s.conf, []byte(""), 0644); err != nil { + return fmt.Errorf("failed to write to nginx config %q: %w", s.conf, err) + } + + if err := systemctl.Reload("nginx"); err != nil { + _, err = shell.Execf("nginx -t") + return fmt.Errorf("failed to reload nginx: %w", err) + } + + return nil +} + type httpSolver struct { conf string } diff --git a/pkg/config/config.go b/pkg/config/config.go index 8f325259..1eb259ad 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -34,6 +34,7 @@ type HTTPConfig struct { Entrance string `yaml:"entrance"` EntranceError string `yaml:"entrance_error"` TLS bool `yaml:"tls"` + ACME bool `yaml:"acme"` LoginCaptcha bool `yaml:"login_captcha"` IPHeader string `yaml:"ip_header"` BindDomain []string `yaml:"bind_domain"` diff --git a/pkg/webserver/nginx/vhost.go b/pkg/webserver/nginx/vhost.go index 61190f57..1243ddcb 100644 --- a/pkg/webserver/nginx/vhost.go +++ b/pkg/webserver/nginx/vhost.go @@ -496,8 +496,6 @@ func (v *baseVhost) SetSSLConfig(cfg *types.SSLConfig) error { } // 设置 OCSP - _ = v.parser.Clear("server.ssl_stapling") - _ = v.parser.Clear("server.ssl_stapling_verify") if cfg.OCSP { if err = v.parser.Set("server", []*config.Directive{ { @@ -535,6 +533,11 @@ func (v *baseVhost) ClearSSL() error { _ = v.parser.Clear("server.ssl_ciphers") _ = v.parser.Clear("server.ssl_prefer_server_ciphers") _ = v.parser.Clear("server.ssl_early_data") + _ = v.parser.Clear("server.ssl_stapling") + _ = v.parser.Clear("server.ssl_stapling_verify") + _ = v.setHSTS(false) + _ = v.setHTTPSRedirect(false) + _ = v.setAltSvc("") return nil } diff --git a/web/src/api/apps/openresty/index.ts b/web/src/api/apps/openresty/index.ts new file mode 100644 index 00000000..3a7f09b5 --- /dev/null +++ b/web/src/api/apps/openresty/index.ts @@ -0,0 +1,14 @@ +import { http } from '@/utils' + +export default { + // 负载状态 + load: (): any => http.Get('/apps/openresty/load'), + // 获取配置 + config: (): any => http.Get('/apps/openresty/config'), + // 保存配置 + saveConfig: (config: string): any => http.Post('/apps/openresty/config', { config }), + // 获取错误日志 + errorLog: (): any => http.Get('/apps/openresty/error_log'), + // 清空错误日志 + clearErrorLog: (): any => http.Post('/apps/openresty/clear_error_log') +} diff --git a/web/src/views/apps/nginx/types.ts b/web/src/views/apps/nginx/types.ts deleted file mode 100644 index f555524d..00000000 --- a/web/src/views/apps/nginx/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface Task { - id: number - name: string - status: string - shell: string - log: string - created_at: string - updated_at: string -} diff --git a/web/src/views/apps/openresty/IndexView.vue b/web/src/views/apps/openresty/IndexView.vue new file mode 100644 index 00000000..3e1251e6 --- /dev/null +++ b/web/src/views/apps/openresty/IndexView.vue @@ -0,0 +1,102 @@ + + + diff --git a/web/src/views/apps/openresty/route.ts b/web/src/views/apps/openresty/route.ts index c531d659..dd4a2213 100644 --- a/web/src/views/apps/openresty/route.ts +++ b/web/src/views/apps/openresty/route.ts @@ -11,7 +11,7 @@ export default { { name: 'apps-openresty-index', path: '', - component: () => import('../nginx/IndexView.vue'), + component: () => import('./IndexView.vue'), meta: { title: 'OpenResty', role: ['admin'], diff --git a/web/src/views/setting/SettingBase.vue b/web/src/views/setting/SettingBase.vue index 23157d9d..a4ed57ce 100644 --- a/web/src/views/setting/SettingBase.vue +++ b/web/src/views/setting/SettingBase.vue @@ -50,10 +50,10 @@ const channels = [ - + - +