depot/web/barf/frontend/frontend.go

174 lines
5.2 KiB
Go
Raw Normal View History

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"
_ "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 *sql.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 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
}
cleanedBody, err := json.Marshal(response)
if err != nil {
log.Printf("failed to remarshal PUT dataset: %v", err)
http.Error(rw, "remarshalling body failed", http.StatusBadRequest)
return
}
res, err := a.db.ExecContext(r.Context(), `UPDATE responses SET responses = ? WHERE key = ?`, string(cleanedBody), key)
if err != nil {
log.Printf("failed to update DB: %v", err)
http.Error(rw, "updating database failed", http.StatusInternalServerError)
return
}
rowCount, err := res.RowsAffected()
if err != nil {
log.Printf("determining affected rows: %v", err)
http.Error(rw, "updating database failed", http.StatusInternalServerError)
return
} else if rowCount == 0 {
log.Printf("no rows affected for key %v", key)
rw.WriteHeader(http.StatusNotFound)
fmt.Fprintf(rw, "Sorry, you need a key to access this application.\n")
return
}
rw.WriteHeader(http.StatusNoContent)
return
}
var responseJSON *string
err := a.db.QueryRowContext(r.Context(), `select responses from responses where key = ?`, key).Scan(&responseJSON)
if errors.Is(err, sql.ErrNoRows) {
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
}
var responseJSONStr string
if responseJSON == nil {
responseJSONStr = "{}"
} else {
var response dataset
if err := json.Unmarshal([]byte(*responseJSON), &response); err != nil {
log.Printf("database key %v corrupt, not valid json?: %v", key, err)
http.Error(rw, "response data corrupt!", http.StatusInternalServerError)
return
}
2024-03-12 01:54:52 +00:00
if response.CurrentPhase >= 6 {
2024-03-12 01:19:14 +00:00
// 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 {
2024-03-12 02:22:13 +00:00
log.Fatalf("parsing index template: %v", err)
2024-03-12 01:19:14 +00:00
}
db, err := sql.Open("sqlite3", *dbPath)
if err != nil {
log.Fatal(err)
}
defer db.Close()
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: %w", *samBackend, err)
}
be := httputil.NewSingleHostReverseProxy(samBackendPath)
http.Handle("/sam", be)
log.Printf("serving on %v", *serve)
log.Fatal(http.ListenAndServe(*serve, nil))
}