diff --git a/internal/storage/face_gallery_repo.go b/internal/storage/face_gallery_repo.go index ef525cc..fb84d4b 100644 --- a/internal/storage/face_gallery_repo.go +++ b/internal/storage/face_gallery_repo.go @@ -7,10 +7,11 @@ import ( ) type PersonRecord struct { - ID int `json:"id"` - Name string `json:"name"` - PhotoCount int `json:"photo_count"` - CreatedAt string `json:"created_at"` + ID int `json:"id"` + Name string `json:"name"` + PhotoCount int `json:"photo_count"` + Photos []string `json:"photos,omitempty"` + CreatedAt string `json:"created_at"` } type FaceGalleryRepo struct { @@ -54,9 +55,7 @@ func (r *FaceGalleryRepo) ListPersons() ([]PersonRecord, error) { } defer db.Close() - rows, err := db.Query(`SELECT p.id, p.name, p.created_at, COUNT(ph.id) AS photo_count -FROM face_persons p LEFT JOIN face_photos ph ON ph.person_id = p.id -GROUP BY p.id ORDER BY p.name`) + rows, err := db.Query(`SELECT id, name, created_at FROM face_persons ORDER BY name`) if err != nil { return nil, err } @@ -65,11 +64,26 @@ GROUP BY p.id ORDER BY p.name`) var out []PersonRecord for rows.Next() { var p PersonRecord - if err := rows.Scan(&p.ID, &p.Name, &p.CreatedAt, &p.PhotoCount); err != nil { + if err := rows.Scan(&p.ID, &p.Name, &p.CreatedAt); err != nil { continue } out = append(out, p) } + // Load photos for each person + for i := range out { + photoRows, err := db.Query(`SELECT photo_path FROM face_photos WHERE person_id = ? ORDER BY id`, out[i].ID) + if err != nil { + continue + } + for photoRows.Next() { + var path string + if photoRows.Scan(&path) == nil { + out[i].Photos = append(out[i].Photos, path) + } + } + photoRows.Close() + out[i].PhotoCount = len(out[i].Photos) + } if out == nil { out = make([]PersonRecord, 0) } diff --git a/internal/web/ui.go b/internal/web/ui.go index d991e15..a22250a 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -665,6 +665,7 @@ func (u *UI) Routes() (chi.Router, error) { r.Get("/diagnostics", u.pageDiagnostics) r.Get("/alarms", u.pageAlarms) r.Get("/face-gallery", u.pageFaceGallery) + r.Get("/face-photo/*", u.serveFacePhoto) r.Post("/face-gallery/import", u.actionFaceGalleryImport) r.Post("/face-gallery/build", u.actionFaceGalleryBuild) r.Post("/face-gallery/add", u.actionFaceGalleryAdd) @@ -4122,3 +4123,13 @@ func personNameFromPath(path string) string { } return strings.TrimSpace(parts[len(parts)-2]) } + +func (u *UI) serveFacePhoto(w http.ResponseWriter, r *http.Request) { + path := chi.URLParam(r, "*") + if path == "" || strings.Contains(path, "..") { + http.Error(w, "invalid", http.StatusBadRequest) + return + } + fullPath := filepath.Join("dataset", path) + http.ServeFile(w, r, fullPath) +} diff --git a/internal/web/ui/templates/face_gallery.html b/internal/web/ui/templates/face_gallery.html index 6828a1b..29042ee 100644 --- a/internal/web/ui/templates/face_gallery.html +++ b/internal/web/ui/templates/face_gallery.html @@ -3,19 +3,19 @@
{{.Message}}