config: make domain configurable via DOMAIN env var

Replace hardcoded evosync.ru with a DOMAIN variable read from .env.
nginx.conf is now generated from nginx.conf.template via envsubst;
init-letsencrypt.sh reads DOMAIN from .env and fails loudly if unset.
README documents the new variable and first-deploy TLS workflow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mguschin
2026-05-12 14:01:38 +03:00
parent 796cf49ff9
commit 4f4081c54c
6 changed files with 110 additions and 16 deletions

View File

@@ -55,7 +55,7 @@ Web service for syncing a product catalog from Evotor POS → VK Market. Users c
| `sync_filters` | Store / group inclusion filters (entity_type: store / group) |
| `cached_stores` | Cached list of Evotor stores |
| `cached_groups` | Cached Evotor product groups |
| `cached_products` | Cached Evotor product catalog |
| `cached_products` | Cached Evotor products; `vk_product_id` stores the VK market item ID after first push |
| `vk_cached_albums` | Cached VK Market albums (product groups) |
| `vk_cached_products` | Cached VK Market products |
| `roles` | RBAC roles |
@@ -69,20 +69,40 @@ Web service for syncing a product catalog from Evotor POS → VK Market. Users c
Periodic tasks run via **Celery Beat** and are executed by the **worker** service.
| Task | Schedule | Description |
|------|----------|-------------|
| `web.tasks.catalog.refresh_catalog` | Every `CATALOG_REFRESH_INTERVAL_SECONDS` | Fetches stores, product groups, and products from the Evotor API for every connected user; upserts into `cached_stores`, `cached_groups`, `cached_products` |
| `web.tasks.vk_catalog.refresh_vk_catalog` | Every `CATALOG_REFRESH_INTERVAL_SECONDS` | Fetches Market albums and products from VK API for every connected user; upserts into `vk_cached_albums`, `vk_cached_products` |
Beat fires a single **sync pipeline** every `CATALOG_REFRESH_INTERVAL_SECONDS`. The three tasks run as a Celery chain — each step starts only after the previous one completes:
**Evotor sync sequence per user:**
```
run_sync_pipeline (beat entry)
└─► refresh_catalog — fetch Evotor stores / groups / products
└─► refresh_vk_catalog — fetch VK Market albums / products
└─► mirror_to_vk — push Evotor → VK
```
| Task | Queue | Description |
|------|-------|-------------|
| `web.tasks.celery_app.run_sync_pipeline` | default | Beat entry point; dispatches the chain |
| `web.tasks.catalog.refresh_catalog` | default | Fetches Evotor catalog for all connected users; upserts `cached_stores`, `cached_groups`, `cached_products` |
| `web.tasks.vk_catalog.refresh_vk_catalog` | default | Fetches VK Market albums and products for all connected users; upserts `vk_cached_albums`, `vk_cached_products` |
| `web.tasks.vk_sync.mirror_to_vk` | sync | Mirrors enabled Evotor groups/products → VK Market (create or conditional update) |
**Evotor fetch sequence per user:**
1. `GET /stores` → upsert `cached_stores`
2. For each store: `GET /stores/{id}/product-groups` → upsert `cached_groups`
3. For each store: `GET /stores/{id}/products` → upsert `cached_products`
**VK sync sequence per user:**
**VK fetch sequence per user:**
1. `market.getAlbums` → upsert `vk_cached_albums`
2. `market.get` (extended=1, paginated) → upsert `vk_cached_products` with album membership
**Mirror logic per user (`mirror_to_vk`):**
- Skips stores not in enabled store filters; skips groups not in enabled group filters
- For each enabled group: ensures a matching VK album exists (creates via `market.addAlbum` if missing)
- For each product in the group:
- **Create** (`allow_to_sell=true`, no `vk_product_id` yet): uploads default photo once per run, calls `market.add`, assigns to album, stores returned `vk_product_id`
- **Update** (has `vk_product_id`): calls `market.edit` only if name, price, description, or stock_amount changed vs the cached VK state
- **Skip**: product disabled and never pushed, or nothing changed
- Price for weight measures (`г`, `гр`, etc.) is multiplied by `VK_WEIGHT_PRICE_MULTIPLIER` before conversion to kopecks
Per-user failures are logged and skipped — one broken token does not block other users.
Evotor stores that return `402 Payment Required` (subscription limit) are silently skipped at debug log level.
@@ -185,13 +205,17 @@ All settings are read from environment variables or a `.env` file:
| `DATABASE_URL` | `mysql+pymysql://…@db:3306/evosync` | MariaDB connection string |
| `REDIS_URL` | `redis://redis:6379/0` | Redis connection string |
| `SECRET_KEY` | `change-me-in-production` | Session signing key |
| `BASE_URL` | `http://localhost:8000` | Public URL of the service |
| `DOMAIN` | — | Public domain name (e.g. `example.com`); used to derive `BASE_URL` and nginx config |
| `BASE_URL` | `https://${DOMAIN}` | Public URL of the service |
| `EVOTOR_APP_ID` | — | Evotor application ID |
| `EVOTOR_WEBHOOK_SECRET` | — | Bearer secret for webhook endpoints |
| `JIVOSITE_WIDGET_ID` | — | JivoSite widget ID |
| `VK_DEFAULT_PHOTO_PATH` | `/app/default_product.png` | Fallback image path for VK products |
| `VK_API_VERSION` | `5.199` | VK API version |
| `CATALOG_REFRESH_INTERVAL_SECONDS` | `3600` | Evotor + VK catalog sync interval (s) |
| `CATALOG_REFRESH_INTERVAL_SECONDS` | `3600` | Sync pipeline interval in seconds |
| `VK_CATEGORY_ID` | `40932` | VK Market category ID for all products |
| `VK_STOCK_AMOUNT` | `1000` | Stock amount set for in-sale products |
| `VK_WEIGHT_PRICE_MULTIPLIER` | `10` | Price multiplier for weight-unit products (г, гр, …) |
| `INVITE_EXPIRE_HOURS` | `48` | Invite link TTL in hours |
| `EMAIL_PROVIDER` | `console` | Email provider (console / smtp / …) |
| `SMS_PROVIDER` | `console` | SMS provider |
@@ -202,13 +226,28 @@ All settings are read from environment variables or a `.env` file:
## Running
```bash
cp .env.example .env # fill in your values
cp .env.example .env # set DOMAIN and other values
docker compose up -d --build
```
App is available at `http://localhost:8080`.
Flower (queue monitor) at `http://localhost:5555`.
### First deploy (TLS)
```bash
# 1. Generate nginx config from template
DOMAIN=yourdomain.com envsubst '${DOMAIN}' < nginx/nginx.conf.template > nginx/nginx.conf
# 2. Obtain TLS certificate (reads DOMAIN from .env automatically)
sudo ./scripts/init-letsencrypt.sh
# 3. Reload nginx
sudo systemctl reload nginx
```
To change the domain later, repeat all three steps with the new domain.
### Development
```bash