|
|
package services
import ( "context" "errors" "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/repositories" "github.com/h2non/filetype" "io" )
var ErrPrivateNoAuthor = errors.New("cannot search for private files without an author") var ErrInvalidName = errors.New("invalid name") var ErrInvalidFileSize = errors.New("file is not of a correct size (min: 320B, max: 16MB)") var ErrCouldNotReadHead = errors.New("could not read file head") var ErrInvalidFileType = errors.New("file type could not be recognized or is not allowed") var ErrCouldNotUploadFile = errors.New("could not upload file")
// FileService is a service for files.
type FileService struct { files repositories.FileRepository }
func (s *FileService) Find(ctx context.Context, id string) (*models.File, error) { file, err := s.files.Find(ctx, id) if err != nil { return nil, err }
if !file.Public { err := auth.CheckPermission(ctx, "view", file) if err != nil { return nil, repositories.ErrNotFound } }
return file, nil }
func (s *FileService) List(ctx context.Context, filter models.FileFilter) ([]*models.File, error) { token := auth.TokenFromContext(ctx)
if filter.Public != nil { if *filter.Public == false { if filter.Author == nil || *filter.Author == "" { return nil, ErrPrivateNoAuthor }
if !token.PermittedUser(*filter.Author, "member", "file.list") { return nil, auth.ErrUnauthorized } } }
return s.files.List(ctx, filter) }
func (s *FileService) Upload(ctx context.Context, reader io.Reader, name string, size int64) (*models.File, error) { if name == "" { return nil, ErrInvalidName } else if size < 320 || size > 16777216 { return nil, ErrInvalidFileSize }
head := make([]byte, 320) n, err := reader.Read(head) if err != nil || n < 320 { return nil, ErrCouldNotReadHead }
fileType, err := filetype.Match(head) if err != nil || !allowedMimeTypes[fileType.MIME.Value] { return nil, ErrInvalidFileType }
panic("implement rest of me") }
func (s *FileService) Edit(ctx context.Context, id string, name *string, public *bool) (*models.File, error) { file, err := s.files.Find(ctx, id) if err != nil { return nil, err }
err = auth.CheckPermission(ctx, "edit", file) if err != nil { if !file.Public { return nil, repositories.ErrNotFound }
return nil, err }
return s.files.Update(ctx, *file, models.FileUpdate{ Name: name, Public: public, }) }
// concatReader is a quick and dirty reader for reading the head and the file.
type concatReader struct { head []byte headPos int body io.Reader }
func (r *concatReader) Read(p []byte) (n int, err error) { if len(p) == 0 { return 0, io.ErrShortBuffer }
if r.headPos < len(r.head) { remainder := r.head[r.headPos:] if len(p) < len(remainder) { r.headPos += len(p) copy(p, remainder)
return len(p), nil }
r.headPos = len(r.head) copy(p, remainder)
return len(p), nil }
return r.body.Read(p) }
var allowedMimeTypes = map[string]bool{ "": false, "image/jpeg": true, "image/png": true, "image/gif": true, "image/tiff": true, "image/tga": true, "text/plain": true, "application/json": true, "application/pdf": false, "binary/octet-stream": false, "video/mp4": false, "audio/mp3": false, }
|