69b28ec087c656005b4ba61d382cddb360f855fe
[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                 if sessionID != nil && len(sessionID.Value) > 0 {
68                         c := newClient(w)
69                         ctx := newCtxWithSesion(req)
70                         err := s.ServeRootPage(ctx, c)
71                         if err != nil {
72                                 w.WriteHeader(http.StatusInternalServerError)
73                                 s.ServeErrorPage(ctx, c, err)
74                                 return
75                         }
76                 } else {
77                         w.Header().Add("Location", "/signin")
78                         w.WriteHeader(http.StatusFound)
79                 }
80         }
81
82         navPage := func(w http.ResponseWriter, req *http.Request) {
83                 c := newClient(w)
84                 ctx := newCtxWithSesion(req)
85                 err := s.ServeNavPage(ctx, c)
86                 if err != nil {
87                         w.WriteHeader(http.StatusInternalServerError)
88                         s.ServeErrorPage(ctx, c, err)
89                         return
90                 }
91         }
92
93         signinPage := func(w http.ResponseWriter, req *http.Request) {
94                 c := newClient(w)
95                 ctx := context.Background()
96                 err := s.ServeSigninPage(ctx, c)
97                 if err != nil {
98                         w.WriteHeader(http.StatusInternalServerError)
99                         s.ServeErrorPage(ctx, c, err)
100                         return
101                 }
102         }
103
104         timelinePage := func(w http.ResponseWriter, req *http.Request) {
105                 c := newClient(w)
106                 ctx := newCtxWithSesion(req)
107                 tType, _ := mux.Vars(req)["type"]
108                 maxID := req.URL.Query().Get("max_id")
109                 minID := req.URL.Query().Get("min_id")
110
111                 err := s.ServeTimelinePage(ctx, c, tType, maxID, minID)
112                 if err != nil {
113                         w.WriteHeader(http.StatusInternalServerError)
114                         s.ServeErrorPage(ctx, c, err)
115                         return
116                 }
117         }
118
119         timelineOldPage := func(w http.ResponseWriter, req *http.Request) {
120                 w.Header().Add("Location", "/timeline/home")
121                 w.WriteHeader(http.StatusFound)
122         }
123
124         threadPage := func(w http.ResponseWriter, req *http.Request) {
125                 c := newClient(w)
126                 ctx := newCtxWithSesion(req)
127                 id, _ := mux.Vars(req)["id"]
128                 reply := req.URL.Query().Get("reply")
129
130                 err := s.ServeThreadPage(ctx, c, id, len(reply) > 1)
131                 if err != nil {
132                         w.WriteHeader(http.StatusInternalServerError)
133                         s.ServeErrorPage(ctx, c, err)
134                         return
135                 }
136         }
137
138         likedByPage := func(w http.ResponseWriter, req *http.Request) {
139                 c := newClient(w)
140                 ctx := newCtxWithSesion(req)
141                 id, _ := mux.Vars(req)["id"]
142
143                 err := s.ServeLikedByPage(ctx, c, id)
144                 if err != nil {
145                         w.WriteHeader(http.StatusInternalServerError)
146                         s.ServeErrorPage(ctx, c, err)
147                         return
148                 }
149         }
150
151         retweetedByPage := func(w http.ResponseWriter, req *http.Request) {
152                 c := newClient(w)
153                 ctx := newCtxWithSesion(req)
154                 id, _ := mux.Vars(req)["id"]
155
156                 err := s.ServeRetweetedByPage(ctx, c, id)
157                 if err != nil {
158                         w.WriteHeader(http.StatusInternalServerError)
159                         s.ServeErrorPage(ctx, c, err)
160                         return
161                 }
162         }
163
164         notificationsPage := func(w http.ResponseWriter, req *http.Request) {
165                 c := newClient(w)
166                 ctx := newCtxWithSesion(req)
167                 maxID := req.URL.Query().Get("max_id")
168                 minID := req.URL.Query().Get("min_id")
169
170                 err := s.ServeNotificationPage(ctx, c, maxID, minID)
171                 if err != nil {
172                         w.WriteHeader(http.StatusInternalServerError)
173                         s.ServeErrorPage(ctx, c, err)
174                         return
175                 }
176         }
177
178         userPage := func(w http.ResponseWriter, req *http.Request) {
179                 c := newClient(w)
180                 ctx := newCtxWithSesion(req)
181                 id, _ := mux.Vars(req)["id"]
182                 pageType, _ := mux.Vars(req)["type"]
183                 maxID := req.URL.Query().Get("max_id")
184                 minID := req.URL.Query().Get("min_id")
185
186                 err := s.ServeUserPage(ctx, c, id, pageType, maxID, minID)
187                 if err != nil {
188                         w.WriteHeader(http.StatusInternalServerError)
189                         s.ServeErrorPage(ctx, c, err)
190                         return
191                 }
192         }
193
194         userSearchPage := func(w http.ResponseWriter, req *http.Request) {
195                 c := newClient(w)
196                 ctx := newCtxWithSesion(req)
197                 id, _ := mux.Vars(req)["id"]
198                 q := req.URL.Query().Get("q")
199                 offsetStr := req.URL.Query().Get("offset")
200
201                 var offset int
202                 var err error
203                 if len(offsetStr) > 1 {
204                         offset, err = strconv.Atoi(offsetStr)
205                         if err != nil {
206                                 w.WriteHeader(http.StatusInternalServerError)
207                                 s.ServeErrorPage(ctx, c, err)
208                                 return
209                         }
210                 }
211
212                 err = s.ServeUserSearchPage(ctx, c, id, q, offset)
213                 if err != nil {
214                         w.WriteHeader(http.StatusInternalServerError)
215                         s.ServeErrorPage(ctx, c, err)
216                         return
217                 }
218         }
219
220         aboutPage := func(w http.ResponseWriter, req *http.Request) {
221                 c := newClient(w)
222                 ctx := newCtxWithSesion(req)
223
224                 err := s.ServeAboutPage(ctx, c)
225                 if err != nil {
226                         w.WriteHeader(http.StatusInternalServerError)
227                         s.ServeErrorPage(ctx, c, err)
228                         return
229                 }
230         }
231
232         emojisPage := func(w http.ResponseWriter, req *http.Request) {
233                 c := newClient(w)
234                 ctx := newCtxWithSesion(req)
235
236                 err := s.ServeEmojiPage(ctx, c)
237                 if err != nil {
238                         w.WriteHeader(http.StatusInternalServerError)
239                         s.ServeErrorPage(ctx, c, err)
240                         return
241                 }
242         }
243
244         searchPage := func(w http.ResponseWriter, req *http.Request) {
245                 c := newClient(w)
246                 ctx := newCtxWithSesion(req)
247                 q := req.URL.Query().Get("q")
248                 qType := req.URL.Query().Get("type")
249                 offsetStr := req.URL.Query().Get("offset")
250
251                 var offset int
252                 var err error
253                 if len(offsetStr) > 1 {
254                         offset, err = strconv.Atoi(offsetStr)
255                         if err != nil {
256                                 w.WriteHeader(http.StatusInternalServerError)
257                                 s.ServeErrorPage(ctx, c, err)
258                                 return
259                         }
260                 }
261
262                 err = s.ServeSearchPage(ctx, c, q, qType, offset)
263                 if err != nil {
264                         w.WriteHeader(http.StatusInternalServerError)
265                         s.ServeErrorPage(ctx, c, err)
266                         return
267                 }
268         }
269
270         settingsPage := func(w http.ResponseWriter, req *http.Request) {
271                 c := newClient(w)
272                 ctx := newCtxWithSesion(req)
273
274                 err := s.ServeSettingsPage(ctx, c)
275                 if err != nil {
276                         w.WriteHeader(http.StatusInternalServerError)
277                         s.ServeErrorPage(ctx, c, err)
278                         return
279                 }
280         }
281
282         signin := func(w http.ResponseWriter, req *http.Request) {
283                 c := newClient(w)
284                 ctx := context.Background()
285                 instance := req.FormValue("instance")
286
287                 url, sessionID, err := s.NewSession(ctx, instance)
288                 if err != nil {
289                         w.WriteHeader(http.StatusInternalServerError)
290                         s.ServeErrorPage(ctx, c, err)
291                         return
292                 }
293
294                 http.SetCookie(w, &http.Cookie{
295                         Name:    "session_id",
296                         Value:   sessionID,
297                         Expires: time.Now().Add(365 * 24 * time.Hour),
298                 })
299
300                 w.Header().Add("Location", url)
301                 w.WriteHeader(http.StatusFound)
302         }
303
304         oauthCallback := func(w http.ResponseWriter, req *http.Request) {
305                 c := newClient(w)
306                 ctx := newCtxWithSesion(req)
307                 token := req.URL.Query().Get("code")
308
309                 _, _, err := s.Signin(ctx, c, "", token)
310                 if err != nil {
311                         w.WriteHeader(http.StatusInternalServerError)
312                         s.ServeErrorPage(ctx, c, err)
313                         return
314                 }
315
316                 w.Header().Add("Location", "/")
317                 w.WriteHeader(http.StatusFound)
318         }
319
320         post := func(w http.ResponseWriter, req *http.Request) {
321                 c := newClient(w)
322                 err := req.ParseMultipartForm(4 << 20)
323                 if err != nil {
324                         w.WriteHeader(http.StatusInternalServerError)
325                         s.ServeErrorPage(context.Background(), c, err)
326                         return
327                 }
328
329                 ctx := newCtxWithSesionCSRF(req,
330                         getMultipartFormValue(req.MultipartForm, "csrf_token"))
331                 content := getMultipartFormValue(req.MultipartForm, "content")
332                 replyToID := getMultipartFormValue(req.MultipartForm, "reply_to_id")
333                 format := getMultipartFormValue(req.MultipartForm, "format")
334                 visibility := getMultipartFormValue(req.MultipartForm, "visibility")
335                 isNSFW := "on" == getMultipartFormValue(req.MultipartForm, "is_nsfw")
336                 files := req.MultipartForm.File["attachments"]
337
338                 id, err := s.Post(ctx, c, content, replyToID, format, visibility, isNSFW, files)
339                 if err != nil {
340                         w.WriteHeader(http.StatusInternalServerError)
341                         s.ServeErrorPage(ctx, c, err)
342                         return
343                 }
344
345                 location := req.Header.Get("Referer")
346                 if len(replyToID) > 0 {
347                         location = "/thread/" + replyToID + "#status-" + id
348                 }
349                 w.Header().Add("Location", location)
350                 w.WriteHeader(http.StatusFound)
351         }
352
353         like := func(w http.ResponseWriter, req *http.Request) {
354                 c := newClient(w)
355                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
356                 id, _ := mux.Vars(req)["id"]
357                 retweetedByID := req.FormValue("retweeted_by_id")
358
359                 _, err := s.Like(ctx, c, id)
360                 if err != nil {
361                         w.WriteHeader(http.StatusInternalServerError)
362                         s.ServeErrorPage(ctx, c, err)
363                         return
364                 }
365
366                 rID := id
367                 if len(retweetedByID) > 0 {
368                         rID = retweetedByID
369                 }
370                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
371                 w.WriteHeader(http.StatusFound)
372         }
373
374         unlike := func(w http.ResponseWriter, req *http.Request) {
375                 c := newClient(w)
376                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
377                 id, _ := mux.Vars(req)["id"]
378                 retweetedByID := req.FormValue("retweeted_by_id")
379
380                 _, err := s.UnLike(ctx, c, id)
381                 if err != nil {
382                         w.WriteHeader(http.StatusInternalServerError)
383                         s.ServeErrorPage(ctx, c, err)
384                         return
385                 }
386
387                 rID := id
388                 if len(retweetedByID) > 0 {
389                         rID = retweetedByID
390                 }
391                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
392                 w.WriteHeader(http.StatusFound)
393         }
394
395         retweet := func(w http.ResponseWriter, req *http.Request) {
396                 c := newClient(w)
397                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
398                 id, _ := mux.Vars(req)["id"]
399                 retweetedByID := req.FormValue("retweeted_by_id")
400
401                 _, err := s.Retweet(ctx, c, id)
402                 if err != nil {
403                         w.WriteHeader(http.StatusInternalServerError)
404                         s.ServeErrorPage(ctx, c, err)
405                         return
406                 }
407
408                 rID := id
409                 if len(retweetedByID) > 0 {
410                         rID = retweetedByID
411                 }
412                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
413                 w.WriteHeader(http.StatusFound)
414         }
415
416         unretweet := func(w http.ResponseWriter, req *http.Request) {
417                 c := newClient(w)
418                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
419                 id, _ := mux.Vars(req)["id"]
420                 retweetedByID := req.FormValue("retweeted_by_id")
421
422                 _, err := s.UnRetweet(ctx, c, id)
423                 if err != nil {
424                         w.WriteHeader(http.StatusInternalServerError)
425                         s.ServeErrorPage(ctx, c, err)
426                         return
427                 }
428
429                 rID := id
430                 if len(retweetedByID) > 0 {
431                         rID = retweetedByID
432                 }
433
434                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
435                 w.WriteHeader(http.StatusFound)
436         }
437
438         vote := 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                 statusID := req.FormValue("status_id")
443                 choices, _ := req.PostForm["choices"]
444
445                 err := s.Vote(ctx, c, id, choices)
446                 if err != nil {
447                         w.WriteHeader(http.StatusInternalServerError)
448                         s.ServeErrorPage(ctx, c, err)
449                         return
450                 }
451
452                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+statusID)
453                 w.WriteHeader(http.StatusFound)
454         }
455
456         follow := func(w http.ResponseWriter, req *http.Request) {
457                 c := newClient(w)
458                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
459                 id, _ := mux.Vars(req)["id"]
460
461                 var reblogs *bool
462                 r, ok := req.URL.Query()["reblogs"]
463                 if ok && len(r) > 0 {
464                         reblogs = new(bool)
465                         *reblogs = r[0] == "true"
466                 }
467
468                 err := s.Follow(ctx, c, id, reblogs)
469                 if err != nil {
470                         w.WriteHeader(http.StatusInternalServerError)
471                         s.ServeErrorPage(ctx, c, err)
472                         return
473                 }
474
475                 w.Header().Add("Location", req.Header.Get("Referer"))
476                 w.WriteHeader(http.StatusFound)
477         }
478
479         unfollow := func(w http.ResponseWriter, req *http.Request) {
480                 c := newClient(w)
481                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
482                 id, _ := mux.Vars(req)["id"]
483
484                 err := s.UnFollow(ctx, c, id)
485                 if err != nil {
486                         w.WriteHeader(http.StatusInternalServerError)
487                         s.ServeErrorPage(ctx, c, err)
488                         return
489                 }
490
491                 w.Header().Add("Location", req.Header.Get("Referer"))
492                 w.WriteHeader(http.StatusFound)
493         }
494
495         mute := func(w http.ResponseWriter, req *http.Request) {
496                 c := newClient(w)
497                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
498                 id, _ := mux.Vars(req)["id"]
499
500                 err := s.Mute(ctx, c, id)
501                 if err != nil {
502                         w.WriteHeader(http.StatusInternalServerError)
503                         s.ServeErrorPage(ctx, c, err)
504                         return
505                 }
506
507                 w.Header().Add("Location", req.Header.Get("Referer"))
508                 w.WriteHeader(http.StatusFound)
509         }
510
511         unMute := func(w http.ResponseWriter, req *http.Request) {
512                 c := newClient(w)
513                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
514                 id, _ := mux.Vars(req)["id"]
515
516                 err := s.UnMute(ctx, c, id)
517                 if err != nil {
518                         w.WriteHeader(http.StatusInternalServerError)
519                         s.ServeErrorPage(ctx, c, err)
520                         return
521                 }
522
523                 w.Header().Add("Location", req.Header.Get("Referer"))
524                 w.WriteHeader(http.StatusFound)
525         }
526
527         block := func(w http.ResponseWriter, req *http.Request) {
528                 c := newClient(w)
529                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
530                 id, _ := mux.Vars(req)["id"]
531
532                 err := s.Block(ctx, c, id)
533                 if err != nil {
534                         w.WriteHeader(http.StatusInternalServerError)
535                         s.ServeErrorPage(ctx, c, err)
536                         return
537                 }
538
539                 w.Header().Add("Location", req.Header.Get("Referer"))
540                 w.WriteHeader(http.StatusFound)
541         }
542
543         unBlock := func(w http.ResponseWriter, req *http.Request) {
544                 c := newClient(w)
545                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
546                 id, _ := mux.Vars(req)["id"]
547
548                 err := s.UnBlock(ctx, c, id)
549                 if err != nil {
550                         w.WriteHeader(http.StatusInternalServerError)
551                         s.ServeErrorPage(ctx, c, err)
552                         return
553                 }
554
555                 w.Header().Add("Location", req.Header.Get("Referer"))
556                 w.WriteHeader(http.StatusFound)
557         }
558
559         subscribe := func(w http.ResponseWriter, req *http.Request) {
560                 c := newClient(w)
561                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
562                 id, _ := mux.Vars(req)["id"]
563
564                 err := s.Subscribe(ctx, c, id)
565                 if err != nil {
566                         w.WriteHeader(http.StatusInternalServerError)
567                         s.ServeErrorPage(ctx, c, err)
568                         return
569                 }
570
571                 w.Header().Add("Location", req.Header.Get("Referer"))
572                 w.WriteHeader(http.StatusFound)
573         }
574
575         unSubscribe := func(w http.ResponseWriter, req *http.Request) {
576                 c := newClient(w)
577                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
578                 id, _ := mux.Vars(req)["id"]
579
580                 err := s.UnSubscribe(ctx, c, id)
581                 if err != nil {
582                         w.WriteHeader(http.StatusInternalServerError)
583                         s.ServeErrorPage(ctx, c, err)
584                         return
585                 }
586
587                 w.Header().Add("Location", req.Header.Get("Referer"))
588                 w.WriteHeader(http.StatusFound)
589         }
590
591         settings := func(w http.ResponseWriter, req *http.Request) {
592                 c := newClient(w)
593                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
594                 visibility := req.FormValue("visibility")
595                 copyScope := req.FormValue("copy_scope") == "true"
596                 threadInNewTab := req.FormValue("thread_in_new_tab") == "true"
597                 maskNSFW := req.FormValue("mask_nsfw") == "true"
598                 arn := req.FormValue("auto_refresh_notifications") == "true"
599                 fluorideMode := req.FormValue("fluoride_mode") == "true"
600                 darkMode := req.FormValue("dark_mode") == "true"
601
602                 settings := &model.Settings{
603                         DefaultVisibility:        visibility,
604                         CopyScope:                copyScope,
605                         ThreadInNewTab:           threadInNewTab,
606                         MaskNSFW:                 maskNSFW,
607                         AutoRefreshNotifications: arn,
608                         FluorideMode:             fluorideMode,
609                         DarkMode:                 darkMode,
610                 }
611
612                 err := s.SaveSettings(ctx, c, settings)
613                 if err != nil {
614                         w.WriteHeader(http.StatusInternalServerError)
615                         s.ServeErrorPage(ctx, c, err)
616                         return
617                 }
618
619                 w.Header().Add("Location", "/")
620                 w.WriteHeader(http.StatusFound)
621         }
622
623         muteConversation := func(w http.ResponseWriter, req *http.Request) {
624                 c := newClient(w)
625                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
626                 id, _ := mux.Vars(req)["id"]
627
628                 err := s.MuteConversation(ctx, c, id)
629                 if err != nil {
630                         w.WriteHeader(http.StatusInternalServerError)
631                         s.ServeErrorPage(ctx, c, err)
632                         return
633                 }
634
635                 w.Header().Add("Location", req.Header.Get("Referer"))
636                 w.WriteHeader(http.StatusFound)
637         }
638
639         unMuteConversation := func(w http.ResponseWriter, req *http.Request) {
640                 c := newClient(w)
641                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
642                 id, _ := mux.Vars(req)["id"]
643
644                 err := s.UnMuteConversation(ctx, c, id)
645                 if err != nil {
646                         w.WriteHeader(http.StatusInternalServerError)
647                         s.ServeErrorPage(ctx, c, err)
648                         return
649                 }
650
651                 w.Header().Add("Location", req.Header.Get("Referer"))
652                 w.WriteHeader(http.StatusFound)
653         }
654
655         delete := func(w http.ResponseWriter, req *http.Request) {
656                 c := newClient(w)
657                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
658                 id, _ := mux.Vars(req)["id"]
659
660                 err := s.Delete(ctx, c, id)
661                 if err != nil {
662                         w.WriteHeader(http.StatusInternalServerError)
663                         s.ServeErrorPage(ctx, c, err)
664                         return
665                 }
666
667                 w.Header().Add("Location", req.Header.Get("Referer"))
668                 w.WriteHeader(http.StatusFound)
669         }
670
671         readNotifications := func(w http.ResponseWriter, req *http.Request) {
672                 c := newClient(w)
673                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
674                 maxID := req.URL.Query().Get("max_id")
675
676                 err := s.ReadNotifications(ctx, c, maxID)
677                 if err != nil {
678                         w.WriteHeader(http.StatusInternalServerError)
679                         s.ServeErrorPage(ctx, c, err)
680                         return
681                 }
682
683                 w.Header().Add("Location", req.Header.Get("Referer"))
684                 w.WriteHeader(http.StatusFound)
685         }
686
687         signout := func(w http.ResponseWriter, req *http.Request) {
688                 c := newClient(w)
689                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
690
691                 s.Signout(ctx, c)
692                 http.SetCookie(w, &http.Cookie{
693                         Name:    "session_id",
694                         Value:   "",
695                         Expires: time.Now(),
696                 })
697
698                 w.Header().Add("Location", "/")
699                 w.WriteHeader(http.StatusFound)
700         }
701
702         fLike := func(w http.ResponseWriter, req *http.Request) {
703                 c := newClient(w)
704                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
705                 id, _ := mux.Vars(req)["id"]
706
707                 count, err := s.Like(ctx, c, id)
708                 if err != nil {
709                         serveJsonError(w, err)
710                         return
711                 }
712
713                 err = serveJson(w, count)
714                 if err != nil {
715                         serveJsonError(w, err)
716                         return
717                 }
718         }
719
720         fUnlike := func(w http.ResponseWriter, req *http.Request) {
721                 c := newClient(w)
722                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
723                 id, _ := mux.Vars(req)["id"]
724                 count, err := s.UnLike(ctx, c, id)
725                 if err != nil {
726                         serveJsonError(w, err)
727                         return
728                 }
729
730                 err = serveJson(w, count)
731                 if err != nil {
732                         serveJsonError(w, err)
733                         return
734                 }
735         }
736
737         fRetweet := func(w http.ResponseWriter, req *http.Request) {
738                 c := newClient(w)
739                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
740                 id, _ := mux.Vars(req)["id"]
741
742                 count, err := s.Retweet(ctx, c, id)
743                 if err != nil {
744                         serveJsonError(w, err)
745                         return
746                 }
747
748                 err = serveJson(w, count)
749                 if err != nil {
750                         serveJsonError(w, err)
751                         return
752                 }
753         }
754
755         fUnretweet := func(w http.ResponseWriter, req *http.Request) {
756                 c := newClient(w)
757                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
758                 id, _ := mux.Vars(req)["id"]
759
760                 count, err := s.UnRetweet(ctx, c, id)
761                 if err != nil {
762                         serveJsonError(w, err)
763                         return
764                 }
765
766                 err = serveJson(w, count)
767                 if err != nil {
768                         serveJsonError(w, err)
769                         return
770                 }
771         }
772
773         r.HandleFunc("/", rootPage).Methods(http.MethodGet)
774         r.HandleFunc("/nav", navPage).Methods(http.MethodGet)
775         r.HandleFunc("/signin", signinPage).Methods(http.MethodGet)
776         r.HandleFunc("//{type}", timelinePage).Methods(http.MethodGet)
777         r.HandleFunc("/timeline/{type}", timelinePage).Methods(http.MethodGet)
778         r.HandleFunc("/timeline", timelineOldPage).Methods(http.MethodGet)
779         r.HandleFunc("/thread/{id}", threadPage).Methods(http.MethodGet)
780         r.HandleFunc("/likedby/{id}", likedByPage).Methods(http.MethodGet)
781         r.HandleFunc("/retweetedby/{id}", retweetedByPage).Methods(http.MethodGet)
782         r.HandleFunc("/notifications", notificationsPage).Methods(http.MethodGet)
783         r.HandleFunc("/user/{id}", userPage).Methods(http.MethodGet)
784         r.HandleFunc("/user/{id}/{type}", userPage).Methods(http.MethodGet)
785         r.HandleFunc("/usersearch/{id}", userSearchPage).Methods(http.MethodGet)
786         r.HandleFunc("/about", aboutPage).Methods(http.MethodGet)
787         r.HandleFunc("/emojis", emojisPage).Methods(http.MethodGet)
788         r.HandleFunc("/search", searchPage).Methods(http.MethodGet)
789         r.HandleFunc("/settings", settingsPage).Methods(http.MethodGet)
790         r.HandleFunc("/signin", signin).Methods(http.MethodPost)
791         r.HandleFunc("/oauth_callback", oauthCallback).Methods(http.MethodGet)
792         r.HandleFunc("/post", post).Methods(http.MethodPost)
793         r.HandleFunc("/like/{id}", like).Methods(http.MethodPost)
794         r.HandleFunc("/unlike/{id}", unlike).Methods(http.MethodPost)
795         r.HandleFunc("/retweet/{id}", retweet).Methods(http.MethodPost)
796         r.HandleFunc("/unretweet/{id}", unretweet).Methods(http.MethodPost)
797         r.HandleFunc("/vote/{id}", vote).Methods(http.MethodPost)
798         r.HandleFunc("/follow/{id}", follow).Methods(http.MethodPost)
799         r.HandleFunc("/unfollow/{id}", unfollow).Methods(http.MethodPost)
800         r.HandleFunc("/mute/{id}", mute).Methods(http.MethodPost)
801         r.HandleFunc("/unmute/{id}", unMute).Methods(http.MethodPost)
802         r.HandleFunc("/block/{id}", block).Methods(http.MethodPost)
803         r.HandleFunc("/unblock/{id}", unBlock).Methods(http.MethodPost)
804         r.HandleFunc("/subscribe/{id}", subscribe).Methods(http.MethodPost)
805         r.HandleFunc("/unsubscribe/{id}", unSubscribe).Methods(http.MethodPost)
806         r.HandleFunc("/settings", settings).Methods(http.MethodPost)
807         r.HandleFunc("/muteconv/{id}", muteConversation).Methods(http.MethodPost)
808         r.HandleFunc("/unmuteconv/{id}", unMuteConversation).Methods(http.MethodPost)
809         r.HandleFunc("/delete/{id}", delete).Methods(http.MethodPost)
810         r.HandleFunc("/notifications/read", readNotifications).Methods(http.MethodPost)
811         r.HandleFunc("/signout", signout).Methods(http.MethodPost)
812         r.HandleFunc("/fluoride/like/{id}", fLike).Methods(http.MethodPost)
813         r.HandleFunc("/fluoride/unlike/{id}", fUnlike).Methods(http.MethodPost)
814         r.HandleFunc("/fluoride/retweet/{id}", fRetweet).Methods(http.MethodPost)
815         r.HandleFunc("/fluoride/unretweet/{id}", fUnretweet).Methods(http.MethodPost)
816         r.PathPrefix("/static").Handler(http.StripPrefix("/static",
817                 http.FileServer(http.Dir(staticDir))))
818
819         return r
820 }