2024-03-12 01:19:14 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"embed"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httputil"
|
|
|
|
"net/url"
|
|
|
|
|
2024-11-16 15:30:41 +00:00
|
|
|
"git.lukegb.com/lukegb/depot/web/barf/frontend/barfdb"
|
2024-03-27 00:36:06 +00:00
|
|
|
|
2024-03-12 01:19:14 +00:00
|
|
|
_ "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 {
|
2024-03-27 00:36:06 +00:00
|
|
|
db *barfdb.DB
|
2024-03-12 01:19:14 +00:00
|
|
|
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
|
|
|
|
}
|
2024-03-27 00:36:06 +00:00
|
|
|
var response barfdb.Dataset
|
2024-03-12 01:19:14 +00:00
|
|
|
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
|
|
|
|
}
|
2024-03-27 00:36:06 +00:00
|
|
|
if err := a.db.SaveResponseForKey(r.Context(), key, &response); errors.Is(err, barfdb.ErrNoSuchKey) {
|
2024-03-12 01:19:14 +00:00
|
|
|
rw.WriteHeader(http.StatusNotFound)
|
|
|
|
fmt.Fprintf(rw, "Sorry, you need a key to access this application.\n")
|
|
|
|
return
|
2024-03-27 00:36:06 +00:00
|
|
|
} else if err != nil {
|
|
|
|
log.Printf("SaveResponseKeyFor %v: %v", key, err)
|
|
|
|
http.Error(rw, "saving response failed", http.StatusInternalServerError)
|
|
|
|
return
|
2024-03-12 01:19:14 +00:00
|
|
|
}
|
|
|
|
rw.WriteHeader(http.StatusNoContent)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-03-27 00:36:06 +00:00
|
|
|
response, err := a.db.GetResponseForKey(r.Context(), key)
|
|
|
|
if errors.Is(err, barfdb.ErrNoSuchKey) {
|
2024-03-12 01:19:14 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-03-27 00:36:06 +00:00
|
|
|
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
|
2024-03-12 01:19:14 +00:00
|
|
|
}
|
2024-03-27 00:36:06 +00:00
|
|
|
responseJSONStr := string(responseJSONBytes)
|
2024-03-12 01:19:14 +00:00
|
|
|
|
|
|
|
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 {
|
2024-03-12 02:22:13 +00:00
|
|
|
log.Fatalf("parsing index template: %v", err)
|
2024-03-12 01:19:14 +00:00
|
|
|
}
|
|
|
|
|
2024-03-27 00:36:06 +00:00
|
|
|
sdb, err := sql.Open("sqlite3", *dbPath)
|
2024-03-12 01:19:14 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2024-03-27 00:36:06 +00:00
|
|
|
defer sdb.Close()
|
|
|
|
db := barfdb.New(sdb)
|
2024-03-12 01:19:14 +00:00
|
|
|
|
|
|
|
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 {
|
2024-03-27 00:36:06 +00:00
|
|
|
log.Fatalf("parsing -sam_backend=%q: %v", *samBackend, err)
|
2024-03-12 01:19:14 +00:00
|
|
|
}
|
|
|
|
be := httputil.NewSingleHostReverseProxy(samBackendPath)
|
|
|
|
http.Handle("/sam", be)
|
|
|
|
|
|
|
|
log.Printf("serving on %v", *serve)
|
|
|
|
log.Fatal(http.ListenAndServe(*serve, nil))
|
|
|
|
}
|