> ██████╗ ██████╗ ██████╗ ███████╗████████╗ █████╗ ███████╗███████╗
> ██╔══██╗██╔══██╗██╔═══██╗██╔════╝╚══██╔══╝██╔══██╗██╔════╝██╔════╝
> ██████╔╝██████╔╝██║ ██║███████╗ ██║ ███████║█████╗ █████╗
> ██╔═══╝ ██╔══██╗██║ ██║╚════██║ ██║ ██╔══██║██╔══╝ ██╔══╝
> ██║ ██║ ██║╚██████╔╝███████║ ██║ ██║ ██║██║ ██║
> ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝
API — eSports Analytics Hub - ProStaff.gg
╔══════════════════════════════════════════════════════════════════════════════╗
║ PROSTAFF API — Ruby on Rails 7.2 (API-Only) ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ Backend for the ProStaff.gg esports team management platform. ║
║ 200+ documented endpoints · JWT Auth · Modular Monolith · p95 ~500ms ║
╚══════════════════════════════════════════════════════════════════════════════╝
▶ Key Features (click to expand)
┌─────────────────────────────────────────────────────────────────────────────┐
│ [■] JWT Authentication — Refresh tokens + token blacklisting │
│ [■] HashID URLs — Base62 encoding for obfuscated URLs │
│ [■] Swagger Docs — 200+ endpoints documented interactively │
│ [■] Riot Games API — Automatic match and player import │
│ [■] Advanced Analytics — KDA trends, champion pools, vision control │
│ [■] Scouting System — Talent discovery + watchlist management │
│ [■] VOD Review System — Collaborative timestamp annotations │
│ [■] Schedule Management — Matches, scrims and team events │
│ [■] Goal Tracking — Performance goals (team and players) │
│ [■] Competitive Module — PandaScore integration + draft analysis │
│ [■] Scrims Management — Opponent tracking + analytics │
│ [■] Strategy Module — Draft planning + tactical boards │
│ [■] Support System — Ticketing + staff dashboard + FAQ │
│ [■] Global Search — Meilisearch full-text search across models │
│ [■] Real-time Messaging — Action Cable WebSocket team chat │
│ [■] Background Jobs — Sidekiq for async background processing │
│ [■] Security Hardened — OWASP Top 10, Brakeman, ZAP tested │
│ [■] High Performance — p95: ~500ms · cached: ~50ms │
│ [■] Modular Monolith — Scalable modular architecture │
│ [■] Observability — /health/live + /health/ready + Sidekiq mon. │
│ [■] 401 Rate Spike Detection — Sliding-window middleware, alerts at >5% │
│ [■] Job Heartbeat Tracking — Stale scheduled job detection via Redis │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ 01 · Quick Start │
│ 02 · Technology Stack │
│ 03 · Architecture │
│ 04 · Setup │
│ 05 · Development Tools │
│ 06 · API Documentation │
│ 07 · Testing │
│ 08 · Performance & Load Testing │
│ 09 · Security │
│ 10 · Observability & Monitoring │
│ 11 · Deployment │
│ 12 · CI/CD │
│ 13 · Contributing │
│ 14 · License │
└──────────────────────────────────────────────────────┘
▶ Option 1: Docker (Recommended)
# Start all services (API, PostgreSQL, Redis, Sidekiq)
docker compose up -d
# Create test user
docker exec prostaff-api-api-1 rails runner scripts/create_test_user.rb
# Get JWT token for testing
./scripts/get-token.sh
# Access API docs
open http://localhost:3333/api-docs
# Run smoke tests
./load_tests/run-tests.sh smoke local
# Run security scan
./security_tests/scripts/brakeman-scan.sh▶ Option 2: Local Development (Without Docker)
# Install dependencies
bundle install
# Generate secrets
./scripts/generate_secrets.sh # Copy output to .env
# Setup database
rails db:create db:migrate db:seed
# Start Redis (in separate terminal)
redis-server
# Start Sidekiq (in separate terminal)
bundle exec sidekiq
# Start Rails server
rails server -p 3333
# Get JWT token for testing
./scripts/get-token.sh
# Access API docs
open http://localhost:3333/api-docs API: http://localhost:3333
Swagger Docs: http://localhost:3333/api-docs
╔══════════════════════╦════════════════════════════════════════════════════╗
║ LAYER ║ TECNOLOGY ║
╠══════════════════════╬════════════════════════════════════════════════════╣
║ Language ║ Ruby 3.4.5 ║
║ Framework ║ Rails 7.2.0 (API-only mode) ║
║ Database ║ PostgreSQL 14+ ║
║ Authentication ║ JWT (access + refresh tokens) ║
║ URL Obfuscation ║ HashID with Base62 encoding ║
║ Background Jobs ║ Sidekiq ║
║ Caching ║ Redis (port 6380) ║
║ API Documentation ║ Swagger/OpenAPI 3.0 (rswag) ║
║ Testing ║ RSpec, Integration Specs, k6, OWASP ZAP ║
║ Authorization ║ Pundit ║
║ Serialization ║ Blueprinter ║
║ Full-text Search ║ Meilisearch ║
║ Real-time ║ Action Cable (WebSocket) ║
╚══════════════════════╩════════════════════════════════════════════════════╝
This API follows a modular monolith architecture:
┌─────────────────────────────────────────────────────────────────────────────┐
│ MODULE │ RESPONSIBILITY │
├─────────────────────┼───────────────────────────────────────────────────────┤
│ authentication │ User auth and authorization │
│ dashboard │ Dashboard statistics and metrics │
│ players │ Player management and statistics │
│ scouting │ Player scouting and talent discovery │
│ analytics │ Performance, competitive draft, tournament & opponent│
│ matches │ Match data and statistics │
│ schedules │ Event and schedule management │
│ vod_reviews │ Video review and timestamp management │
│ team_goals │ Goal setting and tracking │
│ riot_integration │ Riot Games API integration │
│ competitive │ PandaScore integration, pro matches, draft analysis │
│ scrims │ Scrim management and opponent team tracking │
│ strategy │ Draft planning and tactical board system │
│ support │ Support ticket system with staff dashboard and FAQ │
│ messaging │ Real-time team chat via Action Cable WebSocket │
│ search │ Global full-text search powered by Meilisearch │
│ notifications │ In-app notification system │
└─────────────────────┴───────────────────────────────────────────────────────┘
graph TB
subgraph "Client Layer"
Client[Frontend Application]
end
subgraph "API Gateway"
Router[Rails Router]
CORS[CORS Middleware]
RateLimit[Rate Limiting]
Auth[Authentication Middleware]
end
subgraph "Application Layer - Modular Monolith"
subgraph "Authentication Module"
AuthController[Auth Controller]
JWTService[JWT Service]
UserModel[User Model]
end
subgraph "Dashboard Module"
DashboardController[Dashboard Controller]
DashStats[Statistics Service]
end
subgraph "Players Module"
PlayersController[Players Controller]
PlayerModel[Player Model]
ChampionPoolModel[Champion Pool Model]
end
subgraph "Scouting Module"
ScoutingController[Scouting Controller]
ScoutingTargetModel[Scouting Target Model]
Watchlist[Watchlist Service]
end
subgraph "Analytics Module"
AnalyticsController[Analytics Controller]
PerformanceService[Performance Service]
KDAService[KDA Trend Service]
end
subgraph "Matches Module"
MatchesController[Matches Controller]
MatchModel[Match Model]
PlayerMatchStatModel[Player Match Stat Model]
end
subgraph "Schedules Module"
SchedulesController[Schedules Controller]
ScheduleModel[Schedule Model]
end
subgraph "VOD Reviews Module"
VODController[VOD Reviews Controller]
VodReviewModel[VOD Review Model]
VodTimestampModel[VOD Timestamp Model]
end
subgraph "Team Goals Module"
GoalsController[Team Goals Controller]
TeamGoalModel[Team Goal Model]
end
subgraph "Riot Integration Module"
RiotService[Riot API Service]
RiotSync[Sync Service]
end
subgraph "Competitive Module"
CompetitiveController[Competitive Controller]
ProMatchesController[Pro Matches Controller]
PandaScoreService[PandaScore Service]
DraftAnalyzer[Draft Analyzer]
end
subgraph "Scrims Module"
ScrimsController[Scrims Controller]
OpponentTeamsController[Opponent Teams Controller]
ScrimAnalytics[Scrim Analytics Service]
end
subgraph "Strategy Module"
DraftPlansController[Draft Plans Controller]
TacticalBoardsController[Tactical Boards Controller]
DraftAnalysisService[Draft Analysis Service]
end
subgraph "Support Module"
SupportTicketsController[Support Tickets Controller]
SupportFaqsController[Support FAQs Controller]
SupportStaffController[Support Staff Controller]
SupportTicketModel[Support Ticket Model]
SupportFaqModel[Support FAQ Model]
end
end
subgraph "Data Layer"
PostgreSQL[(PostgreSQL Database)]
Redis[(Redis Cache)]
end
subgraph "Background Jobs"
Sidekiq[Sidekiq Workers]
JobQueue[Job Queue]
end
subgraph "External Services"
RiotAPI[Riot Games API]
PandaScoreAPI[PandaScore API]
end
Client -->|HTTP/JSON| CORS
CORS --> RateLimit
RateLimit --> Auth
Auth --> Router
Router --> AuthController
Router --> DashboardController
Router --> PlayersController
Router --> ScoutingController
Router --> AnalyticsController
Router --> MatchesController
Router --> SchedulesController
Router --> VODController
Router --> GoalsController
Router --> CompetitiveController
Router --> ProMatchesController
Router --> ScrimsController
Router --> OpponentTeamsController
Router --> DraftPlansController
Router --> TacticalBoardsController
Router --> SupportTicketsController
Router --> SupportFaqsController
Router --> SupportStaffController
AuthController --> JWTService
AuthController --> UserModel
PlayersController --> PlayerModel
PlayerModel --> ChampionPoolModel
ScoutingController --> ScoutingTargetModel
ScoutingController --> Watchlist
Watchlist --> PostgreSQL
MatchesController --> MatchModel
MatchModel --> PlayerMatchStatModel
SchedulesController --> ScheduleModel
VODController --> VodReviewModel
VodReviewModel --> VodTimestampModel
GoalsController --> TeamGoalModel
AnalyticsController --> PerformanceService
AnalyticsController --> KDAService
CompetitiveController --> PandaScoreService
CompetitiveController --> DraftAnalyzer
ScrimsController --> ScrimAnalytics
ScrimAnalytics --> PostgreSQL
DraftPlansController --> DraftAnalysisService
SupportTicketsController --> SupportTicketModel
SupportFaqsController --> SupportFaqModel
SupportStaffController --> UserModel
AuditLogModel[AuditLog Model] --> PostgreSQL
ChampionPoolModel[ChampionPool Model] --> PostgreSQL
CompetitiveMatchModel[CompetitiveMatch Model] --> PostgreSQL
DraftPlanModel[DraftPlan Model] --> PostgreSQL
MatchModel[Match Model] --> PostgreSQL
NotificationModel[Notification Model] --> PostgreSQL
OpponentTeamModel[OpponentTeam Model] --> PostgreSQL
OrganizationModel[Organization Model] --> PostgreSQL
PasswordResetTokenModel[PasswordResetToken Model] --> PostgreSQL
PlayerModel[Player Model] --> PostgreSQL
PlayerMatchStatModel[PlayerMatchStat Model] --> PostgreSQL
ScheduleModel[Schedule Model] --> PostgreSQL
ScoutingTargetModel[ScoutingTarget Model] --> PostgreSQL
ScrimModel[Scrim Model] --> PostgreSQL
SupportFaqModel[SupportFaq Model] --> PostgreSQL
SupportTicketModel[Support Ticket Model] --> PostgreSQL
SupportTicketMessageModel[SupportTicketMessage Model] --> PostgreSQL
TacticalBoardModel[TacticalBoard Model] --> PostgreSQL
TeamGoalModel[Team Goal Model] --> PostgreSQL
TokenBlacklistModel[TokenBlacklist Model] --> PostgreSQL
UserModel[User Model] --> PostgreSQL
VodReviewModel[VodReview Model] --> PostgreSQL
VodTimestampModel[VodTimestamp Model] --> PostgreSQL
JWTService --> Redis
DashStats --> Redis
PerformanceService --> Redis
PlayersController --> RiotService
MatchesController --> RiotService
ScoutingController --> RiotService
RiotService --> RiotSync
RiotService --> RiotAPI
RiotService --> Sidekiq
PandaScoreService --> PandaScoreAPI
Sidekiq -- Uses --> Redis
style Client fill:#e1f5ff
style PostgreSQL fill:#336791
style Redis fill:#d82c20
style RiotAPI fill:#eb0029
style PandaScoreAPI fill:#ff6b35
style Sidekiq fill:#b1003e
Key Architecture Principles:
- Modular Monolith: Each module is self-contained with its own controllers, models, and services
- API-Only: Rails configured in API mode for JSON responses
- JWT Authentication: Stateless authentication using JWT tokens
- Background Processing: Long-running tasks handled by Sidekiq
- Caching: Redis used for session management and performance optimization
- External Integration: Riot Games API integration for real-time data
- Rate Limiting: Rack::Attack for API rate limiting
- CORS: Configured for cross-origin requests from frontend
graph TB
subgraph "Clients"
FrontendApp["ProStaff.gg<br/>Front + TypeScript SPA"]
PlayerPortal["Player Portal<br/>JWT player token"]
end
subgraph "Production — Coolify"
Traefik["Traefik<br/>TLS + Let's Encrypt<br/>WebSocket proxy"]
end
subgraph "Rails — Puma"
Cable["Action Cable<br/>WebSocket /cable<br/>(team chat)"]
Router["Rails Router<br/>REST API v1<br/>200+ endpoints"]
Sidekiq["Sidekiq<br/>Background Workers<br/>(Riot sync · reindex)"]
end
subgraph "Data"
PG[("PostgreSQL")]
RD[("Redis")]
Meili[("Meilisearch")]
end
subgraph "External APIs"
RiotAPI["Riot Games API"]
PandaScore["PandaScore API"]
end
FrontendApp -- "HTTPS REST" --> Traefik
FrontendApp -- "WSS /cable" --> Traefik
PlayerPortal -- "HTTPS REST" --> Traefik
Traefik -- "HTTP" --> Router
Traefik -- "WS upgrade" --> Cable
Router -- "reads / writes" --> PG
Router -- "cache · JWT blacklist" --> RD
Router -- "full-text search" --> Meili
Cable -- "pub/sub" --> RD
Sidekiq -- "async jobs" --> PG
Sidekiq -- "queue · cache" --> RD
Sidekiq -- "reindex docs" --> Meili
Router -- "player data" --> RiotAPI
Sidekiq -- "match sync" --> RiotAPI
Router -- "pro matches" --> PandaScore
style FrontendApp fill:#1e88e5
style PlayerPortal fill:#5c6bc0
style Traefik fill:#1565c0
style Cable fill:#b1003e
style Sidekiq fill:#b1003e
style PG fill:#336791
style RD fill:#d82c20
style Meili fill:#ff5722
style RiotAPI fill:#eb0029
style PandaScore fill:#ff6b35
[✓] Ruby 3.2+
[✓] PostgreSQL 14+
[✓] Redis 6+
1. Clone the repository:
git clone <repository-url>
cd prostaff-api2. Install dependencies:
bundle install3. Setup environment variables:
cp .env.example .envEdit .env with your configuration:
- Database credentials
- JWT secret key
- Riot API key (get from https://developer.riotgames.com)
- PandaScore API key (optional, for competitive data)
- Redis URL
- CORS origins
- HashID salt (for URL obfuscation — keep secret!)
- Frontend URL
4. Setup the database:
rails db:create
rails db:migrate
rails db:seed5. Start the services:
# Terminal 1 — Redis
redis-server
# Terminal 2 — Sidekiq
bundle exec sidekiq
# Terminal 3 — Rails server
rails serverAPI available at
http://localhost:3333
Generate secure secrets for your .env file:
./scripts/generate_secrets.shGenerates: SECRET_KEY_BASE (Rails) and JWT_SECRET_KEY (JWT signing).
./scripts/get-token.shThis will:
- Create or find a test user (
test@prostaff.gg) - Generate a valid JWT token
- Show instructions on how to use it
Quick usage:
# Export to environment variable
export BEARER_TOKEN=$(./scripts/get-token.sh | grep -oP 'eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*')
# Use in curl
curl -H "Authorization: Bearer $BEARER_TOKEN" http://localhost:3333/api/v1/playersCustom credentials:
TEST_EMAIL="admin@example.com" TEST_PASSWORD="MyPass123!" ./scripts/get-token.sh▶ Interactive Documentation — Swagger UI (click to expand)
Access:
http://localhost:3333/api-docs
Features:
- Try out endpoints directly from the browser
- See request/response schemas
- Authentication support (Bearer token)
- Complete parameter documentation
- Example requests and responses
# Run integration specs and generate Swagger docs
RSWAG_GENERATE=1 bundle exec rake rswag:specs:swaggerize
# Or run specs individually
bundle exec rspec spec/integration/Note:
RSWAG_GENERATE=1bypasses the local test-DB requirement — the swagger formatter uses--dry-runso no database queries are executed.
Generated file: swagger/v1/swagger.yaml
http://localhost:3333/api/v1
All endpoints (except auth) require a Bearer token:
Authorization: Bearer <your-jwt-token>
╔═══════════════╦══════════════════════════════════╗
║ Token Type ║ Bearer (JWT) ║
║ Access TTL ║ 24h (via JWT_EXPIRATION_HOURS) ║
║ Refresh TTL ║ 7 days ║
╚═══════════════╩══════════════════════════════════╝
Getting a token:
# Option 1: Use the script
./scripts/get-token.sh
# Option 2: Login via API
curl -X POST http://localhost:3333/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@prostaff.gg","password":"Test123!@#"}'Refreshing a token:
curl -X POST http://localhost:3333/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token":"your-refresh-token"}'POST /auth/register— Register new organization and admin userPOST /auth/login— Login userPOST /auth/refresh— Refresh JWT tokenPOST /auth/logout— Logout userPOST /auth/forgot-password— Request password resetPOST /auth/reset-password— Reset passwordGET /auth/me— Get current user info
GET /dashboard— Get complete dashboard dataGET /dashboard/stats— Get quick statsGET /dashboard/activities— Get recent activitiesGET /dashboard/schedule— Get upcoming schedule
GET /players— List playersGET /players/:id— Get player detailsPOST /players— Create playerPATCH /players/:id— Update playerDELETE /players/:id— Delete playerGET /players/stats— Get roster statisticsPOST /players/import— Import player from Riot API
GET /matches— List matchesGET /matches/:id— Get match detailsPOST /matches— Create matchPOST /matches/import— Import match from Riot API
GET /scouting/players— List scouting targetsGET /scouting/regions— Get available regionsPOST /scouting/players— Add scouting target
GET /analytics/performance— Team performance analyticsGET /analytics/team-comparison— Compare all playersGET /analytics/champions/:player_id— Champion pool statisticsGET /analytics/kda-trend/:player_id— KDA trend over timeGET /analytics/laning/:player_id— Laning phase performanceGET /analytics/teamfights/:player_id— Teamfight performanceGET /analytics/vision/:player_id— Vision control statisticsGET /analytics/competitive/draft-performance— Pick/ban/side/role performance from competitive matchesGET /analytics/competitive/tournament-stats— Win/loss breakdown per tournament and stageGET /analytics/competitive/opponents— Aggregated record against each unique opponent
All competitive analytics endpoints accept optional query filters:
tournament,patch,region,start_date,end_date
GET /schedules— List all scheduled eventsGET /schedules/:id— Get schedule detailsPOST /schedules— Create new eventPATCH /schedules/:id— Update eventDELETE /schedules/:id— Delete event
GET /team-goals— List all goalsGET /team-goals/:id— Get goal detailsPOST /team-goals— Create new goalPATCH /team-goals/:id— Update goal progressDELETE /team-goals/:id— Delete goal
GET /vod-reviews— List VOD reviewsGET /vod-reviews/:id— Get review detailsPOST /vod-reviews— Create new reviewPATCH /vod-reviews/:id— Update reviewDELETE /vod-reviews/:id— Delete reviewGET /vod-reviews/:id/timestamps— List timestampsPOST /vod-reviews/:id/timestamps— Create timestampPATCH /vod-timestamps/:id— Update timestampDELETE /vod-timestamps/:id— Delete timestamp
GET /riot-data/champions— Get champions ID mapGET /riot-data/champions/:key— Get champion detailsGET /riot-data/all-champions— Get all champions dataGET /riot-data/items— Get all itemsGET /riot-data/summoner-spells— Get summoner spellsGET /riot-data/version— Get current Data Dragon versionPOST /riot-data/clear-cache— Clear Data Dragon cache (owner only)POST /riot-data/update-cache— Update Data Dragon cache (owner only)
GET /riot-integration/sync-status— Get sync status for all players
GET /competitive-matches— List competitive matchesGET /competitive-matches/:id— Get competitive match detailsGET /competitive/pro-matches— List all pro matchesGET /competitive/pro-matches/:id— Get pro match detailsGET /competitive/pro-matches/upcoming— Get upcoming pro matchesGET /competitive/pro-matches/past— Get past pro matchesPOST /competitive/pro-matches/refresh— Refresh pro matches from PandaScorePOST /competitive/pro-matches/import— Import specific pro matchPOST /competitive/draft-comparison— Compare team compositionsGET /competitive/meta/:role— Get meta champions by roleGET /competitive/composition-winrate— Get composition winrate statisticsGET /competitive/counters— Get champion counter suggestions
GET /scrims/scrims— List all scrimsGET /scrims/scrims/:id— Get scrim detailsPOST /scrims/scrims— Create new scrimPATCH /scrims/scrims/:id— Update scrimDELETE /scrims/scrims/:id— Delete scrimPOST /scrims/scrims/:id/add_game— Add game to scrimGET /scrims/scrims/calendar— Get scrims calendarGET /scrims/scrims/analytics— Get scrims analyticsGET /scrims/opponent-teams— List opponent teamsGET /scrims/opponent-teams/:id— Get opponent team detailsPOST /scrims/opponent-teams— Create opponent teamPATCH /scrims/opponent-teams/:id— Update opponent teamDELETE /scrims/opponent-teams/:id— Delete opponent teamGET /scrims/opponent-teams/:id/scrim-history— Get scrim history with opponent
GET /strategy/draft-plans— List draft plansGET /strategy/draft-plans/:id— Get draft plan detailsPOST /strategy/draft-plans— Create new draft planPATCH /strategy/draft-plans/:id— Update draft planDELETE /strategy/draft-plans/:id— Delete draft planPOST /strategy/draft-plans/:id/analyze— Analyze draft planPATCH /strategy/draft-plans/:id/activate— Activate draft planPATCH /strategy/draft-plans/:id/deactivate— Deactivate draft planGET /strategy/tactical-boards— List tactical boardsGET /strategy/tactical-boards/:id— Get tactical board detailsPOST /strategy/tactical-boards— Create new tactical boardPATCH /strategy/tactical-boards/:id— Update tactical boardDELETE /strategy/tactical-boards/:id— Delete tactical boardGET /strategy/tactical-boards/:id/statistics— Get tactical board statisticsGET /strategy/assets/champion/:champion_name— Get champion assetsGET /strategy/assets/map— Get map assets
GET /support/tickets— List user's ticketsGET /support/tickets/:id— Get ticket detailsPOST /support/tickets— Create new support ticketPATCH /support/tickets/:id— Update ticketDELETE /support/tickets/:id— Delete ticketPOST /support/tickets/:id/close— Close ticketPOST /support/tickets/:id/reopen— Reopen ticketPOST /support/tickets/:id/messages— Add message to ticketGET /support/faq— List all FAQsGET /support/faq/:slug— Get FAQ by slugPOST /support/faq/:slug/helpful— Mark FAQ as helpfulPOST /support/faq/:slug/not-helpful— Mark FAQ as not helpfulGET /support/staff/dashboard— Support staff dashboard (staff only)GET /support/staff/analytics— Support analytics (staff only)POST /support/staff/tickets/:id/assign— Assign ticket to staff (staff only)POST /support/staff/tickets/:id/resolve— Resolve ticket (staff only)
GET /search?q=:query— Full-text search across players, organizations, scouting targets, opponent teams and FAQs
GET /notifications— List user notificationsGET /notifications/:id— Get notificationPATCH /notifications/:id/mark-as-read— Mark as readPATCH /notifications/mark-all-as-read— Mark all as readGET /notifications/unread-count— Get unread countDELETE /notifications/:id— Delete notification
GET /health/live — Liveness probe: is Puma alive? Never checks deps.
Always returns 200 while the process responds.
Use for container restart policies (Coolify/K8s).
GET /health/ready — Readiness probe: checks PostgreSQL + Redis + Meilisearch.
Returns 200 (ok/disabled) or 503 (any dep unreachable).
Use for load balancer traffic routing.
GET /api/v1/monitoring/sidekiq — Admin only. Full Sidekiq snapshot:
queue depths, worker count, dead queue, retry queue,
scheduled job heartbeats (stale detection), alert flags.
Returns 503 if Redis unavailable.
Monitoring endpoint response includes:
scheduled_jobs— last run timestamp +stale: true/falseper cron jobalerts.stale_jobs— true if any scheduled job exceeded its alert windowalerts.no_workers— true if no Sidekiq workers runningalerts.dead_queue_exceeded— true if dead queue > 10 jobsalerts.queue_depth_exceeded— true if total queue depth > 100 jobs
GET /team-members— List organization members (staff only — rejects player tokens)
GET /messages— List direct message history with a memberDELETE /messages/:id— Soft-delete a message
For complete endpoint documentation with request/response examples, visit
/api-docs
# Full test suite
bundle exec rspec
# Unit tests (models, services)
bundle exec rspec spec/models
bundle exec rspec spec/services
# Request tests (controllers)
bundle exec rspec spec/requests
# Integration tests (Swagger documentation)
bundle exec rspec spec/integrationIntegration tests serve dual purpose:
- Test API endpoints with real HTTP requests
- Generate Swagger documentation automatically
# Run integration tests and generate Swagger docs
RSWAG_GENERATE=1 bundle exec rake rswag:specs:swaggerize
# Run specific integration spec
bundle exec rspec spec/integration/players_spec.rbCurrent coverage:
╔══════════════════════════╦════════════════════╗
║ MODULE ║ ENDPOINTS ║
╠══════════════════════════╬════════════════════╣
║ Authentication ║ 8 ║
║ Players ║ 9 ║
║ Matches ║ 11 ║
║ Scouting ║ 10 ║
║ Schedules ║ 8 ║
║ Team Goals ║ 8 ║
║ VOD Reviews ║ 11 ║
║ Analytics ║ 7 ║
║ Riot Data ║ 14 ║
║ Riot Integration ║ 1 ║
║ Dashboard ║ 4 ║
║ Competitive ║ 14 ║
║ Scrims ║ 14 ║
║ Strategy ║ 16 ║
║ Support ║ 16 ║
║ Admin ║ 9 ║
║ Notifications ║ 6 ║
║ Profile ║ 4 ║
║ Rosters ║ 4 ║
║ Team Members ║ 1 ║
║ Messages ║ 2 ║
║ Constants ║ 1 ║
║ Fantasy ║ 2 ║
╠══════════════════════════╬════════════════════╣
║ TOTAL ║ 200+ endpoints ║
╚══════════════════════════╩════════════════════╝
open coverage/index.html# Quick smoke test (1 min)
./load_tests/run-tests.sh smoke local
# Full load test (16 min)
./load_tests/run-tests.sh load local
# Stress test (28 min)
./load_tests/run-tests.sh stress local╔═══════════════════════════════════════╗
║ PERFORMANCE BENCHMARKS ║
╠══════════════════╦════════════════════╣
║ p(95) Docker ║ ~880ms ║
║ p(95) Prod est. ║ ~500ms ║
║ With cache ║ ~50ms ║
║ Error rate ║ 0% ║
╚══════════════════╩════════════════════╝
See TESTING_GUIDE.md and QUICK_START.md
# Complete security audit
./security_tests/scripts/full-security-audit.sh
# Individual scans
./security_tests/scripts/brakeman-scan.sh # Code analysis
./security_tests/scripts/dependency-scan.sh # Vulnerable gems
./security_tests/scripts/zap-baseline-scan.sh # Web app scan[✓] OWASP Top 10
[✓] Code security (Brakeman)
[✓] Dependency vulnerabilities
[✓] Runtime security (ZAP)
[✓] CI/CD integration
| Endpoint | Purpose | Returns |
|---|---|---|
GET /health/live |
Liveness — is Puma responding? | Always 200 |
GET /health/ready |
Readiness — all deps reachable? | 200 / 503 |
GET /up |
Legacy backward-compatible alias | 200 |
Rule: never point the liveness probe at an endpoint that checks Redis or DB. A Redis crash → liveness fail → container restart → reconnect storm → worse incident.
# Requires admin Bearer token
curl -H "Authorization: Bearer $TOKEN" https://api.prostaff.gg/api/v1/monitoring/sidekiqResponse shape:
{
"status": "ok | degraded | critical",
"processes": { "count": 1, "workers": [...] },
"queues": { "default": 0, "high": 0 },
"stats": { "enqueued": 0, "dead": 0, "retry": 0 },
"scheduled_jobs": {
"RefreshMetadataViewsJob": { "last_run_at": "...", "stale": false },
"CleanupExpiredTokensJob": { "last_run_at": "...", "stale": false }
},
"alerts": {
"no_workers": false,
"queue_depth_exceeded": false,
"dead_queue_exceeded": false,
"stale_jobs": false
}
}Status rules:
| status | condition |
|---|---|
ok |
all thresholds within bounds |
degraded |
queue > 100, dead > 10, or any scheduled job stale |
critical |
no Sidekiq workers running |
Middleware::AuthFailureTracker counts 401s vs total requests using Redis
sliding-window counters (5-minute window). Emits a structured log alert when
the ratio exceeds 5%:
{
"event": "auth_spike_detected",
"level": "CRITICAL",
"rate_pct": 8.3,
"threshold_pct": 5.0,
"total_requests": 240,
"total_401s": 20
}Threshold and window are configurable via env:
AUTH_TRACKER_THRESHOLD=0.05 # default: 5%
AUTH_TRACKER_WINDOW=5 # default: 5 minutesSIDEKIQ_QUEUE_ALERT_THRESHOLD=100 # queue depth that triggers degraded
SIDEKIQ_DEAD_ALERT_THRESHOLD=10 # dead queue size that triggers degraded# Core
DATABASE_URL=postgresql://user:password@host:5432/database
REDIS_URL=redis://host:6379/0
SECRET_KEY_BASE=your-rails-secret
# Authentication
JWT_SECRET_KEY=your-production-secret
# External APIs
RIOT_API_KEY=your-riot-api-key
PANDASCORE_API_KEY=your-pandascore-api-key
# Frontend
CORS_ORIGINS=https://your-frontend-domain.com
FRONTEND_URL=https://your-frontend-domain.com
# HashID Configuration (for URL obfuscation)
HASHID_SALT=your-secret-salt
HASHID_MIN_LENGTH=6
# Observability thresholds (optional, defaults shown)
SIDEKIQ_QUEUE_ALERT_THRESHOLD=100 # queue depth → degraded
SIDEKIQ_DEAD_ALERT_THRESHOLD=10 # dead queue → degraded
AUTH_TRACKER_THRESHOLD=0.05 # 401 rate spike threshold (5%)
AUTH_TRACKER_WINDOW=5 # sliding window in minutesdocker build -t prostaff-api .
docker run -p 3333:3000 prostaff-api┌────────────────────────────────────────────────────────────────┐
│ TRIGGER — changes in: │
│ · app/modules/** · app/models/** │
│ · app/controllers/** · config/routes.rb · Gemfile │
├────────────────────────────────────────────────────────────────┤
│ PROCESS │
│ 1. GitHub Actions detects relevant code changes │
│ 2. Runs scripts/update_architecture_diagram.rb │
│ 3. Script analyzes project structure │
│ 4. Generates updated Mermaid diagram │
│ 5. Updates README.md with new diagram │
│ 6. Commits changes back to the repository │
└────────────────────────────────────────────────────────────────┘
Manual update:
ruby scripts/update_architecture_diagram.rbAutomated testing on every push:
- Security Scan: Brakeman + dependency check
- Load Test: Nightly smoke tests
- Nightly Audit: Complete security scan
- CORS Smoke Test: Runs after every production deploy — sends a preflight request from each allowed origin and fails the pipeline if CORS is misconfigured
See .github/workflows/ for details.
- Create a feature branch
- Make your changes
- Add tests
- Run security scan:
./security_tests/scripts/brakeman-scan.sh - Ensure all tests pass
- Submit a pull request
The architecture diagram will be automatically updated when you add new modules, models, or controllers.
╔══════════════════════════════════════════════════════════════════════════════╗
║ © 2026 ProStaff.gg. All rights reserved. ║
║ ║
║ This repository contains the official ProStaff.gg API source code. ║
║ Released under: ║
║ ║
║ Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International ║
╚══════════════════════════════════════════════════════════════════════════════╝
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Prostaff.gg isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing Riot Games properties.
Riot Games, and all associated properties are trademarks or registered trademarks of Riot Games, Inc.
▓▒░ · © 2026 PROSTAFF.GG · ░▒▓
