This is still quite ugly and only meant to get things going for now, as a proof of concept. It is usable though, and quite fast. Signed-off-by: Yonah <contrib@gensokyo.uk>
123 lines
2.4 KiB
Go
123 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"io/fs"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"git.gensokyo.uk/yonah/monstersirenfetch"
|
|
)
|
|
|
|
const shutdownTimeout = 5 * time.Second
|
|
|
|
var (
|
|
flagVerbose bool
|
|
flagAddr string
|
|
)
|
|
|
|
func init() {
|
|
flag.BoolVar(&flagVerbose, "v", false, "Increase log verbosity")
|
|
flag.StringVar(&flagAddr, "a", ":3000",
|
|
`TCP address for the server to listen on, in the form "host:port".`)
|
|
}
|
|
|
|
var (
|
|
//go:embed all:static
|
|
staticFS embed.FS
|
|
)
|
|
|
|
func main() {
|
|
log.SetFlags(0)
|
|
log.SetPrefix("msrserve: ")
|
|
flag.Parse()
|
|
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/", http.FileServerFS(mustSub(staticFS, "static")).ServeHTTP)
|
|
|
|
mux.HandleFunc("/{$}", serveAppShell)
|
|
mux.HandleFunc("/about/{$}", serveAppShell)
|
|
mux.HandleFunc("/music/", serveAppShell)
|
|
mux.HandleFunc("/info/{$}", serveAppShell)
|
|
mux.HandleFunc("/contact/{$}", serveAppShell)
|
|
|
|
mux.HandleFunc("GET /api/", handleAPINew(mustReadJSON[*monstersirenfetch.Metadata](flagMetadataPath)))
|
|
mux.HandleFunc("/data/", handleDataNew(flagFetchDirPath))
|
|
|
|
s := http.Server{Addr: flagAddr, Handler: mux}
|
|
sig := make(chan os.Signal, 2)
|
|
go func() {
|
|
defer signal.Stop(sig)
|
|
v := <-sig
|
|
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
|
defer cancel()
|
|
log.Print(v)
|
|
if err := s.Shutdown(ctx); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
|
|
if err := s.ListenAndServe(); err != nil {
|
|
if !errors.Is(err, http.ErrServerClosed) {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func serveAppShell(w http.ResponseWriter, r *http.Request) {
|
|
http.ServeFileFS(w, r, staticFS, "static/appShell.html")
|
|
}
|
|
|
|
func writeResp(writer http.ResponseWriter, data []byte) bool {
|
|
if _, err := writer.Write(data); err != nil {
|
|
verboseln(err)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func mustMarshalJSON(v any) []byte {
|
|
if data, err := json.Marshal(v); err != nil {
|
|
log.Fatal(err)
|
|
return nil
|
|
} else {
|
|
return data
|
|
}
|
|
}
|
|
|
|
func mustReadJSON[T any](pathname string) T {
|
|
var v T
|
|
if r, err := os.OpenFile(pathname, os.O_RDONLY, 0); err != nil {
|
|
log.Fatal(err)
|
|
} else if err = json.NewDecoder(r).Decode(&v); err != nil {
|
|
log.Fatal(err)
|
|
} else if err = r.Close(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func mustSub(fsys fs.FS, dir string) fs.FS {
|
|
if sub, err := fs.Sub(fsys, dir); err != nil {
|
|
log.Fatal(err)
|
|
return nil
|
|
} else {
|
|
return sub
|
|
}
|
|
}
|
|
|
|
func verboseln(v ...any) {
|
|
if flagVerbose {
|
|
log.Println(v...)
|
|
}
|
|
}
|