add rate limiting, CSRF, newsletter, auto-checker, /uses and /projects pages

This commit is contained in:
Blake Ridgway
2026-03-11 14:12:52 -05:00
parent 261745a5b7
commit 58831e2429
17 changed files with 913 additions and 19 deletions

View File

@@ -12,6 +12,7 @@ import (
"time"
"ridgwaysystems.org/website/internal/blog"
"ridgwaysystems.org/website/internal/newsletter"
"ridgwaysystems.org/website/internal/status"
)
@@ -63,6 +64,13 @@ func (h *Handler) AdminRouter(w http.ResponseWriter, r *http.Request) {
case path == "/admin/uploads":
h.requireAuth(h.adminUploads)(w, r)
case path == "/admin/newsletter":
if r.Method == http.MethodPost {
h.requireAuth(h.adminNewsletterDelete)(w, r)
} else {
h.requireAuth(h.adminNewsletter)(w, r)
}
default:
h.renderErr(w, http.StatusNotFound, "Admin page not found.")
}
@@ -374,6 +382,40 @@ func (h *Handler) adminUploads(w http.ResponseWriter, r *http.Request) {
h.render(w, "admin-uploads", uploadsData{Files: files, Flash: r.URL.Query().Get("flash")})
}
// --- Newsletter admin ---
type adminNewsletterData struct {
Subscribers []newsletter.Subscriber
Flash string
Count int
}
func (h *Handler) adminNewsletter(w http.ResponseWriter, r *http.Request) {
subs, err := h.news.All()
if err != nil {
h.renderErr(w, http.StatusInternalServerError, "Could not load subscribers.")
return
}
h.render(w, "admin-newsletter", adminNewsletterData{
Subscribers: subs,
Count: len(subs),
Flash: r.URL.Query().Get("flash"),
})
}
func (h *Handler) adminNewsletterDelete(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
h.renderErr(w, http.StatusBadRequest, "Bad form data.")
return
}
email := r.FormValue("email")
if err := h.news.Remove(email); err != nil {
h.renderErr(w, http.StatusInternalServerError, "Remove failed: "+err.Error())
return
}
http.Redirect(w, r, "/admin/newsletter?flash=Removed", http.StatusSeeOther)
}
// sanitizeSlug ensures a slug is filesystem-safe.
func sanitizeSlug(s string) string {
s = strings.ToLower(strings.TrimSpace(s))