feat: dual TTL reservation cleanup and guest account anonymization

CleanupOldReservations now handles four reservation types:
logged-in (1hr), anonymous (10min), admin-walkin (10min), admin-callin (1hr).

AnonymizeStaleGuestAccounts scrubs PII from guest accounts whose last booking
was 6+ months ago and who have no pending/confirmed bookings. Financial records
remain intact — only personal data is wiped (UK GDPR compliance).
Triggered on every GetAvailableHours call.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-30 11:44:51 +01:00
co-authored by Sisyphus
parent 70cba5e412
commit 4f173b04dc
2 changed files with 52 additions and 15 deletions
+19 -14
View File
@@ -275,6 +275,11 @@ func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
log.Printf("Failed to cleanup old reservations: %v", err)
}
// Anonymize stale guest accounts (6+ months after last booking)
if err := AnonymizeStaleGuestAccounts(r.Context()); err != nil {
log.Printf("Failed to anonymize stale guest accounts: %v", err)
}
// Load default hours
defaultMap := map[int]DefaultHours{}
defRows, _ := db.DB.Query(r.Context(), `SELECT weekday, start_time::text, end_time::text, is_open FROM working_hours`)
@@ -456,21 +461,21 @@ func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
day.Slots = subtractTimeSlots(day.Slots, dayBlockers)
}
// Late night lock: after 22:00, block next morning 00:00-11:00 for non-admin users
now := time.Now()
if !isAdmin && now.Hour() >= 22 {
// Check if this is tomorrow's date
tomorrow := now.AddDate(0, 0, 1)
tomorrowStr := tomorrow.Format("2006-01-02")
if day.Date == tomorrowStr {
// Add a fake blocker for 00:00-11:00
lateNightBlock := TimeSlot{
StartTime: "00:00",
EndTime: "11:00",
// Late night lock: after 22:00, block next morning 00:00-11:00 for non-admin users
now := time.Now()
if !isAdmin && now.Hour() >= 22 {
// Check if this is tomorrow's date
tomorrow := now.AddDate(0, 0, 1)
tomorrowStr := tomorrow.Format("2006-01-02")
if day.Date == tomorrowStr {
// Add a fake blocker for 00:00-11:00
lateNightBlock := TimeSlot{
StartTime: "00:00",
EndTime: "11:00",
}
day.Slots = subtractTimeSlots(day.Slots, []TimeSlot{lateNightBlock})
}
}
day.Slots = subtractTimeSlots(day.Slots, []TimeSlot{lateNightBlock})
}
}
} else {
// Admins: keep blockers visible for warning display
if dayBlockers, ok := blockerMap[day.Date]; ok {