8.9 KiB
ZarinPal Merchant Analytics — Frontend
Persian-first, RTL merchant analytics dashboard built with SvelteKit 2, Svelte 5, strict TypeScript, Tailwind CSS v4, and a generated OpenAPI client contract.
The application is a client-rendered static SPA. In development, Vite serves it and proxies same-origin /api requests to the Go backend. In production, it is built into static files and served by the deployment repository's unprivileged Nginx container. Authentication remains backend-owned through an HttpOnly cookie; credentials and tokens are never stored in browser storage.
Local setup
Prerequisites
- Node.js 20.19+ or 22.12+
- npm
- The backend API running at
http://127.0.0.1:8080 - PostgreSQL and Redis running for the backend
The frontend does not connect directly to PostgreSQL or Redis. Those external services are required by the backend: PostgreSQL stores application/analytics data, while Redis coordinates and caches background work. Follow the backend README to start them, apply migrations, import the dataset, create the demo account, and run both cmd/api and cmd/worker.
1. Configure and install
From the frontend repository root:
cp .env.example .env
npm ci
PowerShell equivalent:
Copy-Item .env.example .env
npm ci
The normal local configuration is:
PUBLIC_API_BASE_URL=/api/v1
PUBLIC_AI_GROWTH_ENABLED=false
PUBLIC_AI_COACH_ENABLED=false
PUBLIC_API_BASE_URL is browser-visible routing configuration, not a secret. Keep /api/v1 for the same-origin Vite proxy. To use a backend at another local address, add LOCAL_API_ORIGIN=http://127.0.0.1:8080; this variable configures Vite only.
Do not put database URLs, Redis passwords, session secrets, AI provider keys, or any other credential in a frontend environment variable. Every PUBLIC_* value is included in the browser build.
2. Start the application
npm run dev
Open http://127.0.0.1:5173. The Vite server proxies /api/* to http://127.0.0.1:8080 by default.
3. Sign in with the local demo user
The backend creates/resets this account when its go run ./cmd/admin command runs with the documented values:
Username: merchant-0001
Password: merchant-demo-2026
If login fails, confirm the backend .env contains those exact DEMO_USERNAME and DEMO_PASSWORD values and rerun go run ./cmd/admin from the backend repository. These are public development credentials; use a different password on every internet-accessible deployment.
Commands
| Command | Purpose |
|---|---|
npm run dev |
Start the Vite development server on 127.0.0.1:5173 |
npm run generate:api |
Regenerate TypeScript API types from the sibling backend OpenAPI file |
npm run check |
Run Svelte and TypeScript checks |
npm test |
Run the Vitest suite once |
npm run test:watch |
Run Vitest in watch mode |
npm run format:check |
Check formatting without changing files |
npm run format |
Format the repository |
npm run build |
Create the production static build in build/ |
npm run preview |
Preview the production build locally |
npm run generate:api expects the backend repository at ../zarinpal-analytics-backend. If your checkout uses another layout, update the input path in package.json or copy the OpenAPI contract into the expected sibling location before running it.
Before submitting a change, run:
npm run generate:api
npm run format:check
npm run check
npm test
npm run build
Production deployment on a remote server
Deploy the frontend and backend together through the zarinpal-analytics-deployment repository. Its Compose stack builds this static SPA, serves it through Nginx, runs the Go API and worker, and keeps PostgreSQL/Redis private. A separate shared Caddy deployment owns the public domain and HTTPS.
The examples use analytics.example.com; replace it with your own domain and replace repository URL placeholders with your template/fork URLs.
1. Prepare the server
- Provision an Ubuntu server with a public IP.
- Install Git, Docker Engine, and the Docker Compose plugin.
- Permit inbound TCP 80/443 and restrict SSH to trusted addresses.
- Never expose PostgreSQL 5432 or Redis 6379 publicly.
2. Install the main Caddy router
Clone and follow the Caddy deployment repository:
git clone https://git.amiirkhl.ir/Interanet/caddy-deployment.git ~/caddy-deployment
cd ~/caddy-deployment
Start Caddy according to that repository's README. Its container must join an external Docker network named caddy_proxy. If the repository does not create it, run docker network create caddy_proxy once before starting Caddy.
3. Clone the application repositories
git clone <deployment-repository-url> ~/zarinpal-analytics-deployment
cd ~/zarinpal-analytics-deployment
git clone <backend-repository-url> backend
git clone <frontend-repository-url> frontend
Keep all three repositories on clean main branches matching origin/main; the guarded deployment script checks this layout.
4. Configure production
cp .env.example .env
cp backend/.env.production.example backend/.env
cp frontend/.env.production.example frontend/.env
chmod 600 .env backend/.env frontend/.env
Replace every placeholder. In particular:
- Set
FRONTEND_ORIGIN=https://analytics.example.comandCOOKIE_SECURE=trueinbackend/.env. - Generate distinct PostgreSQL, Redis, session, and cursor secrets with
openssl rand -hex 32. - Replace the public local demo password before exposing the site.
- Keep
PUBLIC_API_BASE_URL=/api/v1infrontend/.envfor same-origin routing. - Enable frontend AI switches only when the matching backend capability, provider key, and budgets are configured.
- Never place a private value in
frontend/.env.
Place the dataset at the private absolute path configured by IMPORT_FILE, for example /srv/zarinpal-data/challenge_data.csv.gz.
5. Run the first deployment
From ~/zarinpal-analytics-deployment, run in order:
docker compose config --quiet
docker compose build backend frontend
docker compose up -d postgres redis
docker compose --profile tools run --rm migrate
docker compose --profile import run --rm importer
docker compose --profile tools run --rm admin
docker compose up -d backend worker frontend
docker compose ps
The migration, importer, and admin containers are intentional one-off tools. The API, worker, and frontend are long-running. Wait for every service to become healthy and inspect problems with:
docker compose logs --tail=200 postgres redis backend worker frontend
6. Point the domain to the application
Create an A record for analytics.example.com pointing to the server's public IPv4 address. Add AAAA only when IPv6 is actually configured.
In the Caddy deployment, add this site to the Caddyfile or equivalent template:
analytics.example.com {
@backend path /api/* /healthz
handle @backend {
reverse_proxy zarinpal-backend:8080 {
flush_interval -1
}
}
handle {
reverse_proxy zarinpal-frontend:8080
}
}
Validate and reload Caddy with the commands documented in its repository. Once DNS resolves and ports 80/443 reach the server, Caddy obtains and renews the HTTPS certificate automatically.
Verify the public site and API routing:
curl -I https://analytics.example.com/
curl https://analytics.example.com/healthz
Open the site, sign in with the production demo credentials configured in backend/.env, and confirm that dashboard data loads without browser console errors.
7. Deploy later updates
cd ~/zarinpal-analytics-deployment
git pull --ff-only origin main
git -C backend pull --ff-only origin main
git -C frontend pull --ff-only origin main
bash scripts/deploy.sh
Later deployments rebuild both images and apply migrations. They do not rerun the one-time importer or reset the demo account. Never run docker compose down -v in production because it deletes persistent volumes.
Project documentation
DESIGN.mddefines the visual language, accessibility, RTL, responsive, and interaction rules.docs/DASHBOARD-SPEC.mddefines dashboard product behavior.docs/DASHBOARD-UX-SPEC.mddefines screen flows and async/data states.AGENTS.mddefines durable contributor and automation rules.