TFC
System Architecture

Toby FC U13s

Team management, live match control, real-time chat & season tracking โ€” Built by TheGroots Technology

18
Web Pages
17
Mobile Screens
55+
API Routes
22+
DB Tables
3
WebSockets
2
Weekly Scrapers
3
Platforms
~18K
Lines of Code
๐ŸŒ Live App ๐Ÿ“– Docs ๐Ÿ“ฑ Install Chooser iPhone QR ๐Ÿค– Android QR
Architecture Overview
๐Ÿ–ฅ๏ธ

Frontend โ€” Next.js 16

React 19 + Tailwind v4
Home
/
  • Feature cards (login-gated)
  • Welcome message + role title
  • Royal blue gradients
Fixtures
/fixtures
  • Table view with RSVP
  • Match/Training/Social tabs
  • Results tab with W/D/L
  • Admin: create/edit/cancel-with-reason
  • Cancelled events stay dimmed with reason badge
Team Chat
/chat
  • 3 channels: parents/players/staff
  • 1:1 Direct Messages (deterministic dm-{u1}-{u2} channel)
  • Real-time WebSocket
  • Typing indicators
  • Image & PDF attachments
  • Unread badges per channel + DM
  • Reply, forward, emoji reactions
Lineups
/lineups
  • Pitch diagram (5+ formations)
  • Drag & drop positioning
  • SVG shirt icons
  • Custom formation creator
Squad admin
/squad
  • Clickable player grid with avatars
  • Profile modal: stats + editable fields
  • GK: clean sheets + goals conceded
  • Outfield: goals + assists
  • Attendance: league / cup / overall, Thu / Sat training
  • Admin edit: name, shirt #, position, avatar, stat overrides
Live Match
/livematch
  • Real-time score updates
  • WebSocket timeline
Feed
/feed
  • Posts, comments, reactions
  • Auto match reports
  • Image / PDF attachments (auto-published from gallery uploads)
  • Tap-to-open lightbox
Stats
/stats
  • W/D/L, goals, cards
  • Player profiles
Gallery
/gallery
  • Open uploads โ€” any team member can post
  • Images, PDF, MP4, TXT, CSV (executables blocked)
  • Albums + lightbox viewer
  • Native share + send-to-DM / channel
  • Auto-publishes to Feed for reactions & comments
Season Summary
/season-summary
  • EOFL league table
  • Results & upcoming fixtures
  • Team stats overview
Manager Panel
/manage
  • 10 admin sections
  • Fixtures, lineup, live control
  • Squad (opens profile modal with stats + overrides)
  • "Sync from tobyfc.co.uk" trigger
  • Parents, coaches, staff, invites
  • Game reports + per-player Report Card (manual goals/assists/MOTM)
  • PDF export
Settings
/settings
  • Notification preferences
  • QR code mobile login
  • Password change
โ†“
โšก

Backend โ€” FastAPI (Python)

Async + WebSocket
Auth
/api/auth/*
  • JWT (HS256, 24h expiry)
  • bcrypt passwords
  • Forced password change
  • QR Login & Magic Links
  • Rate limited (10/min)
Teams
/api/teams/*
  • CRUD, invites, join
  • Member management
  • Parent-player links
  • GET /players/{user}/stats โ€” per-player season totals + attendance splits
  • PATCH /players/{user}/stats-override โ€” admin corrections (nullable, survive weekly scrape)
  • POST /sync-external โ€” trigger tobyfc.co.uk scrape
Events
/api/events/*
  • Fixtures CRUD
  • Recurring events (weekly)
  • RSVP system
  • Auto RSVP reminders (2 days)
  • Attendance grid
  • POST /{id}/cancel โ€” reason-gated soft cancel + notification
Match
/api/match/*
  • Live event recording
  • Auto score tracking
  • Match report generation โ€” upserts a single Feed post per match (live timeline + per-player report card merged)
  • GET/PATCH /{id}/appearances โ€” Player Report Card (admin manual entry of goals/assists/MOTM per match)
Chat
/api/chat/*
  • Messages + attachments + reactions
  • Read receipts & unread counts
  • DM channel validation โ€” sender โˆˆ {u1, u2}, both team members
  • GET /chat/{team}/dms โ€” DM thread list
Notifications
/api/notifications/*
  • In-app + WebSocket push
  • Email via Resend
  • User preferences
  • Helpers: notify_user (single user), notify_admins, notify_all_members
โ†“
๐Ÿ—„๏ธ

PostgreSQL + Hetzner Storage Box

22 Tables + 1TB SFTP
users
idUUID PK
emailunique
password_hashbcrypt
must_change_passwordbool
teams
idUUID PK
namestring
age_groupstring
team_members
roleadmin/staff/player/parent
titlee.g. "Manager"
statuspending/approved
shirt_numberint
events
typematch/training/social
match_typefriendly/league/cup/tournament
statusscheduled/live/completed/cancelled
cancellation_reasonstring
home_score / away_scoreint
match_events
typegoal/card/sub/kickoff/...
minuteint
detailJSONB
chat_messages
channelteam/players/staff/dm-{u1}-{u2}
server_seqbigint (ordered)
attachment_url / typestring
reactionsJSONB {emoji: [user_ids]}
feed_posts
categorygeneral/announcement/match_report/gallery
image_url / image_typeVARCHAR (gallery uploads)
linkdeep-link string
feed_comments / feed_reactions
post_idUUID FK
author / userUUID FK
content / reactiontext / string
notifications
typersvp/match_event/...
is_readbool
photos
urlstorage box path
captionstring
parent_player_links
parent_user_idUUID FK
player_user_idUUID FK
team_idUUID FK ยท unique tuple
player_season_stats
appearances / clean_sheets / goalsint (scraped)
yellow_cards / red_cardsint (scraped)
*_overridenullable int (admin)
overrides_updated_by / _atUUID / tstz
external_player_map
external_idtobyfc.co.uk idPlayer
external_namee.g. "Max G"
user_idUUID FK (nullable)
external_fixture_map
external_idtobyfc.co.uk idFixture
event_idUUID FK (nullable)
scraped_attstz
external_match_appearance
event_id + user_idcomposite PK
goals / assists / motmint per match
scraped_attstz
External Data Sync โ€” Weekly Scrapers
๐Ÿ”„

Two asyncio loops, Sunday evenings

Source of truth
FA Full-Time scrape
fulltime.thefa.com ยท Sundays 20:00 UTC
  • Pulls standings, results, fixtures
  • Fills home_score/away_score on matching Events
  • Derives match_type from FA division: 13B = league, anything else = cup
  • Serves the /season-summary page
tobyfc.co.uk scrape
tobyfc.co.uk ยท Sundays 20:30 UTC
  • Squad totals (P / CS / G / Y / R) + player photos
  • Results grid (paginated ASP.NET form)
  • Per-match lineups from MatchDetails.aspx
  • Auto-maps "Max G" โ†’ users.id by first-name + last-initial
  • Writes scraped values only; never touches *_override columns
  • Manual trigger: POST /api/teams/{id}/sync-external (admin)
Real-Time WebSocket Channels
๐Ÿ’ฌ Chat
/ws/chat/{team}/{channel}
  • JWT authenticated
  • Messages broadcast
  • Typing indicators
  • Delete broadcasts
โšฝ Match
/ws/match/{event_id}
  • JWT authenticated
  • Goals, cards, subs
  • Score updates
  • Status changes
๐Ÿ”” Notifications
/ws/notifications/{user_id}
  • JWT authenticated + user verified
  • Real-time bell push
  • RSVP, match events
  • New fixtures
Notification Architecture

Every significant action triggers notifications through multiple channels:

TriggerIn-App BellWebSocket PushEmail (Resend)Feed Post
New Fixture Createdโœ… All membersโœ… Real-timeโ€”โœ… Auto announcement (deep-linked)
Fixture Cancelledโœ… All membersโœ… Real-timeโ€”โ€”
RSVP Changeโœ… Adminsโœ… Real-timeโœ… Admin emailsโ€”
Goal Scoredโœ… All membersโœ… Real-timeโ€”โ€”
Card Issuedโœ… All membersโœ… Real-timeโ€”โ€”
Full Timeโœ… All membersโœ… Real-timeโ€”โœ… Match report (refreshes if Report Card edited later)
Gallery Uploadโœ… All except uploaderโœ… Real-timeโ€”โœ… Auto-feed post
Feed Commentโœ… Post author onlyโœ… Real-timeโ€”โ€”
Feed Reaction (like)โœ… Post author onlyโœ… Real-timeโ€”โ€”
Infrastructure & Security
Hetzner VPS
204.168.211.13
  • Ubuntu 24.04
  • nginx reverse proxy
  • Let's Encrypt SSL
  • PM2 process manager
Hetzner Storage Box
u565639.your-storagebox.de · 1TB SFTP
  • Photo storage
  • Chat attachments
  • Password auth
Cloudflare DNS
toby.statty.club
  • A record โ†’ VPS
  • DNS only (not proxied)
Resend
Email delivery
Security
๐Ÿ” Authentication
  • JWT with HS256 (64-byte secret)
  • bcrypt password hashing
  • 24-hour token expiry
  • Min 8-char passwords
  • QR Login & Magic Links
  • WebSocket JWT auth
๐Ÿ›ก๏ธ Firewall (UFW)
  • SSH (22), HTTP (80), HTTPS (443)
  • All other ports blocked
  • PostgreSQL not exposed
๐Ÿšซ fail2ban
  • SSH brute force protection
  • 3 max retries, 1hr ban
  • systemd journal backend
๐Ÿ”‘ SSH
  • Key-only authentication
  • Password auth disabled
  • Root login: prohibit-password
โšก Rate Limiting
  • Global: 120 req/min
  • Login: 10/min
  • Register: 5/min
  • 429 Too Many Requests
๐Ÿ”’ Security Headers
  • HSTS (1 year)
  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • CORS: restricted origins
  • Referrer-Policy: strict
Tech Stack
Frontend
  • Next.js 16 (standalone)
  • React 19
  • Tailwind CSS v4
  • Lucide React icons
Backend
  • FastAPI (Python 3.12)
  • SQLAlchemy (async)
  • asyncpg driver
  • Pydantic v2
  • httpx + BeautifulSoup (scrapers)
Database
  • PostgreSQL 16
  • 22 tables
  • UUID primary keys
  • JSONB for flexible data
  • Nullable *_override columns for admin corrections
Real-Time
  • FastAPI WebSocket
  • 3 WS endpoints
  • Ping/pong keepalive
Deployment
  • PM2 process manager
  • nginx reverse proxy
  • rsync-based deploys
  • Let's Encrypt auto-renew
Android App
  • React Native 0.84
  • Bottom tab navigation
  • Dark/Light mode
  • QR code login
  • 16 screens
  • APK distributed
iOS App
  • React Native 0.84 (shared codebase)
  • iPad + iPhone support
  • TestFlight distribution
  • Toby FC badge app icon