Added basic login functionality to login form
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
.env
|
||||||
tmp/
|
tmp/
|
||||||
projectreshoot
|
projectreshoot
|
||||||
static/css/output.css
|
static/css/output.css
|
||||||
|
|||||||
36
cookies/pagefrom.go
Normal file
36
cookies/pagefrom.go
Normal 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
5
go.mod
@@ -2,4 +2,7 @@ module projectreshoot
|
|||||||
|
|
||||||
go 1.23.5
|
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
2
go.sum
@@ -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/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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
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
75
handlers/login.go
Normal 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)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
5
main.go
5
main.go
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -57,6 +58,10 @@ func run(ctx context.Context, w io.Writer) error {
|
|||||||
var static embed.FS
|
var static embed.FS
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
err := godotenv.Load(".env")
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
if err := run(ctx, os.Stdout); err != nil {
|
if err := run(ctx, os.Stdout); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||||
|
|||||||
@@ -9,8 +9,16 @@ import (
|
|||||||
func addRoutes(
|
func addRoutes(
|
||||||
mux *http.ServeMux,
|
mux *http.ServeMux,
|
||||||
) {
|
) {
|
||||||
|
// Static files
|
||||||
mux.Handle("GET /static/", http.StripPrefix("/static/", handlers.HandleStatic()))
|
mux.Handle("GET /static/", http.StripPrefix("/static/", handlers.HandleStatic()))
|
||||||
|
|
||||||
|
// Index page
|
||||||
mux.Handle("GET /", handlers.HandleRoot())
|
mux.Handle("GET /", handlers.HandleRoot())
|
||||||
|
|
||||||
|
// Static pages
|
||||||
mux.Handle("GET /about", handlers.HandlePage(page.About()))
|
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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import "fmt"
|
|||||||
templ LoginForm(loginError string) {
|
templ LoginForm(loginError string) {
|
||||||
{{
|
{{
|
||||||
var errCreds string
|
var errCreds string
|
||||||
if loginError == "Username or password is incorrect" {
|
if loginError == "Username or password incorrect" {
|
||||||
errCreds = "true"
|
errCreds = "true"
|
||||||
} else {
|
} else {
|
||||||
errCreds = "false"
|
errCreds = "false"
|
||||||
}
|
}
|
||||||
xdata := fmt.Sprintf("{credentialError: %s, errorMessage: '%s'}", errCreds, loginError)
|
xdata := fmt.Sprintf("{credentialError: %s, errorMessage: '%s'}", errCreds, loginError)
|
||||||
}}
|
}}
|
||||||
<form>
|
<form hx-post="/login">
|
||||||
<div
|
<div
|
||||||
class="grid gap-y-4"
|
class="grid gap-y-4"
|
||||||
x-data={ xdata }
|
x-data={ xdata }
|
||||||
@@ -26,7 +26,7 @@ templ LoginForm(loginError string) {
|
|||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="username"
|
idnutanix="username"
|
||||||
name="username"
|
name="username"
|
||||||
class="py-3 px-4 block w-full rounded-lg text-sm
|
class="py-3 px-4 block w-full rounded-lg text-sm
|
||||||
focus:border-blue focus:ring-blue bg-base
|
focus:border-blue focus:ring-blue bg-base
|
||||||
@@ -65,6 +65,7 @@ templ LoginForm(loginError string) {
|
|||||||
text-blue decoration-2 hover:underline
|
text-blue decoration-2 hover:underline
|
||||||
focus:outline-none focus:underline font-medium"
|
focus:outline-none focus:underline font-medium"
|
||||||
href="/recover-account"
|
href="/recover-account"
|
||||||
|
tabindex="-1"
|
||||||
>Forgot password?</a>
|
>Forgot password?</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import "projectreshoot/view/layout"
|
|||||||
templ Error(code string, err string, message string) {
|
templ Error(code string, err string, message string) {
|
||||||
@layout.Global() {
|
@layout.Global() {
|
||||||
<div
|
<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"
|
place-content-center bg-base px-4"
|
||||||
>
|
>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
@@ -22,7 +22,7 @@ templ Error(code string, err string, message string) {
|
|||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
class="mt-6 inline-block rounded-lg bg-mauve px-5 py-3
|
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>
|
>Go to homepage</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user