RSVP 0.13.0 and fix templates

This commit is contained in:
Teajey 2025-08-22 20:29:47 +09:00
parent dfa2525714
commit c763ff40d4
Signed by: Teajey
GPG Key ID: 970E790FE834A713
20 changed files with 574 additions and 455 deletions

View File

@ -5,7 +5,7 @@ go 1.23.3
toolchain go1.24.5
require (
github.com/Teajey/rsvp v0.11.0
github.com/Teajey/rsvp v0.13.0
github.com/Teajey/sqlstore v0.0.6
github.com/gorilla/sessions v1.4.0
golang.org/x/crypto v0.22.0

View File

@ -1,5 +1,5 @@
github.com/Teajey/rsvp v0.11.0 h1:ePx7Oj0nDYRCmFB6fQvSQbYUcWhgqTh9fh8PaHlOnwk=
github.com/Teajey/rsvp v0.11.0/go.mod h1:WCWos0l+K/9heUuvbIUXkKAHAtxoLpkJ43C/fszD4RY=
github.com/Teajey/rsvp v0.13.0 h1:EeuHMHtU2/eLuSbCIlzKqy5FI9f6Qq+yUJnrVPBJvKk=
github.com/Teajey/rsvp v0.13.0/go.mod h1:WCWos0l+K/9heUuvbIUXkKAHAtxoLpkJ43C/fszD4RY=
github.com/Teajey/sqlstore v0.0.6 h1:kUEpA+3BKFHZl128MuMeYY6zVcmq1QmOlNyofcFEJOA=
github.com/Teajey/sqlstore v0.0.6/go.mod h1:hjk0S593/2Q4QxkEXCgpThj9w5KWGTQi9JtgfziHXXk=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=

View File

@ -1,7 +1,6 @@
package response
import (
"fmt"
"lishwist/http/templates"
"log"
"net/http"
@ -17,7 +16,7 @@ type ServeMux struct {
func NewServeMux(store *sqlstore.Store) *ServeMux {
mux := rsvp.NewServeMux()
mux.Config.HtmlTemplate = templates.Templates["login.gotmpl"]
mux.Config.HtmlTemplate = templates.Template
return &ServeMux{
inner: mux,
store: store,
@ -35,7 +34,7 @@ type Handler interface {
type HandlerFunc func(*Session, http.Header, *http.Request) rsvp.Response
func (m *ServeMux) HandleFunc(pattern string, handler HandlerFunc) {
m.inner.Std.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
m.inner.MiddleHandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) rsvp.Response {
session := m.GetSession(r)
response := handler(session, w.Header(), r)
@ -47,10 +46,7 @@ func (m *ServeMux) HandleFunc(pattern string, handler HandlerFunc) {
}
}
err := response.Write(w, r, m.inner.Config)
if err != nil {
panic(fmt.Sprintf("Failed to write rsvp.Response: %s", err))
}
return response
})
}

View File

@ -11,10 +11,10 @@ type Session struct {
func (s *Session) FlashGet() any {
list := s.inner.Flashes()
s.written = true
if len(list) < 1 {
return nil
} else {
s.written = true
return list[0]
}
}

View File

@ -19,7 +19,7 @@ type foreignWishlistProps struct {
func ForeignWishlist(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Response {
userReference := r.PathValue("userReference")
if app.User.Reference == userReference {
return rsvp.SeeOther("/")
return rsvp.Found("/", "You're not allowed to view your own wishlist!")
}
otherUser, err := lishwist.GetUserByReference(userReference)
if err != nil {

View File

@ -44,12 +44,14 @@ func LoginPost(session *response.Session, h http.Header, r *http.Request) rsvp.R
props := api.NewLoginProps(username, password)
resp := rsvp.SeeOther(r.URL.Path, props)
valid := props.Validate()
props.Password.Value = ""
if !valid {
session.FlashSet(&props)
log.Printf("Invalid props: %#v\n", props)
return rsvp.SeeOther("/")
return resp
}
app, err := lishwist.Login(username, password)
@ -59,12 +61,12 @@ func LoginPost(session *response.Session, h http.Header, r *http.Request) rsvp.R
props.GeneralError = "Username or password invalid"
session.FlashSet(&props)
log.Printf("Invalid credentials: %s: %#v\n", err, props)
return rsvp.SeeOther("/")
return resp
default:
props.GeneralError = "Something went wrong."
session.FlashSet(&props)
log.Printf("Login error: %s\n", err)
return rsvp.SeeOther("/")
return resp
}
}
@ -72,5 +74,5 @@ func LoginPost(session *response.Session, h http.Header, r *http.Request) rsvp.R
session.SetValue("authorized", true)
session.SetValue("username", app.User.Name)
return rsvp.SeeOther(r.URL.Path)
return rsvp.SeeOther(r.URL.Path, "Login successful!")
}

View File

@ -11,5 +11,5 @@ func LogoutPost(session *response.Session, h http.Header, r *http.Request) rsvp.
session.Options().MaxAge = 0
session.ClearValues()
return rsvp.SeeOther("/")
return rsvp.SeeOther("/", "Logout successful")
}

View File

@ -45,8 +45,8 @@ func RegisterPost(s *response.Session, h http.Header, r *http.Request) rsvp.Resp
props.ConfirmPassword.Value = ""
if !valid {
s.FlashSet(&props)
log.Printf("Invalid props: %#v\n", props)
return rsvp.SeeOther("/")
log.Printf("Invalid register props: %#v\n", props)
return rsvp.SeeOther(r.URL.Path, props)
}
_, err = lishwist.Register(username, newPassword)
@ -58,9 +58,9 @@ func RegisterPost(s *response.Session, h http.Header, r *http.Request) rsvp.Resp
}
s.FlashSet(&props)
log.Printf("Registration failed: %s\n", err)
return rsvp.SeeOther("/register")
return rsvp.SeeOther(r.URL.Path, props)
}
s.FlashSet(true)
return rsvp.SeeOther("/")
return rsvp.SeeOther("/", "Registration successful!")
}

View File

@ -34,5 +34,5 @@ func TodoUpdate(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Resp
default:
return response.Error(http.StatusBadRequest, "Invalid intent")
}
return rsvp.SeeOther("/")
return rsvp.SeeOther("/", "Update successful")
}

