227 lines
4.9 KiB
Go
227 lines
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"html/template"
|
|
"io"
|
|
"log"
|
|
"math/rand"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
var templates = map[string]*template.Template{}
|
|
|
|
func executeTemplate(name string, w io.Writer, data interface{}) {
|
|
if err := templates[name].Execute(w, data); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func wrap(inner func(http.ResponseWriter, *http.Request)) func(
|
|
http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if r.Method == http.MethodGet {
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
}
|
|
inner(w, r)
|
|
}
|
|
}
|
|
|
|
func handleLogin(w http.ResponseWriter, r *http.Request) {
|
|
redirect := r.FormValue("redirect")
|
|
if redirect == "" {
|
|
redirect = "/"
|
|
}
|
|
|
|
session := sessionGet(w, r)
|
|
if session.LoggedIn {
|
|
http.Redirect(w, r, redirect, http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
params := struct {
|
|
IncorrectPassword bool
|
|
}{}
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
// We're just going to render the template.
|
|
case http.MethodPost:
|
|
if r.FormValue("password") == db.Password {
|
|
session.LoggedIn = true
|
|
http.Redirect(w, r, redirect, http.StatusSeeOther)
|
|
return
|
|
}
|
|
params.IncorrectPassword = true
|
|
default:
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
executeTemplate("login.tmpl", w, ¶ms)
|
|
}
|
|
|
|
func handleLogout(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
session := r.Context().Value(sessionContextKey{}).(*Session)
|
|
session.LoggedIn = false
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
}
|
|
|
|
func handleContainer(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == http.MethodPost {
|
|
// TODO
|
|
}
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
allSeries := map[string]string{}
|
|
for _, s := range indexSeries {
|
|
allSeries[s.Prefix] = s.Description
|
|
}
|
|
|
|
var container *Container
|
|
children := []*Container{}
|
|
|
|
if id := ContainerId(r.FormValue("id")); id == "" {
|
|
children = indexChildren[""]
|
|
} else if c, ok := indexContainer[id]; ok {
|
|
children = c.Children()
|
|
container = c
|
|
}
|
|
|
|
params := struct {
|
|
Container *Container
|
|
Children []*Container
|
|
AllSeries map[string]string
|
|
}{
|
|
Container: container,
|
|
Children: children,
|
|
AllSeries: allSeries,
|
|
}
|
|
|
|
executeTemplate("container.tmpl", w, ¶ms)
|
|
}
|
|
|
|
func handleSeries(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == http.MethodPost {
|
|
// TODO
|
|
}
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
allSeries := map[string]string{}
|
|
for _, s := range indexSeries {
|
|
allSeries[s.Prefix] = s.Description
|
|
}
|
|
|
|
prefix := r.FormValue("prefix")
|
|
description := ""
|
|
|
|
if prefix == "" {
|
|
} else if series, ok := indexSeries[prefix]; ok {
|
|
description = series.Description
|
|
}
|
|
|
|
params := struct {
|
|
Prefix string
|
|
Description string
|
|
AllSeries map[string]string
|
|
}{
|
|
Prefix: prefix,
|
|
Description: description,
|
|
AllSeries: allSeries,
|
|
}
|
|
|
|
executeTemplate("series.tmpl", w, ¶ms)
|
|
}
|
|
|
|
func handleSearch(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
query := r.FormValue("q")
|
|
params := struct {
|
|
Query string
|
|
Series []*Series
|
|
Containers []*Container
|
|
}{
|
|
Query: query,
|
|
Series: dbSearchSeries(query),
|
|
Containers: dbSearchContainers(query),
|
|
}
|
|
|
|
executeTemplate("search.tmpl", w, ¶ms)
|
|
}
|
|
|
|
func handleLabel(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
id := r.FormValue("id")
|
|
_ = id
|
|
|
|
// TODO: See if such a container exists, print a label on the printer.
|
|
|
|
params := struct{}{}
|
|
|
|
executeTemplate("label.tmpl", w, ¶ms)
|
|
}
|
|
|
|
func main() {
|
|
// Randomize the RNG for session string generation.
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
if len(os.Args) != 3 {
|
|
log.Fatalf("Usage: %s ADDRESS DATABASE-FILE\n", os.Args[0])
|
|
}
|
|
|
|
var address string
|
|
address, dbPath = os.Args[1], os.Args[2]
|
|
|
|
// Load database.
|
|
if err := loadDatabase(); err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
// Load HTML templates from the current working directory.
|
|
m, err := filepath.Glob("*.tmpl")
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
for _, name := range m {
|
|
templates[name] = template.Must(template.ParseFiles("base.tmpl", name))
|
|
}
|
|
|
|
// TODO: Eventually we will need to load a font file for label printing.
|
|
// - The path might be part of configuration, or implicit by filename.
|
|
|
|
http.HandleFunc("/login", wrap(handleLogin))
|
|
http.HandleFunc("/logout", sessionWrap(wrap(handleLogout)))
|
|
|
|
http.HandleFunc("/", sessionWrap(wrap(handleContainer)))
|
|
http.HandleFunc("/series", sessionWrap(wrap(handleSeries)))
|
|
http.HandleFunc("/search", sessionWrap(wrap(handleSearch)))
|
|
http.HandleFunc("/label", sessionWrap(wrap(handleLabel)))
|
|
|
|
log.Fatalln(http.ListenAndServe(address, nil))
|
|
}
|