Compare commits

...

2 Commits

Author SHA1 Message Date
Ryan Chen 426df48901 Require login to create events when auth is enabled
Gate the home page and POST /events behind auth. Also add sqlite3 CLI
to the Docker image for DB inspection, and gitignore test.db files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:07:26 -04:00
Ryan Chen 4915af0665 Fix login on legacy DBs: drop NOT NULL phone/email from verification_codes
Old prod DBs had verification_codes.phone (or email) as NOT NULL with no
default, so inserting a code via identifier-only failed silently — users
received an emailed code that was never stored, and verify always said
"Invalid or expired code." Drop the legacy columns in a migration and
stop swallowing the CreateVerificationCode error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:07:18 -04:00
6 changed files with 25 additions and 4 deletions
+3
View File
@@ -2,3 +2,6 @@ bbq
bbq.db bbq.db
bbq.db-shm bbq.db-shm
bbq.db-wal bbq.db-wal
test.db
test.db-shm
test.db-wal
+1 -1
View File
@@ -6,7 +6,7 @@ COPY . .
RUN CGO_ENABLED=1 go build -o /bbq . RUN CGO_ENABLED=1 go build -o /bbq .
FROM debian:bookworm-slim FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates sqlite3 && rm -rf /var/lib/apt/lists/*
COPY --from=build /bbq /usr/local/bin/bbq COPY --from=build /bbq /usr/local/bin/bbq
VOLUME /data VOLUME /data
ENV BBQ_DB=/data/bbq.db ENV BBQ_DB=/data/bbq.db
+6 -2
View File
@@ -101,11 +101,15 @@ func (s *Server) handleLoginSubmit(w http.ResponseWriter, r *http.Request) {
code := generateCode() code := generateCode()
expiresAt := time.Now().UTC().Add(10 * time.Minute) expiresAt := time.Now().UTC().Add(10 * time.Minute)
s.q.CreateVerificationCode(r.Context(), db.CreateVerificationCodeParams{ if err := s.q.CreateVerificationCode(r.Context(), db.CreateVerificationCodeParams{
Identifier: identifier, Identifier: identifier,
Code: code, Code: code,
ExpiresAt: expiresAt, ExpiresAt: expiresAt,
}) }); err != nil {
log.Printf("failed to store verification code for %s: %v", identifier, err)
http.Error(w, "Internal error", 500)
return
}
if err := sendVerificationEmail(identifier, code); err != nil { if err := sendVerificationEmail(identifier, code); err != nil {
log.Printf("failed to send verification email to %s: %v", identifier, err) log.Printf("failed to send verification email to %s: %v", identifier, err)
+4
View File
@@ -49,6 +49,10 @@ func randomSlug() string {
// --- Home / Create Event --- // --- Home / Create Event ---
func (s *Server) handleHome(w http.ResponseWriter, r *http.Request) { func (s *Server) handleHome(w http.ResponseWriter, r *http.Request) {
if s.features.Auth && s.currentUser(r) == nil {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
pageTmpl["home"].ExecuteTemplate(w, "layout", map[string]any{ pageTmpl["home"].ExecuteTemplate(w, "layout", map[string]any{
"User": s.currentUser(r), "User": s.currentUser(r),
"AuthEnabled": s.features.Auth, "AuthEnabled": s.features.Auth,
+4
View File
@@ -174,7 +174,11 @@ func main() {
// Home / create event // Home / create event
r.Get("/", srv.handleHome) r.Get("/", srv.handleHome)
if features.Auth {
r.With(srv.requireAuth).Post("/events", srv.handleCreateEvent)
} else {
r.Post("/events", srv.handleCreateEvent) r.Post("/events", srv.handleCreateEvent)
}
// Guest event view // Guest event view
r.Get("/e/{slug}", srv.handleEvent) r.Get("/e/{slug}", srv.handleEvent)
+6
View File
@@ -22,6 +22,12 @@ func runMigrations(database *sql.DB) {
`DELETE FROM rsvps WHERE id NOT IN (SELECT MIN(id) FROM rsvps GROUP BY event_id, name COLLATE NOCASE)`, `DELETE FROM rsvps WHERE id NOT IN (SELECT MIN(id) FROM rsvps GROUP BY event_id, name COLLATE NOCASE)`,
// Plus-one tracking for RSVPs. // Plus-one tracking for RSVPs.
`ALTER TABLE rsvps ADD COLUMN plus_one INTEGER NOT NULL DEFAULT 0`, `ALTER TABLE rsvps ADD COLUMN plus_one INTEGER NOT NULL DEFAULT 0`,
// Drop legacy phone/email columns from verification_codes. They were
// NOT NULL with no default, so inserts using only identifier failed.
`DROP INDEX IF EXISTS idx_verification_codes_phone`,
`DROP INDEX IF EXISTS idx_verification_codes_email`,
`ALTER TABLE verification_codes DROP COLUMN phone`,
`ALTER TABLE verification_codes DROP COLUMN email`,
// SMS consent logging for Twilio compliance. // SMS consent logging for Twilio compliance.
`CREATE TABLE IF NOT EXISTS sms_consents ( `CREATE TABLE IF NOT EXISTS sms_consents (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,