diff --git a/main.go b/main.go index 4329bfb..ff42597 100644 --- a/main.go +++ b/main.go @@ -1,17 +1,20 @@ package main import ( + "context" "embed" "encoding/json" "flag" "fmt" "io/fs" "log" - "math/rand/v2" "net/http" + "os" + "os/signal" "runtime/debug" "strconv" "sync" + "syscall" "time" "git.chandlerswift.com/chandlerswift/nau-sidewalks/sidewalk" @@ -20,16 +23,31 @@ import ( //go:embed static var embeddedFiles embed.FS -//go:embed data/sidewalks.json -var sidewalk_data []byte - var buildInfoJSON []byte func main() { devel := flag.Bool("devel", false, "Serve from ./static instead of embedded") port := flag.Int("port", 8000, "Port to listen on") + dataFile := flag.String("datafile", "", "File to read sidewalk data from (required)") flag.Parse() + if *dataFile == "" { + panic("datafile arg is required") + } + sidewalkData, err := os.ReadFile(*dataFile) + if err != nil { + panic(err) + } + + // Decode the JSON data + var sidewalks []sidewalk.Sidewalk + var sidewalksMutex sync.RWMutex + if err := json.Unmarshal(sidewalkData, &sidewalks); err != nil { + log.Fatalf("Error decoding JSON: %v", err) + } + sidewalksLen := len(sidewalks) + + // Serve static content if *devel { http.Handle("GET /", http.FileServer(http.Dir("./static"))) } else { @@ -40,17 +58,6 @@ func main() { http.Handle("GET /", http.FileServer(http.FS(frontend))) } - // Decode the JSON data - var sidewalks []sidewalk.Sidewalk - var sidewalksMutex sync.RWMutex - if err := json.Unmarshal(sidewalk_data, &sidewalks); err != nil { - log.Fatalf("Error decoding JSON: %v", err) - } - sidewalksLen := len(sidewalks) - for i := range sidewalks { - sidewalks[i].Condition = sidewalk.Condition(rand.IntN(4)) - } - initializeBuildInfoJSON() http.HandleFunc("GET /api/version", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -87,8 +94,42 @@ func main() { http.Redirect(w, r, "/", http.StatusSeeOther) }) - fmt.Printf("Serving %v sidewalks on :%v\n", &sidewalksLen, *port) - panic(http.ListenAndServe(fmt.Sprintf(":%v", *port), nil)) + server := &http.Server{Addr: fmt.Sprintf(":%v", *port)} + + go func() { + fmt.Printf("Serving %v sidewalks on :%v\n", sidewalksLen, *port) + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("HTTP server error: %v", err) + } + }() + + // Listen for OS termination signals + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, syscall.SIGTERM) + <-stop // Block until signal is received + + fmt.Println("\nShutting down server...") + + // write out JSON + sidewalksMutex.RLock() + f, err := os.Create(*dataFile) + if err != nil { + panic(err) + } + defer f.Close() + err = json.NewEncoder(f).Encode(sidewalks) + if err != nil { + panic(err) + } + sidewalksMutex.RUnlock() + + // graceful shutdown + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := server.Shutdown(ctx); err != nil { + log.Fatalf("Server forced to shutdown: %v", err) + } + } func initializeBuildInfoJSON() {