Nowe funkcje w zopk_knowledge_service.py: - find_duplicate_entities() - wyszukiwanie podobnych encji (pg_trgm) - merge_entities() - łączenie encji z transferem relacji - get_entity_merge_preview() - podgląd przed połączeniem Nowe endpointy w app.py: - GET /admin/zopk/knowledge/duplicates - panel zarządzania duplikatami - POST /api/zopk/knowledge/duplicates/preview - podgląd merge - POST /api/zopk/knowledge/duplicates/merge - wykonanie merge Nowy szablon: - templates/admin/zopk_knowledge_duplicates.html - UI z kartami encji Dodatkowo: - Aktualizacja CLAUDE.md z procedurą wdrażania - Skrypt scripts/run_migration.py do uruchamiania migracji SQL Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
41 KiB
Norda Biznes Hub - Instrukcje dla Claude
Opis projektu
Platforma katalogowa i networkingowa dla członków stowarzyszenia Norda Biznes z Wejherowa.
- Produkcja: https://nordabiznes.pl
- Status: LIVE (od 2025-11-23)
- Firmy: 80 członków Norda Biznes (100% pokrycia)
Struktura projektu
nordabiz/
├── app.py # Główna aplikacja Flask (routes, auth, API)
├── database.py # Modele SQLAlchemy (Company, User, Chat)
├── gemini_service.py # Integracja Google Gemini AI
├── nordabiz_chat.py # Silnik chatu AI z kontekstem firm
├── search_service.py # Unified SearchService (synonimy, FTS, fuzzy)
├── templates/ # Szablony Jinja2
├── static/ # CSS, JS, obrazy
├── database/ # Schematy SQL, migracje
├── data/ # Dane źródłowe JSON
├── tests/ # Testy jakości AI
│ ├── ai_quality_evaluator.py
│ ├── ai_quality_test_cases.json
│ └── results/ # Wyniki testów (JSON)
├── scripts/ # Narzędzia Node.js
└── docs/ # Dokumentacja
├── architecture/ # Architektura systemu (diagramy, przepływy)
├── INCIDENT_REPORT_20260102.md
└── INCIDENT_REPORT_20260115.md
Dokumentacja architektury
Kompletna dokumentacja architektury systemu dostępna w katalogu docs/architecture/.
📚 Dokumenty główne
| Dokument | Opis |
|---|---|
| README.md | Przegląd całej dokumentacji architektury (start tutaj!) |
| 01-system-context.md | Kontekst systemu (C4 Level 1) - aktorzy i systemy zewnętrzne |
| 02-container-diagram.md | Diagram kontenerów (C4 Level 2) - Flask, PostgreSQL, NPM, API |
| 03-deployment-architecture.md | Architektura wdrożenia - serwery, porty, infrastruktura |
| 04-flask-components.md | Komponenty Flask - routes, services, models |
| 05-database-schema.md | Schemat bazy danych - 36 tabel, relacje, indeksy |
| 06-external-integrations.md | Integracje API - Gemini, Brave, PageSpeed, Places, KRS, MS Graph |
| 07-network-topology.md | Topologia sieci - Fortigate, NPM, routing, DNS |
| 08-critical-configurations.md | Konfiguracje krytyczne - NPM proxy, SSL, PostgreSQL, systemd |
| 09-security-architecture.md | Architektura bezpieczeństwa - RBAC, CSRF, strefy zaufania |
| 10-api-endpoints.md | Referencja API - 90+ endpointów, auth, rate limiting |
🔄 Przepływy danych (Data Flows)
| Przepływ | Opis |
|---|---|
| 01-authentication-flow.md | Rejestracja, login, reset hasła, sesje |
| 02-search-flow.md | Wyszukiwanie firm - synonimy, FTS, fuzzy matching |
| 03-ai-chat-flow.md | Chat AI - kontekst, Gemini API, tracking kosztów |
| 04-seo-audit-flow.md | Audyt SEO - PageSpeed API, analiza on-page/technical |
| 05-news-monitoring-flow.md | Monitoring newsów - Brave API, filtrowanie AI, moderacja |
| 06-http-request-flow.md | Przepływ HTTP - user → NPM → Flask → PostgreSQL |
⚡ Szybki start
Dla deweloperów:
- Zacznij od architecture/README.md
- Przejrzyj 04-flask-components.md i 05-database-schema.md
- Sprawdź przepływy w flows/ dla zrozumienia logiki biznesowej
Dla DevOps:
- 03-deployment-architecture.md - infrastruktura
- 07-network-topology.md - sieć i routing
- 08-critical-configurations.md - KRYTYCZNE! NPM port 5000
Dla architektów:
- 01-system-context.md - widok wysokopoziomowy
- 02-container-diagram.md - komponenty główne
- 06-external-integrations.md - zależności zewnętrzne
🛡️ Kluczowe ostrzeżenia z dokumentacji
NPM Proxy (KRYTYCZNE!):
- Port forward MUSI być 5000, NIE 80!
- Szczegóły: 08-critical-configurations.md
- Incydent: INCIDENT_REPORT_20260102.md
PostgreSQL:
- Skrypty używają
localhost (127.0.0.1), NIE10.22.68.249 - Szczegóły: 08-critical-configurations.md
API Limity:
- Gemini: 1,500 req/dzień (free tier)
- PageSpeed: 25,000 req/dzień
- Brave Search: 2,000 req/miesiąc
- Szczegóły: 06-external-integrations.md
Uruchamianie skryptów na produkcji (KRYTYCZNE!):
- SSH timeout NIE oznacza że komenda nie została wykonana!
- ZAWSZE sprawdź czy poprzedni proces nie działa przed ponowną próbą:
ps aux | grep <skrypt> - Dla długich operacji używaj
nohuplubscreen - Przy problemach z SSH użyj QEMU guest agent:
ssh root@10.22.68.123 "qm guest exec 249 -- <komenda>" - Incydent: INCIDENT_REPORT_20260115.md
Technologie
| Warstwa | Technologia |
|---|---|
| Backend | Flask 3.0, SQLAlchemy 2.0, Python 3.9+ |
| Frontend | HTML5, CSS3, Vanilla JS, Jinja2 |
| Baza danych | PostgreSQL (prod i dev via Docker) |
| AI | Google Gemini 2.0 Flash (free tier, 200 req/dzień) |
| Security | Flask-Login, Flask-WTF (CSRF), Flask-Limiter |
Środowiska
Development (lokalne)
- Baza: PostgreSQL via Docker (
localhost:5433/nordabiz) - Port: 5000 lub 5001
- Uruchomienie:
python3 app.py - Docker DB:
docker compose up -d(jeśli nie działa)
Production
- Serwer: NORDABIZ-01 (VM 249, IP 10.22.68.249)
- Baza: PostgreSQL na 10.22.68.249:5432
- Reverse Proxy: NPM na R11-REVPROXY-01 (VM 119, IP 10.22.68.250)
- Domena: nordabiznes.pl (DNS w OVH)
- SSL: Let's Encrypt (auto-renewal)
NPM Proxy Configuration (KRYTYCZNE!)
Proxy Host ID: 27 Forward Port: 5000 (NIE 80!)
PRAWIDŁOWA KONFIGURACJA:
NPM (10.22.68.250) → Backend (10.22.68.249:5000) ✓
BŁĘDNA KONFIGURACJA (powoduje pętlę przekierowań):
NPM (10.22.68.250) → Backend (10.22.68.249:80) ✗
UWAGA: Na serwerze 10.22.68.249 działa nginx na porcie 80 który przekierowuje na HTTPS. Flask/Gunicorn działa na porcie 5000. Przy edycji proxy hosta ZAWSZE sprawdź czy port = 5000!
Weryfikacja po zmianach NPM:
curl -I https://nordabiznes.pl/health
# Oczekiwany: HTTP 200
Raport incydentu: docs/INCIDENT_REPORT_20260102.md
Git & Deployment
Repozytoria Git
| Remote | URL | Cel |
|---|---|---|
| origin (GitHub) | git@github.com:pienczyn/nordabiz.git |
Cloud backup, CI/CD ready |
| inpi (Gitea) | git@10.22.68.180:maciejpi/nordabiz.git |
Wewnętrzny backup, deploy source |
Konta:
- GitHub:
pienczyn - Gitea (r11-git-inpi):
maciejpi(osobiste),gitadmin(admin Gitea)
Workflow Deployment
┌─────────┐ git push ┌─────────┐ git pull ┌─────────┐
│ DEV │ ────────────► │ Gitea │ ◄──────────── │ PROD │
│ (Mac) │ │ (INPI) │ │ │
└─────────┘ └─────────┘ └─────────┘
│
└──── git push ────► GitHub (backup)
Komendy deployment:
# 1. DEV: Push do obu repozytoriów
git push origin master && git push inpi master
# 2. PROD: Pull i restart
ssh maciejpi@10.22.68.249 "cd /var/www/nordabiznes && sudo -u www-data git pull && sudo systemctl restart nordabiznes"
Serwery Git
| Serwer | IP | Port | Usługa |
|---|---|---|---|
| r11-git-inpi | 10.22.68.180 | 3000 (HTTPS) | Gitea |
| GitHub | github.com | 22/443 | GitHub |
Gitea wymaga HTTPS (nie HTTP) - URL: https://10.22.68.180:3000/
PROD Git Config
- Remote:
https://10.22.68.180:3000/maciejpi/nordabiz.git - User: www-data
- SSL verify: disabled (
git -c http.sslVerify=false)
Procedura wdrażania (WAŻNE!)
Pełna procedura wdrażania z migracjami SQL:
# 1. DEV: Push do obu repozytoriów
git push origin master && git push inpi master
# 2. PROD: Pull zmiany
ssh maciejpi@10.22.68.249 "cd /var/www/nordabiznes && sudo -u www-data git pull"
# 3. PROD: Uruchom migracje SQL (jeśli są)
ssh maciejpi@10.22.68.249 "cd /var/www/nordabiznes && /var/www/nordabiznes/venv/bin/python3 scripts/run_migration.py database/migrations/XXX_nazwa.sql"
# 4. PROD: Restart serwisu
ssh maciejpi@10.22.68.249 "sudo systemctl restart nordabiznes"
# 5. Weryfikacja
curl -sI https://nordabiznes.pl/health | head -3
⚠️ UWAGI KRYTYCZNE:
-
Migracje SQL - NIE używaj
psqlbezpośrednio (wymaga hasła). Użyj skryptuscripts/run_migration.pyktóry czyta DATABASE_URL z.env. -
Uprawnienia logów - Serwis działa jako
maciejpi(niewww-data). Jeśli pojawi się błądPermission denied: /var/log/nordabiznes/*:ssh maciejpi@10.22.68.249 "sudo chown -R maciejpi:maciejpi /var/log/nordabiznes/" -
502 po restarcie - Czasami występuje chwilowy 502. Poczekaj 3-5 sekund i sprawdź ponownie.
-
Git pull - Używaj
sudo -u www-data git pull(www-data ma dostęp do kluczy SSH).
Auto Claude - Konfiguracja i rozwiązywanie problemów
Pliki stanu Auto Claude (WAŻNE!)
Auto Claude tworzy lokalne pliki stanu które NIE POWINNY być commitowane:
.auto-claude-security.json- stan bezpieczeństwa projektu.auto-claude-status- status bieżącego zadania.auto-claude/- katalog roboczy Auto Claude
Problem: Auto Claude czasami dodaje te pliki do staging area w worktree branches, co powoduje konflikty merge gdy branch jest mergowany do master.
Rozwiązanie (wdrożone 2026-01-10):
-
.gitignore- pliki są ignorowane:.auto-claude/ .auto-claude-security.json .auto-claude-status -
Pre-commit hook - automatycznie usuwa te pliki ze staging area:
.git/hooks/pre-commitHook sprawdza przed każdym commitem czy pliki Auto Claude są staged i automatycznie je usuwa.
-
Pliki usunięte z śledzenia - wykonano
git rm --cachedna master
Rozwiązywanie konfliktów merge z Auto Claude
Jeśli pojawi się konflikt merge z plikami .auto-claude-*:
# 1. Sprawdź czy to konflikt zmiana/usunięcie
git status
# 2. Usuń pliki Auto Claude z merge (akceptuj usunięcie z master)
git rm .auto-claude-security.json .auto-claude-status
# 3. Dokończ merge
git commit -m "Merge branch 'feature' - resolve Auto Claude file conflicts"
Worktrees Auto Claude
Auto Claude tworzy worktrees dla każdego zadania w:
.auto-claude/worktrees/tasks/<task-id>/
Każdy worktree ma własny branch i własne pliki stanu. Po zamergowaniu zadania worktree może być usunięty.
Sprawdzenie aktywnych worktrees:
git worktree list
Usunięcie nieaktualnego worktree:
git worktree remove .auto-claude/worktrees/tasks/<task-id>
git branch -d auto-claude/<task-id>
Konwencje danych
Identyfikatory firm
- Slug: kebab-case z nazwy, np.
pixlab-sp-z-o-o - NIP: 10 cyfr bez myślników, np.
5882436505 - REGON: 9 lub 14 cyfr
- KRS: 10 cyfr (tylko spółki)
Kategorie firm
IT- IT i TechnologieConstruction- BudownictwoServices- Usługi (prawne, księgowe, doradcze)Production- ProdukcjaTrade- HandelOther- Pozostałe
Poziomy jakości danych
basic- Nazwa, NIP, kontaktenhanced- Pełne dane, zweryfikowanecomplete- Wzbogacone o usługi, kompetencje, certyfikaty
Ważne zasady
Bezpieczeństwo
- NIE edytuj bezpośrednio bazy produkcyjnej PostgreSQL
- Zawsze testuj zmiany na DEV PostgreSQL (Docker: localhost:5433) przed wdrożeniem
- Klucze API i hasła tylko w
.env(nigdy w kodzie) - Rate limiting: 200 req/dzień, 50 req/godzinę
Mechanizmy bezpieczeństwa (wdrożone)
| Mechanizm | Ocena | Opis |
|---|---|---|
| 2FA (TOTP) | ★★★★★ | Uwierzytelnianie dwuskładnikowe przez aplikacje mobilne |
| CSRF Protection | ★★★★★ | Tokeny CSRF w formularzach (Flask-WTF) |
| HTTPS/TLS | ★★★★★ | Let's Encrypt SSL z auto-renewal |
| Hashowanie haseł | ★★★★★ | Werkzeug bcrypt |
| SQL Injection | ★★★★★ | SQLAlchemy ORM (parametryzowane zapytania) |
| XSS Protection | ★★★★★ | Jinja2 autoescape |
| GeoIP Blocking | ★★★★☆ | Blokowanie krajów: RU, CN, KP, IR, BY, SY, VE, CU |
| Rate Limiting | ★★★★☆ | Flask-Limiter + Redis |
| Account Lockout | ★★★★☆ | Blokada po 5 nieudanych logowaniach |
| Audit Log | ★★★★☆ | Śledzenie działań adminów |
| Honeypot | ★★★☆☆ | Wykrywanie botów (/.env, /wp-admin) |
| Security Alerting | ★★★☆☆ | Powiadomienia email o krytycznych zdarzeniach |
Panel bezpieczeństwa: /admin/security (dla adminów)
- Zakładka "Mechanizmy" - lista wszystkich mechanizmów z oceną gwiazdkową
- Zakładka "GeoIP" - statystyki blokowania (dzienne/miesięczne/roczne/od początku)
- Zakładka "Alerty" - alerty bezpieczeństwa do rozwiązania
- Zakładka "Audit log" - historia działań administracyjnych
- Zakładka "Zablokowane konta" - konta zablokowane przez brute-force
GeoIP Configuration (.env):
GEOIP_ENABLED=true
GEOIP_DB_PATH=/var/www/nordabiznes/geoip/GeoLite2-Country.mmdb
MaxMind GeoLite2:
- Account ID: 1282843
- Baza danych: GeoLite2-Country (aktualizowana co miesiąc)
- Lokalizacja:
/var/www/nordabiznes/geoip/GeoLite2-Country.mmdb
Zarządzanie danymi uwierzytelniającymi (KRYTYCZNE!)
NIGDY nie umieszczaj haseł i kluczy API bezpośrednio w kodzie źródłowym!
Jest to krytyczna podatność bezpieczeństwa (CWE-798: Use of Hard-coded Credentials). Narusza standardy bezpieczeństwa i może prowadzić do kompromitacji systemu jeśli repozytorium zostanie ujawnione.
Zasady obowiązkowe:
-
Używaj zmiennych środowiskowych dla wszystkich wrażliwych danych:
# PRAWIDŁOWO: DATABASE_URL = os.getenv('DATABASE_URL') API_KEY = os.getenv('GOOGLE_PAGESPEED_API_KEY') # BŁĘDNIE - NIGDY TAK NIE RÓB: DATABASE_URL = 'postgresql://user:password123@localhost/db' API_KEY = 'AIzaSyAbc123...' -
Konfiguruj wartości domyślne jako bezpieczne placeholdery:
# PRAWIDŁOWO - wartość domyślna która wymusi konfigurację .env: DATABASE_URL = os.getenv('DATABASE_URL', 'postgresql://user:CHANGE_ME@localhost/nordabiz') # BŁĘDNIE - wartość produkcyjna jako fallback: DATABASE_URL = os.getenv('DATABASE_URL', 'postgresql://user:RealPassword@10.22.68.249/nordabiz') -
Przechowuj dane uwierzytelniające w plikach .env:
- Produkcja:
/var/www/nordabiznes/.env - Development:
.envw katalogu projektu - Wzorzec:
.env.example(bez prawdziwych wartości!)
- Produkcja:
-
Wymagane zmienne środowiskowe:
Zmienna Cel Przykład DATABASE_URLPołączenie PostgreSQL dla skryptów Python postgresql://user:pass@127.0.0.1:5432/nordabizPGPASSWORDHasło PostgreSQL dla skryptów shell export PGPASSWORD='your_password'GOOGLE_PAGESPEED_API_KEYAPI Key Google PageSpeed AIzaSy...BRAVE_SEARCH_API_KEYAPI Key Brave Search BSA...GEMINI_API_KEYAPI Key Google Gemini AI AIzaSy... -
Skrypty shell - zawsze sprawdzaj czy zmienne są ustawione:
# PRAWIDŁOWO: if [ -z "$PGPASSWORD" ]; then echo "ERROR: PGPASSWORD not set" exit 1 fi psql -h localhost -U nordabiz_app -d nordabiz # BŁĘDNIE: PGPASSWORD='hardcoded_password' psql -h localhost -U nordabiz_app -d nordabiz -
NIGDY nie commituj plików z credentials:
.envjest w.gitignore- Sprawdzaj przed commitem:
git diffigit status - W razie wątpliwości:
git log -p | grep -i password
-
Co zrobić jeśli przypadkowo scommitujesz hasło:
- ⚠️ NATYCHMIAST zmień hasło w bazie/API
- Nie wystarczy usunąć z najnowszego commita - hasło pozostaje w historii Git
- Rozważ użycie
git filter-branchlubBFG Repo-Cleaner(skomplikowane) - Najlepiej: zmień hasło i traktuj stare jako skompromitowane
-
Wyjątki (kiedy dozwolone jest hasło w kodzie):
- ✅ Pliki dokumentacji (np. przykłady w CLAUDE.md, README)
- ✅
.env.examplejako szablon (z placeholderami) - ⛔ NIGDY w plikach wykonywalnych (.py, .sh, .js)
Weryfikacja przed wdrożeniem:
# Sprawdź czy nie ma hardcoded credentials w kodzie:
grep -r "PGPASSWORD=" --include="*.sh" .
grep -r "postgresql://.*:.*@" --include="*.py" . | grep -v "CHANGE_ME" | grep -v ".example" | grep -v "PASSWORD"
# Oczekiwany wynik: brak znalezisk (lub tylko w dokumentacji/placeholderach)
Import danych
- Używaj skryptów
import_*.pydo dodawania firm - Weryfikuj NIP przez API przed importem
- Zachowaj spójność slugów (unikalne, lowercase)
Deployment
- Przed wdrożeniem:
python -m py_compile app.py - SSH do NORDABIZ-01:
ssh maciejpi@10.22.68.249(ZAWSZE jako maciejpi, NIE root!) - Ścieżka aplikacji:
/var/www/nordabiznes - Restart:
sudo systemctl restart nordabiznes - ZAWSZE aktualizuj historię zmian (
release_notesw app.py) po wdrożeniu - Historia zmian: efekt końcowy, bez powtórzeń, prostym językiem
Szablony Jinja2 - WAŻNE!
- Blok
{% block extra_js %}wbase.htmljest już wewnątrz tagu<script> - NIE DODAWAJ własnych tagów
<script>wextra_js- spowoduje zagnieżdżenie i błąd JS - Prawidłowo:
{% block extra_js %}function foo() {...}{% endblock %} - Błędnie:
{% block extra_js %}<script>function foo() {...}</script>{% endblock %}
Uprawnienia PostgreSQL
- Po utworzeniu nowych tabel:
GRANT ALL ON TABLE ... TO nordabiz_app - Po utworzeniu sekwencji:
GRANT USAGE, SELECT ON SEQUENCE ... TO nordabiz_app - Baza:
nordabiz, użytkownik aplikacji:nordabiz_app
Testowanie na produkcji
- ZAWSZE używaj kont testowych do weryfikacji funkcjonalności
- Używaj przeglądarki (browser automation) do testów wymagających logowania
Konta testowe (PROD):
| Konto | Hasło | Rola | |
|---|---|---|---|
| Test User | test@nordabiznes.pl |
&Rc2LdbSw&jiGR0ek@Bz |
Zwykły użytkownik |
| Test Admin | testadmin@nordabiznes.pl |
cSfQbbwegwv1v3Q2Dm0Q |
Administrator |
Użycie:
- Test User - do testowania funkcji dostępnych dla zwykłych użytkowników
- Test Admin - do testowania panelu admina (rekomendacje, składki, kalendarz, forum, news)
Skrypty danych
Import (wykonywać kolejno)
python import_norda_companies.py # Batch 1 (56 firm)
python import_norda_batch2.py # Batch 2
python import_norda_batch3.py # Batch 3
python import_norda_batch4.py # Batch 4 (9 firm)
python import_norda_batch5.py # Batch 5 (8 firm)
Weryfikacja
python verify_all_companies_data.py # Raport jakości danych
python fix_krs_verification.py # Weryfikacja KRS
API Endpoints
| Endpoint | Metoda | Opis |
|---|---|---|
/ |
GET | Katalog firm |
/company/<slug> |
GET | Profil firmy |
/search |
GET | Wyszukiwanie |
/api/companies |
GET | Lista firm (JSON) |
/api/verify-nip |
GET | Weryfikacja NIP |
/health |
GET | Health check |
/chat |
GET | Interfejs chatu AI |
/admin/news |
GET/POST | Panel moderacji newsów (wymaga admin) |
/api/notifications |
GET | Powiadomienia użytkownika (JSON) |
SearchService (search_service.py)
Unified search dla chatbota AI i wyszukiwarki /search.
Funkcje:
- NIP/REGON lookup - bezpośrednie wyszukiwanie po identyfikatorach
- Synonym expansion - rozszerzenie słów kluczowych (np. "strony" → www, web, portal)
- PostgreSQL FTS - full-text search z tsvector (DEV i PROD)
- SQLite fallback - keyword scoring (tylko jako fallback, nieużywane)
- Fuzzy matching - pg_trgm dla literówek (gdy dostępne)
Scoring:
- Nazwa firmy: +10 punktów
- Opis: +5 punktów
- Usługi: +8 punktów
- Kompetencje: +7 punktów
- Miasto: +3 punktów
Użycie:
from search_service import search_companies
results = search_companies(db, "strony www", limit=10)
# Zwraca List[SearchResult] z company, score, match_type
UWAGA (PostgreSQL):
- Gdy FTS się nie powiedzie, wykonywany jest
db.rollback()przed fallbackiem - Bez tego następuje błąd
InFailedSqlTransaction
Chatbot AI (nordabiz_chat.py)
Konfiguracja:
- Limit firm do AI: 8 (zmienne w
_build_conversation_context, linia ~312) - Historia wiadomości: 10 ostatnich
- Search: używa
search_companies()z SearchService
Testy jakości AI
Uruchomienie:
python run_ai_quality_tests.py -v # Verbose output
python run_ai_quality_tests.py -v -s # Verbose + save report
python run_ai_quality_tests.py -q # Quick (tylko high-priority)
Przypadki testowe (tests/ai_quality_test_cases.json):
- 15 przypadków w 8 kategoriach
- Próg zaliczenia: 70%
- Kategorie: IT/Web, Services/Legal, Services/Accounting, Production/Metal, Construction, HVAC, Energy/Renewable, IT/Security
Powiązane zasoby
- Źródło danych: https://norda-biznes.info/czlonkowie
- Monitoring: Zabbix (do konfiguracji)
- Backup: Proxmox Backup Server (VM snapshots)
- DNS wewnętrzny: nordabiznes.inpi.local
Kontakty
- Projekt: Norda Biznes Hub
- Infrastruktura: INPI (skills: proxmox-manager, dns-manager, npm-manager)
Szablon profilu firmy (company profile)
Zatwierdzone zmiany do wdrożenia
Sekcje do połączenia (redukcja duplikatów):
- "O firmie" + "Profil działalności" → jedna sekcja "O firmie"
- "Oferta i usługi" + "Słowa kluczowe" → jedna sekcja "Usługi i kompetencje"
- "Wyróżniki" + "Wartości firmy" → jedna sekcja "Wyróżniki"
- Blok "Jakość Danych" → usunąć (badge przy nazwie wystarczy)
Sekcje które MUSZĄ pozostać
- Social Media (6 kafelków) - pokazywać WSZYSTKIE platformy, także te bez profilu ("Brak profilu") - ważne by widzieć czego brakuje
- Dane kontaktowe (sekcja z kartami) - to docelowe miejsce na WSZYSTKIE dane kontaktowe firmy (adres, telefony, emaile, godziny otwarcia, itp.)
Docelowa struktura profilu (po optymalizacji)
1. Header (nazwa, kategoria, badge weryfikacji, krótki opis)
2. Pasek kontaktowy (www, email, telefon, lokalizacja) - szybki dostęp
3. O firmie (połączone opisy)
4. Usługi i kompetencje (połączone tagi)
5. Wyróżniki (połączone z wartościami)
6. Dane kontaktowe (pełne karty - główne miejsce na kontakt)
7. Informacje prawne i biznesowe (NIP, REGON, KRS, rok założenia)
8. Social Media (wszystkie 6 platform - widoczne braki)
9. Strona WWW (analiza techniczna) - zawsze na końcu
Szablon: templates/company_detail.html
Plik do modyfikacji przy implementacji zmian.
Plan rozwoju - Aktualności
Social Media Audit (WDROŻONE)
Status: Wdrożone (2026-01-09)
Panel: /admin/social-media
Funkcje:
- Audyt profili Social Media firm (Facebook, Instagram, LinkedIn, YouTube, TikTok, Twitter)
- Weryfikacja aktywności profili (last_checked_at, followers_count)
- Raportowanie brakujących profili
Priorytet 1: Social Media Integration (Posts/Events)
Status: Planowane Cel: Pobieranie postów i wydarzeń z Social Media firm
Źródła danych:
- Facebook Pages firm członkowskich (posty, wydarzenia)
- LinkedIn Company Pages
- Google My Business (recenzje, posty)
Wymagania techniczne:
- Facebook Graph API (wymaga App Review dla pages_read_engagement)
- LinkedIn Marketing API (wymaga partnera lub OAuth)
- Google Business Profile API
Typy zdarzeń do importu:
social_post- posty z social mediasocial_event- wydarzenia z Facebookareview- nowe recenzje Google
Priorytet 2: News Monitoring (Google/Brave API)
Status: Wdrożone (2025-12-29) Źródła danych:
- Wzmianki o firmach w mediach lokalnych/branżowych
- Artykuły prasowe
- Komunikaty branżowe
Wymagania techniczne:
- Brave Search API (bezpłatny tier) LUB
- Google Custom Search API ($5/1000 queries)
- Cykliczne wyszukiwanie nazw firm
Typy zdarzeń do importu:
news_mention- wzmianka w mediachpress_release- komunikat prasowyaward- nagroda/wyróżnienie
Priorytet 3: Zarząd i Wspólnicy (rejestr.io)
Status: Planowane Cel: Wyświetlanie osób powiązanych z firmą bezpośrednio na stronie profilu
Dane do pobrania z rejestr.io:
- Zarząd (Prezes, Wiceprezes, Członkowie Zarządu)
- Prokurenci
- Wspólnicy z % udziałów
- Beneficjenci rzeczywiści
- Linki do profili osób (powiązania z innymi firmami)
Wymagania techniczne:
- Tabela
company_people(company_id, name, role, shares_percent, person_url) - Scraper Playwright (już mamy bazę w
analyze_connections.py) - Sekcja w
company_detail.htmlpo "Informacje prawne i biznesowe"
Przykład wyświetlania:
👥 ZARZĄD I WSPÓLNICY
┌─────────────────────────────────────────┐
│ 👔 Jan Kowalski - Prezes Zarządu │
│ 👔 Anna Nowak - Członek Zarządu │
│ 💼 Firma XYZ Sp. z o.o. - 60% udziałów │
│ 💼 Jan Kowalski - 40% udziałów │
└─────────────────────────────────────────┘
Korzyści:
- Widoczne powiązania między firmami Norda Biznes
- Ułatwiony networking (kto zna kogo)
- Transparentność struktury właścicielskiej
Notatki implementacyjne
- Scraper powinien deduplikować wydarzenia (hash tytułu + daty)
- Moderacja: nowe wydarzenia jako "pending" do zatwierdzenia przez admina
- Rate limiting: max 100 requestów/dzień do zewnętrznych API
News Monitoring
Opis funkcjonalności
System automatycznego monitoringu wzmianek o firmach Norda Biznes w mediach lokalnych i branżowych.
Tabela w bazie danych
company_news (
id SERIAL PRIMARY KEY,
company_id INTEGER REFERENCES companies(id),
title VARCHAR(500) NOT NULL,
description TEXT,
url VARCHAR(1000) NOT NULL,
source VARCHAR(200), -- nazwa portalu/medium
published_at TIMESTAMP,
news_type VARCHAR(50), -- news_mention, press_release, award
relevance_score FLOAT, -- 0.0-1.0 (AI scoring)
status VARCHAR(20) DEFAULT 'pending', -- pending, approved, rejected
moderated_by INTEGER, -- user_id admina
moderated_at TIMESTAMP,
rejection_reason TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(company_id, url)
)
Statusy newsów:
pending- oczekuje na moderacjęapproved- zatwierdzony, widoczny na profilu firmyrejected- odrzucony (spam, nieistotny, duplikat)
Brave Search API Integration
Konfiguracja:
- API Key w
.env:BRAVE_SEARCH_API_KEY - Endpoint:
https://api.search.brave.com/res/v1/news/search - Limit: 2000 req/miesiąc (free tier)
Parametry wyszukiwania:
params = {
"q": f'"{company_name}" OR "{nip}"',
"count": 10,
"freshness": "pw", # past week
"country": "pl",
"search_lang": "pl"
}
Skrypt pobierania: (do implementacji)
# TODO: Skrypt fetch_company_news.py wymaga implementacji
# cd /var/www/nordabiznes
# sudo -u www-data /var/www/nordabiznes/venv/bin/python3 scripts/fetch_company_news.py
AI Filtering (Gemini)
Filtracja wyników przez Google Gemini:
- Ocena relevance_score (0.0-1.0)
- Kategoryzacja news_type
- Wykrywanie duplikatów/spamu
- Automatyczne odrzucanie wyników < 0.3 relevance
Prompt systemu:
Oceń czy artykuł dotyczy działalności firmy {company_name}.
Zwróć JSON: {"relevance": 0.0-1.0, "type": "news_mention|press_release|award", "reason": "..."}
Panel admina /admin/news
URL: /admin/news
Wymaga: Zalogowany użytkownik z is_admin=True
Funkcje:
- Lista newsów pending do moderacji
- Filtrowanie po firmie, statusie, dacie
- Zatwierdzanie/odrzucanie z powodem
- Podgląd oryginalnego artykułu
- Bulk actions (zatwierdź wszystkie z relevance > 0.8)
Sekcja Aktualności na profilu firmy
Lokalizacja: templates/company_detail.html
Warunek: Tylko newsy ze statusem approved
Sortowanie: Po published_at DESC
Limit: 5 ostatnich newsów
Struktura wyświetlania:
AKTUALNOŚCI
├── [data] Tytuł artykułu (źródło)
│ Krótki opis...
│ [Czytaj więcej →]
└── [data] Kolejny artykuł...
System powiadomień
Endpoint: /api/notifications
Zwraca: JSON z listą powiadomień użytkownika
Typy powiadomień:
new_news- nowy news o obserwowanej firmie (dla zalogowanych)news_approved- news firmy użytkownika został zatwierdzonynews_rejected- news firmy użytkownika został odrzucony
Struktura odpowiedzi:
{
"notifications": [
{
"id": 1,
"type": "new_news",
"message": "Nowa wzmianka o PIXLAB",
"url": "/company/pixlab-sp-z-o-o#news",
"created_at": "2025-12-29T10:30:00",
"read": false
}
],
"unread_count": 3
}
Skrypty i cron (do implementacji)
# TODO: Skrypty do pobierania newsów wymagają implementacji
# Planowane komendy:
# python scripts/fetch_company_news.py --all
# python scripts/fetch_company_news.py --company pixlab-sp-z-o-o
# Cron job (do skonfigurowania po implementacji)
# 0 */6 * * * cd /var/www/nordabiznes && /var/www/nordabiznes/venv/bin/python3 scripts/fetch_company_news.py --all >> /var/log/nordabiznes/news_fetch.log 2>&1
ZOP Kaszubia News (ZOPK)
Opis
System monitoringu newsów związanych z projektem Zielony Okręg Przemysłowy Kaszubia.
Panel admina: /admin/zopk/news
Tematy ZOP Kaszubia (istotne)
- Zielony Okręg Przemysłowy Kaszubia - główny projekt
- Elektrownia jądrowa na Pomorzu - Lubiatowo-Kopalino
- Offshore wind Bałtyk - farmy wiatrowe, Baltic Power, Baltica
- Via Pomerania - droga ekspresowa Ustka-Bydgoszcz
- Droga Czerwona - połączenie z Portem Gdynia
- Kongsberg - norweskie inwestycje zbrojeniowe w Rumi
- Pakt Bezpieczeństwa Pomorze Środkowe - MON
- Izba Przedsiębiorców NORDA - lokalne organizacje biznesowe
Tematy NIEZWIĄZANE (do odrzucenia)
- Turystyka na Kaszubach (kuligi, lodowiska, hotele)
- Polityka ogólnopolska (Ziobro, polexit)
- Inne regiony Polski (Śląsk, Lubuskie, Małopolska)
- Wypadki i wydarzenia kryminalne
- Clickbait i lifestyle
Reguły auto-approve (WAŻNE!)
Próg auto-approve: score >= 3 (verified 2026-01-15)
| Score | Status | Opis |
|---|---|---|
| 1-2 | pending |
Wymaga ręcznej moderacji |
| 3-5 | auto_approved |
Automatycznie zatwierdzony |
Plik: zopk_news_service.py (linie 890, 1124, 1145)
Tabela zopk_news
zopk_news (
id, title, url, description,
source_name, source_domain, source_type,
ai_relevance_score INTEGER, -- 1-5 gwiazdek
status VARCHAR(20), -- pending, auto_approved, approved, rejected
confidence_score, source_count,
created_at, updated_at
)
Social Media - Stan aktualny
Statystyki (2025-12-29)
| Platforma | Liczba firm | Pokrycie |
|---|---|---|
| 39 | 49% | |
| 26 | 33% | |
| 22 | 28% | |
| YouTube | 17 | 21% |
| Twitter/X | 7 | 9% |
| TikTok | 4 | 5% |
Łącznie: 115 profili dla 53 firm (66% pokrycia) Firmy bez Social Media: 27
Tabela w bazie danych
company_social_media (
id, company_id, platform, url,
verified_at, source, is_valid,
last_checked_at, check_status,
page_name, followers_count,
created_at, updated_at
)
Platformy: facebook, instagram, youtube, linkedin, tiktok, twitter
Skrypty aktualizacji
# Aktualizacja Social Media z pliku JSON
cd /var/www/nordabiznes
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 update_social_media.py --dry-run # Test
sudo -u www-data /var/www/nordabiznes/venv/bin/python3 update_social_media.py # Produkcja
Pliki:
social_media_found.json- wyniki wyszukiwania (źródło danych)update_social_media.py- skrypt aktualizujący bazę
Firmy z najlepszym pokryciem Social Media
- PORTA KMI - Facebook, Instagram, YouTube, LinkedIn (4 platformy, 124K FB fans)
- Rumia Invest Park - Facebook, Instagram, YouTube, LinkedIn, Twitter (5 platform)
- GRAAL - Facebook, Instagram, YouTube, LinkedIn (4 platformy)
- Chopin Telewizja Kablowa - Facebook, Instagram, YouTube, Twitter (4 platformy)
- Hotel SPA Wieniawa - Facebook, Instagram, YouTube, TikTok (4 platformy)
Firmy bez Social Media (do uzupełnienia)
SIM Rumia, Rubinsolar, KORNIX, KBMS, Semerling Security, ARD Invest, AMA, Jubiler Agat, P&P, Progress Optima, Ampery, Bibrokers, CoolAir, Joker, KAMMET, Alumech, Litwic&Litwic, Orlex MG, Pro-Invest, Round Two, SCROL, ALMARES, Pucka Gospodarka Komunalna, Hebel Masiak, Lenap Hale, MKonsult, Portal
Audyt SEO (Panel /admin/seo)
Opis funkcjonalności
System audytu SEO stron internetowych firm członkowskich Norda Biznes. Wykorzystuje Google PageSpeed Insights API do analizy wydajności i jakości stron.
URL panelu: /admin/seo
Wymaga: Zalogowany użytkownik z is_admin=True
Konfiguracja API
Google PageSpeed Insights API:
- API Key w
.env:GOOGLE_PAGESPEED_API_KEY - Projekt Google Cloud: NORDABIZNES (
gen-lang-client-0540794446) - Limit: 25,000 zapytań/dzień (free tier)
- Endpoint:
https://www.googleapis.com/pagespeedonline/v5/runPagespeed
Klucz API:
- Nazwa w Google Cloud:
Page SPEED SEO Audit v2 - Wartość: Przechowywany w
.env(GOOGLE_PAGESPEED_API_KEY) - UWAGA: Nigdy nie commituj kluczy API do repozytorium!
Metryki audytu
| Metryka | Źródło | Skala |
|---|---|---|
| WYNIK SEO | PageSpeed Insights | 0-100 |
| PERFORMANCE | PageSpeed Insights | 0-100 |
| DOSTĘPNOŚĆ | PageSpeed Insights | 0-100 |
| BEST PRACTICES | PageSpeed Insights | 0-100 |
Interpretacja wyników:
- 90-100 (zielony) - Doskonały
- 50-89 (żółty) - Wymaga poprawy
- 0-49 (czerwony) - Słaby
Skrypty SEO
# Audyt pojedynczej firmy
cd /var/www/nordabiznes/scripts
python seo_audit.py --company-id 26
# Audyt wsadowy (batch)
python seo_audit.py --batch 1-10
# Audyt wszystkich firm
python seo_audit.py --all
# Tryb testowy (bez zapisu)
python seo_audit.py --company-id 26 --dry-run
WAŻNE - Połączenie z bazą danych
Skrypty w scripts/ muszą używać localhost (127.0.0.1) do połączenia z PostgreSQL:
# PRAWIDŁOWO (hasło z .env):
DATABASE_URL = 'postgresql://nordabiz_app:<PASSWORD_FROM_ENV>@127.0.0.1:5432/nordabiz'
# BŁĘDNIE (PostgreSQL nie akceptuje zewnętrznych połączeń):
DATABASE_URL = 'postgresql://nordabiz_app:<PASSWORD>@10.22.68.249:5432/nordabiz'
UWAGA: Hasło do bazy jest w .env na produkcji. NIE commituj haseł do repozytorium!
Pliki z konfiguracją bazy:
scripts/seo_audit.py(linia ~79)scripts/seo_report_generator.py(linia ~47)scripts/social_media_audit.py(linia ~53)
Tabela w bazie danych
seo_metrics (
id SERIAL PRIMARY KEY,
company_id INTEGER REFERENCES companies(id),
url VARCHAR(500),
seo_score INTEGER,
performance_score INTEGER,
accessibility_score INTEGER,
best_practices_score INTEGER,
pwa_score INTEGER,
audit_data JSONB, -- pełne dane z PageSpeed
audited_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
)
UI - Stylizowane modale
Panel SEO używa niestandardowych modali (zamiast natywnych confirm()/alert()):
- Modal potwierdzenia audytu z ikoną ostrzeżenia
- Modal informacyjny o błędach
- Animacje CSS (fade in/out)
Lokalizacja kodu: templates/admin_seo_dashboard.html
Planowane funkcjonalności (Backlog)
Priorytet 4: System rekomendacji i zdjęć
Status: Planowane Cel: Umożliwienie firmom członkowskim wzajemnego polecania się oraz prezentacji zdjęć
Funkcje:
- Rekomendacje między firmami (kto poleca kogo)
- Galeria zdjęć firmy (realizacje, zespół, biuro)
- Wyświetlanie na profilu firmy
Priorytet 5: Status członkostwa i płatności
Status: Planowane Cel: Śledzenie statusu członkostwa i składek miesięcznych
Funkcje:
- Status członka (aktywny, zawieszony, były członek)
- Informacja o opłaconych składkach
- Historia płatności
- Przypomnienia o zaległościach (dla admina)
Tabela membership_fees:
membership_fees (
id SERIAL PRIMARY KEY,
company_id INTEGER REFERENCES companies(id),
period_start DATE NOT NULL, -- początek okresu (np. 2026-01-01)
period_end DATE NOT NULL, -- koniec okresu (np. 2026-01-31)
amount DECIMAL(10,2) NOT NULL, -- kwota składki
status VARCHAR(20) DEFAULT 'pending', -- pending, paid, overdue
paid_at TIMESTAMP,
payment_method VARCHAR(50), -- przelew, gotówka
notes TEXT,
created_at TIMESTAMP DEFAULT NOW()
)
Forma prawna Norda Biznes
Stan obecny
- Forma: OPP (Organizacja Pożytku Publicznego)
- Typ: Stowarzyszenie non-profit
Planowana transformacja
- Docelowa forma: Działalność gospodarcza (przy Izbie)
- Cel: Możliwość pozyskiwania dofinansowań (granty, projekty UE)
- Korzyści:
- Dostęp do funduszy na rozwój platformy
- Możliwość świadczenia płatnych usług
- Elastyczność finansowa
Strategia monetyzacji
Model 3-tier Pricing (Kotwiczenie ceny)
Strategia: Trzy poziomy cenowe z zastosowaniem psychologii kotwiczenia ceny (Price Anchoring). Najwyższy poziom służy jako kotwica - sprawia że środkowy wydaje się atrakcyjny i jest docelowy.
| Poziom | Nazwa | Cena/mies. | Cel strategiczny |
|---|---|---|---|
| 1 | Basic | ~49 zł | Entry point, ograniczone funkcje |
| 2 | Premium | ~99 zł | DOCELOWY - rekomendowany, najlepsza wartość |
| 3 | Enterprise | ~199 zł | KOTWICA - premium, pełny dostęp |
Wyjątek: Członkowie Izby NORDA płacący składki (~200 zł/mies.) mają specjalny status - dostęp Premium za symboliczne 1 zł.
Psychologia 3-tier Pricing
- Ludzie naturalnie wybierają środkową opcję (efekt kompromisu)
- Wysoka cena kotwicy sprawia że środkowa wydaje się "okazją"
- Niska cena Basic sprawia że użytkownik czuje "upgrade jest wart dopłaty"
- Firmy stosujące 3-tier widzą ~30% wzrost przychodów
- Slack: dodanie Enterprise tier zwiększyło konwersję na Professional o 28%
Matryca dostępu do funkcji
| Funkcja | Basic | Premium | Enterprise |
|---|---|---|---|
| Katalog firm | ✅ | ✅ | ✅ |
| Profil firmy | ✅ | ✅ | ✅ |
| Forum | ❌ | ✅ | ✅ |
| Kalendarz wydarzeń | ❌ | ✅ | ✅ |
| Chat AI (NordaGPT) | ❌ | ✅ | ✅ |
| Raporty podstawowe | ❌ | ✅ | ✅ |
| Raporty zaawansowane | ❌ | ❌ | ✅ |
| Eksport danych (CSV/PDF) | ❌ | ❌ | ✅ |
| API dostęp | ❌ | ❌ | ✅ |
| Priorytetowe wsparcie | ❌ | ❌ | ✅ |
Implementacja techniczna (przyszłość)
# Model User - nowe pola
class User(Base):
# ...
subscription_tier = Column(String(20), default='basic') # basic, premium, enterprise
subscription_expires_at = Column(DateTime)
is_norda_member = Column(Boolean, default=False) # Członek Izby = specjalny status
# Dekorator kontroli dostępu
def requires_tier(min_tier):
def decorator(f):
@wraps(f)
def wrapped(*args, **kwargs):
tiers = ['basic', 'premium', 'enterprise']
user_tier_idx = tiers.index(current_user.subscription_tier)
required_idx = tiers.index(min_tier)
if user_tier_idx < required_idx:
flash(f'Ta funkcja wymaga konta {min_tier.title()}.', 'warning')
return redirect(url_for('pricing'))
return f(*args, **kwargs)
return wrapped
return decorator
# Użycie
@app.route('/raporty/zaawansowane')
@login_required
@requires_tier('enterprise')
def advanced_reports():
...
Raporty - podział według poziomu
Raporty podstawowe (Premium+):
- Staż członkostwa w Izbie NORDA
- Pokrycie Social Media
- Struktura branżowa
Raporty zaawansowane (Enterprise only):
- Ranking SEO
- Mapa lokalizacji
- Sieć rekomendacji
- Aktywność w wydarzeniach