chore: #123 unify metrics, logs, variable names and structure
All checks were successful
Build Docker Image / Explore-Gitea-Actions (push) Successful in 45s
All checks were successful
Build Docker Image / Explore-Gitea-Actions (push) Successful in 45s
This commit is contained in:
@@ -16,7 +16,7 @@ WORKDIR /me-fit
|
|||||||
RUN apt-get update && apt-get install -y ca-certificates && echo "" > .env
|
RUN apt-get update && apt-get install -y ca-certificates && echo "" > .env
|
||||||
COPY --from=builder_go /me-fit/me-fit ./me-fit
|
COPY --from=builder_go /me-fit/me-fit ./me-fit
|
||||||
COPY --from=builder_node /me-fit/static ./static
|
COPY --from=builder_node /me-fit/static ./static
|
||||||
COPY migrations ./migrations
|
COPY migration ./migration
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["/me-fit/me-fit"]
|
ENTRYPOINT ["/me-fit/me-fit"]
|
||||||
|
|
||||||
|
|||||||
12
handler.go
12
handler.go
@@ -16,15 +16,15 @@ func getHandler(db *sql.DB) http.Handler {
|
|||||||
// Serve static files (CSS, JS and images)
|
// Serve static files (CSS, JS and images)
|
||||||
router.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
|
router.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
|
||||||
|
|
||||||
router.HandleFunc("/app", service.HandleWorkoutPage(db))
|
router.HandleFunc("/workout", service.HandleWorkoutPage(db))
|
||||||
router.HandleFunc("POST /api/workout", service.HandleNewWorkout(db))
|
router.HandleFunc("POST /api/workout", service.HandleWorkoutNewComp(db))
|
||||||
router.HandleFunc("GET /api/workout", service.HandleGetWorkouts(db))
|
router.HandleFunc("GET /api/workout", service.HandleWorkoutGetComp(db))
|
||||||
router.HandleFunc("DELETE /api/workout/{id}", service.HandleDeleteWorkout(db))
|
router.HandleFunc("DELETE /api/workout/{id}", service.HandleWorkoutDeleteComp(db))
|
||||||
|
|
||||||
router.HandleFunc("/auth/signin", service.HandleSignInPage(db))
|
router.HandleFunc("/auth/signin", service.HandleSignInPage(db))
|
||||||
router.HandleFunc("/auth/signup", service.HandleSignUpPage(db))
|
router.HandleFunc("/auth/signup", service.HandleSignUpPage(db))
|
||||||
router.HandleFunc("/api/auth/signup", service.HandleSignUp(db))
|
router.HandleFunc("/api/auth/signup", service.HandleSignUpComp(db))
|
||||||
router.HandleFunc("/api/auth/signin", service.HandleSignIn(db))
|
router.HandleFunc("/api/auth/signin", service.HandleSignInComp(db))
|
||||||
router.HandleFunc("/api/auth/signout", service.HandleSignOutComp(db))
|
router.HandleFunc("/api/auth/signout", service.HandleSignOutComp(db))
|
||||||
|
|
||||||
return middleware.Logging(middleware.EnableCors(router))
|
return middleware.Logging(middleware.EnableCors(router))
|
||||||
|
|||||||
9
main.go
9
main.go
@@ -1,10 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"me-fit/utils"
|
"me-fit/utils"
|
||||||
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Println("Starting server...")
|
slog.Info("Starting server...")
|
||||||
|
|
||||||
err := godotenv.Load()
|
err := godotenv.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,7 +34,7 @@ func main() {
|
|||||||
Handler: promhttp.Handler(),
|
Handler: promhttp.Handler(),
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
log.Println("Starting prometheus server at", prometheusServer.Addr)
|
slog.Info("Starting prometheus server on " + prometheusServer.Addr)
|
||||||
err := prometheusServer.ListenAndServe()
|
err := prometheusServer.ListenAndServe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -44,7 +45,7 @@ func main() {
|
|||||||
Addr: ":8080",
|
Addr: ":8080",
|
||||||
Handler: getHandler(db),
|
Handler: getHandler(db),
|
||||||
}
|
}
|
||||||
log.Println("Starting server at", server.Addr)
|
slog.Info("Starting server on " + server.Addr)
|
||||||
|
|
||||||
err = server.ListenAndServe()
|
err = server.ListenAndServe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
@@ -11,12 +12,11 @@ func EnableCors(next http.Handler) http.Handler {
|
|||||||
if base_url == "" {
|
if base_url == "" {
|
||||||
log.Fatal("BASE_URL is not set")
|
log.Fatal("BASE_URL is not set")
|
||||||
}
|
}
|
||||||
log.Println("BASE_URL is", base_url)
|
slog.Info("BASE_URL is " + base_url)
|
||||||
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", base_url)
|
w.Header().Set("Access-Control-Allow-Origin", base_url)
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE")
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE")
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "Authorization")
|
|
||||||
|
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
metrics = promauto.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "mefit_request_total",
|
||||||
|
Help: "The total number of requests processed",
|
||||||
|
},
|
||||||
|
[]string{"path", "method", "status"},
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
type WrappedWriter struct {
|
type WrappedWriter struct {
|
||||||
@@ -25,6 +39,8 @@ func Logging(next http.Handler) http.Handler {
|
|||||||
StatusCode: http.StatusOK,
|
StatusCode: http.StatusOK,
|
||||||
}
|
}
|
||||||
next.ServeHTTP(wrapped, r)
|
next.ServeHTTP(wrapped, r)
|
||||||
log.Println(r.RemoteAddr, wrapped.StatusCode, r.Method, r.URL.Path, time.Since(start))
|
|
||||||
|
slog.Info(r.RemoteAddr + " " + strconv.Itoa(wrapped.StatusCode) + " " + r.Method + " " + r.URL.Path + " " + time.Since(start).String())
|
||||||
|
metrics.WithLabelValues(r.URL.Path, r.Method, http.StatusText(wrapped.StatusCode)).Inc()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"log"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
user_uuid uuid.UUID
|
id uuid.UUID
|
||||||
email string
|
email string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var email = r.FormValue("email")
|
var email = r.FormValue("email")
|
||||||
var password = r.FormValue("password")
|
var password = r.FormValue("password")
|
||||||
@@ -69,9 +69,9 @@ func HandleSignUp(db *sql.DB) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user_uuid, err := uuid.NewRandom()
|
userId, err := uuid.NewRandom()
|
||||||
if err != nil {
|
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)
|
auth.Error("Internal Server Error").Render(r.Context(), w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -79,14 +79,14 @@ func HandleSignUp(db *sql.DB) http.HandlerFunc {
|
|||||||
salt := make([]byte, 16)
|
salt := make([]byte, 16)
|
||||||
_, err = rand.Read(salt)
|
_, err = rand.Read(salt)
|
||||||
if err != nil {
|
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)
|
auth.Error("Internal Server Error").Render(r.Context(), w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := getHashPassword(password, salt)
|
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 {
|
if err != nil {
|
||||||
// This does leak information about the email being in use, though not specifically stated
|
// 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
|
// 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)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result := tryCreateSessionAndSetCookie(r, w, db, user_uuid)
|
result := tryCreateSessionAndSetCookie(r, w, db, userId)
|
||||||
if !result {
|
if !result {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Add("HX-Redirect", "/")
|
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) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var email = r.FormValue("email")
|
var email = r.FormValue("email")
|
||||||
var password = r.FormValue("password")
|
var password = r.FormValue("password")
|
||||||
@@ -121,10 +120,10 @@ func HandleSignIn(db *sql.DB) http.HandlerFunc {
|
|||||||
var result bool = true
|
var result bool = true
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
var user_uuid uuid.UUID
|
var userId uuid.UUID
|
||||||
var saved_hash []byte
|
var savedHash []byte
|
||||||
var salt []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 {
|
if err != nil {
|
||||||
result = false
|
result = false
|
||||||
}
|
}
|
||||||
@@ -132,26 +131,26 @@ func HandleSignIn(db *sql.DB) http.HandlerFunc {
|
|||||||
if result {
|
if result {
|
||||||
new_hash := getHashPassword(password, salt)
|
new_hash := getHashPassword(password, salt)
|
||||||
|
|
||||||
if subtle.ConstantTimeCompare(new_hash, saved_hash) == 0 {
|
if subtle.ConstantTimeCompare(new_hash, savedHash) == 0 {
|
||||||
result = false
|
result = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if result {
|
if result {
|
||||||
result := tryCreateSessionAndSetCookie(r, w, db, user_uuid)
|
result := tryCreateSessionAndSetCookie(r, w, db, userId)
|
||||||
if !result {
|
if !result {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
duration := time.Since(start)
|
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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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 {
|
if result {
|
||||||
w.Header().Add("HX-Redirect", "/")
|
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)
|
_, err := db.Exec("DELETE FROM session WHERE session_id = ?", id)
|
||||||
if err != nil {
|
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)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
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 {
|
func tryCreateSessionAndSetCookie(r *http.Request, w http.ResponseWriter, db *sql.DB, user_uuid uuid.UUID) bool {
|
||||||
var session_id_bytes []byte = make([]byte, 32)
|
var session_id_bytes []byte = make([]byte, 32)
|
||||||
_, err := rand.Reader.Read(session_id_bytes)
|
_, err := rand.Reader.Read(session_id_bytes)
|
||||||
if err != nil {
|
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)
|
auth.Error("Internal Server Error").Render(r.Context(), w)
|
||||||
return false
|
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)
|
_, err = db.Exec("INSERT INTO session (session_id, user_uuid, created_at) VALUES (?, ?, datetime())", session_id, user_uuid)
|
||||||
if err != nil {
|
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)
|
auth.Error("Internal Server Error").Render(r.Context(), w)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -254,25 +228,25 @@ func getSessionID(r *http.Request) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func verifySessionAndReturnUser(db *sql.DB, r *http.Request) *User {
|
func verifySessionAndReturnUser(db *sql.DB, r *http.Request) *User {
|
||||||
session_id := getSessionID(r)
|
sessionId := getSessionID(r)
|
||||||
if session_id == "" {
|
if sessionId == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var user User
|
var user User
|
||||||
var created_at time.Time
|
var createdAt time.Time
|
||||||
|
|
||||||
err := db.QueryRow(`
|
err := db.QueryRow(`
|
||||||
SELECT u.user_uuid, u.email, s.created_at
|
SELECT u.user_uuid, u.email, s.created_at
|
||||||
FROM session s
|
FROM session s
|
||||||
INNER JOIN user u ON s.user_uuid = u.user_uuid
|
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 {
|
if err != nil {
|
||||||
log.Printf("Could not verify session: %v", err)
|
slog.Error("Could not verify session: %v", err)
|
||||||
return nil
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import (
|
|||||||
func HandleIndexAnd404(db *sql.DB) http.HandlerFunc {
|
func HandleIndexAnd404(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var comp templ.Component = nil
|
var comp templ.Component = nil
|
||||||
user_comp := UserInfoComp(verifySessionAndReturnUser(db, r))
|
userComp := UserInfoComp(verifySessionAndReturnUser(db, r))
|
||||||
|
|
||||||
if r.URL.Path != "/" {
|
if r.URL.Path != "/" {
|
||||||
comp = template.Layout(template.NotFound(), user_comp)
|
comp = template.Layout(template.NotFound(), userComp)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
} else {
|
} else {
|
||||||
comp = template.Layout(template.Index(), user_comp)
|
comp = template.Layout(template.Index(), userComp)
|
||||||
}
|
}
|
||||||
|
|
||||||
comp.Render(r.Context(), w)
|
comp.Render(r.Context(), w)
|
||||||
|
|||||||
@@ -8,38 +8,28 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"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 {
|
func HandleWorkoutPage(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandleNewWorkout(db *sql.DB) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
metrics.WithLabelValues("new").Inc()
|
|
||||||
|
|
||||||
user := verifySessionAndReturnUser(db, r)
|
user := verifySessionAndReturnUser(db, r)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
http.Redirect(w, r, "/auth/signin", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDate := time.Now().Format("2006-01-02")
|
||||||
|
inner := workout.WorkoutComp(currentDate)
|
||||||
|
userComp := UserInfoComp(user)
|
||||||
|
template.Layout(inner, userComp).Render(r.Context(), w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleWorkoutNewComp(db *sql.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user := verifySessionAndReturnUser(db, r)
|
||||||
|
if user == nil {
|
||||||
|
w.Header().Add("HX-Redirect", "/auth/signin")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +61,7 @@ func HandleNewWorkout(db *sql.DB) http.HandlerFunc {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
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) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
metrics.WithLabelValues("get").Inc()
|
|
||||||
|
|
||||||
user := verifySessionAndReturnUser(db, r)
|
user := verifySessionAndReturnUser(db, r)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
w.Header().Add("HX-Redirect", "/auth/signin")
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
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) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
metrics.WithLabelValues("delete").Inc()
|
|
||||||
|
|
||||||
user := verifySessionAndReturnUser(db, r)
|
user := verifySessionAndReturnUser(db, r)
|
||||||
|
if user == nil {
|
||||||
|
w.Header().Add("HX-Redirect", "/auth/signin")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rowId := r.PathValue("id")
|
rowId := r.PathValue("id")
|
||||||
if rowId == "" {
|
if rowId == "" {
|
||||||
@@ -139,7 +129,7 @@ func HandleDeleteWorkout(db *sql.DB) http.HandlerFunc {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ templ Index() {
|
|||||||
Ever wanted to track your workouts and see your progress over time? ME-FIT is the perfect
|
Ever wanted to track your workouts and see your progress over time? ME-FIT is the perfect
|
||||||
solution for you.
|
solution for you.
|
||||||
</p>
|
</p>
|
||||||
<a href="/app" class="btn btn-primary">Get Started</a>
|
<a href="/workout" class="btn btn-primary">Get Started</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func RunMigrations(db *sql.DB) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m, err := migrate.NewWithDatabaseInstance(
|
m, err := migrate.NewWithDatabaseInstance(
|
||||||
"file://./migrations/",
|
"file://./migration/",
|
||||||
"",
|
"",
|
||||||
driver)
|
driver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func WriteJSON(w http.ResponseWriter, data interface{}) error {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
return json.NewEncoder(w).Encode(data)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user