package routes import ( "bytes" "embed" "fmt" "html/template" "log" "net/http" "strconv" "strings" "sunshine.industries/some-automoderation/rooms" "sunshine.industries/some-automoderation/sessions" ) const roomPath = "/room/" const raiseHandPath = "/rooms/raise/" const subscribeRoomPath = "/rooms/subscribe" // registering all routes for page and logic of /room/:roomName func registerPageRoutes( templateFs *embed.FS, sessionSM sessions.SessionManagement, roomsM rooms.RoomManager, ) { http.Handle(roomPath, // ending in / captures all following path sections, i.e room name http.StripPrefix(roomPath, roomPageRoute(templateFs, roomsM, sessionSM))) http.Handle(raiseHandPath, // ending in / captures all following path sections, i.e gesture num authedPageMiddleware( sessionSM, http.StripPrefix(raiseHandPath, raiseGestureHandRoute(roomsM)))) http.Handle("/rooms/releaseHand", authedPageMiddleware(sessionSM, releaseHandRoute(roomsM))) http.Handle(subscribeRoomPath, authedPageMiddleware( sessionSM, http.StripPrefix(subscribeRoomPath, streamingRoomStates(templateFs, roomsM)))) http.HandleFunc("/rooms/preview-templates", roomTemplatesPreview(templateFs)) } func streamingRoomStates( templateFs *embed.FS, roomsM rooms.RoomManager, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { r.ParseForm() roomName := r.FormValue("roomName") defer log.Printf("/rooms/subscribe/%s stream ended\n", roomName) session, found := getContextSession(r.Context()) if !found { log.Printf("/rooms/raiseGesture session not found, should be impossible") // TODO return error i guess return } if session.RoomId != roomName { // not authorized log.Printf("/rooms/streamingRoom got unauth with session.RoomId (%s) != roomName (%s)", session.RoomId, roomName) w.WriteHeader(http.StatusUnauthorized) return } log.Printf("Starting stream for room %s for %d\n", roomName, session.PersonId) w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") w.Header().Set("Content-Type", "text/event-stream") templFile := "templates/room.gohtml" tableTemplates := "templates/tableTemplates.gohtml" tmpl := template.Must( template.New("").ParseFS(templateFs, tableTemplates, templFile)) roomStream := roomsM.Subscribe(r.Context(), roomName) for room := range roomStream { // log.Printf("/rooms/streamingRoom iterating with %+v", room) fmt.Fprint(w, "data: ") var buffer bytes.Buffer roomTemplateData := roomTableData{ Room: &room, currentPerson: session.PersonId, } err := tmpl.ExecuteTemplate(&buffer, "simpleRoomShow", &roomTemplateData) if err != nil { log.Printf("/rooms/subscribe/%s got error on template %s", roomName, err) } templateStr := buffer.String() templateLine := strings.ReplaceAll(templateStr, "\n", "") fmt.Fprint(w, templateLine) fmt.Fprint(w, "\n\n") w.(http.Flusher).Flush() } } } // if currently speaking? i guess first lower the hand and then raise new // TODO should return control for raised state func raiseGestureHandRoute( roomsM rooms.RoomManager, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { gestureInd, err := strconv.Atoi(r.URL.Path) selectedGesture, found := rooms.GestureFromInt(gestureInd) if err != nil || !found { log.Printf("/rooms/raiseGesture error %s gettin hand symbol index from path %s\n", err, r.URL.Path) return } log.Printf("/rooms/raiseGesture successfully got gesture %d : %s", selectedGesture, selectedGesture.String()) session, found := getContextSession(r.Context()) if !found { log.Printf("/rooms/raiseGesture session not found, should be impossible") // TODO return error i guess return } err = roomsM.Update(r.Context(), session.RoomId, func(fromRoom rooms.Room) (toRoom rooms.Room) { toRoom = fromRoom.RaiseHand(session.PersonId, selectedGesture) return toRoom }) if err != nil { log.Printf("/rooms/raiseGesture error saving hand: %s\n", err) return // TODO return error i guess } tableTemplates := "templates/room.gohtml" tmpl := template.Must( template.New("").ParseFS(templateFs, tableTemplates)) var gesturesData []GestureData for _, gesture := range rooms.GesturesHighToLow { gesturesData = append(gesturesData, GestureData{ Url: fmt.Sprintf("%s%d", raiseHandPath, gesture), Gesture: gesture, IsSelected: selectedGesture == gesture, }) } err = tmpl.ExecuteTemplate(w, "controls", &gesturesData) if err != nil { log.Printf("/rooms/releaseHand error saving hand: %s\n", err) return // TODO return error i guess } } } // TODO should return lowered control for passed hand gesture, i guess optional func releaseHandRoute( roomsM rooms.RoomManager, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { session, found := getContextSession(r.Context()) if !found { log.Printf("/rooms/releaseHand session not found, should be impossible") // TODO return error i guess return } err := roomsM.Update(r.Context(), session.RoomId, func(fromRoom rooms.Room) (toRoom rooms.Room) { toRoom = fromRoom toRoom.ReleaseHand(session.PersonId) return toRoom }) if err != nil { log.Printf("/rooms/releaseHand error saving hand: %s\n", err) return // TODO return error i guess } tableTemplates := "templates/room.gohtml" tmpl := template.Must( template.New("").ParseFS(templateFs, tableTemplates)) var gesturesData []GestureData for _, gesture := range rooms.GesturesHighToLow { gesturesData = append(gesturesData, GestureData{ Url: fmt.Sprintf("%s%d", raiseHandPath, gesture), Gesture: gesture, IsSelected: false, }) } err = tmpl.ExecuteTemplate(w, "controls", &gesturesData) if err != nil { log.Printf("/rooms/releaseHand error saving hand: %s\n", err) return // TODO return error i guess } } } func roomPageRoute( templateFs *embed.FS, roomsM rooms.RoomManager, sessionSM sessions.SessionManagement, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { roomName := r.URL.Path if roomName == "" { log.Printf("access to empty room") // TODO return error i suppose w.Header().Add("HX-Redirect", "/") return } // check session, session, err := getRequestSession(r, sessionSM) room, found, err := roomsM.Get(roomName) if err != nil || session.RoomId != roomName { log.Printf("not authed with session %+v | error %s, but for wrong room, trying to access %s", session, err, roomName) renderLoginPage(w, roomName, found) return } if err != nil || !found { log.Printf("/room room for name %s not found or err: %s / found %t", roomName, err, found) // TODO here should be append to error place w.Header().Add("HX-Redirect", "/") return } // now we should have a session for this specific room fmt.Printf("all checks for room %s passed with %+v", roomName, session) templFile := "templates/room.gohtml" baseFile := "templates/base.gohtml" tableTemplates := "templates/tableTemplates.gohtml" tmpl := template.Must( template.New("").ParseFS(templateFs, tableTemplates, templFile, baseFile)) var gesturesData []GestureData selectedGesture, isSelected := room.ParticipantHands[session.PersonId] for _, gesture := range rooms.GesturesHighToLow { gesturesData = append(gesturesData, GestureData{ Url: fmt.Sprintf("%s%d", raiseHandPath, gesture), Gesture: gesture, IsSelected: isSelected && selectedGesture == gesture, }) } contentData := struct { Room *roomTableData Gestures []GestureData }{ Room: &roomTableData{ Room: &room, currentPerson: session.PersonId, }, Gestures: gesturesData, } data := pageData{ Base: baseData{ Title: "room-lala-from-base", }, Content: contentData, Header: headerData{ Title: room.Name, }, } err = tmpl.ExecuteTemplate(w, "full-page", data) if err != nil { log.Printf("/room/%s my error in executing template, huh\n %s", roomName, err) } } } type GestureData struct { Url string Gesture rooms.HandGesture IsSelected bool }