web.go 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package main
  2. import (
  3. "github.com/lamperi/e4bot/spotify"
  4. "html/template"
  5. "log"
  6. "net/http"
  7. "strings"
  8. )
  9. var (
  10. cachedTemplates = template.Must(template.ParseFiles("web/index.html"))
  11. spotifyClient *spotify.SpotifyClient
  12. )
  13. func indexHandler(w http.ResponseWriter, r *http.Request) {
  14. if r.URL.Path != "/" {
  15. http.Error(w, "Not Found", http.StatusNotFound)
  16. return
  17. }
  18. data := struct {
  19. Username string
  20. Songs []*struct {
  21. RoundID int
  22. RoundName string
  23. Title string
  24. Artist string
  25. URL string
  26. Sync bool
  27. }
  28. }{
  29. "",
  30. make([]*struct {
  31. RoundID int
  32. RoundName string
  33. Title string
  34. Artist string
  35. URL string
  36. Sync bool
  37. }, 0),
  38. }
  39. session, err := getSession(r)
  40. if session != nil {
  41. query := `
  42. SELECT r.id, r.section, e.artist, e.title, e.spotify_url, e.synced
  43. FROM public.round r
  44. LEFT JOIN public.entry e ON r.id = e.round_id
  45. LEFT JOIN public."user" u ON u.id = e.user_id AND lower(u.username) = lower($1)
  46. ORDER BY r.start ASC`
  47. rows, err := getDb().Query(query, session.username)
  48. if err != nil {
  49. log.Println("Error while executing query", err)
  50. http.Error(w, err.Error(), http.StatusInternalServerError)
  51. return
  52. }
  53. defer rows.Close()
  54. for i := 0; rows.Next(); i++ {
  55. row := &struct {
  56. RoundID int
  57. RoundName string
  58. Title string
  59. Artist string
  60. URL string
  61. Sync bool
  62. }{}
  63. data.Songs = append(data.Songs, row)
  64. var (
  65. artist, title, url *string
  66. sync *bool
  67. )
  68. err = rows.Scan(&data.Songs[i].RoundID, &data.Songs[i].RoundName, &artist, &title, &url, &sync)
  69. if err != nil {
  70. log.Println("Error while scanning cursor", err)
  71. http.Error(w, err.Error(), http.StatusInternalServerError)
  72. return
  73. }
  74. if artist != nil {
  75. row.Artist = *artist
  76. }
  77. if title != nil {
  78. row.Title = *title
  79. }
  80. if url != nil {
  81. row.URL = *url
  82. }
  83. if sync != nil {
  84. row.Sync = *sync
  85. }
  86. }
  87. data.Username = session.username
  88. }
  89. var templates = cachedTemplates
  90. if true {
  91. templates = template.Must(template.ParseFiles("web/index.html"))
  92. }
  93. err = templates.ExecuteTemplate(w, "index.html", data)
  94. if err != nil {
  95. log.Println("Error while rendering template", err)
  96. http.Error(w, err.Error(), http.StatusInternalServerError)
  97. }
  98. }
  99. func getTrackID(url string) string {
  100. const externalURLPrefix = "https://open.spotify.com/track/"
  101. const trackURIPrefix = "spotify:track:"
  102. if strings.HasPrefix(url, externalURLPrefix) {
  103. return strings.Split(url, externalURLPrefix)[1]
  104. } else if strings.HasPrefix(url, trackURIPrefix) {
  105. return strings.Split(url, trackURIPrefix)[1]
  106. }
  107. return url
  108. }
  109. func updateHandler(w http.ResponseWriter, r *http.Request) {
  110. if r.URL.Path != "/update" {
  111. http.Error(w, "Forbidden", http.StatusForbidden)
  112. return
  113. }
  114. session, err := getSession(r)
  115. if session == nil {
  116. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  117. return
  118. }
  119. err = r.ParseForm()
  120. if err != nil {
  121. http.Error(w, err.Error(), http.StatusBadRequest)
  122. return
  123. }
  124. round := r.Form.Get("round")
  125. artist := r.Form.Get("artist")
  126. title := r.Form.Get("title")
  127. url := r.Form.Get("url")
  128. if artist == "" && title == "" && url != "" {
  129. log.Println("Resolving Spotify URL")
  130. trackID := getTrackID(url)
  131. err := spotifyClient.Authenticate()
  132. if err != nil {
  133. http.Error(w, err.Error(), http.StatusInternalServerError)
  134. return
  135. }
  136. track, err := spotifyClient.GetTrack(trackID)
  137. if err != nil {
  138. http.Error(w, err.Error(), http.StatusInternalServerError)
  139. return
  140. }
  141. log.Printf("Found Track with name %v and artists %v\n", track.Name, track.Artists)
  142. for index, trackArtist := range track.Artists {
  143. if index == 0 {
  144. artist = trackArtist.Name
  145. } else if index == 1 {
  146. artist = artist + " feat. " + trackArtist.Name
  147. } else {
  148. artist = artist + ", " + trackArtist.Name
  149. }
  150. }
  151. title = track.Name
  152. url = track.ExternalUrls.Spotify
  153. }
  154. query := `
  155. INSERT INTO public.entry
  156. SELECT id, $2, $3, $4, $5, false
  157. FROM public."user" u
  158. WHERE lower(u.username) = lower($1)
  159. ON CONFLICT (user_id, round_id) DO UPDATE SET artist = EXCLUDED.artist, title = EXCLUDED.title, spotify_url = EXCLUDED.spotify_url, synced = EXCLUDED.synced`
  160. res, err := getDb().Exec(query, session.username, round, artist, title, url)
  161. if err != nil {
  162. http.Error(w, err.Error(), http.StatusInternalServerError)
  163. return
  164. }
  165. affected, err := res.RowsAffected()
  166. if err != nil {
  167. http.Error(w, err.Error(), http.StatusInternalServerError)
  168. return
  169. }
  170. if affected != 1 {
  171. http.Error(w, err.Error(), http.StatusInternalServerError)
  172. return
  173. }
  174. log.Printf("Updated song for round %s, artist: %s, title: %s, URL: %s",
  175. round, artist, title, url)
  176. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  177. }
  178. func loginHandler(w http.ResponseWriter, r *http.Request) {
  179. err := r.ParseForm()
  180. if err != nil {
  181. http.Error(w, err.Error(), http.StatusBadRequest)
  182. return
  183. }
  184. username := r.Form.Get("username")
  185. password := r.Form.Get("password")
  186. remember := r.Form.Get("remember")
  187. longer := remember == "remember-me"
  188. cookie, err := tryLogin(username, password, longer)
  189. if err != nil {
  190. log.Println("Error while trying to login", err.Error())
  191. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  192. return
  193. }
  194. http.SetCookie(w, &cookie)
  195. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  196. }
  197. func webStart(listenAddr string, spot *spotify.SpotifyClient) {
  198. spotifyClient = spot
  199. mux := http.NewServeMux()
  200. fs := http.FileServer(http.Dir("web"))
  201. mux.Handle("/css/", fs)
  202. mux.Handle("/fonts/", fs)
  203. mux.Handle("/js/", fs)
  204. mux.Handle("/favicon.ico", fs)
  205. mux.HandleFunc("/", indexHandler)
  206. mux.HandleFunc("/update", updateHandler)
  207. mux.HandleFunc("/login", loginHandler)
  208. http.ListenAndServe(listenAddr, mux)
  209. }