package main
import (
"bytes"
"fmt"
"html/template"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
)
var savedName = "chant.json"
// ErrorHandler handles any errors that occur in the other endpoints
// and nicely displays them to the user
type ErrorHandler func(http.ResponseWriter, *http.Request) error
var templates = template.Must(template.ParseFiles("index.html", "error.html"))
// Page holds the link to the generated PDF, if it exists
type Page struct {
URL string
Saved
}
// ErrorPage holds an error message to show to the user
type ErrorPage struct {
Message string
}
func (fn ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
log.Printf("error: %v\n", err)
templates.ExecuteTemplate(w, "error.html", ErrorPage{err.Error()})
}
}
// ChantHandler serves the finished PDFs of chants
func ChantHandler(w http.ResponseWriter, r *http.Request) {
chantID := path.Base(r.URL.Path)
url := fmt.Sprintf("/tmp/chant%s/psalm.pdf", chantID)
http.ServeFile(w, r, url)
}
// GenerateHandler handles incoming requests to the generate endpoint
func GenerateHandler(w http.ResponseWriter, r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
var buf bytes.Buffer
file, header, err := r.FormFile("score")
if err != nil {
return err
}
dir, err := ioutil.TempDir("", "chant")
if err != nil {
return err
}
stylePath := filepath.Join(dir, "psalm.sty")
style, err := ioutil.ReadFile("psalm.sty")
if err != nil {
return err
}
err = ioutil.WriteFile(stylePath, style, 0644)
if err != nil {
return err
}
io.Copy(&buf, file)
chantPath := filepath.Join(dir, "chant.png")
err = ioutil.WriteFile(chantPath, buf.Bytes(), 0644)
if err != nil {
return err
}
buf.Reset()
c := Chant{
Verses: ParseInput(r.FormValue("chant")),
Title: r.FormValue("title"),
Subtitle: r.FormValue("subtitle"),
ScorePath: chantPath,
PointSize: 12,
}
texPath := filepath.Join(dir, "psalm.tex")
texFile, err := os.OpenFile(texPath, os.O_CREATE|os.O_RDWR, 0644)
defer texFile.Close()
if err != nil {
return err
}
err = WriteTexForChant(texFile, &c)
if err != nil {
return err
}
cmd := exec.Command("pdflatex", texPath)
cmd.Stdout = &buf
cmd.Stderr = &buf
startDir, err := os.Getwd()
if err != nil {
return err
}
os.Chdir(dir)
err = cmd.Run()
if err != nil {
log.Printf("pdflatex error: %v", string(buf.Bytes()))
return fmt.Errorf("pdflatex error: %v", err)
}
os.Chdir(startDir)
relativeURL := "/"
u, err := url.Parse(relativeURL)
if err != nil {
return err
}
savePath := filepath.Join(dir, savedName)
saveFile, err := os.OpenFile(savePath, os.O_CREATE|os.O_RDWR, 0644)
defer saveFile.Close()
if err != nil {
return err
}
err = SaveInput(
saveFile, r.FormValue("title"), r.FormValue("subtitle"),
r.FormValue("chant"), header.Filename, chantPath)
if err != nil {
return err
}
queryString := u.Query()
queryString.Set("id", dir[len("/tmp/chant"):])
u.RawQuery = queryString.Encode()
baseURL := "/"
base, err := url.Parse(baseURL)
if err != nil {
return err
}
http.Redirect(w, r, fmt.Sprint(base.ResolveReference(u)), http.StatusFound)
return nil
}
// IndexHandler handles incoming requests to the generate endpoint
func IndexHandler(w http.ResponseWriter, r *http.Request) error {
chant := r.URL.Query().Get("id")
if chant != "" {
chantDir := fmt.Sprintf("/tmp/chant%s", chant)
chantURL := fmt.Sprintf("/chant/%s", chant)
savedPath := filepath.Join(chantDir, savedName)
savedFile, err := os.Open(savedPath)
defer savedFile.Close()
if err != nil {
return err
}
saved := LoadSaved(savedFile)
return templates.ExecuteTemplate(w, "index.html", Page{
URL: chantURL,
Saved: saved,
})
}
return templates.ExecuteTemplate(w, "index.html", Page{})
}