View File

@ -22,7 +22,7 @@ func WishlistAdd(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Res
log.Printf("%s\n", err)
return response.Error(http.StatusInternalServerError, "Failed to add gift.")
}
return rsvp.SeeOther("/")
return rsvp.SeeOther("/", "Wish added!")
}
func WishlistDelete(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Response {
@ -37,7 +37,7 @@ func WishlistDelete(app *lishwist.Session, h http.Header, r *http.Request) rsvp.
log.Printf("%s\n", err)
return response.Error(http.StatusInternalServerError, "Failed to remove gifts.")
}
return rsvp.SeeOther("/")
return rsvp.SeeOther("/", "Wish deleted")
}
func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Response {
@ -47,6 +47,7 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request)
}
userReference := r.PathValue("userReference")
resp := rsvp.SeeOther("/list/"+userReference, "Update successful")
intent := r.Form.Get("intent")
switch intent {
case "claim":
@ -57,6 +58,7 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request)
log.Printf("%s\n", err)
return response.Error(http.StatusInternalServerError, "Failed to update claim...")
}
resp.Body = "Successfully claimed wishes"
case "complete":
claims := r.Form["claimed"]
err := app.CompleteWishes(claims)
@ -64,6 +66,7 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request)
log.Printf("%s\n", err)
return response.Error(http.StatusInternalServerError, "Failed to complete gifts...")
}
resp.Body = "Successfully completed wishes"
case "add":
wishName := r.Form.Get("gift_name")
if wishName == "" {
@ -74,6 +77,7 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request)
log.Printf("%s\n", err)
return response.Error(http.StatusInternalServerError, "Failed to add gift idea to other user...")
}
resp.Body = "Successfully added wishes"
case "delete":
claims := r.Form["unclaimed"]
unclaims := r.Form["claimed"]
@ -83,8 +87,9 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request)
log.Printf("%s\n", err)
return response.Error(http.StatusInternalServerError, "Failed to remove gift idea for other user...")
}
resp.Body = "Successfully removed wishes"
default:
return response.Error(http.StatusBadRequest, "Invalid intent %q", intent)
}
return rsvp.SeeOther("/list/" + userReference)
return resp
}

