all repos — well-binge @ main

Create positive, recurring habits.

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}