Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

397 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Baller Picks

NBA predictions and community pick sharing. The MVP includes live game browsing, winner picks, reactions, weekly and all-time rankings, profiles and history, preferences, private feedback, and installable app support. FEATURES.md records the feature scenarios and the implementation decisions for this stack.

Stack

  • Vite Plus (vp) for all commands, with Bun as its underlying package manager; strict TypeScript.
  • React + Vite Plus, with the stable React Compiler enabled through reactCompilerPreset and @rolldown/plugin-babel.
  • TanStack Router with generated, type-safe file routes and route code splitting.
  • Tailwind CSS v4 and shadcn/ui's Base UI components, neutral Nova preset.
  • TanStack Form with shared form contexts in src/lib/form.ts; Zod schemas work directly as validators through Standard Schema.
  • Self-hosted Convex, Clerk's current @clerk/react SDK, email/password + Google.
  • Vite Plus bundles Vite, Vitest, Oxlint (including React hooks, accessibility, and type-aware checks), and Oxfmt. Playwright covers desktop/mobile browsers.

Direct packages were resolved from their latest stable registry releases during setup. Exact versions and bun.lock make installs reproducible. Commit updated lockfiles when upgrading; vp update --latest explicitly checks newer releases. The native Rust React Compiler option is currently experimental, so this project uses the stable compiler documented by React.

Start locally

Install the Vite Plus global CLI. It manages Node.js and the pinned Bun 1.4.3 package manager for this project. All commands go through vp; you do not need to invoke Bun directly. Then:

vp install --frozen-lockfile
vp dev

Without service keys, the home page explains that services need connecting. No Clerk/Convex requests are made. Partial or invalid configuration is reported on screen. Production builds require both public values.

The existing demo backend is already recorded in .env.example. Local setup uses .env.local (ignored by Git); copy the example if that file does not exist:

Variable Purpose
VITE_CONVEX_URL Public HTTPS URL of your Convex API, port 3210 behind Coolify
VITE_CLERK_PUBLISHABLE_KEY Clerk development publishable key (pk_test_…)
CONVEX_SELF_HOSTED_URL Same API URL, used by the Convex CLI
CONVEX_SELF_HOSTED_ADMIN_KEY Private admin key generated by your backend

VITE_* values are public and embedded into the frontend at build time. Never prefix an admin key or Clerk secret key with VITE_. The browser does not need a Clerk secret key. Local environment files are ignored by Git and Docker.

In separate terminals, run:

vp run dev:backend
vp dev

The Convex dev process syncs functions to the self-hosted URL in .env.local. This project currently targets the existing demo backend below. Keep its admin key private. Choose separate backend and Clerk configuration before production.

Clerk development setup

The project is linked to Baller Picks (app_3KiTZ43CwiCIDgICysuHdtSOAHN). Its development issuer is https://blessed-cow-1983.clerk.accounts.dev. The local .env.local contains its publishable key and a private tooling key; the frontend receives only the publishable key.

Email/password, email verification at signup, and Google are enabled. clerk.config.json records these settings plus the session claim aud: "convex", email and email-verification status, name, avatar, and username. The audience claim lets ConvexProviderWithClerk use the normal Clerk session token; no separate JWT template is needed.

To connect a fresh checkout to the same development application:

vp dlx clerk@latest auth login
vp dlx clerk@latest link --app app_3KiTZ43CwiCIDgICysuHdtSOAHN
vp dlx clerk@latest env pull --file .env.local

To review or reapply the versioned Clerk settings:

vp dlx clerk@latest config patch --file clerk.config.json --dry-run
vp dlx clerk@latest config patch --file clerk.config.json --yes

Convex's deployment already has the matching issuer. To reproduce that part:

vp exec convex env set CLERK_JWT_ISSUER_DOMAIN https://blessed-cow-1983.clerk.accounts.dev
vp run dev:backend --once

The issuer belongs in Convex's deployment environment. Setting it only in Coolify's frontend build variables or Docker container environment does not configure Convex functions. Keep .env.local private; CLERK_SECRET_KEY is for local CLI/API tooling and is never sent to the frontend.

