web/barf/frontend: init

This commit is contained in:
Luke Granger-Brown 2024-03-12 01:19:14 +00:00
parent 6522ddba8c
commit 2f1b1a736f
23 changed files with 1645 additions and 0 deletions

View file

@ -3,4 +3,5 @@ go 1.21.7
use (
./go
./web/barf/sapi
./web/barf/frontend
)

View file

@ -6,4 +6,5 @@
{
sapi = import ./sapi args;
frontend = import ./frontend args;
}

View file

@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2024 Luke Granger-Brown <depot@lukegb.com>
#
# SPDX-License-Identifier: Apache-2.0
{ pkgs, lib, ... }:
pkgs.buildGoModule {
name = "barf-fe";
src = lib.sourceByRegex ./. [".*\.go$" "go.mod" "go.sum" "static" "static/clipit" ".*/.*\.webm" ".*/.*\.png" ".*/.*\.wav" ".*/.*\.svg" ".*\.html"];
vendorHash = "sha256:0w1k1ykga70af3643lky701kf27pfmgc3lhznfq1v32ww365w57f";
}

View file

@ -0,0 +1,173 @@
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
}
if response.CurrentPhase >= 5 {
// 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: %w", err)
}
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))
}

5
web/barf/frontend/go.mod Normal file
View file

@ -0,0 +1,5 @@
module hg.lukegb.com/lukegb/depot/web/barf/frontend
go 1.21.7
require github.com/mattn/go-sqlite3 v1.14.22

2
web/barf/frontend/go.sum Normal file
View file

@ -0,0 +1,2 @@
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=

1435
web/barf/frontend/index.html Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

After

(image error) Size: 679 B

Binary file not shown.

Binary file not shown.

After

(image error) Size: 1.9 KiB

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32"
height="32"
viewBox="0 0 32 32"
version="1.1"
id="svg1"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><g
id="layer1"><path
style="fill:#000000;stroke-width:3.62827"
d="m 20.503931,19.512586 -9.514108,9.29028 -4.1834316,-4.622637 -4.1834312,-4.622641 v -4.162206 -4.16221 H 4.6153319 6.6077 l 2.9721359,2.972136 2.9721371,2.972136 7.001463,-6.707827 7.001464,-6.7078276 h 1.731571 1.731572 v 3.2302586 3.230259 z"
id="path1" /></g></svg>

After

(image error) Size: 690 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

(image error) Size: 725 B

Binary file not shown.

After

(image error) Size: 2.3 KiB

Binary file not shown.

After

(image error) Size: 1.8 KiB

Binary file not shown.

After

(image error) Size: 2.1 KiB

Binary file not shown.

After

(image error) Size: 582 B

Binary file not shown.

After

(image error) Size: 2.4 KiB