View File

@ -12,10 +12,7 @@
{{end}}
{{end}}
<!DOCTYPE html>
<html>
<head>
{{define "head"}}
<title>Lishwist</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -42,6 +39,11 @@
submitter.disabled = !accepted;
}
</script>
{{end}}
{{define "boilerplate"}}
<head>
{{template "head" .}}
</head>
<body>
@ -61,5 +63,8 @@
{{template "body" .}}
</div>
</body>
{{end}}
</html>
{{define "login_prompt"}}
<a href="/">Login</a> or <a href="/register">register</a>
{{end}}

View File

@ -1,4 +1,19 @@
{{define "navbar"}}
<!doctype html>
<html>
<head>
{{template "head" .}}
</head>
<body>
<div style="height: 100svh;" class="d-flex flex-column">
<div class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<div class="navbar-brand">Lishwist</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<nav>
<ul class="navbar-nav">
<li class="nav-item">
@ -6,12 +21,14 @@
</li>
</ul>
</nav>
{{end}}
{{define "body"}}
</div>
</div>
</div>
<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}}
</div>
</body>
</html>

View File

@ -1,4 +1,19 @@
{{define "navbar"}}
<!doctype html>
<html>
<head>
{{template "head" .}}
</head>
<body>
<div style="height: 100svh;" class="d-flex flex-column">
<div class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<div class="navbar-brand">Lishwist</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<nav>
<ul class="navbar-nav">
<li class="nav-item">
@ -23,9 +38,9 @@
</div>
</li>
</ul>
{{end}}
{{define "body"}}
</div>
</div>
</div>
<div class="overflow-y-scroll flex-grow-1">
<div class="container py-5">
<section class="card">
@ -95,4 +110,6 @@
</section>
</div>
</div>
{{end}}
</div>
</body>
</html>

View File

@ -1,4 +1,19 @@
{{define "navbar"}}
<!doctype html>
<html>
<head>
{{template "head" .}}
</head>
<body>
<div style="height: 100svh;" class="d-flex flex-column">
<div class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<div class="navbar-brand">Lishwist</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<nav>
<ul class="navbar-nav">
<li class="nav-item">
@ -23,9 +38,9 @@
</div>
</li>
</ul>
{{end}}
{{define "body"}}
</div>
</div>
</div>
<div class="overflow-y-scroll flex-grow-1">
<div class="container py-5">
<section class="card">
@ -46,5 +61,6 @@
</section>
</div>
</div>
{{end}}
</div>
</body>
</html>

View File

@ -1,4 +1,19 @@
{{define "navbar"}}
<!doctype html>
<html>
<head>
{{template "head" .}}
</head>
<body>
<div style="height: 100svh;" class="d-flex flex-column">
<div class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<div class="navbar-brand">Lishwist</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<div class="flex-grow-1"></div>
<ul class="navbar-nav">
<li class="nav-item"><button class="btn btn-success"
@ -19,9 +34,9 @@
</div>
</li>
</ul>
{{end}}
{{define "body"}}
</div>
</div>
</div>
<div class="overflow-y-scroll flex-grow-1">
<div class="container py-5">
<section class="card mb-4">
@ -110,4 +125,6 @@
</section>
</div>
</div>
{{end}}
</div>
</body>
</html>

View File

