user_home.go
1package pages
2
3import (
4 "log/slog"
5 "math"
6 "net/http"
7
8 "git.kilimanjaro.io/rtw/document"
9 "git.kilimanjaro.io/rtw/httperr"
10 "git.kilimanjaro.io/rtw/pkg/id"
11 "git.kilimanjaro.io/rtw/pkg/log"
12 "git.kilimanjaro.io/rtw/pkg/session"
13 "git.kilimanjaro.io/rtw/user"
14)
15
16func NewUserHomeHandler(userService *user.Service, docService *document.Service, errHandler *httperr.Handler, apiKey string) http.Handler {
17 return &userHome{us: userService, ds: docService, eh: errHandler, apiKey: apiKey}
18}
19
20type userHome struct {
21 us *user.Service
22 ds *document.Service
23 eh *httperr.Handler
24 apiKey string
25}
26
27type UserHomeData struct {
28 User user.User
29 StyleURL string
30 Bbox [4]float64
31 DocumentIDs []id.Key
32 Documents []document.DocumentContent
33 Waypoints [][]document.WaypointAttrs
34 IsLoggedInUser bool
35}
36
37const styleURL string = "https://api.protomaps.com/styles/v5/white/en.json?key="
38
39func (u userHome) ServeHTTP(w http.ResponseWriter, r *http.Request) {
40 uID := r.PathValue("id")
41 userID, err := id.FromCompact(uID)
42 if err != nil {
43 u.eh.Handle(http.StatusInternalServerError, w, r)
44 return
45 }
46
47 // compare to user serssion, if it exists
48 var isLoggedInUser bool
49 info, err := session.FromContext[id.Key](r.Context())
50 if err == nil && info.ID == userID {
51 isLoggedInUser = true
52 }
53 l := log.FromContext(r.Context())
54 docs, err := u.us.GetDocumentsForUser(userID)
55 if err != nil {
56 l.Error("error getting user permissions", "error", err)
57 u.eh.Handle(http.StatusInternalServerError, w, r)
58 return
59 }
60 var docIDs []id.Key
61 var docContents []document.DocumentContent
62 var allWaypoints [][]document.WaypointAttrs
63 var bbox = [4]float64{180, 90, -180, -90}
64 for _, doc := range docs {
65 docIDs = append(docIDs, id.Key(doc.ResourceID))
66 content, err := u.ds.GetContentByResourceID(id.Key(doc.ResourceID))
67 if err != nil {
68 l.Error("error getting document content", "error", err)
69 u.eh.Handle(http.StatusInternalServerError, w, r)
70 return
71 }
72 waypointNodes := document.Filter(content, func(n document.Node) bool { return n.Type == "waypoint" })
73 var waypoints []document.WaypointAttrs
74 for _, node := range waypointNodes {
75 attrs, err := document.NodeToWaypointAttrs(node)
76 if err != nil {
77 l.Error("error converting node to waypoint attrs", "error", err)
78 u.eh.Handle(http.StatusInternalServerError, w, r)
79 return
80 }
81 bbox = updateBbox(bbox, attrs.Point)
82 for _, route := range attrs.From {
83 bbox = updateBbox(bbox, route.Bbox)
84 }
85 waypoints = append(waypoints, attrs)
86 }
87 allWaypoints = append(allWaypoints, waypoints)
88 docContents = append(docContents, content)
89 }
90
91 user, err := u.us.GetUserByID(userID)
92 if err != nil {
93 l.Error("error getting user", "user_id", userID, "error", err)
94 u.eh.Handle(http.StatusInternalServerError, w, r)
95 return
96 }
97 if u.apiKey == "" {
98 l.Error("error protomaps api key not set")
99 u.eh.Handle(http.StatusInternalServerError, w, r)
100 return
101 }
102 if len(docContents) == 0 {
103 // pick a most-of-world bbox if no documents exist yet
104 bbox = [4]float64{-130, -32, 130, 62}
105 }
106 data := UserHomeData{
107 User: user,
108 StyleURL: styleURL + u.apiKey,
109 DocumentIDs: docIDs,
110 Documents: docContents,
111 Waypoints: allWaypoints,
112 Bbox: bbox,
113 IsLoggedInUser: isLoggedInUser,
114 }
115 l.Info("rendering user home page", "user", userID, "num_documents", len(data.Documents), "bbox", bbox)
116 UserHome(data).Render(r.Context(), w)
117}
118
119func updateBbox(bbox [4]float64, update []float64) [4]float64 {
120 switch len(update) {
121 case 2:
122 bbox[0] = math.Min(bbox[0], update[0])
123 bbox[1] = math.Min(bbox[1], update[1])
124 bbox[2] = math.Max(bbox[2], update[0])
125 bbox[3] = math.Max(bbox[3], update[1])
126 return bbox
127 case 4:
128 // split into recursive update, one point at a time
129 return updateBbox(updateBbox(bbox, update[0:2]), update[2:])
130 default:
131 slog.Error("unexpected bbox update length", "len", len(update))
132 return bbox
133 }
134}