generated from VLADIMIR/template
98 lines
3.4 KiB
Go
98 lines
3.4 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"evening_detective_server/internal/modules/processor_jwt"
|
|
"evening_detective_server/internal/modules/roles"
|
|
"evening_detective_server/internal/services/game_export_service"
|
|
"evening_detective_server/proto"
|
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
func canExportGame(claims *processor_jwt.JWTClaims) bool {
|
|
return claims != nil && (roles.HasRole(claims, roles.Organizer) || claims.HasRole(roles.Developer))
|
|
}
|
|
|
|
func (s *server) StartGameExport(ctx context.Context, req *proto.StartGameExportReq) (*proto.GameExportRsp, error) {
|
|
claims, err := claimsFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !canExportGame(claims) {
|
|
return nil, status.Error(codes.PermissionDenied, "permission denied")
|
|
}
|
|
result, err := s.gameExportService.Start(ctx, int(req.GetID()), claims.UserID)
|
|
if err != nil {
|
|
return nil, gameExportErrorStatus(err)
|
|
}
|
|
return &proto.GameExportRsp{Export: mapGameExport(result)}, nil
|
|
}
|
|
|
|
func (s *server) GetGameExport(ctx context.Context, req *proto.GetGameExportReq) (*proto.GameExportRsp, error) {
|
|
claims, err := claimsFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !canExportGame(claims) {
|
|
return nil, status.Error(codes.PermissionDenied, "permission denied")
|
|
}
|
|
result, err := s.gameExportService.Get(ctx, int(req.GetID()))
|
|
if err != nil {
|
|
return nil, gameExportErrorStatus(err)
|
|
}
|
|
return &proto.GameExportRsp{Export: mapGameExport(result)}, nil
|
|
}
|
|
|
|
// GameExportDownloadHandler authenticates with the existing JWT processor and streams private ZIPs.
|
|
func GameExportDownloadHandler(jwt processor_jwt.IProcessorJWT, exports *game_export_service.GameExportService) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
fail := func(err error) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(runtime.HTTPStatusFromCode(status.Code(err)))
|
|
// The message has already been reduced to a safe transport error.
|
|
_, _ = w.Write([]byte(`{"error":` + strconv.Quote(status.Convert(err).Message()) + `}`))
|
|
}
|
|
token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
|
if token == "" {
|
|
fail(status.Error(codes.Unauthenticated, "Требуется авторизация"))
|
|
return
|
|
}
|
|
claims, err := jwt.GetClaims(token)
|
|
if err != nil {
|
|
fail(status.Error(codes.Unauthenticated, "Недействительный токен"))
|
|
return
|
|
}
|
|
if !canExportGame(claims) {
|
|
fail(status.Error(codes.PermissionDenied, "permission denied"))
|
|
return
|
|
}
|
|
gameID, err := strconv.ParseInt(r.PathValue("id"), 10, 32)
|
|
if err != nil || gameID <= 0 {
|
|
fail(status.Error(codes.InvalidArgument, "Некорректный идентификатор игры"))
|
|
return
|
|
}
|
|
result, body, err := exports.OpenDownload(r.Context(), int(gameID))
|
|
if err != nil {
|
|
fail(gameExportErrorStatus(err))
|
|
return
|
|
}
|
|
defer func() { _ = body.Close() }()
|
|
w.Header().Set("Content-Type", "application/zip")
|
|
w.Header().Set("Content-Disposition", `attachment; filename="game-`+result.PackageID+`.zip"`)
|
|
w.Header().Set("Content-Length", strconv.FormatInt(result.Size, 10))
|
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
if _, err := io.CopyBuffer(w, body, make([]byte, 32<<10)); err != nil && r.Context().Err() == nil {
|
|
log.Printf("game export download: %v", err)
|
|
}
|
|
})
|
|
}
|