- System Context diagram (C4 Level 1) - Container diagram (C4 Level 2) - Flask component diagram (C4 Level 3) - Deployment architecture with NPM proxy - Database schema (PostgreSQL) - External integrations (Gemini AI, Brave Search, PageSpeed) - Network topology (INPI infrastructure) - Security architecture - API endpoints reference - Troubleshooting guide - Data flow diagrams (auth, search, AI chat, SEO audit, news monitoring) All diagrams use Mermaid.js and render automatically on GitHub. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
29 KiB
Container Diagram (C4 Level 2)
Document Version: 1.0 Last Updated: 2026-01-10 Status: Production LIVE Diagram Type: C4 Model - Level 2 (Containers)
Overview
This diagram shows the major containers (applications and data stores) that make up the Norda Biznes Hub system. It illustrates:
- What containers exist (web app, database, proxy, external services)
- How containers communicate with each other
- Technology choices for each container
- Network boundaries and protocols
Abstraction Level: Container (C4 Level 2) Audience: Developers, DevOps, Technical Architects Purpose: Understanding system decomposition and technology stack
Container Diagram
graph TB
%% External actors
Users["👥 Users<br/>(Members, Visitors, Admins)<br/>Web Browser"]
Admin["👨💼 System Admin<br/>Web Browser + SSH"]
%% System boundary
subgraph "Norda Biznes Hub System"
subgraph "R11-REVPROXY-01 [VM 119 | 10.22.68.250]"
NPM["🔒 Nginx Proxy Manager<br/>(Reverse Proxy)<br/><br/>Technology: Nginx + Docker<br/>Port: 443 (HTTPS)<br/><br/>Responsibilities:<br/>- SSL/TLS termination<br/>- Request routing<br/>- HTTP→HTTPS redirect<br/>- Let's Encrypt automation"]
end
subgraph "NORDABIZ-01 [VM 249 | 10.22.68.249]"
WebApp["🌐 Flask Web Application<br/>(Application Server)<br/><br/>Technology: Flask 3.0 + Gunicorn<br/>Language: Python 3.9+<br/>Port: 5000<br/><br/>Responsibilities:<br/>- HTTP request handling<br/>- Business logic<br/>- Template rendering<br/>- API endpoints<br/>- Authentication & authorization<br/>- Session management"]
Database["💾 PostgreSQL Database<br/>(Data Store)<br/><br/>Technology: PostgreSQL 14<br/>Port: 5432 (localhost only)<br/><br/>Responsibilities:<br/>- Persistent data storage<br/>- Full-text search (FTS)<br/>- Fuzzy matching (pg_trgm)<br/>- Data integrity & constraints<br/>- 36 tables (companies, users, etc.)"]
Scripts["⚙️ Background Scripts<br/>(Batch Jobs)<br/><br/>Technology: Python 3.9+<br/>Execution: Cron / Manual<br/><br/>Responsibilities:<br/>- SEO auditing<br/>- Social media auditing<br/>- Data verification<br/>- Database migrations"]
end
subgraph "Service Layer (within Flask)"
SearchSvc["🔍 Search Service<br/>search_service.py"]
ChatSvc["💬 Chat Service<br/>nordabiz_chat.py"]
EmailSvc["📧 Email Service<br/>email_service.py"]
GeminiSvc["🤖 Gemini Service<br/>gemini_service.py"]
KRSSvc["🏛️ KRS Service<br/>krs_api_service.py"]
GBPSvc["📊 GBP Audit Service<br/>gbp_audit_service.py"]
ITSvc["🖥️ IT Audit Service<br/>it_audit_service.py"]
end
end
%% External systems
subgraph "External APIs & Services"
Gemini["🤖 Google Gemini API<br/>gemini-2.5-flash<br/>REST API (HTTPS)"]
BraveAPI["🔍 Brave Search API<br/>News & Social Discovery<br/>REST API (HTTPS)"]
PageSpeed["📊 Google PageSpeed API<br/>SEO & Performance<br/>REST API (HTTPS)"]
Places["📍 Google Places API<br/>Business Profiles<br/>REST API (HTTPS)"]
KRS["🏛️ KRS Open API<br/>Company Registry<br/>REST API (HTTPS)"]
MSGraph["📧 Microsoft Graph API<br/>Email Sending<br/>REST API + OAuth 2.0"]
ALEO["🌐 ALEO.com<br/>NIP Verification<br/>Web Scraping (Playwright)"]
Rejestr["🔗 rejestr.io<br/>Company Connections<br/>Web Scraping (Playwright)"]
end
%% User interactions
Users -->|"HTTPS<br/>Port 443"| NPM
Admin -->|"HTTPS<br/>Port 443"| NPM
Admin -->|"SSH<br/>Port 22"| WebApp
%% NPM routing
NPM -->|"HTTP<br/>Port 5000<br/>(CRITICAL!)"| WebApp
%% Web app to database
WebApp -->|"SQL Queries<br/>SQLAlchemy ORM<br/>localhost:5432"| Database
Scripts -->|"SQL Queries<br/>Direct connection<br/>127.0.0.1:5432"| Database
%% Service layer connections
WebApp --> SearchSvc
WebApp --> ChatSvc
WebApp --> EmailSvc
WebApp --> GeminiSvc
WebApp --> KRSSvc
WebApp --> GBPSvc
WebApp --> ITSvc
SearchSvc --> Database
ChatSvc --> SearchSvc
ChatSvc --> GeminiSvc
%% External API integrations
GeminiSvc -->|"HTTPS<br/>API Key Auth"| Gemini
WebApp -->|"HTTPS<br/>API Key Auth"| BraveAPI
Scripts -->|"HTTPS<br/>API Key Auth"| PageSpeed
GBPSvc -->|"HTTPS<br/>API Key Auth"| Places
KRSSvc -->|"HTTPS<br/>Public API"| KRS
EmailSvc -->|"HTTPS<br/>OAuth 2.0"| MSGraph
Scripts -->|"HTTPS<br/>Web Scraping"| ALEO
Scripts -->|"HTTPS<br/>Web Scraping"| Rejestr
%% Styling
classDef containerStyle fill:#1168bd,stroke:#0b4884,color:#ffffff,stroke-width:3px
classDef databaseStyle fill:#438dd5,stroke:#2e6295,color:#ffffff,stroke-width:3px
classDef serviceStyle fill:#85bbf0,stroke:#5d92c7,color:#000000,stroke-width:2px
classDef proxyStyle fill:#ff6b6b,stroke:#cc5555,color:#ffffff,stroke-width:3px
classDef externalStyle fill:#999999,stroke:#666666,color:#ffffff,stroke-width:2px
classDef personStyle fill:#08427b,stroke:#052e56,color:#ffffff,stroke-width:2px
class WebApp,Scripts containerStyle
class Database databaseStyle
class SearchSvc,ChatSvc,EmailSvc,GeminiSvc,KRSSvc,GBPSvc,ITSvc serviceStyle
class NPM proxyStyle
class Gemini,BraveAPI,PageSpeed,Places,KRS,MSGraph,ALEO,Rejestr externalStyle
class Users,Admin personStyle
Container Descriptions
🔒 Nginx Proxy Manager (NPM)
Location: R11-REVPROXY-01 (VM 119, IP 10.22.68.250) Technology: Nginx + Docker Protocol: HTTPS (Port 443) Purpose: SSL termination and reverse proxy
Responsibilities:
- Terminate SSL/TLS connections (Let's Encrypt certificates)
- Route incoming HTTPS requests to backend Flask app
- Automatically renew SSL certificates
- Force HTTP→HTTPS redirects
- Basic security headers (HSTS, CSP)
Critical Configuration:
HTTPS :443 → HTTP 10.22.68.249:5000
⚠️ WARNING: NPM MUST forward to port 5000, NOT port 80!
- Port 80 on NORDABIZ-01 runs a system nginx that redirects to HTTPS
- Forwarding to port 80 causes infinite redirect loop (ERR_TOO_MANY_REDIRECTS)
- See:
docs/INCIDENT_REPORT_20260102.md
Verification:
curl -I https://nordabiznes.pl/health
# Expected: HTTP 200 OK
NPM Access:
- Web UI: http://10.22.68.250:81
- Proxy Host ID: 27
- Domain: nordabiznes.pl
- SSL: Let's Encrypt (auto-renewal)
🌐 Flask Web Application
Location: NORDABIZ-01 (VM 249, IP 10.22.68.249)
Technology: Flask 3.0 + Gunicorn WSGI server
Language: Python 3.9+
Protocol: HTTP (Port 5000 - internal only)
Main File: /var/www/nordabiznes/app.py (13,144 lines)
Responsibilities:
- Handle HTTP requests from NPM
- Render HTML templates (Jinja2)
- Provide REST API endpoints (90+ routes)
- User authentication (Flask-Login)
- Session management (Flask sessions)
- CSRF protection (Flask-WTF)
- Rate limiting (Flask-Limiter: 200 req/day, 50 req/hour)
- Business logic orchestration
- Database ORM interactions (SQLAlchemy)
Technology Stack:
- Framework: Flask 3.0
- WSGI Server: Gunicorn (4 workers)
- Template Engine: Jinja2
- Forms: Flask-WTF
- Authentication: Flask-Login
- ORM: SQLAlchemy 2.0
- Security: Flask-Limiter, Flask-SeaSurf (CSRF)
Key Modules:
| Module | Lines | Purpose |
|---|---|---|
app.py |
13,144 | Main application (routes, auth, API) |
database.py |
~1,500 | SQLAlchemy models (36 tables) |
search_service.py |
~400 | Company search (FTS, fuzzy) |
nordabiz_chat.py |
~800 | AI chat engine |
gemini_service.py |
~500 | Google Gemini integration |
email_service.py |
~200 | MS Graph email sender |
krs_api_service.py |
~300 | KRS company verification |
gbp_audit_service.py |
~600 | Google Business Profile audit |
it_audit_service.py |
~500 | IT infrastructure assessment |
Service Management:
sudo systemctl status nordabiznes
sudo systemctl restart nordabiznes
sudo journalctl -u nordabiznes -f
Application User: www-data (NOT root or maciejpi)
💾 PostgreSQL Database
Location: NORDABIZ-01 (VM 249, IP 10.22.68.249) Technology: PostgreSQL 14 Protocol: PostgreSQL wire protocol (Port 5432) Access: localhost ONLY (127.0.0.1)
Responsibilities:
- Store all application data (companies, users, chats, etc.)
- Full-text search (tsvector, tsquery)
- Fuzzy matching (pg_trgm extension)
- Enforce data integrity (foreign keys, constraints)
- Transaction management
- Backup and recovery
Database Details:
- Database Name:
nordabiz - Application User:
nordabiz_app - Admin User:
postgres - Encoding: UTF-8
- Locale: en_US.UTF-8
- Total Tables: 36 (via SQLAlchemy)
Key Tables:
| Table | Records | Purpose |
|---|---|---|
companies |
80 | Member company profiles |
users |
~50 | Registered users |
chat_sessions |
~500 | AI chat conversations |
chat_messages |
~2000 | Chat message history |
seo_metrics |
~200 | SEO audit results |
company_social_media |
~115 | Social media profiles |
forum_topics |
~30 | Forum discussions |
company_news |
~50 | News articles (moderated) |
PostgreSQL Extensions:
pg_trgm- Trigram similarity (fuzzy search)uuid-ossp- UUID generationpgcrypto- Cryptographic functions
Connection Strings:
# Flask App (from NORDABIZ-01)
DATABASE_URL = 'postgresql://nordabiz_app:***@127.0.0.1:5432/nordabiz'
# Background Scripts (from NORDABIZ-01)
DATABASE_URL = 'postgresql://nordabiz_app:***@127.0.0.1:5432/nordabiz'
⚠️ IMPORTANT: PostgreSQL is configured to reject external connections.
Only connections from localhost (127.0.0.1) are allowed.
Development Database:
- Host: localhost (Mac)
- Port: 5433 (Docker mapped port)
- Container:
nordabiz-postgres - Access:
postgresql://nordabiz_app:***@localhost:5433/nordabiz
⚙️ Background Scripts
Location: NORDABIZ-01 (VM 249, IP 10.22.68.249)
Directory: /var/www/nordabiznes/scripts/
Technology: Python 3.9+ (same virtualenv as Flask app)
Execution: Manual via SSH or Cron jobs
Purpose:
- Periodic data enrichment (SEO, social media, GBP audits)
- Database maintenance (migrations, cleanup)
- External data verification (NIP, KRS)
- AI quality testing
Key Scripts:
| Script | Purpose | API Dependencies |
|---|---|---|
seo_audit.py |
Website SEO & performance audit | Google PageSpeed |
social_media_audit.py |
Social profile verification | Brave Search |
gbp_audit.py |
Google Business Profile audit | Google Places |
fetch_company_news.py |
News monitoring (planned) | Brave Search + Gemini |
verify_all_companies_data.py |
Data quality report | - |
fix_krs_verification.py |
KRS data validation | KRS API |
import_*.py |
Company data import | ALEO, rejestr.io |
run_ai_quality_tests.py |
AI chat quality tests | Gemini |
Execution Example:
cd /var/www/nordabiznes
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 scripts/seo_audit.py --company-id 26
Cron Jobs (Planned):
# SEO audit - every week
0 2 * * 0 /var/www/nordabiznes/venv/bin/python3 /var/www/nordabiznes/scripts/seo_audit.py --all
# News monitoring - every 6 hours
0 */6 * * * /var/www/nordabiznes/venv/bin/python3 /var/www/nordabiznes/scripts/fetch_company_news.py --all
Service Layer Components
The Flask application uses a service layer pattern to encapsulate business logic and external integrations. Services are Python modules imported by app.py.
🔍 Search Service (search_service.py)
Purpose: Unified company search across multiple methods
Used By: /search route, AI chat, API endpoints
Database Access: Direct SQL + SQLAlchemy ORM
Search Methods:
- NIP/REGON Lookup - Direct identifier match
- Synonym Expansion - Keyword mapping (e.g., "strony" → www, web, portal)
- PostgreSQL FTS - Full-text search with
tsvector - Fuzzy Matching -
pg_trgmsimilarity (handles typos)
Scoring System:
- Company name match: +10 points
- Description match: +5 points
- Services match: +8 points
- Competencies match: +7 points
- City match: +3 points
API:
from search_service import search_companies
results = search_companies(db, "strony www", limit=10)
# Returns: List[SearchResult] with company, score, match_type
💬 Chat Service (nordabiz_chat.py)
Purpose: AI-powered chat with company context
Dependencies: Gemini Service, Search Service
Database Tables: chat_sessions, chat_messages, gemini_usage
Workflow:
- User sends message → create/load chat session
- Search for relevant companies (max 8 companies)
- Build conversation context (last 10 messages + company data)
- Send to Gemini API with system prompt
- Stream response back to user
- Track token usage and costs
Context Limits:
- Companies in context: 8 (prevents token overflow)
- Message history: 10 recent messages
- Max response length: ~1000 tokens
Cost Tracking:
- Input tokens counted per request
- Output tokens counted per response
- Stored in
gemini_usagetable - Cost calculated: $0.075-$1.25 per 1M tokens
🤖 Gemini Service (gemini_service.py)
Purpose: Interface to Google Gemini AI API
Models: gemini-2.5-flash (default), gemini-2.5-pro (advanced)
Authentication: API key from .env
Capabilities:
- Text generation (chat responses)
- Image analysis (logo descriptions)
- Content scoring (news relevance)
- Streaming responses
API Wrapper Functions:
generate_text(prompt, model="gemini-2.5-flash")
generate_chat_response(messages, context, stream=True)
analyze_image(image_bytes, prompt)
score_content_relevance(content, company_name)
Error Handling:
- Rate limit errors → retry with exponential backoff
- Invalid API key → log error + return fallback response
- Timeout → 30s default, configurable
📧 Email Service (email_service.py)
Purpose: Send email notifications via Microsoft Graph API Authentication: OAuth 2.0 client credentials Email Provider: Office 365 / Outlook
Use Cases:
- Email verification during registration
- Password reset emails
- Admin notifications
- User alerts (forum replies, messages)
Configuration:
# .env
MS_GRAPH_CLIENT_ID=...
MS_GRAPH_CLIENT_SECRET=...
MS_GRAPH_TENANT_ID=...
MS_GRAPH_FROM_EMAIL=noreply@nordabiznes.pl
API:
from email_service import send_email
send_email(to="user@example.com", subject="...", body="...")
🏛️ KRS Service (krs_api_service.py)
Purpose: Verify Polish company data from KRS registry API: Polish Ministry of Justice KRS Open API Authentication: Public API (no key required)
Data Retrieved:
- Company legal verification (KRS number)
- Corporate structure
- Board members
- Share capital
- Legal form
API:
from krs_api_service import verify_krs
data = verify_krs(krs_number="0000123456")
# Returns: Dict with company details or None
📊 GBP Audit Service (gbp_audit_service.py)
Purpose: Audit Google Business Profile completeness
Dependencies: Google Places API, Gemini AI
Database Table: gbp_audits
Audit Checks:
- Profile completeness (name, address, hours, etc.)
- Photo quantity and quality
- Review count and average rating
- Q&A presence
- Posts activity
AI Recommendations:
- Uses Gemini to generate improvement suggestions
- Personalized based on profile gaps
- Actionable advice
🖥️ IT Audit Service (it_audit_service.py)
Purpose: Assess IT infrastructure maturity
Method: Questionnaire-based scoring
Database Table: it_audits
Assessment Categories:
- IT Infrastructure (servers, network, backup)
- Security (firewall, antivirus, access control)
- Cloud Services (usage, integration)
- Digital Tools (CRM, ERP, communication)
- IT Management (policies, documentation, support)
Scoring:
- 0-40: Basic level
- 41-70: Intermediate level
- 71-100: Advanced level
External API Integrations
All external APIs are called via HTTPS with appropriate authentication.
🤖 Google Gemini AI API
Endpoint: https://generativelanguage.googleapis.com/v1beta/models/*
Authentication: API Key (in header: x-goog-api-key)
Pricing: $0.075-$1.25 per 1M tokens (varies by model)
Rate Limit: No strict limit (cost-limited)
Used For:
- AI chat responses
- Image analysis
- Content relevance scoring
- Recommendation generation
🔍 Brave Search API
Endpoint: https://api.search.brave.com/res/v1/news/search
Authentication: API Key (header: X-Subscription-Token)
Pricing: Free tier - 2,000 requests/month
Rate Limit: 2,000 req/month
Used For:
- News monitoring (company mentions)
- Social media profile discovery
- Press release detection
📊 Google PageSpeed Insights API
Endpoint: https://www.googleapis.com/pagespeedonline/v5/runPagespeed
Authentication: API Key (query param: key)
Pricing: Free - 25,000 queries/day
Rate Limit: 25,000 req/day
Used For:
- SEO score calculation
- Performance metrics (Core Web Vitals)
- Accessibility auditing
- Best practices assessment
📍 Google Places API
Endpoint: https://maps.googleapis.com/maps/api/place/*
Authentication: API Key
Pricing: Pay-per-use (varies by request type)
Rate Limit: Quota-based
Used For:
- Business reviews and ratings
- Business hours and location
- Photo gallery
- Google Maps integration
🏛️ KRS Open API
Endpoint: https://api-krs.ms.gov.pl/
Authentication: None (public API)
Pricing: Free
Rate Limit: Not strictly enforced
Used For:
- Company legal verification
- Corporate structure data
- Board member information
- Share capital details
📧 Microsoft Graph API
Endpoint: https://graph.microsoft.com/v1.0/
Authentication: OAuth 2.0 (client credentials)
Pricing: Included with Office 365
Rate Limit: Throttling based on tenant
Used For:
- Email notifications
- User mailbox access (delegated)
🌐 ALEO.com
Method: Web scraping (Playwright) Authentication: None Rate Limit: Self-imposed (polite scraping) Reliability: Medium (subject to website changes)
Used For:
- NIP number validation
- Company legal status check
- Basic company information
🔗 rejestr.io
Method: Web scraping (Playwright) Authentication: None Rate Limit: Self-imposed Reliability: Medium
Used For:
- Board member identification
- Shareholder/ownership structure
- Cross-company relationships
- Beneficial owners
Network Flow and Protocols
1. User HTTP Request Flow
User Browser (HTTPS :443)
│
▼
NPM @ 10.22.68.250:443 (SSL termination)
│
├─ Decrypt HTTPS
├─ Verify SSL certificate
└─ Forward as HTTP
│
▼
Flask App @ 10.22.68.249:5000 (HTTP)
│
├─ Authenticate user (session cookie)
├─ Authorize request (permissions)
├─ Execute business logic
└─ Query database
│
▼
PostgreSQL @ 127.0.0.1:5432 (local only)
│
├─ Execute SQL query
├─ Return result set
└─ Commit transaction
│
▼
Flask App (render template / JSON)
│
▼
NPM (encrypt response)
│
▼
User Browser (HTTPS response)
2. AI Chat Request Flow
User → Flask /api/chat/<id>/message (POST)
│
▼
Chat Service (nordabiz_chat.py)
│
├─ Load chat session from DB
├─ Extract user query
└─ Search for relevant companies
│
▼
Search Service (search_companies)
│
└─ PostgreSQL FTS + fuzzy search
│
▼
Chat Service (build context)
│
├─ Last 10 messages
├─ Max 8 company profiles
└─ System prompt
│
▼
Gemini Service → Google Gemini API (HTTPS)
│
├─ Send context + query
├─ Receive streaming response
└─ Track token usage
│
▼
Chat Service (save to DB)
│
├─ Store user message
├─ Store AI response
└─ Update gemini_usage
│
▼
Flask → User (streaming JSON)
3. SEO Audit Flow
Admin → /admin/seo (trigger audit)
│
▼
Background Script: scripts/seo_audit.py
│
├─ Get company URL from DB
└─ Call PageSpeed API
│
▼
Google PageSpeed API @ https://www.googleapis.com/...
│
├─ Analyze website
├─ Calculate scores (SEO, Performance, etc.)
└─ Return audit data (JSON)
│
▼
Script (parse results)
│
└─ Store in seo_metrics table
│
▼
PostgreSQL (save audit)
│
▼
Admin Dashboard (display results)
Security Boundaries
Network Segmentation
| Zone | Components | Access Level | Trust Level |
|---|---|---|---|
| Public Internet | User browsers | Untrusted | Low |
| DMZ (Proxy) | NPM (10.22.68.250) | Semi-trusted | Medium |
| App Zone | Flask App (10.22.68.249:5000) | Trusted | High |
| Data Zone | PostgreSQL (127.0.0.1:5432) | Highly trusted | Critical |
| External APIs | Gemini, Brave, etc. | Untrusted | Low |
Authentication & Authorization
User Authentication:
- Method: Email/password (bcrypt hashed)
- Session: Flask-Login (server-side sessions)
- Cookie:
session(encrypted, HttpOnly, SameSite=Lax)
Admin Authorization:
- Check:
current_user.is_authenticated and current_user.is_admin - Decorator:
@login_required+ manualis_admincheck
API Authentication:
- External APIs: API keys in
.env(never committed) - MS Graph: OAuth 2.0 client credentials
HTTPS/TLS
- Certificate: Let's Encrypt (auto-renewal via NPM)
- Protocols: TLS 1.2, TLS 1.3
- Cipher Suites: Modern (AES-GCM, ChaCha20-Poly1305)
- HSTS: Enabled (max-age=31536000)
Database Security
- Access: Localhost only (no external connections)
- Authentication: Password-based (strong passwords)
- Encryption: At-rest encryption (OS-level)
- Backups: Encrypted snapshots (Proxmox Backup Server)
Deployment Configuration
Production Environment
NPM Container (R11-REVPROXY-01):
Proxy Host Configuration:
Domain: nordabiznes.pl
Scheme: http
Forward Hostname: 10.22.68.249
Forward Port: 5000 # CRITICAL: Must be 5000, not 80!
Cache Assets: Yes
Block Common Exploits: Yes
Websockets Support: No
SSL:
Force SSL: Yes
HTTP/2 Support: Yes
HSTS Enabled: Yes
Certificate: Let's Encrypt
Auto-renewal: Yes
Flask/Gunicorn (NORDABIZ-01):
# /etc/systemd/system/nordabiznes.service
[Unit]
Description=Norda Biznes Hub - Flask Application
After=network.target postgresql.service
[Service]
Type=notify
User=www-data
Group=www-data
WorkingDirectory=/var/www/nordabiznes
Environment="PATH=/var/www/nordabiznes/venv/bin"
ExecStart=/var/www/nordabiznes/venv/bin/gunicorn \
--workers 4 \
--bind 127.0.0.1:5000 \
--timeout 120 \
--access-logfile /var/log/nordabiznes/access.log \
--error-logfile /var/log/nordabiznes/error.log \
app:app
[Install]
WantedBy=multi-user.target
PostgreSQL (NORDABIZ-01):
# /etc/postgresql/14/main/postgresql.conf
listen_addresses = 'localhost' # ONLY localhost!
port = 5432
max_connections = 100
shared_buffers = 256MB
# /etc/postgresql/14/main/pg_hba.conf
local nordabiz nordabiz_app md5
host nordabiz nordabiz_app 127.0.0.1/32 md5
Development Environment
Local Development (Mac)
Flask Application:
# Run locally on port 5000 or 5001
python3 app.py
# Access: http://localhost:5000
PostgreSQL (Docker):
# Start PostgreSQL container
docker compose up -d
# Connection string
postgresql://nordabiz_app:***@localhost:5433/nordabiz
Environment Variables:
# .env file (NEVER commit!)
DATABASE_URL=postgresql://nordabiz_app:***@localhost:5433/nordabiz
SECRET_KEY=...
GOOGLE_API_KEY=...
BRAVE_SEARCH_API_KEY=...
GOOGLE_PAGESPEED_API_KEY=...
Monitoring and Diagnostics
Health Check Endpoint
# Production
curl -I https://nordabiznes.pl/health
# Expected: HTTP 200 OK
# Development
curl -I http://localhost:5000/health
# Expected: HTTP 200 OK
Logs
Flask Application:
# Systemd journal
sudo journalctl -u nordabiznes -f
# Application logs
tail -f /var/log/nordabiznes/access.log
tail -f /var/log/nordabiznes/error.log
NPM (Nginx Proxy Manager):
# Docker logs
docker logs -f <npm-container-id>
# NPM access logs
docker exec <npm-container-id> tail -f /data/logs/proxy-host-27_access.log
PostgreSQL:
# PostgreSQL logs
sudo tail -f /var/log/postgresql/postgresql-14-main.log
Database Diagnostics
# Connect to database
sudo -u postgres psql nordabiz
# Check active connections
SELECT count(*) FROM pg_stat_activity WHERE datname = 'nordabiz';
# Check table sizes
SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))
FROM pg_tables WHERE schemaname = 'public' ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
Critical Configuration Warnings
⚠️ NPM Proxy Port Configuration
CRITICAL: NPM MUST forward to port 5000, NOT 80!
Why?
- Port 80 on NORDABIZ-01 runs system nginx
- System nginx redirects HTTP → HTTPS
- NPM → :80 → nginx → HTTPS → NPM (infinite loop!)
- Results in:
ERR_TOO_MANY_REDIRECTS
Correct Configuration:
NPM (10.22.68.250:443) → Flask (10.22.68.249:5000) ✓
Incorrect Configuration (causes loop):
NPM (10.22.68.250:443) → Nginx (10.22.68.249:80) → NPM ✗
Verification After Changes:
curl -I https://nordabiznes.pl/health
# Must return: HTTP 200 OK (not redirect loop)
Incident Report: docs/INCIDENT_REPORT_20260102.md
⚠️ Database Connection Security
PostgreSQL MUST reject external connections:
# postgresql.conf
listen_addresses = 'localhost' # NOT '*' or '0.0.0.0'!
Scripts MUST use localhost (127.0.0.1):
# CORRECT (from NORDABIZ-01)
DATABASE_URL = 'postgresql://nordabiz_app:***@127.0.0.1:5432/nordabiz'
# INCORRECT (external connection attempt)
DATABASE_URL = 'postgresql://nordabiz_app:***@10.22.68.249:5432/nordabiz'
⚠️ API Keys and Secrets
NEVER commit to git:
.envfile (must be in.gitignore)- API keys in code
- Database passwords
- OAuth client secrets
Storage:
- Production:
.envfile on NORDABIZ-01 - Development:
.envfile on local machine - Backup: Secure password manager (not in git!)
Related Documentation
Next Level (Deeper Dive)
- Flask Application Components - Internal Flask structure (routes, services, models)
- Database Schema - Table relationships and data model
- External Integrations - Detailed API documentation
Same Level (Alternative Views)
- Deployment Architecture - Infrastructure view (servers, IPs, ports)
- Network Topology - Network diagram with Fortigate, VLANs
Higher Level (Context)
- System Context Diagram - Overall system and external actors
Data Flows
- Authentication Flow - User login sequence
- Search Flow - Company search process
- AI Chat Flow - AI conversation handling
- SEO Audit Flow - Audit execution
- HTTP Request Flow - NPM → Flask → DB
Maintenance Notes
When to Update This Diagram
✏️ UPDATE when:
- New container added (e.g., Redis cache, Celery worker)
- Container technology changed (e.g., Flask → FastAPI)
- New external API integrated
- Database technology changed (e.g., PostgreSQL → MySQL)
- Service layer significantly refactored
❌ DON'T UPDATE for:
- New Flask routes (component-level change)
- New database tables (schema-level change)
- Code refactoring within existing containers
- Infrastructure changes (deployment diagram)
Review Frequency
- After major releases: Review container boundaries
- Quarterly: Verify technology versions
- When onboarding: Ensure diagram matches reality
Glossary
| Term | Definition |
|---|---|
| Container | Separately deployable/runnable unit (app, database, service) |
| NPM | Nginx Proxy Manager - reverse proxy with SSL termination |
| Gunicorn | Python WSGI HTTP server for running Flask apps |
| WSGI | Web Server Gateway Interface - Python web server standard |
| ORM | Object-Relational Mapping (SQLAlchemy) |
| FTS | Full-Text Search (PostgreSQL feature) |
| pg_trgm | PostgreSQL trigram extension for fuzzy search |
| Service Layer | Business logic abstraction (services/*.py) |
| Flask-Login | Flask extension for user session management |
| bcrypt | Password hashing algorithm |
| HSTS | HTTP Strict Transport Security |
| DMZ | Demilitarized Zone (network security boundary) |
Document Status: ✅ Complete Diagram Validated: 2026-01-10 Mermaid Syntax: v10.6+ Renders in: GitHub, GitLab, VS Code (with Mermaid extension) Production Verified: 2026-01-10 (via health check)