1
0
mirror of https://github.com/pocket-id/pocket-id.git synced 2026-02-09 14:54:20 +00:00

refactor: complete graceful shutdown implementation and add service runner (#493)

This commit is contained in:
Alessandro (Ale) Segala
2025-05-03 14:25:22 -07:00
committed by GitHub
parent 6bd6cefaa6
commit bda178c2bb
13 changed files with 454 additions and 130 deletions

View File

@@ -26,12 +26,12 @@ type AppConfigService struct {
db *gorm.DB
}
func NewAppConfigService(ctx context.Context, db *gorm.DB) *AppConfigService {
func NewAppConfigService(initCtx context.Context, db *gorm.DB) *AppConfigService {
service := &AppConfigService{
db: db,
}
err := service.LoadDbConfig(ctx)
err := service.LoadDbConfig(initCtx)
if err != nil {
log.Fatalf("Failed to initialize app config service: %v", err)
}

View File

@@ -33,7 +33,7 @@ type EmailService struct {
textTemplates map[string]*ttemplate.Template
}
func NewEmailService(appConfigService *AppConfigService, db *gorm.DB) (*EmailService, error) {
func NewEmailService(db *gorm.DB, appConfigService *AppConfigService) (*EmailService, error) {
htmlTemplates, err := email.PrepareHTMLTemplates(emailTemplatesPaths)
if err != nil {
return nil, fmt.Errorf("prepare html templates: %w", err)

View File

@@ -23,7 +23,7 @@ import (
type GeoLiteService struct {
disableUpdater bool
mutex sync.Mutex
mutex sync.RWMutex
}
var localhostIPNets = []*net.IPNet{
@@ -42,25 +42,22 @@ var tailscaleIPNets = []*net.IPNet{
}
// NewGeoLiteService initializes a new GeoLiteService instance and starts a goroutine to update the GeoLite2 City database.
func NewGeoLiteService(ctx context.Context) *GeoLiteService {
func NewGeoLiteService() *GeoLiteService {
service := &GeoLiteService{}
if common.EnvConfig.MaxMindLicenseKey == "" && common.EnvConfig.GeoLiteDBUrl == common.MaxMindGeoLiteCityUrl {
// Warn the user, and disable the updater.
// Warn the user, and disable the periodic updater
log.Println("MAXMIND_LICENSE_KEY environment variable is empty. The GeoLite2 City database won't be updated.")
service.disableUpdater = true
}
go func() {
err := service.updateDatabase(ctx)
if err != nil {
log.Printf("Failed to update GeoLite2 City database: %v", err)
}
}()
return service
}
func (s *GeoLiteService) DisableUpdater() bool {
return s.disableUpdater
}
// GetLocationByIP returns the country and city of the given IP address.
func (s *GeoLiteService) GetLocationByIP(ipAddress string) (country, city string, err error) {
// Check the IP address against known private IP ranges
@@ -83,8 +80,8 @@ func (s *GeoLiteService) GetLocationByIP(ipAddress string) (country, city string
}
// Race condition between reading and writing the database.
s.mutex.Lock()
defer s.mutex.Unlock()
s.mutex.RLock()
defer s.mutex.RUnlock()
db, err := maxminddb.Open(common.EnvConfig.GeoLiteDBPath)
if err != nil {
@@ -92,7 +89,10 @@ func (s *GeoLiteService) GetLocationByIP(ipAddress string) (country, city string
}
defer db.Close()
addr := netip.MustParseAddr(ipAddress)
addr, err := netip.ParseAddr(ipAddress)
if err != nil {
return "", "", fmt.Errorf("failed to parse IP address: %w", err)
}
var record struct {
City struct {
@@ -112,18 +112,13 @@ func (s *GeoLiteService) GetLocationByIP(ipAddress string) (country, city string
}
// UpdateDatabase checks the age of the database and updates it if it's older than 14 days.
func (s *GeoLiteService) updateDatabase(parentCtx context.Context) error {
if s.disableUpdater {
// Avoid updating the GeoLite2 City database.
return nil
}
func (s *GeoLiteService) UpdateDatabase(parentCtx context.Context) error {
if s.isDatabaseUpToDate() {
log.Println("GeoLite2 City database is up-to-date.")
log.Println("GeoLite2 City database is up-to-date")
return nil
}
log.Println("Updating GeoLite2 City database...")
log.Println("Updating GeoLite2 City database")
downloadUrl := fmt.Sprintf(common.EnvConfig.GeoLiteDBUrl, common.EnvConfig.MaxMindLicenseKey)
ctx, cancel := context.WithTimeout(parentCtx, 10*time.Minute)
@@ -145,7 +140,8 @@ func (s *GeoLiteService) updateDatabase(parentCtx context.Context) error {
}
// Extract the database file directly to the target path
if err := s.extractDatabase(resp.Body); err != nil {
err = s.extractDatabase(resp.Body)
if err != nil {
return fmt.Errorf("failed to extract database: %w", err)
}
@@ -179,10 +175,9 @@ func (s *GeoLiteService) extractDatabase(reader io.Reader) error {
// Iterate over the files in the tar archive
for {
header, err := tarReader.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {
} else if err != nil {
return fmt.Errorf("failed to read tar archive: %w", err)
}