chore: #123 unify metrics, logs, variable names and structure
This commit is contained in:
@@ -5,7 +5,7 @@ import (
|
||||
"crypto/subtle"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"strings"
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
)
|
||||
|
||||
type User struct {
|
||||
user_uuid uuid.UUID
|
||||
email string
|
||||
id uuid.UUID
|
||||
email string
|
||||
}
|
||||
|
||||
func HandleSignInPage(db *sql.DB) http.HandlerFunc {
|
||||
@@ -49,7 +49,7 @@ func UserInfoComp(user *User) templ.Component {
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSignUp(db *sql.DB) http.HandlerFunc {
|
||||
func HandleSignUpComp(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var email = r.FormValue("email")
|
||||
var password = r.FormValue("password")
|
||||
@@ -69,9 +69,9 @@ func HandleSignUp(db *sql.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
user_uuid, err := uuid.NewRandom()
|
||||
userId, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
log.Printf("Could not generate UUID: %v", err)
|
||||
slog.Error("Could not generate UUID: %v", err)
|
||||
auth.Error("Internal Server Error").Render(r.Context(), w)
|
||||
return
|
||||
}
|
||||
@@ -79,14 +79,14 @@ func HandleSignUp(db *sql.DB) http.HandlerFunc {
|
||||
salt := make([]byte, 16)
|
||||
_, err = rand.Read(salt)
|
||||
if err != nil {
|
||||
log.Printf("Could not generate salt: %v", err)
|
||||
slog.Error("Could not generate salt: %v", err)
|
||||
auth.Error("Internal Server Error").Render(r.Context(), w)
|
||||
return
|
||||
}
|
||||
|
||||
hash := getHashPassword(password, salt)
|
||||
|
||||
_, err = db.Exec("INSERT INTO user (user_uuid, email, email_verified, is_admin, password, salt, created_at) VALUES (?, ?, FALSE, FALSE, ?, ?, datetime())", user_uuid, email, hash, salt)
|
||||
_, err = db.Exec("INSERT INTO user (user_uuid, email, email_verified, is_admin, password, salt, created_at) VALUES (?, ?, FALSE, FALSE, ?, ?, datetime())", userId, email, hash, salt)
|
||||
if err != nil {
|
||||
// This does leak information about the email being in use, though not specifically stated
|
||||
// It needs to be refacoteres to "If the email is not already in use, an email has been send to your address", or something
|
||||
@@ -99,21 +99,20 @@ func HandleSignUp(db *sql.DB) http.HandlerFunc {
|
||||
}
|
||||
|
||||
auth.Error("Internal Server Error").Render(r.Context(), w)
|
||||
log.Printf("Could not insert user: %v", err)
|
||||
slog.Error("Could not insert user: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
result := tryCreateSessionAndSetCookie(r, w, db, user_uuid)
|
||||
result := tryCreateSessionAndSetCookie(r, w, db, userId)
|
||||
if !result {
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("HX-Redirect", "/")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSignIn(db *sql.DB) http.HandlerFunc {
|
||||
func HandleSignInComp(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var email = r.FormValue("email")
|
||||
var password = r.FormValue("password")
|
||||
@@ -121,10 +120,10 @@ func HandleSignIn(db *sql.DB) http.HandlerFunc {
|
||||
var result bool = true
|
||||
start := time.Now()
|
||||
|
||||
var user_uuid uuid.UUID
|
||||
var saved_hash []byte
|
||||
var userId uuid.UUID
|
||||
var savedHash []byte
|
||||
var salt []byte
|
||||
err := db.QueryRow("SELECT user_uuid, password, salt FROM user WHERE email = ?", email).Scan(&user_uuid, &saved_hash, &salt)
|
||||
err := db.QueryRow("SELECT user_uuid, password, salt FROM user WHERE email = ?", email).Scan(&userId, &savedHash, &salt)
|
||||
if err != nil {
|
||||
result = false
|
||||
}
|
||||
@@ -132,26 +131,26 @@ func HandleSignIn(db *sql.DB) http.HandlerFunc {
|
||||
if result {
|
||||
new_hash := getHashPassword(password, salt)
|
||||
|
||||
if subtle.ConstantTimeCompare(new_hash, saved_hash) == 0 {
|
||||
if subtle.ConstantTimeCompare(new_hash, savedHash) == 0 {
|
||||
result = false
|
||||
}
|
||||
}
|
||||
|
||||
if result {
|
||||
result := tryCreateSessionAndSetCookie(r, w, db, user_uuid)
|
||||
result := tryCreateSessionAndSetCookie(r, w, db, userId)
|
||||
if !result {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
duration := time.Since(start)
|
||||
time_to_wait := 100 - duration.Milliseconds()
|
||||
timeToWait := 100 - duration.Milliseconds()
|
||||
// It is important to sleep for a while to prevent timing attacks
|
||||
// If the email is correct, the server will calculate the hash, which will take some time
|
||||
// This way an attacker could guess emails when comparing the response time
|
||||
// Because of that, we cant use WriteHeader in the middle of the function. We have to wait until the end
|
||||
// Unfortunatly this makes the code harder to read
|
||||
time.Sleep(time.Duration(time_to_wait) * time.Millisecond)
|
||||
time.Sleep(time.Duration(timeToWait) * time.Millisecond)
|
||||
|
||||
if result {
|
||||
w.Header().Add("HX-Redirect", "/")
|
||||
@@ -168,7 +167,7 @@ func HandleSignOutComp(db *sql.DB) http.HandlerFunc {
|
||||
|
||||
_, err := db.Exec("DELETE FROM session WHERE session_id = ?", id)
|
||||
if err != nil {
|
||||
log.Printf("Could not delete session: %v", err)
|
||||
slog.Error("Could not delete session: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -188,36 +187,11 @@ func HandleSignOutComp(db *sql.DB) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// var (
|
||||
// metricsAuthSignUp = promauto.NewCounterVec(
|
||||
// prometheus.CounterOpts{
|
||||
// Name: "mefit_api_auth_signup_total",
|
||||
// Help: "The total number of auth signup api requests processed",
|
||||
// },
|
||||
// []string{"result"},
|
||||
// )
|
||||
//
|
||||
// metricsError = promauto.NewCounterVec(
|
||||
// prometheus.CounterOpts{
|
||||
// Name: "mefit_api_error_total",
|
||||
// Help: "The total number of errors",
|
||||
// },
|
||||
// []string{"result"},
|
||||
// )
|
||||
//
|
||||
// // metricsAuthSignIn = promauto.NewCounterVec(
|
||||
// // prometheus.CounterOpts{
|
||||
// // Name: "mefit_api_auth_signin_total",
|
||||
// // },
|
||||
// // []string{"result"},
|
||||
// // )
|
||||
// )
|
||||
|
||||
func tryCreateSessionAndSetCookie(r *http.Request, w http.ResponseWriter, db *sql.DB, user_uuid uuid.UUID) bool {
|
||||
var session_id_bytes []byte = make([]byte, 32)
|
||||
_, err := rand.Reader.Read(session_id_bytes)
|
||||
if err != nil {
|
||||
log.Printf("Could not generate session ID: %v", err)
|
||||
slog.Error("Could not generate session ID: %v", err)
|
||||
auth.Error("Internal Server Error").Render(r.Context(), w)
|
||||
return false
|
||||
}
|
||||
@@ -225,7 +199,7 @@ func tryCreateSessionAndSetCookie(r *http.Request, w http.ResponseWriter, db *sq
|
||||
|
||||
_, err = db.Exec("INSERT INTO session (session_id, user_uuid, created_at) VALUES (?, ?, datetime())", session_id, user_uuid)
|
||||
if err != nil {
|
||||
log.Printf("Could not insert session: %v", err)
|
||||
slog.Error("Could not insert session: %v", err)
|
||||
auth.Error("Internal Server Error").Render(r.Context(), w)
|
||||
return false
|
||||
}
|
||||
@@ -254,25 +228,25 @@ func getSessionID(r *http.Request) string {
|
||||
}
|
||||
|
||||
func verifySessionAndReturnUser(db *sql.DB, r *http.Request) *User {
|
||||
session_id := getSessionID(r)
|
||||
if session_id == "" {
|
||||
sessionId := getSessionID(r)
|
||||
if sessionId == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var user User
|
||||
var created_at time.Time
|
||||
var createdAt time.Time
|
||||
|
||||
err := db.QueryRow(`
|
||||
SELECT u.user_uuid, u.email, s.created_at
|
||||
FROM session s
|
||||
INNER JOIN user u ON s.user_uuid = u.user_uuid
|
||||
WHERE session_id = ?`, session_id).Scan(&user.user_uuid, &user.email, &created_at)
|
||||
WHERE session_id = ?`, sessionId).Scan(&user.id, &user.email, &createdAt)
|
||||
if err != nil {
|
||||
log.Printf("Could not verify session: %v", err)
|
||||
slog.Error("Could not verify session: " + err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
if created_at.Add(time.Duration(8 * time.Hour)).Before(time.Now()) {
|
||||
if createdAt.Add(time.Duration(8 * time.Hour)).Before(time.Now()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@ import (
|
||||
func HandleIndexAnd404(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var comp templ.Component = nil
|
||||
user_comp := UserInfoComp(verifySessionAndReturnUser(db, r))
|
||||
userComp := UserInfoComp(verifySessionAndReturnUser(db, r))
|
||||
|
||||
if r.URL.Path != "/" {
|
||||
comp = template.Layout(template.NotFound(), user_comp)
|
||||
comp = template.Layout(template.NotFound(), userComp)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
comp = template.Layout(template.Index(), user_comp)
|
||||
comp = template.Layout(template.Index(), userComp)
|
||||
}
|
||||
|
||||
comp.Render(r.Context(), w)
|
||||
|
||||
@@ -8,38 +8,28 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
metrics = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "mefit_api_workout_total",
|
||||
Help: "The total number of workout api requests processed",
|
||||
},
|
||||
[]string{"type"},
|
||||
)
|
||||
)
|
||||
|
||||
func HandleWorkoutPage(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
user := verifySessionAndReturnUser(db, r)
|
||||
if user == nil {
|
||||
http.Redirect(w, r, "/auth/signin", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
currentDate := time.Now().Format("2006-01-02")
|
||||
inner := workout.WorkoutComp(currentDate)
|
||||
user_comp := UserInfoComp(verifySessionAndReturnUser(db, r))
|
||||
layout := template.Layout(inner, user_comp)
|
||||
layout.Render(r.Context(), w)
|
||||
userComp := UserInfoComp(user)
|
||||
template.Layout(inner, userComp).Render(r.Context(), w)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleNewWorkout(db *sql.DB) http.HandlerFunc {
|
||||
func HandleWorkoutNewComp(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
metrics.WithLabelValues("new").Inc()
|
||||
|
||||
user := verifySessionAndReturnUser(db, r)
|
||||
if user == nil {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
w.Header().Add("HX-Redirect", "/auth/signin")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -71,7 +61,7 @@ func HandleNewWorkout(db *sql.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.Exec("INSERT INTO workout (user_id, date, type, sets, reps) VALUES (?, ?, ?, ?, ?)", user.user_uuid, date, typeStr, sets, reps)
|
||||
_, err = db.Exec("INSERT INTO workout (user_id, date, type, sets, reps) VALUES (?, ?, ?, ?, ?)", user.id, date, typeStr, sets, reps)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@@ -88,17 +78,15 @@ func HandleNewWorkout(db *sql.DB) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func HandleGetWorkouts(db *sql.DB) http.HandlerFunc {
|
||||
func HandleWorkoutGetComp(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
metrics.WithLabelValues("get").Inc()
|
||||
|
||||
user := verifySessionAndReturnUser(db, r)
|
||||
if user == nil {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
w.Header().Add("HX-Redirect", "/auth/signin")
|
||||
return
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT rowid, date, type, sets, reps FROM workout WHERE user_id = ?", user.user_uuid)
|
||||
rows, err := db.Query("SELECT rowid, date, type, sets, reps FROM workout WHERE user_id = ?", user.id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -127,11 +115,13 @@ func HandleGetWorkouts(db *sql.DB) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func HandleDeleteWorkout(db *sql.DB) http.HandlerFunc {
|
||||
func HandleWorkoutDeleteComp(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
metrics.WithLabelValues("delete").Inc()
|
||||
|
||||
user := verifySessionAndReturnUser(db, r)
|
||||
if user == nil {
|
||||
w.Header().Add("HX-Redirect", "/auth/signin")
|
||||
return
|
||||
}
|
||||
|
||||
rowId := r.PathValue("id")
|
||||
if rowId == "" {
|
||||
@@ -139,7 +129,7 @@ func HandleDeleteWorkout(db *sql.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := db.Exec("DELETE FROM workout WHERE user_id = ? AND rowid = ?", user.user_uuid, rowId)
|
||||
res, err := db.Exec("DELETE FROM workout WHERE user_id = ? AND rowid = ?", user.id, rowId)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user