14 KiB
🛠️ PinPoint Golf — Developer & Architecture Guide
This guide is written for engineers looking to maintain, customize, or extend PinPoint Golf. It explains the full-stack architecture, user authentication flows, OAuth/SSO integration, database persistence, community benchmark mathematics, and outlines the prioritized roadmap of future features.
1. Project Directory Structure
.
├── server.ts # Full-stack Express server (API endpoints + Vite middleware / static files)
├── Dockerfile # Multi-stage production container build (Node.js -> Node.js Runner)
├── docker-compose.yaml # Compose file with persistent volume and OAuth environment vars
├── .env.example # Template for Google Maps, JWT, Google OAuth, and Authentik OIDC
├── index.html # HTML entry point with Plus Jakarta Sans & JetBrains Mono
├── package.json # Dependencies, esbuild scripts, and npm scripts
├── vite.config.ts # Vite 8 configuration with Tailwind v4 & path aliases
├── data/
│ └── pinpoint_db.json # Atomic JSON persistent database (users, rounds, settings, courses)
├── src/
│ ├── main.tsx # React root mount point
│ ├── App.tsx # Root coordinator, global state, persistence, sync & modal hubs
│ ├── index.css # Tailwind CSS v4 setup & custom scrollbars
│ ├── context/
│ │ └── AuthContext.tsx # User session state, JWT tokens, login, register, and SSO hooks
│ ├── types/
│ │ ├── golf.ts # Core golf interfaces (Hole, Course, Round, Stats, GPS)
│ │ └── auth.ts # User profiles, PublicGolferStats, CommunityComparison schemas
│ ├── data/
│ │ ├── courses.ts # Default course presets (Pebble Cove, Pine Valley, St Andrews)
│ │ └── sampleRounds.ts # Preloaded 18-hole demonstration round with full stats
│ ├── services/
│ │ ├── api.ts # Full client HTTP client with Bearer auth headers
│ │ ├── golfApi.ts # OpenGolfAPI client for searching 32,000+ public courses
│ │ └── weatherService.ts # Open-Meteo Open Weather API client & GPS geolocation service
│ ├── server/
│ │ ├── db.ts # Database manager (atomic file writes, seed golfers, user scoping)
│ │ ├── authMiddleware.ts # JWT verification & request user injection
│ │ └── routes/
│ │ ├── authRoutes.ts # Local register/login, user profile, settings, and SSO endpoints
│ │ ├── roundsRoutes.ts # Scoped rounds CRUD, sync-batch, and custom course endpoints
│ │ └── communityRoutes.ts # Global public leaderboard and comparative benchmark engine
│ ├── utils/
│ │ ├── golfStats.ts # Pure statistical calculation engine & scoring formulas
│ │ ├── geoUtils.ts # Haversine distance, bearings, green edges & hole auto-detect
│ │ └── achievements.ts # Automated trophy & achievements evaluator (23 achievements)
│ └── components/
│ ├── Navbar.tsx # 3-Zone top bar + user avatar profile dropdown + mobile tab bar
│ ├── AuthModal.tsx # Tabbed modal: Local Auth (Email/Pass), Register, and SSO/Authentik
│ ├── CommunityComparisonModal.tsx # Head-to-head comparison cards & global public leaderboard
│ ├── HoleEntry.tsx # Tactile on-course scoring UI with large touch hitboxes
│ ├── GolfGpsRangefinder.tsx # Google Maps satellite map + Front/Mid/Back digital HUD
│ ├── Scorecard.tsx # Paper-style 18-hole digital scorecard with Out/In splits
│ ├── Analytics.tsx # Performance dashboard (FIR radar, GIR misses, bunker saves)
│ ├── Settings.tsx # Granular stat switchboard & 1-click tracking presets
│ ├── NewRoundModal.tsx # Start round dialog with OpenGolfAPI search & tees
│ ├── CourseEditorModal.tsx # Builder to create new courses or duplicate existing ones
│ ├── RoundsHistoryModal.tsx # Round history, round switching, and CSV/text export
│ └── AchievementsModal.tsx # Trophy cabinet with tier filters and progress tracking
2. Authentication & Identity Architecture
PinPoint Golf supports dual-mode authentication:
- Local Authentication:
- Passwords hashed with
bcryptjs(salt rounds: 10). - Generates signed JWTs (30-day expiry) containing user
id,email, andusername. - Passwords and sensitive fields are stripped before transmitting user objects.
- Passwords hashed with
- OAuth & SSO Providers:
- Google OAuth 2.0: Compatible with Google Identity Services. Configurable via
GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRET. - Authentik (OIDC): Designed for homelab setups, enterprise Identity Providers, and Keycloak. Configurable via
AUTHENTIK_ISSUER_URL,AUTHENTIK_CLIENT_ID, andAUTHENTIK_CLIENT_SECRET. - GitHub OAuth: Configurable via
GITHUB_CLIENT_IDandGITHUB_CLIENT_SECRET. - Zero-Friction Test Mode: If external secrets are not yet provisioned in
.env, the modal allows one-click instant testing using authentic golfer SSO credentials.
- Google OAuth 2.0: Compatible with Google Identity Services. Configurable via
API Auth Endpoints:
| Endpoint | Method | Auth Required | Description |
|---|---|---|---|
/api/auth/register |
POST | No | Create local account with email, username, password, handicap |
/api/auth/login |
POST | No | Authenticate with email/username + password |
/api/auth/me |
GET | Bearer | Retrieve profile and synchronized tracking settings |
/api/auth/profile |
PUT | Bearer | Update name, handicap index, home club, avatar, or isPublic flag |
/api/auth/settings |
GET/PUT | Bearer | Read or persist user tracking switchboard preferences |
/api/auth/oauth/providers |
GET | No | Discovery endpoint for configured OAuth options |
/api/auth/oauth/sso-login |
POST | No | Ingest OAuth token / SSO identity and return JWT |
3. Persistent Database (data/pinpoint_db.json)
The datastore in src/server/db.ts uses atomic temp-file write-and-rename semantics (fs.renameSync) to ensure durability against crashes. In Docker, it mounts to the named volume pinpoint_data:/app/data.
Database Schema:
interface DatabaseSchema {
users: Array<User & { passwordHash?: string }>;
settings: Record<string, TrackingSettings>; // userId -> settings
rounds: Record<string, Round[]>; // userId -> rounds
customCourses: Record<string, Course[]>; // userId -> courses
publicSeedGolfers: PublicGolferStats[]; // Seed public players for comparison
}
4. Community Benchmark & Public Comparison Engine
Users can toggle their profile between Public (default) and Private via the profile dropdown or the Community modal:
- When
isPublic: true, their rounds automatically compute into the global database metrics:- Scoring Average (18-hole stroke average)
- FIR % (Fairways in Regulation)
- GIR % (Greens in Regulation)
- Putts Per Round (PPR)
- Scramble % (Up & Down par saves after missing GIR)
- Sand Save % (Greenside bunker par conversion)
- Penalties Per Round (Strokes lost to hazards/OB)
- The
/api/community/compareendpoint calculates:- Global community averages across all public scorecards.
- Handicap bracket averages: Scratch (< 5.0), Mid-Cap (5.0–15.0), and High-Cap (> 15.0).
- Percentile rankings (0–100%) for each metric.
- Automated coaching insights identifying the golfer's highest-leverage scoring improvements.
5. Real-Time Weather Forecast Integration (Open Weather API)
Before teeing off, golfers evaluate live environmental conditions—temperature, wind velocity, heading, and precipitation probability—to select appropriate clubs, prepare rain equipment, and adjust for air density effects on ball carry.
Architecture & Service Design (src/services/weatherService.ts)
- Open Weather API Provider: Uses the Open-Meteo Meteorological API (
https://api.open-meteo.com/v1/forecast), an open-source, non-commercial and commercial-ready meteorological service operated with Open Database License (ODbL). - Zero API-Key Requirement: Works out-of-the-box with zero paid subscription dependencies or API keys.
- Dual Geolocation Resolution Strategy:
- Active Device GPS Position: Calls browser
navigator.geolocation.getCurrentPositionwithenableHighAccuracy: trueto get the player's immediate physical coordinates. - Course Location Fallback: If the player is planning a round indoors, has GPS disabled, or denies browser permission, the system automatically falls back to coordinates from the selected course's Hole 1 tee box or course center (e.g. Pebble Beach, Pine Valley, St Andrews).
- Seamless Toggle: Users can switch between
[ 📍 My GPS ]and[ ⛳ Course GPS ]at any time with a 1-click refresh button.
- Active Device GPS Position: Calls browser
Real-Time Weather Metrics Captured
| Metric | Unit | API Parameter | Relevance to Golf |
|---|---|---|---|
| Current Temperature | °C (with °F converted) | current.temperature_2m |
Ball compression and air density (colder air = less carry distance) |
| Feels-Like Temperature | °C | current.apparent_temperature |
Golfer comfort, layering, and thermal prep |
| Wind Speed | km/h | current.wind_speed_10m |
Club adjustment (rule of thumb: ~1 club per 15 km/h wind) |
| Wind Direction & Heading | 16-point Compass (SW) + Degrees (0–360°) |
current.wind_direction_10m |
Aim lines and shot shaping (crosswind, headwind, tailwind) |
| Wind Intensity Category | Calm / Breezy / Strong / Gusty |
Auto-classified based on speed | Stored directly in Round summary for fast filtering |
| Precipitation Probability | % (0–100) | hourly.precipitation_probability |
Rain risk assessment and green speed (wet greens roll slower) |
| Weather Condition & WMO Code | WMO 4677 code | current.weather_code |
Mapped to UI icons (Sun, CloudSun, CloudRain, CloudLightning) |
| Relative Humidity | % | current.relative_humidity_2m |
Atmospheric moisture and swing comfort |
Scorecard Persistence & Data Flow
When a player starts a new round via NewRoundModal:
- The forecast is automatically preloaded via GPS.
- The user can review the live weather card and make optional manual adjustments or notes.
- Upon clicking Tee Off, the full
WeatherForecastobject is attached toround.weatherData, and summary strings (weatherandwind) are recorded. - Saved rounds in
Scorecard,RoundsHistoryModal, and exports reflect the actual weather encountered during the round.
6. How-To Guides for Extending the App
Guide A: Adding a New Tracked Stat (e.g. Approach Shot Club)
- Update Data Types (
src/types/golf.ts): AddapproachClub?: stringtoHoleScoreandtrackApproachClub: booleantoTrackingSettings. - Update Settings Defaults (
src/data/courses.ts): Set default inDEFAULT_TRACKING_SETTINGS. - Add Toggle Switch (
src/components/Settings.tsx): Add toggle under the relevant category. - Add Input Controls (
src/components/HoleEntry.tsx): Render input whensettings.trackApproachClubis active. - Scorecard & Analytics (
Scorecard.tsx&golfStats.ts): Display row in scorecard and aggregate in round stats.
Guide B: Adding a New Achievement / Trophy
- Add definition in
ALL_ACHIEVEMENTSinsrc/utils/achievements.ts. - Add rule evaluation in
evaluateAchievements(). - Add icon mapping in
src/components/AchievementsModal.tsx.
7. 🎯 Wanted / Future Features Roadmap
Here is the prioritized list of planned future enhancements:
High Priority
- Strokes Gained (SG) Analytics Engine:
- Compare shot performance against PGA Tour and scratch golfer baselines.
- Categorize strokes gained into SG: Off-the-Tee (OTT), SG: Approach, SG: Around-the-Green (ARG), and SG: Putting.
- Official World Handicap System (WHS) Calculator:
- Automatic calculation of Handicap Differential:
\text{Differential} = \frac{113}{\text{Slope}} \times (\text{Adjusted Gross Score} - \text{Course Rating} - \text{PCC}). - Automatic 20-round scoring record maintaining the official index from the best 8 differentials.
- Automatic calculation of Handicap Differential:
- Multiplayer Live Round & Foursome Matchplay:
- Real-time WebSockets synchronization so groups playing together can view live leaderboards, skins, match play, and stableford games hole-by-hole.
Medium Priority
- AI Caddie & Shot Recommender:
- Based on user's logged club distances, live GPS yardage to the pin, wind speed, and elevation, automatically suggest club selection and target aim line.
- Ball Flight & Dispersion Heatmaps on GPS Green Overlays:
- Plot approach shot landing spots visually on the satellite map green to detect left/right slice or pull patterns.
- Progressive Web App (PWA) Offline Service Worker:
- Installable on iOS/Android home screens with offline caching for courses with no cellular reception.
- Apple Watch / Wear OS Companion App:
- High-contrast wrist interface showing Bushnell-style Front/Center/Back yardages and quick 1-tap score increments.
Community & Social
- Golf Society & Tournament Director Mode:
- Flight divisions (A/B/C), net and gross prizes, two-man scrambles, and printable tournament scorecards.
- Course Review & Condition Notes:
- Golfer feedback on green speeds (Stimp reading), aerated greens notices, and course ratings.
8. Scripts Reference
# Start full-stack development server on port 3000
npm run dev
# Run TypeScript typechecks
npm run lint
# Build production bundle (client + server.cjs)
npm run build
# Clean temporary files
npm run clean