feat(backend): log file views
deploy / deploy (push) Successful in 1m0s

The activity.file_views table existed but nothing ever wrote to it. Add a
POST /files/{id}/views endpoint: FileRepo.RecordView inserts a history row,
FileService.RecordView enforces view ACL first. The file viewer fires it
(fire-and-forget) when a file is opened, including while paging prev/next.

Documented in openapi.yaml; covered by TestRecordFileView (204 on view,
repeatable, 404 for unknown file).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 14:49:14 +03:00
parent 8f213e780c
commit a78fc5ba9a
8 changed files with 93 additions and 0 deletions
+20
View File
@@ -208,6 +208,26 @@ func (s *FileService) Get(ctx context.Context, id uuid.UUID) (*domain.File, erro
return f, nil
}
// RecordView appends a view-history entry for the current user, enforcing view
// ACL (you can only record a view of a file you may see).
func (s *FileService) RecordView(ctx context.Context, id uuid.UUID) error {
userID, isAdmin, _ := domain.UserFromContext(ctx)
f, err := s.files.GetByID(ctx, id)
if err != nil {
return err
}
ok, err := s.acl.CanView(ctx, userID, isAdmin, f.CreatorID, f.IsPublic, fileObjectTypeID, id)
if err != nil {
return err
}
if !ok {
return domain.ErrForbidden
}
return s.files.RecordView(ctx, id, userID)
}
// Update applies metadata changes to a file, enforcing edit ACL.
func (s *FileService) Update(ctx context.Context, id uuid.UUID, p UpdateParams) (*domain.File, error) {
userID, isAdmin, _ := domain.UserFromContext(ctx)