add hire/resume pages, contact form, security middleware, and admin improvements

This commit is contained in:
Blake Ridgway
2026-03-08 21:36:47 -05:00
parent c916186d78
commit 261745a5b7
22 changed files with 1237 additions and 72 deletions

View File

@@ -2,6 +2,8 @@ package handler
import (
"encoding/xml"
"fmt"
"log"
"net/http"
"path/filepath"
"strconv"
@@ -10,6 +12,7 @@ import (
"ridgwaysystems.org/website/internal/blog"
"ridgwaysystems.org/website/internal/feed"
"ridgwaysystems.org/website/internal/mailer"
"ridgwaysystems.org/website/internal/status"
)
@@ -107,6 +110,13 @@ func (h *Handler) BlogList(w http.ResponseWriter, r *http.Request) {
})
}
// postPageData is passed to the post template.
type postPageData struct {
*blog.Post
Older *blog.Post
Newer *blog.Post
}
func (h *Handler) BlogPost(w http.ResponseWriter, r *http.Request) {
slug := r.PathValue("slug")
if slug == "" {
@@ -123,7 +133,8 @@ func (h *Handler) BlogPost(w http.ResponseWriter, r *http.Request) {
h.renderErr(w, http.StatusNotFound, "Post not found.")
return
}
h.render(w, "post", post)
older, newer, _ := h.store.Neighbors(slug)
h.render(w, "post", postPageData{Post: post, Older: older, Newer: newer})
}
func (h *Handler) Feed(w http.ResponseWriter, r *http.Request) {
@@ -167,6 +178,62 @@ func (h *Handler) About(w http.ResponseWriter, r *http.Request) {
h.render(w, "about", nil)
}
func (h *Handler) Resume(w http.ResponseWriter, r *http.Request) {
h.render(w, "resume", nil)
}
// --- Hire / Contact ---
type hireData struct {
Name string
Email string
Company string
Message string
Error string
Success bool
}
func (h *Handler) Hire(w http.ResponseWriter, r *http.Request) {
h.render(w, "hire", hireData{})
}
func (h *Handler) HirePost(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
h.renderErr(w, http.StatusBadRequest, "Bad form data.")
return
}
d := hireData{
Name: strings.TrimSpace(r.FormValue("name")),
Email: strings.TrimSpace(r.FormValue("email")),
Company: strings.TrimSpace(r.FormValue("company")),
Message: strings.TrimSpace(r.FormValue("message")),
}
if d.Name == "" || d.Email == "" || d.Message == "" {
d.Error = "Name, email, and message are required."
h.render(w, "hire", d)
return
}
if !strings.Contains(d.Email, "@") {
d.Error = "Please enter a valid email address."
h.render(w, "hire", d)
return
}
subject := "Hire inquiry from " + d.Name
body := fmt.Sprintf("Name: %s\nEmail: %s\nCompany: %s\n\nMessage:\n%s\n",
d.Name, d.Email, d.Company, d.Message)
if err := mailer.Send(h.contactEmail, subject, body); err != nil {
log.Printf("contact form mail error: %v", err)
d.Error = "Could not send message. Please email hire@ridgwaysystems.org directly."
h.render(w, "hire", d)
return
}
d.Success = true
h.render(w, "hire", d)
}
// --- Sitemap ---
type urlset struct {
@@ -188,6 +255,8 @@ func (h *Handler) Sitemap(w http.ResponseWriter, r *http.Request) {
urls := []sitemapURL{
{Loc: h.siteURL + "/", Freq: "weekly", Prio: "1.0"},
{Loc: h.siteURL + "/blog", Freq: "weekly", Prio: "0.9"},
{Loc: h.siteURL + "/hire", Freq: "monthly", Prio: "0.9"},
{Loc: h.siteURL + "/resume", Freq: "monthly", Prio: "0.7"},
{Loc: h.siteURL + "/infrastructure", Freq: "monthly", Prio: "0.7"},
{Loc: h.siteURL + "/status", Freq: "daily", Prio: "0.6"},
{Loc: h.siteURL + "/about", Freq: "monthly", Prio: "0.5"},