feat: add certificate expiration notifications #387

This commit is contained in:
Jacky 2025-04-09 14:04:52 +00:00
parent 7a0972495f
commit 1512e1de85
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
21 changed files with 1616 additions and 1235 deletions

View file

@ -0,0 +1,67 @@
package cert
import (
"time"
"github.com/0xJacky/Nginx-UI/internal/notification"
"github.com/0xJacky/Nginx-UI/query"
"github.com/uozi-tech/cosy/logger"
)
func CertExpiredNotify() {
c := query.Cert
certs, err := c.Find()
if err != nil {
logger.Errorf("CertExpiredNotify: Err: %v\n", err)
return
}
for _, certModel := range certs {
if certModel.SSLCertificatePath == "" {
continue
}
certInfo, err := GetCertInfo(certModel.SSLCertificatePath)
if err != nil {
continue
}
now := time.Now()
// Calculate days until expiration
daysUntilExpiration := int(certInfo.NotAfter.Sub(now).Hours() / 24)
// ignore expired certificate
if daysUntilExpiration < -1 {
continue
}
mask := map[string]any{
"name": certModel.Name,
"days": daysUntilExpiration,
}
// Check if certificate is already expired
if now.After(certInfo.NotAfter) {
notification.Error("Certificate Expired", "Certificate %{name} has expired", mask)
continue
}
// Send notifications based on remaining days
switch {
case daysUntilExpiration <= 14:
notification.Info("Certificate Expiration Notice",
"Certificate %{name} will expire in %{days} days", mask)
case daysUntilExpiration <= 7:
notification.Warning("Certificate Expiring Soon",
"Certificate %{name} will expire in %{days} days", mask)
case daysUntilExpiration <= 3:
notification.Warning("Certificate Expiring Soon",
"Certificate %{name} will expire in %{days} days", mask)
case daysUntilExpiration <= 1:
notification.Error("Certificate Expiring Soon",
"Certificate %{name} will expire in 1 day", mask)
}
}
}

View file

@ -0,0 +1,35 @@
package cron
import (
"time"
"github.com/0xJacky/Nginx-UI/internal/cert"
"github.com/go-co-op/gocron/v2"
"github.com/uozi-tech/cosy/logger"
)
// setupAutoCertJob initializes the automatic certificate renewal job
func setupAutoCertJob(scheduler gocron.Scheduler) (gocron.Job, error) {
job, err := scheduler.NewJob(gocron.DurationJob(30*time.Minute),
gocron.NewTask(cert.AutoCert),
gocron.WithSingletonMode(gocron.LimitModeWait),
gocron.JobOption(gocron.WithStartImmediately()))
if err != nil {
logger.Errorf("AutoCert Job: Err: %v\n", err)
return nil, err
}
return job, nil
}
// setupCertExpiredJob initializes the certificate expiration check job
func setupCertExpiredJob(scheduler gocron.Scheduler) (gocron.Job, error) {
job, err := scheduler.NewJob(gocron.DurationJob(6*time.Hour),
gocron.NewTask(cert.CertExpiredNotify),
gocron.WithSingletonMode(gocron.LimitModeWait),
gocron.JobOption(gocron.WithStartImmediately()))
if err != nil {
logger.Errorf("CertExpired Job: Err: %v\n", err)
return nil, err
}
return job, nil
}

View file

@ -0,0 +1,29 @@
package cron
import (
"time"
"github.com/0xJacky/Nginx-UI/query"
"github.com/go-co-op/gocron/v2"
"github.com/uozi-tech/cosy/logger"
)
// setupAuthTokenCleanupJob initializes the job to clean expired auth tokens
func setupAuthTokenCleanupJob(scheduler gocron.Scheduler) (gocron.Job, error) {
job, err := scheduler.NewJob(
gocron.DurationJob(5*time.Minute),
gocron.NewTask(func() {
logger.Debug("clean expired auth tokens")
q := query.AuthToken
_, _ = q.Where(q.ExpiredAt.Lt(time.Now().Unix())).Delete()
}),
gocron.WithSingletonMode(gocron.LimitModeWait),
gocron.JobOption(gocron.WithStartImmediately()))
if err != nil {
logger.Errorf("CleanExpiredAuthToken Err: %v\n", err)
return nil, err
}
return job, nil
}

