2023-08-25 20:44:44 +00:00
|
|
|
package morningalarm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
2023-11-06 23:21:12 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2023-08-25 20:44:44 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Device struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
ID string `json:"id"`
|
|
|
|
}
|
|
|
|
|
2023-10-02 17:54:00 +00:00
|
|
|
type AlarmPatch struct {
|
|
|
|
Disabled bool `json:"disabled"`
|
|
|
|
}
|
|
|
|
|
2023-08-25 20:44:44 +00:00
|
|
|
func (ma *MorningAlarm) setupWebserver() {
|
2023-11-06 23:21:12 +00:00
|
|
|
|
|
|
|
// TODO: This is stupid
|
|
|
|
execPath, err := os.Executable()
|
|
|
|
if err != nil {
|
|
|
|
panic("Shit")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serve static files
|
|
|
|
ma.ro.NoRoute(gin.WrapH(http.FileServer(http.Dir(filepath.Join(filepath.Dir(execPath), "public")))))
|
|
|
|
|
2023-10-02 17:54:00 +00:00
|
|
|
// Create a new alarm
|
2023-08-25 20:44:44 +00:00
|
|
|
ma.ro.POST("/api/alarm", func(c *gin.Context) {
|
|
|
|
var body alarm
|
|
|
|
|
|
|
|
if err := c.BindJSON(&body); err != nil {
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-08-26 00:12:30 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 20:44:44 +00:00
|
|
|
_, err := ma.addAlarm(body.Time, body.Name)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
} else {
|
2023-10-02 17:54:00 +00:00
|
|
|
err = ma.saveAlarmsToFile()
|
2023-08-25 20:44:44 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-08-26 00:12:30 +00:00
|
|
|
c.Header("Location", "/api/alarm/"+body.Name)
|
2023-08-25 20:44:44 +00:00
|
|
|
c.JSON(http.StatusCreated, gin.H{})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2023-10-02 17:54:00 +00:00
|
|
|
// Get an alarm
|
2023-08-26 00:12:30 +00:00
|
|
|
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)
|
|
|
|
})
|
|
|
|
|
2023-10-02 17:54:00 +00:00
|
|
|
// Get all alarms
|
2023-08-26 00:12:30 +00:00
|
|
|
ma.ro.GET("/api/alarm", func(c *gin.Context) {
|
|
|
|
c.JSON(http.StatusOK, ma.alarms)
|
|
|
|
})
|
|
|
|
|
2023-10-02 17:54:00 +00:00
|
|
|
// Delete an alarm
|
2023-08-26 00:12:30 +00:00
|
|
|
ma.ro.DELETE("/api/alarm/:id", func(c *gin.Context) {
|
|
|
|
id := c.Param("id")
|
|
|
|
|
2023-10-02 17:54:00 +00:00
|
|
|
if ma.removeAlarm(id) {
|
|
|
|
err := ma.saveAlarmsToFile()
|
2023-08-26 00:12:30 +00:00
|
|
|
|
|
|
|
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"})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2023-10-02 17:54:00 +00:00
|
|
|
// Get info about the server and the alarms
|
2023-08-25 20:44:44 +00:00
|
|
|
ma.ro.GET("/api/info", func(c *gin.Context) {
|
|
|
|
spotifyUser, err := ma.sp.CurrentUser(c)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
currentTime := time.Now()
|
|
|
|
zone, offset := currentTime.Zone()
|
|
|
|
|
|
|
|
nextIn := ma.nextAlarm()
|
|
|
|
|
|
|
|
response := gin.H{
|
|
|
|
"spotifyUser": spotifyUser.User.DisplayName,
|
|
|
|
"serverTime": currentTime.Format("15:04:05"),
|
|
|
|
"timezone": zone,
|
|
|
|
"timezoneOffsetInH": offset / 60 / 60,
|
|
|
|
"alarms": len(ma.cr.Entries()),
|
2023-08-26 00:12:30 +00:00
|
|
|
"wakeupPlaylist": ma.config.PlaylistID,
|
2023-09-25 23:07:01 +00:00
|
|
|
"deviceName": ma.config.DeviceName,
|
2023-08-25 20:44:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if nextIn != nil {
|
|
|
|
response["nextAlarmAt"] = nextIn.String()
|
|
|
|
response["nextAlarmIn"] = nextIn.Sub(currentTime).String()
|
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, response)
|
|
|
|
})
|
|
|
|
|
2023-10-02 17:54:00 +00:00
|
|
|
// Get available spotify devices
|
2023-08-25 20:44:44 +00:00
|
|
|
ma.ro.GET("/api/device", func(c *gin.Context) {
|
|
|
|
devices, err := ma.getAvailableDevices()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, devices)
|
|
|
|
})
|
|
|
|
|
2023-10-02 17:54:00 +00:00
|
|
|
// Trigger an alarm immediately
|
2023-08-25 20:44:44 +00:00
|
|
|
ma.ro.POST("/api/trigger", func(c *gin.Context) {
|
|
|
|
if _, err := ma.playWakeUpMusic(); err != nil {
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{})
|
|
|
|
})
|
2023-10-02 17:54:00 +00:00
|
|
|
|
|
|
|
ma.ro.PATCH("/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
|
|
|
|
}
|
|
|
|
|
|
|
|
var body AlarmPatch
|
|
|
|
|
|
|
|
if err := c.BindJSON(&body); err != nil {
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ma.setDisabled(id, body.Disabled)
|
|
|
|
|
|
|
|
err := ma.saveAlarmsToFile()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{})
|
|
|
|
})
|
2023-08-25 20:44:44 +00:00
|
|
|
}
|