Add single instance mode
[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 const (
18         sessionExp = 365 * 24 * time.Hour
19 )
20
21 func newClient(w io.Writer) *model.Client {
22         return &model.Client{
23                 Writer: w,
24         }
25 }
26
27 func setSessionCookie(w http.ResponseWriter, sessionID string, exp time.Duration) {
28         http.SetCookie(w, &http.Cookie{
29                 Name:    "session_id",
30                 Value:   sessionID,
31                 Expires: time.Now().Add(exp),
32         })
33 }
34
35 func newCtxWithSesion(req *http.Request) context.Context {
36         ctx := context.Background()
37         sessionID, err := req.Cookie("session_id")
38         if err != nil {
39                 return ctx
40         }
41         return context.WithValue(ctx, "session_id", sessionID.Value)
42 }
43
44 func newCtxWithSesionCSRF(req *http.Request, csrfToken string) context.Context {
45         ctx := newCtxWithSesion(req)
46         return context.WithValue(ctx, "csrf_token", csrfToken)
47 }
48
49 func getMultipartFormValue(mf *multipart.Form, key string) (val string) {
50         vals, ok := mf.Value[key]
51         if !ok {
52                 return ""
53         }
54         if len(vals) < 1 {
55                 return ""
56         }
57         return vals[0]
58 }
59
60 func serveJson(w io.Writer, data interface{}) (err error) {
61         var d = make(map[string]interface{})
62         d["data"] = data
63         return json.NewEncoder(w).Encode(d)
64 }
65
66 func serveJsonError(w http.ResponseWriter, err error) {
67         var d = make(map[string]interface{})
68         d["error"] = err.Error()
69         w.WriteHeader(http.StatusInternalServerError)
70         json.NewEncoder(w).Encode(d)
71         return
72 }
73
74 func NewHandler(s Service, staticDir string) http.Handler {
75         r := mux.NewRouter()
76
77         rootPage := func(w http.ResponseWriter, req *http.Request) {
78                 sessionID, _ := req.Cookie("session_id")
79                 if sessionID != nil && len(sessionID.Value) > 0 {
80                         c := newClient(w)
81                         ctx := newCtxWithSesion(req)
82                         err := s.ServeRootPage(ctx, c)
83                         if err != nil {
84                                 w.WriteHeader(http.StatusInternalServerError)
85                                 s.ServeErrorPage(ctx, c, err)
86                                 return
87                         }
88                 } else {
89                         w.Header().Add("Location", "/signin")
90                         w.WriteHeader(http.StatusFound)
91                 }
92         }
93
94         navPage := func(w http.ResponseWriter, req *http.Request) {
95                 c := newClient(w)
96                 ctx := newCtxWithSesion(req)
97                 err := s.ServeNavPage(ctx, c)
98                 if err != nil {
99                         w.WriteHeader(http.StatusInternalServerError)
100                         s.ServeErrorPage(ctx, c, err)
101                         return
102                 }
103         }
104
105         signinPage := func(w http.ResponseWriter, req *http.Request) {
106                 c := newClient(w)
107                 ctx := context.Background()
108                 instance, ok := s.SingleInstance(ctx)
109                 if ok {
110                         url, sessionID, err := s.NewSession(ctx, instance)
111                         if err != nil {
112                                 w.WriteHeader(http.StatusInternalServerError)
113                                 s.ServeErrorPage(ctx, c, err)
114                                 return
115                         }
116
117                         setSessionCookie(w, sessionID, sessionExp)
118                         w.Header().Add("Location", url)
119                         w.WriteHeader(http.StatusFound)
120                 } else {
121                         err := s.ServeSigninPage(ctx, c)
122                         if err != nil {
123                                 w.WriteHeader(http.StatusInternalServerError)
124                                 s.ServeErrorPage(ctx, c, err)
125                                 return
126                         }
127                 }
128         }
129
130         timelinePage := func(w http.ResponseWriter, req *http.Request) {
131                 c := newClient(w)
132                 ctx := newCtxWithSesion(req)
133                 tType, _ := mux.Vars(req)["type"]
134                 maxID := req.URL.Query().Get("max_id")
135                 minID := req.URL.Query().Get("min_id")
136
137                 err := s.ServeTimelinePage(ctx, c, tType, maxID, minID)
138                 if err != nil {
139                         w.WriteHeader(http.StatusInternalServerError)
140                         s.ServeErrorPage(ctx, c, err)
141                         return
142                 }
143         }
144
145         timelineOldPage := func(w http.ResponseWriter, req *http.Request) {
146                 w.Header().Add("Location", "/timeline/home")
147                 w.WriteHeader(http.StatusFound)
148         }
149
150         threadPage := func(w http.ResponseWriter, req *http.Request) {
151                 c := newClient(w)
152                 ctx := newCtxWithSesion(req)
153                 id, _ := mux.Vars(req)["id"]
154                 reply := req.URL.Query().Get("reply")
155
156                 err := s.ServeThreadPage(ctx, c, id, len(reply) > 1)
157                 if err != nil {
158                         w.WriteHeader(http.StatusInternalServerError)
159                         s.ServeErrorPage(ctx, c, err)
160                         return
161                 }
162         }
163
164         likedByPage := func(w http.ResponseWriter, req *http.Request) {
165                 c := newClient(w)
166                 ctx := newCtxWithSesion(req)
167                 id, _ := mux.Vars(req)["id"]
168
169                 err := s.ServeLikedByPage(ctx, c, id)
170                 if err != nil {
171                         w.WriteHeader(http.StatusInternalServerError)
172                         s.ServeErrorPage(ctx, c, err)
173                         return
174                 }
175         }
176
177         retweetedByPage := func(w http.ResponseWriter, req *http.Request) {
178                 c := newClient(w)
179                 ctx := newCtxWithSesion(req)
180                 id, _ := mux.Vars(req)["id"]
181
182                 err := s.ServeRetweetedByPage(ctx, c, id)
183                 if err != nil {
184                         w.WriteHeader(http.StatusInternalServerError)
185                         s.ServeErrorPage(ctx, c, err)
186                         return
187                 }
188         }
189
190         notificationsPage := func(w http.ResponseWriter, req *http.Request) {
191                 c := newClient(w)
192                 ctx := newCtxWithSesion(req)
193                 maxID := req.URL.Query().Get("max_id")
194                 minID := req.URL.Query().Get("min_id")
195
196                 err := s.ServeNotificationPage(ctx, c, maxID, minID)
197                 if err != nil {
198                         w.WriteHeader(http.StatusInternalServerError)
199                         s.ServeErrorPage(ctx, c, err)
200                         return
201                 }
202         }
203
204         userPage := func(w http.ResponseWriter, req *http.Request) {
205                 c := newClient(w)
206                 ctx := newCtxWithSesion(req)
207                 id, _ := mux.Vars(req)["id"]
208                 pageType, _ := mux.Vars(req)["type"]
209                 maxID := req.URL.Query().Get("max_id")
210                 minID := req.URL.Query().Get("min_id")
211
212                 err := s.ServeUserPage(ctx, c, id, pageType, maxID, minID)
213                 if err != nil {
214                         w.WriteHeader(http.StatusInternalServerError)
215                         s.ServeErrorPage(ctx, c, err)
216                         return
217                 }
218         }
219
220         userSearchPage := func(w http.ResponseWriter, req *http.Request) {
221                 c := newClient(w)
222                 ctx := newCtxWithSesion(req)
223                 id, _ := mux.Vars(req)["id"]
224                 q := req.URL.Query().Get("q")
225                 offsetStr := req.URL.Query().Get("offset")
226
227                 var offset int
228                 var err error
229                 if len(offsetStr) > 1 {
230                         offset, err = strconv.Atoi(offsetStr)
231                         if err != nil {
232                                 w.WriteHeader(http.StatusInternalServerError)
233                                 s.ServeErrorPage(ctx, c, err)
234                                 return
235                         }
236                 }
237
238                 err = s.ServeUserSearchPage(ctx, c, id, q, offset)
239                 if err != nil {
240                         w.WriteHeader(http.StatusInternalServerError)
241                         s.ServeErrorPage(ctx, c, err)
242                         return
243                 }
244         }
245
246         aboutPage := func(w http.ResponseWriter, req *http.Request) {
247                 c := newClient(w)
248                 ctx := newCtxWithSesion(req)
249
250                 err := s.ServeAboutPage(ctx, c)
251                 if err != nil {
252                         w.WriteHeader(http.StatusInternalServerError)
253                         s.ServeErrorPage(ctx, c, err)
254                         return
255                 }
256         }
257
258         emojisPage := func(w http.ResponseWriter, req *http.Request) {
259                 c := newClient(w)
260                 ctx := newCtxWithSesion(req)
261
262                 err := s.ServeEmojiPage(ctx, c)
263                 if err != nil {
264                         w.WriteHeader(http.StatusInternalServerError)
265                         s.ServeErrorPage(ctx, c, err)
266                         return
267                 }
268         }
269
270         searchPage := func(w http.ResponseWriter, req *http.Request) {
271                 c := newClient(w)
272                 ctx := newCtxWithSesion(req)
273                 q := req.URL.Query().Get("q")
274                 qType := req.URL.Query().Get("type")
275                 offsetStr := req.URL.Query().Get("offset")
276
277                 var offset int
278                 var err error
279                 if len(offsetStr) > 1 {
280                         offset, err = strconv.Atoi(offsetStr)
281                         if err != nil {
282                                 w.WriteHeader(http.StatusInternalServerError)
283                                 s.ServeErrorPage(ctx, c, err)
284                                 return
285                         }
286                 }
287
288                 err = s.ServeSearchPage(ctx, c, q, qType, offset)
289                 if err != nil {
290                         w.WriteHeader(http.StatusInternalServerError)
291                         s.ServeErrorPage(ctx, c, err)
292                         return
293                 }
294         }
295
296         settingsPage := func(w http.ResponseWriter, req *http.Request) {
297                 c := newClient(w)
298                 ctx := newCtxWithSesion(req)
299
300                 err := s.ServeSettingsPage(ctx, c)
301                 if err != nil {
302                         w.WriteHeader(http.StatusInternalServerError)
303                         s.ServeErrorPage(ctx, c, err)
304                         return
305                 }
306         }
307
308         signin := func(w http.ResponseWriter, req *http.Request) {
309                 c := newClient(w)
310                 ctx := context.Background()
311                 instance := req.FormValue("instance")
312
313                 url, sessionID, err := s.NewSession(ctx, instance)
314                 if err != nil {
315                         w.WriteHeader(http.StatusInternalServerError)
316                         s.ServeErrorPage(ctx, c, err)
317                         return
318                 }
319
320                 setSessionCookie(w, sessionID, sessionExp)
321                 w.Header().Add("Location", url)
322                 w.WriteHeader(http.StatusFound)
323         }
324
325         oauthCallback := func(w http.ResponseWriter, req *http.Request) {
326                 c := newClient(w)
327                 ctx := newCtxWithSesion(req)
328                 token := req.URL.Query().Get("code")
329
330                 _, _, err := s.Signin(ctx, c, "", token)
331                 if err != nil {
332                         w.WriteHeader(http.StatusInternalServerError)
333                         s.ServeErrorPage(ctx, c, err)
334                         return
335                 }
336
337                 w.Header().Add("Location", "/")
338                 w.WriteHeader(http.StatusFound)
339         }
340
341         post := func(w http.ResponseWriter, req *http.Request) {
342                 c := newClient(w)
343                 err := req.ParseMultipartForm(4 << 20)
344                 if err != nil {
345                         w.WriteHeader(http.StatusInternalServerError)
346                         s.ServeErrorPage(context.Background(), c, err)
347                         return
348                 }
349
350                 ctx := newCtxWithSesionCSRF(req,
351                         getMultipartFormValue(req.MultipartForm, "csrf_token"))
352                 content := getMultipartFormValue(req.MultipartForm, "content")
353                 replyToID := getMultipartFormValue(req.MultipartForm, "reply_to_id")
354                 format := getMultipartFormValue(req.MultipartForm, "format")
355                 visibility := getMultipartFormValue(req.MultipartForm, "visibility")
356                 isNSFW := "on" == getMultipartFormValue(req.MultipartForm, "is_nsfw")
357                 files := req.MultipartForm.File["attachments"]
358
359                 id, err := s.Post(ctx, c, content, replyToID, format, visibility, isNSFW, files)
360                 if err != nil {
361                         w.WriteHeader(http.StatusInternalServerError)
362                         s.ServeErrorPage(ctx, c, err)
363                         return
364                 }
365
366                 location := req.Header.Get("Referer")
367                 if len(replyToID) > 0 {
368                         location = "/thread/" + replyToID + "#status-" + id
369                 }
370                 w.Header().Add("Location", location)
371                 w.WriteHeader(http.StatusFound)
372         }
373
374         like := 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.Like(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         unlike := 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.UnLike(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         retweet := 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.Retweet(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                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
434                 w.WriteHeader(http.StatusFound)
435         }
436
437         unretweet := func(w http.ResponseWriter, req *http.Request) {
438                 c := newClient(w)
439                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
440                 id, _ := mux.Vars(req)["id"]
441                 retweetedByID := req.FormValue("retweeted_by_id")
442
443                 _, err := s.UnRetweet(ctx, c, id)
444                 if err != nil {
445                         w.WriteHeader(http.StatusInternalServerError)
446                         s.ServeErrorPage(ctx, c, err)
447                         return
448                 }
449
450                 rID := id
451                 if len(retweetedByID) > 0 {
452                         rID = retweetedByID
453                 }
454
455                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+rID)
456                 w.WriteHeader(http.StatusFound)
457         }
458
459         vote := func(w http.ResponseWriter, req *http.Request) {
460                 c := newClient(w)
461                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
462                 id, _ := mux.Vars(req)["id"]
463                 statusID := req.FormValue("status_id")
464                 choices, _ := req.PostForm["choices"]
465
466                 err := s.Vote(ctx, c, id, choices)
467                 if err != nil {
468                         w.WriteHeader(http.StatusInternalServerError)
469                         s.ServeErrorPage(ctx, c, err)
470                         return
471                 }
472
473                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+statusID)
474                 w.WriteHeader(http.StatusFound)
475         }
476
477         follow := func(w http.ResponseWriter, req *http.Request) {
478                 c := newClient(w)
479                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
480                 id, _ := mux.Vars(req)["id"]
481
482                 var reblogs *bool
483                 r, ok := req.URL.Query()["reblogs"]
484                 if ok && len(r) > 0 {
485                         reblogs = new(bool)
486                         *reblogs = r[0] == "true"
487                 }
488
489                 err := s.Follow(ctx, c, id, reblogs)
490                 if err != nil {
491                         w.WriteHeader(http.StatusInternalServerError)
492                         s.ServeErrorPage(ctx, c, err)
493                         return
494                 }
495
496                 w.Header().Add("Location", req.Header.Get("Referer"))
497                 w.WriteHeader(http.StatusFound)
498         }
499
500         unfollow := func(w http.ResponseWriter, req *http.Request) {
501                 c := newClient(w)
502                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
503                 id, _ := mux.Vars(req)["id"]
504
505                 err := s.UnFollow(ctx, c, id)
506                 if err != nil {
507                         w.WriteHeader(http.StatusInternalServerError)
508                         s.ServeErrorPage(ctx, c, err)
509                         return
510                 }
511
512                 w.Header().Add("Location", req.Header.Get("Referer"))
513                 w.WriteHeader(http.StatusFound)
514         }
515
516         mute := func(w http.ResponseWriter, req *http.Request) {
517                 c := newClient(w)
518                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
519                 id, _ := mux.Vars(req)["id"]
520
521                 err := s.Mute(ctx, c, id)
522                 if err != nil {
523                         w.WriteHeader(http.StatusInternalServerError)
524                         s.ServeErrorPage(ctx, c, err)
525                         return
526                 }
527
528                 w.Header().Add("Location", req.Header.Get("Referer"))
529                 w.WriteHeader(http.StatusFound)
530         }
531
532         unMute := func(w http.ResponseWriter, req *http.Request) {
533                 c := newClient(w)
534                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
535                 id, _ := mux.Vars(req)["id"]
536
537                 err := s.UnMute(ctx, c, id)
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         block := 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.Block(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         unBlock := 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.UnBlock(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         subscribe := 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.Subscribe(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         unSubscribe := func(w http.ResponseWriter, req *http.Request) {
597                 c := newClient(w)
598                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
599                 id, _ := mux.Vars(req)["id"]
600
601                 err := s.UnSubscribe(ctx, c, id)
602                 if err != nil {
603                         w.WriteHeader(http.StatusInternalServerError)
604                         s.ServeErrorPage(ctx, c, err)
605                         return
606                 }
607
608                 w.Header().Add("Location", req.Header.Get("Referer"))
609                 w.WriteHeader(http.StatusFound)
610         }
611
612         settings := func(w http.ResponseWriter, req *http.Request) {
613                 c := newClient(w)
614                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
615                 visibility := req.FormValue("visibility")
616                 copyScope := req.FormValue("copy_scope") == "true"
617                 threadInNewTab := req.FormValue("thread_in_new_tab") == "true"
618                 maskNSFW := req.FormValue("mask_nsfw") == "true"
619                 arn := req.FormValue("auto_refresh_notifications") == "true"
620                 fluorideMode := req.FormValue("fluoride_mode") == "true"
621                 darkMode := req.FormValue("dark_mode") == "true"
622
623                 settings := &model.Settings{
624                         DefaultVisibility:        visibility,
625                         CopyScope:                copyScope,
626                         ThreadInNewTab:           threadInNewTab,
627                         MaskNSFW:                 maskNSFW,
628                         AutoRefreshNotifications: arn,
629                         FluorideMode:             fluorideMode,
630                         DarkMode:                 darkMode,
631                 }
632
633                 err := s.SaveSettings(ctx, c, settings)
634                 if err != nil {
635                         w.WriteHeader(http.StatusInternalServerError)
636                         s.ServeErrorPage(ctx, c, err)
637                         return
638                 }
639
640                 w.Header().Add("Location", "/")
641                 w.WriteHeader(http.StatusFound)
642         }
643
644         muteConversation := func(w http.ResponseWriter, req *http.Request) {
645                 c := newClient(w)
646                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
647                 id, _ := mux.Vars(req)["id"]
648
649                 err := s.MuteConversation(ctx, c, id)
650                 if err != nil {
651                         w.WriteHeader(http.StatusInternalServerError)
652                         s.ServeErrorPage(ctx, c, err)
653                         return
654                 }
655
656                 w.Header().Add("Location", req.Header.Get("Referer"))
657                 w.WriteHeader(http.StatusFound)
658         }
659
660         unMuteConversation := 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                 err := s.UnMuteConversation(ctx, c, id)
666                 if err != nil {
667                         w.WriteHeader(http.StatusInternalServerError)
668                         s.ServeErrorPage(ctx, c, err)
669                         return
670                 }
671
672                 w.Header().Add("Location", req.Header.Get("Referer"))
673                 w.WriteHeader(http.StatusFound)
674         }
675
676         delete := func(w http.ResponseWriter, req *http.Request) {
677                 c := newClient(w)
678                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
679                 id, _ := mux.Vars(req)["id"]
680
681                 err := s.Delete(ctx, c, id)
682                 if err != nil {
683                         w.WriteHeader(http.StatusInternalServerError)
684                         s.ServeErrorPage(ctx, c, err)
685                         return
686                 }
687
688                 w.Header().Add("Location", req.Header.Get("Referer"))
689                 w.WriteHeader(http.StatusFound)
690         }
691
692         readNotifications := func(w http.ResponseWriter, req *http.Request) {
693                 c := newClient(w)
694                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
695                 maxID := req.URL.Query().Get("max_id")
696
697                 err := s.ReadNotifications(ctx, c, maxID)
698                 if err != nil {
699                         w.WriteHeader(http.StatusInternalServerError)
700                         s.ServeErrorPage(ctx, c, err)
701                         return
702                 }
703
704                 w.Header().Add("Location", req.Header.Get("Referer"))
705                 w.WriteHeader(http.StatusFound)
706         }
707
708         signout := func(w http.ResponseWriter, req *http.Request) {
709                 c := newClient(w)
710                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
711
712                 s.Signout(ctx, c)
713
714                 setSessionCookie(w, "", 0)
715                 w.Header().Add("Location", "/")
716                 w.WriteHeader(http.StatusFound)
717         }
718
719         fLike := func(w http.ResponseWriter, req *http.Request) {
720                 c := newClient(w)
721                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
722                 id, _ := mux.Vars(req)["id"]
723
724                 count, err := s.Like(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         fUnlike := 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                 count, err := s.UnLike(ctx, c, id)
742                 if err != nil {
743                         serveJsonError(w, err)
744                         return
745                 }
746
747                 err = serveJson(w, count)
748                 if err != nil {
749                         serveJsonError(w, err)
750                         return
751                 }
752         }
753
754         fRetweet := func(w http.ResponseWriter, req *http.Request) {
755                 c := newClient(w)
756                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
757                 id, _ := mux.Vars(req)["id"]
758
759                 count, err := s.Retweet(ctx, c, id)
760                 if err != nil {
761                         serveJsonError(w, err)
762                         return
763                 }
764
765                 err = serveJson(w, count)
766                 if err != nil {
767                         serveJsonError(w, err)
768                         return
769                 }
770         }
771
772         fUnretweet := func(w http.ResponseWriter, req *http.Request) {
773                 c := newClient(w)
774                 ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
775                 id, _ := mux.Vars(req)["id"]
776
777                 count, err := s.UnRetweet(ctx, c, id)
778                 if err != nil {
779                         serveJsonError(w, err)
780                         return
781                 }
782
783                 err = serveJson(w, count)
784                 if err != nil {
785                         serveJsonError(w, err)
786                         return
787                 }
788         }
789
790         r.HandleFunc("/", rootPage).Methods(http.MethodGet)
791         r.HandleFunc("/nav", navPage).Methods(http.MethodGet)
792         r.HandleFunc("/signin", signinPage).Methods(http.MethodGet)
793         r.HandleFunc("//{type}", timelinePage).Methods(http.MethodGet)
794         r.HandleFunc("/timeline/{type}", timelinePage).Methods(http.MethodGet)
795         r.HandleFunc("/timeline", timelineOldPage).Methods(http.MethodGet)
796         r.HandleFunc("/thread/{id}", threadPage).Methods(http.MethodGet)
797         r.HandleFunc("/likedby/{id}", likedByPage).Methods(http.MethodGet)
798         r.HandleFunc("/retweetedby/{id}", retweetedByPage).Methods(http.MethodGet)
799         r.HandleFunc("/notifications", notificationsPage).Methods(http.MethodGet)
800         r.HandleFunc("/user/{id}", userPage).Methods(http.MethodGet)
801         r.HandleFunc("/user/{id}/{type}", userPage).Methods(http.MethodGet)
802         r.HandleFunc("/usersearch/{id}", userSearchPage).Methods(http.MethodGet)
803         r.HandleFunc("/about", aboutPage).Methods(http.MethodGet)
804         r.HandleFunc("/emojis", emojisPage).Methods(http.MethodGet)
805         r.HandleFunc("/search", searchPage).Methods(http.MethodGet)
806         r.HandleFunc("/settings", settingsPage).Methods(http.MethodGet)
807         r.HandleFunc("/signin", signin).Methods(http.MethodPost)
808         r.HandleFunc("/oauth_callback", oauthCallback).Methods(http.MethodGet)
809         r.HandleFunc("/post", post).Methods(http.MethodPost)
810         r.HandleFunc("/like/{id}", like).Methods(http.MethodPost)
811         r.HandleFunc("/unlike/{id}", unlike).Methods(http.MethodPost)
812         r.HandleFunc("/retweet/{id}", retweet).Methods(http.MethodPost)
813         r.HandleFunc("/unretweet/{id}", unretweet).Methods(http.MethodPost)
814         r.HandleFunc("/vote/{id}", vote).Methods(http.MethodPost)
815         r.HandleFunc("/follow/{id}", follow).Methods(http.MethodPost)
816         r.HandleFunc("/unfollow/{id}", unfollow).Methods(http.MethodPost)
817         r.HandleFunc("/mute/{id}", mute).Methods(http.MethodPost)
818         r.HandleFunc("/unmute/{id}", unMute).Methods(http.MethodPost)
819         r.HandleFunc("/block/{id}", block).Methods(http.MethodPost)
820         r.HandleFunc("/unblock/{id}", unBlock).Methods(http.MethodPost)
821         r.HandleFunc("/subscribe/{id}", subscribe).Methods(http.MethodPost)
822         r.HandleFunc("/unsubscribe/{id}", unSubscribe).Methods(http.MethodPost)
823         r.HandleFunc("/settings", settings).Methods(http.MethodPost)
824         r.HandleFunc("/muteconv/{id}", muteConversation).Methods(http.MethodPost)
825         r.HandleFunc("/unmuteconv/{id}", unMuteConversation).Methods(http.MethodPost)
826         r.HandleFunc("/delete/{id}", delete).Methods(http.MethodPost)
827         r.HandleFunc("/notifications/read", readNotifications).Methods(http.MethodPost)
828         r.HandleFunc("/signout", signout).Methods(http.MethodPost)
829         r.HandleFunc("/fluoride/like/{id}", fLike).Methods(http.MethodPost)
830         r.HandleFunc("/fluoride/unlike/{id}", fUnlike).Methods(http.MethodPost)
831         r.HandleFunc("/fluoride/retweet/{id}", fRetweet).Methods(http.MethodPost)
832         r.HandleFunc("/fluoride/unretweet/{id}", fUnretweet).Methods(http.MethodPost)
833         r.PathPrefix("/static").Handler(http.StripPrefix("/static",
834                 http.FileServer(http.Dir(staticDir))))
835
836         return r
837 }