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)
}