` | IT infrastructure audit | `audit_it.html` |
**Key Features:**
- Public access (no login required)
- Read-only reports
- PDF export capability (planned)
- Share links for business owners
- Recommendations and action items
- Historical audit comparisons
---
### 3. Service Layer (7 modules)
#### 🔍 SearchService (`search_service.py` - ~400 lines)
**Purpose:** Unified company search across multiple strategies
**Primary Function:** `search_companies(db, query, limit=10)`
**Search Strategies:**
1. **NIP/REGON Direct Lookup** - Exact match on identifiers
2. **Synonym Expansion** - Expand keywords (e.g., "strony" → www, web, portal)
3. **PostgreSQL FTS** - Full-text search with tsvector (production)
4. **Fuzzy Matching** - pg_trgm for typo tolerance
5. **SQLite Fallback** - Keyword scoring (development only)
**Scoring System:**
- **Name match:** +10 points
- **Services match:** +8 points
- **Competencies match:** +7 points
- **Description match:** +5 points
- **City match:** +3 points
**Configuration:**
- **Max Results:** 10 (configurable)
- **Min Score:** 3 (filter low-quality matches)
- **Synonyms:** Defined in `SEARCH_SYNONYMS` dict
**Error Handling:**
- PostgreSQL FTS failure → rollback + fallback to SQLite
- Empty queries → return empty list
- Invalid parameters → raise ValueError
**Usage Example:**
```python
from search_service import search_companies
results = search_companies(db, "strony www", limit=10)
# Returns: List[SearchResult(company, score, match_type)]
```
#### 💬 ChatService (`nordabiz_chat.py` - ~800 lines)
**Purpose:** AI chat assistant with company context
**Primary Function:** `process_chat_message(session_id, user_message)`
**Key Responsibilities:**
1. **Session Management** - Create, load, and manage chat sessions
2. **Context Building** - Find relevant companies (max 8)
3. **History Management** - Last 10 messages per session
4. **AI Integration** - Call Gemini API with streaming
5. **Token Tracking** - Monitor usage and costs
**Context Limits:**
- **Max Companies:** 8 (prevents token overflow)
- **Max Messages:** 10 (sliding window)
- **Max Tokens:** ~30,000 input tokens
- **Streaming:** Real-time response chunks
**Database Tables:**
- `chat_sessions` - Session metadata
- `chat_messages` - Message history
- `gemini_usage` - Token usage tracking
**Cost Tracking:**
- Input tokens × $0.075 per 1M
- Output tokens × $0.30 per 1M
- Stored in `gemini_usage` table
**Example Flow:**
```python
# 1. User sends message
user_msg = "Kto robi strony www?"
# 2. Search for relevant companies
companies = search_companies(db, user_msg, limit=8)
# 3. Build context with last 10 messages
context = build_context(session, companies, history)
# 4. Call Gemini API
response = gemini_service.generate_streaming(context)
# 5. Save message and track tokens
save_message(session, user_msg, response)
track_usage(session, input_tokens, output_tokens)
```
#### 🤖 GeminiService (`gemini_service.py` - ~500 lines)
**Purpose:** Google Gemini AI API integration
**Primary Functions:**
- `generate_text(prompt, model="gemini-3-flash-preview")`
- `generate_streaming(prompt, model="gemini-3-flash-preview")`
- `analyze_image(image_path, prompt)`
- `moderate_content(text)`
**Supported Models:**
- **gemini-3-flash-preview** - Default, 7x better reasoning (free tier)
- **gemini-3-pro-preview** - Advanced reasoning, 2M context ($2.00/1M tokens)
**Key Features:**
1. **Text Generation** - Chat, content creation, analysis
2. **Image Analysis** - Logo recognition, screenshot analysis
3. **Streaming Responses** - Real-time output for chat
4. **Content Moderation** - Safety filters (harassment, hate, violence)
5. **Cost Tracking** - Token usage monitoring
**Configuration:**
- **API Key:** `GEMINI_API_KEY` (from .env)
- **Rate Limit:** 200 requests/day (free tier)
- **Max Tokens:** 1M input, 8K output (flash model)
- **Temperature:** 0.7 (default for chat)
**Safety Settings:**
```python
safety_settings = {
"HARM_CATEGORY_HARASSMENT": "BLOCK_MEDIUM_AND_ABOVE",
"HARM_CATEGORY_HATE_SPEECH": "BLOCK_MEDIUM_AND_ABOVE",
"HARM_CATEGORY_SEXUALLY_EXPLICIT": "BLOCK_MEDIUM_AND_ABOVE",
"HARM_CATEGORY_DANGEROUS_CONTENT": "BLOCK_MEDIUM_AND_ABOVE",
}
```
**Error Handling:**
- API errors → retry with exponential backoff
- Rate limit exceeded → graceful degradation
- Safety block → return user-friendly error message
#### 📧 EmailService (`email_service.py` - ~200 lines)
**Purpose:** Email notifications via Microsoft Graph API
**Primary Function:** `send_email(to, subject, body_html)`
**Authentication:** OAuth 2.0 (MSAL)
**Configuration:**
- **Client ID:** `MS_GRAPH_CLIENT_ID`
- **Client Secret:** `MS_GRAPH_CLIENT_SECRET`
- **Tenant ID:** `MS_GRAPH_TENANT_ID`
- **Sender:** `noreply@nordabiznes.pl`
**Email Types:**
1. **Email Verification** - Registration confirmation
2. **Password Reset** - Password recovery
3. **Payment Reminders** - Membership fees
4. **Event Notifications** - Calendar invites
5. **Forum Notifications** - New replies
6. **Admin Alerts** - System notifications
**Template Rendering:**
- Jinja2 HTML templates in `templates/emails/`
- Plain text fallback for compatibility
- Inline CSS for email client support
**Error Handling:**
- OAuth token refresh on 401
- Retry on transient failures (5xx)
- Fallback to local logging on total failure
#### 🏛️ KRSService (`krs_api_service.py` - ~300 lines)
**Purpose:** Polish company registry (KRS) verification
**Primary Function:** `fetch_company_data(krs_number)`
**API:** KRS Open API (Ministry of Justice)
**Endpoint:** `https://api-krs.ms.gov.pl/api/krs/OdpisAktualny/{krs}`
**Authentication:** Public API (no key required)
**Data Retrieved:**
- **Company Name** - Legal name
- **NIP** - Tax identification number
- **REGON** - Statistical number
- **Address** - Registered office
- **Legal Form** - Sp. z o.o., S.A., etc.
- **Share Capital** - Initial capital
- **Board Members** - Management names
- **Shareholders** - Ownership structure
**Caching:**
- Database table: `company_krs_data`
- TTL: 30 days (data rarely changes)
- Refresh on demand via admin panel
**Error Handling:**
- KRS not found → return None
- API timeout → retry 3 times
- Invalid KRS format → validation error
#### 📊 GBPService (`gbp_audit_service.py` - ~600 lines)
**Purpose:** Google Business Profile auditing
**Primary Functions:**
- `search_place(company_name, address)`
- `get_place_details(place_id)`
- `generate_recommendations(audit_data)`
**APIs Used:**
1. **Google Places API** - Place search, details, reviews
2. **Gemini AI** - Generate improvement recommendations
**Audit Metrics:**
- **Profile Completeness** - % of filled fields
- **Review Count** - Number of Google reviews
- **Average Rating** - Star rating (1-5)
- **Response Rate** - % of reviews replied to
- **Photo Count** - Number of uploaded photos
- **Verification Status** - Verified vs unverified
**Recommendations Generated by AI:**
- Complete missing profile fields
- Respond to unanswered reviews
- Upload more photos (categories: exterior, interior, products, team)
- Improve category selection
- Add special hours (holidays, events)
- Enable messaging
**Database Storage:**
- `gbp_audits` - Audit results
- `gbp_recommendations` - AI-generated recommendations
**Cost:**
- **Places API:** $0.017 per search, $0.017 per details call
- **Gemini AI:** ~$0.02 per recommendation generation
#### 🖥️ ITService (`it_audit_service.py` - ~500 lines)
**Purpose:** IT infrastructure maturity assessment
**Primary Function:** `calculate_maturity_score(responses)`
**Assessment Areas (7 domains):**
1. **Website & Online Presence** - Website quality, SEO, analytics
2. **Email & Communication** - Professional email, collaboration tools
3. **Data Security** - Backups, encryption, access control
4. **Cloud Services** - Cloud adoption, SaaS usage
5. **Business Software** - ERP, CRM, accounting tools
6. **IT Support** - Support availability, SLA
7. **Digital Skills** - Team training, digital literacy
**Scoring:**
- **Level 0:** No implementation (0 points)
- **Level 1:** Basic implementation (25 points)
- **Level 2:** Intermediate (50 points)
- **Level 3:** Advanced (75 points)
- **Level 4:** Excellent (100 points)
**Maturity Levels:**
- **0-25%:** Basic - Minimal digital presence
- **26-50%:** Developing - Some digital tools
- **51-75%:** Advanced - Good digital infrastructure
- **76-100%:** Excellent - Mature digital organization
**Report Includes:**
- Overall maturity score
- Domain-specific scores
- Recommendations for improvement
- Comparison to industry average
- Action plan with priorities
**Database Storage:**
- `it_infrastructure_audits` - Audit results
- `digital_maturity_scores` - Historical scores
---
### 4. Database Layer (36 SQLAlchemy Models)
#### 🗄️ SQLAlchemy ORM
**Location:** `database.py` (~1,500 lines)
**Purpose:** Object-Relational Mapping and database abstraction
**Key Features:**
- **Database Agnostic:** PostgreSQL (production), SQLite (development)
- **Connection Pooling:** SQLAlchemy pool management
- **Session Management:** Scoped sessions per request
- **Query Builder:** Pythonic query construction
- **Relationship Mapping:** One-to-many, many-to-many, one-to-one
- **Lazy Loading:** Relationships loaded on demand
- **Cascade Behaviors:** Delete cascade, update cascade
**Session Management:**
```python
# Request-scoped session (Flask integration)
@app.before_request
def create_session():
db.session = db.Session()
@app.teardown_request
def close_session(exception=None):
db.session.close()
```
**Connection String:**
```python
# Production
DATABASE_URL = "postgresql://nordabiz_app:password@127.0.0.1:5432/nordabiz"
# Development
DATABASE_URL = "postgresql://postgres:postgres@localhost:5433/nordabiz"
```
#### 📂 Domain Models (36 total)
##### Core Domain (5 models)
**Purpose:** Fundamental business entities
**1. Company**
- **Fields:** name, slug, nip, regon, krs, category, description, website, email, phone, city, address, logo_url, verified_at, data_quality_level, created_at, updated_at
- **Relationships:** social_media (1:N), news (1:N), photos (1:N), audits (1:N), recommendations (1:N), fees (1:N)
- **Indexes:** slug (unique), nip (unique), regon (unique), krs (unique), category, city
- **Full-Text Search:** tsvector on name, description, services, competencies
**2. User**
- **Fields:** email, password_hash, company_id, is_admin, is_active, email_verified_at, created_at, last_login_at
- **Relationships:** company (N:1), chat_sessions (1:N), messages_sent (1:N), messages_received (1:N), forum_topics (1:N), forum_replies (1:N)
- **Indexes:** email (unique), company_id, is_admin
- **Authentication:** bcrypt password hashing, Flask-Login integration
**3. Category**
- **Fields:** name, slug, icon, description, company_count
- **Relationships:** companies (1:N)
- **Values:** IT, Construction, Services, Production, Trade, Other
**4. CompanyOwner**
- **Fields:** company_id, name, role, shares_percent, person_url, source
- **Relationships:** company (N:1)
- **Purpose:** Track board members and shareholders (from rejestr.io)
**5. SearchHistory**
- **Fields:** user_id, query, results_count, clicked_company_id, created_at
- **Relationships:** user (N:1), clicked_company (N:1)
- **Purpose:** Search analytics and personalization
##### Content Domain (6 models)
**Purpose:** User-generated and external content
**6. CompanyNews**
- **Fields:** company_id, title, description, url, source, published_at, news_type, relevance_score, status, moderated_by, moderated_at, rejection_reason
- **Relationships:** company (N:1), moderator (N:1), verification (1:1)
- **Status:** pending, approved, rejected
- **Types:** news_mention, press_release, award
**7. NewsVerification**
- **Fields:** news_id, verified_by, verified_at, verification_notes, ai_confidence_score
- **Relationships:** news (1:1), verifier (N:1)
- **Purpose:** Track AI and manual verification of news
**8. ForumTopic**
- **Fields:** author_id, title, content, category, is_pinned, is_locked, view_count, reply_count, created_at, updated_at
- **Relationships:** author (N:1), replies (1:N)
- **Categories:** general, announcements, networking, help, offers
**9. ForumReply**
- **Fields:** topic_id, author_id, content, created_at, updated_at
- **Relationships:** topic (N:1), author (N:1)
**10. ClassifiedAd**
- **Fields:** company_id, title, description, category, price, image_url, expires_at, is_active, created_at
- **Relationships:** company (N:1)
- **Categories:** offers, requests, announcements
**11. CompanyPhoto**
- **Fields:** company_id, url, caption, category, uploaded_by, created_at
- **Relationships:** company (N:1), uploader (N:1)
- **Categories:** exterior, interior, team, products, events
##### Social Domain (5 models)
**Purpose:** Social media and community features
**12. CompanySocialMedia**
- **Fields:** company_id, platform, url, verified_at, source, is_valid, last_checked_at, check_status, page_name, followers_count
- **Relationships:** company (N:1), audits (1:N)
- **Platforms:** facebook, instagram, linkedin, youtube, tiktok, twitter
**13. SocialMediaAudit**
- **Fields:** company_id, audit_data, audit_type, audited_at
- **Relationships:** company (N:1)
- **Types:** profile_discovery, activity_check, engagement_analysis
**14. SocialMediaProfile**
- **Fields:** social_media_id, profile_data, last_fetched_at, fetch_status
- **Relationships:** social_media (1:1)
- **Purpose:** Cache profile data from APIs
**15. Recommendation**
- **Fields:** recommender_company_id, recommended_company_id, comment, rating, status, created_at, moderated_at
- **Relationships:** recommender (N:1), recommended (N:1)
- **Status:** pending, approved, rejected
**16. Like**
- **Fields:** user_id, target_type, target_id, created_at
- **Relationships:** user (N:1)
- **Target Types:** forum_topic, forum_reply, company_news, company
##### Audit Domain (4 models)
**Purpose:** SEO, GBP, and IT auditing
**17. SEOMetrics**
- **Fields:** company_id, url, seo_score, performance_score, accessibility_score, best_practices_score, pwa_score, audit_data, audited_at
- **Relationships:** company (N:1)
- **Source:** Google PageSpeed Insights API
**18. GBPAudit**
- **Fields:** company_id, place_id, audit_data, profile_completeness, review_count, average_rating, photo_count, verified_status, audited_at
- **Relationships:** company (N:1), recommendations (1:N)
- **Source:** Google Places API + Gemini AI
**19. ITInfrastructureAudit**
- **Fields:** company_id, responses, overall_score, domain_scores, recommendations, audited_at
- **Relationships:** company (N:1)
- **Purpose:** IT maturity self-assessment
**20. DigitalMaturityScore**
- **Fields:** company_id, score, level, breakdown, calculated_at
- **Relationships:** company (N:1)
- **Levels:** basic, developing, advanced, excellent
##### Chat Domain (3 models)
**Purpose:** AI chat assistant
**21. ChatSession**
- **Fields:** user_id, company_context, message_count, total_tokens, total_cost, started_at, last_message_at
- **Relationships:** user (N:1), messages (1:N), usage (1:N)
**22. ChatMessage**
- **Fields:** session_id, role, content, created_at
- **Relationships:** session (N:1)
- **Roles:** user, assistant, system
**23. GeminiUsage**
- **Fields:** session_id, model, input_tokens, output_tokens, total_tokens, cost, created_at
- **Relationships:** session (N:1)
- **Purpose:** Track AI usage and costs
##### Community Domain (5 models)
**Purpose:** Events, fees, messaging, notifications
**24. Event**
- **Fields:** title, description, location, start_time, end_time, organizer_id, max_attendees, rsvp_deadline, created_at
- **Relationships:** organizer (N:1), attendees (1:N)
**25. EventAttendee**
- **Fields:** event_id, user_id, status, rsvp_at
- **Relationships:** event (N:1), user (N:1)
- **Status:** pending, attending, declined
**26. MembershipFee**
- **Fields:** company_id, period_start, period_end, amount, status, paid_at, payment_method, notes
- **Relationships:** company (N:1)
- **Status:** pending, paid, overdue
**27. Message**
- **Fields:** sender_id, recipient_id, subject, body, is_read, read_at, created_at
- **Relationships:** sender (N:1), recipient (N:1)
**28. Notification**
- **Fields:** user_id, type, message, url, is_read, read_at, created_at
- **Relationships:** user (N:1)
- **Types:** new_news, news_approved, forum_reply, message_received, event_invite
##### System Domain (8 models)
**Purpose:** System management and logging
**29. EmailVerification**
- **Fields:** user_id, token, expires_at, verified_at, created_at
- **Relationships:** user (N:1)
**30. PasswordReset**
- **Fields:** user_id, token, expires_at, used_at, created_at
- **Relationships:** user (N:1)
**31. APIQuota**
- **Fields:** api_name, quota_limit, quota_used, period_start, period_end
- **Purpose:** Track API usage against quotas
- **APIs:** gemini, brave, pagespeed, places
**32. APILog**
- **Fields:** api_name, endpoint, request_data, response_status, response_time, created_at
- **Purpose:** API call logging and debugging
**33. DataQualityCheck**
- **Fields:** company_id, check_type, passed, issues, checked_at
- **Relationships:** company (N:1)
- **Check Types:** nip_verification, krs_verification, website_check, social_media_check
**34. ActivityLog**
- **Fields:** user_id, action, target_type, target_id, ip_address, user_agent, created_at
- **Relationships:** user (N:1)
- **Purpose:** User activity tracking
**35. AuditLog**
- **Fields:** user_id, action, target_type, target_id, changes, ip_address, created_at
- **Relationships:** user (N:1)
- **Purpose:** Admin action auditing
**36. SystemSetting**
- **Fields:** key, value, description, updated_at
- **Purpose:** Application configuration
- **Examples:** maintenance_mode, registration_enabled, chat_enabled
---
### 5. Template Rendering
#### 📄 Jinja2 Template Engine
**Location:** `templates/` directory
**Purpose:** Server-side HTML rendering
**Template Structure:**
```
templates/
├── base.html # Base template (layout)
├── components/ # Reusable components
│ ├── navbar.html
│ ├── footer.html
│ ├── company_card.html
│ └── pagination.html
├── index.html # Homepage
├── company_detail.html # Company profile
├── search_results.html # Search page
├── auth/ # Authentication pages
│ ├── login.html
│ ├── register.html
│ └── reset_password.html
├── dashboard/ # User dashboard
│ ├── dashboard.html
│ ├── chat.html
│ └── profile.html
├── admin/ # Admin pages
│ ├── seo_dashboard.html
│ ├── forum_moderation.html
│ └── users.html
├── forum/ # Forum pages
├── audit/ # Audit report pages
└── emails/ # Email templates
```
**Template Inheritance:**
```jinja2
{# base.html #}
{% block title %}Norda Biznes{% endblock %}
{% block extra_css %}{% endblock %}
{% include 'components/navbar.html' %}
{% block content %}{% endblock %}
{% include 'components/footer.html' %}
{# company_detail.html #}
{% extends 'base.html' %}
{% block title %}{{ company.name }} - Norda Biznes{% endblock %}
{% block content %}
{{ company.name }}
{{ company.description }}
{% endblock %}
```
**Common Template Variables:**
- `current_user` - Logged-in user (Flask-Login)
- `request` - Flask request object
- `config` - App configuration
- `url_for()` - URL generation function
- `get_flashed_messages()` - Flash messages
**Template Filters:**
```jinja2
{{ company.created_at|datetimeformat }} # Format datetime
{{ company.description|truncate(200) }} # Truncate text
{{ company.name|title }} # Title case
{{ price|currency }} # Format currency
{{ url|urlize }} # Convert URLs to links
```
**Security:**
- **Auto-escaping:** HTML is escaped by default
- **CSRF Tokens:** Included in all forms via `{{ csrf_token() }}`
- **XSS Prevention:** Jinja2 auto-escapes all variables
---
### 6. Static Assets
#### 🎨 Static File Structure
**Location:** `static/` directory
```
static/
├── css/
│ ├── main.css # Main stylesheet
│ ├── admin.css # Admin panel styles
│ └── components.css # Component styles
├── js/
│ ├── main.js # Global JavaScript
│ ├── chat.js # AI chat functionality
│ ├── search.js # Search autocomplete
│ └── admin.js # Admin panel JS
├── images/
│ ├── logo.png # Site logo
│ ├── companies/ # Company logos
│ └── icons/ # UI icons
└── uploads/ # User uploads (not in Git)
├── logos/
└── photos/
```
**CSS Framework:** Custom CSS (no Bootstrap/Tailwind)
**JavaScript:** Vanilla JS (minimal dependencies)
**Icons:** Font Awesome or SVG icons
**Key JavaScript Modules:**
- `chat.js` - WebSocket-like streaming for AI chat
- `search.js` - Autocomplete and instant search
- `admin.js` - Admin dashboard interactions
- `forum.js` - Forum post editing and moderation
---
## Component Interaction Patterns
### Pattern 1: Public Company Search
**Flow:**
```
User → /search?q=strony www
↓
Router → PublicRoutes.search()
↓
SearchService.search_companies(db, "strony www")
↓ (synonym expansion)
SearchService → PostgreSQL FTS (tsvector search)
↓
ORM → Company model + related data
↓
PublicRoutes → Templates (search_results.html)
↓
Jinja2 → HTML response
↓
User Browser
```
**Key Components:**
- **Router** - `/search` route handler
- **SearchService** - Query processing and scoring
- **Database** - PostgreSQL FTS query
- **Templates** - Jinja2 rendering
- **Static** - CSS for search results
### Pattern 2: AI Chat Request
**Flow:**
```
User → /api/chat//message (POST JSON)
↓
Router → APIRoutes.chat_message()
↓
Auth → Check @login_required
↓
RateLimit → Check API quota (100/day)
↓
CSRF → Validate token
↓
ChatService.process_message(session_id, message)
↓
SearchService.search_companies(db, message, limit=8)
↓
ChatService → Build context (companies + history)
↓
GeminiService.generate_streaming(prompt)
↓
Gemini API → Streaming response
↓
ChatService → Save message to DB
↓
ChatService → Track token usage
↓
JSON response → User (streaming chunks)
```
**Key Components:**
- **Router** - `/api/chat//message` handler
- **Auth** - Flask-Login authentication
- **RateLimit** - API quota enforcement
- **ChatService** - Session and context management
- **SearchService** - Company discovery
- **GeminiService** - AI integration
- **Database** - Message and usage storage
### Pattern 3: Admin SEO Audit
**Flow:**
```
Admin → /admin/seo (GET)
↓
Router → AdminRoutes.seo_dashboard()
↓
Auth → Check current_user.is_admin
↓
ORM → Load SEOMetrics + Company data
↓
Templates → Render admin_seo_dashboard.html
↓
User clicks "Audit Company X"
↓
JavaScript → POST /api/seo/audit (AJAX)
↓
Router → APIRoutes.trigger_seo_audit()
↓
Background Script → scripts/seo_audit.py
↓
PageSpeed API → Audit URL
↓
Background Script → Parse results
↓
ORM → Insert/update SEOMetrics
↓
JSON response → Update UI
```
**Key Components:**
- **Router** - `/admin/seo` and `/api/seo/audit` handlers
- **Auth** - Admin-only access control
- **Templates** - Admin dashboard with AJAX
- **Static** - JavaScript for audit triggering
- **Background Scripts** - SEO audit execution
- **External API** - Google PageSpeed Insights
- **Database** - SEOMetrics storage
### Pattern 4: User Registration
**Flow:**
```
User → /register (GET)
↓
Router → AuthRoutes.register()
↓
Templates → Render register.html (with CSRF token)
↓
User submits form
↓
POST /register
↓
Router → AuthRoutes.register()
↓
CSRF → Validate token
↓
RateLimit → Check registration quota (5/hour)
↓
Validation → Email, password strength, company_id
↓
ORM → Create User (bcrypt password hash)
↓
EmailVerification → Create token
↓
EmailService.send_email(verification email)
↓
MSGraph API → Send email
↓
Redirect → /login (with flash message)
```
**Key Components:**
- **Router** - `/register` GET and POST handlers
- **CSRF** - Form protection
- **RateLimit** - Registration throttling
- **ORM** - User creation
- **EmailService** - Verification email
- **External API** - Microsoft Graph for email
- **Templates** - Registration form
---
## Data Flow Patterns
### Database Query Patterns
**1. Simple Query (ORM):**
```python
# Get company by slug
company = db.session.query(Company).filter_by(slug='pixlab-sp-z-o-o').first()
```
**2. Join Query (Relationships):**
```python
# Get company with social media
company = db.session.query(Company)\
.options(joinedload(Company.social_media))\
.filter_by(slug='pixlab-sp-z-o-o')\
.first()
```
**3. Full-Text Search (PostgreSQL):**
```python
# Search companies by keyword
results = db.session.query(Company)\
.filter(Company.search_vector.match('strony www'))\
.order_by(desc(func.ts_rank(Company.search_vector, func.to_tsquery('strony & www'))))\
.limit(10)\
.all()
```
**4. Aggregation Query:**
```python
# Count companies by category
category_counts = db.session.query(
Category.name,
func.count(Company.id)
).join(Company).group_by(Category.name).all()
```
### Error Handling Patterns
**1. Service Layer Error Handling:**
```python
def search_companies(db, query, limit=10):
try:
# Try PostgreSQL FTS
results = _search_fts(db, query, limit)
return results
except Exception as e:
logger.error(f"FTS search failed: {e}")
db.rollback() # CRITICAL: Rollback failed transaction
# Fallback to SQLite-style search
return _search_fallback(db, query, limit)
```
**2. API Route Error Handling:**
```python
@app.route('/api/chat//message', methods=['POST'])
@login_required
@limiter.limit("100/day")
def chat_message(session_id):
try:
data = request.get_json()
if not data or 'message' not in data:
return jsonify({"error": "Message required"}), 400
response = chat_service.process_message(session_id, data['message'])
return jsonify({"success": True, "response": response}), 200
except ValueError as e:
return jsonify({"error": str(e)}), 400
except Exception as e:
logger.exception("Chat error")
return jsonify({"error": "Internal server error"}), 500
```
**3. External API Error Handling:**
```python
def call_gemini_api(prompt):
max_retries = 3
retry_delay = 1 # seconds
for attempt in range(max_retries):
try:
response = genai.generate_text(prompt)
return response
except genai.RateLimitError:
logger.warning("Gemini rate limit exceeded")
return {"error": "AI service temporarily unavailable"}
except genai.APIError as e:
if attempt < max_retries - 1:
time.sleep(retry_delay * (2 ** attempt)) # Exponential backoff
continue
else:
logger.error(f"Gemini API failed: {e}")
raise
```
### Authentication Flow
**Login Flow:**
```
1. User submits credentials → POST /login
2. Validate CSRF token
3. Query User by email
4. Verify password (bcrypt.check_password_hash)
5. Check email_verified_at (must not be null)
6. Check is_active flag
7. Flask-Login: login_user(user, remember=True)
8. Update last_login_at timestamp
9. Set session cookie (httponly, secure, samesite)
10. Redirect to /dashboard
```
**Authorization Checks:**
```python
# Public route (no auth)
@app.route('/')
def index():
# Anyone can access
pass
# User route (requires login)
@app.route('/dashboard')
@login_required
def dashboard():
# Must be authenticated
pass
# Admin route (requires admin flag)
@app.route('/admin/users')
@login_required
def admin_users():
if not current_user.is_admin:
abort(403) # Forbidden
# Admin-only logic
pass
```
---
## Configuration and Environment
### Environment Variables (.env)
**Required Variables:**
```bash
# Flask
SECRET_KEY=
FLASK_ENV=production # or development
# Database
DATABASE_URL=postgresql://nordabiz_app:password@127.0.0.1:5432/nordabiz
# Google Gemini AI
GEMINI_API_KEY=
# Brave Search API
BRAVE_SEARCH_API_KEY=
# Google PageSpeed Insights
GOOGLE_PAGESPEED_API_KEY=
# Google Places API
GOOGLE_PLACES_API_KEY=
# Microsoft Graph API (Email)
MS_GRAPH_CLIENT_ID=
MS_GRAPH_CLIENT_SECRET=
MS_GRAPH_TENANT_ID=
# Rate Limiting
REDIS_URL=redis://localhost:6379/0 # Production only
# Session
SESSION_TYPE=filesystem # or redis
```
**⚠️ SECURITY WARNING:**
- **NEVER** commit `.env` file to Git
- Use `.env.example` as template (without actual secrets)
- Production secrets stored on server only
### Application Configuration (app.py)
```python
# Flask configuration
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY')
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_POOL_SIZE'] = 10
app.config['SQLALCHEMY_MAX_OVERFLOW'] = 20
# Session configuration
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_PERMANENT'] = True
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
# Security
app.config['WTF_CSRF_ENABLED'] = True
app.config['WTF_CSRF_TIME_LIMIT'] = None
# Rate limiting
app.config['RATELIMIT_STORAGE_URL'] = os.getenv('REDIS_URL', 'memory://')
app.config['RATELIMIT_HEADERS_ENABLED'] = True
```
---
## Deployment Configuration
### Gunicorn WSGI Server
**systemd Service:** `/etc/systemd/system/nordabiznes.service`
```ini
[Unit]
Description=Norda Biznes Partner
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
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
**Key Settings:**
- **Workers:** 4 (2 × CPU cores + 1)
- **Bind:** 127.0.0.1:5000 (localhost only, NPM proxies from external)
- **Timeout:** 120 seconds (for long AI requests)
- **User:** www-data (non-root for security)
**Service Management:**
```bash
# Start service
sudo systemctl start nordabiznes
# Stop service
sudo systemctl stop nordabiznes
# Restart service
sudo systemctl restart nordabiznes
# Check status
sudo systemctl status nordabiznes
# View logs
sudo journalctl -u nordabiznes -f
```
---
## Logging and Monitoring
### Application Logging
**Configuration:**
```python
import logging
# Production logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('/var/log/nordabiznes/app.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
```
**Log Locations:**
- **Application:** `/var/log/nordabiznes/app.log`
- **Gunicorn Access:** `/var/log/nordabiznes/access.log`
- **Gunicorn Error:** `/var/log/nordabiznes/error.log`
- **systemd:** `journalctl -u nordabiznes`
**Log Levels:**
- **DEBUG:** Development only, verbose output
- **INFO:** General information (startup, shutdown, API calls)
- **WARNING:** Degraded performance, fallback scenarios
- **ERROR:** Errors that don't crash the app
- **CRITICAL:** Errors that crash the app
### Health Check Endpoint
**Route:** `/health`
**Method:** GET
**Authentication:** None (public)
**Response:**
```json
{
"status": "healthy",
"database": "connected",
"timestamp": "2026-01-10T12:00:00Z",
"version": "1.0.0"
}
```
**Usage:**
- NPM health checks
- Monitoring systems (Zabbix, Prometheus)
- Uptime monitoring (UptimeRobot)
- Load balancer health checks
---
## Security Features
### 1. Authentication Security
- **Password Hashing:** bcrypt (cost factor 12)
- **Session Security:** httponly, secure, samesite cookies
- **Email Verification:** Required before login
- **Password Reset:** Time-limited tokens (1 hour expiry)
- **Account Lockout:** (Planned) After 5 failed attempts
### 2. Authorization
- **Three Levels:** Public, User, Admin
- **Decorator Pattern:** `@login_required`, `@admin_required`
- **Object-Level:** Company owners can edit their profiles
### 3. Input Validation
- **CSRF Protection:** All POST/PUT/DELETE requests
- **XSS Prevention:** Jinja2 auto-escaping
- **SQL Injection:** SQLAlchemy parameterized queries
- **File Upload:** Whitelist extensions, size limits
### 4. API Security
- **Rate Limiting:** 200/day global, 100/day chat
- **API Keys:** Environment variables only
- **CORS:** Disabled by default
- **HTTPS Only:** All API calls to external services
### 5. Database Security
- **Localhost Only:** PostgreSQL accepts only 127.0.0.1 connections
- **Password Auth:** Strong passwords for DB users
- **Least Privilege:** Application user has limited permissions
- **No Root Access:** Application runs as www-data user
---
## Related Documentation
### Higher-Level Diagrams
- [System Context Diagram (C4 Level 1)](./01-system-context.md) - External actors and systems
- [Container Diagram (C4 Level 2)](./02-container-diagram.md) - Major containers and technology stack
### Same-Level Diagrams
- [Database Schema Diagram](./05-database-schema.md) - (Planned) Detailed ER diagram
- [External Integrations Diagram](./06-external-integrations.md) - (Planned) API integration details
### Lower-Level Documentation
- [Data Flow Diagrams](./flows/) - (Planned) Sequence diagrams for key flows
- [API Endpoints Reference](./10-api-endpoints.md) - (Planned) Complete API documentation
### Infrastructure Documentation
- [Deployment Architecture](./03-deployment-architecture.md) - Servers, network, ports
- [Network Topology](./07-network-topology.md) - (Planned) Network diagram
- [Critical Configurations](./08-critical-configurations.md) - (Planned) NPM, SSL, etc.
### Analysis Documents (Phase 1)
- [Flask Application Structure Analysis](../.auto-claude/specs/003-.../analysis/flask-application-structure.md) - Detailed code analysis
- [Database Schema Analysis](../.auto-claude/specs/003-.../analysis/database-schema.md) - Database deep dive
- [External API Integrations Analysis](../.auto-claude/specs/003-.../analysis/external-api-integrations.md) - API details
- [Data Flows Analysis](../.auto-claude/specs/003-.../analysis/data-flows.md) - Flow documentation
---
## Maintenance Notes
### When to Update This Diagram
Update this document when:
- **New routes** are added to `app.py`
- **New service modules** are created
- **Database models** are added or significantly changed
- **Major refactoring** of component structure
- **New external API** integrations
- **Authentication/authorization** logic changes
### What NOT to Update Here
Don't update for:
- Bug fixes that don't change architecture
- Template content changes
- CSS/JS styling updates
- Minor field additions to existing models
- Configuration value changes
### Review Frequency
- **Quarterly** review during architecture meetings
- **After major releases** (version bumps)
- **Before onboarding** new developers
---
## Glossary
**C4 Model**
- Software architecture diagramming approach with 4 levels: Context, Container, Component, Code
**Flask**
- Python web framework for building web applications
**SQLAlchemy**
- Object-Relational Mapping (ORM) library for Python
**Gunicorn**
- WSGI HTTP server for Python applications
**WSGI**
- Web Server Gateway Interface, Python web application standard
**ORM**
- Object-Relational Mapping, database abstraction layer
**FTS**
- Full-Text Search, PostgreSQL text search feature
**pg_trgm**
- PostgreSQL extension for fuzzy text matching using trigrams
**Jinja2**
- Template engine for Python (used by Flask)
**bcrypt**
- Password hashing algorithm
**CSRF**
- Cross-Site Request Forgery, web security vulnerability
**XSS**
- Cross-Site Scripting, web security vulnerability
**Rate Limiting**
- Controlling request frequency to prevent abuse
**Streaming Response**
- Sending response data in chunks (used for AI chat)
---
**Document Status:** ✅ Complete
**Last Reviewed:** 2026-01-10
**Next Review:** 2026-04-10 (Quarterly)