> βββββββ βββββββ βββββββ βββββββββββββββββ ββββββ ββββββββββββββββ
> ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
> βββββββββββββββββββ βββββββββββ βββ ββββββββββββββ ββββββ
> βββββββ βββββββββββ βββββββββββ βββ ββββββββββββββ ββββββ
> βββ βββ ββββββββββββββββββββ βββ βββ ββββββ βββ
> βββ βββ βββ βββββββ ββββββββ βββ βββ ββββββ βββ
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 ~200ms β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βΆ 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 + ES match detail + H2H β
β [β ] Match Detail View β Per-game picks, KDA, gold, CS, DMG from ES β
β [β ] Pro Match Data Lake β 97K+ games (2014-2026) in Elasticsearch β
β [β ] Multi-League Backfill β CBLOL Β· Academy Β· CD auto-sync daily β
β [β ] Scrims Management β Opponent tracking + analytics β
β [β ] Strategy Module β Draft planning + tactical boards β
β [β ] AI Pick Recommendations β Champion2Vec + XGBoost, 97K+ game dataset β
β [β ] Meta Intelligence β Build aggregation, champion/item analytics β
β [β ] Support System β Ticketing + staff dashboard + FAQ β
β [β ] Global Search β Meilisearch full-text search across models β
β [β ] Search Fallback β PostgreSQL ILIKE fallback when Meili offlineβ
β [β ] Real-time Messaging β Action Cable WebSocket team chat β
β [β ] Background Jobs β Sidekiq for async background processing β
β [β ] Circuit Breaker β Riot API isolation (3-state, Redis-backed) β
β [β ] Async Audit Log β Non-blocking audit trail via Sidekiq job β
β [β ] Response Cache Layer β Redis cache on 6 endpoints (TTL 5β30 min) β
β [β ] Security Hardened β OWASP Top 10, Brakeman, Semgrep, CodeQL, ZAPβ
β [β ] Rate Limiting β Rack::Attack: 5 rules + Retry-After headers β
β [β ] High Performance β p95: ~200ms prod Β· cached: ~50ms Β· >60% hit β
β [β ] Modular Monolith β Scalable modular architecture β
β [β ] Observability β /health+/live /health/ready + cache metrics β
β [β ] 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 & CodeQL β
β 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.8 β
β Framework β Rails 7.2.3.1 (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) β
β Data Lake β Elasticsearch 8 (97K+ pro games, all leagues) β
β ML Service β Python 3.11 Β· FastAPI Β· XGBoost Β· Gensim Word2Vec β
ββββββββββββββββββββββββ©βββββββββββββββββββββββββββββββββββββββββββββββββββββ
This API follows a modular monolith architecture with the following modules:
authentication- User authentication and authorizationdashboard- Dashboard statistics and metricsplayers- Player management and statisticsscouting- Player scouting and talent discoveryanalytics- Performance analytics and reportingmatches- Match data and statisticsschedules- Event and schedule managementvod_reviews- Video review and timestamp managementteam_goals- Goal setting and trackingriot_integration- Riot Games API integrationcompetitive- PandaScore integration, pro matches, draft analysisscrims- Scrim management and opponent team trackingstrategy- Draft planning and tactical board systemsupport- Support ticket system with staff and FAQ management
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
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
** Better Visualization Options:**
The diagram above may be difficult to read in GitHub's preview. For better visualization:
- View in Mermaid Live Editor - Open
diagram.mmdfile in the live editor- View in VS Code - Install Mermaid extension
- Export diagram: Use the standalone
diagram.mmdfile for import into diagramming toolsThe complete Mermaid source is available in
diagram.mmd.
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
[β] Ruby 3.4.8+
[β] PostgreSQL 14+
[β] Redis 6+
βΆ Installation (click to expand)
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 matchGET /competitive/pro-matches/match-previewβ Per-game picks + stats for a recent series (ES)GET /competitive/pro-matches/es-seriesβ H2H series history between two teams (ES)POST /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
match-previewandes-seriesquery the Elasticsearch data lake (97K+ games) and are league-agnostic. They accept?team1=&team2=&league=&limit=query params.
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
Requires Tier 1 (Professional) subscription β
predictive_analyticsfeature gate.
POST /ai/draft/analyzeβ Analyze a saved draft plan (synergy, counter, risk, readiness)POST /ai/recommend-pickβ Top-5 ML champion recommendations for a partial draft
Request (/ai/recommend-pick):
{
"our_picks": ["Jinx", "Thresh", "Azir"],
"opponent_picks": ["Caitlyn", "Nautilus", "Syndra", "Renekton", "Graves"],
"our_bans": ["Corki"],
"opponent_bans": ["Zeri"],
"patch": "16.08",
"league": "LCK"
}Response:
{
"data": {
"source": "ml_v2",
"model_version": "v2",
"recommendations": [
{
"champion": "Lissandra",
"score": 0.5219,
"win_probability": 0.553,
"synergy_score": 0.3557,
"counter_score": 0.3252,
"reasoning_tokens": ["high win probability (55%)", "decent synergy with current picks"]
}
]
}
}Response header X-AI-Source: ml_v2 (XGBoost) or X-AI-Source: legacy (DraftSuggester fallback when ML service is unreachable).
The ML service (prostaff-ml) is a FastAPI container trained on 97K+ competitive matches using Champion2Vec embeddings (64D, Gensim Word2Vec) and an XGBoost classifier with 327 features. Training pipeline: extract_features β train_champion2vec β train_win_probability β validate β export. See prostaff-ml.
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 /meta/buildsβ List aggregated champion buildsGET /meta/builds/:championβ Get build stats for a specific championPOST /meta/builds/aggregateβ Trigger build aggregation job (admin)GET /meta/itemsβ List item analyticsGET /meta/items/:item_idβ Get item performance stats
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 /tournamentsβ List active tournaments (public)GET /tournaments/:idβ Show tournament with full bracket (public)POST /tournamentsβ Create tournament (admin only)PATCH /tournaments/:idβ Update tournament (admin only)POST /tournaments/:id/generate_bracketβ Generate 16-team double-elimination bracket (admin only)GET /tournaments/:id/teamsβ List enrolled teams with roster snapshot (public)POST /tournaments/:id/teamsβ Enroll organization as teamPATCH /tournaments/:id/teams/:team_id/approveβ Approve enrollment + lock roster (admin only)PATCH /tournaments/:id/teams/:team_id/rejectβ Reject enrollment (admin only)DELETE /tournaments/:id/teams/:team_idβ Withdraw team (own org, before bracket)GET /tournaments/:id/matchesβ List all bracket matches (public)GET /tournaments/:id/matches/:match_idβ Show match detail with checkin statusPOST /tournaments/:id/matches/:match_id/checkinβ Captain confirms presenceGET /tournaments/:id/matches/:match_id/reportβ Get report statusPOST /tournaments/:id/matches/:match_id/reportβ Submit result report with evidencePOST /tournaments/:id/matches/:match_id/report/admin_resolveβ Admin resolves dispute (admin 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.
GET /api/v1/monitoring/cache_stats β Admin only. Real-time cache hit rate:
total reads, hits, misses, hit_rate (%).
Counters persist in Redis, reset on Redis flush.
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 β
β Meta Intelligence β 5 β
β 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. β <200ms(target) β
β With cache β ~50ms β
β Cache hit rate β >60%(after warmup)β
β Error rate β 0% β
ββββββββββββββββββββ©βββββββββββββββββββββ
Cached endpoints (Redis, org-scoped, bypass on filter params):
| Endpoint | TTL | Invalidation |
|---|---|---|
GET /players |
5 min | after_commit on Player |
GET /players/:id |
5 min | After Riot sync |
GET /matches |
5 min | after_commit on Match |
GET /analytics/performance |
15 min | After Match sync |
GET /tournaments |
30 min | after_commit on Tournament |
All cached responses include X-Cache-Hit: true/false header.
See TESTING_GUIDE.md and QUICK_START.md
# Complete security audit
./security_tests/scripts/full-security-audit.sh
# SAST β code + dependency analysis
./security_tests/scripts/brakeman-scan.sh # Rails-specific SAST
./security_tests/scripts/dependency-scan.sh # Vulnerable gems (bundle-audit)
# DAST β runtime scanning
./security_tests/scripts/zap-baseline-scan.sh # OWASP ZAP baseline
./security_tests/scripts/zap-api-scan.sh # ZAP API scan (OpenAPI)
# Application-specific tests
./security_tests/scripts/test-multi-tenancy-isolation.sh # cross-org data leakage
./security_tests/scripts/test-ssrf-protection.sh # SSRF in Riot API URLs
./security_tests/scripts/test-rate-limiting.sh # Rack::Attack throttle rules
./security_tests/scripts/test-timing-oracle.sh # user enumeration via timing
./security_tests/scripts/test-body-fuzzing.sh # mass assignment + type confusion[β] OWASP Top 10
[β] SAST: Brakeman (Rails) + Semgrep + CodeQL (security-extended)
[β] Dependency audit: bundle-audit + FOSSA
[β] Secrets: TruffleHog (verified secrets, full git history)
[β] DAST: OWASP ZAP baseline + API scan
[β] Multi-tenancy isolation (cross-org IDOR)
[β] Rate limiting: Rack::Attack rules validated (5 throttle rules)
[β] Timing oracle: login/register user enumeration
[β] Mass assignment: StrongParameters coverage
[β] CI/CD: security gates on every push + weekly CodeQL
Last Audit: 2026-04-21 Overall Grade: A (all application security tests passing) Status: Production-ready
| Rule | Limit | Window |
|---|---|---|
logins/ip |
5 requests | 20 seconds |
register/ip |
3 requests | 1 hour |
password_reset/ip |
5 requests | 1 hour |
req/ip |
300 requests (configurable) | per period |
req/authenticated_user |
1000 requests | 1 hour |
All 429 responses include a Retry-After header with the exact seconds until the window resets.
We take security seriously. If you discover a security vulnerability, please follow our Security Policy.
DO NOT create public GitHub issues for security vulnerabilities.
Email: security@prostaff.gg
- Security Policy - Vulnerability disclosure process
- Security Testing Guide - Running security tests
βΆ for details (click to expand)
| 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/sidekiq
# Cache hit rate
curl -H "Authorization: Bearer $TOKEN" https://api.prostaff.gg/api/v1/monitoring/cache_stats
# { "reads": 4200, "hits": 2730, "misses": 1470, "hit_rate": "65.0%" }Response 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 |
CircuitBreakerService protects the Riot API integration from cascade failures.
State persists in Redis (shared across all Puma workers and Sidekiq threads).
closed (normal) β requests pass through; failure count incremented on error
open (tripped) β requests rejected immediately (<100ms); no upstream call
half-open (recovery)β one probe request allowed; success closes, failure re-opens
| Parameter | Default | Env override |
|---|---|---|
| Failure threshold | 5 consecutive errors | CIRCUIT_BREAKER_THRESHOLD |
| Recovery timeout | 60 seconds | β |
Log events emitted on state transitions:
[CIRCUIT_BREAKER] Circuit riot_api OPENED after 5 consecutive failures
[CIRCUIT_BREAKER] Circuit riot_api CLOSED after recovery
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 degradedThis API is one service in the ProStaff ecosystem. The other services it integrates with:
| Service | Stack | Role |
|---|---|---|
| prostaff-events | Elixir / Phoenix 1.7 | Real-time event bus β subscribes to Redis pub/sub and pushes via Phoenix Channels |
| prostaff-riot-gateway | Go 1.23 | Riot API proxy β token bucket rate limiting, L1/L2 cache, circuit breaker |
| ProStaff-Scraper | Python / FastAPI | Pro match data pipeline β Leaguepedia + Oracle's Elixir β Elasticsearch |
| πprostaff-ml | Python 3.11 / FastAPI | ML service β Champion2Vec + XGBoost pick recommendations (serves POST /ai/recommend-pick) |
| πprostaff-analytics-hub | Next.js 15 / vinext | Frontend SPA β consumes API (also: https://prostaff.gg, https://scrims.lol) |
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"]
CoolifyNode["Coolify<br/>Deploys & Manages<br/>all production services"]
end
subgraph "Rails β Puma"
Cable["Action Cable<br/>WSS /cable<br/>(team chat)"]
Router["Rails Router<br/>REST API v1<br/>200+ endpoints"]
Sidekiq["Sidekiq<br/>Background Workers<br/>(sync + backfill)"]
end
subgraph "prostaff-events β Elixir/Phoenix"
PhoenixEndpoint["Phoenix Endpoint<br/>WSS /socket<br/>(domain events)"]
RedisSub["RedisSubscriber<br/>PSUBSCRIBE prostaff:events:*"]
InhouseQ["InhouseQueue<br/>GenServer per active queue"]
end
subgraph "prostaff-riot-gateway β Go"
Gateway["Riot Gateway :4444<br/>Token bucket Β· L1/L2 cache<br/>Circuit breaker"]
end
subgraph "prostaff-ml β Python/FastAPI"
MlService["ML Service :8001<br/>Champion2Vec + XGBoost<br/>POST /recommend Β· /win-probability"]
MlModels[("Models<br/>champion2vec.bin<br/>win_probability_v2.pkl")]
end
subgraph "prostaff-scraper β Python/FastAPI"
ScraperApi["Scraper API :8000<br/>GET /health Β· /matches Β· /status"]
ScraperCron["scraper-cron<br/>polls LoL Esports API<br/>(every SYNC_INTERVAL_HOURS)"]
Enrichment["enrichment daemon<br/>Leaguepedia + Riot<br/>(items/runes/KDA)"]
Backfill["backfill daemon<br/>historical Leaguepedia<br/>(2013 β present)"]
end
subgraph "Data"
PG[("PostgreSQL")]
RD[("Redis")]
Meili[("Meilisearch")]
ES[("Elasticsearch\n97K+ pro games")]
end
subgraph "External APIs"
RiotAPI["Riot Games API"]
PandaScore["PandaScore API"]
Grid.gg["Grid.gg"]
LoLEsports["LoL Esports API"]
Leaguepedia["Leaguepedia<br/>(lol.fandom.com)"]
end
%% === ConexΓ΅es ===
FrontendApp -- "HTTPS REST" --> Traefik
FrontendApp -- "WSS /cable" --> Traefik
FrontendApp -- "WSS /socket" --> Traefik
PlayerPortal -- "HTTPS REST" --> Traefik
Traefik -- "HTTP" --> Router
Traefik -- "WS upgrade /cable" --> Cable
Traefik -- "WS upgrade /socket" --> PhoenixEndpoint
Router -- "reads / writes" --> PG
Router -- "cache Β· JWT blacklist" --> RD
Router -- "full-text search" --> Meili
Router -- "publish prostaff:events:*" --> RD
Router -- "match detail Β· H2H" --> ES
Router -. "internal JWT<br/>(internal only)" .-> Gateway
Cable -- "pub/sub" --> RD
Sidekiq -- "async jobs" --> PG
Sidekiq -- "queue Β· cache" --> RD
Sidekiq -- "reindex docs" --> Meili
Sidekiq -- "historical backfill" --> ES
Sidekiq -. "internal JWT<br/>(internal only)" .-> Gateway
RedisSub -- "PSUBSCRIBE" --> RD
RedisSub --> InhouseQ
RedisSub --> PhoenixEndpoint
Gateway -- "rate limited" --> RiotAPI
Router -- "pro matches" --> PandaScore
Router -- "pro matches" --> Grid.gg
Router -. "HTTP POST /recommend<br/>(fallback: DraftSuggester)" .-> MlService
MlService --- MlModels
ScraperCron -- "indexes new games" --> ES
ScraperCron -- "polls events" --> LoLEsports
Enrichment -- "enriches KDA/items" --> ES
Enrichment -- "items/runes/KDA" --> Leaguepedia
Backfill -- "historical backfill" --> ES
Backfill -- "historical data" --> Leaguepedia
ScraperApi -- "reads / status" --> ES
%% === Estilos ===
style FrontendApp fill:#1e88e5
style PlayerPortal fill:#5c6bc0
style Traefik fill:#1565c0
style CoolifyNode fill:#0d47a1, stroke:#ffffff, stroke-width:3px
style Cable fill:#b1003e
style Sidekiq fill:#b1003e
style PhoenixEndpoint fill:#4B275F
style RedisSub fill:#4B275F
style InhouseQ fill:#4B275F
style Gateway fill:#00ADD8
style PG fill:#336791
style RD fill:#d82c20
style Meili fill:#ff5722
style ES fill:#005571
style RiotAPI fill:#eb0029
style PandaScore fill:#B069DB
style Grid.gg fill:#000000
style LoLEsports fill:#c89b3c
style Leaguepedia fill:#8a6914
style MlService fill:#1a6b3a
style MlModels fill:#0f3d22
style ScraperApi fill:#3d6b1a
style ScraperCron fill:#2d5010
style Enrichment fill:#2d5010
style Backfill fill:#2d5010
ββββββββββββββββββββββββββββββββ¦ββββββββββββββββ¦ββββββββββββββββββββββββββββββββββββββββββββ
β Job β Schedule β Description β
β βββββββββββββββββββββββββββββββ¬ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββ£
β CleanupExpiredTokensJob β 0 2 * * * β Purge expired JWT blacklist + pwd tokens β
β RefreshMetadataViewsJob β 0 */2 * * * β Refresh DB metadata materialized views β
β HistoricalBackfillJob β 0 4 * * * β CBLOL: Leaguepedia β ES β DB β
β HistoricalBackfillJob β 30 4 * * * β CBLOL Academy: Leaguepedia β ES β DB β
β HistoricalBackfillJob β 0 5 * * * β Circuito Desafiante: Leaguepedia β ES β
β ScrimResultReminderJob β 0 10 * * * β Send deadline reminders, expire reports β
β RebuildChampionMatrixJob β 0 3 * * * β Rebuild AI champion matrices/vectors β
β StatusSnapshotJob β */15 * * * * β Record component health snapshots β
ββββββββββββββββββββββββββββββββ©ββββββββββββββββ©ββββββββββββββββββββββββββββββββββββββββββββ
Backfill jobs are resumable β re-running skips already-completed tournaments. First run imports full history (~8-12h); subsequent runs only process new/failed tournaments (minutes).
Production Stack (Coolify):
- Reverse Proxy: Traefik with automatic TLS (Let's Encrypt)
- Application: Rails 7.2 API (Puma) + Action Cable + Sidekiq
- Event Bus: prostaff-events β Elixir/Phoenix 1.7 (domain events via Phoenix Channels)
- Riot Gateway: prostaff-riot-gateway β Go 1.23 (token bucket, L1/L2 cache, circuit breaker)
- Database: PostgreSQL 14+ (Supabase self-hosted)
- Cache/Queue: Redis 7
- Search: Meilisearch (self-hosted)
- Data Lake: Elasticsearch 8 (self-hosted, 97K+ pro games)
Data Flow:
- Clients connect via HTTPS/WSS through Traefik
- REST requests β Rails Router β PostgreSQL / Redis / Meilisearch / Elasticsearch
- Team chat WebSocket β Action Cable β Redis pub/sub
- Domain event WebSocket β prostaff-events (Phoenix Channels) β Redis
PSUBSCRIBE prostaff:events:*β Rails - Riot API calls β prostaff-riot-gateway (rate limiter + cache) β Riot Games API
- Background jobs β Sidekiq β PostgreSQL / Redis / Meilisearch / Elasticsearch / Gateway
βΆ Environments (click to expand)
# 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
RIOT_GATEWAY_URL=http://riot-gateway:4444 # prostaff-riot-gateway internal URL
INTERNAL_JWT_SECRET=your-internal-jwt-secret # shared with prostaff-riot-gateway (must match)
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 minutes
# Circuit breaker (optional, defaults shown)
CIRCUIT_BREAKER_THRESHOLD=5 # consecutive failures before opening circuit
# Elasticsearch data lake
ELASTICSEARCH_URL=https://user:password@elastic.example.com # ES 8.x with basic auth
# ML AI Service (prostaff-ml FastAPI container)
# Local dev: http://localhost:8001 | Coolify production: http://ai-service:8001
AI_SERVICE_URL=http://ai-service:8001
# Historical backfill (Sidekiq scheduled jobs β override per-job via sidekiq.yml kwargs)
BACKFILL_LEAGUE=CBLOL # default league for manual runs
BACKFILL_OUR_TEAM=paiN Gaming # team name used in sync step
BACKFILL_MIN_YEAR=2013 # earliest year to import
BACKFILL_SYNC_LIMIT=500 # max matches synced per job run
SIDEKIQ_CONCURRENCY=10 # Sidekiq thread count (keep DB_POOL equal)
DB_POOL=10 # ActiveRecord pool size for Sidekiq containerdocker build -t prostaff-api .
docker run -p 3333:3000 prostaff-api| Workflow | Trigger | What it does |
|---|---|---|
security-scan.yml |
Push / PR β master, develop | Brakeman, Bundle Audit, Semgrep, TruffleHog, SSRF + auth + SQLi runtime tests |
codeql.yml |
Push / PR β master + Saturdays 3am UTC | CodeQL security-extended + Actions workflows; SARIF to GitHub Security tab |
nightly-security.yml |
Nightly 1am UTC + manual dispatch | Full audit: Brakeman + Bundle Audit + ZAP baseline + ZAP API scan |
load-test.yml |
Manual dispatch | k6 smoke/load/stress tests |
snyk-container.yml |
Push / PR β master, develop + weekly | Snyk container image vulnerability scan |
deploy-production.yml |
Push tag v*.*.* + manual dispatch |
Build, test, deploy to Coolify + CORS smoke test post-deploy |
deploy-staging.yml |
Push β develop + manual dispatch | Same pipeline targeting staging |
update-architecture-diagram.yml Push / PR + manual dispatch |
Auto-regenerates Mermaid diagram and commits |
CodeQL runs as a complementary SAST engine alongside Brakeman and Semgrep, covering different vulnerability classes:
- SQL injection patterns outside standard ActiveRecord usage
- Path traversal in file operations
- SSRF in custom HTTP clients
- Code injection via
eval/sendwith unsanitized input - ReDoS (regex denial of service)
Results are published to the GitHub Security tab in SARIF format.
Config: .github/codeql/codeql-config.yml β analysis scoped to app/, lib/, config/ (excludes vendor, tests, scripts).
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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.rbSee .github/workflows/ for full workflow sources.
We welcome contributions from the community! Before contributing, please read our guidelines.
- Read the Contributing Guidelines
- Review the Code of Conduct
- Fork the repository
- Create a feature branch
- Make your changes following our code style
- Add tests for new functionality
- Run security scans:
./security_tests/scripts/brakeman-scan.sh - Ensure all tests pass:
bundle exec rspec - Submit a pull request
feature/- New featuresfix/- Bug fixesrefactor/- Code refactoringdocs/- Documentation changessecurity/- Security fixes
We follow Ruby Style Guide and enforce code quality standards:
- Cyclomatic complexity β€ 7
- Method length β€ 50 lines
- All queries must be scoped by organization (multi-tenant!)
- Run Brakeman before committing (no HIGH/CRITICAL issues)
- Contributing Guidelines - Detailed contribution process
- Code of Conduct - Community standards
- Security Policy - Reporting security vulnerabilities
- Testing Guide - How to run tests
- Quick Start - Development environment setup
Note: 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: β
β β
β GNU Affero General Public License v3.0 (AGPLv3) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
This project is licensed under the GNU Affero General Public License v3.0.
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 Β· βββ