Преглед изворни кода

feat: add websocket and dockerfile to build location

master
Blaz Smehov пре 3 дана
родитељ
комит
d284f48496
4 измењених фајлова са 102 додато и 2 уклоњено
  1. +17
    -0
      build/package/Dockerfile.location
  2. +76
    -1
      cmd/server/main.go
  3. +6
    -0
      internal/pkg/common/appcontext/context.go
  4. +3
    -1
      internal/pkg/controller/beacons_controller.go

+ 17
- 0
build/package/Dockerfile.location Прегледај датотеку

@@ -0,0 +1,17 @@
# syntax=docker/dockerfile:1

FROM golang:1.24.0 AS builder
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o location ./cmd/location

FROM alpine:latest
RUN apk add --no-cache ca-certificates
WORKDIR /app
COPY --from=builder /app/location .

ENTRYPOINT ["./location"]

+ 76
- 1
cmd/server/main.go Прегледај датотеку

@@ -2,11 +2,14 @@ package main

import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os/signal"
"sync"
"syscall"
"time"

"github.com/AFASystems/presence/internal/pkg/common/appcontext"
"github.com/AFASystems/presence/internal/pkg/config"
@@ -16,8 +19,14 @@ import (
"github.com/AFASystems/presence/internal/pkg/service"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}

var wg sync.WaitGroup

func main() {
@@ -51,7 +60,7 @@ func main() {
r := mux.NewRouter()

// For now just add beacon DELETE / GET / POST / PUT methods
r.HandleFunc("/api/beacons/{beacon_id}", controller.BeaconsDeleteController(writer, ctx)).Methods("DELETE")
r.HandleFunc("/api/beacons/{beacon_id}", controller.BeaconsDeleteController(writer, ctx, appState)).Methods("DELETE")
r.HandleFunc("/api/beacons", controller.BeaconsListController(appState)).Methods("GET")
r.HandleFunc("/api/beacons/{beacon_id}", controller.BeaconsListSingleController(appState)).Methods("GET")
r.HandleFunc("/api/beacons", controller.BeaconsAddController(writer, ctx)).Methods("POST")
@@ -60,6 +69,8 @@ func main() {
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/beacons/ws", serveWs(appState, ctx))

http.ListenAndServe(cfg.HTTPAddr, handlers.CORS(originsOk, headersOk, methodsOk)(r))

eventLoop:
@@ -87,3 +98,67 @@ eventLoop:
fmt.Println("All kafka clients shutdown, starting shutdown of valkey client")
appState.CleanValkeyClient()
}

func serveWs(appstate *appcontext.AppState, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
if _, ok := err.(websocket.HandshakeError); !ok {
log.Println(err)
}
return
}
wg.Add(1)
go writer(ws, appstate, ctx)
reader(ws)
}
}

func writer(ws *websocket.Conn, appstate *appcontext.AppState, ctx context.Context) {
pingTicker := time.NewTicker((60 * 9) / 10 * time.Second)
beaconTicker := time.NewTicker(2 * time.Second)
defer func() {
pingTicker.Stop()
beaconTicker.Stop()
ws.Close()
wg.Done()
}()
for {
select {
case <-ctx.Done():
log.Println("WebSocket writer received shutdown signal.")
ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
return
case <-beaconTicker.C:
beacons := appstate.GetAllBeacons()
js, err := json.Marshal(beacons)
if err != nil {
js = []byte("error")
}

ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
return
}
case <-pingTicker.C:
ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
return
}
}
}
}

func reader(ws *websocket.Conn) {
defer ws.Close()
ws.SetReadLimit(512)
ws.SetReadDeadline(time.Now().Add((60 * 9) / 10 * time.Second))
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add((60 * 9) / 10 * time.Second)); return nil })
for {
_, _, err := ws.ReadMessage()
if err != nil {
break
}
}
}

+ 6
- 0
internal/pkg/common/appcontext/context.go Прегледај датотеку

@@ -167,6 +167,12 @@ func (m *AppState) RemoveBeaconFromLookup(id string) {
delete(m.beaconsLookup, id)
}

func (m *AppState) RemoveBeacon(id string) {
m.beacons.Lock.Lock()
delete(m.beacons.Beacons, id)
m.beacons.Lock.Unlock()
}

// BeaconExists checks if a beacon exists in the lookup
func (m *AppState) BeaconExists(id string) bool {
_, exists := m.beaconsLookup[id]


+ 3
- 1
internal/pkg/controller/beacons_controller.go Прегледај датотеку

@@ -59,7 +59,7 @@ func sendKafkaMessage(writer *kafka.Writer, value *model.ApiUpdate, ctx context.
return nil
}

func BeaconsDeleteController(writer *kafka.Writer, ctx context.Context) http.HandlerFunc {
func BeaconsDeleteController(writer *kafka.Writer, ctx context.Context, appstate *appcontext.AppState) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
beaconId := vars["beacon_id"]
@@ -76,6 +76,8 @@ func BeaconsDeleteController(writer *kafka.Writer, ctx context.Context) http.Han
return
}

// If message is succesfully sent delete the beacon from the list
appstate.RemoveBeacon(beaconId)
w.Write([]byte("ok"))
}
}


Loading…
Откажи
Сачувај