Markdown Converter
Agent skill for markdown-converter
> **Status**: Production-Ready | **Version**: 1.0.0
Sign in to like and favorite skills
Status: Production-Ready | Version: 1.0.0 Last Updated: 2026-01-26 Move to:
after permissions restartwebsite/CLAUDE.md
The Slomix Website is a modern, responsive web frontend for the ET:Legacy stats tracking system. It provides real-time player statistics, leaderboards, match histories, and live server status.
Stack: FastAPI (Python) + Vanilla JavaScript + Tailwind CSS + Chart.js Database: PostgreSQL (read-only user, shared with bot) Auth: Discord OAuth2 Port: 8000 | Screen Session:
website
Browser (User) ↓ HTTP requests FastAPI Backend (port 8000) ↓ Async queries PostgreSQL (shared with bot) ↑ Bot writes stats Discord Bot
Key Pattern: Website is READ-ONLY. Bot writes all data, website only queries.
website/ ├── index.html # SPA entry point (749 lines) ├── js/ │ ├── app.js # Navigation, view management │ ├── utils.js # XSS prevention, fetch helpers │ ├── auth.js # Discord OAuth, player linking │ ├── player-profile.js # Player stats + charts │ ├── leaderboard.js # Rankings │ ├── matches.js # Match browser, maps, weapons │ ├── sessions.js # Gaming session browser │ ├── records.js # Hall of Fame │ ├── live-status.js # Server status polling │ └── ... ├── backend/ │ ├── main.py # FastAPI app entry │ ├── dependencies.py # DB pool injection │ ├── routers/ │ │ ├── api.py # Stats API (19 endpoints, 2,444 lines) │ │ ├── auth.py # Discord OAuth │ │ └── predictions.py # Match predictions │ └── services/ │ ├── game_server_query.py # UDP Quake3 server query │ └── ... ├── .env.example # Config template ├── requirements.txt # Python deps └── etlegacy-website.service # Systemd service
GET /api/status - Health checkGET /api/live-status - Game server + voice channel statusGET /api/stats/last-session - Latest gaming sessionGET /api/sessions - All sessions (paginated)GET /api/sessions/{date} - Session detailsGET /api/stats/player/{name} - Lifetime statsGET /api/stats/player/{name}/form - Session form chart (15 sessions)GET /api/stats/player/{name}/rounds - Round chart (30 rounds)GET /api/player/{name}/matches - Recent 10 matchesGET /api/player/search?q= - AutocompleteGET /api/stats/leaderboard?stat=dpm&period=30d - Top playersGET /api/stats/matches?limit=50 - Recent matchesGET /api/stats/matches/{match_id} - Match details with teamsGET /api/stats/maps - Map listGET /api/stats/weapons - Weapon statsGET /api/stats/records - Hall of FameGET /api/stats/overview - Global statsGET /auth/login - Discord OAuth startGET /auth/callback - OAuth callbackGET /auth/me - Current userPOST /auth/link - Link Discord to player| View | Route | Purpose |
|---|---|---|
| Home | | Dashboard, search, widgets |
| Profile | | Player stats + charts |
| Leaderboard | | Rankings by DPM/Kills/K/D |
| Matches | | Match browser with details modal |
| Sessions | | Gaming session history |
| Maps | | Map statistics + balance |
| Weapons | | Weapon usage stats |
| Records | | Hall of Fame |
| Awards | | Achievement badges |
// Always use these for user-generated content: escapeHtml(userInput) // For innerHTML escapeJsString(playerName) // For onclick handlers
# All queries use parameterized statements query = "SELECT * FROM players WHERE name = $1" await db.fetch(query, (user_input,)) # LIKE patterns escaped from bot.core.utils import escape_like_pattern pattern = escape_like_pattern(search_term)
# Database (read-only user recommended) DATABASE_TYPE=postgresql POSTGRES_HOST=localhost POSTGRES_DATABASE=etlegacy POSTGRES_USER=website_readonly POSTGRES_PASSWORD=... # Session security (REQUIRED) SESSION_SECRET=<generate-with-secrets.token_urlsafe(32)> # Discord OAuth DISCORD_CLIENT_ID=... DISCORD_CLIENT_SECRET=... DISCORD_REDIRECT_URI=http://localhost:8000/auth/callback # Server WEBSITE_PORT=8000 WEBSITE_HOST=0.0.0.0
-- Run setup_readonly_user.sql CREATE USER website_readonly WITH PASSWORD '...'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO website_readonly;
| Table | Written By | Read By |
|---|---|---|
| Bot | Website |
| Bot | Website |
| Bot | Website |
| Bot + Website | Both |
| Bot | Website |
# Running in screen session "website" screen -r website # Attach # Ctrl+A D to detach
cd /home/samba/share/slomix_discord/website source venv/bin/activate uvicorn backend.main:app --host 0.0.0.0 --port 8000
sudo cp etlegacy-website.service /etc/systemd/system/ sudo systemctl enable etlegacy-website sudo systemctl start etlegacy-website
player_links tablewebsite/backend/routers/api.py$1, $2)curl http://localhost:8000/api/your-endpointindex.htmlwebsite/js/your-view.jsapp.jsescapeHtml() for user content# Test query directly PGPASSWORD='...' psql -h localhost -U website_readonly -d etlegacy \ -c "SELECT COUNT(*) FROM rounds;" # Check API response curl -s http://localhost:8000/api/status | jq
No direct integration needed. Data flow is automatic:
Shared data:
player_links table used by both for Discord ↔ player mapping.
| What | Where |
|---|---|
| Start website | or systemctl |
| API code | |
| Frontend views | |
| Config | |
| Logs | |
| Port | 8000 |
Status: Production-Ready