This commit is contained in:
2026-10-03 12:46:38 +07:00
parent c16c1ed5ac
commit 7a972ea960
45 changed files with 75 additions and 26 deletions
+2
View File
@@ -5,5 +5,7 @@ package docs
import "embed"
// FS contains the embedded legal documents.
//
//go:embed TERMS.md PRIVACY.md CONSENT.md
var FS embed.FS
@@ -1,7 +1,9 @@
// Package email_sender sends email messages over SMTP.
package email_sender
import "context"
// Message describes an email message.
type Message struct {
To string
Subject string
@@ -9,6 +11,7 @@ type Message struct {
HTML string
}
// IEmailSender sends email messages.
type IEmailSender interface {
Send(ctx context.Context, message Message) error
}
+1
View File
@@ -26,6 +26,7 @@ type sender struct {
tlsConfig *tls.Config
}
// NewSender creates an SMTP email sender.
func NewSender(
smtpHost string,
smtpPort string,
@@ -2,6 +2,7 @@ package email_sender
import "html"
// ButtonHTML returns an HTML button linking to link.
func ButtonHTML(link, label string) string {
return `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:20px 0;">
<tr><td class="email-button" bgcolor="#342a22" style="border-radius:5px; background-color:#342a22;">
@@ -1,3 +1,4 @@
// Package file_storage stores files in object storage.
package file_storage
import (
@@ -12,12 +13,14 @@ import (
// отсутствующих объектов (маппинг smithy-ошибок NoSuchKey/NotFound).
var ErrFileNotFound = errors.New("файл не найден")
// File describes a stored file.
type File struct {
Name string
Data []byte
Mime string
}
// IFileStorage stores and retrieves files.
type IFileStorage interface {
Put(ctx context.Context, file *File) error
Get(ctx context.Context, filename string) (*File, error)
+1
View File
@@ -18,6 +18,7 @@ type storage struct {
bucket string
}
// NewRustFSStorage creates a RustFS-backed file storage.
func NewRustFSStorage(
ctx context.Context,
endpoint string,
@@ -1,3 +1,4 @@
// Package network_utils provides network-related helpers.
package network_utils
import "net"
@@ -13,6 +13,7 @@ type generator struct {
length int
}
// NewGenerator creates a password generator.
func NewGenerator(length int) IPasswordGenerator {
return &generator{
length: length,
@@ -1,5 +1,7 @@
// Package password_generator generates passwords.
package password_generator
// IPasswordGenerator generates passwords.
type IPasswordGenerator interface {
Generate() (string, error)
}
@@ -1,3 +1,4 @@
// Package processor_jwt processes JSON Web Tokens.
package processor_jwt
import "context"
@@ -10,6 +10,7 @@ import (
"google.golang.org/grpc/status"
)
// NewAuthorizationInterceptor creates a gRPC JWT authorization interceptor.
func NewAuthorizationInterceptor(
publicMethods map[string]bool,
processorJWT IProcessorJWT,
@@ -6,6 +6,7 @@ import (
"github.com/golang-jwt/jwt/v5"
)
// JWTClaims contains claims extracted from a JSON Web Token.
type JWTClaims struct {
UserID int `json:"user_id"`
UserEmail string `json:"user_email"`
@@ -13,10 +14,12 @@ type JWTClaims struct {
jwt.RegisteredClaims
}
// HasRole reports whether the claims contain role.
func (c *JWTClaims) HasRole(role string) bool {
return slices.Contains(c.Roles, role)
}
// IProcessorJWT validates and creates JSON Web Tokens.
type IProcessorJWT interface {
GenerateAccessToken(userID int, userEmail string, userRoles []string) (string, error)
GenerateRefreshToken(userID int) (string, error)
@@ -12,6 +12,7 @@ type processor struct {
secret []byte
}
// NewProcessor creates a JWT processor.
func NewProcessor(secret string) IProcessorJWT {
return &processor{
secret: []byte(secret),
+1
View File
@@ -1,3 +1,4 @@
// Package roles defines application roles and role checks.
package roles
// Роли пользователей системы.
+1
View File
@@ -7,6 +7,7 @@ type subject struct {
roles []string
}
// NewSubject creates a role-bearing subject.
func NewSubject(roles ...string) IHasRole {
return &subject{roles: roles}
}
@@ -6,10 +6,10 @@ import (
"evening_detective_server/internal/modules/storytelling"
)
// Имя файла с описанием сценария внутри архива.
// FileName is the name of the scenario description inside an archive.
const FileName = "scenario.json"
// Текущая версия формата scenario.json.
// Version is the current scenario.json format version.
const Version = 1
// ScenarioJSON — описание сценария в архиве; Story — в формате колонки
@@ -30,6 +30,7 @@ type Scenario struct {
Story string
}
// Bundle contains an archived scenario and its metadata.
type Bundle struct {
Scenario *ScenarioJSON
Files map[string][]byte
@@ -1,5 +1,7 @@
// Package storytelling models scenario stories and their elements.
package storytelling
// ICleaner cleans text used by a story.
type ICleaner interface {
ClearCode(code string) string
ClearText(text string) string
+17 -24
View File
@@ -1,53 +1,49 @@
package storytelling
// IStory provides access to a scenario story.
type IStory interface {
GetStory(scenario *Story, codes []string) *Story
}
// История - это введение, набор точек и вопросы дела
// Story describes a scenario story.
type Story struct {
Background string `json:"background,omitempty"`
Introduction *Introduction `json:"introduction,omitempty"`
// Закрывашка доступна в редакторе сценария, игрокам не передаётся.
// Conclusion is available in the scenario editor and is not sent to players.
Conclusion *Conclusion `json:"conclusion,omitempty"`
Places []*Place `json:"places"`
// Вопросы дела - список вопросов, на которые команда должна ответить
// Questions are the questions the team must answer.
Questions []*Question `json:"questions,omitempty"`
}
// Вопрос дела - вопрос, который автор сценария добавляет вместе с
// правильным ответом; команда отвечает на вопросы в конце игры
// Question describes a question and its correct answer.
type Question struct {
Code string `json:"code"`
Text string `json:"text"`
// Правильный ответ - виден только автору и организаторам,
// команде не передаётся
// Answer is visible only to the author and organizers.
Answer string `json:"answer"`
}
// Закрывашка - текст и видео для редактора сценария.
// Conclusion describes text and video for the scenario editor.
type Conclusion struct {
Text string `json:"text"`
Video string `json:"video,omitempty"`
}
// Введение - текст и аудио, которые игрок получает в начале игры.
// Introduction describes the text and audio shown at the start of the game.
type Introduction struct {
Text string `json:"text"`
Audio string `json:"audio,omitempty"`
}
// Точка - что-то куда можно сходить
// Это может быть место - аптека, остановка, площадь...
// Это может быть персонаж - пострадавший, продавец, полицейский...
// Это может быть описание действия игрока - ответ на вопрос, реакция на выбитую дверь...
// Place describes a location, character, or player action in the story.
type Place struct {
Code string `json:"code"`
@@ -57,42 +53,39 @@ type Place struct {
Image string `json:"image"`
// Признак скрытой карточки - некоторые места изначально не доступны
// Hidden reports whether the place is initially unavailable.
Hidden bool `json:"hidden"`
Applications []*Application `json:"applications,omitempty"`
// Двери к другим точкам - действия или диалоги, открывающие другие точки
// Doors are actions or dialogues that open other places.
Doors []*Door `json:"doors"`
// Получаемые ключи - ключи используются для получения доступа к скрытым дверям
// Keys are used to access hidden doors.
Keys []*Key `json:"keys"`
}
// Приложение - улика
// Это может быть оружие, отпечатки, фотография...
// Application describes evidence such as a weapon, fingerprint, or photo.
type Application struct {
Name string `json:"name"`
Image string `json:"image"`
// Тип файла улики: image | pdf | audio.
// FileType is the evidence file type: image, pdf, or audio.
FileType string `json:"file_type,omitempty"`
}
// Дверь - действие или диалог
// Door describes an action or dialogue.
type Door struct {
Code string `json:"code"`
Name string `json:"name"`
// Некоторые двери изначально не доступны
// Ключи требуемые для получения доступа к скрытым дверям
// Если указаны ключи то дверь считается скрытой
// Keys are required to access a hidden door.
Keys []*Key `json:"keys"`
}
// Аналог карт допуска в "Тайны Аркхема"
// Key describes an item that grants access to a door.
type Key struct {
Name string `json:"name"`
}
+1
View File
@@ -4,6 +4,7 @@ type story struct {
cleaner ICleaner
}
// NewStory creates a story from a cleaner.
func NewStory(
cleaner ICleaner,
) IStory {
@@ -1,3 +1,4 @@
// Package string_tools provides string transformation helpers.
package string_tools
import (
@@ -1,3 +1,4 @@
// Package team_qr_pdf generates team QR-code PDFs.
package team_qr_pdf
import (
@@ -12,8 +13,10 @@ import (
"github.com/skip2/go-qrcode"
)
// ErrNoCards indicates that no cards were provided for generation.
var ErrNoCards = errors.New("нет карточек")
// Card contains the data rendered on a team QR-code card.
type Card struct {
Name string
URL string
@@ -22,6 +25,7 @@ type Card struct {
//go:embed fonts/DejaVuSansCondensed.ttf
var font []byte
// Generate returns a PDF containing team QR-code cards.
func Generate(cards []Card) ([]byte, error) {
if len(cards) == 0 {
return nil, ErrNoCards
+1
View File
@@ -1,3 +1,4 @@
// Package repos contains database repositories and persistence models.
package repos
// TeamAnswer — ответ команды на вопрос дела (вопросы живут в сценарии,
+1
View File
@@ -1,3 +1,4 @@
// Package game_map_repo persists game map data.
package game_map_repo
import (
+1
View File
@@ -1,3 +1,4 @@
// Package game_reviews_repo persists game reviews.
package game_reviews_repo
import (
@@ -1,3 +1,4 @@
// Package refresh_tokens_repo persists refresh tokens.
package refresh_tokens_repo
import (
+1
View File
@@ -1,3 +1,4 @@
// Package scenarios_repo persists scenarios.
package scenarios_repo
import (
@@ -1,3 +1,4 @@
// Package screen_sessions_repo persists screen sessions.
package screen_sessions_repo
import (
+1
View File
@@ -1,3 +1,4 @@
// Package team_answers_repo persists team answers.
package team_answers_repo
import (
@@ -1,3 +1,4 @@
// Package teams_repo persists teams and participation data.
package teams_repo
import (
@@ -1,3 +1,4 @@
// Package tickets_repo tests ticket persistence.
package tickets_repo
import (
@@ -1,3 +1,4 @@
// Package user_agreements_repo persists user agreements.
package user_agreements_repo
// AgreementType — типы документов, акцептуемых пользователем.
+1
View File
@@ -1,3 +1,4 @@
// Package users_repo persists users.
package users_repo
import (
@@ -1,3 +1,4 @@
// Package answer_service handles game answers.
package answer_service
import (
+1
View File
@@ -1,3 +1,4 @@
// Package game_model contains game service models and mappers.
package game_model
import (
@@ -1,3 +1,4 @@
// Package game_review_service handles game reviews.
package game_review_service
import (
@@ -1,3 +1,4 @@
// Package game_service handles game lifecycle operations.
package game_service
import (
+1
View File
@@ -1,3 +1,4 @@
// Package map_service handles game map operations.
package map_service
import "evening_detective_server/internal/repos/game_map_repo"
+1
View File
@@ -3,6 +3,7 @@
// напрямую (через интерфейс GamePlayer), без HTTP-прослойки. MCP-сервер
// монтируется в основной процесс как HTTP-эндпоинт /mcp (streamable HTTP)
// на REST-gateway.
// Package mcp_service exposes game operations through MCP.
package mcp_service
import (
@@ -1,3 +1,4 @@
// Package participation_service handles team participation.
package participation_service
import "evening_detective_server/internal/repos/teams_repo"
@@ -1,3 +1,4 @@
// Package scenarios_service handles scenario operations.
package scenarios_service
import (
@@ -1,3 +1,4 @@
// Package screen_service handles screen sessions.
package screen_service
import (
@@ -1,3 +1,4 @@
// Package team_service tests team operations.
package team_service
import (
@@ -1,3 +1,4 @@
// Package ticket_service handles support tickets.
package ticket_service
import (
+1
View File
@@ -1,3 +1,4 @@
// Package ui_service serves the user interface.
package ui_service
import (
@@ -1,3 +1,4 @@
// Package users_service tests user operations.
package users_service
import (