Some checks are pending
Sprint 9 пункт 3 (mobile-адаптация):
- DataTable: min-w-max sm:min-w-[640px] → узкие таблицы (Loyalty, Promotions)
влезают на 375px без horizontal-scroll, широкие (Products) скроллятся
внутри overflow-auto родителя.
- Mobile-audit спека (stage-ui-s9-mobile-audit) — 20 screenshot'ов в
reports/mobile/ (375 + 768 viewport × 10 страниц + seed-demo).
Smoke: no console-errors, layouts читаемы.
Sprint 9 пункт 4 (P2-9 PWA):
- public/manifest.webmanifest — read-only PWA владельца. Shortcuts:
Дашборд, Sales/Profit/Stock отчёты. display=standalone (homescreen icon).
- public/sw.js — service worker:
• SPA navigate: network-first + offline-fallback на /offline.html.
• GET /api/*: network-first + cache-fallback (read-only кеш).
• CSS/JS/SVG: stale-while-revalidate.
• Мутации (POST/PUT/DELETE): не вмешиваемся, сеть.
- public/offline.html — статический fallback с кнопкой «Открыть дашборд».
- index.html: <link rel='manifest'>, apple-touch-meta, lang=ru-KZ.
- main.tsx: navigator.serviceWorker.register('/sw.js') в production only
(dev hot-reload не мешает).
- deploy/nginx.conf: /sw.js no-cache, /manifest.webmanifest правильный
content-type, /offline.html static.
Stage e2e:
- stage-ui-s9-loyalty.spec (4/4 ✓): programs/cards/promotions endpoints
+ UI рендер + SALE20 на 500₸ → total=400 (валидно через API).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
92 lines
3.5 KiB
Nginx Configuration File
92 lines
3.5 KiB
Nginx Configuration File
server {
|
||
listen 80 default_server;
|
||
root /usr/share/nginx/html;
|
||
index index.html;
|
||
|
||
# Long-running admin imports (MoySklad etc.) read from upstream for tens of
|
||
# minutes. Bump timeouts only on that path so normal API stays snappy.
|
||
location /api/admin/import/ {
|
||
proxy_pass http://api:8080;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_read_timeout 60m;
|
||
proxy_send_timeout 60m;
|
||
proxy_request_buffering off;
|
||
proxy_buffering off;
|
||
}
|
||
|
||
# API reverse-proxy — upstream name "api" resolves in the compose network.
|
||
location /api/ {
|
||
proxy_pass http://api:8080;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
}
|
||
|
||
location /connect/ {
|
||
proxy_pass http://api:8080;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
}
|
||
|
||
# SignalR хаб для live-уведомлений (см. NotificationsHub).
|
||
# WebSocket требует upgrade-хедеры и большой read_timeout (иначе nginx
|
||
# будет рвать idle-коннекшен каждые 60 сек). access_token приходит как
|
||
# query (?access_token=...), Authorization-хедер middleware на API его
|
||
# перекладывает в нужный вид до UseAuthentication.
|
||
location /hubs/ {
|
||
proxy_pass http://api:8080;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
proxy_read_timeout 86400; # 24h — webSocket долгоживущий
|
||
proxy_send_timeout 86400;
|
||
proxy_buffering off;
|
||
}
|
||
|
||
location /health {
|
||
proxy_pass http://api:8080;
|
||
}
|
||
|
||
# Статика изображений товаров — api раздаёт /uploads/... из volume.
|
||
location /uploads/ {
|
||
proxy_pass http://api:8080;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
}
|
||
|
||
# PWA: SW и manifest должны отдаваться с правильным content-type и без
|
||
# кеша на самом ответе (внутри SW свой versioned cache). Иначе старый
|
||
# SW залипает на клиенте и не подхватывает обновления.
|
||
location = /sw.js {
|
||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||
add_header Pragma "no-cache";
|
||
expires off;
|
||
try_files /sw.js =404;
|
||
}
|
||
location = /manifest.webmanifest {
|
||
types { } default_type application/manifest+json;
|
||
add_header Cache-Control "public, max-age=3600";
|
||
try_files /manifest.webmanifest =404;
|
||
}
|
||
location = /offline.html {
|
||
try_files /offline.html =404;
|
||
}
|
||
|
||
# SPA fallback — all other routes return index.html
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
}
|