feat: improvements
- better error presentation - public wishlist view
This commit is contained in:
parent
fb658e16fa
commit
0cc6abe03b
|
|
@ -1,6 +1,7 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
|
|
@ -41,6 +42,8 @@ func (auth *AuthMiddleware) ExpectUser(r *http.Request) *db.User {
|
|||
}
|
||||
|
||||
func NewAuthMiddleware(protectedHandler http.Handler, publicHandler http.Handler) *AuthMiddleware {
|
||||
gob.Register(&RegisterProps{})
|
||||
gob.Register(&LoginProps{})
|
||||
store := sessions.NewCookieStore([]byte(env.JwtSecret))
|
||||
store.Options.MaxAge = 86_400
|
||||
return &AuthMiddleware{store, protectedHandler, publicHandler}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,62 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
sesh "lishwist/session"
|
||||
"lishwist/templates"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type LoginGetProps struct {
|
||||
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 (auth *AuthMiddleware) Login(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := auth.Store.Get(r, "lishwist_user")
|
||||
successfulReg, ok := session.Values["successful_registration"].(bool)
|
||||
if ok {
|
||||
delete(session.Values, "successful_registration")
|
||||
if err := session.Save(r, w); err != nil {
|
||||
log.Println("Couldn't save session:", err)
|
||||
http.Error(w, "Something went wrong. Error code: Zuko", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
props := NewLoginProps()
|
||||
|
||||
flash, err := sesh.GetFirstFlash(w, r, session, "login_props")
|
||||
if err != nil {
|
||||
http.Error(w, "Something went wrong. Error code: Zuko", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
templates.Execute(w, "login.gotmpl", LoginGetProps{
|
||||
SuccessfulRegistration: successfulReg,
|
||||
})
|
||||
flashProps, ok := flash.(*LoginProps)
|
||||
if ok {
|
||||
props.Username.Value = flashProps.Username.Value
|
||||
|
||||
props.GeneralError = flashProps.GeneralError
|
||||
props.Username.Error = flashProps.Username.Error
|
||||
props.Password.Error = flashProps.Password.Error
|
||||
}
|
||||
|
||||
flash, err = sesh.GetFirstFlash(w, r, session, "successful_registration")
|
||||
if err != nil {
|
||||
http.Error(w, "Something went wrong. Error code: Zuko", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
successfulReg, _ := flash.(bool)
|
||||
if successfulReg {
|
||||
props.SuccessfulRegistration = true
|
||||
}
|
||||
|
||||
templates.Execute(w, "login.gotmpl", props)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,29 +18,36 @@ func (auth *AuthMiddleware) LoginPost(w http.ResponseWriter, r *http.Request) {
|
|||
username := r.Form.Get("username")
|
||||
password := r.Form.Get("password")
|
||||
|
||||
props := NewLoginProps()
|
||||
props.Username.Value = username
|
||||
|
||||
user, err := db.GetUserByName(username)
|
||||
if user == nil || err != nil {
|
||||
time.Sleep(time.Second)
|
||||
http.Error(w, "Username or password invalid", http.StatusUnauthorized)
|
||||
props.GeneralError = "Username or password invalid"
|
||||
auth.RedirectWithFlash(w, r, "/", "login_props", &props)
|
||||
return
|
||||
}
|
||||
|
||||
passHash, err := user.GetPassHash()
|
||||
if err != nil {
|
||||
http.Error(w, "Something went wrong. Error code: Momo", http.StatusInternalServerError)
|
||||
props.GeneralError = "Something went wrong. Error code: Momo"
|
||||
auth.RedirectWithFlash(w, r, "/", "login_props", &props)
|
||||
return
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword(passHash, []byte(password))
|
||||
if err != nil {
|
||||
http.Error(w, "Username or password invalid", http.StatusUnauthorized)
|
||||
props.GeneralError = "Username or password invalid"
|
||||
auth.RedirectWithFlash(w, r, "/", "login_props", &props)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := auth.Store.Get(r, "lishwist_user")
|
||||
if err != nil {
|
||||
log.Println("Couldn't get jwt:", err)
|
||||
http.Error(w, "Something went wrong. Error code: Sokka", http.StatusInternalServerError)
|
||||
props.GeneralError = "Something went wrong. Error code: Sokka"
|
||||
auth.RedirectWithFlash(w, r, "/", "login_props", &props)
|
||||
return
|
||||
}
|
||||
session.Values["authorized"] = true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (auth *AuthMiddleware) RedirectWithFlash(w http.ResponseWriter, r *http.Request, url string, key string, flash any) {
|
||||
session, _ := auth.Store.Get(r, "lishwist_user")
|
||||
session.AddFlash(flash, key)
|
||||
if err := session.Save(r, w); err != nil {
|
||||
log.Println("Couldn't save session:", err)
|
||||
http.Error(w, "Something went wrong. Error code: Zuko", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, url, http.StatusSeeOther)
|
||||
}
|
||||
|
|
@ -5,10 +5,61 @@ import (
|
|||
"net/http"
|
||||
|
||||
"lishwist/db"
|
||||
"lishwist/templates"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type RegisterProps struct {
|
||||
GeneralError string
|
||||
Username templates.InputProps
|
||||
Password templates.InputProps
|
||||
ConfirmPassword templates.InputProps
|
||||
}
|
||||
|
||||
func NewRegisterProps() RegisterProps {
|
||||
return RegisterProps{
|
||||
GeneralError: "",
|
||||
Username: templates.InputProps{
|
||||
Name: "username",
|
||||
Required: true,
|
||||
},
|
||||
Password: templates.InputProps{
|
||||
Type: "password",
|
||||
Name: "newPassword",
|
||||
Required: true,
|
||||
MinLength: 5,
|
||||
},
|
||||
ConfirmPassword: templates.InputProps{
|
||||
Type: "password",
|
||||
Name: "confirmPassword",
|
||||
Required: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (auth *AuthMiddleware) Register(w http.ResponseWriter, r *http.Request) {
|
||||
props := NewRegisterProps()
|
||||
|
||||
session, _ := auth.Store.Get(r, "lishwist_user")
|
||||
if flashes := session.Flashes("register_props"); len(flashes) > 0 {
|
||||
flashProps, _ := flashes[0].(*RegisterProps)
|
||||
props.Username.Value = flashProps.Username.Value
|
||||
|
||||
props.GeneralError = flashProps.GeneralError
|
||||
props.Username.Error = flashProps.Username.Error
|
||||
props.ConfirmPassword.Error = flashProps.ConfirmPassword.Error
|
||||
}
|
||||
|
||||
if err := session.Save(r, w); err != nil {
|
||||
log.Println("Couldn't save session:", err)
|
||||
http.Error(w, "Something went wrong. Error code: Zuko", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
templates.Execute(w, "register.gotmpl", props)
|
||||
}
|
||||
|
||||
func (auth *AuthMiddleware) RegisterPost(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "Couldn't parse form", http.StatusBadRequest)
|
||||
|
|
@ -19,31 +70,40 @@ func (auth *AuthMiddleware) RegisterPost(w http.ResponseWriter, r *http.Request)
|
|||
newPassword := r.Form.Get("newPassword")
|
||||
confirmPassword := r.Form.Get("confirmPassword")
|
||||
|
||||
props := NewRegisterProps()
|
||||
props.Username.Value = username
|
||||
props.Password.Value = newPassword
|
||||
props.ConfirmPassword.Value = confirmPassword
|
||||
|
||||
existingUser, _ := db.GetUserByName(username)
|
||||
if existingUser != nil {
|
||||
http.Error(w, "Username is taken", http.StatusBadRequest)
|
||||
props.Username.Error = "Username is taken"
|
||||
auth.RedirectWithFlash(w, r, "/register", "register_props", &props)
|
||||
return
|
||||
}
|
||||
|
||||
if newPassword != confirmPassword {
|
||||
http.Error(w, "passwords didn't match", http.StatusBadRequest)
|
||||
props.ConfirmPassword.Error = "Password didn't match"
|
||||
auth.RedirectWithFlash(w, r, "/register", "register_props", &props)
|
||||
return
|
||||
}
|
||||
|
||||
hashedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.MinCost)
|
||||
if err != nil {
|
||||
http.Error(w, "Something went wrong. Error code: Aang", http.StatusInternalServerError)
|
||||
props.GeneralError = "Something went wrong. Error code: Aang"
|
||||
auth.RedirectWithFlash(w, r, "/register", "register_props", &props)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.CreateUser(username, hashedPasswordBytes)
|
||||
if err != nil {
|
||||
http.Error(w, "Something went wrong. Error code: Ozai", http.StatusInternalServerError)
|
||||
props.GeneralError = "Something went wrong. Error code: Ozai"
|
||||
auth.RedirectWithFlash(w, r, "/register", "register_props", &props)
|
||||
return
|
||||
}
|
||||
|
||||
session, _ := auth.Store.Get(r, "lishwist_user")
|
||||
session.Values["successful_registration"] = true
|
||||
session.AddFlash(true, "successful_registration")
|
||||
if err := session.Save(r, w); err != nil {
|
||||
log.Println("Couldn't save session:", err)
|
||||
http.Error(w, "Something went wrong. Error code: Zuko", http.StatusInternalServerError)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package context
|
|||
|
||||
import (
|
||||
"lishwist/auth"
|
||||
"log"
|
||||
"lishwist/error"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
|
@ -19,8 +19,7 @@ func (ctx *Context) WishlistAdd(w http.ResponseWriter, r *http.Request) {
|
|||
newGiftName := r.Form.Get("gift_name")
|
||||
err := user.AddGift(newGiftName)
|
||||
if err != nil {
|
||||
log.Printf("Failed to add gift: %s\n", err)
|
||||
http.Error(w, "Failed to add gift.", http.StatusInternalServerError)
|
||||
error.Page(w, "Failed to add gift.", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
|
|
@ -35,8 +34,7 @@ func (ctx *Context) WishlistDelete(w http.ResponseWriter, r *http.Request) {
|
|||
targets := r.Form["gift"]
|
||||
err := user.RemoveGifts(targets...)
|
||||
if err != nil {
|
||||
log.Printf("Failed to remove gifts: %s\n", err)
|
||||
http.Error(w, "Failed to remove gifts.", http.StatusInternalServerError)
|
||||
error.Page(w, "Failed to remove gifts.", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
|
|
@ -45,7 +43,7 @@ func (ctx *Context) WishlistDelete(w http.ResponseWriter, r *http.Request) {
|
|||
func (ctx *Context) ForeignWishlistPost(w http.ResponseWriter, r *http.Request) {
|
||||
user := ctx.Auth.ExpectUser(r)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
error.Page(w, "Failed to parse form...", http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
userReference := r.PathValue("userReference")
|
||||
|
|
@ -55,27 +53,25 @@ func (ctx *Context) ForeignWishlistPost(w http.ResponseWriter, r *http.Request)
|
|||
unclaims := r.Form["claimed"]
|
||||
err := user.ClaimGifts(claims, unclaims)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to update claim...", http.StatusInternalServerError)
|
||||
error.Page(w, "Failed to update claim...", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
case "complete":
|
||||
claims := r.Form["claimed"]
|
||||
err := user.CompleteGifts(claims)
|
||||
if err != nil {
|
||||
log.Printf("Failed to complete gifts: %s\n", err)
|
||||
http.Error(w, "Failed to complete gifts...", http.StatusInternalServerError)
|
||||
error.Page(w, "Failed to complete gifts...", http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
case "add":
|
||||
giftName := r.Form.Get("gift_name")
|
||||
if giftName == "" {
|
||||
http.Error(w, "Gift name not provided", http.StatusBadRequest)
|
||||
error.Page(w, "Gift name not provided", http.StatusBadRequest, nil)
|
||||
return
|
||||
}
|
||||
err := user.AddGiftToUser(userReference, giftName)
|
||||
if err != nil {
|
||||
log.Printf("Failed to add gift idea to other user: %s\n", err)
|
||||
http.Error(w, "Failed to add gift idea to other user...", http.StatusInternalServerError)
|
||||
error.Page(w, "Failed to add gift idea to other user...", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
case "delete":
|
||||
|
|
@ -84,8 +80,7 @@ func (ctx *Context) ForeignWishlistPost(w http.ResponseWriter, r *http.Request)
|
|||
gifts := append(claims, unclaims...)
|
||||
err := user.RemoveGifts(gifts...)
|
||||
if err != nil {
|
||||
log.Printf("Failed to remove gift idea for other user: %s\n", err)
|
||||
http.Error(w, "Failed to remove gift idea for other user...", http.StatusInternalServerError)
|
||||
error.Page(w, "Failed to remove gift idea for other user...", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -2,16 +2,15 @@ package context
|
|||
|
||||
import (
|
||||
"lishwist/db"
|
||||
"lishwist/error"
|
||||
"lishwist/templates"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ForeignWishlistProps struct {
|
||||
type foreignWishlistProps struct {
|
||||
CurrentUserId string
|
||||
CurrentUserName string
|
||||
Username string
|
||||
UserReference string
|
||||
Gifts []db.Gift
|
||||
}
|
||||
|
||||
|
|
@ -19,25 +18,48 @@ func (ctx *Context) ForeignWishlist(w http.ResponseWriter, r *http.Request) {
|
|||
userReference := r.PathValue("userReference")
|
||||
user := ctx.Auth.ExpectUser(r)
|
||||
if user.Reference == userReference {
|
||||
http.Error(w, "You can't view your own list, silly ;)", http.StatusForbidden)
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
otherUser, err := db.GetUserByReference(userReference)
|
||||
if err != nil {
|
||||
log.Printf("An error occurred while fetching a user: %s\n", err)
|
||||
http.Error(w, "An error occurred while fetching this user :(", http.StatusInternalServerError)
|
||||
error.Page(w, "An error occurred while fetching this user :(", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if otherUser == nil {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
error.Page(w, "User not found", http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
gifts, err := user.GetOtherUserGifts(userReference)
|
||||
if err != nil {
|
||||
log.Printf("An error occurred while fetching %s's wishlist: %s\n", otherUser.Name, err)
|
||||
http.Error(w, "An error occurred while fetching this user's wishlist :(", http.StatusInternalServerError)
|
||||
error.Page(w, "An error occurred while fetching this user's wishlist :(", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
p := ForeignWishlistProps{CurrentUserId: user.Id, CurrentUserName: user.Name, Username: otherUser.Name, UserReference: userReference, Gifts: gifts}
|
||||
p := foreignWishlistProps{CurrentUserId: user.Id, CurrentUserName: user.Name, Username: otherUser.Name, Gifts: gifts}
|
||||
templates.Execute(w, "foreign_wishlist.gotmpl", p)
|
||||
}
|
||||
|
||||
type publicForeignWishlistProps struct {
|
||||
Username string
|
||||
GiftCount int
|
||||
}
|
||||
|
||||
func (ctx *Context) PublicForeignWishlist(w http.ResponseWriter, r *http.Request) {
|
||||
userReference := r.PathValue("userReference")
|
||||
otherUser, err := db.GetUserByReference(userReference)
|
||||
if err != nil {
|
||||
error.Page(w, "An error occurred while fetching this user :(", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if otherUser == nil {
|
||||
error.Page(w, "User not found", http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
giftCount, err := otherUser.CountGifts()
|
||||
if err != nil {
|
||||
error.Page(w, "An error occurred while fetching data about this user :(", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
p := publicForeignWishlistProps{Username: otherUser.Name, GiftCount: giftCount}
|
||||
templates.Execute(w, "public_foreign_wishlist.gotmpl", p)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"lishwist/db"
|
||||
"lishwist/env"
|
||||
"lishwist/error"
|
||||
"lishwist/templates"
|
||||
)
|
||||
|
||||
|
|
@ -20,12 +21,12 @@ func (ctx *Context) Home(w http.ResponseWriter, r *http.Request) {
|
|||
user := ctx.Auth.ExpectUser(r)
|
||||
gifts, err := user.GetGifts()
|
||||
if err != nil {
|
||||
http.Error(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError)
|
||||
error.Page(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
todo, err := user.GetTodo()
|
||||
if err != nil {
|
||||
http.Error(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError)
|
||||
error.Page(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
p := HomeProps{Username: user.Name, Gifts: gifts, Todo: todo, Reference: user.Reference, HostUrl: env.HostUrl.String()}
|
||||
|
|
|
|||
10
db/user.go
10
db/user.go
|
|
@ -85,6 +85,16 @@ func (u *User) GetPassHash() ([]byte, error) {
|
|||
return []byte(passHash), nil
|
||||
}
|
||||
|
||||
func (u *User) CountGifts() (int, error) {
|
||||
stmt := "SELECT COUNT(gift.id) AS gift_count FROM gift JOIN user ON gift.recipient_id = user.id LEFT JOIN user AS claimant ON gift.claimant_id = claimant.id WHERE gift.creator_id = user.id AND user.id = ?"
|
||||
var giftCount int
|
||||
err := database.QueryRow(stmt, u.Id).Scan(&giftCount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return giftCount, nil
|
||||
}
|
||||
|
||||
func (u *User) GetGifts() ([]Gift, error) {
|
||||
stmt := "SELECT gift.id, gift.name, claimant.id, claimant.name, gift.sent FROM gift JOIN user ON gift.recipient_id = user.id LEFT JOIN user AS claimant ON gift.claimant_id = claimant.id WHERE gift.creator_id = user.id AND user.id = ?"
|
||||
rows, err := database.Query(stmt, u.Id)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package error
|
||||
|
||||
import (
|
||||
"lishwist/templates"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type pageProps struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func Page(w http.ResponseWriter, publicMessage string, status int, err error) {
|
||||
log.Printf("%s --- %s\n", publicMessage, err)
|
||||
templates.Execute(w, "error_page.gotmpl", pageProps{publicMessage})
|
||||
http.Error(w, "", http.StatusInternalServerError)
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
password := []byte(os.Args[1])
|
||||
cost, err := strconv.ParseInt(os.Args[2], 10, 0)
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to parse cost: ", err)
|
||||
}
|
||||
hash, err := bcrypt.GenerateFromPassword(password, int(cost))
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to hash: ", err)
|
||||
}
|
||||
fmt.Println(string(hash))
|
||||
}
|
||||
4
main.go
4
main.go
|
|
@ -8,7 +8,6 @@ import (
|
|||
"lishwist/context"
|
||||
"lishwist/db"
|
||||
"lishwist/env"
|
||||
"lishwist/templates"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -30,10 +29,11 @@ func main() {
|
|||
Auth: authMiddleware,
|
||||
}
|
||||
|
||||
publicMux.HandleFunc("GET /register", templates.Register)
|
||||
publicMux.HandleFunc("GET /register", authMiddleware.Register)
|
||||
publicMux.HandleFunc("POST /register", authMiddleware.RegisterPost)
|
||||
publicMux.HandleFunc("GET /", authMiddleware.Login)
|
||||
publicMux.HandleFunc("POST /", authMiddleware.LoginPost)
|
||||
publicMux.HandleFunc("GET /list/{userReference}", ctx.PublicForeignWishlist)
|
||||
|
||||
protectedMux.HandleFunc("GET /{$}", ctx.Home)
|
||||
protectedMux.HandleFunc("POST /{$}", ctx.HomePost)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package sesh
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
func GetFirstFlash(w http.ResponseWriter, r *http.Request, session *sessions.Session, key ...string) (any, error) {
|
||||
flashes := session.Flashes(key...)
|
||||
|
||||
if len(flashes) < 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
flash := flashes[0]
|
||||
|
||||
if err := session.Save(r, w); err != nil {
|
||||
log.Println("Couldn't save session:", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return flash, nil
|
||||
}
|
||||
|
|
@ -1,3 +1,17 @@
|
|||
{{define "input"}}
|
||||
<input style="padding: .375rem .75rem;" class="form-control{{if .Error}} is-invalid{{end}}" {{if .Type}}type="{{.Type}}" {{end}}name="{{.Name}}"
|
||||
value="{{.Value}}" {{if .Required}}required {{end}}aria-describedby="{{if .Error}}{{.Name}}Error{{end}}">
|
||||
{{with .Error}}
|
||||
<div id="{{$.Name}}Error" class="invalid-feedback">
|
||||
{{.}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div style="margin-top: .25rem; font-size: .875em;">
|
||||
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
{{define "navbar"}}
|
||||
<nav>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/">Home</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
||||
{{define "body"}}
|
||||
<div class="container d-flex flex-grow-1 justify-content-center align-items-center flex-column">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<p class="mb-0">{{.Message}}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
{{end}}
|
||||
<form method="post">
|
||||
<div class="input-group mt-3">
|
||||
<input class="form-control" name="gift_name" required aria-describedby="gift_name_help">
|
||||
<input class="form-control" name="gift_name" required aria-describedby="gift_name_help" placeholder="Write a gift idea here" autofocus>
|
||||
<button class="btn btn-primary" type="submit" name="intent" value="add">Add gift idea</button>
|
||||
</div>
|
||||
<div id="gift_name_help" class="form-text">This will be invisible to {{.Username}}, but everyone else will be
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
{{end}}
|
||||
<form method="post">
|
||||
<div class="input-group">
|
||||
<input class="form-control" name="gift_name" required>
|
||||
<input class="form-control" name="gift_name" required placeholder="Write a gift idea here" autofocus>
|
||||
<button class="btn btn-primary" type="submit" name="intent" value="add_idea">Add gift idea</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -8,17 +8,22 @@
|
|||
<p class="mb-0">Registration successful. Now you can login.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{with .GeneralError}}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<p class="mb-0">{{.}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
<form method="post">
|
||||
<div class="d-flex flex-column gap-3">
|
||||
<div class="d-flex flex-column">
|
||||
<label>
|
||||
Username
|
||||
<input class="form-control" name="username" required>
|
||||
{{template "input" .Username}}
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input class="form-control" name="password" type="password" required>
|
||||
{{template "input" .Password}}
|
||||
</label>
|
||||
<div>
|
||||
<div class="mb-3">
|
||||
<a href="/register">Register</a>
|
||||
</div>
|
||||
<input class="btn btn-primary" type="submit" value="Login">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
{{define "navbar"}}
|
||||
<nav>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/">Home</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
||||
{{define "login_prompt"}}
|
||||
<a href="/">Login</a> or <a href="/register">register</a>
|
||||
{{end}}
|
||||
|
||||
{{define "body"}}
|
||||
<div class="overflow-y-scroll flex-grow-1">
|
||||
<div class="container py-5">
|
||||
<section class="card">
|
||||
<div class="card-body">
|
||||
<h2>{{.Username}}'s list</h2>
|
||||
{{if eq .GiftCount 0}}
|
||||
<p>{{.Username}} doesn't have any gift ideas!</p>
|
||||
<p>{{template "login_prompt"}} to add some! :^)</p>
|
||||
{{else}}
|
||||
{{if eq .GiftCount 1}}
|
||||
<p>{{.Username}} only has one gift idea.</p>
|
||||
<p>{{template "login_prompt"}} to claim it, or add more! :^)</p>
|
||||
{{else}}
|
||||
<p>{{.Username}} has {{.GiftCount}} gift ideas.</p>
|
||||
<p>{{template "login_prompt"}} to claim an idea, or add more! :^)</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package templates
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Register(w http.ResponseWriter, r *http.Request) {
|
||||
Execute(w, "register.gotmpl", nil)
|
||||
}
|
||||
|
|
@ -1,4 +1,11 @@
|
|||
{{define "navbar"}}
|
||||
<nav>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/">Home</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
||||
{{define "body"}}
|
||||
|
|
@ -7,19 +14,24 @@
|
|||
<p>Your password will be stored in a safe, responsible manner; but don't trust my programming skills!</p>
|
||||
<p class="mb-0">Maybe use a password here that you don't use for important things...</p>
|
||||
</div>
|
||||
{{with .GeneralError}}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<p class="mb-0">{{.}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
<form method="post">
|
||||
<div class="d-flex flex-column gap-3">
|
||||
<div class="d-flex flex-column">
|
||||
<label>
|
||||
Username
|
||||
<input class="form-control" name="username" required>
|
||||
{{template "input" .Username}}
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input class="form-control" name="newPassword" type="password" required minlength="5">
|
||||
{{template "input" .Password}}
|
||||
</label>
|
||||
<label>
|
||||
Confirm password
|
||||
<input class="form-control" name="confirmPassword" type="password" required minlength="5">
|
||||
{{template "input" .ConfirmPassword}}
|
||||
</label>
|
||||
<input class="btn btn-primary" type="submit" value="Register">
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,15 @@ import (
|
|||
"text/template"
|
||||
)
|
||||
|
||||
type InputProps struct {
|
||||
Type string
|
||||
Name string
|
||||
Required bool
|
||||
Value string
|
||||
Error string
|
||||
MinLength uint
|
||||
}
|
||||
|
||||
var tmpls map[string]*template.Template = loadTemplates()
|
||||
|
||||
func Execute(w http.ResponseWriter, name string, data any) {
|
||||
|
|
@ -21,10 +30,14 @@ func loadTemplates() map[string]*template.Template {
|
|||
loginTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/login.gotmpl"))
|
||||
registerTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/register.gotmpl"))
|
||||
foreignTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/foreign_wishlist.gotmpl"))
|
||||
publicForeignTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/public_foreign_wishlist.gotmpl"))
|
||||
errorTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/error_page.gotmpl"))
|
||||
return map[string]*template.Template{
|
||||
"home.gotmpl": homeTmpl,
|
||||
"login.gotmpl": loginTmpl,
|
||||
"register.gotmpl": registerTmpl,
|
||||
"foreign_wishlist.gotmpl": foreignTmpl,
|
||||
"home.gotmpl": homeTmpl,
|
||||
"login.gotmpl": loginTmpl,
|
||||
"register.gotmpl": registerTmpl,
|
||||
"foreign_wishlist.gotmpl": foreignTmpl,
|
||||
"public_foreign_wishlist.gotmpl": publicForeignTmpl,
|
||||
"error_page.gotmpl": errorTmpl,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue