Matthew Gisi

Astro 5 React Drizzle ORM PostgreSQL Docker Cloudflare Tunnel

Team Pakistan
Online

Overwatch World Cup · Built independently

Official website for the Overwatch World Cup Team Pakistan organization. Roster, schedule, news, VODs, merch, and a fan community forum — all managed through a private admin dashboard. Bilingual in English and Urdu with RTL support. Self-hosted on a homelab via Docker, Caddy, and Cloudflare Tunnel.

Built for
the whole org.

🌍

Fan-First Platform

A central hub for fans, players, and staff. Roster profiles, match schedule, news coverage, VOD library, merch, and a community forum — all in one place. Bilingual in English and Urdu with full RTL support for Pakistan's national language, using the Noto Nastaliq Urdu font.

🛠️

Admin-Driven Content

Team admins and committee members control all content through a private dashboard at admin.teampakistan.online — roster updates, news posts, VOD uploads, schedule entries, merch listings, and site-wide announcements — with no code deploys required.

🏠

Fully Self-Hosted

The entire stack — app, database, and media — runs on a homelab server inside Docker Compose. Cloudflare Tunnel routes production traffic without exposing any inbound ports. Media is served from a self-managed library rather than external image hosts.

~20 pages
for fans & players.

Homepage

Hero & Feed

Announcement ticker, featured news and VODs, upcoming schedule — a live pulse of the team surfaced immediately on load.

Roster

Player Profiles

Full roster with group filter tabs (Players, Committee, Coaches, Staff, Creators). Each player gets a dedicated profile page: bio, hero pool, socials, fun facts, and Liquipedia link.

VODs

VOD Library

Match recordings, highlights, scrims, and interviews filterable by type. Thumbnails and bilingual titles managed from the admin dashboard.

Community

Fan Forum

Fan-posted content with a flair system, upvotes, and threaded comments. Verified members get elevated trust. Reports feed directly into the admin moderation queue.

Schedule

Match Schedule

Upcoming matches and events with opponent info and external links. Admin can set override entries to surface one-off events alongside calendar-synced matches.

Merch

Merchandise

Merchandise listings with bilingual names and descriptions, pricing, purchase URL, stock status, sizes, colors, and custom badge labels managed entirely from the admin panel.

News

News & Coverage

Team news and external coverage aggregation. Pinnable articles, platform tags, full bilingual content, and external URL passthrough for linking to third-party coverage.

Contact

Contact & Inquiry

Three form types: general contact, Ask a Player, and sponsor inquiries. All submissions are logged in the database and push a notification to the team's devices via a self-hosted ntfy instance.

Full control,
no code deploys.

The private admin area at admin.teampakistan.online is gated by subdomain and role check. Every piece of site content is manageable here — day-to-day operations require zero engineering involvement.

Roster

Add / edit / delete players; handle, real name, role, pronouns, group, bilingual bio, hero pool, socials, fun facts, image, Liquipedia URL, sort order, published toggle.

VODs

Add / edit / delete VODs; bilingual title, YouTube URL, thumbnail, type (match / highlight / scrim / interview), tags, featured flag, published toggle.

News

Full bilingual articles, external URL, platform tag, pin and publish controls. Supports both original articles and external coverage aggregation.

Schedule

Add / edit / delete events; date/time, opponent, external URL, override flag for manually injected one-off entries.

Merch

Pricing, purchase URL, sizes, colors, customization options, stock status badge, bilingual names and descriptions.

Media

Upload images to server storage; organize into named folders; copy full URL for use in other admin fields; rename and delete; search by filename.

Community

View and moderate posts; pin, lock, or delete content; review flagged reports from the community report queue.

Users

View all users; promote or demote roles (superadmin → admin → verified_member → user); ban and unban with reason tracking.

Announcements

Create and toggle site-wide announcement banners shown in the ticker on the homepage.

Settings

Site-wide configuration via a key/value JSONB store — no hardcoded config values required in the codebase.

Self-hosted,
zero open ports.

Docker Compose

Containerized Stack

Two containers: teampk-app (Astro Node.js server) and teampk-db (PostgreSQL 16 Alpine). Both share an external proxy Docker network with the rest of the homelab so Caddy can route traffic to them. Named volumes persist the database and all uploaded media across container rebuilds.

Caddy

Reverse Proxy & Subdomain Routing

Caddy handles HTTPS termination and routes two domains to the same container: teampakistan.online for the public site and admin.teampakistan.online for the admin dashboard. Caddy injects an X-Admin-Subdomain: true header on admin traffic — the app reads this header to gate the admin role check without requiring a second deployment.

Cloudflare Tunnel

No Exposed Ports

Cloudflare Tunnel sits in front of Caddy. The homelab initiates an outbound connection to Cloudflare's network — no inbound firewall rules, no exposed home IP. All traffic flows through Cloudflare, providing DDoS protection, HTTPS everywhere, and a stable public address regardless of the server's dynamic IP.

Media Library

Self-Hosted Asset Storage

Admins upload images directly to the server via the Media admin section. Files land on a named Docker volume at /app/uploads/<folder>/<uuid>.ext and are tracked in the media_items table. A custom Astro route serves them with MIME detection, path traversal protection, and immutable cache headers. No external image hosts.

Auth

better-auth + OAuth

better-auth handles sessions and OAuth with two providers — Google and Discord. Four roles: superadmin, admin, verified_member, and user. Superadmin emails are defined in an environment variable; matching accounts are auto-promoted on first sign-in.

Notifications

Self-Hosted Push via ntfy

Contact form submissions and community reports push real-time notifications to the team's devices through a self-hosted ntfy instance on the homelab. Configured via NTFY_URL and NTFY_TOPIC environment variables — no third-party notification services required.

Modern tools,
practical choices.

🚀

Astro 5 SSR

Server-rendered pages via the Node.js standalone adapter. React islands used only where client interactivity is needed — login forms, filter tabs, community voting. Most pages are pure server HTML for SEO and fast initial loads.

🎨

Tailwind CSS

Custom design token palette built around the Pakistan green brand color. Dark esports aesthetic with RadianceBeverage (Overwatch's display font) for headings and TASA Orbiter for body text.

🗃️

Drizzle ORM + PostgreSQL

Schema-as-code with type-safe queries. 15+ tables covering auth, roster, VODs, news, schedule, merch, community posts/comments/votes, media, and site settings. All managed by Drizzle migrations.

🔑

better-auth

Modern auth library handling Google and Discord OAuth, session management, and token lifecycle. Role-based access with four tiers enforced server-side on every request. No rolling custom session logic.

🌐

Bilingual EN / UR

All major content types carry parallel English and Urdu fields in the database. Urdu text areas use dir="rtl" throughout the admin forms. Rendered with the Noto Nastaliq Urdu font via Google Fonts CDN.

🐳

Docker + Caddy

Full stack runs as a Docker Compose deployment on private hardware. Caddy handles reverse proxying, HTTPS, and subdomain-based admin gating. Cloudflare Tunnel exposes the homelab without opening inbound ports.

Astro 5ReactTypeScriptTailwind CSSDrizzle ORMPostgreSQLbetter-authGoogle OAuthDiscord OAuthDockerDocker ComposeCaddyCloudflare TunnelSelf-HostingRole-Based Access ControlBilingual UIRTL SupportSelf-Hosted MediantfySystems DesignFull-Stack DevelopmentAdmin Dashboard