/sign-in and /sign-up use Clerk's prebuilt components with hash routing and the shadcn theme from @clerk/ui. /account sits under a protected TanStack layout and loads account data only after Convex confirms authentication. The users.viewer query independently checks the token on the backend; frontend guards are navigation convenience. Clerk's UserProfile provides account management. Signed-in sessions provision a Convex profile before entering the app. Clerk session claims carry verified email, display name, and avatar information; only the current user can access their email through users.viewer. Public feeds and rankings expose public profile fields only.

Real Clerk-issued development tokens are used to validate the backend integration; automated backend tests also exercise anonymous and unverified-user rejection. To check your own account, run vp run dev, open /sign-up, complete email verification or Google sign-in, and confirm that /profile shows your Baller Picks identity. The provider-managed email-verification and Google OAuth handoffs still need a check on the actual release domain.

For production, create Clerk's production instance, complete its domain/DNS setup, and configure your own Google OAuth credentials as instructed by Clerk. Use the production publishable key and issuer with the production Convex backend.

Connect the existing Convex instance

The demo backend is already deployed at:

baller-picks-demo

Its root responds with Convex's running-deployment message and /version responds successfully. This is the API URL, suitable for both VITE_CONVEX_URL and CONVEX_SELF_HOSTED_URL. No backend Compose files or new Convex services are needed in this repository.

  1. Open the running Convex backend container's terminal in Coolify and run:

    ./generate_admin_key.sh
  2. Save the generated key as CONVEX_SELF_HOSTED_ADMIN_KEY in .env.local. Both URL variables are already set to the demo API URL. Never put this admin key in a VITE_* variable or commit it.

  3. Sync functions and generated API types once:

    vp run dev:backend --once

    For ongoing development, run vp run dev:backend to watch and sync changes. No Convex Cloud account or convex login is required for this self-hosted configuration. Do not set CONVEX_DEPLOYMENT or CONVEX_DEPLOY_KEY alongside these self-hosted credentials.

The demo backend is already configured to trust the Clerk development issuer above. Preserve CLERK_JWT_ISSUER_DOMAIN when redeploying it.

For later deployments, the CLI's target is whichever self-hosted URL/admin key is in the selected private environment file:

vp run convex:deploy --env-file /path/to/private-environment.env

Frontend builds do not deploy backend functions or need admin keys. Deploy functions explicitly before frontend changes that require them.

Frontend on Coolify (Railpack)

Create a Git-backed application and select Railpack as its build pack. Railpack reads Bun 1.4.3 from packageManager in package.json, installs dependencies with its Bun provider, and runs the package build script, which uses Vite Plus. These are internal Railpack steps; local and CI commands use vp. It automatically detects the Vite SPA and its dist output. No railpack.json, Nixpacks configuration, or Dockerfile is needed. Update packageManager when changing the Bun version. Keep .dockerignore: Railpack uses it to exclude local credentials and artifacts from the build context.

Coolify currently labels Railpack Beta. Docker Buildx must be installed on the build server and in Coolify's helper container.

Configure the application in Coolify:

Setting Value
Build pack / build strategy Railpack
Base directory /
Site type SPA (single-page application)
Publish directory /dist
Container port 80
Health-check path /
Install, build, start command overrides Leave empty; use automatic detection

Choose Static site output during creation if the SPA option appears only after creation, then set Site type to SPA under General > Build pipeline. Use the Railpack build pack with SPA output; the separate Static build pack only packages prebuilt files and does not compile this repository's source.

Coolify serves the compiled files with its managed Nginx image. The SPA fallback must send /account, /sign-in, and /sign-up to index.html. Keep the generated SPA configuration. Coolify copies /dist into its Nginx image for SPA output; Railpack's standalone Caddy server is not the frontend runtime in this setup.

Add these two environment variables and mark them Available during build:

Variable Value
VITE_CONVEX_URL https://baller-picks-demo-tij89ve1ep5dno7avstucptn.lab.thestu.xyz for the demo
VITE_CLERK_PUBLISHABLE_KEY The matching Clerk publishable key from .env.local / Clerk API Keys

Both values are public and embedded during compilation; changing them requires rebuilding. Do not add CLERK_SECRET_KEY or CONVEX_SELF_HOSTED_ADMIN_KEY to the frontend deployment. The already deployed Convex backend is managed separately. Use the demo Clerk development key for a demo deployment; use matching production Clerk and Convex values when provisioning production.

