diff --git a/cmd/morningalarm.go b/cmd/morningalarm.go index f1e3875..5dda4bd 100644 --- a/cmd/morningalarm.go +++ b/cmd/morningalarm.go @@ -11,7 +11,7 @@ type args struct { DeviceID string `arg:"--device-id,required,env:SPOTIFY_DEVICE_ID" placeholder:"SPOTIFY_DEVICE_ID" help:"Can be found later in the API"` PlaylistID string `arg:"--playlist-id,required,env:SPOTIFY_PLAYLIST_ID" help:"Just the id" placeholder:"SPOTIFY_PLAYLIST_ID"` RedirectURL string `arg:"--redirect-url,required,env:SPOTIFY_REDIRECT_URL" help:"Must be the same as in the Spotify developer dashboard." placeholder:"SPOTIFY_REDIRECT_URL"` - ListenAddr string `arg:"--listen-addr,required,env:LISTEN_ADDR" help:"Address to listen on" placeholder:"LISTEN_ADDR" default:":3000"` + ListenAddr string `arg:"--listen-addr,env:LISTEN_ADDR" help:"Address to listen on" placeholder:"LISTEN_ADDR" default:":3000"` } func main() { diff --git a/internal/cron.go b/internal/cron.go index b198bff..edc2e81 100644 --- a/internal/cron.go +++ b/internal/cron.go @@ -2,6 +2,7 @@ package morningalarm import ( "encoding/json" + "errors" "os" "time" @@ -25,6 +26,11 @@ func (ma *MorningAlarm) nextAlarm() *time.Time { } func (ma *MorningAlarm) addAlarm(spec string, name string) (cron.EntryID, error) { + // Check if alarm already exists + if ma.getAlarm(name) != nil { + return 0, errors.New("Alarm already exists") + } + id, err := ma.cr.AddFunc(spec, ma.fireAlarm) if err != nil { @@ -89,3 +95,32 @@ func (ma *MorningAlarm) loadAlarms() error { func (ma *MorningAlarm) fireAlarm() { ma.playWakeUpMusic() } + +func (ma *MorningAlarm) getAlarm(name string) *alarm { + for _, alarm := range ma.alarms { + if alarm.Name == name { + return &alarm + } + } + + return nil +} + +func (ma *MorningAlarm) deleteAlarm(name string) bool { + for _, alarm := range ma.alarms { + if alarm.Name == name { + ma.cr.Remove(alarm.id) + + for i, a := range ma.alarms { + if a.Name == name { + ma.alarms = append(ma.alarms[:i], ma.alarms[i+1:]...) + break + } + } + + return true + } + } + + return false +} diff --git a/internal/webserver.go b/internal/webserver.go index 1e01291..e2736b8 100644 --- a/internal/webserver.go +++ b/internal/webserver.go @@ -21,6 +21,20 @@ func (ma *MorningAlarm) setupWebserver() { return } + // Check if alarm name is valid + if body.Name == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Alarm name cannot be empty"}) + return + } + + // Chekc if alarm name contains only alphanumeric characters + for _, char := range body.Name { + if !((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9')) { + c.JSON(http.StatusBadRequest, gin.H{"error": "Alarm name must contain only alphanumeric characters"}) + return + } + } + _, err := ma.addAlarm(body.Time, body.Name) if err != nil { @@ -33,10 +47,45 @@ func (ma *MorningAlarm) setupWebserver() { return } + c.Header("Location", "/api/alarm/"+body.Name) c.JSON(http.StatusCreated, gin.H{}) } }) + ma.ro.GET("/api/alarm/:id", func(c *gin.Context) { + id := c.Param("id") + + alarm := ma.getAlarm(id) + + if alarm == nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Alarm not found"}) + return + } + + c.JSON(http.StatusOK, alarm) + }) + + ma.ro.GET("/api/alarm", func(c *gin.Context) { + c.JSON(http.StatusOK, ma.alarms) + }) + + ma.ro.DELETE("/api/alarm/:id", func(c *gin.Context) { + id := c.Param("id") + + if ma.deleteAlarm(id) { + err := ma.saveAlarms() + + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{}) + } else { + c.JSON(http.StatusNotFound, gin.H{"error": "Alarm not found"}) + } + }) + ma.ro.GET("/api/info", func(c *gin.Context) { spotifyUser, err := ma.sp.CurrentUser(c) @@ -56,7 +105,7 @@ func (ma *MorningAlarm) setupWebserver() { "timezone": zone, "timezoneOffsetInH": offset / 60 / 60, "alarms": len(ma.cr.Entries()), - "wakeupContext": ma.config.PlaylistID, + "wakeupPlaylist": ma.config.PlaylistID, "deviceId": ma.config.DeviceID, }