From 4fa797d36ac63803af1ac5313461b14712f354d3 Mon Sep 17 00:00:00 2001 From: riwiwa Date: Thu, 29 Jan 2026 20:20:36 -0800 Subject: [PATCH] started working on user profiles --- templates/create_account.gohtml | 20 +++++ templates/history.gohtml | 2 +- templates/login.gohtml | 20 +++++ templates/profile.gohtml | 12 +++ web/web.go | 153 ++++++++++++++++++++++++++++++-- 5 files changed, 198 insertions(+), 9 deletions(-) create mode 100644 templates/create_account.gohtml create mode 100644 templates/login.gohtml create mode 100644 templates/profile.gohtml diff --git a/templates/create_account.gohtml b/templates/create_account.gohtml new file mode 100644 index 0000000..2d7c748 --- /dev/null +++ b/templates/create_account.gohtml @@ -0,0 +1,20 @@ + + + + + + muzi | Create Account + + + +
+
+ +

+ +

+ +
+
+ + diff --git a/templates/history.gohtml b/templates/history.gohtml index 781b723..a575777 100644 --- a/templates/history.gohtml +++ b/templates/history.gohtml @@ -3,7 +3,7 @@ - muzi | history + muzi | History diff --git a/templates/login.gohtml b/templates/login.gohtml new file mode 100644 index 0000000..087597d --- /dev/null +++ b/templates/login.gohtml @@ -0,0 +1,20 @@ + + + + + + muzi | Login + + + +
+
+ +

+ +

+ +
+
+ + diff --git a/templates/profile.gohtml b/templates/profile.gohtml new file mode 100644 index 0000000..0aa35fa --- /dev/null +++ b/templates/profile.gohtml @@ -0,0 +1,12 @@ + + + + + + muzi | {{.Username}}'s Profile + + + + {{.Bio}} + + diff --git a/web/web.go b/web/web.go index 2af8e93..5805bb2 100644 --- a/web/web.go +++ b/web/web.go @@ -7,6 +7,8 @@ import ( "net/http" "os" "strconv" + "muzi/importsongs" + "golang.org/x/crypto/bcrypt" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" @@ -22,6 +24,7 @@ type PageData struct { Page int } + func Sub(a int, b int) int { return a - b } @@ -97,7 +100,116 @@ func getScrobbles(conn *pgx.Conn) int { return count } -func tmp(w http.ResponseWriter, r *http.Request) { +func hashPassword(pass []byte) string { + hashedPassword, err := bcrypt.GenerateFromPassword(pass, bcrypt.DefaultCost) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't hash password: %v\n", err) + } + return string(hashedPassword) +} + +func verifyPassword(hashedPassword string, enteredPassword []byte) bool { + err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), enteredPassword) + if err != nil { + fmt.Fprintf(os.Stderr, "Error while comparing passwords: %v\n", err) + return false + } + return true +} + +func createAccount(w http.ResponseWriter, r *http.Request) { + conn, err := pgx.Connect(context.Background(), "postgres://postgres:postgres@localhost:5432/muzi") + if err != nil { + fmt.Fprintf(os.Stderr, "Cannot connect to muzi database: %v\n", err) + return + } + defer conn.Close(context.Background()) + + if r.Method == "POST" { + r.ParseForm() + + username := r.FormValue("uname") + hashedPassword := hashPassword([]byte(r.FormValue("pass"))) + + if importsongs.TableExists("users", conn) == false { + _, err = conn.Exec( + context.Background(), + `CREATE TABLE users (username TEXT, password TEXT, pk SERIAL, PRIMARY KEY (pk));`, + ) + if err != nil { + fmt.Fprintf(os.Stderr, "Cannot create users table: %v\n", err) + panic(err) + } + } + + _, err = conn.Exec( + context.Background(), `INSERT INTO users (username, password) VALUES ($1, $2);`, + username, + hashedPassword, + ) + if err != nil { + fmt.Fprintf(os.Stderr, "Cannot add new user to users table: %v\n", err) + http.Redirect(w, r, "/createaccount", http.StatusSeeOther) + } else { + http.Redirect(w, r, "/profile/" + username, http.StatusSeeOther) + } + } +} + +func createAccountHandler(w http.ResponseWriter, r *http.Request) { + tmp, err := template.New("create_account.gohtml").ParseFiles("./templates/create_account.gohtml") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + err = tmp.Execute(w, nil) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func loginSubmit(w http.ResponseWriter, r *http.Request) { + conn, err := pgx.Connect(context.Background(), "postgres://postgres:postgres@localhost:5432/muzi") + if err != nil { + fmt.Fprintf(os.Stderr, "Cannot connect to muzi database: %v\n", err) + return + } + defer conn.Close(context.Background()) + + if r.Method == "POST" { + r.ParseForm() + + username := r.FormValue("uname") + password := r.FormValue("pass") + var storedPassword string + err := conn.QueryRow(context.Background(), "SELECT password FROM users WHERE username = $1;", username).Scan(&storedPassword) + if err != nil { + fmt.Fprintf(os.Stderr, "Cannot get password for entered username: %v\n", err) + } + + if verifyPassword(storedPassword, []byte(password)) { + http.Redirect(w, r, "/profile/" + username, http.StatusSeeOther) + } + } +} + +func loginPage(w http.ResponseWriter, r *http.Request) { + tmp, err := template.New("login.gohtml").ParseFiles("./templates/login.gohtml") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + err = tmp.Execute(w, nil) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func historyPage(w http.ResponseWriter, r *http.Request) { conn, err := pgx.Connect(context.Background(), "postgres://postgres:postgres@localhost:5432/muzi") if err != nil { @@ -130,24 +242,29 @@ func tmp(w http.ResponseWriter, r *http.Request) { Page: pageInt, } - funcMap := template.FuncMap{ - "Sub": Sub, - "Add": Add, - } +funcMap := template.FuncMap{ + "Sub": Sub, + "Add": Add, +} - t, err := template.New("history.gohtml").Funcs(funcMap).ParseFiles("./templates/history.gohtml") + tmp, err := template.New("history.gohtml").Funcs(funcMap).ParseFiles("./templates/history.gohtml") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - err = t.Execute(w, data) + err = tmp.Execute(w, data) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } +type Profile struct { + Username string + Bio string +} + func Start() { addr := ":1234" r := chi.NewRouter() @@ -155,7 +272,27 @@ func Start() { r.Get("/static/style.css", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/style.css") }) - r.Get("/history", tmp) + r.Get("/history", historyPage) + r.Get("/login", loginPage) + r.Get("/createaccount", createAccountHandler) + // TODO: clean this up + r.Get("/profile/{username}", func(w http.ResponseWriter, r *http.Request) { + username := chi.URLParam(r, "username") + + profileData := Profile { + Username: username, + Bio: "default", + } + + tmp, err := template.ParseFiles("./templates/profile.gohtml") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + tmp.Execute(w, profileData) + }) + r.Post("/loginsubmit", loginSubmit) + r.Post("/createaccountsubmit", createAccount) fmt.Printf("WebUI starting on %s\n", addr) http.ListenAndServe(addr, r) }