135 lines
8.7 KiB
Markdown
135 lines
8.7 KiB
Markdown
# Qlockify deployment adaptation contract
|
|
|
|
- Status: Approved planning baseline
|
|
- Source: `D:/Programing/Scripts/Qlockify/qlockify-deployment` (read-only)
|
|
- Target: `/home/ubuntu/zarinpal-analytics-deployment`
|
|
- Implementation authorization: Granted by user on 2026-08-20
|
|
- Last updated: 2026-08-20
|
|
|
|
## Purpose
|
|
|
|
Use Qlockify's proven single-server deployment shape and environment-file layout for ZarinPal Analytics without requiring production IPs, credentials, or operator names during planning. Real values are supplied on the server when deployment occurs; they are not planning blockers and never enter public Git.
|
|
|
|
## Repository layout
|
|
|
|
```text
|
|
/home/ubuntu/zarinpal-analytics-deployment/
|
|
├── .env ignored deployment/TLS/backup values
|
|
├── backend/ ignored backend Git checkout
|
|
│ └── .env ignored backend/runtime values
|
|
├── frontend/ ignored frontend Git checkout
|
|
│ └── .env ignored public build values only
|
|
├── data/incoming/ ignored one-time CSV input
|
|
├── data/reports/ ignored import reports
|
|
├── releases/ ignored non-secret release records
|
|
└── backups/ ignored encrypted local backups
|
|
```
|
|
|
|
This deliberately simplifies Qlockify's extra application subdirectory layer. Each service checkout is directly under the deployment root and keeps an independent `.git` directory.
|
|
|
|
## File adaptation matrix
|
|
|
|
| Qlockify source | Planned ZarinPal artifact | Adaptation |
|
|
|---|---|---|
|
|
| `.env.sample` | `.env.example` | Keep TLS and ParsPack variable names; use the ZarinPal domain/prefix; remove alternate-domain and duplicate database values. |
|
|
| `backend/.env.sample` | backend-owned `.env.example`, consumed as `backend/.env` | Replace Django, JWT, Celery, OAuth, SMS, and demo-cleanup variables with the Go API/worker/import contracts. |
|
|
| `frontend/.env.sample` | frontend-owned `.env.example`, consumed as `frontend/.env` | Keep only public same-origin build configuration; never place a secret here. |
|
|
| `docker-compose.yml` | `compose.yaml` | Keep single-host orchestration and nested build contexts; replace Django/Celery/media services with API, worker, migrate, importer, and admin boundaries. |
|
|
| `backend/Dockerfile` | deployment backend image definition | Build the backend checkout once and select API/worker/migrate/import/admin commands from the same recorded backend revision. |
|
|
| `frontend/Dockerfile` | deployment frontend image definition | Build the ordinary frontend checkout, including its tracked font and license. |
|
|
| `nginx/*` | `nginx/*` | Keep HTTP-01, automatic renewal, temporary bootstrap certificate, and reload flow; use only `zarinpal.amiirkhl.ir` and ZarinPal routes. |
|
|
| `scripts/deploy.sh` | `scripts/deploy.sh` plus release-record helper | Keep manual Compose build/start structure; Git pulls happen interactively before the script; remove fetch/reset and partial-repository release selection. |
|
|
| `scripts/backup.sh` | `scripts/backup.sh` | Keep plain-SQL `pg_dump` and temporary archive structure; include PostgreSQL plus non-secret manifest/checksums only. |
|
|
| `scripts/backup-upload-s3.sh` | same filename | Keep root `.env` loading, OpenSSL/rclone flow, ParsPack support, and 3/7 retention; retain encrypted local artifacts. |
|
|
| `scripts/restore.sh` | same filename | Keep plain-SQL restore basis; remove env/media restore and add empty-target plus production guards. |
|
|
| `scripts/restore-from-s3.sh` | same filename | Keep rclone/decrypt handoff; require an explicit object id for production and validate it before restore. |
|
|
| `.gitea/workflows/*` | none | Do not port. ZarinPal deployment is manual and has no CI/CD pipeline or runner. |
|
|
|
|
## Environment layout
|
|
|
|
Future tracked examples contain placeholders or safe public defaults only. Production files are Git-ignored, owned by `ubuntu`, mode `0600`, excluded from backup, and loaded without printing resolved values.
|
|
|
|
### Deployment root `.env`
|
|
|
|
The future `.env.example` uses this deployment-owned contract:
|
|
|
|
```dotenv
|
|
COMPOSE_PROJECT_NAME=zarinpal_analytics
|
|
|
|
SSL_CERT_MODE=letsencrypt
|
|
LETSENCRYPT_EMAIL=
|
|
PRIMARY_DOMAIN=zarinpal.amiirkhl.ir
|
|
CERTBOT_STAGING=0
|
|
|
|
S3_BACKUP_BUCKET=
|
|
S3_BACKUP_PREFIX=backup/zarinpal-analytics
|
|
S3_BACKUP_ENDPOINT_URL=
|
|
S3_BACKUP_ACCESS_KEY_ID=
|
|
S3_BACKUP_SECRET_ACCESS_KEY=
|
|
BACKUP_ENCRYPTION_PASSPHRASE=
|
|
BACKUP_LOCAL_KEEP_LATEST=3
|
|
BACKUP_REMOTE_KEEP_LATEST=7
|
|
```
|
|
|
|
There is no `ALT_DOMAINS`, runner, registry, deploy-host, SSH-key, or CI variable. The endpoint, bucket, email, access keys, and passphrase may remain blank in the tracked example; the future validation command requires them only for the operation that consumes them.
|
|
|
|
### Backend `backend/.env`
|
|
|
|
The backend repository owns the complete Go application example. Deployment requires these semantic groups and consumes their eventual exact backend-owned names without maintaining a second competing template:
|
|
|
|
- production mode and structured-log level
|
|
- API listen address and public origin `https://zarinpal.amiirkhl.ir`
|
|
- `POSTGRES_HOST=postgres`, port `5432`, database/user/password
|
|
- `REDIS_HOST=redis`, port `6379`, password, and database selection required by the backend/Asynq contract
|
|
- session/cookie secrets and secure same-origin cookie settings
|
|
- analytics TTL, rate limits, worker concurrency `16`, per-merchant concurrency `4`, 60-second attempt timeout, and retry policy
|
|
- import source/report paths, schema version, readiness timeout, and bounded batch/resource settings
|
|
|
|
The same protected file supplies PostgreSQL, Redis, API, worker, migrate, importer, and admin services only with the variables each service needs. Qlockify-only Django, JWT, Celery, Google OAuth, SMS, demo-environment, media, and scheduled-cleanup variables are not carried over.
|
|
|
|
### Frontend `frontend/.env`
|
|
|
|
The frontend repository owns its exact public variable name. Its production value is the same-origin API prefix, initially `/api/v1`. No database, Redis, session, backup, AvalAI, or other secret is allowed in this file or the browser bundle.
|
|
|
|
## Manual deployment flow
|
|
|
|
1. The operator manually runs `git pull --ff-only origin main` in deployment, backend, and frontend checkouts and enters source-forge credentials interactively.
|
|
2. The future deploy command acquires the operation lock and verifies exact paths, remotes, `main`, equality with `origin/main`, and clean trees. It performs no Git fetch, pull, checkout, or reset.
|
|
3. It records all three commit hashes and validates the root/backend/frontend environment contracts without printing values.
|
|
4. It validates Compose, builds from the three recorded revisions, starts data services, runs the one-off migration, and replaces application services only after the required gates pass.
|
|
5. It validates private readiness plus public TLS/routing/font/SSE behavior, observes for 15 minutes, then marks the release active.
|
|
6. Rollback selects the prior release's three hashes, rebuilds, and re-runs the same checks. It never automatically reverses a migration.
|
|
|
|
The ordinary deployment path never invokes CSV import or credential generation. The first server uses the separately planned `first-start.sh` path after migration and before the first public activation.
|
|
|
|
## Always / Ask / Never
|
|
|
|
### Always
|
|
|
|
- Adapt from the named tracked Qlockify source file and record intentional differences.
|
|
- Keep all three real `.env` files ignored, mode `0600`, and out of backups/logs.
|
|
- Validate only the variables needed by the requested operation.
|
|
- Preserve exact-path guards before any mutation, cleanup, restore, or retention action.
|
|
|
|
### Ask first
|
|
|
|
- Rename a deployment-owned variable after implementation begins.
|
|
- Add a public origin/domain, external provider, persistent data class, or secret consumer.
|
|
- Change the manual three-repository release rule or Qlockify-derived backup encryption/retention contract.
|
|
|
|
### Never
|
|
|
|
- Port Qlockify's Gitea workflows, runner/deploy-key variables, or automated deployment.
|
|
- Port Qlockify's destructive `git reset --hard` synchronization.
|
|
- Copy Qlockify credentials, domains, database names, service names, or secret values.
|
|
- Back up or restore `.env` files, Redis, TLS keys, raw CSV, credentials, logs, or frontend assets.
|
|
- Create a second font path or private asset-injection workflow.
|
|
|
|
## Planning acceptance
|
|
|
|
- Every future deployment artifact maps to a named Qlockify source or a documented ZarinPal-only requirement.
|
|
- Root/backend/frontend environment ownership is unambiguous and has no duplicate secret source.
|
|
- Tracked examples can be committed with blank secret placeholders and useful safe defaults.
|
|
- Planning and local validation do not require a server IP, real DNS access, real ParsPack values, or named operators.
|
|
- Real values are required only at the first-server or operation-specific execution gate.
|