Promo: order page + red banner + contact form; fix admin login rehash

- /order page: single tag $5.50 / 10-pack $22.00 (GST-incl), qty via
  Stripe Payment Links (single 1-25, pack 1-2), thanks state
- Red 'Protect & recover your valuables — ORDER NOW' banner in base layout
  (every page incl. found-dog tag page) + footer promo links
- Contact page: form -> Gmail SMTP (env SMTP_*), hello@where-woof.com
- Fix admin login 500: User.getAuthPasswordName() = password_hash so
  Laravel 13 rehash-on-login writes the correct column (was trying
  UPDATE users SET password = ...)
This commit is contained in:
2026-09-01 12:53:53 +10:00
parent aabaa750f4
commit 2a101e9a62
9 changed files with 223 additions and 3 deletions

View File

@@ -58,6 +58,7 @@ func LoadTemplates() (Templates, error) {
"about": {dir + "/about.html"},
"what": {dir + "/what.html"},
"contact": {dir + "/contact.html"},
"order": {dir + "/order.html"},
"register": {dir + "/register.html"},
"login": {dir + "/login.html"},
"account": {dir + "/account.html", dir + "/account-panel.html", dir + "/tag-list.html", dir + "/tag-edit-inline.html"},

View File

@@ -14,5 +14,11 @@ func (a *App) WhatIsThis(w http.ResponseWriter, r *http.Request) {
// Contact renders the contact page.
func (a *App) Contact(w http.ResponseWriter, r *http.Request) {
a.render(w, r, "contact", "Contact", nil, "")
a.render(w, r, "contact", "Contact", contactData{}, "")
}
// contactData passed to the contact page (form submission state).
type contactData struct {
Sent bool
Error string
}

View File

@@ -0,0 +1,92 @@
package handlers
import (
"fmt"
"net/http"
"net/smtp"
"os"
"strings"
)
// orderData passes the Stripe Payment Links (env-configured) to the order page.
// Links are bought via Stripe-hosted checkout; the buyer chooses quantity there
// (single 1-25, 10-pack 1-2). Prices: $5.00 + GST, $20.00 + GST (fee handled
// by Stripe Managed Payments).
type orderData struct {
SingleLink string
PackLink string
Thanks bool
}
// Order renders the public ordering page.
func (a *App) Order(w http.ResponseWriter, r *http.Request) {
a.render(w, r, "order", "Order tags",
orderData{
SingleLink: os.Getenv("ORDER_SINGLE_LINK"),
PackLink: os.Getenv("ORDER_PACK_LINK"),
Thanks: r.URL.Query().Get("thanks") == "1",
}, "")
}
// ContactSubmit receives the contact form and emails the team via SMTP
// (env: SMTP_HOST/SMTP_PORT/SMTP_USER/SMTP_PASS, CONTACT_TO).
// Returns the contact page with a success/error notice.
func (a *App) ContactSubmit(w http.ResponseWriter, r *http.Request) {
name := strings.TrimSpace(r.FormValue("name"))
email := strings.TrimSpace(r.FormValue("email"))
subject := strings.TrimSpace(r.FormValue("subject"))
message := strings.TrimSpace(r.FormValue("message"))
errMsg := ""
if name == "" || email == "" || message == "" {
errMsg = "Please fill in your name, email and message."
} else if !strings.Contains(email, "@") {
errMsg = "Please enter a valid email address."
}
if errMsg == "" {
if err := sendContactEmail(name, email, subject, message); err != nil {
fmt.Println("contact email failed:", err)
errMsg = "Sorry — we couldn't send your message right now. Please email us directly at hello@where-woof.com."
} else {
// Success: show a confirmation instead of re-rendering the form.
a.render(w, r, "contact", "Get in touch", contactData{Sent: true}, "")
return
}
}
a.render(w, r, "contact", "Get in touch", contactData{Error: errMsg}, errMsg)
}
// sendContactEmail sends the form contents to CONTACT_TO via Gmail SMTP
// (ENV: SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, CONTACT_TO).
func sendContactEmail(name, email, subject, message string) error {
host := os.Getenv("SMTP_HOST")
port := os.Getenv("SMTP_PORT")
user := os.Getenv("SMTP_USER")
pass := os.Getenv("SMTP_PASS")
to := os.Getenv("CONTACT_TO")
if host == "" || user == "" || pass == "" || to == "" {
return fmt.Errorf("SMTP not configured")
}
if port == "" {
port = "587"
}
subj := subject
if subj == "" {
subj = "Where Woof contact form"
}
body := fmt.Sprintf("From: %s <%s>\nSubject: %s\n\n%s", name, email, subj, message)
msg := []byte("To: " + to + "\r\n" +
"From: " + user + "\r\n" +
"Subject: " + subj + "\r\n" +
"MIME-Version: 1.0\r\n" +
"Content-Type: text/plain; charset=utf-8\r\n" +
"\r\n" + body + "\r\n")
addr := host + ":" + port
auth := smtp.PlainAuth("", user, pass, host)
return smtp.SendMail(addr, auth, user, []string{to}, msg)
}