feat(api): add endpoint to list guest photos

* implement handleListPhotos to retrieve uploaded photos
* create PhotoFileRecord struct for photo data
* add fetchGuestPhotos function in the UI
* display guest photos in the photo upload page
* remove index.html as it is no longer needed
* add calendar functionality to download wedding event
This commit is contained in:
2026-08-23 10:33:50 +02:00
parent 4eddecea8e
commit f16d280f9a
9 changed files with 198 additions and 46 deletions
+22 -5
View File
@@ -36,6 +36,7 @@ func (s *Server) Routes(mux *http.ServeMux) {
mux.HandleFunc("GET /api/config", s.handleConfig)
mux.HandleFunc("POST /api/rsvp", s.handleRSVP)
mux.HandleFunc("GET /api/guests", s.handleGuestList)
mux.HandleFunc("GET /api/photos", s.handleListPhotos)
mux.HandleFunc("POST /api/photos/upload", s.handlePhotoUpload)
}
@@ -146,6 +147,27 @@ func (s *Server) handleGuestList(w http.ResponseWriter, r *http.Request) {
})
}
type photoResponse struct {
URL string `json:"url"`
UploaderName string `json:"uploaderName"`
}
func (s *Server) handleListPhotos(w http.ResponseWriter, r *http.Request) {
files, err := s.store.ListPhotoFiles()
if err != nil {
log.Printf("photos: list failed: %v", err)
writeError(w, http.StatusInternalServerError, "could not load photos")
return
}
out := make([]photoResponse, 0, len(files))
for _, f := range files {
out = append(out, photoResponse{URL: f.URL, UploaderName: f.UploaderName})
}
writeJSON(w, http.StatusOK, map[string]any{"photos": out})
}
var allowedImageTypes = map[string]bool{
"image/jpeg": true,
"image/png": true,
@@ -155,11 +177,6 @@ var allowedImageTypes = map[string]bool{
}
func (s *Server) handlePhotoUpload(w http.ResponseWriter, r *http.Request) {
if time.Now().Before(s.cfg.Wedding.Date) {
writeError(w, http.StatusForbidden, "photo uploads open on the wedding day")
return
}
maxTotalBytes := int64(s.cfg.Upload.MaxSizeMB) * int64(s.cfg.Upload.MaxFiles) * 1024 * 1024
r.Body = http.MaxBytesReader(w, r.Body, maxTotalBytes+1<<20)
+30
View File
@@ -159,6 +159,36 @@ func (s *Store) CreatePhotoUpload(u PhotoUpload) (int64, error) {
return res.LastInsertId()
}
type PhotoFileRecord struct {
URL string
UploaderName string
}
// ListPhotoFiles returns every uploaded photo, most recent first.
func (s *Store) ListPhotoFiles() ([]PhotoFileRecord, error) {
rows, err := s.db.Query(
`SELECT f.url, u.name
FROM photo_files f
JOIN photo_uploads u ON u.id = f.upload_id
ORDER BY f.created_at DESC, f.id DESC
LIMIT 200`,
)
if err != nil {
return nil, err
}
defer rows.Close()
var out []PhotoFileRecord
for rows.Next() {
var rec PhotoFileRecord
if err := rows.Scan(&rec.URL, &rec.UploaderName); err != nil {
return nil, err
}
out = append(out, rec)
}
return out, rows.Err()
}
func (s *Store) AddPhotoFiles(uploadID int64, files []PhotoFile) error {
tx, err := s.db.Begin()
if err != nil {
-1
View File
@@ -1 +0,0 @@
<!-- placeholder, replaced by the ui build output before building the Go binary -->