feat: use rsvp module #6

Merged
Teajey merged 1 commits from response-middleware-1 into main 2024-12-05 01:05:31 +13:00
24 changed files with 466 additions and 438 deletions
Showing only changes of commit ca484e95a1 - Show all commits

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ gin-bin
lishwist.db lishwist.db
.env*.local .env*.local
server/db/init_sql.go server/db/init_sql.go
.ignored/

View File

@ -9,8 +9,8 @@ import (
) )
type LoginProps struct { type LoginProps struct {
GeneralError string GeneralError string `json:",omitempty"`
SuccessfulRegistration bool SuccessfulRegistration bool `json:",omitempty"`
Username templates.InputProps Username templates.InputProps
Password templates.InputProps Password templates.InputProps
} }

View File

@ -40,27 +40,27 @@ func queryForGroup(query string, args ...any) (*Group, error) {
func queryForGroups(query string, args ...any) ([]Group, error) { func queryForGroups(query string, args ...any) ([]Group, error) {
groups := []Group{} groups := []Group{}
rows, err := database.Query(query) rows, err := database.Query(query, args...)
if err != nil { if err != nil {
return groups, err return groups, fmt.Errorf("Query failed: %w", err)
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var group Group var group Group
err := rows.Scan(&group.Id, &group.Name, &group.Reference) err := rows.Scan(&group.Id, &group.Name, &group.Reference)
if err != nil { if err != nil {
return groups, err return groups, fmt.Errorf("Failed to scan row: %w", err)
} }
members, err := queryForGroupMembers(group.Id) members, err := queryForGroupMembers(group.Id)
if err != nil { if err != nil {
return groups, err return groups, fmt.Errorf("Failed to query for group members: %w", err)
} }
group.Members = members group.Members = members
groups = append(groups, group) groups = append(groups, group)
} }
err = rows.Err() err = rows.Err()
if err != nil { if err != nil {
return groups, err return groups, fmt.Errorf("Rows error: %w", err)
} }
return groups, nil return groups, nil
} }

View File

@ -18,14 +18,14 @@ type User struct {
type Gift struct { type Gift struct {
Id string Id string
Name string Name string
ClaimantId string ClaimantId string `json:",omitempty"`
ClaimantName string ClaimantName string `json:",omitempty"`
Sent bool Sent bool
RecipientId string RecipientId string `json:",omitempty"`
RecipientName string RecipientName string `json:",omitempty"`
RecipientRef string RecipientRef string `json:",omitempty"`
CreatorId string CreatorId string `json:",omitempty"`
CreatorName string CreatorName string `json:",omitempty"`
} }
func queryForUser(query string, args ...any) (*User, error) { func queryForUser(query string, args ...any) (*User, error) {
@ -406,31 +406,7 @@ func (u *User) AddGiftToUser(otherUserReference string, giftName string) error {
func (u *User) GetGroups() ([]Group, error) { func (u *User) GetGroups() ([]Group, error) {
stmt := "SELECT [group].id, [group].name, [group].reference FROM [group] JOIN group_member ON group_member.group_id = [group].id JOIN v_user AS user ON user.id = group_member.user_id WHERE user.id = ?" stmt := "SELECT [group].id, [group].name, [group].reference FROM [group] JOIN group_member ON group_member.group_id = [group].id JOIN v_user AS user ON user.id = group_member.user_id WHERE user.id = ?"
rows, err := database.Query(stmt, u.Id) return queryForGroups(stmt, u.Id)
if err != nil {
return nil, err
}
defer rows.Close()
groups := []Group{}
for rows.Next() {
var id string
var name string
var reference string
err := rows.Scan(&id, &name, &reference)
if err != nil {
return nil, err
}
groups = append(groups, Group{
Id: id,
Name: name,
Reference: reference,
})
}
err = rows.Err()
if err != nil {
return nil, err
}
return groups, nil
} }
func (u *User) GetGroupByReference(reference string) (*Group, error) { func (u *User) GetGroupByReference(reference string) (*Group, error) {

View File

@ -1,19 +0,0 @@
package error
import (
"lishwist/templates"
"log"
"net/http"
)
type pageProps struct {
Message string
}
func Page(w http.ResponseWriter, publicMessage string, status int, err error) {
w.WriteHeader(status)
if err != nil {
log.Printf("%s --- %s\n", publicMessage, err)
}
templates.Execute(w, "error_page.gotmpl", pageProps{publicMessage})
}

View File

@ -35,32 +35,26 @@ func main() {
r := router.New(store) r := router.New(store)
route := routing.NewContext(store) r.Public.HandleFunc("GET /", routing.Login)
r.Public.HandleFunc("GET /group/{groupReference}", routing.PublicGroupPage)
r.Public.HandleFunc("GET /list/{userReference}", routing.PublicWishlist)
r.Public.HandleFunc("GET /register", routing.Register)
r.Public.HandleFunc("POST /", routing.LoginPost)
r.Public.HandleFunc("POST /register", routing.RegisterPost)
r.Html.Public.HandleFunc("GET /register", route.Register) r.Private.HandleFunc("GET /", routing.NotFound)
r.Html.Public.HandleFunc("POST /register", route.RegisterPost) r.Private.HandleFunc("GET /group/{groupReference}", routing.ExpectUser(routing.GroupPage))
r.Html.Public.HandleFunc("GET /", route.Login) r.Private.HandleFunc("GET /groups", routing.ExpectUser(routing.GroupsJson))
r.Html.Public.HandleFunc("POST /", route.LoginPost) r.Private.HandleFunc("GET /groups/{groupReference}", routing.ExpectUser(routing.Group))
r.Html.Public.HandleFunc("GET /list/{userReference}", route.PublicWishlist) r.Private.HandleFunc("GET /list/{userReference}", routing.ExpectUser(routing.ForeignWishlist))
r.Html.Public.HandleFunc("GET /group/{groupReference}", route.PublicGroupPage) r.Private.HandleFunc("GET /users", routing.ExpectUser(routing.Users))
r.Private.HandleFunc("GET /users/{userReference}", routing.ExpectUser(routing.User))
r.Html.Private.Handle("GET /{$}", route.ExpectUser(route.Home)) r.Private.HandleFunc("GET /{$}", routing.ExpectUser(routing.Home))
r.Html.Private.Handle("POST /{$}", route.ExpectUser(route.HomePost)) r.Private.HandleFunc("POST /groups/{groupReference}", routing.ExpectUser(routing.GroupPost))
r.Html.Private.Handle("GET /list/{userReference}", route.ExpectUser(route.ForeignWishlist)) r.Private.HandleFunc("POST /list/{userReference}", routing.ExpectUser(routing.ForeignWishlistPost))
r.Html.Private.Handle("POST /list/{userReference}", route.ExpectUser(route.ForeignWishlistPost)) r.Private.HandleFunc("POST /logout", routing.LogoutPost)
r.Html.Private.Handle("GET /group/{groupReference}", route.ExpectUser(route.GroupPage)) r.Private.HandleFunc("POST /users/{userReference}", routing.ExpectUser(routing.UserPost))
r.Html.Private.HandleFunc("POST /logout", route.LogoutPost) r.Private.HandleFunc("POST /{$}", routing.ExpectUser(routing.HomePost))
r.Html.Private.HandleFunc("GET /", routing.NotFound)
r.Json.Public.HandleFunc("GET /", routing.NotFoundJson)
r.Json.Private.Handle("GET /users", route.ExpectUser(route.UsersJson))
r.Json.Private.Handle("GET /users/{userReference}", route.ExpectUser(route.User))
r.Json.Private.Handle("POST /users/{userReference}", route.ExpectUser(route.UserPost))
r.Json.Private.Handle("GET /groups", route.ExpectUser(route.GroupsJson))
r.Json.Private.Handle("POST /groups/{groupReference}", route.ExpectUser(route.GroupPost))
r.Json.Private.Handle("GET /groups/{groupReference}", route.ExpectUser(route.Group))
r.Json.Private.HandleFunc("GET /", routing.NotFoundJson)
http.Handle("/", r) http.Handle("/", r)

View File

@ -1,16 +1,16 @@
package router package router
import ( import (
"lishwist/rsvp"
"net/http" "net/http"
"strings"
"github.com/Teajey/sqlstore" "github.com/Teajey/sqlstore"
) )
type VisibilityRouter struct { type VisibilityRouter struct {
Store *sqlstore.Store Store *sqlstore.Store
Public *http.ServeMux Public *rsvp.ServeMux
Private *http.ServeMux Private *rsvp.ServeMux
} }
func (s *VisibilityRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *VisibilityRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -24,33 +24,10 @@ func (s *VisibilityRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
} }
type Router struct { func New(store *sqlstore.Store) *VisibilityRouter {
Json VisibilityRouter return &VisibilityRouter{
Html VisibilityRouter Store: store,
} Public: rsvp.NewServeMux(store),
Private: rsvp.NewServeMux(store),
func (s *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
accept := r.Header.Get("Accept")
switch {
case strings.HasPrefix(accept, "application/json"):
s.Json.ServeHTTP(w, r)
default:
s.Html.ServeHTTP(w, r)
}
}
func New(store *sqlstore.Store) *Router {
return &Router{
Json: VisibilityRouter{
Store: store,
Public: http.NewServeMux(),
Private: http.NewServeMux(),
},
Html: VisibilityRouter{
Store: store,
Public: http.NewServeMux(),
Private: http.NewServeMux(),
},
} }
} }

View File

@ -2,39 +2,23 @@ package routing
import ( import (
"lishwist/db" "lishwist/db"
"log" "lishwist/rsvp"
"net/http" "net/http"
"github.com/Teajey/sqlstore"
) )
type Context struct { func ExpectUser(next func(*db.User, http.Header, *rsvp.Request) rsvp.Response) rsvp.HandlerFunc {
store *sqlstore.Store return func(w http.Header, r *rsvp.Request) rsvp.Response {
} session := r.GetSession()
username, ok := session.GetValue("username").(string)
func NewContext(store *sqlstore.Store) *Context {
return &Context{
store,
}
}
func (ctx *Context) ExpectUser(next func(*db.User, http.ResponseWriter, *http.Request)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session, _ := ctx.store.Get(r, "lishwist_user")
username, ok := session.Values["username"].(string)
if !ok { if !ok {
log.Println("Failed to get username") return rsvp.Error(http.StatusInternalServerError, "Something went wrong.").Log("Failed to get username from session")
http.Error(w, "", http.StatusInternalServerError)
return
} }
user, err := db.GetUserByName(username) user, err := db.GetUserByName(username)
if err != nil { if err != nil {
log.Printf("Failed to get user: %s\n", err) return rsvp.Error(http.StatusInternalServerError, "Something went wrong.").Log("Failed to get user %q: %s", username, err)
http.Error(w, "", http.StatusInternalServerError)
return
} }
next(user, w, r) return next(user, w, r)
}) }
} }

View File

@ -2,8 +2,7 @@ package routing
import ( import (
"lishwist/db" "lishwist/db"
"lishwist/error" "lishwist/rsvp"
"lishwist/templates"
"net/http" "net/http"
) )
@ -14,28 +13,24 @@ type foreignWishlistProps struct {
Gifts []db.Gift Gifts []db.Gift
} }
func (ctx *Context) ForeignWishlist(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func ForeignWishlist(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
userReference := r.PathValue("userReference") userReference := r.PathValue("userReference")
if currentUser.Reference == userReference { if currentUser.Reference == userReference {
http.Redirect(w, r, "/", http.StatusSeeOther) return rsvp.SeeOther("/")
return
} }
otherUser, err := db.GetUserByReference(userReference) otherUser, err := db.GetUserByReference(userReference)
if err != nil { if err != nil {
error.Page(w, "An error occurred while fetching this user :(", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("Couldn't get user by reference %q: %s", userReference, err)
return
} }
if otherUser == nil { if otherUser == nil {
error.Page(w, "User not found", http.StatusNotFound, err) return rsvp.Error(http.StatusInternalServerError, "User not found")
return
} }
gifts, err := currentUser.GetOtherUserGifts(userReference) gifts, err := currentUser.GetOtherUserGifts(userReference)
if err != nil { if err != nil {
error.Page(w, "An error occurred while fetching this user's wishlist :(", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("%q couldn't get wishes of other user %q: %s", currentUser.Name, otherUser.Name, err)
return
} }
p := foreignWishlistProps{CurrentUserId: currentUser.Id, CurrentUserName: currentUser.Name, Username: otherUser.Name, Gifts: gifts} p := foreignWishlistProps{CurrentUserId: currentUser.Id, CurrentUserName: currentUser.Name, Username: otherUser.Name, Gifts: gifts}
templates.Execute(w, "foreign_wishlist.gotmpl", p) return rsvp.Data("foreign_wishlist.gotmpl", p)
} }
type publicWishlistProps struct { type publicWishlistProps struct {
@ -43,22 +38,19 @@ type publicWishlistProps struct {
GiftCount int GiftCount int
} }
func (ctx *Context) PublicWishlist(w http.ResponseWriter, r *http.Request) { func PublicWishlist(h http.Header, r *rsvp.Request) rsvp.Response {
userReference := r.PathValue("userReference") userReference := r.PathValue("userReference")
otherUser, err := db.GetUserByReference(userReference) otherUser, err := db.GetUserByReference(userReference)
if err != nil { if err != nil {
error.Page(w, "An error occurred while fetching this user :(", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("Couldn't get user by reference %q on public wishlist: %s", userReference, err)
return
} }
if otherUser == nil { if otherUser == nil {
error.Page(w, "User not found", http.StatusNotFound, err) return rsvp.Error(http.StatusInternalServerError, "User not found")
return
} }
giftCount, err := otherUser.CountGifts() giftCount, err := otherUser.CountGifts()
if err != nil { if err != nil {
error.Page(w, "An error occurred while fetching data about this user :(", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("Couldn't get wishes of user %q on public wishlist: %s", otherUser.Name, err)
return
} }
p := publicWishlistProps{Username: otherUser.Name, GiftCount: giftCount} p := publicWishlistProps{Username: otherUser.Name, GiftCount: giftCount}
templates.Execute(w, "public_foreign_wishlist.gotmpl", p) return rsvp.Data("public_foreign_wishlist.gotmpl", p)
} }

View File

@ -1,13 +1,11 @@
package routing package routing
import ( import (
"encoding/json"
"net/http" "net/http"
"slices" "slices"
"lishwist/db" "lishwist/db"
"lishwist/error" "lishwist/rsvp"
"lishwist/templates"
) )
type GroupProps struct { type GroupProps struct {
@ -15,16 +13,14 @@ type GroupProps struct {
CurrentUsername string CurrentUsername string
} }
func (ctx *Context) GroupPage(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func GroupPage(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
groupReference := r.PathValue("groupReference") groupReference := r.PathValue("groupReference")
group, err := currentUser.GetGroupByReference(groupReference) group, err := currentUser.GetGroupByReference(groupReference)
if err != nil { if err != nil {
error.Page(w, "An error occurred while fetching this group :(", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this group :(").Log("Couldn't get group: %s", err)
return
} }
if group == nil { if group == nil {
error.Page(w, "Group not found. (It might be because you're not a member)", http.StatusNotFound, nil) return rsvp.Error(http.StatusNotFound, "Group not found. (It might be because you're not a member)").Log("Couldn't get group: %s", err)
return
} }
index := group.MemberIndex(currentUser.Id) index := group.MemberIndex(currentUser.Id)
group.Members = slices.Delete(group.Members, index, index+1) group.Members = slices.Delete(group.Members, index, index+1)
@ -32,68 +28,58 @@ func (ctx *Context) GroupPage(currentUser *db.User, w http.ResponseWriter, r *ht
Group: group, Group: group,
CurrentUsername: currentUser.Name, CurrentUsername: currentUser.Name,
} }
templates.Execute(w, "group_page.gotmpl", p) return rsvp.Data("group_page.gotmpl", p)
} }
func (ctx *Context) PublicGroupPage(w http.ResponseWriter, r *http.Request) { func PublicGroupPage(h http.Header, r *rsvp.Request) rsvp.Response {
groupReference := r.PathValue("groupReference") groupReference := r.PathValue("groupReference")
group, err := db.GetGroupByReference(groupReference) group, err := db.GetGroupByReference(groupReference)
if err != nil { if err != nil {
error.Page(w, "An error occurred while fetching this group :(", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this group :(").Log("Couldn't get group: %s", err)
return
} }
p := GroupProps{ p := GroupProps{
Group: group, Group: group,
} }
templates.Execute(w, "public_group_page.gotmpl", p) return rsvp.Data("public_group_page.gotmpl", p)
} }
func (ctx *Context) GroupPost(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func GroupPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if !currentUser.IsAdmin { if !currentUser.IsAdmin {
NotFoundJson(w, r) return NotFound(h, r)
return
}
if err := r.ParseForm(); err != nil {
writeGeneralErrorJson(w, http.StatusBadRequest, "Failed to parse form: "+err.Error())
return
} }
form := r.ParseForm()
var group *db.Group var group *db.Group
reference := r.PathValue("groupReference") reference := r.PathValue("groupReference")
name := r.Form.Get("name") name := form.Get("name")
addUsers := r.Form["addUser"] addUsers := form["addUser"]
removeUsers := r.Form["removeUser"] removeUsers := form["removeUser"]
if name != "" { if name != "" {
createdGroup, err := db.CreateGroup(name, reference) createdGroup, err := db.CreateGroup(name, reference)
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusBadRequest, "Failed to create group: "+err.Error()) return rsvp.Error(http.StatusInternalServerError, "Failed to create group: %w", err)
return
} }
group = createdGroup group = createdGroup
} else { } else {
existingGroup, err := db.GetGroupByReference(reference) existingGroup, err := db.GetGroupByReference(reference)
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusBadRequest, "Failed to get group: "+err.Error()) return rsvp.Error(http.StatusInternalServerError, "Failed to get group: %w", err)
return
} }
if existingGroup == nil { if existingGroup == nil {
writeGeneralErrorJson(w, http.StatusNotFound, "Group not found") return rsvp.Error(http.StatusNotFound, "Group not found", err)
return
} }
group = existingGroup group = existingGroup
for _, userId := range removeUsers { for _, userId := range removeUsers {
index := group.MemberIndex(userId) index := group.MemberIndex(userId)
if index == -1 { if index == -1 {
writeGeneralErrorJson(w, http.StatusBadRequest, "Group %q does not contain a user with id %s", reference, userId) return rsvp.Error(http.StatusBadRequest, "Group %q does not contain a user with id %s", reference, userId)
return
} }
err = group.RemoveUser(userId) err = group.RemoveUser(userId)
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusBadRequest, "On group %q failed to remove user with id %s: %s", reference, userId, err) return rsvp.Error(http.StatusInternalServerError, "On group %q failed to remove user with id %s: %s", reference, userId, err)
return
} }
group.Members = slices.Delete(group.Members, index, index+1) group.Members = slices.Delete(group.Members, index, index+1)
} }
@ -102,54 +88,47 @@ func (ctx *Context) GroupPost(currentUser *db.User, w http.ResponseWriter, r *ht
for _, userId := range addUsers { for _, userId := range addUsers {
user, err := db.GetUser(userId) user, err := db.GetUser(userId)
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusBadRequest, "Groups exists, but a user with id %s could not be fetched: %s", userId, err) return rsvp.Error(http.StatusInternalServerError, "Groups exists, but a user with id %s could not be fetched: %s", userId, err)
return
} }
if user == nil { if user == nil {
writeGeneralErrorJson(w, http.StatusBadRequest, "Groups exists, but a user with id %s does not exist", userId) return rsvp.Error(http.StatusInternalServerError, "Groups exists, but a user with id %s does not exist", userId)
return
} }
err = group.AddUser(user.Id) err = group.AddUser(user.Id)
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusBadRequest, "Groups exists, but failed to add user with id %s: %s", userId, err) return rsvp.Error(http.StatusInternalServerError, "Groups exists, but failed to add user with id %s: %s", userId, err)
return
} }
group.Members = append(group.Members, *user) group.Members = append(group.Members, *user)
} }
_ = json.NewEncoder(w).Encode(group) return rsvp.Data("", group)
} }
func (ctx *Context) GroupsJson(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func GroupsJson(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if !currentUser.IsAdmin { if !currentUser.IsAdmin {
NotFoundJson(w, r) return NotFound(h, r)
return
} }
groups, err := db.GetAllGroups() groups, err := db.GetAllGroups()
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusBadRequest, "Failed to get groups: "+err.Error()) return rsvp.Error(http.StatusInternalServerError, "Failed to get groups: %s", err)
return
} }
_ = json.NewEncoder(w).Encode(groups) return rsvp.Data("", groups)
} }
func (ctx *Context) Group(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func Group(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if !currentUser.IsAdmin { if !currentUser.IsAdmin {
NotFoundJson(w, r) return NotFound(h, r)
return
} }
groupReference := r.PathValue("groupReference") groupReference := r.PathValue("groupReference")
group, err := db.GetGroupByReference(groupReference) group, err := db.GetGroupByReference(groupReference)
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusBadRequest, "Couldn't get group: %s", err) return rsvp.Error(http.StatusInternalServerError, "Couldn't get group: %s", err)
return
} }
if group == nil { if group == nil {
writeGeneralErrorJson(w, http.StatusNotFound, "Group not found.") return rsvp.Error(http.StatusNotFound, "Group not found.")
return
} }
_ = json.NewEncoder(w).Encode(group) return rsvp.Data("", group)
} }

