Skip to content

Repository files navigation

Mini Golf Score Tracker

Download on the App Store

An iOS + web app for tracking mini golf games: create a course, add any number of players, enter scores hole-by-hole, and celebrate the winner. Works offline during a round; wins persist per player across games.

Home screen Score tracking during a round Winner celebration

Stack

Layer Tech
iOS wrapper Capacitor 7
Web app React 18 + Vite (ui/)
Marketing site Next.js 15 (app-view/) → Netlify
API Flask + gunicorn (gthread, 4 threads) on Render (api/)
Auth JWT (PyJWT)
Database Turso (libsql) via a custom pure-Python HTTP client — see below
Monetization Google AdMob + SKAdNetwork

The Turso HTTP client story

The API originally used the official libsql Python package. It deadlocked under gunicorn's threaded worker — the embedded tokio runtime can't cleanly shut down when called across Python threads, and every request would 502 with a Rust panic in Render's logs:

thread 'tokio-runtime-worker' panicked: failed to join thread: Resource deadlock avoided (os error 35)

Rather than move to a sync worker or off Turso, I wrote a pure-Python DB-API 2.0 client for Turso's hrana /v2/pipeline HTTP protocol using only requests. It's now its own repo you can pip-install and drop in wherever sqlite3 used to sit.

Repo layout

api/          Flask API + database access layer (uses turso-http in prod, sqlite3 in dev)
ui/           React + Vite web app; also the Capacitor iOS project (ui/ios/)
app-view/     Next.js marketing site → minigolfscoretracker.com
app-ads.txt   AdMob authorized-sellers file (served from the marketing site)

Running locally

API (SQLite fallback, no Turso credentials required):

cd api
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python wsgi.py            # or: gunicorn wsgi:app

Web app:

cd ui
npm install
npm run dev               # http://localhost:5173

Marketing site:

cd app-view
npm install
npm run dev               # http://localhost:3000

iOS simulator (from ui/):

npm run build && npx cap sync ios
open ios/App/App.xcworkspace
# ⌘R in Xcode with a simulator selected

Deployment

  • api/ deploys to Render on push to main. The wsgi.py entry point is what gunicorn boots; Procfile / Render dashboard sets -k gthread -w 1 --threads 4 --timeout 120. Turso credentials live in Render env vars (TURSO_DATABASE_URL, TURSO_AUTH_TOKEN) — when they're absent, the API falls back to a local sqlite file so dev keeps working.
  • ui/ builds via Netlify → served at minigolfscoretracker.com and bundled into the Capacitor iOS build.
  • app-view/ (marketing) builds via Netlify → also served at minigolfscoretracker.com root routes.
  • iOS releases are cut from ui/ios/App in Xcode, uploaded to App Store Connect, and submitted for review.

License

MIT.

About

iOS + web mini-golf scorekeeping. Flask + gunicorn on Render, Turso storage, React + Vite web, Capacitor iOS.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages