package app import ( "context" "errors" "evening_detective_server/internal/modules/file_storage" "evening_detective_server/internal/modules/processor_jwt" "evening_detective_server/internal/modules/roles" "evening_detective_server/internal/services/file_service" proto "evening_detective_server/proto" "google.golang.org/genproto/googleapis/api/httpbody" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) func (s *server) UploadFile(ctx context.Context, req *proto.UploadFileReq) (*proto.UploadFileRsp, error) { // Файлы (в т.ч. улики) загружают авторы сценариев в редакторе. claims := ctx.Value("claims").(*processor_jwt.JWTClaims) if !roles.HasRole(claims, roles.Author) { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } filename, fileType, err := s.fileService.UploadFile(ctx, req.Filename, req.Data) if err != nil { switch { case errors.Is(err, file_service.ErrFileTooLarge): return nil, status.Errorf(codes.ResourceExhausted, "%v", err) case errors.Is(err, file_service.ErrEmptyName), errors.Is(err, file_service.ErrEmptyData), errors.Is(err, file_service.ErrInvalidExtension), errors.Is(err, file_service.ErrInvalidContentType): return nil, status.Errorf(codes.InvalidArgument, "%v", err) default: return nil, status.Errorf(codes.Internal, "%v", err) } } return &proto.UploadFileRsp{ Filename: filename, FileType: fileType, }, nil } func (s *server) DownloadFile(ctx context.Context, req *proto.DownloadFileReq) (*httpbody.HttpBody, error) { file, err := s.fileService.DownloadFile(ctx, req.Filename) if err != nil { switch { case errors.Is(err, file_storage.ErrFileNotFound): return nil, status.Errorf(codes.NotFound, "%v", err) default: return nil, status.Errorf(codes.Internal, "%v", err) } } // nosniff — при публичном скачивании браузер не должен угадывать // Content-Type по содержимому (защита от stored-XSS при расхождении). if err := grpc.SetHeader(ctx, metadata.Pairs("X-Content-Type-Options", "nosniff")); err != nil { return nil, status.Errorf(codes.Internal, "failed to set response header: %v", err) } return &httpbody.HttpBody{ Data: file.Data, ContentType: file.Mime, }, nil }