diff --git a/server/backend/internal/store/store.go b/server/backend/internal/store/store.go index 2595920..9463471 100644 --- a/server/backend/internal/store/store.go +++ b/server/backend/internal/store/store.go @@ -50,16 +50,19 @@ type ScreenSchedule struct { } type MediaAsset struct { - ID string `json:"id"` - TenantID string `json:"tenant_id"` - Title string `json:"title"` - Type string `json:"type"` // image | video | pdf | web - StoragePath string `json:"storage_path,omitempty"` - OriginalURL string `json:"original_url,omitempty"` - MimeType string `json:"mime_type,omitempty"` - SizeBytes int64 `json:"size_bytes,omitempty"` - Enabled bool `json:"enabled"` - CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + TenantID string `json:"tenant_id"` + Title string `json:"title"` + Type string `json:"type"` // image | video | pdf | web + StoragePath string `json:"storage_path,omitempty"` + OriginalURL string `json:"original_url,omitempty"` + MimeType string `json:"mime_type,omitempty"` + SizeBytes int64 `json:"size_bytes,omitempty"` + Enabled bool `json:"enabled"` + CreatedAt time.Time `json:"created_at"` + CreatedByUserID string `json:"created_by_user_id,omitempty"` + OwnerIsRestricted bool `json:"owner_is_restricted,omitempty"` + OwnerUsername string `json:"owner_username,omitempty"` } type Playlist struct { @@ -367,19 +370,31 @@ func (s *ScreenStore) GetDisplayState(ctx context.Context, screenID string) (str // MediaStore // ------------------------------------------------------------------ -func (s *MediaStore) List(ctx context.Context, tenantID string) ([]*MediaAsset, error) { - rows, err := s.pool.Query(ctx, - `select id, tenant_id, title, type, coalesce(storage_path,''), - coalesce(original_url,''), coalesce(mime_type,''), coalesce(size_bytes,0), - enabled, created_at - from media_assets where tenant_id=$1 order by created_at desc`, tenantID) +func (s *MediaStore) List(ctx context.Context, tenantID, ownerUserID string) ([]*MediaAsset, error) { + const base = ` + SELECT m.id, m.tenant_id, m.title, m.type, + coalesce(m.storage_path,''), coalesce(m.original_url,''), + coalesce(m.mime_type,''), coalesce(m.size_bytes,0), + m.enabled, m.created_at, coalesce(m.created_by_user_id,''), + coalesce(u.role,''), coalesce(u.username,'') + FROM media_assets m + LEFT JOIN users u ON m.created_by_user_id = u.id + WHERE m.tenant_id=$1` + + var rows pgx.Rows + var err error + if ownerUserID != "" { + rows, err = s.pool.Query(ctx, base+` AND m.created_by_user_id=$2 ORDER BY m.created_at DESC`, tenantID, ownerUserID) + } else { + rows, err = s.pool.Query(ctx, base+` ORDER BY m.created_at DESC`, tenantID) + } if err != nil { return nil, err } defer rows.Close() var out []*MediaAsset for rows.Next() { - m, err := scanMedia(rows) + m, err := scanMediaFull(rows) if err != nil { return nil, err } @@ -390,21 +405,21 @@ func (s *MediaStore) List(ctx context.Context, tenantID string) ([]*MediaAsset, func (s *MediaStore) Get(ctx context.Context, id string) (*MediaAsset, error) { row := s.pool.QueryRow(ctx, - `select id, tenant_id, title, type, coalesce(storage_path,''), + `SELECT id, tenant_id, title, type, coalesce(storage_path,''), coalesce(original_url,''), coalesce(mime_type,''), coalesce(size_bytes,0), - enabled, created_at - from media_assets where id=$1`, id) + enabled, created_at, coalesce(created_by_user_id,'') + FROM media_assets WHERE id=$1`, id) return scanMedia(row) } -func (s *MediaStore) Create(ctx context.Context, tenantID, title, assetType, storagePath, originalURL, mimeType string, sizeBytes int64) (*MediaAsset, error) { +func (s *MediaStore) Create(ctx context.Context, tenantID, title, assetType, storagePath, originalURL, mimeType, createdByUserID string, sizeBytes int64) (*MediaAsset, error) { row := s.pool.QueryRow(ctx, - `insert into media_assets(tenant_id, title, type, storage_path, original_url, mime_type, size_bytes) - values($1,$2,$3,nullif($4,''),nullif($5,''),nullif($6,''),nullif($7,0)) - returning id, tenant_id, title, type, coalesce(storage_path,''), + `INSERT INTO media_assets(tenant_id, title, type, storage_path, original_url, mime_type, size_bytes, created_by_user_id) + VALUES($1,$2,$3,nullif($4,''),nullif($5,''),nullif($6,''),nullif($7,0),nullif($8,'')) + RETURNING id, tenant_id, title, type, coalesce(storage_path,''), coalesce(original_url,''), coalesce(mime_type,''), coalesce(size_bytes,0), - enabled, created_at`, - tenantID, title, assetType, storagePath, originalURL, mimeType, sizeBytes) + enabled, created_at, coalesce(created_by_user_id,'')`, + tenantID, title, assetType, storagePath, originalURL, mimeType, sizeBytes, createdByUserID) return scanMedia(row) } @@ -419,13 +434,29 @@ func scanMedia(row interface { var m MediaAsset err := row.Scan(&m.ID, &m.TenantID, &m.Title, &m.Type, &m.StoragePath, &m.OriginalURL, &m.MimeType, &m.SizeBytes, - &m.Enabled, &m.CreatedAt) + &m.Enabled, &m.CreatedAt, &m.CreatedByUserID) if err != nil { return nil, fmt.Errorf("scan media: %w", err) } return &m, nil } +func scanMediaFull(row interface { + Scan(dest ...any) error +}) (*MediaAsset, error) { + var m MediaAsset + var ownerRole string + err := row.Scan(&m.ID, &m.TenantID, &m.Title, &m.Type, + &m.StoragePath, &m.OriginalURL, &m.MimeType, &m.SizeBytes, + &m.Enabled, &m.CreatedAt, &m.CreatedByUserID, + &ownerRole, &m.OwnerUsername) + if err != nil { + return nil, fmt.Errorf("scan media full: %w", err) + } + m.OwnerIsRestricted = ownerRole == "restricted" + return &m, nil +} + // ------------------------------------------------------------------ // PlaylistStore // ------------------------------------------------------------------