blob: 7def3c85279b347e4c0a57d7dfad15dbae026fe7 [file] [log] [blame]
Serge Bazanskia7674672021-05-30 21:09:34 +00001package main
2
3import (
radexa28fa4d2023-10-09 00:07:04 +02004 "context"
Serge Bazanskia7674672021-05-30 21:09:34 +00005 "flag"
6 "fmt"
Serge Bazanski717aad42021-07-11 16:03:43 +00007 "math/rand"
Serge Bazanskia7674672021-05-30 21:09:34 +00008 "mime"
9 "net/http"
Serge Bazanski1f062302021-06-01 22:24:34 +000010 "regexp"
Serge Bazanskia7674672021-05-30 21:09:34 +000011 "strings"
Serge Bazanski3c9092a2021-05-30 23:15:20 +000012 "sync"
Serge Bazanski717aad42021-07-11 16:03:43 +000013 "time"
Serge Bazanskia7674672021-05-30 21:09:34 +000014
Serge Bazanskia7674672021-05-30 21:09:34 +000015 "github.com/golang/glog"
16
Serge Bazanski717aad42021-07-11 16:03:43 +000017 "code.hackerspace.pl/hscloud/hswaw/site/calendar"
Serge Bazanskia7674672021-05-30 21:09:34 +000018 "code.hackerspace.pl/hscloud/hswaw/site/static"
19)
20
21var (
22 flagSitePublic string
23)
24
25type service struct {
Serge Bazanski3c9092a2021-05-30 23:15:20 +000026 // feeds is a map from atom feed name to atom feed. This is updated by a
27 // background worker.
28 feeds map[string]*atomFeed
29 // feedsMu locks the feeds field.
30 feedsMu sync.RWMutex
Serge Bazanski717aad42021-07-11 16:03:43 +000031
32 // events is a list of upcoming events, sorted so the first event is the
33 // one that will happen the soonests.
34 events []*calendar.UpcomingEvent
35 // eventsMu locks the events field.
36 eventsMu sync.RWMutex
Serge Bazanskia7674672021-05-30 21:09:34 +000037}
38
39func main() {
40 flag.StringVar(&flagSitePublic, "site_public", "0.0.0.0:8080", "Address at which to serve public HTTP requests")
41 flag.Parse()
42
Serge Bazanski717aad42021-07-11 16:03:43 +000043 rand.Seed(time.Now().UnixNano())
44
radexa28fa4d2023-10-09 00:07:04 +020045 // TODO(q3k): use sigint-interruptible context
46 ctx := context.Background()
Serge Bazanskia7674672021-05-30 21:09:34 +000047
48 s := &service{}
radexa28fa4d2023-10-09 00:07:04 +020049 go s.feedWorker(ctx)
50 go s.eventsWorker(ctx)
Serge Bazanskia7674672021-05-30 21:09:34 +000051
52 mux := http.NewServeMux()
53 s.registerHTTP(mux)
54
55 go func() {
56 glog.Infof("Listening for public HTTP at %v", flagSitePublic)
57 if err := http.ListenAndServe(flagSitePublic, mux); err != nil {
58 glog.Exit(err)
59 }
60 }()
61
radexa28fa4d2023-10-09 00:07:04 +020062 <-ctx.Done()
Serge Bazanskia7674672021-05-30 21:09:34 +000063}
64
Serge Bazanski1f062302021-06-01 22:24:34 +000065var (
66 // staticRoutes define the resolution of static file paths into assets
67 // built into //hswaw/site/static, whose names correspond to their origin
68 // within the Bazel workspace.
69 // The regexp will be matched against the normalized within the URL. If it
70 // matches, its first group/submatch will be used to format the string
71 // corresponding to this regexp, and that string will be then used to
72 // retrieve a path embedded within //hswaw/site/static:static
73 // (go_embed_data).
74 //
75 // To see paths available within that go_embed_data, you can do:
76 // $ bazel build //hswaw/site/static:static
77 // $ grep -A100 'Data =' bazel-bin/hswaw/site/static/static.go
78 staticRoutes = map[*regexp.Regexp]string{
Serge Bazanski19c8b602022-02-01 09:36:47 +000079 regexp.MustCompile(`^static/site/(.+)$`): "hswaw/site/static/%s",
80 regexp.MustCompile(`^static/leaflet/(.+)$`): "external/com_npmjs_leaflet/package/dist/%s",
81 regexp.MustCompile(`^static/gfonts/([a-z]+)/([0-9]+)\.ttf$`): "external/com_gstatic_fonts_%s_%s/file/font.ttf",
Serge Bazanski1f062302021-06-01 22:24:34 +000082 }
83)
84
85// handleHTTPStatic uses staticRoutes to serve static files embedded within
86// //hswaw/site/static.
Serge Bazanskia7674672021-05-30 21:09:34 +000087func (s *service) handleHTTPStatic(w http.ResponseWriter, r *http.Request) {
88 path := strings.TrimPrefix(r.URL.Path, "/")
Serge Bazanski1f062302021-06-01 22:24:34 +000089 for from, to := range staticRoutes {
90 match := from.FindStringSubmatch(path)
91 if match == nil {
92 continue
93 }
Serge Bazanski19c8b602022-02-01 09:36:47 +000094 var args []interface{}
95 for _, m := range match[1:] {
96 args = append(args, m)
97 }
98 if len(args) != strings.Count(to, "%s") {
99 http.NotFound(w, r)
100 return
101 }
102 to := fmt.Sprintf(to, args...)
Serge Bazanski1f062302021-06-01 22:24:34 +0000103 data, ok := static.Data[to]
104 if !ok {
105 continue
106 }
Serge Bazanskia7674672021-05-30 21:09:34 +0000107 parts := strings.Split(path, ".")
108 ext := fmt.Sprintf(".%s", parts[len(parts)-1])
109 t := mime.TypeByExtension(ext)
110 w.Header().Set("Content-Type", t)
111 w.Write(data)
Serge Bazanski1f062302021-06-01 22:24:34 +0000112 return
113
Serge Bazanskia7674672021-05-30 21:09:34 +0000114 }
Serge Bazanski1f062302021-06-01 22:24:34 +0000115 http.NotFound(w, r)
Serge Bazanskia7674672021-05-30 21:09:34 +0000116}
117
118func (s *service) registerHTTP(mux *http.ServeMux) {
119 mux.HandleFunc("/static/", s.handleHTTPStatic)
Serge Bazanski11b276d2021-07-11 23:49:55 +0000120 mux.HandleFunc("/spaceapi", s.handleSpaceAPI)
Serge Bazanski717aad42021-07-11 16:03:43 +0000121 mux.HandleFunc("/events.json", s.handleJSONEvents)
122 mux.HandleFunc("/event/", s.handleEvent)
Serge Bazanski15b5bc12022-11-06 17:04:16 +0000123 mux.HandleFunc("/robots.txt", s.handleRobotsTxt)
Serge Bazanski4d7b2f02021-05-31 22:33:51 +0000124 mux.HandleFunc("/", s.handleIndex)
Serge Bazanskia7674672021-05-30 21:09:34 +0000125}