feat: json login support

This commit is contained in:
Teajey 2024-11-21 09:10:14 +09:00
parent fac92511ee
commit 994f4ee64a
Signed by: Teajey
GPG Key ID: 970E790FE834A713
4 changed files with 98 additions and 59 deletions

85
server/api/login.go Normal file
View File

@ -0,0 +1,85 @@
package api
import (
"lishwist/db"
"lishwist/templates"
"log"
"golang.org/x/crypto/bcrypt"
)
type LoginProps struct {
GeneralError string
SuccessfulRegistration bool
Username templates.InputProps
Password templates.InputProps
}
func NewLoginProps(username, password string) *LoginProps {
return &LoginProps{
Username: templates.InputProps{
Name: "username",
Required: true,
Value: username,
},
Password: templates.InputProps{
Name: "password",
Type: "password",
Required: true,
Value: password,
},
}
}
func (p *LoginProps) Validate() (valid bool) {
valid = true
if !p.Username.Validate() {
valid = false
}
if !p.Password.Validate() {
valid = false
}
return
}
func Login(username, password string) *LoginProps {
props := NewLoginProps(username, password)
valid := props.Validate()
props.Password.Value = ""
if !valid {
log.Printf("Invalid props: %#v\n", props)
return props
}
user, err := db.GetUserByName(username)
if err != nil {
log.Printf("Failed to fetch user: %s\n", err)
props.GeneralError = "Username or password invalid"
return props
}
if user == nil {
log.Printf("User not found by name: %q\n", username)
props.GeneralError = "Username or password invalid"
return props
}
passHash, err := user.GetPassHash()
if err != nil {
log.Println("Failed to get password hash: " + err.Error())
props.GeneralError = "Something went wrong. Error code: Momo"
return props
}
err = bcrypt.CompareHashAndPassword(passHash, []byte(password))
if err != nil {
log.Println("Username or password invalid: " + err.Error())
props.GeneralError = "Username or password invalid"
return props
}
return nil
}

View File

@ -14,7 +14,7 @@ import (
func main() { func main() {
gob.Register(&api.RegisterProps{}) gob.Register(&api.RegisterProps{})
gob.Register(&routing.LoginProps{}) gob.Register(&api.LoginProps{})
err := db.Open() err := db.Open()
if err != nil { if err != nil {
@ -55,6 +55,7 @@ func main() {
r.Json.Public.HandleFunc("GET /", routing.NotFoundJson) r.Json.Public.HandleFunc("GET /", routing.NotFoundJson)
r.Json.Private.Handle("GET /users", route.ExpectUser(route.UsersJson)) r.Json.Private.Handle("GET /users", route.ExpectUser(route.UsersJson))
r.Json.Private.HandleFunc("GET /", routing.NotFoundJson)
http.Handle("/", r) http.Handle("/", r)

View File

@ -2,6 +2,7 @@ package router
import ( import (
"net/http" "net/http"
"strings"
"github.com/Teajey/sqlstore" "github.com/Teajey/sqlstore"
) )
@ -29,10 +30,10 @@ type Router struct {
} }
func (s *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
contentType := r.Header.Get("Content-Type") accept := r.Header.Get("Accept")
switch contentType { switch {
case "application/json": case strings.HasPrefix(accept, "application/json"):
s.Json.ServeHTTP(w, r) s.Json.ServeHTTP(w, r)
default: default:
s.Html.ServeHTTP(w, r) s.Html.ServeHTTP(w, r)

View File

@ -1,40 +1,18 @@
package routing package routing
import ( import (
"lishwist/db" "encoding/json"
"lishwist/api"
sesh "lishwist/session" sesh "lishwist/session"
"lishwist/templates" "lishwist/templates"
"log" "log"
"net/http" "net/http"
"golang.org/x/crypto/bcrypt"
) )
type LoginProps struct {
GeneralError string
SuccessfulRegistration bool
Username templates.InputProps
Password templates.InputProps
}
func NewLoginProps() LoginProps {
return LoginProps{
Username: templates.InputProps{
Name: "username",
Required: true,
},
Password: templates.InputProps{
Name: "password",
Type: "password",
Required: true,
},
}
}
func (ctx *Context) Login(w http.ResponseWriter, r *http.Request) { func (ctx *Context) Login(w http.ResponseWriter, r *http.Request) {
session, _ := ctx.store.Get(r, "lishwist_user") session, _ := ctx.store.Get(r, "lishwist_user")
props := NewLoginProps() props := api.NewLoginProps("", "")
flash, err := sesh.GetFirstFlash(w, r, session, "login_props") flash, err := sesh.GetFirstFlash(w, r, session, "login_props")
if err != nil { if err != nil {
@ -42,7 +20,7 @@ func (ctx *Context) Login(w http.ResponseWriter, r *http.Request) {
return return
} }
flashProps, ok := flash.(*LoginProps) flashProps, ok := flash.(*api.LoginProps)
if ok { if ok {
props.Username.Value = flashProps.Username.Value props.Username.Value = flashProps.Username.Value
@ -74,36 +52,10 @@ func (ctx *Context) LoginPost(w http.ResponseWriter, r *http.Request) {
username := r.Form.Get("username") username := r.Form.Get("username")
password := r.Form.Get("password") password := r.Form.Get("password")
props := NewLoginProps() props := api.Login(username, password)
props.Username.Value = username if props != nil {
user, err := db.GetUserByName(username)
if err != nil {
log.Printf("Failed to fetch user: %s\n", err)
props.GeneralError = "Username or password invalid"
ctx.RedirectWithFlash(w, r, "/", "login_props", &props)
return
}
if user == nil {
log.Printf("User not found by name: %q\n", username)
props.GeneralError = "Username or password invalid"
ctx.RedirectWithFlash(w, r, "/", "login_props", &props)
return
}
passHash, err := user.GetPassHash()
if err != nil {
log.Println("Failed to get password hash: " + err.Error())
props.GeneralError = "Something went wrong. Error code: Momo"
ctx.RedirectWithFlash(w, r, "/", "login_props", &props)
return
}
err = bcrypt.CompareHashAndPassword(passHash, []byte(password))
if err != nil {
log.Println("Username or password invalid: " + err.Error())
props.GeneralError = "Username or password invalid"
ctx.RedirectWithFlash(w, r, "/", "login_props", &props) ctx.RedirectWithFlash(w, r, "/", "login_props", &props)
_ = json.NewEncoder(w).Encode(props)
return return
} }