| 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) |
| } |