src/app/handlers.go (view raw)
1package app
2
3import (
4 "net/http"
5 "strconv"
6 "time"
7)
8
9func getIndexHandler(w http.ResponseWriter, r *http.Request) {
10 xt.ExecuteTemplate(w, "index.tmpl", nil)
11}
12
13func getHabitsHandler(w http.ResponseWriter, r *http.Request) {
14 user, ok := getLoggedUser(r)
15 if !ok {
16 http.Error(w, "Could not find user in context.", http.StatusInternalServerError)
17 return
18 }
19
20 positive, negative, err := getAllHabits(user.ID)
21 if err != nil {
22 http.Error(w, "Could not get user habits.", http.StatusInternalServerError)
23 return
24 }
25
26 data := map[string]interface{}{
27 "User": user,
28 "Positive": positive,
29 "Negative": negative,
30 }
31
32 xt.ExecuteTemplate(w, "habits.tmpl", data)
33}
34
35func getHabitsIDHandler(w http.ResponseWriter, r *http.Request) {
36 habit, err := getHabitHelper(w, r)
37 if err != nil {
38 return
39 }
40
41 xt.ExecuteTemplate(w, "habits-id.tmpl", habit)
42}
43
44func getNewPositiveHandler(w http.ResponseWriter, r *http.Request) {
45 data := map[string]interface{}{"Negative": false}
46 xt.ExecuteTemplate(w, "new.tmpl", data)
47}
48
49func getNewNegativeHandler(w http.ResponseWriter, r *http.Request) {
50 data := map[string]interface{}{"Negative": true}
51 xt.ExecuteTemplate(w, "new.tmpl", data)
52}
53
54func postNewHandler(w http.ResponseWriter, r *http.Request) {
55 negative := r.FormValue("negative") == "on"
56 name := r.FormValue("name")
57
58 if !checkHabitName(name) {
59 http.Error(w, "Bad habit name.", http.StatusBadRequest)
60 return
61 }
62
63 var days uint
64 if !negative {
65 res, err := strconv.ParseUint(r.FormValue("days"), 10, 64)
66 if err != nil {
67 http.Error(w, "Bad days value.", http.StatusBadRequest)
68 return
69 }
70 days = uint(res)
71 }
72
73 user, ok := getLoggedUser(r)
74 if !ok {
75 http.Error(w, "Could not get logged user", http.StatusInternalServerError)
76 return
77 }
78
79 db.Create(&Habit{
80 UserID: user.ID,
81 Name: name,
82 Days: days,
83 Negative: negative,
84 })
85
86 http.Redirect(w, r, "/habits", http.StatusFound)
87}
88
89func postHabitsIDHandler(w http.ResponseWriter, r *http.Request) {
90 habit, err := getHabitHelper(w, r)
91 if err != nil {
92 return
93 }
94
95 var changed bool
96
97 name := r.FormValue("name")
98 if name != habit.Name {
99 if !checkHabitName(name) {
100 http.Error(w, "Bad habit name.", http.StatusBadRequest)
101 return
102 }
103 habit.Name = name
104 changed = true
105 }
106
107 if !habit.Negative {
108 res, err := strconv.ParseUint(r.FormValue("days"), 10, 64)
109 if err != nil {
110 http.Error(w, "Bad days value.", http.StatusBadRequest)
111 return
112 }
113 days := uint(res)
114 if days != habit.Days {
115 habit.Days = days
116 changed = true
117 }
118 }
119
120 disabled := r.FormValue("enabled") != "on"
121 if disabled != habit.Disabled {
122 habit.Disabled = disabled
123 changed = true
124 }
125
126 if changed {
127 db.Save(&habit)
128 }
129
130 http.Redirect(w, r, "/habits", http.StatusFound)
131}
132
133func postDeleteIDHandler(w http.ResponseWriter, r *http.Request) {
134 id := getID(r)
135 if id == 0 {
136 http.Error(w, "bad request", http.StatusBadRequest)
137 return
138 }
139
140 user, ok := getLoggedUser(r)
141 if !ok {
142 http.Error(w, "unauthorized", http.StatusUnauthorized)
143 return
144 }
145
146 db.Delete(&Habit{}, "id = ? AND user_id = ?", id, user.ID)
147
148 http.Redirect(w, r, "/habits", http.StatusFound)
149}
150
151func postAckIDHandler(w http.ResponseWriter, r *http.Request) {
152 habit, err := getHabitHelper(w, r)
153 if err != nil {
154 return
155 }
156
157 if habit.LastAck != nil {
158 if time.Since(*habit.LastAck) < 6*time.Hour {
159 http.Redirect(w, r, "/habits", http.StatusFound) // TODO: redirect to an error page instead
160 return
161 }
162 }
163
164 db.Create(&Ack{HabitID: habit.ID})
165
166 now := time.Now()
167 habit.LastAck = &now
168 db.Save(&habit)
169
170 http.Redirect(w, r, "/habits", http.StatusFound)
171}
172
173func getRegisterHandler(w http.ResponseWriter, r *http.Request) {
174 xt.ExecuteTemplate(w, "auth-register.tmpl", nil)
175}
176
177func getLoginHandler(w http.ResponseWriter, r *http.Request) {
178 _, err := readSessionCookie(r)
179 if err != nil {
180 xt.ExecuteTemplate(w, "auth-login.tmpl", nil)
181 return
182 }
183
184 http.Redirect(w, r, "/habits", http.StatusFound)
185}
186
187func getResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
188 xt.ExecuteTemplate(w, "auth-reset_password.tmpl", nil)
189}
190
191func postRegisterHandler(w http.ResponseWriter, r *http.Request) {
192 if !registrationEnabled {
193 http.Error(w, "Registration is currently disabled.", http.StatusForbidden)
194 return
195 }
196
197 username, err := sanitizeUsername(r.FormValue("username"))
198 if err != nil {
199 http.Error(w, "Invalid username.", http.StatusBadRequest)
200 return
201 }
202
203 email, err := sanitizeEmail(r.FormValue("email"))
204 if err != nil {
205 http.Error(w, "Invalid email.", http.StatusBadRequest)
206 return
207 }
208
209 _, err = getUserByName(username, 0)
210 if err == nil {
211 http.Error(w, "This username is already registered.", http.StatusConflict)
212 return
213 }
214
215 hashedPassword, salt, err := g.HashPassword(r.FormValue("password"))
216 if err != nil {
217 http.Error(w, "Invalid password.", http.StatusBadRequest)
218 return
219 }
220
221 user := User{
222 Username: username,
223 Email: email,
224 PasswordHash: hashedPassword,
225 Salt: salt,
226 }
227
228 db.Create(&user)
229 if user.ID == 0 {
230 http.Error(w, "This email is already registered.", http.StatusConflict)
231 return
232 }
233
234 login(w, user.ID, false)
235 http.Redirect(w, r, "/login", http.StatusFound)
236}
237
238func postLoginHandler(w http.ResponseWriter, r *http.Request) {
239 username := r.FormValue("username")
240 password := r.FormValue("password")
241 remember := r.FormValue("remember")
242
243 user, err := getUserByName(username, 0)
244
245 if err != nil || !g.CheckPassword(password, user.Salt, user.PasswordHash) {
246 http.Error(w, "Invalid credentials", http.StatusUnauthorized)
247 return
248 }
249
250 login(w, user.ID, remember == "on")
251 http.Redirect(w, r, "/login", http.StatusFound)
252}
253
254func logoutHandler(w http.ResponseWriter, r *http.Request) {
255 http.SetCookie(w, g.GenerateEmptyCookie())
256 http.Redirect(w, r, "/login", http.StatusFound)
257}
258
259func postResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
260 emailInput := r.FormValue("email")
261
262 var user User
263 db.Where("email = ?", emailInput).First(&user)
264
265 if user.ID == 0 {
266 http.Redirect(w, r, "/login", http.StatusFound)
267 return
268 }
269
270 resetToken, err := g.GenerateRandomToken(32)
271 if err != nil {
272 http.Error(w, "Could not generate reset token.", http.StatusInternalServerError)
273 return
274 }
275
276 ks.Set("reset:"+resetToken, user.ID, time.Hour)
277 sendResetEmail(user.Email, resetToken)
278
279 http.Redirect(w, r, "/login", http.StatusFound)
280
281}
282
283func getResetPasswordConfirmHandler(w http.ResponseWriter, r *http.Request) {
284 token := r.URL.Query().Get("token")
285 _, err := ks.Get("reset:" + token)
286 if err != nil {
287 http.Error(w, "Token is invalid or expired.", http.StatusUnauthorized)
288 return
289 }
290
291 xt.ExecuteTemplate(w, "auth-new_password.tmpl", nil)
292}
293
294func postResetPasswordConfirmHandler(w http.ResponseWriter, r *http.Request) {
295 token := r.URL.Query().Get("token")
296 userID, err := ks.Get("reset:" + token)
297 if err != nil {
298 http.Error(w, "Token is invalid or expired.", http.StatusUnauthorized)
299 return
300 }
301
302 var user User
303 db.First(&user, *userID)
304
305 password := r.FormValue("password")
306
307 hashedPassword, salt, err := g.HashPassword(password)
308 if err != nil {
309 http.Error(w, "Invalid password.", http.StatusBadRequest)
310 return
311 }
312
313 user.PasswordHash = hashedPassword
314 user.Salt = salt
315 db.Save(&user)
316 ks.Delete(token)
317
318 http.Redirect(w, r, "/login", http.StatusFound)
319}