View file

@ -1,16 +1,11 @@
package cron
import (
"time"
"github.com/0xJacky/Nginx-UI/internal/cert"
"github.com/0xJacky/Nginx-UI/internal/logrotate"
"github.com/0xJacky/Nginx-UI/query"
"github.com/0xJacky/Nginx-UI/settings"
"github.com/go-co-op/gocron/v2"
"github.com/uozi-tech/cosy/logger"
)
// Global scheduler instance
var s gocron.Scheduler
func init() {
@ -21,58 +16,34 @@ func init() {
}
}
var logrotateJob gocron.Job
// InitCronJobs initializes and starts all cron jobs
func InitCronJobs() {
_, err := s.NewJob(gocron.DurationJob(30*time.Minute),
gocron.NewTask(cert.AutoCert),
gocron.WithSingletonMode(gocron.LimitModeWait),
gocron.JobOption(gocron.WithStartImmediately()))
// Initialize auto cert job
_, err := setupAutoCertJob(s)
if err != nil {
logger.Fatalf("AutoCert Err: %v\n", err)
}
startLogrotate()
cleanExpiredAuthToken()
s.Start()
}
func RestartLogrotate() {
logger.Debug("Restart Logrotate")
if logrotateJob != nil {
err := s.RemoveJob(logrotateJob.ID())
if err != nil {
logger.Error(err)
return
}
}
startLogrotate()
}
func startLogrotate() {
if !settings.LogrotateSettings.Enabled {
return
}
var err error
logrotateJob, err = s.NewJob(
gocron.DurationJob(time.Duration(settings.LogrotateSettings.Interval)*time.Minute),
gocron.NewTask(logrotate.Exec),
gocron.WithSingletonMode(gocron.LimitModeWait))
// Initialize certificate expiration check job
_, err = setupCertExpiredJob(s)
if err != nil {
logger.Fatalf("LogRotate Job: Err: %v\n", err)
logger.Fatalf("CertExpired Err: %v\n", err)
}
}
func cleanExpiredAuthToken() {
_, err := s.NewJob(gocron.DurationJob(5*time.Minute), gocron.NewTask(func() {
logger.Debug("clean expired auth tokens")
q := query.AuthToken
_, _ = q.Where(q.ExpiredAt.Lt(time.Now().Unix())).Delete()
}), gocron.WithSingletonMode(gocron.LimitModeWait), gocron.JobOption(gocron.WithStartImmediately()))
// Start logrotate job
setupLogrotateJob(s)
// Initialize auth token cleanup job
_, err = setupAuthTokenCleanupJob(s)
if err != nil {
logger.Fatalf("CleanExpiredAuthToken Err: %v\n", err)
}
// Start the scheduler
s.Start()
}
// RestartLogrotate is a public API to restart the logrotate job
func RestartLogrotate() {
restartLogrotateJob(s)
}

View file

@ -0,0 +1,42 @@
package cron
import (
"time"
"github.com/0xJacky/Nginx-UI/internal/logrotate"
"github.com/0xJacky/Nginx-UI/settings"
"github.com/go-co-op/gocron/v2"
"github.com/uozi-tech/cosy/logger"
)
// logrotate job instance
var logrotateJobInstance gocron.Job
// setupLogrotateJob initializes and starts the logrotate job
func setupLogrotateJob(scheduler gocron.Scheduler) {
if !settings.LogrotateSettings.Enabled {
return
}
var err error
logrotateJobInstance, err = scheduler.NewJob(
gocron.DurationJob(time.Duration(settings.LogrotateSettings.Interval)*time.Minute),
gocron.NewTask(logrotate.Exec),
gocron.WithSingletonMode(gocron.LimitModeWait))
if err != nil {
logger.Fatalf("LogRotate Job: Err: %v\n", err)
}
}
// restartLogrotateJob stops and restarts the logrotate job
func restartLogrotateJob(scheduler gocron.Scheduler) {
logger.Debug("Restart Logrotate")
if logrotateJobInstance != nil {
err := scheduler.RemoveJob(logrotateJobInstance.ID())
if err != nil {
logger.Error(err)
return
}
}
setupLogrotateJob(scheduler)
}