package main
import (
"bytes"
"embed"
"fmt"
"html/template"
"log"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
)
//go:embed http/*
var httpFS embed.FS
var rootTemplate *template.Template
func serveHTTP(addr string) {
log.SetFlags(log.Lshortfile)
go signalHandler()
tmplFuncs := template.FuncMap{
"join": func(sep string, a []string) string {
return strings.Join(a, sep)
},
}
rootTemplate = template.Must(
template.New("root").Funcs(tmplFuncs).
ParseFS(httpFS, "http/index.tmpl.html"))
http.HandleFunc("GET /{$}", handleRoot)
http.HandleFunc("GET /svg", handleSVG)
http.HandleFunc("PUT /svg", handleSVG)
log.Printf("firstones %s", Version())
log.Printf("Starting HTTP server on %q", addr)
log.Fatal(http.ListenAndServe(addr, nil))
}
func signalHandler() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
s := <-c
fatalf("Received signal %v, shutting down", s)
}
func wordsFromRequest(r *http.Request) []string {
r.ParseForm()
wordsF := r.Form["words"]
// For extra convenience, the words can be provided as a single
// space-separated string.
words := []string{}
for _, wordF := range wordsF {
words = append(words, strings.Fields(wordF)...)
}
// Max number of words supported is 10.
if len(words) > 10 {
words = words[:10]
}
return words
}
func handleRoot(w http.ResponseWriter, r *http.Request) {
words := wordsFromRequest(r)
svg := ""
var svgErr error
if len(words) > 0 {
svg, svgErr = genSVG(words, r.FormValue("grid") == "1")
}
data := map[string]interface{}{
"Words": words,
// The generated HTML should be already safe for embedding.
"SVG": template.HTML(svg),
"Error": svgErr,
}
err := rootTemplate.ExecuteTemplate(w, "index.tmpl.html", data)
if err != nil {
http.Error(w, fmt.Sprintf("Error rendering template: %v", err),
http.StatusInternalServerError)
return
}
}
func genSVG(words []string, grid bool) (string, error) {
wsvg, width, height, err := wordsToSVG(words)
if err != nil {
return "", err
}
buf := &bytes.Buffer{}
buf.WriteString(string(svgHeader(width, height)))
writeDefs(buf)
if grid {
buf.WriteString(string(svgGrid(width, height)))
}
buf.WriteString(string(wsvg))
buf.WriteString("</svg>\n")
return buf.String(), nil
}
func handleSVG(w http.ResponseWriter, r *http.Request) {
words := wordsFromRequest(r)
if len(words) == 0 {
http.Error(w, "No words provided", http.StatusBadRequest)
return
}
svg, err := genSVG(words, r.FormValue("grid") == "1")
if err != nil {
http.Error(w, fmt.Sprintf("Error generating SVG: %v", err),
http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "image/svg+xml")
w.Write([]byte(svg))
}