package app import ( "context" "errors" "fmt" "strings" "evening_detective_server/internal/modules/roles" "evening_detective_server/internal/modules/string_tools" "evening_detective_server/internal/services/scenarios_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) DownloadScenarioArchive(ctx context.Context, req *proto.DownloadScenarioArchiveReq) (*httpbody.HttpBody, error) { claims, claimsErr := claimsFromContext(ctx) if claimsErr != nil { return nil, claimsErr } if !roles.HasRole(claims, roles.Author) { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } data, name, err := s.scenarioService.DownloadArchive( ctx, int(req.ID), claims.UserID, roles.HasRole(claims, roles.Admin), ) if err != nil { switch { case errors.Is(err, scenarios_service.ErrScenarioNotFound): return nil, status.Error(codes.NotFound, err.Error()) case errors.Is(err, scenarios_service.ErrScenarioNotOwner): return nil, status.Error(codes.PermissionDenied, err.Error()) default: return nil, status.Error(codes.Internal, "не удалось скачать архив сценария") } } transliterated := sanitizeFilename(string_tools.Transliterate(name)) if transliterated == "" { transliterated = "scenario" } filename := transliterated + ".zip" if err := grpc.SetHeader(ctx, metadata.Pairs("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))); err != nil { return nil, status.Error(codes.Internal, "не удалось установить заголовки архива") } return &httpbody.HttpBody{ Data: data, ContentType: "application/zip", }, nil } func (s *server) UploadScenarioArchive(ctx context.Context, req *httpbody.HttpBody) (*proto.UploadScenarioArchiveRsp, error) { claims, claimsErr := claimsFromContext(ctx) if claimsErr != nil { return nil, claimsErr } if !roles.HasRole(claims, roles.Author) { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } if req == nil || len(req.Data) == 0 { return nil, status.Errorf(codes.InvalidArgument, "пустое тело запроса") } if len(req.Data) > scenarios_service.MaxArchiveSize() { return nil, status.Errorf(codes.InvalidArgument, "архив больше максимального размера (%d байт)", scenarios_service.MaxArchiveSize()) } id, err := s.scenarioService.UploadArchive(ctx, req.Data, claims.UserID) if err != nil { if errors.Is(err, scenarios_service.ErrInvalidArchive) { return nil, status.Error(codes.InvalidArgument, scenarios_service.ErrInvalidArchive.Error()) } return nil, status.Error(codes.Internal, "не удалось загрузить архив сценария") } return &proto.UploadScenarioArchiveRsp{ ID: int32(id), }, nil } func sanitizeFilename(name string) string { replacer := strings.NewReplacer( `"`, "_", "\r", "_", "\n", "_", ) return replacer.Replace(name) }