Per-user podcasts, file uploads, RSS images, Docker support
Build and Deploy / build-and-deploy (push) Failing after 13s

- Each user owns a podcast: podcast title/author/image on users table
- Episodes scoped by user_id with context-based auth
- Audio file upload replaces URL linking, served from /uploads/
- Podcast and per-episode images with itunes:image in RSS
- RSS per-user via ?user=N, dashboard shows user-specific feed URL
- Settings form for title + author + image per user
- Docker multi-stage build (golang:1.25-alpine / alpine:3.21)
- Removed PODSTALK_TITLE/AUTHOR env vars
This commit is contained in:
2026-07-08 09:20:19 -04:00
parent f43c98b56f
commit 0861f7510b
8 changed files with 151 additions and 105 deletions
+14 -3
View File
@@ -1,6 +1,7 @@
package handler
import (
"context"
"crypto/rand"
"encoding/hex"
"net/http"
@@ -135,8 +136,16 @@ func (h *AuthHandler) ServeSignout(w http.ResponseWriter, r *http.Request) {
})
http.Redirect(w, r, "/signin", http.StatusSeeOther)
}
type contextKey string
const userIDKey contextKey = "userID"
// UserIDFromContext extracts the authenticated user's ID from the request context.
func UserIDFromContext(r *http.Request) int64 {
id, _ := r.Context().Value(userIDKey).(int64)
return id
}
// AuthMiddleware redirects to /signin (or /signup if no users) when not authenticated.
type AuthMiddleware struct {
Store *store.Store
Sessions *SessionStore
@@ -149,11 +158,13 @@ func (am *AuthMiddleware) RequireAuth(next http.HandlerFunc) http.HandlerFunc {
http.Redirect(w, r, "/signin", http.StatusSeeOther)
return
}
if _, ok := am.Sessions.Get(cookie.Value); !ok {
userID, ok := am.Sessions.Get(cookie.Value)
if !ok {
http.Redirect(w, r, "/signin", http.StatusSeeOther)
return
}
next(w, r)
ctx := context.WithValue(r.Context(), userIDKey, userID)
next(w, r.WithContext(ctx))
}
}