Files
MSE-PI-E2EEDA-Plein-de-eeee…/db/src/rest/rest.go
Klagarge c4cf1ba704 fix(db): CORS request
Cross-Origin Resource Sharing now allow all *.e.kb28.ch

Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Klagarge <remi@heredero.ch>
2026-06-04 14:46:32 +02:00

187 lines
5.0 KiB
Go

package rest
import (
"context"
"fmt"
"gateway/influx"
"net/http"
"regexp"
_ "gateway/docs"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
type RestGateway struct {
influxGateway *influx.InfluxGateway
engine *gin.Engine
measurementName string
username string
password string
}
func NewRestGateway(influxGateway *influx.InfluxGateway, measurementName string, username, password string) *RestGateway {
g := &RestGateway{
influxGateway: influxGateway,
engine: gin.Default(),
measurementName: measurementName,
username: username,
password: password,
}
g.setupRoutes()
return g
}
func (g *RestGateway) setupRoutes() {
// Setup CORS middleware to allow all *.e.kb28.ch origins
corsConfig := cors.Config{
AllowOriginFunc: func(origin string) bool {
// Match any origin like *.e.kb28.ch
pattern := regexp.MustCompile(`^https://.*\.e\.kb28\.ch$`)
return pattern.MatchString(origin)
},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Authorization", "Content-Type"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
}
g.engine.Use(cors.New(corsConfig))
v1 := g.engine.Group("/api/v1")
if g.username != "" && g.password != "" {
v1.Use(gin.BasicAuth(gin.Accounts{
g.username: g.password,
}))
}
{
v1.GET("/rooms", g.getRooms)
v1.GET("/rooms/:room-id/current", g.getRoomCurrent)
v1.GET("/rooms/:room-id/history", g.getRoomHistory)
}
g.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
func (g *RestGateway) Run(addr string) error {
return g.engine.Run(addr)
}
// GET /api/v1/rooms
// getRooms godoc
// @Summary Get all unique rooms
// @Description Get a list of all unique rooms from the measurement
// @Tags rooms
// @Produce json
// @Success 200 {array} string
// @Failure 500 {object} map[string]string
// @Security BasicAuth
// @Router /rooms [get]
func (g *RestGateway) getRooms(c *gin.Context) {
// Query unique rooms from the measurement
query := fmt.Sprintf(`SELECT DISTINCT("room") FROM "%s"`, g.measurementName)
// Using context.Background() as seen in working snippet
it, err := g.influxGateway.Query(context.Background(), query)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
var rooms []string
for it.Next() {
val := it.Value()
if room, ok := val["room"].(string); ok {
rooms = append(rooms, room)
}
}
if err := it.Err(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, rooms)
}
// GET /api/v1/rooms/{room-id}/current
// getRoomCurrent godoc
// @Summary Get current data for a room
// @Description Get the latest record for a specific room
// @Tags rooms
// @Produce json
// @Param room-id path string true "Room ID"
// @Success 200 {object} map[string]any
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security BasicAuth
// @Router /rooms/{room-id}/current [get]
func (g *RestGateway) getRoomCurrent(c *gin.Context) {
roomID := c.Param("room-id")
// Get the last record for the specific room
query := fmt.Sprintf(`SELECT * FROM "%s" WHERE "room" = '%s' ORDER BY time DESC LIMIT 1`, g.measurementName, roomID)
// Using context.Background() as seen in working snippet
it, err := g.influxGateway.Query(context.Background(), query)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if it.Next() {
c.JSON(http.StatusOK, it.Value())
return
}
if err := it.Err(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusNotFound, gin.H{"error": "Room not found or no data available"})
}
// GET /api/v1/rooms/{room-id}/history
// getRoomHistory godoc
// @Summary Get history for a room
// @Description Get history for a specific room
// @Tags rooms
// @Produce json
// @Param room-id path string true "Room ID"
// @Param window query string false "Time window (e.g., 1 day, 1 hour, 30 min)" default(1 day)
// @Success 200 {array} map[string]any
// @Failure 500 {object} map[string]string
// @Security BasicAuth
// @Router /rooms/{room-id}/history [get]
func (g *RestGateway) getRoomHistory(c *gin.Context) {
roomID := c.Param("room-id")
window := c.DefaultQuery("window", "1 day")
query := fmt.Sprintf(` SELECT * FROM "%s" WHERE "room" = '%s' AND time > now() - INTERVAL '%s' ORDER BY time ASC
`, g.measurementName, roomID, window)
// Using context.Background() as seen in working snippet
it, err := g.influxGateway.Query(context.Background(), query)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
var history []map[string]any
for it.Next() {
history = append(history, it.Value())
}
if err := it.Err(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, history)
}