Genji Parkour is a community website for Overwatch Genji parkour maps: leaderboards, powerful search, submissions & playtests, newsfeed, statistics/graphs, and a Convertor (OverPy → Workshop) with multilingual support. This repository hosts the Laravel 12 application that serves the UI (dark theme, Tailwind, Blade + per-page JS). The app is API-first by default and can optionally use a local database for specific modules.
- Leaderboard — XP, tiers, skill ranks, world records, maps made, playtest votes.
- Maps Search — rich filters (code, creator, name, difficulty, type, mechanics, restrictions, flags).
- Submit & Playtest — submit maps/records, playtest queue, difficulty pills, custom dropdowns.
- Newsfeed — announcements, new maps/records, guides, community picks, changelogs.
- Statistics — charts & insights (difficulty distribution, popularity, time played, rank distribution).
- Convertor (Beta) — OverPy → Workshop, translation helpers, map-data editor.
- Multi-language — EN/FR (extensible), language detection & user selection.
- Discord OAuth — login, navbar avatar, profile/notifications modals.
- Strict CSP —
csp_nonce()for scripts (including CDN), no inline styles. - API-first — UI reads from an external API secured. Local DB mode is optional.
- Backend: Laravel 12 (PHP ≥ 8.2), custom middlewares (language, user context, Sentry).
- Frontend: Blade, modular page-scoped JS, Tailwind CSS, Vite.
- i18n:
resources/lang/{en,fr}+ Convertor dictionaries. - Data: External API (default). Local DB is optional for persistence-heavy modules.
- PHP ≥ 8.2
- Composer 2
- Node ≥ 20 and npm
- SQLite / MySQL / PostgreSQL (only if you enable local DB mode)
git clone <your-repo-url> genji
cd genji
cp .env.example .env
composer install
npm install
php artisan key:generate
APP_NAME="Genji Parkour" APP_ENV=local APP_URL=http://genji.test APP_DEBUG=trueSESSION_DRIVER=file CACHE_STORE=file
API_ROOT="https://your.own.api/" # example base URL API_KEY="your-api-key"
DISCORD_CLIENT_ID="..." DISCORD_CLIENT_SECRET="..." DISCORD_REDIRECT="${APP_URL}/callback"
SENTRY_DSN="" SENTRY_ENV="${APP_ENV}"
FALLBACK_LOCALE=en
In API-first mode, the UI fetches data from the external API (leaderboard, maps, newsfeed, etc.). It’s ideal for quick deployments and avoids heavy SQL on the web server.
DB_CONNECTION=pgsql # or mysql/sqlite
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=genji
DB_USERNAME=genji
DB_PASSWORD=secret
SESSION_DRIVER=database
CACHE_STORE=database
Then run migrations (if present):
php artisan migrate --seed
You can combine API usage and a local DB if some modules need persistence. API-first remains the default for public pages.
# Dev (Vite + HMR) npm run dev
php artisan serve
Open: http://127.0.0.1:8000 (or APP_URL).
npm run build
php artisan optimize
- The project enforces a strict CSP. Always use the nonce from
csp_nonce()on scripts (including CDN imports):
@push('head')
@php($nonce = csp_nonce())
<script nonce="{{ $nonce }}" src="https://cdn.jsdelivr.net/gh/Zezombye/overpy@master/out/overpy_standalone.js" defer></script>
<script nonce="{{ $nonce }}" src="https://cdn.jsdelivr.net/npm/diff@5.1.0/dist/diff.min.js" defer></script>
@endpush
- Avoid inline styles; prefer Tailwind utility classes.
- Never commit secrets (API keys, Discord credentials, Sentry DSN, etc.).
- Core translations live in
resources/lang/{en,fr}. - Convertor & domain dictionaries live in
resources/translationsand feature files (e.g.,resources/lang/en/convertor.php). - Language is selected in the navbar; middleware aids detection and persistence via cookie.
| Page | Blade View | Page Script | Notes |
|---|---|---|---|
| Home | resources/views/index.blade.php | (landing logic / optional prism.js) | Hero + top maps |
| Leaderboard | resources/views/leaderboard.blade.php | resources/js/pages/leaderboard.js | filters, ranks, WR |
| Search / Maps | resources/views/search.blade.php | resources/js/pages/search.js | tabs: Search / Completions / Guides / PR |
| Submit & Playtest | resources/views/submit.blade.php | resources/js/pages/submit.js | custom dropdowns, difficulty pills |
| Statistics | resources/views/statistics.blade.php | resources/js/pages/statistics.js | charts & insights |
| Newsfeed | resources/views/newsfeed.blade.php | resources/js/pages/newsfeed.js | tags, community picks, changelogs |
| Dashboard | resources/views/dashboard.blade.php | resources/js/pages/dashboard.js | personal aggregation |
| Lootbox | resources/views/lootbox.blade.php | resources/js/pages/lootbox.js | effects, rarities, keys |
| Rank Card | resources/views/rank_card.blade.php | resources/js/pages/rank_card.js | skeleton + render |
| Convertor | resources/views/convertor.blade.php | resources/js/pages/convertor.js | OverPy → Workshop, translations |
The UI is API-first. Requests include an API-KEY header and use same-origin credentials when relevant.
async function api(path, opts = {}) {
const headers = {
Accept: 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'API-KEY': (import.meta.env && import.meta.env.VITE_API_KEY) ?? window.API_KEY,
...(opts.headers || {}),
};
return fetch(`${window.API_ROOT}${path}`, {
method: 'GET',
credentials: 'same-origin',
...opts,
headers,
}).then((r) => (r.ok ? r.json() : Promise.reject(r)));
}
For POST/PUT/DELETE from Blade pages, also include the CSRF header from the meta tag and the XSRF-TOKEN cookie (Laravel defaults).
Add PHPUnit/Pest for backend and Vitest/Playwright for UI as needed. For now, rely on npm run build, php artisan optimize, and visual verification of core pages.
- Code: MIT unless otherwise stated for specific assets.
- This project is not affiliated with Blizzard Entertainment. Overwatch™ and related marks are the property of their respective owners.
- Laravel and its community.
- Tailwind CSS.
- OverPy by Zezombye (used by the Convertor).
- All map creators, playtesters, and contributors of the Genji Parkour community.
Open an issue or discussion for bugs and feature requests. For sensitive matters (security, API keys), contact the maintainers privately.
- API-first by default: set
API_ROOT+API_KEY. Local DB is optional. - Start:
composer install→npm install→php artisan key:generate→npm run dev+php artisan serve. - CSP: always use the nonce and avoid inline styles.