package web
import (
"bytes"
"fmt"
"html/template"
"log"
"net/http"
"github.com/kellegous/go/context"
"github.com/syndtr/goleveldb/leveldb"
)
// Serve a bundled asset over HTTP.
func serveAsset(w http.ResponseWriter, r *http.Request, name string) {
n, err := AssetInfo(name)
if err != nil {
http.NotFound(w, r)
return
}
a, err := Asset(name)
if err != nil {
http.NotFound(w, r)
return
}
http.ServeContent(w, r, n.Name(), n.ModTime(), bytes.NewReader(a))
}
func templateFromAssetFn(fn func() (*asset, error)) (*template.Template, error) {
a, err := fn()
if err != nil {
return nil, err
}
t := template.New(a.info.Name())
return t.Parse(string(a.bytes))
}
// The default handler responds to most requests. It is responsible for the
// shortcut redirects and for sending unmapped shortcuts to the edit page.
func getDefault(ctx *context.Context, w http.ResponseWriter, r *http.Request) {
p := parseName("/", r.URL.Path)
if p == "" {
http.Redirect(w, r, "/edit/", http.StatusTemporaryRedirect)
return
}
rt, err := ctx.Get(p)
if err == leveldb.ErrNotFound {
http.Redirect(w, r,
fmt.Sprintf("/edit/%s", cleanName(p)),
http.StatusTemporaryRedirect)
return
} else if err != nil {
log.Panic(err)
}
http.Redirect(w, r,
rt.URL,
http.StatusTemporaryRedirect)
}
func getLinks(ctx *context.Context, w http.ResponseWriter, r *http.Request) {
t, err := templateFromAssetFn(linksHtml)
if err != nil {
log.Panic(err)
}
rts, err := ctx.GetAll()
if err != nil {
log.Panic(err)
}
if err := t.Execute(w, rts); err != nil {
log.Panic(err)
}
}
// ListenAndServe sets up all web routes, binds the port and handles incoming
// web requests.
func ListenAndServe(addr string, admin bool, version string, ctx *context.Context) error {
mux := http.NewServeMux()
mux.HandleFunc("/api/url/", func(w http.ResponseWriter, r *http.Request) {
apiURL(ctx, w, r)
})
mux.HandleFunc("/api/urls/", func(w http.ResponseWriter, r *http.Request) {
apiURLs(ctx, w, r)
})
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
getDefault(ctx, w, r)
})
mux.HandleFunc("/edit/", func(w http.ResponseWriter, r *http.Request) {
p := parseName("/edit/", r.URL.Path)
// if this is a banned name, just redirect to the local URI. That'll show em.
if isBannedName(p) {
http.Redirect(w, r, fmt.Sprintf("/%s", p), http.StatusTemporaryRedirect)
return
}
serveAsset(w, r, "edit.html")
})
mux.HandleFunc("/links/", func(w http.ResponseWriter, r *http.Request) {
getLinks(ctx, w, r)
})
mux.HandleFunc("/s/", func(w http.ResponseWriter, r *http.Request) {
serveAsset(w, r, r.URL.Path[len("/s/"):])
})
mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, version)
})
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "👍")
})
// TODO(knorton): Remove the admin handler.
if admin {
mux.Handle("/admin/", &adminHandler{ctx})
}
return http.ListenAndServe(addr, mux)
}