12 listen := flag.String("listen", ":80", "addr:port or :port to listen on")
15 log.Printf("starting server at %s", *listen)
16 log.Fatal(http.ListenAndServe(*listen, stack(logger, apiOrAssets)))
19 <-(chan struct{})(nil)
22 type middleware func(http.Handler) http.Handler
24 var notFound = http.NotFoundHandler()
26 // returns a handler that implements a stack of middlewares.
27 func stack(m ...middleware) http.Handler {
31 return m[0](stack(m[1:]...))
35 func logger(next http.Handler) http.Handler {
36 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
39 log.Printf("%.6f %q %q %q", time.Since(t).Seconds(), r.RemoteAddr, r.Method, r.URL.Path)
43 // dispatches /api/ to the API stack, everything else to the static
45 func apiOrAssets(next http.Handler) http.Handler {
46 mux := http.NewServeMux()
47 mux.Handle("/api/", stack(apiHeaders, apiRoutes))
48 mux.Handle("/", http.FileServer(assetFS()))
52 // adds response headers suitable for API responses
53 func apiHeaders(next http.Handler) http.Handler {
54 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
55 w.Header().Set("Content-Type", "application/json")
60 // dispatches API routes
61 func apiRoutes(http.Handler) http.Handler {
62 mux := http.NewServeMux()
63 mux.HandleFunc("/api/ping", func(w http.ResponseWriter, r *http.Request) {
64 json.NewEncoder(w).Encode(map[string]interface{}{"time": time.Now().UTC()})
66 mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
67 w.WriteHeader(http.StatusNotFound)
68 json.NewEncoder(w).Encode(map[string]string{"error": "not found"})