feat: wire reserve route, dual TTL cleanup, and booking reservation deletion
Add POST /api/bookings/reserve route with OptionalAuth middleware and rate limiting. Update CleanupOldReservations to handle dual TTLs: 1hr for logged-in users, 10min for anonymous reservations. Update CreateBookingHandler to also match anon reservations by start_time for users who register mid-flow. Add comprehensive tests for reservation creation, validation, conflict detection, and cleanup. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -19,20 +19,20 @@ import (
|
||||
// --- Types ---
|
||||
|
||||
type TimeBlocker struct {
|
||||
ID string `json:"id"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
DurationMinutes int `json:"duration_minutes"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CronExpression *string `json:"cron_expression,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CreatedBy *string `json:"created_by,omitempty"`
|
||||
ID string `json:"id"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
DurationMinutes int `json:"duration_minutes"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CronExpression *string `json:"cron_expression,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CreatedBy *string `json:"created_by,omitempty"`
|
||||
}
|
||||
|
||||
type CreateTimeBlockerRequest struct {
|
||||
StartTime time.Time `json:"start_time"`
|
||||
DurationMinutes int `json:"duration_minutes"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CronExpression *string `json:"cron_expression,omitempty"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
DurationMinutes int `json:"duration_minutes"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CronExpression *string `json:"cron_expression,omitempty"`
|
||||
}
|
||||
|
||||
// --- List Time Blockers ---
|
||||
@@ -185,7 +185,6 @@ func DeleteTimeBlocker(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
|
||||
// --- Helper: Get Time Blockers in Range ---
|
||||
|
||||
// --- Helper: Get Time Blockers in Range ---
|
||||
@@ -334,14 +333,17 @@ func CheckTimeBlockerOverlap(ctx context.Context, startTime, endTime time.Time)
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
// CleanupOldReservations deletes reservations (time_blockers with RESERVATION: description prefix)
|
||||
// that are older than 1 hour.
|
||||
// CleanupOldReservations deletes expired reservations:
|
||||
// - Logged-in (RESERVATION:user): older than 1 hour
|
||||
// - Anonymous (RESERVATION:anon): older than 10 minutes
|
||||
func CleanupOldReservations(ctx context.Context) error {
|
||||
oneHourAgo := time.Now().Add(-1 * time.Hour)
|
||||
tenMinutesAgo := time.Now().Add(-10 * time.Minute)
|
||||
|
||||
_, err := db.DB.Exec(ctx, `
|
||||
DELETE FROM time_blockers
|
||||
WHERE description LIKE 'RESERVATION:%'
|
||||
AND created_at < $1
|
||||
`, oneHourAgo)
|
||||
WHERE (description LIKE 'RESERVATION:user:%' AND created_at < $1)
|
||||
OR (description LIKE 'RESERVATION:anon:%' AND created_at < $2)
|
||||
`, oneHourAgo, tenMinutesAgo)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user