@ -1,7 +1,22 @@
{{define "navbar"}}
{{end}}
<!doctype html>
<html>
<head>
{{template "head" .}}
</head>
{{define "body"}}
<body>
<div style="height: 100svh;" class="d-flex flex-column">
<div class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<div class="navbar-brand">Lishwist</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
</div>
</div>
</div>
<div class="container d-flex flex-grow-1 justify-content-center align-items-center flex-column">
{{if .SuccessfulRegistration}}
<div class="alert alert-success" role="alert">
@ -30,4 +45,6 @@
</div>
</form>
</div>
{{end}}
</div>
</body>
</html>

View File

@ -1,4 +1,19 @@
{{define "navbar"}}
<!doctype html>
<html>
<head>
{{template "head" .}}
</head>
<body>
<div style="height: 100svh;" class="d-flex flex-column">
<div class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<div class="navbar-brand">Lishwist</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<nav>
<ul class="navbar-nav">
<li class="nav-item">
@ -6,13 +21,9 @@
</li>
</ul>
</nav>
{{end}}
{{define "login_prompt"}}
<a href="/">Login</a> or <a href="/register">register</a>
{{end}}
{{define "body"}}
</div>
</div>
</div>
<div class="overflow-y-scroll flex-grow-1">
<div class="container py-5">
<section class="card">
@ -34,4 +45,6 @@
</section>
</div>
</div>
{{end}}
</div>
</body>
</html>

View File

@ -1,4 +1,19 @@
{{define "navbar"}}
<!doctype html>
<html>
<head>
{{template "head" .}}
</head>
<body>
<div style="height: 100svh;" class="d-flex flex-column">
<div class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<div class="navbar-brand">Lishwist</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<nav>
<ul class="navbar-nav">
<li class="nav-item">
@ -6,13 +21,9 @@
</li>
</ul>
</nav>
{{end}}
{{define "login_prompt"}}
<a href="/">Login</a> or <a href="/register">register</a>
{{end}}
{{define "body"}}
</div>
</div>
</div>
<div class="overflow-y-scroll flex-grow-1">
<div class="container py-5">
<section class="card">
@ -34,4 +45,6 @@
</section>
</div>
</div>
{{end}}
</div>
</body>
</html>

View File

@ -1,4 +1,19 @@
{{define "navbar"}}
<!doctype html>
<html>
<head>
{{template "head" .}}
</head>
<body>
<div style="height: 100svh;" class="d-flex flex-column">
<div class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<div class="navbar-brand">Lishwist</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<nav>
<ul class="navbar-nav">
<li class="nav-item">
@ -6,9 +21,9 @@
</li>
</ul>
</nav>
{{end}}
{{define "body"}}
</div>
</div>
</div>
<div class="container d-flex flex-grow-1 justify-content-center align-items-center flex-column">
<div class="alert alert-warning" role="alert">
<p>Your password will be stored in a safe, responsible manner; but don't trust my programming skills!</p>
@ -37,4 +52,6 @@
</div>
</form>
</div>
{{end}}
</div>
</body>
</html>

View File

@ -29,29 +29,13 @@ func (p *InputProps) Validate() bool {
return true
}
var Templates map[string]*template.Template
var Template *template.Template
func init() {
Templates = load()
Template = load()
}
func load() map[string]*template.Template {
homeTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/home.gotmpl"))
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"))
publicWishlistTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/public_foreign_wishlist.gotmpl"))
errorTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/error_page.gotmpl"))
groupTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/group_page.gotmpl"))
publicGroupTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/public_group_page.gotmpl"))
return map[string]*template.Template{
"home.gotmpl": homeTmpl,
"login.gotmpl": loginTmpl,
"register.gotmpl": registerTmpl,
"foreign_wishlist.gotmpl": foreignTmpl,
"public_foreign_wishlist.gotmpl": publicWishlistTmpl,
"error_page.gotmpl": errorTmpl,
"group_page.gotmpl": groupTmpl,
"public_group_page.gotmpl": publicGroupTmpl,
}
func load() *template.Template {
t := template.Must(template.ParseGlob("templates/*.gotmpl"))
return t
}