1326c580f99575685094a33272a2083ac23304ae
[bloat] / service / transport.go
1 package service
2
3 import (
4         "context"
5         "fmt"
6         "mime/multipart"
7         "net/http"
8         "path"
9
10         "github.com/gorilla/mux"
11 )
12
13 var (
14         ctx       = context.Background()
15         cookieAge = "31536000"
16 )
17
18 func getContextWithSession(ctx context.Context, req *http.Request) context.Context {
19         sessionID, err := req.Cookie("session_id")
20         if err != nil {
21                 return ctx
22         }
23         return context.WithValue(ctx, "session_id", sessionID.Value)
24 }
25
26 func NewHandler(s Service, staticDir string) http.Handler {
27         r := mux.NewRouter()
28
29         r.PathPrefix("/static").Handler(http.StripPrefix("/static",
30                 http.FileServer(http.Dir(path.Join(".", staticDir)))))
31
32         r.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
33                 location := "/signin"
34
35                 sessionID, _ := req.Cookie("session_id")
36                 if sessionID != nil && len(sessionID.Value) > 0 {
37                         location = "/timeline"
38                 }
39
40                 w.Header().Add("Location", location)
41                 w.WriteHeader(http.StatusFound)
42         }).Methods(http.MethodGet)
43
44         r.HandleFunc("/signin", func(w http.ResponseWriter, req *http.Request) {
45                 err := s.ServeSigninPage(ctx, w)
46                 if err != nil {
47                         s.ServeErrorPage(ctx, w, err)
48                         return
49                 }
50         }).Methods(http.MethodGet)
51
52         r.HandleFunc("/signin", func(w http.ResponseWriter, req *http.Request) {
53                 instance := req.FormValue("instance")
54                 url, sessionId, err := s.GetAuthUrl(ctx, instance)
55                 if err != nil {
56                         s.ServeErrorPage(ctx, w, err)
57                         return
58                 }
59
60                 w.Header().Add("Set-Cookie", fmt.Sprintf("session_id=%s;max-age=%s", sessionId, cookieAge))
61                 w.Header().Add("Location", url)
62                 w.WriteHeader(http.StatusFound)
63         }).Methods(http.MethodPost)
64
65         r.HandleFunc("/oauth_callback", func(w http.ResponseWriter, req *http.Request) {
66                 ctx := getContextWithSession(context.Background(), req)
67                 token := req.URL.Query().Get("code")
68                 _, err := s.GetUserToken(ctx, "", nil, token)
69                 if err != nil {
70                         s.ServeErrorPage(ctx, w, err)
71                         return
72                 }
73
74                 w.Header().Add("Location", "/timeline")
75                 w.WriteHeader(http.StatusFound)
76         }).Methods(http.MethodGet)
77
78         r.HandleFunc("/timeline", func(w http.ResponseWriter, req *http.Request) {
79                 ctx := getContextWithSession(context.Background(), req)
80
81                 maxID := req.URL.Query().Get("max_id")
82                 sinceID := req.URL.Query().Get("since_id")
83                 minID := req.URL.Query().Get("min_id")
84
85                 err := s.ServeTimelinePage(ctx, w, nil, maxID, sinceID, minID)
86                 if err != nil {
87                         s.ServeErrorPage(ctx, w, err)
88                         return
89                 }
90         }).Methods(http.MethodGet)
91
92         r.HandleFunc("/thread/{id}", func(w http.ResponseWriter, req *http.Request) {
93                 ctx := getContextWithSession(context.Background(), req)
94                 id, _ := mux.Vars(req)["id"]
95                 reply := req.URL.Query().Get("reply")
96                 err := s.ServeThreadPage(ctx, w, nil, id, len(reply) > 1)
97                 if err != nil {
98                         s.ServeErrorPage(ctx, w, err)
99                         return
100                 }
101         }).Methods(http.MethodGet)
102
103         r.HandleFunc("/like/{id}", func(w http.ResponseWriter, req *http.Request) {
104                 ctx := getContextWithSession(context.Background(), req)
105                 id, _ := mux.Vars(req)["id"]
106                 err := s.Like(ctx, w, nil, id)
107                 if err != nil {
108                         s.ServeErrorPage(ctx, w, err)
109                         return
110                 }
111
112                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+id)
113                 w.WriteHeader(http.StatusFound)
114         }).Methods(http.MethodGet)
115
116         r.HandleFunc("/unlike/{id}", func(w http.ResponseWriter, req *http.Request) {
117                 ctx := getContextWithSession(context.Background(), req)
118                 id, _ := mux.Vars(req)["id"]
119                 err := s.UnLike(ctx, w, nil, id)
120                 if err != nil {
121                         s.ServeErrorPage(ctx, w, err)
122                         return
123                 }
124
125                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+id)
126                 w.WriteHeader(http.StatusFound)
127         }).Methods(http.MethodGet)
128
129         r.HandleFunc("/retweet/{id}", func(w http.ResponseWriter, req *http.Request) {
130                 ctx := getContextWithSession(context.Background(), req)
131                 id, _ := mux.Vars(req)["id"]
132                 err := s.Retweet(ctx, w, nil, id)
133                 if err != nil {
134                         s.ServeErrorPage(ctx, w, err)
135                         return
136                 }
137
138                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+id)
139                 w.WriteHeader(http.StatusFound)
140         }).Methods(http.MethodGet)
141
142         r.HandleFunc("/unretweet/{id}", func(w http.ResponseWriter, req *http.Request) {
143                 ctx := getContextWithSession(context.Background(), req)
144                 id, _ := mux.Vars(req)["id"]
145                 err := s.UnRetweet(ctx, w, nil, id)
146                 if err != nil {
147                         s.ServeErrorPage(ctx, w, err)
148                         return
149                 }
150
151                 w.Header().Add("Location", req.Header.Get("Referer")+"#status-"+id)
152                 w.WriteHeader(http.StatusFound)
153         }).Methods(http.MethodGet)
154
155         r.HandleFunc("/post", func(w http.ResponseWriter, req *http.Request) {
156                 ctx := getContextWithSession(context.Background(), req)
157
158                 err := req.ParseMultipartForm(4 << 20)
159                 if err != nil {
160                         s.ServeErrorPage(ctx, w, err)
161                         return
162                 }
163
164                 content := getMultipartFormValue(req.MultipartForm, "content")
165                 replyToID := getMultipartFormValue(req.MultipartForm, "reply_to_id")
166                 files := req.MultipartForm.File["attachments"]
167
168                 id, err := s.PostTweet(ctx, w, nil, content, replyToID, files)
169                 if err != nil {
170                         s.ServeErrorPage(ctx, w, err)
171                         return
172                 }
173
174                 location := "/timeline" + "#status-" + id
175                 if len(replyToID) > 0 {
176                         location = "/thread/" + replyToID + "#status-" + id
177                 }
178                 w.Header().Add("Location", location)
179                 w.WriteHeader(http.StatusFound)
180         }).Methods(http.MethodPost)
181
182         r.HandleFunc("/notifications", func(w http.ResponseWriter, req *http.Request) {
183                 ctx := getContextWithSession(context.Background(), req)
184
185                 maxID := req.URL.Query().Get("max_id")
186                 minID := req.URL.Query().Get("min_id")
187
188                 err := s.ServeNotificationPage(ctx, w, nil, maxID, minID)
189                 if err != nil {
190                         s.ServeErrorPage(ctx, w, err)
191                         return
192                 }
193         }).Methods(http.MethodGet)
194
195         r.HandleFunc("/signout", func(w http.ResponseWriter, req *http.Request) {
196                 // TODO remove session from database
197                 w.Header().Add("Set-Cookie", fmt.Sprintf("session_id=;max-age=0"))
198                 w.Header().Add("Location", "/")
199                 w.WriteHeader(http.StatusFound)
200         }).Methods(http.MethodGet)
201
202         return r
203 }
204
205 func getMultipartFormValue(mf *multipart.Form, key string) (val string) {
206         vals, ok := mf.Value[key]
207         if !ok {
208                 return ""
209         }
210         if len(vals) < 1 {
211                 return ""
212         }
213         return vals[0]
214 }