package api import ( "net/http" "strings" "time" "gardomatic.kleiax.de/internal/platform/validate" "gardomatic.kleiax.de/internal/storage" ) type applicationSettingsInput struct { LifecycleStatusEnabled *bool `json:"lifecycle_status_enabled"` LifecycleRemovalMonth *int `json:"lifecycle_removal_month"` LifecycleRemovalDay *int `json:"lifecycle_removal_day"` Timezone *string `json:"timezone"` } func (input applicationSettingsInput) apply(settings *storage.ApplicationSettings) { if input.LifecycleStatusEnabled != nil { settings.LifecycleStatusEnabled = *input.LifecycleStatusEnabled } if input.LifecycleRemovalMonth != nil { settings.LifecycleRemovalMonth = *input.LifecycleRemovalMonth } if input.LifecycleRemovalDay != nil { settings.LifecycleRemovalDay = *input.LifecycleRemovalDay } if input.Timezone != nil { settings.Timezone = strings.TrimSpace(*input.Timezone) } } func (app *application) showAdminApplicationSettingsHandler(w http.ResponseWriter, r *http.Request) { settings, err := app.models.ApplicationSettings.Get() if err != nil { app.respondToEntityModelError(w, r, err) return } if err := app.writeJSON(w, http.StatusOK, envelope{"settings": settings}, nil); err != nil { app.serverErrorResponse(w, r, err) } } func (app *application) updateAdminApplicationSettingsHandler(w http.ResponseWriter, r *http.Request) { settings, err := app.models.ApplicationSettings.Get() if err != nil { app.respondToEntityModelError(w, r, err) return } var input applicationSettingsInput if err := app.readJSON(w, r, &input); err != nil { app.badRequestResponse(w, r, err) return } input.apply(&settings) v := validate.New() storage.ValidateApplicationSettings(v, settings) if !v.Valid() { app.failedValidationResponse(w, r, v.Errors) return } settings, err = app.models.ApplicationSettings.Update(settings) if err != nil { app.respondToEntityModelError(w, r, err) return } if err := app.writeJSON(w, http.StatusOK, envelope{"settings": settings}, nil); err != nil { app.serverErrorResponse(w, r, err) } } func (app *application) runLifecycleMaintenance(now time.Time) error { if app.models.ApplicationSettings == nil { return nil } settings, err := app.models.ApplicationSettings.Get() if err != nil || !settings.LifecycleStatusEnabled { return err } location, err := time.LoadLocation(settings.Timezone) if err != nil { return err } localNow := now.In(location) lastDay := time.Date(localNow.Year(), time.Month(settings.LifecycleRemovalMonth)+1, 0, 0, 0, 0, 0, location).Day() day := settings.LifecycleRemovalDay if day > lastDay { day = lastDay } cutoff := time.Date(localNow.Year(), time.Month(settings.LifecycleRemovalMonth), day, 0, 0, 0, 0, location) if localNow.Before(cutoff) { return nil } count, err := app.models.ApplicationSettings.RemoveExpiredPlants(cutoff, settings.LifecycleRemovalMonth, day) if err == nil && count > 0 { app.logger.Info("removed plants which reached their configured lifecycle", "count", count, "cutoff", cutoff.Format("2006-01-02")) } return err }