Move runtime data into config directory

This commit is contained in:
troogs
2026-10-01 18:20:05 +02:00
parent 944eed3eb3
commit 2e182d63a3
13 changed files with 509 additions and 79 deletions
+149 -56
View File
@@ -11,6 +11,7 @@ import (
"net/http"
"net/smtp"
"os"
"path/filepath"
"sort"
"strings"
"sync"
@@ -44,10 +45,24 @@ type AvailableDate struct {
}
type AppSettings struct {
Activities []Activity `json:"activities"`
AvailableDates []AvailableDate `json:"availableDates"`
Names []string `json:"names"`
AdminPassword string `json:"adminPassword"`
Activities []Activity `json:"-"`
AvailableDates []AvailableDate `json:"-"`
}
type Booking struct {
BookedAt string `json:"bookedAt"`
Date string `json:"date"`
DateType string `json:"dateType"`
Name string `json:"name"`
Activities []BookingActivity `json:"activities"`
}
type BookingActivity struct {
Name string `json:"name"`
Icon string `json:"icon"`
Type string `json:"type"`
}
type PublicSettings struct {
@@ -61,6 +76,7 @@ type passwordRequest struct {
}
var settingsMu sync.Mutex
var bookingsMu sync.Mutex
var sessionsMu sync.Mutex
var adminSessions = make(map[string]time.Time)
@@ -486,11 +502,7 @@ func handleAdminBulkDates(w http.ResponseWriter, r *http.Request) {
}
func getSettingsFilePath() string {
path := os.Getenv("SETTINGS_FILE")
if path == "" {
path = "appsettings.json"
}
return path
return filepath.Join("config", "appsettings.json")
}
func readSettings() (AppSettings, error) {
@@ -508,9 +520,42 @@ func readSettingsFile() (AppSettings, error) {
if err := json.Unmarshal(data, &settings); err != nil {
return settings, err
}
var legacy struct {
Activities []Activity `json:"activities"`
AvailableDates []AvailableDate `json:"availableDates"`
}
if err := json.Unmarshal(data, &legacy); err != nil {
return settings, err
}
settings.Activities = legacy.Activities
settings.AvailableDates = legacy.AvailableDates
if activities, found, err := readOptionalJSON(getActivitiesFilePath(), &[]Activity{}); err != nil {
return settings, err
} else if found {
settings.Activities = *activities.(*[]Activity)
}
if dates, found, err := readOptionalJSON(getAvailableDatesFilePath(), &[]AvailableDate{}); err != nil {
return settings, err
} else if found {
settings.AvailableDates = *dates.(*[]AvailableDate)
}
return settings, nil
}
func readOptionalJSON(path string, target interface{}) (interface{}, bool, error) {
data, err := os.ReadFile(path)
if os.IsNotExist(err) {
return target, false, nil
}
if err != nil {
return target, false, err
}
if err := json.Unmarshal(data, target); err != nil {
return target, false, err
}
return target, true, nil
}
func updateAvailableDates(update func([]AvailableDate) ([]AvailableDate, error)) error {
settingsMu.Lock()
defer settingsMu.Unlock()
@@ -526,15 +571,7 @@ func updateAvailableDates(update func([]AvailableDate) ([]AvailableDate, error))
sort.Slice(dates, func(i, j int) bool { return dates[i].Date < dates[j].Date })
settings.AvailableDates = dates
data, err := json.MarshalIndent(settings, "", " ")
if err != nil {
return err
}
temporaryPath := getSettingsFilePath() + ".tmp"
if err := os.WriteFile(temporaryPath, data, 0600); err != nil {
return err
}
return os.Rename(temporaryPath, getSettingsFilePath())
return writeJSONFile(getAvailableDatesFilePath(), dates)
}
func updateActivities(update func([]Activity) ([]Activity, error)) error {
@@ -551,15 +588,27 @@ func updateActivities(update func([]Activity) ([]Activity, error)) error {
}
settings.Activities = activities
data, err := json.MarshalIndent(settings, "", " ")
return writeJSONFile(getActivitiesFilePath(), activities)
}
func writeJSONFile(path string, value interface{}) error {
data, err := json.MarshalIndent(value, "", " ")
if err != nil {
return err
}
temporaryPath := getSettingsFilePath() + ".tmp"
temporaryPath := path + ".tmp"
if err := os.WriteFile(temporaryPath, data, 0600); err != nil {
return err
}
return os.Rename(temporaryPath, getSettingsFilePath())
return os.Rename(temporaryPath, path)
}
func getActivitiesFilePath() string {
return filepath.Join("config", "activities.json")
}
func getAvailableDatesFilePath() string {
return filepath.Join("config", "dates_available.json")
}
func validateActivity(activity Activity) error {
@@ -577,11 +626,22 @@ func validateActivity(activity Activity) error {
func enabledActivities(activities []Activity) []Activity {
enabled := make([]Activity, 0, len(activities))
var customActivity *Activity
for _, activity := range activities {
if activity.Type == "custom" {
if !activity.Disabled {
custom := activity
customActivity = &custom
}
continue
}
if !activity.Disabled {
enabled = append(enabled, activity)
}
}
if customActivity != nil {
enabled = append(enabled, *customActivity)
}
return enabled
}
@@ -665,7 +725,7 @@ func handleSubmit(w http.ResponseWriter, r *http.Request) {
log.Printf("Received survey submission from %s for date %s (%s)", payload.Name, payload.Date, payload.Activity)
if err := recordBookedDate(payload.Date); err != nil {
if err := recordBooking(payload); err != nil {
log.Printf("Error recording booked date: %v", err)
}
@@ -706,65 +766,98 @@ func handleBookedDates(w http.ResponseWriter, r *http.Request) {
}
func getBookedDatesFilePath() string {
path := os.Getenv("BOOKED_DATES_FILE")
if path == "" {
path = "booked_dates.json"
}
return path
return filepath.Join("config", "dates_booked.json")
}
func getBookedDates() ([]string, error) {
filePath := getBookedDatesFilePath()
file, err := os.Open(filePath)
bookings, err := getBookings()
if err != nil {
if os.IsNotExist(err) {
return []string{}, nil
}
return nil, err
}
defer file.Close()
var rawDates []string
if err := json.NewDecoder(file).Decode(&rawDates); err != nil {
return []string{}, nil
}
dates := make([]string, 0, len(rawDates))
for _, d := range rawDates {
trimmed := strings.TrimSpace(d)
if trimmed != "" {
dates := make([]string, 0, len(bookings))
for _, booking := range bookings {
if trimmed := strings.TrimSpace(booking.Date); trimmed != "" {
dates = append(dates, trimmed)
}
}
return dates, nil
}
func recordBookedDate(dateStr string) error {
trimmedDate := strings.TrimSpace(dateStr)
if trimmedDate == "" {
func getBookings() ([]Booking, error) {
bookingsMu.Lock()
defer bookingsMu.Unlock()
return readBookings()
}
func readBookings() ([]Booking, error) {
filePath := getBookedDatesFilePath()
data, err := os.ReadFile(filePath)
if os.IsNotExist(err) {
legacyPath := os.Getenv("LEGACY_BOOKED_DATES_FILE")
if legacyPath == "" {
legacyPath = filepath.Join("config", "booked_dates.json")
}
data, err = os.ReadFile(legacyPath)
if os.IsNotExist(err) {
return []Booking{}, nil
}
}
if err != nil {
return nil, err
}
var bookings []Booking
if err := json.Unmarshal(data, &bookings); err == nil {
return bookings, nil
}
var legacyDates []string
if err := json.Unmarshal(data, &legacyDates); err != nil {
return nil, err
}
bookings = make([]Booking, 0, len(legacyDates))
for _, date := range legacyDates {
if trimmed := strings.TrimSpace(date); trimmed != "" {
bookings = append(bookings, Booking{Date: trimmed})
}
}
return bookings, nil
}
func recordBooking(payload SurveyPayload) error {
date := strings.TrimSpace(payload.Date)
if date == "" {
return nil
}
dates, err := getBookedDates()
bookingsMu.Lock()
defer bookingsMu.Unlock()
bookings, err := readBookings()
if err != nil {
dates = []string{}
bookings = []Booking{}
}
for _, d := range dates {
if d == trimmedDate {
for _, booking := range bookings {
if booking.Date == date {
return nil
}
}
dates = append(dates, trimmedDate)
filePath := getBookedDatesFilePath()
data, err := json.MarshalIndent(dates, "", " ")
if err != nil {
return err
bookingActivities := make([]BookingActivity, 0, len(payload.Activities))
for _, activity := range payload.Activities {
bookingActivities = append(bookingActivities, BookingActivity{
Name: activity.Name,
Icon: activity.Icon,
Type: activity.Type,
})
}
return os.WriteFile(filePath, data, 0644)
bookings = append(bookings, Booking{
BookedAt: time.Now().UTC().Format(time.RFC3339),
Date: date,
DateType: payload.DateType,
Name: payload.Name,
Activities: bookingActivities,
})
return writeJSONFile(getBookedDatesFilePath(), bookings)
}
func enableCORS(w http.ResponseWriter, r *http.Request) bool {