all repos — well-binge @ 4690b04bb57834544694aa1b0d740fe746ec7236

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	}
 61
 62	var days uint
 63	if !negative {
 64		res, err := strconv.ParseUint(r.FormValue("days"), 10, 64)
 65		if err != nil {
 66			http.Error(w, "Bad days value.", http.StatusBadRequest)
 67			return
 68		}
 69		days = uint(res)
 70	}
 71
 72	user, ok := getLoggedUser(r)
 73	if !ok {
 74		http.Error(w, "Could not get logged user", http.StatusInternalServerError)
 75	}
 76
 77	db.Create(&Habit{
 78		UserID:   user.ID,
 79		Name:     name,
 80		Days:     days,
 81		Negative: negative,
 82	})
 83
 84	http.Redirect(w, r, "/habits", http.StatusFound)
 85}
 86
 87func postAckIDHandler(w http.ResponseWriter, r *http.Request) {
 88	habit, err := getHabitHelper(w, r)
 89	if err != nil {
 90		return
 91	}
 92
 93	if habit.LastAck != nil {
 94		if time.Since(*habit.LastAck) < 6*time.Hour {
 95			http.Redirect(w, r, "/habits", http.StatusFound) // TODO: redirect to an error page instead
 96			return
 97		}
 98	}
 99
100	db.Create(Ack{HabitID: habit.ID})
101
102	now := time.Now()
103	habit.LastAck = &now
104	db.Save(&habit)
105
106	http.Redirect(w, r, "/habits", http.StatusFound)
107}
108
109func getRegisterHandler(w http.ResponseWriter, r *http.Request) {
110	xt.ExecuteTemplate(w, "auth-register.tmpl", nil)
111}
112
113func getLoginHandler(w http.ResponseWriter, r *http.Request) {
114	_, err := readSessionCookie(r)
115	if err != nil {
116		xt.ExecuteTemplate(w, "auth-login.tmpl", nil)
117		return
118	}
119
120	http.Redirect(w, r, "/habits", http.StatusFound)
121}
122
123func getResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
124	xt.ExecuteTemplate(w, "auth-reset_password.tmpl", nil)
125}
126
127func postRegisterHandler(w http.ResponseWriter, r *http.Request) {
128	if !registrationEnabled {
129		http.Error(w, "Registration is currently disabled.", http.StatusForbidden)
130		return
131	}
132
133	username, err := sanitizeUsername(r.FormValue("username"))
134	if err != nil {
135		http.Error(w, "Invalid username.", http.StatusBadRequest)
136		return
137	}
138
139	email, err := sanitizeEmail(r.FormValue("email"))
140	if err != nil {
141		http.Error(w, "Invalid email.", http.StatusBadRequest)
142		return
143	}
144
145	_, err = getUserByName(username, 0)
146	if err == nil {
147		http.Error(w, "This username is already registered.", http.StatusConflict)
148		return
149	}
150
151	hashedPassword, salt, err := g.HashPassword(r.FormValue("password"))
152	if err != nil {
153		http.Error(w, "Invalid password.", http.StatusBadRequest)
154		return
155	}
156
157	user := User{
158		Username:     username,
159		Email:        email,
160		PasswordHash: hashedPassword,
161		Salt:         salt,
162	}
163
164	db.Create(&user)
165	if user.ID == 0 {
166		http.Error(w, "This email is already registered.", http.StatusConflict)
167		return
168	}
169
170	login(w, user.ID, false)
171	http.Redirect(w, r, "/login", http.StatusFound)
172}
173
174func postLoginHandler(w http.ResponseWriter, r *http.Request) {
175	username := r.FormValue("username")
176	password := r.FormValue("password")
177	remember := r.FormValue("remember")
178
179	user, err := getUserByName(username, 0)
180
181	if err != nil || !g.CheckPassword(password, user.Salt, user.PasswordHash) {
182		http.Error(w, "Invalid credentials", http.StatusUnauthorized)
183		return
184	}
185
186	login(w, user.ID, remember == "on")
187	http.Redirect(w, r, "/login", http.StatusFound)
188}
189
190func logoutHandler(w http.ResponseWriter, r *http.Request) {
191	http.SetCookie(w, g.GenerateEmptyCookie())
192	http.Redirect(w, r, "/login", http.StatusFound)
193}
194
195func postResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
196	emailInput := r.FormValue("email")
197
198	var user User
199	db.Where("email = ?", emailInput).First(&user)
200
201	if user.ID == 0 {
202		http.Redirect(w, r, "/login", http.StatusFound)
203		return
204	}
205
206	resetToken, err := g.GenerateRandomToken(32)
207	if err != nil {
208		http.Error(w, "Could not generate reset token.", http.StatusInternalServerError)
209		return
210	}
211
212	ks.Set("reset:"+resetToken, user.ID, time.Hour)
213	sendResetEmail(user.Email, resetToken)
214
215	http.Redirect(w, r, "/login", http.StatusFound)
216
217}
218
219func getResetPasswordConfirmHandler(w http.ResponseWriter, r *http.Request) {
220	token := r.URL.Query().Get("token")
221	_, err := ks.Get("reset:" + token)
222	if err != nil {
223		http.Error(w, "Token is invalid or expired.", http.StatusUnauthorized)
224		return
225	}
226
227	xt.ExecuteTemplate(w, "auth-new_password.tmpl", nil)
228}
229
230func postResetPasswordConfirmHandler(w http.ResponseWriter, r *http.Request) {
231	token := r.URL.Query().Get("token")
232	userID, err := ks.Get("reset:" + token)
233	if err != nil {
234		http.Error(w, "Token is invalid or expired.", http.StatusUnauthorized)
235		return
236	}
237
238	var user User
239	db.First(&user, *userID)
240
241	password := r.FormValue("password")
242
243	hashedPassword, salt, err := g.HashPassword(password)
244	if err != nil {
245		http.Error(w, "Invalid password.", http.StatusBadRequest)
246		return
247	}
248
249	user.PasswordHash = hashedPassword
250	user.Salt = salt
251	db.Save(&user)
252	ks.Delete(token)
253
254	http.Redirect(w, r, "/login", http.StatusFound)
255}