feat(photos): full EXIF metadata extraction, backfill and info panel

- backend: switch EXIF parser to dsoprea/go-exif/v3 (all recognised tags, numeric normalization); store in photos.exif jsonb (migration 000003); add storage.Get and cmd/backfill-exif to backfill existing photos from S3 originals
- frontend: info panel renders all EXIF attributes with RU labels, enum translation and units (1/N, f/N, mm, EV); service tags hidden
- viewer: remove make-cover action (cover managed from panel only), icon-only download
- openspec: sync lightroom-workspace-ui artifacts (EXIF, cover-from-panel) and add plan for configurable info attributes/labels stored in user profile
This commit is contained in:
iu
2026-10-10 20:31:34 +03:00
parent d8da885020
commit e1592549b2
20 changed files with 744 additions and 108 deletions
+5 -4
View File
@@ -62,7 +62,7 @@ func (s *Service) Upload(ctx context.Context, in UploadInput) (*models.Photo, er
if !ok {
return nil, httpx.BadRequest("invalid_image", "could not decode image")
}
taken, lat, lon := extractEXIF(in.Data)
meta := extractEXIF(in.Data)
id := auth.NewID()
ext := strings.ToLower(filepath.Ext(in.Filename))
@@ -85,9 +85,10 @@ func (s *Service) Upload(ctx context.Context, in UploadInput) (*models.Photo, er
Width: width,
Height: height,
MimeType: mime,
TakenAt: taken,
Latitude: lat,
Longitude: lon,
TakenAt: meta.TakenAt,
Latitude: meta.Latitude,
Longitude: meta.Longitude,
EXIF: meta.Attributes,
}
if err := s.store.Create(ctx, p); err != nil {
return nil, err