| @@ -0,0 +1,5 @@ | |||||
| DB_HOST=localhost | |||||
| DB_PORT=5432 | |||||
| DB_USER=postgres | |||||
| DB_PASSWORD=postgres | |||||
| DB_NAME=go_crud_db | |||||
| @@ -0,0 +1,37 @@ | |||||
| package main | |||||
| import ( | |||||
| "fmt" | |||||
| "log" | |||||
| "os" | |||||
| "github.com/joho/godotenv" | |||||
| "gorm.io/driver/postgres" | |||||
| "gorm.io/gorm" | |||||
| ) | |||||
| var DB *gorm.DB | |||||
| func main() { | |||||
| err := godotenv.Load() | |||||
| if err != nil { | |||||
| log.Fatal("Error loading .env file") | |||||
| } | |||||
| dsn := fmt.Sprintf( | |||||
| "host=%s user=%s password=%s dbname=%s port=%s sslmode=disable", | |||||
| os.Getenv("DB_HOST"), | |||||
| os.Getenv("DB_USER"), | |||||
| os.Getenv("DB_PASSWORD"), | |||||
| os.Getenv("DB_NAME"), | |||||
| os.Getenv("DB_PORT"), | |||||
| ) | |||||
| db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) | |||||
| if err != nil { | |||||
| log.Fatal("Failed to connect to the database:", err) | |||||
| } | |||||
| DB = db | |||||
| fmt.Println("Database connection established") | |||||
| } | |||||
| @@ -0,0 +1,7 @@ | |||||
| package models | |||||
| type Book struct { | |||||
| ID uint `json:"id" gorm:"primaryKey"` | |||||
| Title string `json:"title"` | |||||
| Author string `json:"author"` | |||||
| } | |||||
| @@ -65,9 +65,6 @@ func main() { | |||||
| alertsReader := appState.AddKafkaReader(cfg.KafkaURL, "alertbeacons", "gid-alert-serv") | alertsReader := appState.AddKafkaReader(cfg.KafkaURL, "alertbeacons", "gid-alert-serv") | ||||
| slog.Info("Kafka readers topics: locevents, alertbeacons initialized") | slog.Info("Kafka readers topics: locevents, alertbeacons initialized") | ||||
| client := appState.AddValkeyClient(cfg.ValkeyURL) | |||||
| slog.Info("Valkey DB client created") | |||||
| chLoc := make(chan model.HTTPLocation, 200) | chLoc := make(chan model.HTTPLocation, 200) | ||||
| chEvents := make(chan model.BeaconEvent, 500) | chEvents := make(chan model.BeaconEvent, 500) | ||||
| @@ -87,8 +84,8 @@ func main() { | |||||
| r.HandleFunc("/api/beaconids", controller.GetBeaconIds(appState)).Methods("GET") | r.HandleFunc("/api/beaconids", controller.GetBeaconIds(appState)).Methods("GET") | ||||
| r.HandleFunc("/api/settings", controller.SettingsListController(appState, client, ctx)).Methods("GET") | |||||
| r.HandleFunc("/api/settings", controller.SettingsEditController(settingsWriter, appState, client, ctx)).Methods("POST") | |||||
| r.HandleFunc("/api/settings", controller.SettingsListController(appState, ctx)).Methods("GET") | |||||
| r.HandleFunc("/api/settings", controller.SettingsEditController(settingsWriter, appState, ctx)).Methods("POST") | |||||
| wsHandler := http.HandlerFunc(serveWs(appState, ctx)) | wsHandler := http.HandlerFunc(serveWs(appState, ctx)) | ||||
| restApiHandler := handlers.CORS(originsOk, headersOk, methodsOk)(r) | restApiHandler := handlers.CORS(originsOk, headersOk, methodsOk)(r) | ||||
| @@ -114,12 +111,12 @@ eventLoop: | |||||
| case <-ctx.Done(): | case <-ctx.Done(): | ||||
| break eventLoop | break eventLoop | ||||
| case msg := <-chLoc: | case msg := <-chLoc: | ||||
| if err := service.LocationToBeaconService(msg, appState, client, ctx); err != nil { | |||||
| if err := service.LocationToBeaconService(msg, appState, ctx); err != nil { | |||||
| eMsg := fmt.Sprintf("Error in writing location change to beacon: %v\n", err) | eMsg := fmt.Sprintf("Error in writing location change to beacon: %v\n", err) | ||||
| slog.Error(eMsg) | slog.Error(eMsg) | ||||
| } | } | ||||
| case msg := <-chEvents: | case msg := <-chEvents: | ||||
| if err := service.EventToBeaconService(msg, appState, client, ctx); err != nil { | |||||
| if err := service.EventToBeaconService(msg, appState, ctx); err != nil { | |||||
| eMsg := fmt.Sprintf("Error in writing event change to beacon: %v\n", err) | eMsg := fmt.Sprintf("Error in writing event change to beacon: %v\n", err) | ||||
| slog.Error(eMsg) | slog.Error(eMsg) | ||||
| } | } | ||||
| @@ -140,8 +137,6 @@ eventLoop: | |||||
| appState.CleanKafkaWriters() | appState.CleanKafkaWriters() | ||||
| slog.Info("All kafka clients shutdown, starting shutdown of valkey client") | slog.Info("All kafka clients shutdown, starting shutdown of valkey client") | ||||
| appState.CleanValkeyClient() | |||||
| slog.Info("API server shutting down") | slog.Info("API server shutting down") | ||||
| logFile.Close() | logFile.Close() | ||||
| } | } | ||||
| @@ -1,14 +1,11 @@ | |||||
| package appcontext | package appcontext | ||||
| import ( | import ( | ||||
| "context" | |||||
| "encoding/json" | |||||
| "fmt" | "fmt" | ||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "github.com/AFASystems/presence/internal/pkg/model" | "github.com/AFASystems/presence/internal/pkg/model" | ||||
| "github.com/redis/go-redis/v9" | |||||
| "github.com/segmentio/kafka-go" | "github.com/segmentio/kafka-go" | ||||
| ) | ) | ||||
| @@ -22,7 +19,6 @@ type AppState struct { | |||||
| latestList model.LatestBeaconsList | latestList model.LatestBeaconsList | ||||
| kafkaReadersList model.KafkaReadersList | kafkaReadersList model.KafkaReadersList | ||||
| kafkaWritersList model.KafkaWritersList | kafkaWritersList model.KafkaWritersList | ||||
| valkeyDB *redis.Client | |||||
| } | } | ||||
| // NewAppState creates a new application context AppState with default values | // NewAppState creates a new application context AppState with default values | ||||
| @@ -61,25 +57,6 @@ func NewAppState() *AppState { | |||||
| } | } | ||||
| } | } | ||||
| func (m *AppState) AddValkeyClient(url string) *redis.Client { | |||||
| valkeyDB := redis.NewClient(&redis.Options{ | |||||
| Addr: url, | |||||
| Password: "", | |||||
| }) | |||||
| m.valkeyDB = valkeyDB | |||||
| return valkeyDB | |||||
| } | |||||
| func (m *AppState) CleanValkeyClient() { | |||||
| fmt.Println("shutdown of valkey client starts") | |||||
| if err := m.valkeyDB.Close(); err != nil { | |||||
| fmt.Println("Error in shuting down valkey client") | |||||
| } | |||||
| fmt.Println("Succesfully shutting down valkey client") | |||||
| } | |||||
| func (m *AppState) AddKafkaWriter(kafkaUrl, topic string) *kafka.Writer { | func (m *AppState) AddKafkaWriter(kafkaUrl, topic string) *kafka.Writer { | ||||
| kafkaWriter := &kafka.Writer{ | kafkaWriter := &kafka.Writer{ | ||||
| Addr: kafka.TCP(kafkaUrl), | Addr: kafka.TCP(kafkaUrl), | ||||
| @@ -316,15 +293,3 @@ func (m *AppState) UpdateSettings(newSettings model.SettingsVal) { | |||||
| m.settings.Settings = newSettings | m.settings.Settings = newSettings | ||||
| } | } | ||||
| func (m *AppState) PersistSettings(client *redis.Client, ctx context.Context) { | |||||
| d, err := json.Marshal(m.GetSettingsValue()) | |||||
| if err != nil { | |||||
| fmt.Printf("Error in marshalling settings: %v", err) | |||||
| return | |||||
| } | |||||
| if err := client.Set(ctx, "settings", d, 0).Err(); err != nil { | |||||
| fmt.Printf("Error in persisting settings: %v", err) | |||||
| } | |||||
| } | |||||
| @@ -9,10 +9,7 @@ type Config struct { | |||||
| MQTTUser string | MQTTUser string | ||||
| MQTTPass string | MQTTPass string | ||||
| MQTTClientID string | MQTTClientID string | ||||
| DBPath string | |||||
| KafkaURL string | KafkaURL string | ||||
| RedisURL string | |||||
| ValkeyURL string | |||||
| } | } | ||||
| // getEnv returns env var value or a default if not set. | // getEnv returns env var value or a default if not set. | ||||
| @@ -31,8 +28,6 @@ func Load() *Config { | |||||
| MQTTUser: getEnv("MQTT_USERNAME", "user"), | MQTTUser: getEnv("MQTT_USERNAME", "user"), | ||||
| MQTTPass: getEnv("MQTT_PASSWORD", "pass"), | MQTTPass: getEnv("MQTT_PASSWORD", "pass"), | ||||
| MQTTClientID: getEnv("MQTT_CLIENT_ID", "presence-detector"), | MQTTClientID: getEnv("MQTT_CLIENT_ID", "presence-detector"), | ||||
| DBPath: getEnv("DB_PATH", "/data/conf/presence/presence.db"), | |||||
| KafkaURL: getEnv("KAFKA_URL", "127.0.0.1:9092"), | KafkaURL: getEnv("KAFKA_URL", "127.0.0.1:9092"), | ||||
| ValkeyURL: getEnv("VALKEY_URL", "127.0.0.1:6379"), | |||||
| } | } | ||||
| } | } | ||||
| @@ -8,11 +8,10 @@ import ( | |||||
| "github.com/AFASystems/presence/internal/pkg/common/appcontext" | "github.com/AFASystems/presence/internal/pkg/common/appcontext" | ||||
| "github.com/AFASystems/presence/internal/pkg/model" | "github.com/AFASystems/presence/internal/pkg/model" | ||||
| "github.com/redis/go-redis/v9" | |||||
| "github.com/segmentio/kafka-go" | "github.com/segmentio/kafka-go" | ||||
| ) | ) | ||||
| func SettingsEditController(writer *kafka.Writer, appstate *appcontext.AppState, client *redis.Client, ctx context.Context) http.HandlerFunc { | |||||
| func SettingsEditController(writer *kafka.Writer, appstate *appcontext.AppState, ctx context.Context) http.HandlerFunc { | |||||
| return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
| decoder := json.NewDecoder(r.Body) | decoder := json.NewDecoder(r.Body) | ||||
| var inSettings model.SettingsVal | var inSettings model.SettingsVal | ||||
| @@ -47,7 +46,6 @@ func SettingsEditController(writer *kafka.Writer, appstate *appcontext.AppState, | |||||
| // if all is OK persist settings | // if all is OK persist settings | ||||
| appstate.UpdateSettings(inSettings) | appstate.UpdateSettings(inSettings) | ||||
| appstate.PersistSettings(client, ctx) | |||||
| w.Write([]byte("ok")) | w.Write([]byte("ok")) | ||||
| } | } | ||||
| @@ -61,19 +59,8 @@ func settingsCheck(settings model.SettingsVal) bool { | |||||
| return true | return true | ||||
| } | } | ||||
| func SettingsListController(appstate *appcontext.AppState, client *redis.Client, ctx context.Context) http.HandlerFunc { | |||||
| func SettingsListController(appstate *appcontext.AppState, ctx context.Context) http.HandlerFunc { | |||||
| return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
| v, err := client.Get(ctx, "settings").Result() | |||||
| if err == redis.Nil { | |||||
| msg := "No list found for key settings, starting empty" | |||||
| fmt.Println(msg) | |||||
| http.Error(w, "msg", 500) | |||||
| } else if err != nil { | |||||
| msg := fmt.Sprintf("Error in connecting to Redis: %v, key: settings returning empty map\n", err) | |||||
| fmt.Println(msg) | |||||
| http.Error(w, msg, 500) | |||||
| } else { | |||||
| w.Write([]byte(v)) | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,37 +2,12 @@ package service | |||||
| import ( | import ( | ||||
| "context" | "context" | ||||
| "fmt" | |||||
| "github.com/AFASystems/presence/internal/pkg/common/appcontext" | "github.com/AFASystems/presence/internal/pkg/common/appcontext" | ||||
| "github.com/AFASystems/presence/internal/pkg/model" | "github.com/AFASystems/presence/internal/pkg/model" | ||||
| "github.com/redis/go-redis/v9" | |||||
| ) | ) | ||||
| type RedisHashable interface { | |||||
| RedisHashable() (map[string]any, error) | |||||
| model.BeaconEvent | model.HTTPLocation | |||||
| } | |||||
| func persistBeaconValkey[T RedisHashable](id string, msg T, client *redis.Client, ctx context.Context) error { | |||||
| key := fmt.Sprintf("beacon:%s", id) | |||||
| hashM, err := msg.RedisHashable() | |||||
| if err != nil { | |||||
| fmt.Println("Error in converting location into hashmap for Redis insert: ", err) | |||||
| return err | |||||
| } | |||||
| if err := client.HSet(ctx, key, hashM).Err(); err != nil { | |||||
| fmt.Println("Error in persisting set in Redis key: ", key) | |||||
| return err | |||||
| } | |||||
| if err := client.SAdd(ctx, "beacons", key).Err(); err != nil { | |||||
| fmt.Println("Error in adding beacon to the beacons list for get all operation: ", err) | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func LocationToBeaconService(msg model.HTTPLocation, appState *appcontext.AppState, client *redis.Client, ctx context.Context) error { | |||||
| func LocationToBeaconService(msg model.HTTPLocation, appState *appcontext.AppState, ctx context.Context) error { | |||||
| id := msg.ID | id := msg.ID | ||||
| beacon, ok := appState.GetHTTPResult(id) | beacon, ok := appState.GetHTTPResult(id) | ||||
| if !ok { | if !ok { | ||||
| @@ -45,14 +20,11 @@ func LocationToBeaconService(msg model.HTTPLocation, appState *appcontext.AppSta | |||||
| beacon.PreviousConfidentLocation = msg.PreviousConfidentLocation | beacon.PreviousConfidentLocation = msg.PreviousConfidentLocation | ||||
| appState.UpdateHTTPResult(id, beacon) | appState.UpdateHTTPResult(id, beacon) | ||||
| } | } | ||||
| if err := persistBeaconValkey(id, msg, client, ctx); err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | return nil | ||||
| } | } | ||||
| func EventToBeaconService(msg model.BeaconEvent, appState *appcontext.AppState, client *redis.Client, ctx context.Context) error { | |||||
| func EventToBeaconService(msg model.BeaconEvent, appState *appcontext.AppState, ctx context.Context) error { | |||||
| id := msg.ID | id := msg.ID | ||||
| beacon, ok := appState.GetHTTPResult(id) | beacon, ok := appState.GetHTTPResult(id) | ||||
| if !ok { | if !ok { | ||||
| @@ -64,9 +36,6 @@ func EventToBeaconService(msg model.BeaconEvent, appState *appcontext.AppState, | |||||
| beacon.Event = msg.Event | beacon.Event = msg.Event | ||||
| appState.UpdateHTTPResult(id, beacon) | appState.UpdateHTTPResult(id, beacon) | ||||
| } | } | ||||
| if err := persistBeaconValkey(id, msg, client, ctx); err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | return nil | ||||
| } | } | ||||