Files
lambdaiot-core/cmd/server/main.go

128 lines
3.0 KiB
Go

package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-gonic/gin"
"git.piskot.si/SeminarM2/lambdaiot-core/internal/config"
"git.piskot.si/SeminarM2/lambdaiot-core/internal/handler"
"git.piskot.si/SeminarM2/lambdaiot-core/internal/middleware"
mqttclient "git.piskot.si/SeminarM2/lambdaiot-core/internal/mqtt"
)
func main() {
// load configuration (look for ./config.toml by default)
cfg, err := config.Load("")
if err != nil {
log.Fatalf("failed to load config: %v", err)
}
// determine address
addr := cfg.Server.Address
if addr == "" {
addr = ":8080"
}
if cfg.Server.Port != 0 {
addr = fmt.Sprintf(":%d", cfg.Server.Port)
}
// connect to MQTT broker (best-effort)
var mq *mqttclient.Client
if cfg.MQTT.Broker != "" {
mqc, err := mqttclient.Connect(cfg.MQTT)
if err != nil {
log.Printf("warning: mqtt connect failed: %v", err)
} else {
mq = mqc
mqttclient.SetDefault(mqc)
// subscribe to the configured topic and log incoming messages
if cfg.MQTT.Topic != "" {
if err := mq.Subscribe(cfg.MQTT.Topic, func(t string, p []byte) {
log.Printf("mqtt recv on %s: %s", t, string(p))
}); err != nil {
log.Printf("warning: mqtt subscribe failed: %v", err)
}
}
// publish a startup message (non-blocking)
go func() {
msg := fmt.Sprintf("lambdaiot-core started on %s", addr)
if err := mq.Publish(cfg.MQTT.Topic, []byte(msg)); err != nil {
log.Printf("mqtt publish failed: %v", err)
}
}()
}
}
// mqttping endpoint will be added after router initialization
// Gin setup
r := gin.New()
r.Use(gin.Recovery())
r.Use(middleware.GinLogger())
// Public routes
r.GET("/health", handler.Health)
r.GET("/hello", handler.Hello)
r.POST("/login", handler.Login)
// mqttping endpoint: publish current timestamp to MQTT topic
r.POST("/mqttping", func(c *gin.Context) {
ts := time.Now().Format(time.RFC3339)
if mq == nil {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "mqtt not connected"})
return
}
if err := mq.Publish(cfg.MQTT.Topic, []byte(ts)); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"timestamp": ts})
})
// Protected routes
auth := r.Group("/")
auth.Use(middleware.AuthMiddleware(cfg.Server.JWTSecret))
auth.GET("/protected", handler.Protected)
srv := &http.Server{
Addr: addr,
Handler: r,
}
go func() {
log.Printf("starting server on %s", addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("server forced to shutdown: %v", err)
}
if mq != nil {
mq.Close()
}
log.Println("server exiting")
}
func getJWTSecret() string {
if s := os.Getenv("JWT_SECRET"); s != "" {
return s
}
return "secret"
}