106 lines
3.7 KiB
Go
106 lines
3.7 KiB
Go
package files
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"telesrv/internal/domain"
|
|
)
|
|
|
|
const uploadedMediaIntentVersion = 1
|
|
|
|
type uploadedMediaIntent struct {
|
|
Version int `json:"version"`
|
|
Kind domain.UploadedMediaKind `json:"kind"`
|
|
File domain.UploadedFileRef `json:"file"`
|
|
Spec *domain.DocumentSpec `json:"spec,omitempty"`
|
|
}
|
|
|
|
func uploadedMediaIntentHash(kind domain.UploadedMediaKind, file domain.UploadedFileRef, spec *domain.DocumentSpec) ([]byte, error) {
|
|
payload, err := json.Marshal(uploadedMediaIntent{
|
|
Version: uploadedMediaIntentVersion,
|
|
Kind: kind,
|
|
File: file,
|
|
Spec: spec,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal uploaded media intent: %w", err)
|
|
}
|
|
sum := sha256.Sum256(payload)
|
|
return sum[:], nil
|
|
}
|
|
|
|
func sameUploadedMediaReceipt(receipt domain.UploadedMediaReceipt, kind domain.UploadedMediaKind, intentHash []byte) bool {
|
|
return receipt.Kind == kind && len(intentHash) == sha256.Size && bytes.Equal(receipt.IntentHash, intentHash)
|
|
}
|
|
|
|
func (s *Service) replayUploadedPhoto(ctx context.Context, file domain.UploadedFileRef, intentHash []byte) (domain.Photo, bool, error) {
|
|
receipt, found, err := s.media.GetUploadedMediaReceipt(ctx, file.OwnerUserID, file.FileID)
|
|
if err != nil || !found {
|
|
return domain.Photo{}, false, err
|
|
}
|
|
if !sameUploadedMediaReceipt(receipt, domain.UploadedMediaPhoto, intentHash) {
|
|
return domain.Photo{}, false, domain.ErrFilePartsInvalid
|
|
}
|
|
photo, found, err := s.media.GetPhoto(ctx, receipt.MediaID)
|
|
if err != nil {
|
|
return domain.Photo{}, false, err
|
|
}
|
|
if !found {
|
|
return domain.Photo{}, false, fmt.Errorf("uploaded photo receipt %d/%d references missing photo %d", file.OwnerUserID, file.FileID, receipt.MediaID)
|
|
}
|
|
s.cleanupMaterializedUpload(ctx, file, "photo replay")
|
|
return photo, true, nil
|
|
}
|
|
|
|
func (s *Service) replayUploadedDocument(ctx context.Context, file domain.UploadedFileRef, intentHash []byte) (domain.Document, bool, error) {
|
|
receipt, found, err := s.media.GetUploadedMediaReceipt(ctx, file.OwnerUserID, file.FileID)
|
|
if err != nil || !found {
|
|
return domain.Document{}, false, err
|
|
}
|
|
if !sameUploadedMediaReceipt(receipt, domain.UploadedMediaDocument, intentHash) {
|
|
return domain.Document{}, false, domain.ErrFilePartsInvalid
|
|
}
|
|
doc, found, err := s.media.GetDocument(ctx, receipt.MediaID)
|
|
if err != nil {
|
|
return domain.Document{}, false, err
|
|
}
|
|
if !found {
|
|
return domain.Document{}, false, fmt.Errorf("uploaded document receipt %d/%d references missing document %d", file.OwnerUserID, file.FileID, receipt.MediaID)
|
|
}
|
|
s.cleanupMaterializedUpload(ctx, file, "document replay")
|
|
return doc, true, nil
|
|
}
|
|
|
|
func (s *Service) commitUploadedMediaReceipt(ctx context.Context, file domain.UploadedFileRef, kind domain.UploadedMediaKind, intentHash []byte, mediaID int64) (domain.UploadedMediaReceipt, error) {
|
|
receipt, _, err := s.media.PutUploadedMediaReceipt(ctx, domain.UploadedMediaReceipt{
|
|
OwnerUserID: file.OwnerUserID,
|
|
FileID: file.FileID,
|
|
IntentHash: intentHash,
|
|
Kind: kind,
|
|
MediaID: mediaID,
|
|
})
|
|
if err != nil {
|
|
return domain.UploadedMediaReceipt{}, err
|
|
}
|
|
if !sameUploadedMediaReceipt(receipt, kind, intentHash) {
|
|
return domain.UploadedMediaReceipt{}, domain.ErrFilePartsInvalid
|
|
}
|
|
return receipt, nil
|
|
}
|
|
|
|
func (s *Service) cleanupMaterializedUpload(ctx context.Context, file domain.UploadedFileRef, reason string) {
|
|
if err := s.cleanupUploadParts(ctx, file.OwnerUserID, file.FileID); err != nil {
|
|
s.log.Warn("cleanup materialized upload parts failed",
|
|
zap.String("reason", reason),
|
|
zap.Int64("owner_user_id", file.OwnerUserID),
|
|
zap.Int64("file_id", file.FileID),
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
}
|