Assign the frontend domain, deploy, and inspect the build log for Bun 1.4.3, a frozen dependency install, and the Vite Plus build script. Clear older Coolify command overrides and NIXPACKS_* variables when switching an existing application. Verify that refreshing /account loads the app, assets load successfully, and signup/sign-in completes on that domain. The Railpack plan and build commands can be checked locally. Run the actual image build and routing checks on the Coolify server when Docker is unavailable locally.

Progressive web app

Production builds include a stable root-scoped manifest, standalone home-screen launch, Apple touch icon, 192/512px icons, and a separate maskable icon. The geometric basketball mark is authored in public/icons/icon.svg; run vp run pwa:assets after changing it to regenerate the PNGs and copy the bundled Geist font for the offline page. The SVG is the source for all four PNGs; no external raster artwork is used. Its mark fits inside the maskable safe zone.

Open Install & updates at the bottom of any screen for installation help or manual update checks. Supported Chromium browsers offer their actual installer; iPhone/iPad users get Share > Add to Home Screen instructions. Installed windows show App updates instead. Installation is optional and never auto-prompts.

A new frontend build downloads in the background. When it is fully installed, the app shows An app update is ready, with Later and Update now. Update checks run on launch, reconnection, return to a visible/focused window (at most once a minute), and every five minutes while visible. Update now activates the waiting worker and reloads that window at its current URL. Other open windows get their own reload prompt and keep their in-progress forms. Later keeps an update action in the footer; the banner returns when the user resumes the app or a newer update is found. There are no forced reloads.

Only public build assets are precached. Clerk, Convex, API responses, and the main HTML document are never cached by the worker. Navigation requires the network and has a ten-second timeout. An offline or unavailable server opens a small cached reconnect page at the original URL, independent of authentication scripts; it retries that URL when connectivity returns. The live page shows an offline notice without unmounting forms. Accounts and live data require a connection. Known-offline writes are blocked before submission. A request already sent while online may retry through Convex after a connection drop; pick writes still recheck the deadline on the server. First-ever offline visits and browser-evicted storage cannot show the fallback until the worker has successfully installed online.

Hosting and release blind spots

  • HTTPS and cache headers: deploy the whole dist atomically over HTTPS at the origin root. Merge deploy/nginx-pwa.conf into Coolify's existing Nginx server block, retaining its SPA fallback. Do not cache /sw.js, /index.html, or /manifest.webmanifest in the CDN/proxy. A missing worker/chunk must return 404, not HTML. Repository files cannot configure the live Coolify proxy by themselves; confirm the actual response headers after deployment.
  • Old windows: keep the previous release's hashed /assets/ files available when possible, so a window that defers an update can still load an old lazy route after another window updates. The app also offers an update action when a stale route chunk fails. Do not make breaking backend changes while older frontends may still be open; deploy compatible backend changes first.
  • Phone checks: verify Add to Home Screen, safe areas, keyboard scrolling, cold/warm launches, and Clerk email/Google sign-in from an installed iPhone and Android app on the actual HTTPS domain. Browser emulation does not exercise OS installation, Safari's storage policies, or OAuth handoffs. App identity, icon, and manifest metadata updates follow browser/OS rules and may not update immediately when the app content updates.
  • Storage and features: the browser may evict cached assets; downloads can fail due to connectivity or storage pressure and are retryable. Push alerts, background sync, offline picks, and persisted drafts require separate product behavior and backend support; they are not implied by installation.

Run vp run test:pwa for production-built worker tests on desktop and a mobile Chromium viewport. These exercise real registration/activation, two-window update consent, offline deep links and recovery, failure/retry, installation controls, and cache boundaries. The test server simulates release changes only in test fixtures; it does not add deployment endpoints to the application. The explicit e2e build mode permits an unconfigured shell so tests do not contact live Clerk/Convex; ordinary production builds still require both service values.

The update flow follows the browser's waiting-worker lifecycle, and installation uses the browser's install prompt when available.

