docs: aktualizacja architektury po migracji na OVH VPS + e-deklaracja w roadmap
- ROADMAP: dodano funkcję #2 (e-deklaracja PZ) z analizą flow PDF + samodzielny podpis - architecture/03,07,08,09,11 + flows/06: aktualizacja pod OVH VPS (IP, user maciejpi zamiast www-data, brak NPM dla prod) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5893645c46
commit
bfd48a6e20
@ -93,6 +93,35 @@ Najwyższy poziom (Enterprise) służy jako **kotwica cenowa** — sprawia że P
|
||||
| # | Funkcjonalność | Opis | Zgłosił | Data | Priorytet | Status |
|
||||
|---|---------------|------|---------|------|-----------|--------|
|
||||
| 1 | Wiele lokalizacji firmy na mapie | Firmy z kilkoma oddziałami/punktami mogą dodać wiele adresów. Każdy wyświetlany na profilu z linkiem do Google Maps. Nowa tabela `company_locations` (label, adres, lat/lng na przyszłość), edycja w zakładce Kontakt w profilu firmy, wzorzec jak CompanyWebsite (dynamiczne wiersze, delete-and-reinsert). | Daniel Kochański (Stalpunkt) | 2026-04-09 | Średni | Planowane |
|
||||
| 2 | E-deklaracja członkowska z podpisem elektronicznym | Elektroniczny formularz deklaracji przystąpienia do Izby z możliwością podpisu Profilem Zaufanym. Szczegóły poniżej w sekcji dedykowanej. | Jacek Pomieczyński (Eura-Tech), Paweł Kwidziński | 2026-04-08 | Niski | Rozważane |
|
||||
|
||||
#### E-deklaracja członkowska — analiza (priorytet: niski)
|
||||
|
||||
**Kontekst:** Jacek Pomieczyński (Eura-Tech) i Paweł Kwidziński postulują elektroniczną deklarację przystąpienia do Izby zamiast papierowego formularza. Poprzednia próba wdrożenia zablokowana przez brak rozwiązania podpisu reprezentacji (kto jest uprawniony do podpisania w imieniu firmy).
|
||||
|
||||
**Rekomendowany flow (bez integracji API, zero kosztów):**
|
||||
|
||||
1. Użytkownik wypełnia formularz deklaracji na NordaBiznes.pl (dane firmy, NIP, KRS, dane osoby reprezentującej)
|
||||
2. System generuje gotowy PDF deklaracji
|
||||
3. Użytkownik pobiera PDF i podpisuje go **samodzielnie** Profilem Zaufanym na moj.gov.pl → "Podpisz dokument elektronicznie" (lub w aplikacji mObywatel)
|
||||
4. Użytkownik uploaduje podpisany plik (.xml z podpisem XAdES) z powrotem na portal
|
||||
5. Biuro Nordy weryfikuje podpis (moj.gov.pl → "Sprawdź podpis elektroniczny") i proceduje przyjęcie
|
||||
|
||||
**Co trzeba zbudować:** formularz deklaracji + generator PDF (np. WeasyPrint/ReportLab). Infrastruktura podpisu — darmowa, po stronie państwa.
|
||||
|
||||
**Wymagania prawne (do ustalenia z mec. Masiakiem):**
|
||||
- Czy statut Izby wymaga formy pisemnej deklaracji?
|
||||
- Jeśli tak — zmiana statutu dopuszczająca formę elektroniczną (możliwa na Walnym 28.05.2026)
|
||||
- Podpis Zaufany w relacjach prywatnych nie jest automatycznie równoważny z podpisem ręcznym — statut musi go wprost dopuszczać
|
||||
|
||||
**Rozważane alternatywy (odrzucone lub odłożone):**
|
||||
- Autenti/Signius (API podpisu) — działa, ale zbędny koszt i zależność od zewnętrznego dostawcy przy tak prostym flow
|
||||
- Bezpośrednia integracja z API Podpisu Zaufanego (podpis.gov.pl) — niedostępne dla podmiotów prywatnych
|
||||
- Integracja logowania przez Węzeł Krajowy (login.gov.pl) — możliwa technicznie (SAML 2.0), ale nadmiarowa dla samych deklaracji
|
||||
|
||||
**Uwaga:** Przy skali ~10-15 nowych członków rocznie pełna automatyzacja (API) jest nieuzasadniona. Prosty flow PDF + samodzielny podpis PZ rozwiązuje problem.
|
||||
|
||||
---
|
||||
|
||||
#### Funkcje strategiczne (ze slajdu 9, prezentacja Rada 13.02.2026)
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
---
|
||||
|
||||
> **UWAGA (2026-04-04):** Produkcja przeniesiona z OVH VPS inpi-vps-waw01 (VM 249, 57.128.200.27) na **OVH VPS (57.128.200.27, hostname inpi-vps-waw01)**. Diagramy Mermaid poniżej odzwierciedlają starą architekturę — faktyczny stan to: DNS nordabiznes.pl -> 57.128.200.27 (bezpośrednio, bez NPM). NPM (10.22.68.250) obsługuje tylko staging.
|
||||
> **NOTE (2026-04-04):** Production migrated to OVH VPS (57.128.200.27, hostname inpi-vps-waw01). Traffic flow: Internet -> Nginx (57.128.200.27:443) -> Gunicorn (127.0.0.1:5000). No FortiGate or NPM for production. NPM (10.22.68.250) serves staging only.
|
||||
|
||||
## Overview
|
||||
|
||||
@ -108,7 +108,7 @@ graph TB
|
||||
|--------|-------|------------|----------|-----|------------|
|
||||
| **NORDABIZ-STAGING-01** | 248 | 10.22.68.248 | nordabiz-staging-01 | Ubuntu 22.04 | Proxmox VE |
|
||||
|
||||
**Note:** The old on-prem production VM 249 (57.128.200.27) and NPM reverse proxy (10.22.68.250) are no longer used for production. Staging still uses NPM + FortiGate path.
|
||||
**Note:** The old on-prem production VM 249 (10.22.68.249) and NPM reverse proxy (10.22.68.250) are no longer used for production. Staging still uses NPM + FortiGate path.
|
||||
|
||||
---
|
||||
|
||||
@ -440,7 +440,7 @@ curl -I https://nordabiznes.pl/health
|
||||
**API Key Storage:**
|
||||
- **Location:** `/var/www/nordabiznes/.env` (production)
|
||||
- **Permissions:** `chmod 600` (owner read/write only)
|
||||
- **Owner:** `www-data:www-data`
|
||||
- **Owner:** `maciejpi:maciejpi`
|
||||
- **⚠️ NEVER commit to git!** (in `.gitignore`)
|
||||
|
||||
---
|
||||
@ -816,7 +816,7 @@ sudo -u postgres psql -c "SELECT 1;"
|
||||
# Expected: 1
|
||||
|
||||
# 3. Check application database access
|
||||
sudo -u www-data psql -h 127.0.0.1 -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
||||
sudo -u maciejpi psql -h 127.0.0.1 -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
||||
# Expected: 1 (if password prompt, check .env)
|
||||
|
||||
# 4. Check active connections
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
---
|
||||
|
||||
> **UWAGA (2026-04-04):** Produkcja przeniesiona z OVH VPS inpi-vps-waw01 (VM 249, 57.128.200.27) na **OVH VPS (57.128.200.27, hostname inpi-vps-waw01)**. Diagramy Mermaid poniżej odzwierciedlają starą architekturę — ruch produkcyjny nie przechodzi już przez Fortigate/NPM. Staging (10.22.68.248) i NPM (10.22.68.250) bez zmian.
|
||||
> **NOTE (2026-04-04):** Production migrated to OVH VPS (57.128.200.27). Production traffic no longer goes through FortiGate/NPM. Staging (10.22.68.248) and NPM (10.22.68.250) remain unchanged.
|
||||
|
||||
## Overview
|
||||
|
||||
@ -422,8 +422,9 @@ App Server (57.128.200.27)
|
||||
|
||||
| Record Type | Name | Value | TTL | Purpose |
|
||||
|-------------|------|-------|-----|---------|
|
||||
| A | nordabiznes.pl | 85.237.177.83 | 3600 | Main website |
|
||||
| A | www.nordabiznes.pl | 85.237.177.83 | 3600 | WWW subdomain |
|
||||
| A | nordabiznes.pl | 57.128.200.27 | 3600 | Main website (OVH VPS) |
|
||||
| A | www.nordabiznes.pl | 57.128.200.27 | 3600 | WWW subdomain (OVH VPS) |
|
||||
| A | staging.nordabiznes.pl | 85.237.177.83 | 3600 | Staging (via FortiGate) |
|
||||
| MX | nordabiznes.pl | (Not configured) | - | No email hosting |
|
||||
| TXT | nordabiznes.pl | (SPF, DKIM if configured) | - | Email authentication |
|
||||
|
||||
@ -431,11 +432,11 @@ App Server (57.128.200.27)
|
||||
```bash
|
||||
# Check DNS resolution
|
||||
dig nordabiznes.pl +short
|
||||
# Expected: 85.237.177.83
|
||||
# Expected: 57.128.200.27
|
||||
|
||||
# Check from Google DNS
|
||||
dig @8.8.8.8 nordabiznes.pl +short
|
||||
# Expected: 85.237.177.83
|
||||
# Expected: 57.128.200.27
|
||||
|
||||
# Check WHOIS
|
||||
whois nordabiznes.pl
|
||||
@ -499,23 +500,19 @@ default via 10.22.68.1 dev ens18 # All non-local traffic → Fortigate
|
||||
```
|
||||
User Browser (Internet)
|
||||
↓ DNS query
|
||||
OVH DNS: nordabiznes.pl → 85.237.177.83
|
||||
OVH DNS: nordabiznes.pl → 57.128.200.27
|
||||
↓ HTTPS :443
|
||||
Fortigate WAN (85.237.177.83:443)
|
||||
↓ NAT translation
|
||||
Fortigate LAN → NPM (10.22.68.250:443)
|
||||
↓ SSL termination, proxy
|
||||
NPM → Flask/Gunicorn (57.128.200.27:5000)
|
||||
Nginx @ OVH VPS (57.128.200.27:443)
|
||||
↓ SSL termination, proxy_pass
|
||||
Gunicorn (127.0.0.1:5000)
|
||||
↓ HTTP request processing
|
||||
Flask → PostgreSQL (127.0.0.1:5432)
|
||||
↓ SQL query
|
||||
PostgreSQL → Flask (result set)
|
||||
↓ HTML rendering
|
||||
Flask → NPM (HTTP response)
|
||||
Flask → Nginx (HTTP response)
|
||||
↓ SSL encryption
|
||||
NPM → Fortigate LAN (HTTPS)
|
||||
↓ NAT reverse
|
||||
Fortigate WAN → User Browser (85.237.177.83:443)
|
||||
Nginx → User Browser (57.128.200.27:443)
|
||||
```
|
||||
|
||||
**Total Hops:** 6 (external) + 3 (internal) = 9 hops
|
||||
|
||||
@ -364,7 +364,7 @@ curl -vI https://nordabiznes.pl 2>&1 | grep "expire date"
|
||||
### Production Environment Variables
|
||||
|
||||
**Location:** `/var/www/nordabiznes/.env`
|
||||
**Owner:** `www-data:www-data`
|
||||
**Owner:** `maciejpi:maciejpi`
|
||||
**Permissions:** `0600` (read/write owner only)
|
||||
|
||||
**⚠️ WARNING:** Never commit `.env` to version control!
|
||||
@ -419,7 +419,7 @@ openssl rand -base64 32
|
||||
|
||||
# Verify .env file permissions
|
||||
ls -la /var/www/nordabiznes/.env
|
||||
# Expected: -rw------- 1 www-data www-data ... .env
|
||||
# Expected: -rw------- 1 maciejpi maciejpi ... .env
|
||||
```
|
||||
|
||||
### Development Environment Variables
|
||||
@ -470,7 +470,7 @@ postgresql://nordabiz_app:NordaBiz2025Secure@127.0.0.1:5433/nordabiz
|
||||
**Direct psql (production):**
|
||||
```bash
|
||||
# As application user
|
||||
sudo -u www-data psql -U nordabiz_app -d nordabiz -h 127.0.0.1
|
||||
psql -U nordabiz_app -d nordabiz -h 127.0.0.1
|
||||
|
||||
# As postgres superuser
|
||||
sudo -u postgres psql nordabiz
|
||||
@ -744,7 +744,7 @@ curl -I https://nordabiznes.pl/health
|
||||
### SSH Keys for Git Access
|
||||
|
||||
**Production Server:**
|
||||
- Location: `/home/www-data/.ssh/`
|
||||
- Location: `/home/maciejpi/.ssh/`
|
||||
- Public key: `id_rsa.pub`
|
||||
- Private key: `id_rsa`
|
||||
- Known hosts: `known_hosts` (includes Gitea fingerprint)
|
||||
@ -761,14 +761,14 @@ curl -I https://nordabiznes.pl/health
|
||||
|
||||
| Path | Description | Owner | Permissions |
|
||||
|------|-------------|-------|-------------|
|
||||
| `/var/www/nordabiznes/` | Application root directory | www-data:www-data | 0755 |
|
||||
| `/var/www/nordabiznes/app.py` | Main Flask application | www-data:www-data | 0644 |
|
||||
| `/var/www/nordabiznes/.env` | Environment variables (secrets) | www-data:www-data | 0600 |
|
||||
| `/var/www/nordabiznes/venv/` | Python virtual environment | www-data:www-data | 0755 |
|
||||
| `/var/www/nordabiznes/static/` | Static assets (CSS, JS, images) | www-data:www-data | 0755 |
|
||||
| `/var/www/nordabiznes/templates/` | Jinja2 templates | www-data:www-data | 0755 |
|
||||
| `/var/www/nordabiznes/database.py` | SQLAlchemy models | www-data:www-data | 0644 |
|
||||
| `/var/www/nordabiznes/scripts/` | Background scripts | www-data:www-data | 0755 |
|
||||
| `/var/www/nordabiznes/` | Application root directory | maciejpi:maciejpi | 0755 |
|
||||
| `/var/www/nordabiznes/app.py` | Main Flask application | maciejpi:maciejpi | 0644 |
|
||||
| `/var/www/nordabiznes/.env` | Environment variables (secrets) | maciejpi:maciejpi | 0600 |
|
||||
| `/var/www/nordabiznes/venv/` | Python virtual environment | maciejpi:maciejpi | 0755 |
|
||||
| `/var/www/nordabiznes/static/` | Static assets (CSS, JS, images) | maciejpi:maciejpi | 0755 |
|
||||
| `/var/www/nordabiznes/templates/` | Jinja2 templates | maciejpi:maciejpi | 0755 |
|
||||
| `/var/www/nordabiznes/database.py` | SQLAlchemy models | maciejpi:maciejpi | 0644 |
|
||||
| `/var/www/nordabiznes/scripts/` | Background scripts | maciejpi:maciejpi | 0755 |
|
||||
|
||||
### Configuration Files
|
||||
|
||||
@ -782,8 +782,8 @@ curl -I https://nordabiznes.pl/health
|
||||
|
||||
| Path | Description | Rotation | Owner |
|
||||
|------|-------------|----------|-------|
|
||||
| `/var/log/nordabiznes/gunicorn_access.log` | Gunicorn access log | Daily | www-data |
|
||||
| `/var/log/nordabiznes/gunicorn_error.log` | Gunicorn error log | Daily | www-data |
|
||||
| `/var/log/nordabiznes/gunicorn_access.log` | Gunicorn access log | Daily | maciejpi |
|
||||
| `/var/log/nordabiznes/gunicorn_error.log` | Gunicorn error log | Daily | maciejpi |
|
||||
| `/var/log/postgresql/postgresql-*.log` | PostgreSQL logs | Weekly | postgres |
|
||||
| `journalctl -u nordabiznes` | Systemd service logs | 30 days | root |
|
||||
|
||||
@ -953,20 +953,20 @@ sudo chmod 600 /home/maciejpi/backups/.env.backup
|
||||
4. **Clone application:**
|
||||
```bash
|
||||
sudo mkdir -p /var/www/nordabiznes
|
||||
sudo chown www-data:www-data /var/www/nordabiznes
|
||||
sudo -u www-data git clone https://10.22.68.180:3000/maciejpi/nordabiz.git /var/www/nordabiznes
|
||||
sudo chown maciejpi:maciejpi /var/www/nordabiznes
|
||||
sudo -u maciejpi git clone https://10.22.68.180:3000/maciejpi/nordabiz.git /var/www/nordabiznes
|
||||
```
|
||||
5. **Restore .env file:**
|
||||
```bash
|
||||
sudo cp /path/to/.env.backup /var/www/nordabiznes/.env
|
||||
sudo chown www-data:www-data /var/www/nordabiznes/.env
|
||||
sudo chown maciejpi:maciejpi /var/www/nordabiznes/.env
|
||||
sudo chmod 600 /var/www/nordabiznes/.env
|
||||
```
|
||||
6. **Install Python dependencies:**
|
||||
```bash
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data python3 -m venv venv
|
||||
sudo -u www-data venv/bin/pip install -r requirements.txt
|
||||
sudo -u maciejpi python3 -m venv venv
|
||||
sudo -u maciejpi venv/bin/pip install -r requirements.txt
|
||||
```
|
||||
7. **Configure systemd service:**
|
||||
```bash
|
||||
@ -999,7 +999,7 @@ sudo chmod 600 /home/maciejpi/backups/.env.backup
|
||||
|
||||
# Application
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data git status # Ensure clean state
|
||||
sudo -u maciejpi git status # Ensure clean state
|
||||
```
|
||||
|
||||
2. **Test changes in development**
|
||||
@ -1022,7 +1022,7 @@ sudo chmod 600 /home/maciejpi/backups/.env.backup
|
||||
|
||||
# Pull changes
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data git pull
|
||||
sudo -u maciejpi git pull
|
||||
|
||||
# Restart service
|
||||
sudo systemctl restart nordabiznes
|
||||
@ -1047,7 +1047,7 @@ sudo chmod 600 /home/maciejpi/backups/.env.backup
|
||||
sudo systemctl stop nordabiznes
|
||||
|
||||
# Rollback code
|
||||
sudo -u www-data git reset --hard HEAD~1
|
||||
sudo -u maciejpi git reset --hard HEAD~1
|
||||
|
||||
# Rollback database (if needed)
|
||||
sudo -u postgres psql nordabiz < /tmp/backup_before_change.sql
|
||||
@ -1130,10 +1130,10 @@ curl -I https://nordabiznes.pl/health
|
||||
|
||||
```bash
|
||||
# Check database connectivity
|
||||
sudo -u www-data psql -U nordabiz_app -d nordabiz -h 127.0.0.1 -c "SELECT COUNT(*) FROM companies;"
|
||||
psql -U nordabiz_app -d nordabiz -h 127.0.0.1 -c "SELECT COUNT(*) FROM companies;"
|
||||
|
||||
# Check recent data
|
||||
sudo -u www-data psql -U nordabiz_app -d nordabiz -h 127.0.0.1 -c \
|
||||
psql -U nordabiz_app -d nordabiz -h 127.0.0.1 -c \
|
||||
"SELECT name, slug FROM companies ORDER BY created_at DESC LIMIT 5;"
|
||||
|
||||
# Check database size
|
||||
@ -1249,7 +1249,7 @@ curl -I https://nordabiznes.pl/health # Test health
|
||||
ssh maciejpi@57.128.200.27
|
||||
cd /var/www/nordabiznes
|
||||
sudo systemctl stop nordabiznes
|
||||
sudo -u www-data git reset --hard HEAD~1
|
||||
sudo -u maciejpi git reset --hard HEAD~1
|
||||
sudo systemctl start nordabiznes
|
||||
curl -I https://nordabiznes.pl/health
|
||||
```
|
||||
|
||||
@ -809,7 +809,7 @@ SMTP_PASSWORD=<password>
|
||||
|
||||
**Secrets Storage:**
|
||||
- **Development:** `.env` file (gitignored)
|
||||
- **Production:** `.env` file on server (owned by www-data, mode 600)
|
||||
- **Production:** `.env` file on server (owned by maciejpi, mode 600)
|
||||
- **Never committed to Git:** `.env` in `.gitignore`
|
||||
|
||||
**Secret Rotation:**
|
||||
@ -819,7 +819,7 @@ SMTP_PASSWORD=<password>
|
||||
**Access Control:**
|
||||
```bash
|
||||
# Production secrets file permissions
|
||||
-rw------- 1 www-data www-data .env # Mode 600 (owner read/write only)
|
||||
-rw------- 1 maciejpi maciejpi .env # Mode 600 (owner read/write only)
|
||||
```
|
||||
|
||||
---
|
||||
@ -1151,7 +1151,7 @@ app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # CSRF protection
|
||||
**Storage:**
|
||||
- **Location:** `.env` file
|
||||
- **Permissions:** 600 (owner read/write only)
|
||||
- **Owner:** www-data (Flask app user)
|
||||
- **Owner:** maciejpi (Flask app user)
|
||||
|
||||
**Best Practices:**
|
||||
- ✅ API keys in .env (not hardcoded)
|
||||
@ -1274,7 +1274,7 @@ deny all from 10.22.68.250 to ANY # NPM cannot initiate outbound
|
||||
- **Port:** 22 (default)
|
||||
- **Access:** Restricted to ADMIN_NET (firewall rule)
|
||||
- **Authentication:** SSH key-based (password auth disabled)
|
||||
- **Users:** maciejpi (primary admin), www-data (app deployment)
|
||||
- **Users:** maciejpi (admin + app user)
|
||||
|
||||
**SSH Hardening:**
|
||||
```bash
|
||||
@ -1283,7 +1283,7 @@ PermitRootLogin no # Disable root login
|
||||
PasswordAuthentication no # Key-based auth only
|
||||
PubkeyAuthentication yes # Allow SSH keys
|
||||
X11Forwarding no # Disable X11
|
||||
AllowUsers maciejpi www-data # Whitelist users
|
||||
AllowUsers maciejpi # Whitelist users
|
||||
```
|
||||
|
||||
**SSH Key Management:**
|
||||
@ -1312,16 +1312,16 @@ AllowUsers maciejpi www-data # Whitelist users
|
||||
**File Permissions:**
|
||||
```bash
|
||||
# Application directory
|
||||
/var/www/nordabiznes/ # Owner: www-data, Mode: 755
|
||||
/var/www/nordabiznes/.env # Owner: www-data, Mode: 600 (secrets)
|
||||
/var/www/nordabiznes/app.py # Owner: www-data, Mode: 644
|
||||
/var/www/nordabiznes/ # Owner: maciejpi, Mode: 755
|
||||
/var/www/nordabiznes/.env # Owner: maciejpi, Mode: 600 (secrets)
|
||||
/var/www/nordabiznes/app.py # Owner: maciejpi, Mode: 644
|
||||
|
||||
# Database directory
|
||||
/var/lib/postgresql/ # Owner: postgres, Mode: 700
|
||||
```
|
||||
|
||||
**User Separation:**
|
||||
- **www-data:** Flask application (limited privileges)
|
||||
- **maciejpi:** Flask application (limited privileges)
|
||||
- **postgres:** PostgreSQL database (limited privileges)
|
||||
- **maciejpi:** Admin user (sudo access)
|
||||
|
||||
@ -1643,7 +1643,7 @@ docker restart <npm-container-id>
|
||||
- ✅ Firewall (Fortigate)
|
||||
- ✅ Network segmentation (DMZ, App, Data zones)
|
||||
- ✅ SSH key-based authentication
|
||||
- ✅ Principle of least privilege (www-data user)
|
||||
- ✅ Principle of least privilege (maciejpi user)
|
||||
- ❌ IDS/IPS
|
||||
- ❌ DDoS protection (beyond Fortigate)
|
||||
|
||||
|
||||
@ -71,48 +71,36 @@ graph TD
|
||||
|
||||
## 2. Infrastructure & Network Issues
|
||||
|
||||
### 2.1 ERR_TOO_MANY_REDIRECTS
|
||||
### 2.1 Site Not Accessible / ERR_TOO_MANY_REDIRECTS
|
||||
|
||||
**Severity:** CRITICAL
|
||||
**Incident History:** 2026-01-02 (30 min outage)
|
||||
|
||||
> **Note:** The ERR_TOO_MANY_REDIRECTS issue from 2026-01-02 was specific to the old NPM+on-prem setup. With the current OVH VPS architecture, redirect loops are unlikely since nginx and Gunicorn run on the same host.
|
||||
|
||||
#### Symptoms
|
||||
|
||||
- Browser error: `ERR_TOO_MANY_REDIRECTS`
|
||||
- Portal completely inaccessible via https://nordabiznes.pl
|
||||
- Internal access works fine (http://57.128.200.27:5000)
|
||||
- Affects 100% of external users
|
||||
|
||||
#### Root Cause
|
||||
|
||||
Nginx Proxy Manager (NPM) configured to forward to **port 80** instead of **port 5000**.
|
||||
|
||||
**Why this causes redirect loop:**
|
||||
1. NPM forwards HTTPS → HTTP to backend port 80
|
||||
2. Nginx on port 80 sees HTTP and redirects to HTTPS
|
||||
3. Request goes back to NPM, creating infinite loop
|
||||
4. Browser aborts after ~20 redirects
|
||||
- Browser error or timeout accessing https://nordabiznes.pl
|
||||
- Portal completely inaccessible
|
||||
|
||||
#### Diagnosis
|
||||
|
||||
```bash
|
||||
# 1. Check NPM proxy configuration
|
||||
ssh maciejpi@10.22.68.250
|
||||
docker exec nginx-proxy-manager_app_1 \
|
||||
sqlite3 /data/database.sqlite \
|
||||
"SELECT id, domain_names, forward_host, forward_port FROM proxy_host WHERE id = 27;"
|
||||
# 1. Check nginx status on OVH VPS
|
||||
ssh maciejpi@57.128.200.27 "sudo systemctl status nginx"
|
||||
|
||||
# Expected output:
|
||||
# 27|["nordabiznes.pl","www.nordabiznes.pl"]|57.128.200.27|5000
|
||||
# 2. Check Gunicorn status
|
||||
ssh maciejpi@57.128.200.27 "sudo systemctl status nordabiznes"
|
||||
|
||||
# If forward_port shows 80 → PROBLEM FOUND!
|
||||
|
||||
# 2. Test backend directly
|
||||
curl -I http://57.128.200.27:80/
|
||||
# If this returns 301 redirect → confirms issue
|
||||
|
||||
curl -I http://57.128.200.27:5000/health
|
||||
# 3. Test backend directly
|
||||
ssh maciejpi@57.128.200.27 "curl -I http://localhost:5000/health"
|
||||
# Should return 200 OK if Flask is running
|
||||
|
||||
# 4. Check DNS resolution
|
||||
dig nordabiznes.pl +short
|
||||
# Expected: 57.128.200.27
|
||||
|
||||
# 5. Test nginx config
|
||||
ssh maciejpi@57.128.200.27 "sudo nginx -t"
|
||||
```
|
||||
|
||||
#### Solution
|
||||
@ -127,54 +115,30 @@ open http://10.22.68.250:81
|
||||
# 3. Edit configuration:
|
||||
# - Forward Hostname/IP: 57.128.200.27
|
||||
# - Forward Port: 5000 (CRITICAL!)
|
||||
# - Scheme: http
|
||||
# 4. Save and test
|
||||
```
|
||||
#### Solution
|
||||
|
||||
**Option B: Fix via NPM API**
|
||||
```bash
|
||||
# If nginx is down:
|
||||
ssh maciejpi@57.128.200.27 "sudo systemctl restart nginx"
|
||||
|
||||
```python
|
||||
import requests
|
||||
# If Gunicorn is down:
|
||||
ssh maciejpi@57.128.200.27 "sudo systemctl restart nordabiznes"
|
||||
|
||||
NPM_URL = "http://10.22.68.250:81/api"
|
||||
# Login to get token first (see NPM API docs)
|
||||
|
||||
data = {
|
||||
"domain_names": ["nordabiznes.pl", "www.nordabiznes.pl"],
|
||||
"forward_scheme": "http",
|
||||
"forward_host": "57.128.200.27",
|
||||
"forward_port": 5000, # CRITICAL: Must be 5000!
|
||||
"certificate_id": 27,
|
||||
"ssl_forced": True,
|
||||
"http2_support": True
|
||||
}
|
||||
|
||||
response = requests.put(
|
||||
f"{NPM_URL}/nginx/proxy-hosts/27",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
json=data
|
||||
)
|
||||
# If nginx config is broken:
|
||||
ssh maciejpi@57.128.200.27 "sudo nginx -t && sudo systemctl reload nginx"
|
||||
```
|
||||
|
||||
#### Verification
|
||||
|
||||
```bash
|
||||
# 1. External test (from outside INPI network)
|
||||
curl -I https://nordabiznes.pl/health
|
||||
# Expected: HTTP/2 200
|
||||
|
||||
# 2. Check NPM logs
|
||||
ssh maciejpi@10.22.68.250
|
||||
docker logs nginx-proxy-manager_app_1 --tail 20
|
||||
# Should show 200 responses, not 301
|
||||
```
|
||||
|
||||
#### Prevention
|
||||
|
||||
- **ALWAYS verify port 5000 after ANY NPM configuration change**
|
||||
- Add monitoring alert for non-200 responses on /health
|
||||
- Document NPM configuration in change requests
|
||||
- Test from external network before marking changes complete
|
||||
- Monitor /health endpoint for non-200 responses
|
||||
- Test nginx config before reload: `sudo nginx -t`
|
||||
|
||||
---
|
||||
|
||||
@ -185,14 +149,13 @@ docker logs nginx-proxy-manager_app_1 --tail 20
|
||||
#### Symptoms
|
||||
|
||||
- Browser shows "502 Bad Gateway" error
|
||||
- NPM logs show "upstream connection failed"
|
||||
- nginx error log shows "upstream connection failed"
|
||||
- Site completely inaccessible
|
||||
|
||||
#### Root Causes
|
||||
|
||||
1. Flask/Gunicorn service stopped
|
||||
2. Backend server (57.128.200.27) unreachable
|
||||
3. Firewall blocking port 5000
|
||||
2. Gunicorn not listening on 127.0.0.1:5000
|
||||
|
||||
#### Diagnosis
|
||||
|
||||
@ -233,28 +196,26 @@ curl http://localhost:5000/health
|
||||
```bash
|
||||
# Check for syntax errors
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 -m py_compile app.py
|
||||
/var/www/nordabiznes/venv/bin/python3 -m py_compile app.py
|
||||
|
||||
# Check for missing dependencies
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 -c "import flask; import sqlalchemy"
|
||||
/var/www/nordabiznes/venv/bin/python3 -c "import flask; import sqlalchemy"
|
||||
|
||||
# Check environment variables
|
||||
sudo -u www-data cat /var/www/nordabiznes/.env | grep -v "PASSWORD\|SECRET\|KEY"
|
||||
cat /var/www/nordabiznes/.env | grep -v "PASSWORD\|SECRET\|KEY"
|
||||
|
||||
# Try running manually (for debugging)
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 app.py
|
||||
/var/www/nordabiznes/venv/bin/python3 app.py
|
||||
```
|
||||
|
||||
**If network issue:**
|
||||
**If Gunicorn not responding:**
|
||||
|
||||
```bash
|
||||
# Test connectivity from NPM to backend
|
||||
ssh maciejpi@10.22.68.250
|
||||
curl -I http://57.128.200.27:5000/health
|
||||
# Check if Gunicorn is listening
|
||||
ssh maciejpi@57.128.200.27 "ss -tlnp | grep 5000"
|
||||
|
||||
# Check firewall rules
|
||||
ssh maciejpi@57.128.200.27
|
||||
sudo iptables -L -n | grep 5000
|
||||
# Check nginx error log
|
||||
ssh maciejpi@57.128.200.27 "sudo tail -20 /var/log/nginx/error.log"
|
||||
```
|
||||
|
||||
#### Verification
|
||||
@ -292,11 +253,11 @@ ps aux | grep gunicorn
|
||||
# Look for zombie workers or high CPU usage
|
||||
|
||||
# 2. Check database connections
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -c \
|
||||
psql -h localhost -U nordabiz_app -d nordabiz -c \
|
||||
"SELECT count(*) FROM pg_stat_activity WHERE datname = 'nordabiz';"
|
||||
|
||||
# 3. Check for long-running queries
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -c \
|
||||
psql -h localhost -U nordabiz_app -d nordabiz -c \
|
||||
"SELECT pid, now() - query_start AS duration, query
|
||||
FROM pg_stat_activity
|
||||
WHERE state = 'active' AND now() - query_start > interval '5 seconds';"
|
||||
@ -316,7 +277,7 @@ sudo journalctl -u nordabiznes -n 100 --no-pager | grep -E "slow|timeout|took"
|
||||
|
||||
```bash
|
||||
# Identify and kill long-running query
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
# Find problematic query
|
||||
SELECT pid, query FROM pg_stat_activity
|
||||
@ -383,11 +344,8 @@ echo | openssl s_client -servername nordabiznes.pl -connect nordabiznes.pl:443 2
|
||||
# 2. Check certificate details
|
||||
curl -vI https://nordabiznes.pl 2>&1 | grep -E "SSL|certificate"
|
||||
|
||||
# 3. Check NPM certificate status
|
||||
ssh maciejpi@10.22.68.250
|
||||
docker exec nginx-proxy-manager_app_1 \
|
||||
sqlite3 /data/database.sqlite \
|
||||
"SELECT id, nice_name, expires_on FROM certificate WHERE id = 27;"
|
||||
# 3. Check certbot certificate status
|
||||
ssh maciejpi@57.128.200.27 "sudo certbot certificates"
|
||||
```
|
||||
|
||||
#### Solution
|
||||
@ -395,18 +353,12 @@ docker exec nginx-proxy-manager_app_1 \
|
||||
**If certificate expired:**
|
||||
|
||||
```bash
|
||||
# NPM auto-renews Let's Encrypt certificates
|
||||
# Force renewal via NPM UI or API
|
||||
# certbot auto-renews Let's Encrypt certificates
|
||||
# Force renewal:
|
||||
ssh maciejpi@57.128.200.27 "sudo certbot renew --force-renewal"
|
||||
|
||||
# Via UI:
|
||||
# 1. Access http://10.22.68.250:81
|
||||
# 2. SSL Certificates → nordabiznes.pl
|
||||
# 3. Click "Renew" button
|
||||
|
||||
# Via CLI (if auto-renewal failed):
|
||||
ssh maciejpi@10.22.68.250
|
||||
docker exec nginx-proxy-manager_app_1 \
|
||||
node /app/index.js certificate renew 27
|
||||
# Reload nginx after renewal:
|
||||
ssh maciejpi@57.128.200.27 "sudo systemctl reload nginx"
|
||||
```
|
||||
|
||||
**If mixed content warnings:**
|
||||
@ -445,9 +397,9 @@ nslookup nordabiznes.inpi.local 10.22.68.1
|
||||
# 3. Test from different locations
|
||||
curl -I -H "Host: nordabiznes.pl" http://85.237.177.83/health
|
||||
|
||||
# 4. Check Fortigate NAT rules
|
||||
# Access Fortigate admin panel and verify NAT entry:
|
||||
# External: 85.237.177.83:443 → Internal: 10.22.68.250:443
|
||||
# 4. Check DNS points to OVH VPS
|
||||
dig nordabiznes.pl +short
|
||||
# Expected: 57.128.200.27
|
||||
```
|
||||
|
||||
#### Solution
|
||||
@ -494,7 +446,7 @@ sudo journalctl -u nordabiznes -n 100 --no-pager
|
||||
|
||||
# 3. Try manual start for detailed error
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 app.py
|
||||
/var/www/nordabiznes/venv/bin/python3 app.py
|
||||
# Read the traceback carefully
|
||||
```
|
||||
|
||||
@ -507,12 +459,12 @@ sudo -u www-data /var/www/nordabiznes/venv/bin/python3 app.py
|
||||
# Cause: Recent code change introduced syntax error
|
||||
|
||||
# Fix: Check syntax
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 -m py_compile app.py
|
||||
/var/www/nordabiznes/venv/bin/python3 -m py_compile app.py
|
||||
|
||||
# Rollback if necessary
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data git log --oneline -5
|
||||
sudo -u www-data git revert HEAD # or specific commit
|
||||
git log --oneline -5
|
||||
git revert HEAD # or specific commit
|
||||
sudo systemctl restart nordabiznes
|
||||
```
|
||||
|
||||
@ -523,8 +475,8 @@ sudo systemctl restart nordabiznes
|
||||
# Cause: .env file missing or incomplete
|
||||
|
||||
# Fix: Check .env exists and has required variables
|
||||
sudo -u www-data ls -la /var/www/nordabiznes/.env
|
||||
sudo -u www-data cat /var/www/nordabiznes/.env | grep -E "^[A-Z_]+=" | wc -l
|
||||
ls -la /var/www/nordabiznes/.env
|
||||
cat /var/www/nordabiznes/.env | grep -E "^[A-Z_]+=" | wc -l
|
||||
# Should have ~20 environment variables
|
||||
|
||||
# Required variables (add if missing):
|
||||
@ -547,7 +499,7 @@ sudo -u www-data cat /var/www/nordabiznes/.env | grep -E "^[A-Z_]+=" | wc -l
|
||||
sudo systemctl status postgresql
|
||||
|
||||
# Test connection
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
||||
psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
||||
|
||||
# If password wrong, update .env and restart
|
||||
```
|
||||
@ -560,10 +512,10 @@ sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
||||
|
||||
# Fix: Reinstall dependencies
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/pip install -r requirements.txt
|
||||
/var/www/nordabiznes/venv/bin/pip install -r requirements.txt
|
||||
|
||||
# Verify specific package
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/pip show flask
|
||||
/var/www/nordabiznes/venv/bin/pip show flask
|
||||
```
|
||||
|
||||
**E. Port 5000 Already in Use**
|
||||
@ -635,7 +587,7 @@ sudo journalctl -u nordabiznes -n 100 | grep -i jinja
|
||||
|
||||
# Test template syntax
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 -c "
|
||||
/var/www/nordabiznes/venv/bin/python3 -c "
|
||||
from jinja2 import Template
|
||||
with open('templates/index.html') as f:
|
||||
Template(f.read())
|
||||
@ -669,7 +621,7 @@ with open('templates/index.html') as f:
|
||||
sudo journalctl -u nordabiznes -n 50 | grep -i "sqlalchemy\|database"
|
||||
|
||||
# Check database connectivity
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
||||
psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
||||
```
|
||||
|
||||
---
|
||||
@ -695,7 +647,7 @@ ssh maciejpi@57.128.200.27
|
||||
sudo journalctl -u nordabiznes -n 100 | grep -i search
|
||||
|
||||
# 3. Test database FTS
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
# Test FTS query
|
||||
SELECT name, ts_rank(search_vector, to_tsquery('polish', 'web')) AS score
|
||||
@ -716,7 +668,7 @@ SELECT * FROM pg_extension WHERE extname = 'pg_trgm';
|
||||
# Cause: search_vector not updated
|
||||
|
||||
# Fix: Rebuild FTS index
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
UPDATE companies SET search_vector =
|
||||
to_tsvector('polish',
|
||||
@ -750,7 +702,7 @@ grep -A 20 "SYNONYM_EXPANSION" search_service.py
|
||||
# Cause: Missing database indexes
|
||||
|
||||
# Fix: Add indexes
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_companies_search_vector ON companies USING gin(search_vector);
|
||||
CREATE INDEX IF NOT EXISTS idx_companies_name_trgm ON companies USING gin(name gin_trgm_ops);
|
||||
@ -787,7 +739,7 @@ Quick check:
|
||||
```bash
|
||||
# 1. Verify Gemini API key
|
||||
ssh maciejpi@57.128.200.27
|
||||
sudo -u www-data cat /var/www/nordabiznes/.env | grep GEMINI_API_KEY
|
||||
cat /var/www/nordabiznes/.env | grep GEMINI_API_KEY
|
||||
# Should not be empty
|
||||
|
||||
# 2. Test Gemini API directly
|
||||
@ -877,7 +829,7 @@ sudo systemctl restart nordabiznes
|
||||
#### Verification
|
||||
|
||||
```bash
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT count(*) FROM companies;"
|
||||
psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT count(*) FROM companies;"
|
||||
# Should return count
|
||||
```
|
||||
|
||||
@ -897,7 +849,7 @@ sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT count(
|
||||
|
||||
```bash
|
||||
# 1. Check for slow queries
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT pid, now() - query_start AS duration, query
|
||||
FROM pg_stat_activity
|
||||
@ -927,7 +879,7 @@ ALTER DATABASE nordabiz SET log_min_duration_statement = 1000;
|
||||
|
||||
```bash
|
||||
# Add appropriate indexes based on queries
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
-- Example: Index on foreign key
|
||||
CREATE INDEX idx_company_news_company_id ON company_news(company_id);
|
||||
@ -945,7 +897,7 @@ VACUUM ANALYZE;
|
||||
|
||||
```bash
|
||||
# Run vacuum
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
VACUUM ANALYZE;
|
||||
|
||||
# For severe cases
|
||||
@ -955,7 +907,7 @@ VACUUM FULL companies; -- Locks table!
|
||||
**If table statistics outdated:**
|
||||
|
||||
```bash
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
ANALYZE companies;
|
||||
ANALYZE users;
|
||||
ANALYZE ai_chat_messages;
|
||||
@ -991,7 +943,7 @@ df -h
|
||||
# Check /var/lib/postgresql usage
|
||||
|
||||
# 2. Check database size
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT pg_size_pretty(pg_database_size('nordabiz'));
|
||||
|
||||
@ -1038,7 +990,7 @@ sudo -u postgres pg_archivecleanup /var/lib/postgresql/*/main/pg_wal/ 0000000100
|
||||
|
||||
```bash
|
||||
# Archive old data before deletion
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
-- Example: Archive old AI chat messages (>6 months)
|
||||
CREATE TABLE ai_chat_messages_archive AS
|
||||
@ -1067,7 +1019,7 @@ VACUUM FULL ai_chat_messages;
|
||||
|
||||
```bash
|
||||
# 1. Check current schema version
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
\dt
|
||||
# List all tables
|
||||
|
||||
@ -1079,7 +1031,7 @@ ls -la /var/www/nordabiznes/database/migrations/
|
||||
|
||||
# 3. Check Flask-Migrate status (if using Alembic)
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/flask db current
|
||||
/var/www/nordabiznes/venv/bin/flask db current
|
||||
```
|
||||
|
||||
#### Solution
|
||||
@ -1089,14 +1041,14 @@ sudo -u www-data /var/www/nordabiznes/venv/bin/flask db current
|
||||
```bash
|
||||
# Re-run migration script
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz < database/schema.sql
|
||||
psql -h localhost -U nordabiz_app -d nordabiz < database/schema.sql
|
||||
```
|
||||
|
||||
**If column added but missing:**
|
||||
|
||||
```bash
|
||||
# Add column manually
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
ALTER TABLE companies ADD COLUMN IF NOT EXISTS new_column VARCHAR(255);
|
||||
|
||||
@ -1108,16 +1060,16 @@ GRANT ALL ON TABLE companies TO nordabiz_app;
|
||||
|
||||
```bash
|
||||
# Rollback last migration
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/flask db downgrade
|
||||
/var/www/nordabiznes/venv/bin/flask db downgrade
|
||||
|
||||
# Re-apply
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/flask db upgrade
|
||||
/var/www/nordabiznes/venv/bin/flask db upgrade
|
||||
```
|
||||
|
||||
#### Verification
|
||||
|
||||
```bash
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
\d companies
|
||||
# Verify schema matches expected structure
|
||||
```
|
||||
@ -1141,7 +1093,7 @@ sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
```bash
|
||||
# 1. Check API usage in database
|
||||
ssh maciejpi@57.128.200.27
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
-- Gemini API usage today
|
||||
SELECT COUNT(*), SUM(input_tokens), SUM(output_tokens)
|
||||
@ -1211,7 +1163,7 @@ pkill -f seo_audit.py
|
||||
# Create script: /var/www/nordabiznes/scripts/check_api_quotas.sh
|
||||
|
||||
#!/bin/bash
|
||||
GEMINI_COUNT=$(sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -t -c \
|
||||
GEMINI_COUNT=$(psql -h localhost -U nordabiz_app -d nordabiz -t -c \
|
||||
"SELECT COUNT(*) FROM ai_api_costs WHERE DATE(created_at) = CURRENT_DATE;")
|
||||
|
||||
if [ "$GEMINI_COUNT" -gt 1400 ]; then
|
||||
@ -1240,7 +1192,7 @@ fi
|
||||
|
||||
```bash
|
||||
# 1. Test Gemini API directly
|
||||
GEMINI_KEY=$(sudo -u www-data grep GEMINI_API_KEY /var/www/nordabiznes/.env | cut -d= -f2)
|
||||
GEMINI_KEY=$( grep GEMINI_API_KEY /var/www/nordabiznes/.env | cut -d= -f2)
|
||||
|
||||
curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent" \
|
||||
-H "x-goog-api-key: $GEMINI_KEY" \
|
||||
@ -1252,7 +1204,7 @@ ssh maciejpi@57.128.200.27
|
||||
sudo journalctl -u nordabiznes -n 100 | grep -i gemini
|
||||
|
||||
# 3. Check conversation ownership
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT id, user_id, created_at
|
||||
FROM ai_chat_conversations
|
||||
@ -1268,7 +1220,7 @@ WHERE id = 123; -- Replace with conversation ID
|
||||
# OR context too long
|
||||
|
||||
# Check last message for safety filter
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT message, ai_response, error_message
|
||||
FROM ai_chat_messages
|
||||
@ -1325,7 +1277,7 @@ AND created_at < (
|
||||
# Symptom: 401 Unauthorized or 403 Forbidden
|
||||
|
||||
# Verify API key
|
||||
sudo -u www-data grep GEMINI_API_KEY /var/www/nordabiznes/.env
|
||||
grep GEMINI_API_KEY /var/www/nordabiznes/.env
|
||||
|
||||
# Test key directly
|
||||
curl -H "x-goog-api-key: YOUR_KEY" \
|
||||
@ -1363,7 +1315,7 @@ curl -X POST https://nordabiznes.pl/api/chat \
|
||||
|
||||
```bash
|
||||
# 1. Test PageSpeed API directly
|
||||
PAGESPEED_KEY=$(sudo -u www-data grep GOOGLE_PAGESPEED_API_KEY /var/www/nordabiznes/.env | cut -d= -f2)
|
||||
PAGESPEED_KEY=$( grep GOOGLE_PAGESPEED_API_KEY /var/www/nordabiznes/.env | cut -d= -f2)
|
||||
|
||||
curl "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://nordabiznes.pl&key=$PAGESPEED_KEY"
|
||||
|
||||
@ -1372,7 +1324,7 @@ ssh maciejpi@57.128.200.27
|
||||
sudo journalctl -u nordabiznes -n 100 | grep -i pagespeed
|
||||
|
||||
# 3. Check recent audits
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT company_id, url, seo_score, performance_score,
|
||||
audited_at, error_message
|
||||
@ -1435,13 +1387,13 @@ python seo_audit.py --batch 1-10
|
||||
|
||||
```bash
|
||||
# 1. Test Brave API directly
|
||||
BRAVE_KEY=$(sudo -u www-data grep BRAVE_SEARCH_API_KEY /var/www/nordabiznes/.env | cut -d= -f2)
|
||||
BRAVE_KEY=$( grep BRAVE_SEARCH_API_KEY /var/www/nordabiznes/.env | cut -d= -f2)
|
||||
|
||||
curl -H "X-Subscription-Token: $BRAVE_KEY" \
|
||||
"https://api.search.brave.com/res/v1/news/search?q=test&count=5"
|
||||
|
||||
# 2. Check usage this month
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT COUNT(*) AS searches_this_month
|
||||
FROM company_news
|
||||
@ -1496,7 +1448,7 @@ sudo systemctl restart nordabiznes
|
||||
```bash
|
||||
# 1. Check user exists and is active
|
||||
ssh maciejpi@57.128.200.27
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT id, email, is_active, email_verified, failed_login_attempts
|
||||
FROM users
|
||||
@ -1519,7 +1471,7 @@ curl -c /tmp/cookies.txt -X POST http://localhost:5000/login \
|
||||
|
||||
```bash
|
||||
# Check failed attempts
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT email, failed_login_attempts, last_failed_login
|
||||
FROM users
|
||||
@ -1618,7 +1570,7 @@ curl -c /tmp/cookies.txt -X POST http://localhost:5000/login \
|
||||
|
||||
```bash
|
||||
# 1. Check user role
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT id, email, is_admin, is_norda_member
|
||||
FROM users
|
||||
@ -1639,7 +1591,7 @@ sudo journalctl -u nordabiznes -n 50 | grep -i "forbidden\|unauthorized"
|
||||
|
||||
```bash
|
||||
# Grant admin role
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
UPDATE users SET is_admin = TRUE
|
||||
WHERE email = 'admin@nordabiznes.pl';
|
||||
@ -1691,7 +1643,7 @@ WHERE email = 'user@example.com';
|
||||
|
||||
```bash
|
||||
# 1. Check user reset token
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT email, reset_token, reset_token_expiry
|
||||
FROM users
|
||||
@ -1702,7 +1654,7 @@ sudo journalctl -u nordabiznes -n 100 | grep -i "email\|smtp"
|
||||
|
||||
# 3. Test MS Graph API (email service)
|
||||
# Check if AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID set
|
||||
sudo -u www-data grep AZURE /var/www/nordabiznes/.env
|
||||
grep AZURE /var/www/nordabiznes/.env
|
||||
```
|
||||
|
||||
#### Solution
|
||||
@ -1722,7 +1674,7 @@ WHERE email = 'user@example.com';
|
||||
|
||||
```bash
|
||||
# Check MS Graph credentials
|
||||
sudo -u www-data python3 << 'EOF'
|
||||
python3 << 'EOF'
|
||||
import os
|
||||
from email_service import EmailService
|
||||
|
||||
@ -1743,14 +1695,14 @@ EOF
|
||||
|
||||
```bash
|
||||
# Generate new password hash
|
||||
sudo -u www-data python3 << 'EOF'
|
||||
python3 << 'EOF'
|
||||
from werkzeug.security import generate_password_hash
|
||||
password = "NewPassword123"
|
||||
print(generate_password_hash(password))
|
||||
EOF
|
||||
|
||||
# Update database
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
UPDATE users SET password_hash = 'HASH_FROM_ABOVE'
|
||||
WHERE email = 'user@example.com';
|
||||
@ -1786,7 +1738,7 @@ top -n 1
|
||||
# Look at: CPU usage, memory usage, load average
|
||||
|
||||
# 4. Check database query times
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT calls, mean_exec_time, query
|
||||
FROM pg_stat_statements
|
||||
@ -1830,7 +1782,7 @@ ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements';
|
||||
sudo systemctl restart postgresql
|
||||
|
||||
# Check slow queries
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT calls, mean_exec_time, query
|
||||
FROM pg_stat_statements
|
||||
@ -2016,7 +1968,7 @@ uptime
|
||||
# Load should be < number of CPU cores
|
||||
|
||||
# 3. Identify CPU-heavy queries
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT pid, now() - query_start AS duration, state, query
|
||||
FROM pg_stat_activity
|
||||
@ -2030,7 +1982,7 @@ ORDER BY now() - query_start DESC;
|
||||
|
||||
```bash
|
||||
# Kill long-running query
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
SELECT pg_terminate_backend(PID);
|
||||
|
||||
# Add index to optimize query
|
||||
@ -2085,11 +2037,10 @@ curl https://nordabiznes.pl/health
|
||||
}
|
||||
|
||||
# Database health
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
||||
psql -h localhost -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
||||
|
||||
# NPM health (from proxy server)
|
||||
ssh maciejpi@10.22.68.250
|
||||
docker ps | grep nginx-proxy-manager
|
||||
# Nginx health (on OVH VPS)
|
||||
ssh maciejpi@57.128.200.27 "sudo systemctl status nginx"
|
||||
# Should show: Up X hours
|
||||
|
||||
# Flask service health
|
||||
@ -2112,9 +2063,8 @@ sudo journalctl -u nordabiznes -f
|
||||
# PostgreSQL logs
|
||||
sudo journalctl -u postgresql -n 50
|
||||
|
||||
# NPM logs
|
||||
ssh maciejpi@10.22.68.250
|
||||
docker logs nginx-proxy-manager_app_1 --tail 50 -f
|
||||
# Nginx logs (OVH VPS)
|
||||
ssh maciejpi@57.128.200.27 "sudo tail -50 /var/log/nginx/error.log"
|
||||
|
||||
# System logs
|
||||
sudo journalctl -n 100
|
||||
@ -2145,7 +2095,7 @@ fi
|
||||
|
||||
```bash
|
||||
# Database performance
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
-- Connection count
|
||||
SELECT count(*) FROM pg_stat_activity;
|
||||
@ -2254,7 +2204,7 @@ sudo systemctl restart nordabiznes
|
||||
|
||||
# If restart fails, check manually
|
||||
cd /var/www/nordabiznes
|
||||
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 app.py
|
||||
/var/www/nordabiznes/venv/bin/python3 app.py
|
||||
# Read error message
|
||||
|
||||
# 3. Common quick fixes:
|
||||
@ -2303,7 +2253,7 @@ sudo systemctl stop nordabiznes
|
||||
sudo -u postgres pg_dump nordabiz > /tmp/nordabiz_emergency_$(date +%Y%m%d_%H%M%S).sql
|
||||
|
||||
# 3. Assess damage
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
-- Check table counts
|
||||
SELECT 'companies' AS table, count(*) FROM companies
|
||||
@ -2341,7 +2291,7 @@ sudo systemctl start nordabiznes
|
||||
|
||||
```bash
|
||||
# Identify missing/corrupted records
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
-- Example: Find companies with NULL required fields
|
||||
SELECT id, slug, name FROM companies WHERE name IS NULL;
|
||||
@ -2372,7 +2322,7 @@ sudo -u postgres pg_dump nordabiz > /tmp/forensic_$(date +%Y%m%d_%H%M%S).sql
|
||||
sudo tar czf /tmp/www_forensic.tar.gz /var/www/nordabiznes/
|
||||
|
||||
# 3. Check for unauthorized access
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
-- Check for new admin users
|
||||
SELECT id, email, created_at, is_admin
|
||||
@ -2406,7 +2356,7 @@ sudo find /var/www/nordabiznes/ -type f -mtime -1 -ls
|
||||
sudo grep -r "eval\|exec\|system\|subprocess" /var/www/nordabiznes/*.py
|
||||
|
||||
# Check database for malicious data
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
|
||||
SELECT * FROM users WHERE email LIKE '%<script%';
|
||||
SELECT * FROM companies WHERE description LIKE '%<script%';
|
||||
@ -2421,7 +2371,7 @@ SELECT * FROM companies WHERE description LIKE '%<script%';
|
||||
# - API keys
|
||||
|
||||
# 2. Revoke compromised sessions
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz
|
||||
psql -h localhost -U nordabiz_app -d nordabiz
|
||||
DELETE FROM flask_sessions; -- Force all users to re-login
|
||||
|
||||
# 3. Update all API keys
|
||||
@ -2449,7 +2399,7 @@ curl -I https://nordabiznes.pl/health && \
|
||||
echo -e "\n=== Service Status ===" && \
|
||||
ssh maciejpi@57.128.200.27 "sudo systemctl status nordabiznes --no-pager | head -5" && \
|
||||
echo -e "\n=== Database Connection ===" && \
|
||||
ssh maciejpi@57.128.200.27 "sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz -c 'SELECT count(*) FROM companies;'" && \
|
||||
ssh maciejpi@57.128.200.27 "psql -h localhost -U nordabiz_app -d nordabiz -c 'SELECT count(*) FROM companies;'" && \
|
||||
echo -e "\n=== Server Load ===" && \
|
||||
ssh maciejpi@57.128.200.27 "uptime"
|
||||
```
|
||||
@ -2476,7 +2426,7 @@ ssh maciejpi@10.22.68.250 "curl -I http://57.128.200.27:5000/health"
|
||||
|
||||
```bash
|
||||
# Database quick stats
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz << 'EOF'
|
||||
psql -h localhost -U nordabiz_app -d nordabiz << 'EOF'
|
||||
SELECT 'Companies' AS metric, count(*) AS value FROM companies
|
||||
UNION ALL SELECT 'Users', count(*) FROM users
|
||||
UNION ALL SELECT 'Active sessions', count(*) FROM pg_stat_activity
|
||||
@ -2484,7 +2434,7 @@ UNION ALL SELECT 'DB size (MB)', pg_database_size('nordabiz')/1024/1024;
|
||||
EOF
|
||||
|
||||
# Find slow queries
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz << 'EOF'
|
||||
psql -h localhost -U nordabiz_app -d nordabiz << 'EOF'
|
||||
SELECT pid, now() - query_start AS duration, query
|
||||
FROM pg_stat_activity
|
||||
WHERE state = 'active' AND now() - query_start > interval '2 seconds'
|
||||
@ -2492,7 +2442,7 @@ ORDER BY duration DESC;
|
||||
EOF
|
||||
|
||||
# Check locks
|
||||
sudo -u www-data psql -h localhost -U nordabiz_app -d nordabiz << 'EOF'
|
||||
psql -h localhost -U nordabiz_app -d nordabiz << 'EOF'
|
||||
SELECT relation::regclass, mode, granted
|
||||
FROM pg_locks
|
||||
WHERE NOT granted;
|
||||
@ -2528,16 +2478,16 @@ echo | openssl s_client -servername nordabiznes.pl -connect nordabiznes.pl:443 2
|
||||
ssh maciejpi@57.128.200.27
|
||||
|
||||
# Gemini API
|
||||
GEMINI_KEY=$(sudo -u www-data grep GEMINI_API_KEY .env | cut -d= -f2)
|
||||
GEMINI_KEY=$( grep GEMINI_API_KEY .env | cut -d= -f2)
|
||||
curl -s -H "x-goog-api-key: $GEMINI_KEY" \
|
||||
"https://generativelanguage.googleapis.com/v1beta/models" | jq '.models[0].name'
|
||||
|
||||
# PageSpeed API
|
||||
PAGESPEED_KEY=$(sudo -u www-data grep GOOGLE_PAGESPEED_API_KEY .env | cut -d= -f2)
|
||||
PAGESPEED_KEY=$( grep GOOGLE_PAGESPEED_API_KEY .env | cut -d= -f2)
|
||||
curl -s "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://nordabiznes.pl&key=$PAGESPEED_KEY" | jq '.lighthouseResult.categories.performance.score'
|
||||
|
||||
# Brave Search API
|
||||
BRAVE_KEY=$(sudo -u www-data grep BRAVE_SEARCH_API_KEY .env | cut -d= -f2)
|
||||
BRAVE_KEY=$( grep BRAVE_SEARCH_API_KEY .env | cut -d= -f2)
|
||||
curl -s -H "X-Subscription-Token: $BRAVE_KEY" \
|
||||
"https://api.search.brave.com/res/v1/web/search?q=test&count=1" | jq '.web.results[0].title'
|
||||
|
||||
|
||||
@ -1,34 +1,28 @@
|
||||
# HTTP Request Flow
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** 2026-01-10
|
||||
**Status:** Production LIVE
|
||||
**Last Updated:** 2026-04-04
|
||||
**Status:** Production LIVE (OVH VPS)
|
||||
**Flow Type:** HTTP Request Handling & Response Cycle
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the **complete HTTP request flow** for the Norda Biznes Partner application, from external user through reverse proxy to Flask application and back. It covers:
|
||||
This document describes the **complete HTTP request flow** for the Norda Biznes Partner application, from external user through nginx reverse proxy to Flask application and back. It covers:
|
||||
|
||||
- **Complete request path** (Internet → Fortigate → NPM → Flask → Response)
|
||||
- **Critical port configurations** and routing decisions
|
||||
- **Complete request path** (Internet → Nginx → Flask → Response)
|
||||
- **SSL/TLS termination** and security boundaries
|
||||
- **Request/response transformation** at each layer
|
||||
- **Common failure scenarios** and troubleshooting
|
||||
|
||||
**Key Infrastructure:**
|
||||
- **Public Entry:** 85.237.177.83:443 (Fortigate NAT)
|
||||
- **Reverse Proxy:** NPM on 10.22.68.250:443 (SSL termination)
|
||||
- **Backend Application:** Flask/Gunicorn on 57.128.200.27:5000
|
||||
- **Protocol Flow:** HTTPS → NPM → HTTP → Flask → HTTP → NPM → HTTPS
|
||||
- **Public Entry:** 57.128.200.27:443 (OVH VPS, direct)
|
||||
- **Reverse Proxy:** Nginx on 57.128.200.27:443 (SSL termination)
|
||||
- **Backend Application:** Flask/Gunicorn on 127.0.0.1:5000
|
||||
- **Protocol Flow:** HTTPS → Nginx → HTTP (localhost) → Flask → HTTP → Nginx → HTTPS
|
||||
|
||||
**⚠️ CRITICAL CONFIGURATION:**
|
||||
```
|
||||
NPM MUST forward to port 5000, NOT port 80!
|
||||
Port 80 on NORDABIZ-01 runs nginx that redirects to HTTPS
|
||||
Forwarding to port 80 causes infinite redirect loop
|
||||
```
|
||||
> **Note:** The old on-prem setup used FortiGate NAT (85.237.177.83) and NPM (10.22.68.250) as intermediate layers. Production now runs directly on OVH VPS without FortiGate or NPM.
|
||||
|
||||
**Related Documentation:**
|
||||
- Incident Report: `docs/INCIDENT_REPORT_20260102.md` (ERR_TOO_MANY_REDIRECTS)
|
||||
@ -45,24 +39,19 @@ Forwarding to port 80 causes infinite redirect loop
|
||||
sequenceDiagram
|
||||
actor User
|
||||
participant Browser
|
||||
participant Fortigate as 🛡️ Fortigate Firewall<br/>85.237.177.83
|
||||
participant NPM as 🔒 NPM Reverse Proxy<br/>10.22.68.250:443
|
||||
participant Flask as 🌐 Flask/Gunicorn<br/>57.128.200.27:5000
|
||||
participant Nginx as 🔒 Nginx Reverse Proxy<br/>57.128.200.27:443
|
||||
participant Flask as 🌐 Flask/Gunicorn<br/>127.0.0.1:5000
|
||||
participant DB as 💾 PostgreSQL<br/>localhost:5432
|
||||
|
||||
Note over User,DB: SUCCESSFUL REQUEST FLOW
|
||||
|
||||
User->>Browser: Navigate to https://nordabiznes.pl/
|
||||
Browser->>Fortigate: HTTPS GET / (Port 443)
|
||||
Note over Fortigate: NAT Translation<br/>85.237.177.83:443 → 10.22.68.250:443
|
||||
Browser->>Nginx: HTTPS GET / (Port 443)
|
||||
Note over Nginx: SSL/TLS Termination<br/>Let's Encrypt Certificate (certbot)
|
||||
|
||||
Fortigate->>NPM: HTTPS GET / (Port 443)
|
||||
Note over NPM: SSL/TLS Termination<br/>Let's Encrypt Certificate<br/>nordabiznes.pl (Cert ID: 27)
|
||||
Note over Nginx: Request Processing<br/>• Validate certificate<br/>• Decrypt HTTPS<br/>• Extract headers<br/>• proxy_pass to localhost:5000
|
||||
|
||||
Note over NPM: Request Processing<br/>• Validate certificate<br/>• Decrypt HTTPS<br/>• Extract headers<br/>• Check routing rules
|
||||
|
||||
NPM->>Flask: HTTP GET / (Port 5000) ✓
|
||||
Note over NPM,Flask: ⚠️ CRITICAL: Port 5000<br/>NOT port 80!
|
||||
Nginx->>Flask: HTTP GET / (127.0.0.1:5000)
|
||||
|
||||
Note over Flask: Flask Request Handling<br/>• WSGI via Gunicorn<br/>• Route matching (app.py)<br/>• Session validation<br/>• CSRF check (if POST)
|
||||
|
||||
@ -71,26 +60,27 @@ sequenceDiagram
|
||||
|
||||
Note over Flask: Template Rendering<br/>• Jinja2 template: index.html<br/>• Inject company data<br/>• Apply filters & sorting
|
||||
|
||||
Flask->>NPM: HTTP 200 OK<br/>Content-Type: text/html<br/>Set-Cookie: session=...<br/>HTML content
|
||||
Flask->>Nginx: HTTP 200 OK<br/>Content-Type: text/html<br/>Set-Cookie: session=...<br/>HTML content
|
||||
|
||||
Note over NPM: Response Processing<br/>• Encrypt response (HTTPS)<br/>• Add security headers<br/>• HSTS, CSP, X-Frame-Options
|
||||
Note over Nginx: Response Processing<br/>• Encrypt response (HTTPS)<br/>• Add security headers<br/>• HSTS, CSP, X-Frame-Options
|
||||
|
||||
NPM->>Fortigate: HTTPS 200 OK (Port 443)
|
||||
Fortigate->>Browser: HTTPS 200 OK
|
||||
Nginx->>Browser: HTTPS 200 OK
|
||||
Browser->>User: Display page
|
||||
```
|
||||
|
||||
### 1.2 Failed Request (Wrong Port Configuration)
|
||||
### 1.2 Historical: Failed Request (Old NPM Setup - Port Misconfiguration)
|
||||
|
||||
> **Note:** This failure scenario applied to the old on-prem setup with NPM. It is no longer applicable to the current OVH VPS production setup.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor User
|
||||
participant Browser
|
||||
participant NPM as 🔒 NPM Reverse Proxy<br/>10.22.68.250:443
|
||||
participant NginxSys as ⚠️ Nginx System<br/>57.128.200.27:80
|
||||
participant Flask as 🌐 Flask/Gunicorn<br/>57.128.200.27:5000
|
||||
participant NginxSys as ⚠️ Nginx System<br/>10.22.68.249:80
|
||||
participant Flask as 🌐 Flask/Gunicorn<br/>10.22.68.249:5000
|
||||
|
||||
Note over User,Flask: FAILED REQUEST FLOW (REDIRECT LOOP)
|
||||
Note over User,Flask: FAILED REQUEST FLOW (REDIRECT LOOP) — HISTORICAL
|
||||
|
||||
User->>Browser: Navigate to https://nordabiznes.pl/
|
||||
Browser->>NPM: HTTPS GET / (Port 443)
|
||||
@ -120,11 +110,11 @@ sequenceDiagram
|
||||
|
||||
## 2. Layer-by-Layer Request Processing
|
||||
|
||||
### 2.1 Layer 1: Fortigate Firewall (NAT Gateway)
|
||||
### 2.1 Layer 1: DNS + Direct Connection (OVH VPS)
|
||||
|
||||
**Server:** Fortigate Firewall
|
||||
**Public IP:** 85.237.177.83
|
||||
**Function:** Network Address Translation (NAT) + Firewall
|
||||
**Server:** OVH VPS
|
||||
**Public IP:** 57.128.200.27
|
||||
**Function:** Direct internet-facing server (no NAT, no FortiGate for production)
|
||||
|
||||
**Processing Steps:**
|
||||
|
||||
@ -160,12 +150,12 @@ Firewall: ALLOW from any to 85.237.177.83:443 (state: NEW,ESTABLISHED)
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Layer 2: NPM Reverse Proxy (SSL Termination)
|
||||
### 2.2 Layer 2: Nginx Reverse Proxy (SSL Termination)
|
||||
|
||||
**Server:** R11-REVPROXY-01 (VM 119)
|
||||
**IP:** 10.22.68.250
|
||||
**Server:** OVH VPS (inpi-vps-waw01)
|
||||
**IP:** 57.128.200.27
|
||||
**Port:** 443 (HTTPS)
|
||||
**Technology:** Nginx Proxy Manager (Docker)
|
||||
**Technology:** Nginx with Let's Encrypt (certbot)
|
||||
|
||||
**Processing Steps:**
|
||||
|
||||
@ -230,7 +220,7 @@ Firewall: ALLOW from any to 85.237.177.83:443 (state: NEW,ESTABLISHED)
|
||||
|-----------|-------|-------|
|
||||
| Domain Names | nordabiznes.pl, www.nordabiznes.pl | Primary + www alias |
|
||||
| Forward Scheme | http | NPM→Backend uses HTTP (secure internal network) |
|
||||
| Forward Host | 57.128.200.27 | NORDABIZ-01 backend server |
|
||||
| Forward Host | 127.0.0.1 | Localhost (same OVH VPS) |
|
||||
| **Forward Port** | **5000** | **Flask/Gunicorn port (CRITICAL!)** |
|
||||
| SSL Certificate | 27 (Let's Encrypt) | Auto-renewal enabled |
|
||||
| SSL Forced | Yes | Redirect HTTP→HTTPS |
|
||||
@ -254,8 +244,8 @@ ssh maciejpi@10.22.68.250 "docker exec nginx-proxy-manager_app_1 \
|
||||
|
||||
### 2.3 Layer 3: Flask/Gunicorn Application (Request Processing)
|
||||
|
||||
**Server:** NORDABIZ-01 (VM 249)
|
||||
**IP:** 57.128.200.27
|
||||
**Server:** OVH VPS (inpi-vps-waw01)
|
||||
**IP:** 127.0.0.1 (localhost, via nginx proxy_pass)
|
||||
**Port:** 5000
|
||||
**Technology:** Gunicorn 20.1.0 + Flask 3.0
|
||||
|
||||
@ -263,7 +253,7 @@ ssh maciejpi@10.22.68.250 "docker exec nginx-proxy-manager_app_1 \
|
||||
|
||||
1. **Gunicorn Receives HTTP Request:**
|
||||
```
|
||||
Binding: 0.0.0.0:5000 (all interfaces)
|
||||
Binding: 127.0.0.1:5000 (all interfaces)
|
||||
Workers: 4 (Gunicorn worker processes)
|
||||
Worker Class: sync (synchronous workers)
|
||||
Timeout: 120 seconds
|
||||
@ -382,11 +372,11 @@ ssh maciejpi@10.22.68.250 "docker exec nginx-proxy-manager_app_1 \
|
||||
```ini
|
||||
# /etc/systemd/system/nordabiznes.service
|
||||
[Service]
|
||||
User=www-data
|
||||
Group=www-data
|
||||
User=maciejpi
|
||||
Group=maciejpi
|
||||
WorkingDirectory=/var/www/nordabiznes
|
||||
ExecStart=/var/www/nordabiznes/venv/bin/gunicorn \
|
||||
--bind 0.0.0.0:5000 \
|
||||
--bind 127.0.0.1:5000 \
|
||||
--workers 4 \
|
||||
--timeout 120 \
|
||||
--access-logfile /var/log/nordabiznes/access.log \
|
||||
@ -410,7 +400,7 @@ ssh maciejpi@57.128.200.27 "ps aux | grep gunicorn"
|
||||
|
||||
### 2.4 Layer 4: PostgreSQL Database (Data Retrieval)
|
||||
|
||||
**Server:** NORDABIZ-01 (same server as Flask)
|
||||
**Server:** OVH VPS (same server as Flask)
|
||||
**IP:** 127.0.0.1 (localhost only)
|
||||
**Port:** 5432
|
||||
**Technology:** PostgreSQL 14
|
||||
@ -490,7 +480,7 @@ sequenceDiagram
|
||||
Note over Flask: Response Construction
|
||||
Flask->>Flask: Render Jinja2 template<br/>HTML content (50 KB)
|
||||
|
||||
Flask->>NPM: HTTP/1.1 200 OK<br/>Content-Type: text/html; charset=utf-8<br/>Content-Length: 51234<br/>Set-Cookie: session=...<br/><br/>[HTML content]
|
||||
Flask->>Nginx: HTTP/1.1 200 OK<br/>Content-Type: text/html; charset=utf-8<br/>Content-Length: 51234<br/>Set-Cookie: session=...<br/><br/>[HTML content]
|
||||
|
||||
Note over NPM: Response Processing
|
||||
NPM->>NPM: Add security headers:<br/>• Strict-Transport-Security<br/>• X-Frame-Options: SAMEORIGIN<br/>• X-Content-Type-Options: nosniff<br/>• Referrer-Policy: strict-origin
|
||||
@ -499,7 +489,7 @@ sequenceDiagram
|
||||
|
||||
NPM->>NPM: Encrypt response (TLS 1.3)<br/>Certificate: Let's Encrypt
|
||||
|
||||
NPM->>Fortigate: HTTPS 200 OK<br/>Encrypted response<br/>Size: 12 KB (gzip)
|
||||
Nginx->>Browser: HTTPS 200 OK<br/>Encrypted response<br/>Size: 12 KB (gzip)
|
||||
|
||||
Note over Fortigate: NAT reverse translation
|
||||
Fortigate->>Browser: HTTPS 200 OK<br/>Source: 85.237.177.83
|
||||
@ -579,27 +569,27 @@ x-request-id: abc123def456
|
||||
│ HTTP (Port 5000) ✓
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ FLASK/GUNICORN (NORDABIZ-01) │
|
||||
│ FLASK/GUNICORN (OVH VPS) │
|
||||
│ IP: 57.128.200.27:5000 │
|
||||
│ Binding: 0.0.0.0:5000 │
|
||||
│ Binding: 127.0.0.1:5000 │
|
||||
│ Workers: 4 (Gunicorn) │
|
||||
│ Function: Application logic, template rendering │
|
||||
└────────────────────────────┬────────────────────────────────────┘
|
||||
│ SQL (localhost:5432)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ POSTGRESQL (NORDABIZ-01) │
|
||||
│ POSTGRESQL (OVH VPS) │
|
||||
│ IP: 127.0.0.1:5432 (localhost only) │
|
||||
│ Database: nordabiz │
|
||||
│ Function: Data storage │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.2 Port Table (NORDABIZ-01)
|
||||
### 4.2 Port Table (OVH VPS)
|
||||
|
||||
| Port | Service | Binding | User | Purpose | NPM Should Use? |
|
||||
|------|---------|---------|------|---------|-----------------|
|
||||
| **5000** | **Gunicorn/Flask** | **0.0.0.0** | **www-data** | **Main Application** | **✓ YES** |
|
||||
| **5000** | **Gunicorn/Flask** | **0.0.0.0** | **maciejpi** | **Main Application** | **✓ YES** |
|
||||
| 80 | Nginx (system) | 0.0.0.0 | root | HTTP→HTTPS redirect | ❌ NO (causes loop!) |
|
||||
| 443 | Nginx (system) | 0.0.0.0 | root | HTTPS redirect | ❌ NO (NPM handles SSL) |
|
||||
| 5432 | PostgreSQL | 127.0.0.1 | postgres | Database | ❌ NO (localhost only) |
|
||||
@ -607,7 +597,7 @@ x-request-id: abc123def456
|
||||
|
||||
**⚠️ CRITICAL WARNING:**
|
||||
```
|
||||
Port 80 and 443 on NORDABIZ-01 run a system nginx that:
|
||||
Port 80 and 443 on OVH VPS run a system nginx that:
|
||||
1. Redirects ALL HTTP requests to HTTPS
|
||||
2. Redirects ALL HTTPS requests to https://nordabiznes.pl
|
||||
|
||||
@ -629,7 +619,7 @@ SOLUTION: NPM must ALWAYS forward to port 5000!
|
||||
|
||||
**Flow:**
|
||||
```
|
||||
User → NPM → Flask → Static file handler → Return CSS
|
||||
User → Nginx → Flask → Static file handler → Return CSS
|
||||
```
|
||||
|
||||
**Flask Handling:**
|
||||
@ -650,7 +640,7 @@ def static_files(filename):
|
||||
|
||||
**Flow:**
|
||||
```
|
||||
User → NPM → Flask → API route → Database → JSON response
|
||||
User → Nginx → Flask → API route → Database → JSON response
|
||||
```
|
||||
|
||||
**Response:**
|
||||
@ -682,7 +672,7 @@ Access-Control-Allow-Origin: * (if CORS enabled)
|
||||
|
||||
**Flow:**
|
||||
```
|
||||
User → NPM → Flask → CSRF validation → Auth check → Database → Redirect
|
||||
User → Nginx → Flask → CSRF validation → Auth check → Database → Redirect
|
||||
```
|
||||
|
||||
**Additional Processing:**
|
||||
@ -707,7 +697,7 @@ Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax
|
||||
|
||||
**Flow:**
|
||||
```
|
||||
Monitor → NPM → Flask → Simple response (no DB query)
|
||||
Monitor → Nginx → Flask → Simple response (no DB query)
|
||||
```
|
||||
|
||||
**Response:**
|
||||
@ -802,7 +792,7 @@ graph TB
|
||||
|-------|-----------------|-------|
|
||||
| Fortigate NAT | < 1 ms | Hardware NAT, negligible latency |
|
||||
| NPM SSL Termination | 10-20 ms | TLS handshake + decryption |
|
||||
| NPM → Flask Network | < 1 ms | Internal 10 Gbps network |
|
||||
| Nginx → Flask Network | < 1 ms | Internal 10 Gbps network |
|
||||
| Flask Request Handling | 50-150 ms | Route matching, template rendering |
|
||||
| Database Query | 10-30 ms | Indexed queries, connection pool |
|
||||
| Template Rendering | 20-50 ms | Jinja2 template compilation |
|
||||
@ -898,7 +888,7 @@ ssh maciejpi@57.128.200.27 "sudo systemctl status nordabiznes"
|
||||
|
||||
# 2. Check if port 5000 is listening
|
||||
ssh maciejpi@57.128.200.27 "sudo netstat -tlnp | grep 5000"
|
||||
# Expected: 0.0.0.0:5000 ... gunicorn
|
||||
# Expected: 127.0.0.1:5000 ... gunicorn
|
||||
|
||||
# 3. Test direct connection
|
||||
curl -I http://57.128.200.27:5000/health
|
||||
@ -1026,7 +1016,7 @@ ssh maciejpi@57.128.200.27 "sudo -u postgres psql -c \
|
||||
|
||||
**Network Connectivity:**
|
||||
```bash
|
||||
# Test NPM → Flask connectivity
|
||||
# Test Nginx → Flask connectivity
|
||||
ssh maciejpi@10.22.68.250 "curl -I http://57.128.200.27:5000/health"
|
||||
|
||||
# Test Flask → Database connectivity
|
||||
@ -1103,12 +1093,12 @@ After=network.target postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
User=www-data
|
||||
Group=www-data
|
||||
User=maciejpi
|
||||
Group=maciejpi
|
||||
WorkingDirectory=/var/www/nordabiznes
|
||||
Environment="PATH=/var/www/nordabiznes/venv/bin"
|
||||
ExecStart=/var/www/nordabiznes/venv/bin/gunicorn \
|
||||
--bind 0.0.0.0:5000 \
|
||||
--bind 127.0.0.1:5000 \
|
||||
--workers 4 \
|
||||
--worker-class sync \
|
||||
--timeout 120 \
|
||||
@ -1127,7 +1117,7 @@ WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
**Parameters Explained:**
|
||||
- `--bind 0.0.0.0:5000` - Listen on all interfaces, port 5000
|
||||
- `--bind 127.0.0.1:5000` - Listen on all interfaces, port 5000
|
||||
- `--workers 4` - 4 worker processes (matches CPU cores)
|
||||
- `--worker-class sync` - Synchronous workers (default)
|
||||
- `--timeout 120` - 120 second request timeout
|
||||
@ -1221,7 +1211,7 @@ ssh maciejpi@57.128.200.27 "sudo tail -f /var/log/postgresql/postgresql-14-main.
|
||||
- Database size growth
|
||||
- Table bloat
|
||||
|
||||
**System Metrics (NORDABIZ-01):**
|
||||
**System Metrics (OVH VPS):**
|
||||
- CPU usage (should be < 80%)
|
||||
- Memory usage (should be < 6 GB / 8 GB)
|
||||
- Disk I/O (should be low)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user