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

  1. Provision an Ubuntu server with a public IP.
  2. Install Git, Docker Engine, and the Docker Compose plugin.
  3. Permit inbound TCP 80/443 and restrict SSH to trusted addresses.
  4. 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.com and COOKIE_SECURE=true in backend/.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/v1 in frontend/.env for 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

Description
No description provided
Readme 920 KiB
Languages
Svelte 74.1%
TypeScript 24.1%
CSS 1.5%
HTML 0.2%