Add account muting and blocking
[bloat] / service / transport.go
1 package service
2
3 import (
4         "context"
5         "encoding/json"
6         "io"
7         "mime/multipart"
8         "net/http"
9         "strconv"
10         "time"
11
12         "bloat/model"
13
14         "github.com/gorilla/mux"
15 )
16
17 func newClient(w io.Writer) *model.Client {
18         return &model.Client{
19                 Writer: w,
20         }
21 }
22
23 func newCtxWithSesion(req *http.Request) context.Context {
24         ctx := context.Background()
25         sessionID, err := req.Cookie("session_id")
26         if err != nil {
27                 return ctx
28         }
29         return context.WithValue(ctx, "session_id", sessionID.Value)
30 }
31
32 func newCtxWithSesionCSRF(req *http.Request, csrfToken string) context.Context {
33         ctx := newCtxWithSesion(req)
34         return context.WithValue(ctx, "csrf_token", csrfToken)
35 }
36
37 func getMultipartFormValue(mf *multipart.Form, key string) (val string) {
38         vals, ok := mf.Value[key]
39         if !ok {
40                 return ""
41         }
42         if len(vals) < 1 {
43                 return ""
44         }
45         return vals[0]
46 }
47
48 func serveJson(w io.Writer, data interface{}) (err error) {
49         var d = make(map[string]interface{})
50         d["data"] = data
51         return json.NewEncoder(w).Encode(d)
52 }
53
54 func serveJsonError(w http.ResponseWriter, err error) {
55         var d = make(map[string]interface{})
56         d["error"] = err.Error()
57         w.WriteHeader(http.StatusInternalServerError)
58         json.NewEncoder(w).Encode(d)
59         return
60 }
61
62 func NewHandler(s Service, staticDir string) http.Handler {
63         r := mux.NewRouter()
64
65         rootPage := func(w http.ResponseWriter, req *http.Request) {
66                 sessionID, _ := req.Cookie("session_id")
67
68                 location := "/signin"
69                 if sessionID != nil && len(sessionID.Value) > 0 {
70                         location = "/timeline/home"
71                 }
72
73                 w.Header().Add("Location", location)
74                 w.WriteHeader(http.StatusFound)
75         }
76
77         signinPage := func(w http.ResponseWriter, req *http.Request) {
78                 c := newClient(w)
79                 ctx := context.Background()
80                 err := s.ServeSigninPage(ctx, c)
81                 if err != nil {
82                         w.WriteHeader(http.StatusInternalServerError)
83                         s.ServeErrorPage(ctx, c, err)
84                         return
85                 }
86         }
87
88         timelinePage := func(w http.ResponseWriter, req *http.Request) {
89                 c := newClient(w)
90                 ctx := newCtxWithSesion(req)
91                 tType, _ := mux.Vars(req)["type"]
92                 maxID := req.URL.Query().Get("max_id")
93                 minID := req.URL.Query().Get("min_id")
94
95                 err := s.ServeTimelinePage(ctx, c, tType, maxID, minID)
96                 if err != nil {
97                         w.WriteHeader(http.StatusInternalServerError)
98                         s.ServeErrorPage(ctx, c, err)
99                         return
100                 }
101         }
102
103         timelineOldPage := func(w http.ResponseWriter, req *http.Request) {
104                 w.Header().Add("Location", "/timeline/home")
105                 w.WriteHeader(http.StatusFound)
106         }
107
108         threadPage := func(w http.ResponseWriter, req *http.Request) {
109                 c := newClient(w)
110                 ctx := newCtxWithSesion(req)
111                 id, _ := mux.Vars(req)["id"]
112                 reply := req.URL.Query().Get("reply")
113
114                 err := s.ServeThreadPage(ctx, c, id, len(reply) > 1)
115                 if err != nil {
116                         w.WriteHeader(http.StatusInternalServerError)
117                         s.ServeErrorPage(ctx, c, err)
118                         return
119                 }
120         }
121
122         likedByPage := func(w http.ResponseWriter, req *http.Request) {
123                 c := newClient(w)
124                 ctx := newCtxWithSesion(req)
125                 id, _ := mux.Vars(req)["id"]
126
127                 err := s.ServeLikedByPage(ctx, c, id)
128                 if err != nil {
129                         w.WriteHeader(http.StatusInternalServerError)
130                         s.ServeErrorPage(ctx, c, err)
131                         return
132                 }
133         }
134
135         retweetedByPage := func(w http.ResponseWriter, req *http.Request) {
136                 c := newClient(w)
137                 ctx := newCtxWithSesion(req)
138                 id, _ := mux.Vars(req)["id"]
139
140                 err := s.ServeRetweetedByPage(ctx, c, id)
141                 if err != nil {
142                         w.WriteHeader(http.StatusInternalServerError)
143                         s.ServeErrorPage(ctx, c, err)
144                         return
145                 }
146         }
147
148         notificationsPage := func(w http.ResponseWriter, req *http.Request) {
149                 c := newClient(w)
150                 ctx := newCtxWithSesion(req)
151                 maxID := req.URL.Query().Get("max_id")
152                 minID := req.URL.Query().Get("min_id")
153
154                 err := s.ServeNotificationPage(ctx, c, maxID, minID)
155                 if err != nil {
156                         w.WriteHeader(http.StatusInternalServerError)
157                         s.ServeErrorPage(ctx, c, err)
158                         return
159                 }
160         }
161
162         userPage := func(w http.ResponseWriter, req *http.Request) {
163                 c := newClient(w)
164                 ctx := newCtxWithSesion(req)
165                 id, _ := mux.Vars(req)["id"]
166                 pageType, _ := mux.Vars(req)["type"]
167                 maxID := req.URL.Query().Get("max_id")
168                 minID := req.URL.Query().Get("min_id")
169
170                 err := s.ServeUserPage(ctx, c, id, pageType, maxID, minID)
171                 if err != nil {
172                         w.WriteHeader(http.StatusInternalServerError)
173                         s.ServeErrorPage(ctx, c, err)
174                         return
175                 }
176         }
177
178         userSearchPage := func(w http.ResponseWriter, req *http.Request) {
179                 c := newClient(w)
180                 ctx := newCtxWithSesion(req)
181                 id, _ := mux.Vars(req)["id"]
182                 q := req.URL.Query().Get("q")
183                 offsetStr := req.URL.Query().Get("offset")
184
185                 var offset int
186                 var err error
187                 if len(offsetStr) > 1 {
188                         offset, err = strconv.Atoi(offsetStr)
189                         if err != nil {
190                                 w.WriteHeader(http.StatusInternalServerError)
191                                 s.ServeErrorPage(ctx, c, err)
192                                 return
193                         }
194                 }
195
196                 err = s.ServeUserSearchPage(ctx, c, id, q, offset)
197                 if err != nil {
198                         w.WriteHeader(http.StatusInternalServerError)
199                         s.ServeErrorPage(ctx, c, err)
200                         return
201                 }
202         }
203
204         aboutPage := func(w http.ResponseWriter, req *http.Request) {
205                 c := newClient(w)
206                 ctx := newCtxWithSesion(req)
207
208                 err := s.ServeAboutPage(ctx, c)
209                 if err != nil {
210                         w.WriteHeader(http.StatusInternalServerError)
211                         s.ServeErrorPage(ctx, c, err)
212                         return
213                 }
214         }
215
216         emojisPage := func(w http.ResponseWriter, req *http.Request) {
217                 c := newClient(w)
218                 ctx := newCtxWithSesion(req)
219
220                 err := s.ServeEmojiPage(ctx, c)
221                 if err != nil {
222                         w.WriteHeader(http.StatusInternalServerError)
223                         s.ServeErrorPage(ctx, c, err)
224                         return
225                 }
226         }
227
228         searchPage := func(w http.ResponseWriter, req *http.Request) {
229                 c := newClient(w)
230                 ctx := newCtxWithSesion(req)
231                 q := req.URL.Query().Get("q")
232                 qType := req.URL.Query().Get("type")
233                 offsetStr := req.URL.Query().Get("offset")
234
235                 var offset int
236                 var err error
237                 if len(offsetStr) > 1 {
238                         offset, err = strconv.Atoi(offsetStr)
239                         if err != nil {
240                                 w.WriteHeader(http.StatusInternalServerError)
241                                 s.ServeErrorPage(ctx, c, err)
242                                 return
243                         }
244                 }
245
246                 err = s.ServeSearchPage(ctx, c, q, qType, offset)
247                 if err != nil {
248                         w.WriteHeader(http.StatusInternalServerError)
249                         s.ServeErrorPage(ctx, c, err)
250                         return
251                 }
252         }
253
254         settingsPage := func(w http.ResponseWriter, req *http.Request) {
255                 c := newClient(w)
256                 ctx := newCtxWithSesion(req)
257
258                 err := s.ServeSettingsPage(ctx, c)
259                 if err != nil {
260                         w.WriteHeader(http.StatusInternalServerError)
261                         s.ServeErrorPage(ctx, c, err)
262                         return
263                 }
264         }
265
266         signin := func(w http.ResponseWriter, req *http.Request) {
267                 c := newClient(w)
268                 ctx := context.Background()
269                 instance := req.FormValue("instance")
270
271                 url, sessionID, err := s.NewSession(ctx, instance)
272                 if err != nil {
273                         w.WriteHeader(http.StatusInternalServerError)
274                         s.ServeErrorPage(ctx, c, err)
275                         return
276                 }
277
278                 http.SetCookie(w, &http.Cookie{
279                         Name:    "session_id",
280                         Value:   sessionID,
281                         Expires: time.Now().Add(365 * 24 * time.Hour),
282                 })
283
284                 w.Header().Add("Location", url)
285                 w.WriteHeader(http.StatusFound)
286         }
287
288         oauthCallback := func(w http.ResponseWriter, req *http.Request) {
289                 c := newClient(w)
290                 ctx := newCtxWithSesion(req)
291                 token := req.URL.Query().Get("code")
292
293                 _, _, err := s.Signin(ctx, c, "", token)
294                 if err != nil {
295                         w.WriteHeader(http.StatusInternalServerError)
296                         s.ServeErrorPage(ctx, c, err)
297                         return
298                 }
299
300                 w.Header().Add("Location", "/timeline/home")
301                 w.WriteHeader(http.StatusFound)
302         }
303
304         post := func(w http.ResponseWriter, req *http.Request) {
305                 c := newClient(w)
306                 err := req.ParseMultipartForm(4 << 20)
307                 if err != nil {
308                         w.WriteHeader(http.StatusInternalServerError)
309                         s.ServeErrorPage(context.Background(), c, err)
310                         return
311                 }
312
313                 ctx := newCtxWithSesionCSRF(req,
314                         getMultipartFormValue(req.MultipartForm, "csrf_token"))
315                 content := getMultipartFormValue(req.MultipartForm, "content")
316                 replyToID := getMultipartFormValue(req.MultipartForm, "reply_to_id")
317                 format := getMultipartFormValue(req.MultipartForm, "format")
318                 visibility := getMultipartFormValue(req.MultipartForm, "visibility")
319                 isNSFW := "on" == getMultipartFormValue(req.MultipartForm, "is_nsfw")
320                 files := req.MultipartForm.File["attachments"]
321
322                 id, err := s.Post(ctx, c, content, replyToID, format, visibility, isNSFW, files)
323                 if err != nil {
324                         w.WriteHeader(http.StatusInternalServerError)
325                         s.ServeErrorPage(ctx, c, err)
326                         return
327                 }
328
329                 location := "/timeline/home" + "#status-" + id
330                 if len(replyToID) > 0 {
331                         location = "/thread/" + replyToID + "#status-" + id
332                 }
333                 w.Header().Add("Location", location)
334                 w.WriteHeader(http.StatusFound)
335         }
336
337         like := func(w http.ResponseWriter, req *http.Request) {
338                 c := newClient(w)
339                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
340                 id, _ := mux.Vars(req)["id"]
341                 retweetedByID := req.FormValue("retweeted_by_id")
342
343                 _, err := s.Like(ctx, c, id)
344                 if err != nil {
345                         w.WriteHeader(http.StatusInternalServerError)
346                         s.ServeErrorPage(ctx, c, err)
347                         return
348                 }
349
350                 rID := id
351                 if len(retweetedByID) > 0 {
352                         rID = retweetedByID
353                 }
354                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
355                 w.WriteHeader(http.StatusFound)
356         }
357
358         unlike := func(w http.ResponseWriter, req *http.Request) {
359                 c := newClient(w)
360                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
361                 id, _ := mux.Vars(req)["id"]
362                 retweetedByID := req.FormValue("retweeted_by_id")
363
364                 _, err := s.UnLike(ctx, c, id)
365                 if err != nil {
366                         w.WriteHeader(http.StatusInternalServerError)
367                         s.ServeErrorPage(ctx, c, err)
368                         return
369                 }
370
371                 rID := id
372                 if len(retweetedByID) > 0 {
373                         rID = retweetedByID
374                 }
375                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
376                 w.WriteHeader(http.StatusFound)
377         }
378
379         retweet := func(w http.ResponseWriter, req *http.Request) {
380                 c := newClient(w)
381                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
382                 id, _ := mux.Vars(req)["id"]
383                 retweetedByID := req.FormValue("retweeted_by_id")
384
385                 _, err := s.Retweet(ctx, c, id)
386                 if err != nil {
387                         w.WriteHeader(http.StatusInternalServerError)
388                         s.ServeErrorPage(ctx, c, err)
389                         return
390                 }
391
392                 rID := id
393                 if len(retweetedByID) > 0 {
394                         rID = retweetedByID
395                 }
396                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
397                 w.WriteHeader(http.StatusFound)
398         }
399
400         unretweet := func(w http.ResponseWriter, req *http.Request) {
401                 c := newClient(w)
402                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
403                 id, _ := mux.Vars(req)["id"]
404                 retweetedByID := req.FormValue("retweeted_by_id")
405
406                 _, err := s.UnRetweet(ctx, c, id)
407                 if err != nil {
408                         w.WriteHeader(http.StatusInternalServerError)
409                         s.ServeErrorPage(ctx, c, err)
410                         return
411                 }
412
413                 rID := id
414                 if len(retweetedByID) > 0 {
415                         rID = retweetedByID
416                 }
417
418                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
419                 w.WriteHeader(http.StatusFound)
420         }
421
422         follow := func(w http.ResponseWriter, req *http.Request) {
423                 c := newClient(w)
424                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
425                 id, _ := mux.Vars(req)["id"]
426
427                 err := s.Follow(ctx, c, id)
428                 if err != nil {
429                         w.WriteHeader(http.StatusInternalServerError)
430                         s.ServeErrorPage(ctx, c, err)
431                         return
432                 }
433
434                 w.Header().Add("Location", req.Header.Get("Referer"))
435                 w.WriteHeader(http.StatusFound)
436         }
437
438         unfollow := func(w http.ResponseWriter, req *http.Request) {
439                 c := newClient(w)
440                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
441                 id, _ := mux.Vars(req)["id"]
442
443                 err := s.UnFollow(ctx, c, id)
444                 if err != nil {
445                         w.WriteHeader(http.StatusInternalServerError)
446                         s.ServeErrorPage(ctx, c, err)
447                         return
448                 }
449
450                 w.Header().Add("Location", req.Header.Get("Referer"))
451                 w.WriteHeader(http.StatusFound)
452         }
453
454         mute := func(w http.ResponseWriter, req *http.Request) {
455                 c := newClient(w)
456                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
457                 id, _ := mux.Vars(req)["id"]
458
459                 err := s.Mute(ctx, c, id)
460                 if err != nil {
461                         w.WriteHeader(http.StatusInternalServerError)
462                         s.ServeErrorPage(ctx, c, err)
463                         return
464                 }
465
466                 w.Header().Add("Location", req.Header.Get("Referer"))
467                 w.WriteHeader(http.StatusFound)
468         }
469
470         unMute := func(w http.ResponseWriter, req *http.Request) {
471                 c := newClient(w)
472                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
473                 id, _ := mux.Vars(req)["id"]
474
475                 err := s.UnMute(ctx, c, id)
476                 if err != nil {
477                         w.WriteHeader(http.StatusInternalServerError)
478                         s.ServeErrorPage(ctx, c, err)
479                         return
480                 }
481
482                 w.Header().Add("Location", req.Header.Get("Referer"))
483                 w.WriteHeader(http.StatusFound)
484         }
485
486         block := func(w http.ResponseWriter, req *http.Request) {
487                 c := newClient(w)
488                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
489                 id, _ := mux.Vars(req)["id"]
490
491                 err := s.Block(ctx, c, id)
492                 if err != nil {
493                         w.WriteHeader(http.StatusInternalServerError)
494                         s.ServeErrorPage(ctx, c, err)
495                         return
496                 }
497
498                 w.Header().Add("Location", req.Header.Get("Referer"))
499                 w.WriteHeader(http.StatusFound)
500         }
501
502         unBlock := func(w http.ResponseWriter, req *http.Request) {
503                 c := newClient(w)
504                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
505                 id, _ := mux.Vars(req)["id"]
506
507                 err := s.UnBlock(ctx, c, id)
508                 if err != nil {
509                         w.WriteHeader(http.StatusInternalServerError)
510                         s.ServeErrorPage(ctx, c, err)
511                         return
512                 }
513
514                 w.Header().Add("Location", req.Header.Get("Referer"))
515                 w.WriteHeader(http.StatusFound)
516         }
517
518         settings := func(w http.ResponseWriter, req *http.Request) {
519                 c := newClient(w)
520                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
521                 visibility := req.FormValue("visibility")
522                 copyScope := req.FormValue("copy_scope") == "true"
523                 threadInNewTab := req.FormValue("thread_in_new_tab") == "true"
524                 maskNSFW := req.FormValue("mask_nsfw") == "true"
525                 fluorideMode := req.FormValue("fluoride_mode") == "true"
526                 darkMode := req.FormValue("dark_mode") == "true"
527
528                 settings := &model.Settings{
529                         DefaultVisibility: visibility,
530                         CopyScope:         copyScope,
531                         ThreadInNewTab:    threadInNewTab,
532                         MaskNSFW:          maskNSFW,
533                         FluorideMode:      fluorideMode,
534                         DarkMode:          darkMode,
535                 }
536
537                 err := s.SaveSettings(ctx, c, settings)
538                 if err != nil {
539                         w.WriteHeader(http.StatusInternalServerError)
540                         s.ServeErrorPage(ctx, c, err)
541                         return
542                 }
543
544                 w.Header().Add("Location", req.Header.Get("Referer"))
545                 w.WriteHeader(http.StatusFound)
546         }
547
548         muteConversation := func(w http.ResponseWriter, req *http.Request) {
549                 c := newClient(w)
550                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
551                 id, _ := mux.Vars(req)["id"]
552
553                 err := s.MuteConversation(ctx, c, id)
554                 if err != nil {
555                         w.WriteHeader(http.StatusInternalServerError)
556                         s.ServeErrorPage(ctx, c, err)
557                         return
558                 }
559
560                 w.Header().Add("Location", req.Header.Get("Referer"))
561                 w.WriteHeader(http.StatusFound)
562         }
563
564         unMuteConversation := func(w http.ResponseWriter, req *http.Request) {
565                 c := newClient(w)
566                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
567                 id, _ := mux.Vars(req)["id"]
568
569                 err := s.UnMuteConversation(ctx, c, id)
570                 if err != nil {
571                         w.WriteHeader(http.StatusInternalServerError)
572                         s.ServeErrorPage(ctx, c, err)
573                         return
574                 }
575
576                 w.Header().Add("Location", req.Header.Get("Referer"))
577                 w.WriteHeader(http.StatusFound)
578         }
579
580         delete := func(w http.ResponseWriter, req *http.Request) {
581                 c := newClient(w)
582                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
583                 id, _ := mux.Vars(req)["id"]
584
585                 err := s.Delete(ctx, c, id)
586                 if err != nil {
587                         w.WriteHeader(http.StatusInternalServerError)
588                         s.ServeErrorPage(ctx, c, err)
589                         return
590                 }
591
592                 w.Header().Add("Location", req.Header.Get("Referer"))
593                 w.WriteHeader(http.StatusFound)
594         }
595
596         signout := func(w http.ResponseWriter, req *http.Request) {
597                 // TODO remove session from database
598                 http.SetCookie(w, &http.Cookie{
599                         Name:    "session_id",
600                         Value:   "",
601                         Expires: time.Now(),
602                 })
603                 w.Header().Add("Location", "/")
604                 w.WriteHeader(http.StatusFound)
605         }
606
607         fLike := func(w http.ResponseWriter, req *http.Request) {
608                 c := newClient(w)
609                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
610                 id, _ := mux.Vars(req)["id"]
611
612                 count, err := s.Like(ctx, c, id)
613                 if err != nil {
614                         serveJsonError(w, err)
615                         return
616                 }
617
618                 err = serveJson(w, count)
619                 if err != nil {
620                         serveJsonError(w, err)
621                         return
622                 }
623         }
624
625         fUnlike := func(w http.ResponseWriter, req *http.Request) {
626                 c := newClient(w)
627                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
628                 id, _ := mux.Vars(req)["id"]
629                 count, err := s.UnLike(ctx, c, id)
630                 if err != nil {
631                         serveJsonError(w, err)
632                         return
633                 }
634
635                 err = serveJson(w, count)
636                 if err != nil {
637                         serveJsonError(w, err)
638                         return
639                 }
640         }
641
642         fRetweet := func(w http.ResponseWriter, req *http.Request) {
643                 c := newClient(w)
644                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
645                 id, _ := mux.Vars(req)["id"]
646
647                 count, err := s.Retweet(ctx, c, id)
648                 if err != nil {
649                         serveJsonError(w, err)
650                         return
651                 }
652
653                 err = serveJson(w, count)
654                 if err != nil {
655                         serveJsonError(w, err)
656                         return
657                 }
658         }
659
660         fUnretweet := func(w http.ResponseWriter, req *http.Request) {
661                 c := newClient(w)
662                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
663                 id, _ := mux.Vars(req)["id"]
664
665                 count, err := s.UnRetweet(ctx, c, id)
666                 if err != nil {
667                         serveJsonError(w, err)
668                         return
669                 }
670
671                 err = serveJson(w, count)
672                 if err != nil {
673                         serveJsonError(w, err)
674                         return
675                 }
676         }
677
678         r.HandleFunc("/", rootPage).Methods(http.MethodGet)
679         r.HandleFunc("/signin", signinPage).Methods(http.MethodGet)
680         r.HandleFunc("/timeline/{type}", timelinePage).Methods(http.MethodGet)
681         r.HandleFunc("/timeline", timelineOldPage).Methods(http.MethodGet)
682         r.HandleFunc("/thread/{id}", threadPage).Methods(http.MethodGet)
683         r.HandleFunc("/likedby/{id}", likedByPage).Methods(http.MethodGet)
684         r.HandleFunc("/retweetedby/{id}", retweetedByPage).Methods(http.MethodGet)
685         r.HandleFunc("/notifications", notificationsPage).Methods(http.MethodGet)
686         r.HandleFunc("/user/{id}", userPage).Methods(http.MethodGet)
687         r.HandleFunc("/user/{id}/{type}", userPage).Methods(http.MethodGet)
688         r.HandleFunc("/usersearch/{id}", userSearchPage).Methods(http.MethodGet)
689         r.HandleFunc("/about", aboutPage).Methods(http.MethodGet)
690         r.HandleFunc("/emojis", emojisPage).Methods(http.MethodGet)
691         r.HandleFunc("/search", searchPage).Methods(http.MethodGet)
692         r.HandleFunc("/settings", settingsPage).Methods(http.MethodGet)
693         r.HandleFunc("/signin", signin).Methods(http.MethodPost)
694         r.HandleFunc("/oauth_callback", oauthCallback).Methods(http.MethodGet)
695         r.HandleFunc("/post", post).Methods(http.MethodPost)
696         r.HandleFunc("/like/{id}", like).Methods(http.MethodPost)
697         r.HandleFunc("/unlike/{id}", unlike).Methods(http.MethodPost)
698         r.HandleFunc("/retweet/{id}", retweet).Methods(http.MethodPost)
699         r.HandleFunc("/unretweet/{id}", unretweet).Methods(http.MethodPost)
700         r.HandleFunc("/follow/{id}", follow).Methods(http.MethodPost)
701         r.HandleFunc("/unfollow/{id}", unfollow).Methods(http.MethodPost)
702         r.HandleFunc("/mute/{id}", mute).Methods(http.MethodPost)
703         r.HandleFunc("/unmute/{id}", unMute).Methods(http.MethodPost)
704         r.HandleFunc("/block/{id}", block).Methods(http.MethodPost)
705         r.HandleFunc("/unblock/{id}", unBlock).Methods(http.MethodPost)
706         r.HandleFunc("/settings", settings).Methods(http.MethodPost)
707         r.HandleFunc("/muteconv/{id}", muteConversation).Methods(http.MethodPost)
708         r.HandleFunc("/unmuteconv/{id}", unMuteConversation).Methods(http.MethodPost)
709         r.HandleFunc("/delete/{id}", delete).Methods(http.MethodPost)
710         r.HandleFunc("/signout", signout).Methods(http.MethodGet)
711         r.HandleFunc("/fluoride/like/{id}", fLike).Methods(http.MethodPost)
712         r.HandleFunc("/fluoride/unlike/{id}", fUnlike).Methods(http.MethodPost)
713         r.HandleFunc("/fluoride/retweet/{id}", fRetweet).Methods(http.MethodPost)
714         r.HandleFunc("/fluoride/unretweet/{id}", fUnretweet).Methods(http.MethodPost)
715         r.PathPrefix("/static").Handler(http.StripPrefix("/static",
716                 http.FileServer(http.Dir(staticDir))))
717
718         return r
719 }