Skip to content

Getting Started

Prerequisites

Tool Version Needed for
Go 1.25 (aim-backend/go.mod) backend
Node.js 20.x website, admin portal, Expo app
npm ships with Node 20 all three JS projects
Docker + Compose v2 syntax local data stores, prod stack
Expo CLI via npx expo (Expo SDK 54) mobile app only

Install and run locally

Backend (aim-backend)

cd aim-backend
docker compose up -d          # local postgres:16 (schema auto-applied), mongo:7, redis:7
cp .env.example .env          # fill in — see the configuration table below
make dev                      # builds ./cmd/api and runs it on :8080

Notes that will save you time:

  • The entrypoint is cmd/api/main.go — the main.go at the package root is not what the Makefile builds (CMD = ./cmd/api/).
  • ENTITY_ACCESS_TOKEN is required at boot: config.Load() calls log.Fatalf if it is unset, so the process exits immediately without it (config/config.go). Fantasy data comes from the Entity Sport API.
  • JWT_SECRET fails closed: with APP_ENV unset or production, a missing or weak secret refuses to start; only APP_ENV=development gets a dev fallback.
  • Firebase Admin SDK credentials are read from FIREBASE_SERVICE_ACCOUNT_PATH (default ./config/serviceAccountKey.json, gitignored).
  • Health: curl localhost:8080/health · Prometheus metrics at /metrics.

Seed test fantasy data (matches, series, contests) once the API's stores are up:

go run ./cmd/seed/            # 3 test matches; -n 5 for more, -clean to wipe

Useful targets: make build (stripped binary), make vet, make vendor.

Website (aim-store-website)

cd aim-store-website
npm install
npm run dev                   # concurrently: Vite dev server + Express payment server (:3001)

