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— themain.goat the package root is not what the Makefile builds (CMD = ./cmd/api/). ENTITY_ACCESS_TOKENis required at boot:config.Load()callslog.Fatalfif it is unset, so the process exits immediately without it (config/config.go). Fantasy data comes from the Entity Sport API.JWT_SECRETfails closed: withAPP_ENVunset orproduction, a missing or weak secret refuses to start; onlyAPP_ENV=developmentgets 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¶
- Start the data stores and the API as above (OTP needs the SMS provider, so use password auth locally).
- 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"}' - Browse the public catalogue (no auth):
curl localhost:8080/api/v1/products curl localhost:8080/api/v1/categories - Authenticated calls send the custom JWT in the
x-sdk-access-tokenheader (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 - Read the API Reference for the full surface.