package main import ( "database/sql" "embed" "encoding/json" "errors" "flag" "fmt" "html/template" "io" "log" "net/http" "net/http/httputil" "net/url" "hg.lukegb.com/lukegb/depot/web/barf/frontend/barfdb" _ "github.com/mattn/go-sqlite3" ) var ( serve = flag.String("serve", ":11111", "Port number.") samBackend = flag.String("sam_backend", "http://localhost:11316", "Sam backend.") dbPath = flag.String("db_path", "./db.db", "Path to database.") ) //go:embed index.html var indexTmplBytes []byte //go:embed static var staticFS embed.FS type application struct { db *barfdb.DB indexTmpl *template.Template } type dataset struct { CurrentPhase int `json:"currentPhase"` CurrentPhaseDialog int `json:"currentPhaseDialog"` AudioEnabled bool `json:"audioEnabled"` Name string `json:"name"` DiscordUsername string `json:"discordUsername"` ReceiveEmail bool `json:"receiveEmail"` Email string `json:"email"` DateSat31August string `json:"dateSat31August"` DateSat7September string `json:"dateSat7September"` ActivityEscapeRoom string `json:"activityEscapeRoom"` ActivityPub string `json:"activityPub"` ActivityMeanGirlsTheMusical string `json:"activityMeanGirlsTheMusical"` ActivityKaraoke string `json:"activityKaraoke"` AccommodationRequired bool `json:"accommodationRequired"` TravelCosts bool `json:"travelCosts"` Misc string `json:"misc"` } func (a *application) ServeHTTP(rw http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { rw.WriteHeader(http.StatusNotFound) fmt.Fprintf(rw, "Sorry, you need a key to access this application.\n") return } key := r.URL.Path[1:] if r.Method == "PUT" { body, err := io.ReadAll(r.Body) if err != nil { log.Printf("failed to read PUT body: %v", err) http.Error(rw, "reading body failed", http.StatusInternalServerError) return } var response barfdb.Dataset if err := json.Unmarshal(body, &response); err != nil { log.Printf("failed to unmarshal PUT dataset: %v", err) http.Error(rw, "unmarshalling body failed", http.StatusBadRequest) return } if err := a.db.SaveResponseForKey(r.Context(), key, &response); errors.Is(err, barfdb.ErrNoSuchKey) { rw.WriteHeader(http.StatusNotFound) fmt.Fprintf(rw, "Sorry, you need a key to access this application.\n") return } else if err != nil { log.Printf("SaveResponseKeyFor %v: %v", key, err) http.Error(rw, "saving response failed", http.StatusInternalServerError) return } rw.WriteHeader(http.StatusNoContent) return } response, err := a.db.GetResponseForKey(r.Context(), key) if errors.Is(err, barfdb.ErrNoSuchKey) { rw.WriteHeader(http.StatusNotFound) fmt.Fprintf(rw, "Sorry, you need a key to access this application.\n") return } else if err != nil { log.Printf("getting responses: %v", err) http.Error(rw, "internal error getting responses", http.StatusInternalServerError) return } if response.CurrentPhase >= 6 { // Make sure people who have saved _completion_ start at the beginning. response.CurrentPhase = 0 response.CurrentPhaseDialog = 0 } responseJSONBytes, err := json.Marshal(response) if err != nil { log.Printf("couldn't reserialise database key %v: %v", key, err) http.Error(rw, "something went wrong with reserialisation...", http.StatusInternalServerError) return } responseJSONStr := string(responseJSONBytes) rw.Header().Set("Content-type", "text/html") a.indexTmpl.Execute(rw, template.JS(responseJSONStr)) } func main() { flag.Parse() indexTmpl, err := template.New("index").Parse(string(indexTmplBytes)) if err != nil { log.Fatalf("parsing index template: %v", err) } sdb, err := sql.Open("sqlite3", *dbPath) if err != nil { log.Fatal(err) } defer sdb.Close() db := barfdb.New(sdb) app := &application{db: db, indexTmpl: indexTmpl} http.Handle("/", app) http.Handle("/static/", http.FileServer(http.FS(staticFS))) samBackendPath, err := url.Parse(*samBackend) if err != nil { log.Fatalf("parsing -sam_backend=%q: %v", *samBackend, err) } be := httputil.NewSingleHostReverseProxy(samBackendPath) http.Handle("/sam", be) log.Printf("serving on %v", *serve) log.Fatal(http.ListenAndServe(*serve, nil)) }