This commit is contained in:
2026-09-10 02:39:55 +07:00
parent 79bd3e2cf8
commit 9986d593d3
5 changed files with 196 additions and 8 deletions
+103 -8
View File
@@ -2,9 +2,12 @@ package app
import (
"context"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"net/netip"
"strconv"
"strings"
"evening_detective_server/docs"
@@ -14,6 +17,7 @@ import (
"evening_detective_server/internal/modules/scenario_archive"
"evening_detective_server/internal/modules/string_tools"
"evening_detective_server/internal/repos/scenarios_repo"
"evening_detective_server/internal/repos/screen_sessions_repo"
"evening_detective_server/internal/repos/users_repo"
"evening_detective_server/internal/services/file_service"
"evening_detective_server/internal/services/game_service"
@@ -22,6 +26,7 @@ import (
"evening_detective_server/internal/services/users_service"
proto "evening_detective_server/proto"
"github.com/google/uuid"
"google.golang.org/genproto/googleapis/api/httpbody"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
@@ -40,6 +45,7 @@ type server struct {
fileService *file_service.FileService
scenarioService *scenarios_service.ScenarioService
gameService *game_service.GameService
screenRepo *screen_sessions_repo.ScreenSessionsRepo
}
func NewServer(
@@ -48,6 +54,7 @@ func NewServer(
fileService *file_service.FileService,
scenarioService *scenarios_service.ScenarioService,
gameService *game_service.GameService,
screenRepo *screen_sessions_repo.ScreenSessionsRepo,
) proto.EveningDetectiveServerServer {
return &server{
usersService: usersService,
@@ -55,23 +62,111 @@ func NewServer(
fileService: fileService,
scenarioService: scenarioService,
gameService: gameService,
screenRepo: screenRepo,
}
}
func (s *server) CreateScreenSession(context.Context, *proto.CreateScreenSessionReq) (*proto.CreateScreenSessionRsp, error) {
panic("CreateScreenSession is not implemented")
func (s *server) CreateScreenSession(ctx context.Context, _ *proto.CreateScreenSessionReq) (*proto.CreateScreenSessionRsp, error) {
token := uuid.New().String()
id, err := s.screenRepo.Create(ctx, hashScreenToken(token))
if err != nil {
return nil, status.Error(codes.Internal, "не удалось создать сессию экрана")
}
return &proto.CreateScreenSessionRsp{ScreenId: fmt.Sprint(id), AccessToken: token}, nil
}
func (s *server) GetScreenState(context.Context, *proto.GetScreenStateReq) (*proto.GetScreenStateRsp, error) {
panic("GetScreenState is not implemented")
func (s *server) GetScreenState(ctx context.Context, req *proto.GetScreenStateReq) (*proto.GetScreenStateRsp, error) {
if req == nil || req.ScreenId == "" || req.AccessToken == "" {
return nil, status.Error(codes.InvalidArgument, "некорректные данные экрана")
}
id, err := strconv.Atoi(req.ScreenId)
if err != nil || id <= 0 {
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор экрана")
}
session, err := s.screenRepo.Get(ctx, id, hashScreenToken(req.AccessToken))
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
return nil, status.Error(codes.Unauthenticated, "недействительная сессия экрана")
}
if err != nil {
return nil, status.Error(codes.Internal, "не удалось получить состояние экрана")
}
return &proto.GetScreenStateRsp{State: screenState(session)}, nil
}
func (s *server) BindScreen(context.Context, *proto.BindScreenReq) (*proto.BindScreenRsp, error) {
panic("BindScreen is not implemented")
func (s *server) BindScreen(ctx context.Context, req *proto.BindScreenReq) (*proto.BindScreenRsp, error) {
if !canManageScreens(ctx) {
return nil, status.Error(codes.PermissionDenied, "permission denied")
}
if req == nil || req.ScreenId == "" || req.AccessToken == "" || req.GameId <= 0 {
return nil, status.Error(codes.InvalidArgument, "некорректные данные привязки")
}
id, err := strconv.Atoi(req.ScreenId)
if err != nil || id <= 0 {
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор экрана")
}
if _, err = s.gameService.GetGame(ctx, int(req.GameId)); err != nil {
return nil, status.Error(codes.NotFound, "игра не найдена")
}
hash := hashScreenToken(req.AccessToken)
if err = s.screenRepo.Bind(ctx, id, int(req.GameId), hash); err != nil {
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
return nil, status.Error(codes.Unauthenticated, "недействительная сессия экрана")
}
return nil, status.Error(codes.Internal, "не удалось привязать экран")
}
session, err := s.screenRepo.Get(ctx, id, hash)
if err != nil {
return nil, status.Error(codes.Internal, "не удалось получить состояние экрана")
}
return &proto.BindScreenRsp{State: screenState(session)}, nil
}
func (s *server) UnbindScreen(context.Context, *proto.UnbindScreenReq) (*proto.UnbindScreenRsp, error) {
panic("UnbindScreen is not implemented")
func (s *server) UnbindScreen(ctx context.Context, req *proto.UnbindScreenReq) (*proto.UnbindScreenRsp, error) {
if !canManageScreens(ctx) {
return nil, status.Error(codes.PermissionDenied, "permission denied")
}
if req == nil || req.ScreenId == "" {
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор экрана")
}
id, err := strconv.Atoi(req.ScreenId)
if err != nil || id <= 0 {
return nil, status.Error(codes.InvalidArgument, "некорректный идентификатор экрана")
}
if err = s.screenRepo.Unbind(ctx, id); err != nil {
if errors.Is(err, screen_sessions_repo.ErrScreenNotFound) {
return nil, status.Error(codes.NotFound, "экран не найден")
}
return nil, status.Error(codes.Internal, "не удалось отвязать экран")
}
return &proto.UnbindScreenRsp{}, nil
}
func canManageScreens(ctx context.Context) bool {
claims, ok := ctx.Value("claims").(*processor_jwt.JWTClaims)
if !ok || claims == nil {
return false
}
return roles.HasRole(claims, roles.Organizer) || roles.HasRole(claims, roles.Author)
}
func hashScreenToken(token string) string {
sum := sha256.Sum256([]byte(token))
return base64.RawURLEncoding.EncodeToString(sum[:])
}
func screenState(session *screen_sessions_repo.ScreenSession) *proto.ScreenState {
state := &proto.ScreenState{
Id: fmt.Sprint(session.ID),
Status: "waiting",
Mode: proto.ScreenMode_SCREEN_MODE_UNSPECIFIED,
}
if session.GameID != nil {
gameID := int32(*session.GameID)
state.GameId = &gameID
state.Status = "bound"
state.Mode = proto.ScreenMode_SCREEN_MODE_COVER
}
return state
}
func (s *server) SetScreenMode(context.Context, *proto.SetScreenModeReq) (*proto.SetScreenModeRsp, error) {