View File

@ -5,8 +5,7 @@ import (
"lishwist/db" "lishwist/db"
"lishwist/env" "lishwist/env"
"lishwist/error" "lishwist/rsvp"
"lishwist/templates"
) )
type HomeProps struct { type HomeProps struct {
@ -18,40 +17,31 @@ type HomeProps struct {
Groups []db.Group Groups []db.Group
} }
func (ctx *Context) Home(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func Home(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
gifts, err := currentUser.GetGifts() gifts, err := currentUser.GetGifts()
if err != nil { if err != nil {
error.Page(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(").Log("Failed to get gifts: %s", err)
return
} }
todo, err := currentUser.GetTodo() todo, err := currentUser.GetTodo()
if err != nil { if err != nil {
error.Page(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(").Log("Failed to get todo: %s", err)
return
} }
groups, err := currentUser.GetGroups() groups, err := currentUser.GetGroups()
if err != nil { if err != nil {
error.Page(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(").Log("Failed to get groups: %s", err)
return
} }
p := HomeProps{Username: currentUser.Name, Gifts: gifts, Todo: todo, Reference: currentUser.Reference, HostUrl: env.HostUrl.String(), Groups: groups} p := HomeProps{Username: currentUser.Name, Gifts: gifts, Todo: todo, Reference: currentUser.Reference, HostUrl: env.HostUrl.String(), Groups: groups}
templates.Execute(w, "home.gotmpl", p) return rsvp.Data("home.gotmpl", p)
} }
func (ctx *Context) HomePost(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func HomePost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if err := r.ParseForm(); err != nil { form := r.ParseForm()
http.Error(w, "Couldn't parse form", http.StatusBadRequest) switch form.Get("intent") {
return
}
switch r.Form.Get("intent") {
case "add_idea": case "add_idea":
ctx.WishlistAdd(currentUser, w, r) return WishlistAdd(currentUser, h, r)
return
case "delete_idea": case "delete_idea":
ctx.WishlistDelete(currentUser, w, r) return WishlistDelete(currentUser, h, r)
return
default: default:
ctx.TodoUpdate(currentUser, w, r) return TodoUpdate(currentUser, h, r)
return
} }
} }

View File

@ -1,25 +1,17 @@
package routing package routing
import ( import (
"encoding/json"
"lishwist/api" "lishwist/api"
sesh "lishwist/session" "lishwist/rsvp"
"lishwist/templates"
"log"
"net/http" "net/http"
) )
func (ctx *Context) Login(w http.ResponseWriter, r *http.Request) { func Login(h http.Header, r *rsvp.Request) rsvp.Response {
session, _ := ctx.store.Get(r, "lishwist_user") session := r.GetSession()
props := api.NewLoginProps("", "") props := api.NewLoginProps("", "")
flash, err := sesh.GetFirstFlash(w, r, session, "login_props") flash := session.FlashGet("login_props")
if err != nil {
http.Error(w, "Something went wrong. Error code: Zuko", http.StatusInternalServerError)
return
}
flashProps, ok := flash.(*api.LoginProps) flashProps, ok := flash.(*api.LoginProps)
if ok { if ok {
props.Username.Value = flashProps.Username.Value props.Username.Value = flashProps.Username.Value
@ -29,46 +21,30 @@ func (ctx *Context) Login(w http.ResponseWriter, r *http.Request) {
props.Password.Error = flashProps.Password.Error props.Password.Error = flashProps.Password.Error
} }
flash, err = sesh.GetFirstFlash(w, r, session, "successful_registration") flash = session.FlashGet("successful_registration")
if err != nil {
http.Error(w, "Something went wrong. Error code: Zuko", http.StatusInternalServerError)
return
}
successfulReg, _ := flash.(bool) successfulReg, _ := flash.(bool)
if successfulReg { if successfulReg {
props.SuccessfulRegistration = true props.SuccessfulRegistration = true
} }
templates.Execute(w, "login.gotmpl", props) return rsvp.Data("login.gotmpl", props).SaveSession(session)
} }
func (ctx *Context) LoginPost(w http.ResponseWriter, r *http.Request) { func LoginPost(h http.Header, r *rsvp.Request) rsvp.Response {
if err := r.ParseForm(); err != nil { form := r.ParseForm()
http.Error(w, "Couldn't parse form", http.StatusBadRequest) session := r.GetSession()
return
}
username := r.Form.Get("username") username := form.Get("username")
password := r.Form.Get("password") password := form.Get("password")
props := api.Login(username, password) props := api.Login(username, password)
if props != nil { if props != nil {
ctx.RedirectWithFlash(w, r, "/", "login_props", &props) return rsvp.SeeOther("/").SaveSession(session)
_ = json.NewEncoder(w).Encode(props)
return
} }
// NOTE: Overwriting any existing cookie or session here. So we don't care if there's an error session.SetID("")
session, _ := ctx.store.Get(r, "lishwist_user") session.SetValue("authorized", true)
session.ID = "" session.SetValue("username", username)
session.Values["authorized"] = true
session.Values["username"] = username
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, r.URL.Path, http.StatusSeeOther) return rsvp.SeeOther(r.URL().Path).SaveSession(session)
} }

View File

@ -1,22 +1,15 @@
package routing package routing
import ( import (
"lishwist/rsvp"
"net/http" "net/http"
) )
func (ctx *Context) LogoutPost(w http.ResponseWriter, r *http.Request) { func LogoutPost(h http.Header, r *rsvp.Request) rsvp.Response {
session, err := ctx.store.Get(r, "lishwist_user") session := r.GetSession()
if err != nil {
http.Error(w, "Something went wrong. Error code: Iroh", http.StatusInternalServerError)
return
}
session.Options.MaxAge = 0 session.Options().MaxAge = 0
session.Values = nil session.ClearValues()
if err := session.Save(r, w); err != nil {
http.Error(w, "Something went wrong. Error code: Azula", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/", http.StatusSeeOther) return rsvp.SeeOther("/").SaveSession(session)
} }

View File

@ -3,15 +3,9 @@ package routing
import ( import (
"net/http" "net/http"
"lishwist/error" "lishwist/rsvp"
) )
func NotFoundJson(w http.ResponseWriter, r *http.Request) { func NotFound(h http.Header, r *rsvp.Request) rsvp.Response {
w.Header().Add("Content-Type", "application/json; charset=utf-8") return rsvp.Error(http.StatusNotFound, "Page not found")
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"GeneralError":"Not Found"}`))
}
func NotFound(w http.ResponseWriter, r *http.Request) {
error.Page(w, "404 -- Page not found", http.StatusNotFound, nil)
} }

View File

@ -1,17 +0,0 @@
package routing
import (
"log"
"net/http"
)
func (ctx *Context) RedirectWithFlash(w http.ResponseWriter, r *http.Request, url string, key string, flash any) {
session, _ := ctx.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)
}

View File

@ -1,20 +1,19 @@
package routing package routing
import ( import (
"encoding/json"
"lishwist/api" "lishwist/api"
"lishwist/templates" "lishwist/rsvp"
"log"
"net/http" "net/http"
) )
func (ctx *Context) Register(w http.ResponseWriter, r *http.Request) { func Register(h http.Header, r *rsvp.Request) rsvp.Response {
props := api.NewRegisterProps("", "", "") props := api.NewRegisterProps("", "", "")
session, _ := ctx.store.Get(r, "lishwist_user") session := r.GetSession()
if flashes := session.Flashes("register_props"); len(flashes) > 0 { flash := session.FlashGet("register_props")
log.Printf("Register found flashes: %#v\n", flashes)
flashProps, _ := flashes[0].(*api.RegisterProps) flashProps, _ := flash.(*api.RegisterProps)
if flashProps != nil {
props.Username.Value = flashProps.Username.Value props.Username.Value = flashProps.Username.Value
props.GeneralError = flashProps.GeneralError props.GeneralError = flashProps.GeneralError
@ -22,32 +21,25 @@ func (ctx *Context) Register(w http.ResponseWriter, r *http.Request) {
props.ConfirmPassword.Error = flashProps.ConfirmPassword.Error props.ConfirmPassword.Error = flashProps.ConfirmPassword.Error
} }
if err := session.Save(r, w); err != nil { return rsvp.Data("register.gotmpl", props).SaveSession(session)
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 (ctx *Context) RegisterPost(w http.ResponseWriter, r *http.Request) { func RegisterPost(h http.Header, r *rsvp.Request) rsvp.Response {
if err := r.ParseForm(); err != nil { form := r.ParseForm()
http.Error(w, "Couldn't parse form", http.StatusBadRequest)
return
}
username := r.Form.Get("username") username := form.Get("username")
newPassword := r.Form.Get("newPassword") newPassword := form.Get("newPassword")
confirmPassword := r.Form.Get("confirmPassword") confirmPassword := form.Get("confirmPassword")
props := api.Register(username, newPassword, confirmPassword) props := api.Register(username, newPassword, confirmPassword)
s := r.GetSession()
if props != nil { if props != nil {
ctx.RedirectWithFlash(w, r, "/register", "register_props", &props) s.FlashSet(&props, "register_props")
_ = json.NewEncoder(w).Encode(props) return rsvp.SeeOther("/register").SaveSession(s)
return
} }
ctx.RedirectWithFlash(w, r, "/", "successful_registration", true) s.FlashSet(true, "successful_registration")
return rsvp.SeeOther("/").SaveSession(s)
} }

View File

@ -2,34 +2,28 @@ package routing
import ( import (
"lishwist/db" "lishwist/db"
"log" "lishwist/rsvp"
"net/http" "net/http"
) )
func (ctx *Context) TodoUpdate(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func TodoUpdate(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if err := r.ParseForm(); err != nil { form := r.ParseForm()
http.Error(w, err.Error(), http.StatusBadRequest)
return switch form.Get("intent") {
}
switch r.Form.Get("intent") {
case "unclaim_todo": case "unclaim_todo":
unclaims := r.Form["gift"] unclaims := form["gift"]
err := currentUser.ClaimGifts([]string{}, unclaims) err := currentUser.ClaimGifts([]string{}, unclaims)
if err != nil { if err != nil {
http.Error(w, "Failed to update claim...", http.StatusInternalServerError) return rsvp.Error(http.StatusInternalServerError, "Failed to update claim...").LogError(err)
return
} }
case "complete_todo": case "complete_todo":
claims := r.Form["gift"] claims := form["gift"]
err := currentUser.CompleteGifts(claims) err := currentUser.CompleteGifts(claims)
if err != nil { if err != nil {
log.Printf("Failed to complete gifts: %s\n", err) return rsvp.Error(http.StatusInternalServerError, "Failed to complete gifts...").LogError(err)
http.Error(w, "Failed to complete gifts...", http.StatusInternalServerError)
return
} }
default: default:
http.Error(w, "Invalid intent", http.StatusBadRequest) return rsvp.Error(http.StatusBadRequest, "Invalid intent")
return
} }
http.Redirect(w, r, "/", http.StatusSeeOther) return rsvp.SeeOther("/")
} }

View File

@ -1,82 +1,70 @@
package routing package routing
import ( import (
"encoding/json"
"lishwist/db" "lishwist/db"
"lishwist/rsvp"
"net/http" "net/http"
) )
func (ctx *Context) UsersJson(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func Users(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if !currentUser.IsAdmin { if !currentUser.IsAdmin {
NotFoundJson(w, r) return NotFound(h, r)
return
} }
users, err := db.GetAllUsers() users, err := db.GetAllUsers()
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusInternalServerError, "Failed to get users: "+err.Error()) return rsvp.Error(http.StatusInternalServerError, "Failed to get users: %s", err)
return
} }
_ = json.NewEncoder(w).Encode(users) return rsvp.Data("", users)
} }
func (ctx *Context) User(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func User(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if !currentUser.IsAdmin { if !currentUser.IsAdmin {
NotFoundJson(w, r) return NotFound(h, r)
return
} }
reference := r.PathValue("userReference") reference := r.PathValue("userReference")
user, err := db.GetUserByReference(reference) user, err := db.GetUserByReference(reference)
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusInternalServerError, "Failed to get user: %s", err) return rsvp.Error(http.StatusInternalServerError, "Failed to get user: %s", err)
return
} }
if user == nil { if user == nil {
writeGeneralErrorJson(w, http.StatusNotFound, "User not found") return rsvp.Error(http.StatusNotFound, "User not found")
return
} }
_ = json.NewEncoder(w).Encode(user) return rsvp.Data("", user)
} }
func (ctx *Context) UserPost(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func UserPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if !currentUser.IsAdmin { if !currentUser.IsAdmin {
NotFoundJson(w, r) return NotFound(h, r)
return
}
if err := r.ParseForm(); err != nil {
writeGeneralErrorJson(w, http.StatusInternalServerError, "Failed to parse form: %s", err)
return
} }
form := r.ParseForm()
reference := r.PathValue("userReference") reference := r.PathValue("userReference")
if reference == currentUser.Reference { if reference == currentUser.Reference {
writeGeneralErrorJson(w, http.StatusForbidden, "You cannot delete yourself.") return rsvp.Error(http.StatusForbidden, "You cannot delete yourself.")
return
} }
user, err := db.GetAnyUserByReference(reference) user, err := db.GetAnyUserByReference(reference)
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusInternalServerError, "Failed to get user: %s", err) return rsvp.Error(http.StatusInternalServerError, "Failed to get user: %s", err)
return
} }
if user == nil { if user == nil {
writeGeneralErrorJson(w, http.StatusNotFound, "User not found") return rsvp.Error(http.StatusNotFound, "User not found")
return
} }
intent := r.Form.Get("intent") intent := form.Get("intent")
if intent != "" { if intent != "" {
err = user.SetLive(intent != "delete") err = user.SetLive(intent != "delete")
if err != nil { if err != nil {
writeGeneralErrorJson(w, http.StatusInternalServerError, "Failed to delete user: "+err.Error()) return rsvp.Error(http.StatusInternalServerError, "Failed to delete user: %s", err)
return
} }
} }
_ = json.NewEncoder(w).Encode(user) return rsvp.Data("", user)
} }

View File

@ -2,82 +2,67 @@ package routing
import ( import (
"lishwist/db" "lishwist/db"
"lishwist/error" "lishwist/rsvp"
"net/http" "net/http"
) )
func (ctx *Context) WishlistAdd(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func WishlistAdd(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if err := r.ParseForm(); err != nil { form := r.ParseForm()
http.Error(w, err.Error(), http.StatusBadRequest) newGiftName := form.Get("gift_name")
return
}
newGiftName := r.Form.Get("gift_name")
err := currentUser.AddGift(newGiftName) err := currentUser.AddGift(newGiftName)
if err != nil { if err != nil {
error.Page(w, "Failed to add gift.", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "Failed to add gift.").LogError(err)
return
} }
http.Redirect(w, r, "/", http.StatusSeeOther) return rsvp.SeeOther("/")
} }
func (ctx *Context) WishlistDelete(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func WishlistDelete(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if err := r.ParseForm(); err != nil { form := r.ParseForm()
http.Error(w, err.Error(), http.StatusBadRequest) targets := form["gift"]
return
}
targets := r.Form["gift"]
err := currentUser.RemoveGifts(targets...) err := currentUser.RemoveGifts(targets...)
if err != nil { if err != nil {
error.Page(w, "Failed to remove gifts.", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "Failed to remove gifts.").LogError(err)
return
} }
http.Redirect(w, r, "/", http.StatusSeeOther) return rsvp.SeeOther("/")
} }
func (ctx *Context) ForeignWishlistPost(currentUser *db.User, w http.ResponseWriter, r *http.Request) { func ForeignWishlistPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
if err := r.ParseForm(); err != nil { form := r.ParseForm()
error.Page(w, "Failed to parse form...", http.StatusBadRequest, err)
return
}
userReference := r.PathValue("userReference") userReference := r.PathValue("userReference")
switch r.Form.Get("intent") { intent := form.Get("intent")
switch intent {
case "claim": case "claim":
claims := r.Form["unclaimed"] claims := form["unclaimed"]
unclaims := r.Form["claimed"] unclaims := form["claimed"]
err := currentUser.ClaimGifts(claims, unclaims) err := currentUser.ClaimGifts(claims, unclaims)
if err != nil { if err != nil {
error.Page(w, "Failed to update claim...", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "Failed to update claim...").LogError(err)
return
} }
case "complete": case "complete":
claims := r.Form["claimed"] claims := form["claimed"]
err := currentUser.CompleteGifts(claims) err := currentUser.CompleteGifts(claims)
if err != nil { if err != nil {
error.Page(w, "Failed to complete gifts...", http.StatusInternalServerError, nil) return rsvp.Error(http.StatusInternalServerError, "Failed to complete gifts...").LogError(err)
return
} }
case "add": case "add":
giftName := r.Form.Get("gift_name") giftName := form.Get("gift_name")
if giftName == "" { if giftName == "" {
error.Page(w, "Gift name not provided", http.StatusBadRequest, nil) return rsvp.Error(http.StatusBadRequest, "Gift name not provided")
return
} }
err := currentUser.AddGiftToUser(userReference, giftName) err := currentUser.AddGiftToUser(userReference, giftName)
if err != nil { if err != nil {
error.Page(w, "Failed to add gift idea to other user...", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "Failed to add gift idea to other user...").LogError(err)
return
} }
case "delete": case "delete":
claims := r.Form["unclaimed"] claims := form["unclaimed"]
unclaims := r.Form["claimed"] unclaims := form["claimed"]
gifts := append(claims, unclaims...) gifts := append(claims, unclaims...)
err := currentUser.RemoveGifts(gifts...) err := currentUser.RemoveGifts(gifts...)
if err != nil { if err != nil {
error.Page(w, "Failed to remove gift idea for other user...", http.StatusInternalServerError, err) return rsvp.Error(http.StatusInternalServerError, "Failed to remove gift idea for other user...").LogError(err)
return
} }
default: default:
http.Error(w, "Invalid intent", http.StatusBadRequest) return rsvp.Error(http.StatusBadRequest, "Invalid intent %q", intent)
} }
http.Redirect(w, r, "/list/"+userReference, http.StatusSeeOther) return rsvp.SeeOther("/list/" + userReference)
} }

56
server/rsvp/handler.go Normal file
View File

@ -0,0 +1,56 @@
package rsvp
import (
"log"
"net/http"
"github.com/Teajey/sqlstore"
)
type ServeMux struct {
inner *http.ServeMux
store *sqlstore.Store
}
func NewServeMux(store *sqlstore.Store) *ServeMux {
return &ServeMux{
inner: http.NewServeMux(),
store: store,
}
}
func (m *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
m.inner.ServeHTTP(w, r)
}
type Handler interface {
ServeHTTP(h http.Header, r *Request) Response
}
type HandlerFunc func(h http.Header, r *Request) Response
func (m *ServeMux) HandleFunc(pattern string, handler HandlerFunc) {
m.inner.HandleFunc(pattern, func(w http.ResponseWriter, stdReq *http.Request) {
r := wrapStdRequest(m.store, stdReq)
response := handler(w.Header(), &r)
err := response.Write(w, stdReq)
if err != nil {
response.Data = struct{ Message error }{err}
response.HtmlTemplateName = "error_page.gotmpl"
response.Status = http.StatusInternalServerError
} else {
return
}
err = response.Write(w, stdReq)
if err != nil {
log.Printf("Failed to write rsvp.Response to bytes: %s\n", err)
http.Error(w, "Failed to write response", http.StatusInternalServerError)
}
})
}
func (m *ServeMux) Handle(pattern string, handler Handler) {
m.HandleFunc(pattern, handler.ServeHTTP)
}

42
server/rsvp/request.go Normal file
View File

@ -0,0 +1,42 @@
package rsvp
import (
"log"
"net/http"
"net/url"
"github.com/Teajey/sqlstore"
)
type Request struct {
inner *http.Request
store *sqlstore.Store
}
func wrapStdRequest(store *sqlstore.Store, r *http.Request) Request {
return Request{
inner: r,
store: store,
}
}
func (r *Request) GetSession() Session {
session, _ := r.store.Get(r.inner, "lishwist_user")
return Session{session}
}
func (r *Request) ParseForm() url.Values {
err := r.inner.ParseForm()
if err != nil {
log.Printf("Failed to parse form: %s\n", err)
}
return r.inner.Form
}
func (r *Request) PathValue(name string) string {
return r.inner.PathValue(name)
}
func (r *Request) URL() *url.URL {
return r.inner.URL
}

110
server/rsvp/response.go Normal file
View File

@ -0,0 +1,110 @@
package rsvp
import (
"bytes"
"encoding/json"
"fmt"
"lishwist/templates"
"log"
"net/http"
"strings"
)
type Response struct {
HtmlTemplateName string
Data any
SeeOther string
Session *Session
Status int
LogMessage string
}
func (res *Response) Write(w http.ResponseWriter, r *http.Request) error {
if res.Session != nil {
err := res.Session.inner.Save(r, w)
if err != nil {
return fmt.Errorf("Failed to write session: %w", err)
}
}
if res.SeeOther != "" {
http.Redirect(w, r, res.SeeOther, http.StatusSeeOther)
return nil
}
bodyBytes := bytes.NewBuffer([]byte{})
accept := r.Header.Get("Accept")
if res.LogMessage != "" {
log.Printf("%s --- %s\n", res.Data, res.LogMessage)
}
if res.Status != 0 {
w.WriteHeader(res.Status)
}
switch {
case strings.Contains(accept, "text/html"):
if res.HtmlTemplateName == "" {
err := json.NewEncoder(bodyBytes).Encode(res.Data)
if err != nil {
return err
}
} else {
err := templates.Execute(bodyBytes, res.HtmlTemplateName, res.Data)
if err != nil {
return err
}
}
case strings.Contains(accept, "application/json"):
err := json.NewEncoder(bodyBytes).Encode(res.Data)
if err != nil {
return err
}
default:
err := json.NewEncoder(bodyBytes).Encode(res.Data)
if err != nil {
return err
}
}
_, err := w.Write(bodyBytes.Bytes())
if err != nil {
log.Printf("Failed to write rsvp.Response to HTTP: %s\n", err)
}
return nil
}
func Data(htmlTemplateName string, data any) Response {
return Response{
HtmlTemplateName: htmlTemplateName,
Data: data,
}
}
func (r Response) Log(format string, a ...any) Response {
r.LogMessage = fmt.Sprintf(format, a...)
return r
}
func (r Response) LogError(err error) Response {
r.LogMessage = fmt.Sprintf("%s", err)
return r
}
func (r Response) SaveSession(s Session) Response {
r.Session = &s
return r
}
func SeeOther(url string) Response {
return Response{SeeOther: url}
}
func Error(status int, format string, a ...any) Response {
return Response{
Status: status,
HtmlTemplateName: "error_page.gotmpl",
Data: struct{ Message string }{fmt.Sprintf(format, a...)},
}
}

42
server/rsvp/session.go Normal file
View File

@ -0,0 +1,42 @@
package rsvp
import (
"github.com/gorilla/sessions"
)
type Session struct {
inner *sessions.Session
}
func (s *Session) FlashGet(key ...string) any {
list := s.inner.Flashes(key...)
if len(list) < 1 {
return nil
} else {
return list[0]
}
}
func (s *Session) FlashSet(value any, key ...string) {
s.inner.AddFlash(value, key...)
}
func (s *Session) SetID(value string) {
s.inner.ID = value
}
func (s *Session) SetValue(key any, value any) {
s.inner.Values[key] = value
}
func (s *Session) GetValue(key any) any {
return s.inner.Values[key]
}
func (s *Session) ClearValues() {
s.inner.Values = nil
}
func (s *Session) Options() *sessions.Options {
return s.inner.Options
}

View File

@ -2,8 +2,7 @@ package templates
import ( import (
"fmt" "fmt"
"log" "io"
"net/http"
"text/template" "text/template"
) )
@ -33,12 +32,12 @@ func (p *InputProps) Validate() bool {
var tmpls map[string]*template.Template = loadTemplates() var tmpls map[string]*template.Template = loadTemplates()
func Execute(w http.ResponseWriter, name string, data any) { func Execute(w io.Writer, name string, data any) error {
err := tmpls[name].Execute(w, data) err := tmpls[name].Execute(w, data)
if err != nil { if err != nil {
log.Printf("Failed to execute '%s' template: %s\n", name, err) return fmt.Errorf("Failed to execute '%s' template: %w\n", name, err)
return
} }
return nil
} }
func loadTemplates() map[string]*template.Template { func loadTemplates() map[string]*template.Template {