blob: a0872d8b017e2dc7e559fd89df228d781bd08159 [file] [log] [blame]
package main
import (
"flag"
"fmt"
"math/rand"
"mime"
"net/http"
"regexp"
"strings"
"sync"
"time"
"code.hackerspace.pl/hscloud/go/mirko"
"github.com/golang/glog"
"code.hackerspace.pl/hscloud/hswaw/site/calendar"
"code.hackerspace.pl/hscloud/hswaw/site/static"
)
var (
flagSitePublic string
)
type service struct {
// feeds is a map from atom feed name to atom feed. This is updated by a
// background worker.
feeds map[string]*atomFeed
// feedsMu locks the feeds field.
feedsMu sync.RWMutex
// events is a list of upcoming events, sorted so the first event is the
// one that will happen the soonests.
events []*calendar.UpcomingEvent
// eventsMu locks the events field.
eventsMu sync.RWMutex
}
func main() {
flag.StringVar(&flagSitePublic, "site_public", "0.0.0.0:8080", "Address at which to serve public HTTP requests")
flag.Parse()
rand.Seed(time.Now().UnixNano())
mi := mirko.New()
if err := mi.Listen(); err != nil {
glog.Exitf("Listen failed: %v", err)
}
s := &service{}
go s.feedWorker(mi.Context())
go s.eventsWorker(mi.Context())
mux := http.NewServeMux()
s.registerHTTP(mux)
go func() {
glog.Infof("Listening for public HTTP at %v", flagSitePublic)
if err := http.ListenAndServe(flagSitePublic, mux); err != nil {
glog.Exit(err)
}
}()
if err := mi.Serve(); err != nil {
glog.Exitf("Serve failed: %v", err)
}
<-mi.Done()
}
var (
// staticRoutes define the resolution of static file paths into assets
// built into //hswaw/site/static, whose names correspond to their origin
// within the Bazel workspace.
// The regexp will be matched against the normalized within the URL. If it
// matches, its first group/submatch will be used to format the string
// corresponding to this regexp, and that string will be then used to
// retrieve a path embedded within //hswaw/site/static:static
// (go_embed_data).
//
// To see paths available within that go_embed_data, you can do:
// $ bazel build //hswaw/site/static:static
// $ grep -A100 'Data =' bazel-bin/hswaw/site/static/static.go
staticRoutes = map[*regexp.Regexp]string{
regexp.MustCompile(`^static/site/(.+)$`): "hswaw/site/static/%s",
regexp.MustCompile(`^static/leaflet/(.+)$`): "external/com_npmjs_leaflet/package/dist/%s",
regexp.MustCompile(`^static/gfonts/([a-z]+)/([0-9]+)\.ttf$`): "external/com_gstatic_fonts_%s_%s/file/font.ttf",
}
)
// handleHTTPStatic uses staticRoutes to serve static files embedded within
// //hswaw/site/static.
func (s *service) handleHTTPStatic(w http.ResponseWriter, r *http.Request) {
path := strings.TrimPrefix(r.URL.Path, "/")
for from, to := range staticRoutes {
match := from.FindStringSubmatch(path)
if match == nil {
continue
}
var args []interface{}
for _, m := range match[1:] {
args = append(args, m)
}
if len(args) != strings.Count(to, "%s") {
http.NotFound(w, r)
return
}
to := fmt.Sprintf(to, args...)
data, ok := static.Data[to]
if !ok {
continue
}
parts := strings.Split(path, ".")
ext := fmt.Sprintf(".%s", parts[len(parts)-1])
t := mime.TypeByExtension(ext)
w.Header().Set("Content-Type", t)
w.Write(data)
return
}
http.NotFound(w, r)
}
func (s *service) registerHTTP(mux *http.ServeMux) {
mux.HandleFunc("/static/", s.handleHTTPStatic)
mux.HandleFunc("/spaceapi", s.handleSpaceAPI)
mux.HandleFunc("/events.json", s.handleJSONEvents)
mux.HandleFunc("/event/", s.handleEvent)
mux.HandleFunc("/", s.handleIndex)
}