The Vite dev server proxies /api/* to the Express server on :3001 (vite.config.js). The payment server boots without gateway credentials — the storefront works, but checkout/top-up endpoints return 503 until the CCAVENUE_* / PHONEPE_* variables are set (server/index.js). npm run build builds the SPA; npm start serves the production bundle.

Admin portal (aim-store-admin)

cd aim-store-admin
npm install
# .env.local: NEXTAUTH_URL=http://localhost:3002, NEXTAUTH_SECRET=<random>,
#             ADMIN_API_INTERNAL_URL=http://localhost:8080
npm run dev                   # http://localhost:3002

The portal signs in through the backend's POST /api/v1/auth/admin/login (phone + password), so you need a Postgres users row with is_admin = true and a password_hash set — register a user via POST /api/v1/auth/register, then flip is_admin in SQL.

Mobile app (aim-store-app)

cd aim-store-app
npm install
npx expo start                # or: npm run android / npm run ios

Environment is selected by Expo from .env.development / .env.production (.env.staging must be loaded explicitly — see the comments in that file). Only EXPO_PUBLIC_* values belong there: they are inlined into the shipped bundle in plain text. The dev env deliberately points at the live HTTPS hosts because the OTP flow needs a real SMS provider; use a git-ignored .env.local to point at a local backend.

Production

Production is deploy-by-push: see Deployment. The stack lives on the VPS under /opt/aim-store/deploy; deploy/gen-env.sh generates a fresh .env on a new box, and deploy/README.md is the ops runbook (rollback, backups, migrations).

Configuration reference

Secrets

Never commit values. aim-backend/.env.example is the authoritative names-only template for the backend. Production values live only in /opt/aim-store/deploy/.env, /opt/aim-store/deploy/portal.env.production and /opt/aim-store/monitoring/.env on the server.

Backend (aim-backend/.env.example)

Variable Purpose Required
APP_ENV production (default) fails closed on weak JWT_SECRET no
APP_PORT API port no (default 8080)
POSTGRES_HOST / POSTGRES_PORT / POSTGRES_DB / POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_MAX_CONNS Postgres connection yes
MONGO_URI / MONGO_DB MongoDB (fantasy: series/matches/teams/predictions) yes
REDIS_ADDR / REDIS_PASSWORD Redis (OTP, rate limits, leaderboards) yes
JWT_SECRET custom-JWT signing key, ≥ 32 chars in production yes
FIREBASE_SERVICE_ACCOUNT_PATH Firebase Admin SDK credentials file yes
ENTITY_BASE_URL / ENTITY_ACCESS_TOKEN Entity Sport cricket data — process exits without the token yes
ROANUZ_API_KEY / ROANUZ_PROJECT_KEY optional secondary cricket provider no
PHONEPE_CLIENT_ID / PHONEPE_CLIENT_SECRET PhonePe status verification for top-ups top-up
STORE_WEBHOOK_SECRET HMAC secret shared with the website payment server (/wallet/topup-credit returns 503 without it) top-up
TOPUP_PAGE_BASE_URL hosted top-up page base (/wallet/topup-initiate returns 503 without it) top-up
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD / SUPPORT_EMAIL ticket notification email no
SMS_APIKEY / SMS_SENDER / SMS_TEMPLATE_ID / SMS_ROUTE / SMS_BASE_URL alots.in SMS gateway for OTP (pkg/sms) OTP auth
OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_SERVICE_NAME OTLP-HTTP trace export (Tempo) no

Website payment server (aim-store-website/server/index.js)

Variable Purpose Required
PORT Express server port no (default 3001)
NODE_ENV production serves the built SPA from dist/ no
AIM_API_URL backend base URL (prod: http://aim_api:8080) yes
APP_BASE_URL public origin for gateway redirects (prod: https://aimstoreorg.com) yes
AIM_APP_SCHEME mobile deep-link scheme back into the app no (default aimstore)
CCAVENUE_MERCHANT_ID / CCAVENUE_ACCESS_CODE / CCAVENUE_WORKING_KEY / CCAVENUE_URL CCAvenue gateway payments
PHONEPE_CLIENT_ID / PHONEPE_CLIENT_SECRET PhonePe gateway payments

Admin portal (portal.env.production on the server)

Variable Purpose Required
NEXTAUTH_URL portal canonical URL (https://admin.aimstoreorg.com) yes
NEXTAUTH_SECRET NextAuth session secret yes
ADMIN_API_INTERNAL_URL backend base for server-side proxy/login (prod: http://aim_api:8080) yes
NEXT_PUBLIC_BRAND_NAME brand label no (default AIM Store)
NEXT_PUBLIC_MONITORING_URL Monitoring link target no (default https://monitor.aimstoreorg.com)

Mobile app (aim-store-app/.env.*, baked into the bundle)

Variable Purpose
EXPO_PUBLIC_ENVIRONMENT development / staging / production
EXPO_PUBLIC_AUTH_API_URL backend origin (prod: https://api.aimstoreorg.com)
EXPO_PUBLIC_STORE_API_URL store/payment origin (prod: https://aimstoreorg.com)
EXPO_PUBLIC_WEB_URL website origin (prod: https://aimstoreorg.com)

Monitoring (/opt/aim-store/monitoring/.env on the server)

Variable Purpose Required
GRAFANA_ADMIN_USER Grafana admin login no (default admin)
GRAFANA_ADMIN_PASSWORD Grafana admin password yes

CI (GitHub repo secrets)

Secret Purpose
VPS_HOST / VPS_USER / VPS_SSH_KEY SSH target + key for deploys and pinned health checks
GHCR_PULL_USER / GHCR_PULL_TOKEN read:packages credentials the VPS uses to pull images
GITHUB_TOKEN auto-provided; pushes built images to GHCR

First task: run the register → wallet → catalogue flow locally

  1. Start the data stores and the API as above (OTP needs the SMS provider, so use password auth locally).
  2. Register — this creates the user and their wallet:
    curl -X POST localhost:8080/api/v1/auth/register \
      -H 'Content-Type: application/json' \
      -d '{"phone":"+919999999999","password":"secret123","full_name":"Dev User"}'
    
  3. Browse the public catalogue (no auth):
    curl localhost:8080/api/v1/products
    curl localhost:8080/api/v1/categories
    
  4. Authenticated calls send the custom JWT in the x-sdk-access-token header (the OTP/admin-login flows return one directly; the register/login endpoints return a Firebase custom token for the app's Firebase exchange):
    curl -H "x-sdk-access-token: $TOKEN" localhost:8080/api/v1/wallet
    
  5. Read the API Reference for the full surface.