MVP data and operation

  • Public routes: /games, /community, /rankings (weekly/all-time). Signed-in / opens the community feed; signed-out / is the welcome screen.
  • Personal routes: /matchups, /profile, /profile/:username, /profile/edit, /settings, /feedback, and Clerk's /account.
  • Convex tables: users, games, picks, reactions, weeklyStats, feedback, and syncState. Profile email and authentication subject stay out of public feeds/rankings. Feedback has no public read endpoint.
  • Picks lock at scheduled tipoff or an unavailable game status. Transactions enforce ownership, one pick per user/game, valid teams, and a 250-character comment limit. Repeated result imports do not double-count records; final-score corrections adjust both all-time and weekly statistics.
  • convex/crons.ts refreshes the scoreboard every minute and the schedule every six hours. NBA's official endpoints are attempted first. ESPN is a fallback because both official feeds return HTTP 403 from the current demo host. ESPN schedule queries use calendar months covering approximately 30 days back through 180 days ahead; scoreboard queries cover yesterday and today. Date ranges are intentionally not used because that endpoint rejects them.
  • Source timestamps, provider names, and failures are available in games.list and syncState. Failures retain saved games and show recovery copy. Built-in historical JSON files are parser test fixtures only.
  • Queries currently cap rankings and a displayed history/matchup list at 100, Home at 20, and the latest carousel at 10. This is an MVP, not a load-tested large-community service; database scans/settlement batches should be revisited before growing substantially beyond the initial audience.

After deploying a new backend, import real data immediately:

vp run dev:backend --once
vp exec convex run nba:sync '{"source":"schedule"}'
vp exec convex run nba:sync '{"source":"scoreboard"}'

Clerk profile-image uploads remain with Clerk; the app validates PNG/JPEG/GIF/WebP and 5 MB, previews locally, and stores the resulting image URL in Convex. Account security and deletion remain provider-managed. Removing a Clerk account does not automatically delete its community data: an operator can remove that exact member's Convex profile, picks, reactions, weekly records, and feedback using the admin-only maintenance:removeMember function with the Clerk subject. Reconcile account deletion operationally until a verified Clerk deletion webhook is added. Never expose the Convex admin key or this maintenance action to the browser.

FEATURES.md lists the intentional stack adaptations and deferred fictional currency rules. This checkout uses the existing demo services; production Clerk/domain configuration and real-phone release checks are still distinct from local implementation validation.

Development commands

vp run check is the complete project check (route generation, static checks, and unit tests); vp check is the built-in static checker. vp run build adds route generation and type checking before vp build. Use vp dev for the dev server, vp add <package> for dependencies, and vp dlx for one-off tools.

Tooling settings live in vite.config.ts: build plugins, unit tests, lint rules, formatting, and staged checks. TypeScript project configs remain because they specify the different browser, tooling, and Convex environments. The vite dependency is an alias to Vite Plus core for plugin and hosting compatibility.

vp install automatically installs the Vite Plus Git hooks. Each commit runs route generation, fixes formatting/lint issues in staged files, then checks types across the project (including Convex). Failed checks block the commit. Review any automatic fixes; partially staged edits are protected by the staged runner. Run vp hooks status to inspect the installation. CI also runs checks, unit tests, and an isolated build.

vp run check             # lint, formatting, types, unit tests; no service keys needed
vp run build             # production build; requires both public values
vp run test:e2e           # desktop + mobile smoke tests, no service keys needed
vp run test:live          # explicit demo Clerk/Convex integration smoke test
vp run format            # format project files
vp run test:watch
vp run convex:codegen    # regenerate API types against the configured backend
vp dlx shadcn@latest add input field

Run vp exec playwright install chromium once before browser tests. Browser tests start an isolated Vite server and explicitly clear service values. They cover the unconfigured page, protected-route handling, unknown deep links, client errors, and mobile overflow; they do not simulate successful authentication.

Commit src/routeTree.gen.ts, convex/_generated/, and bun.lock. Do not edit generated code manually. vp run typecheck generates routes before checking; convex dev generates the backend API. src/lib/form.ts is ready for shared TanStack forms; add real field components and Zod schemas when building features. No separate client state/query library is needed for Convex's reactive queries.

The explicit test:live suite uses the configured development Clerk and demo Convex services. It creates a temporary verified Clerk test user, exercises a real future game and profile/preferences/feedback, then removes only that user's Clerk account and Convex data. It needs the private tooling/admin variables in the environment and never prints credentials. It starts its own frontend on port 4175. Screenshots contain synthetic test-account data; browser traces and videos are disabled. Ordinary check, test:e2e, and test:pwa do not write to live services.

Official setup references

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages