Added basic login functionality to login form

This commit is contained in:
2025-02-08 15:29:17 +11:00
parent 5a93ef81dd
commit 1004dc9563
9 changed files with 138 additions and 7 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.env
tmp/
projectreshoot
static/css/output.css

36
cookies/pagefrom.go Normal file
View File

@@ -0,0 +1,36 @@
package cookies
import (
"net/http"
"net/url"
"os"
"time"
)
func CheckPageFrom(w http.ResponseWriter, r *http.Request) string {
pageFromCookie, err := r.Cookie("pagefrom")
if err != nil {
return "/"
}
pageFrom := pageFromCookie.Value
deleteCookie := &http.Cookie{Name: "pagefrom", Value: "", Expires: time.Unix(0, 0)}
http.SetCookie(w, deleteCookie)
return pageFrom
}
func SetPageFrom(w http.ResponseWriter, r *http.Request) {
referer := r.Referer()
parsedURL, err := url.Parse(referer)
if err != nil {
return
}
var pageFrom string
expectedHost := os.Getenv("TRUSTED_HOST")
if parsedURL.Path == "" || parsedURL.Host != expectedHost {
pageFrom = "/"
} else {
pageFrom = parsedURL.Path
}
pageFromCookie := &http.Cookie{Name: "pagefrom", Value: pageFrom}
http.SetCookie(w, pageFromCookie)
}

5
go.mod
View File

@@ -2,4 +2,7 @@ module projectreshoot
go 1.23.5
require github.com/a-h/templ v0.3.833
require (
github.com/a-h/templ v0.3.833
github.com/joho/godotenv v1.5.1
)

2
go.sum
View File

@@ -2,3 +2,5 @@ github.com/a-h/templ v0.3.833 h1:L/KOk/0VvVTBegtE0fp2RJQiBm7/52Zxv5fqlEHiQUU=
github.com/a-h/templ v0.3.833/go.mod h1:cAu4AiZhtJfBjMY0HASlyzvkrtjnHWPeEsyGK2YYmfk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=

75
handlers/login.go Normal file
View File

@@ -0,0 +1,75 @@
package handlers
import (
"errors"
"fmt"
"net/http"
"projectreshoot/cookies"
"projectreshoot/view/component/form"
"projectreshoot/view/page"
)
// TODO: here for testing only, move to database
type User struct {
id int
username string
password string
}
// TODO: here for testing only, move to database
func testUser() User {
return User{id: 1, username: "Haelnorr", password: "test"}
}
func validateLogin(r *http.Request) (int, error) {
formUsername := r.FormValue("username")
formPassword := r.FormValue("password")
// TODO: search database for username
validUser := testUser()
// TODO: check password is valid
if formUsername != validUser.username || formPassword != validUser.password {
return 0, errors.New("Username or password incorrect")
}
// TODO: return the users ID
return validUser.id, nil
}
func checkRememberMe(r *http.Request) bool {
rememberMe := r.FormValue("remember-me")
if rememberMe == "on" {
return true
} else {
return false
}
}
func HandleLoginRequest() http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
userID, err := validateLogin(r)
if err != nil {
// TODO: add debug log
fmt.Printf("Login failed: %s\n", err)
form.LoginForm(err.Error()).Render(r.Context(), w)
return
}
// TODO: login success, use the userID to set the session
rememberMe := checkRememberMe(r)
fmt.Printf("Login success, user ID: %v - remember me?: %t\n", userID, rememberMe)
pageFrom := cookies.CheckPageFrom(w, r)
w.Header().Set("HX-Redirect", pageFrom)
},
)
}
func HandleLoginPage() http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
cookies.SetPageFrom(w, r)
page.Login().Render(r.Context(), w)
},
)
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"embed"
"fmt"
"github.com/joho/godotenv"
"io"
"net"
"net/http"
@@ -57,6 +58,10 @@ func run(ctx context.Context, w io.Writer) error {
var static embed.FS
func main() {
err := godotenv.Load(".env")
if err != nil {
panic(err.Error())
}
ctx := context.Background()
if err := run(ctx, os.Stdout); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)

View File

@@ -9,8 +9,16 @@ import (
func addRoutes(
mux *http.ServeMux,
) {
// Static files
mux.Handle("GET /static/", http.StripPrefix("/static/", handlers.HandleStatic()))
// Index page
mux.Handle("GET /", handlers.HandleRoot())
// Static pages
mux.Handle("GET /about", handlers.HandlePage(page.About()))
mux.Handle("GET /login", handlers.HandlePage(page.Login()))
// Login page and handlers
mux.Handle("GET /login", handlers.HandleLoginPage())
mux.Handle("POST /login", handlers.HandleLoginRequest())
}

View File

@@ -5,14 +5,14 @@ import "fmt"
templ LoginForm(loginError string) {
{{
var errCreds string
if loginError == "Username or password is incorrect" {
if loginError == "Username or password incorrect" {
errCreds = "true"
} else {
errCreds = "false"
}
xdata := fmt.Sprintf("{credentialError: %s, errorMessage: '%s'}", errCreds, loginError)
}}
<form>
<form hx-post="/login">
<div
class="grid gap-y-4"
x-data={ xdata }
@@ -26,7 +26,7 @@ templ LoginForm(loginError string) {
<div class="relative">
<input
type="text"
id="username"
idnutanix="username"
name="username"
class="py-3 px-4 block w-full rounded-lg text-sm
focus:border-blue focus:ring-blue bg-base
@@ -65,6 +65,7 @@ templ LoginForm(loginError string) {
text-blue decoration-2 hover:underline
focus:outline-none focus:underline font-medium"
href="/recover-account"
tabindex="-1"
>Forgot password?</a>
</div>
<div class="relative">

View File

@@ -5,7 +5,7 @@ import "projectreshoot/view/layout"
templ Error(code string, err string, message string) {
@layout.Global() {
<div
class="grid absolute left-0 right-0 top-0 bottom-0 z-[-1]
class="grid mt-24 left-0 right-0 top-0 bottom-0
place-content-center bg-base px-4"
>
<div class="text-center">
@@ -22,7 +22,7 @@ templ Error(code string, err string, message string) {
<a
href="/"
class="mt-6 inline-block rounded-lg bg-mauve px-5 py-3
text-sm text-crust hover:bg-mauve/75"
text-sm text-crust transition hover:bg-mauve/75"
>Go to homepage</a>
</div>
</div>