docs: Strategia Alias Bridge dla bezpiecznego wdrożenia Fazy 2
Odkrycie: Flask pozwala zarejestrować ten sam URL pod dwoma nazwami
- url_for('login') i url_for('auth.login') mogą współistnieć
- Zero zmian w szablonach podczas wdrożenia blueprintów
Strategia 3 podfaz:
- Faza 2a: Blueprinty + aliasy (niskie ryzyko)
- Faza 2b: Stopniowa migracja szablonów
- Faza 2c: Usunięcie aliasów
Porównanie z Big Bang: 20 vs 125 plików do zmiany naraz
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d6d5fa07f2
commit
9c39ff06ba
417
docs/MODULAR_MONOLITH_DEPLOYMENT_STRATEGY.md
Normal file
417
docs/MODULAR_MONOLITH_DEPLOYMENT_STRATEGY.md
Normal file
@ -0,0 +1,417 @@
|
||||
# Strategia Bezpiecznego Wdrożenia Fazy 2
|
||||
|
||||
> **Data:** 2026-01-31
|
||||
> **Status:** KRYTYCZNE - wymaga starannego planowania
|
||||
> **Ryzyko:** Wysokie - 125+ zmian musi być atomowych
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
### Obecne nazwy endpointów (bez prefiksu)
|
||||
|
||||
```python
|
||||
# app.py
|
||||
@app.route('/')
|
||||
def index(): ... # url_for('index')
|
||||
|
||||
@app.route('/login')
|
||||
def login(): ... # url_for('login')
|
||||
|
||||
@app.route('/dashboard')
|
||||
def dashboard(): ... # url_for('dashboard')
|
||||
```
|
||||
|
||||
### Docelowe nazwy endpointów (z prefiksem blueprintu)
|
||||
|
||||
```python
|
||||
# blueprints/public/routes.py
|
||||
@bp.route('/')
|
||||
def index(): ... # url_for('public.index')
|
||||
|
||||
# blueprints/auth/routes.py
|
||||
@bp.route('/login')
|
||||
def login(): ... # url_for('auth.login')
|
||||
```
|
||||
|
||||
### Skala zmian
|
||||
|
||||
| Lokalizacja | url_for('index') | url_for('login') | url_for('dashboard') | url_for('register') |
|
||||
|-------------|------------------|------------------|----------------------|---------------------|
|
||||
| app.py | 18 | 10 | 43 | 0 |
|
||||
| szablony | 27 | 11 | 6 | 10 |
|
||||
| **RAZEM** | **45** | **21** | **49** | **10** |
|
||||
|
||||
**Łącznie: ~125 miejsc do zmiany ATOMICZNIE**
|
||||
|
||||
---
|
||||
|
||||
## Strategie wdrożenia
|
||||
|
||||
### ❌ Opcja A: Stopniowe wdrażanie (NIEMOŻLIWE)
|
||||
|
||||
Nie można wdrożyć blueprintu bez zmiany wszystkich url_for.
|
||||
Każda częściowa zmiana = awaria.
|
||||
|
||||
### ⚠️ Opcja B: Aliasy / Redirecty (RYZYKOWNE)
|
||||
|
||||
```python
|
||||
# Po rejestracji blueprintu, dodać alias w app.py:
|
||||
@app.route('/_redirect_index')
|
||||
def _old_index():
|
||||
return redirect(url_for('public.index'))
|
||||
|
||||
# Ale url_for('index') wciąż nie zadziała!
|
||||
```
|
||||
|
||||
**Problem:** Flask nie wspiera natywnie aliasów endpointów.
|
||||
|
||||
### ⚠️ Opcja C: Endpoint override (CZĘŚCIOWO)
|
||||
|
||||
```python
|
||||
# W blueprincie - nadpisać nazwę endpointu
|
||||
@bp.route('/', endpoint='index') # Bez prefiksu!
|
||||
def index_view():
|
||||
...
|
||||
```
|
||||
|
||||
**Problem:**
|
||||
- `url_for('index')` zadziała ✅
|
||||
- `url_for('public.index')` NIE zadziała ❌
|
||||
- `utils/decorators.py` wymaga `url_for('public.index')` ❌
|
||||
|
||||
### ✅ Opcja D: Big Bang z testami (REKOMENDOWANA)
|
||||
|
||||
1. Przygotować WSZYSTKIE zmiany lokalnie
|
||||
2. Uruchomić PEŁNE testy lokalne
|
||||
3. Wdrożyć jednym commitem
|
||||
4. Mieć gotowy rollback
|
||||
|
||||
---
|
||||
|
||||
## Plan wdrożenia Fazy 2 (bezpieczny)
|
||||
|
||||
### Etap 1: Przygotowanie (lokalnie)
|
||||
|
||||
```bash
|
||||
# 1. Stworzyć nowy branch
|
||||
git checkout -b feature/phase2-auth-public
|
||||
|
||||
# 2. Stworzyć blueprinty
|
||||
mkdir -p blueprints/auth blueprints/public
|
||||
```
|
||||
|
||||
### Etap 2: Implementacja blueprintów
|
||||
|
||||
#### blueprints/auth/routes.py
|
||||
- Przenieść: login, logout, register, verify-2fa, forgot-password, reset-password, verify-email, resend-verification
|
||||
- Użyć: `from utils.helpers import validate_email, validate_password, sanitize_input`
|
||||
|
||||
#### blueprints/public/routes.py
|
||||
- Przenieść: index, company_detail, search, events, new_members, connections_map, release_notes
|
||||
- Użyć: `from utils.helpers import sanitize_input`
|
||||
|
||||
### Etap 3: Masowa zmiana url_for
|
||||
|
||||
```bash
|
||||
# Zmiana w szablonach
|
||||
find templates/ -name "*.html" -exec sed -i '' \
|
||||
-e "s/url_for('index')/url_for('public.index')/g" \
|
||||
-e "s/url_for('login')/url_for('auth.login')/g" \
|
||||
-e "s/url_for('logout')/url_for('auth.logout')/g" \
|
||||
-e "s/url_for('register')/url_for('auth.register')/g" \
|
||||
-e "s/url_for('dashboard')/url_for('account.dashboard')/g" \
|
||||
{} \;
|
||||
|
||||
# Zmiana w app.py
|
||||
sed -i '' \
|
||||
-e "s/url_for('index')/url_for('public.index')/g" \
|
||||
-e "s/url_for('login')/url_for('auth.login')/g" \
|
||||
... \
|
||||
app.py
|
||||
```
|
||||
|
||||
### Etap 4: Testy lokalne (KRYTYCZNE!)
|
||||
|
||||
```bash
|
||||
# 1. Uruchomić aplikację
|
||||
python3 app.py
|
||||
|
||||
# 2. Testy manualne - WSZYSTKIE muszą przejść!
|
||||
□ Strona główna ładuje się
|
||||
□ Menu nawigacji działa
|
||||
□ Link "Zaloguj" działa
|
||||
□ Formularz logowania działa
|
||||
□ Logowanie przekierowuje na dashboard
|
||||
□ Dashboard ładuje się
|
||||
□ Wylogowanie działa
|
||||
□ Rejestracja działa
|
||||
□ Reset hasła działa
|
||||
□ Wyszukiwarka działa
|
||||
□ Profil firmy działa
|
||||
□ Wszystkie istniejące blueprinty działają (reports, contacts, etc.)
|
||||
```
|
||||
|
||||
### Etap 5: Wdrożenie
|
||||
|
||||
```bash
|
||||
# 1. Commit
|
||||
git add .
|
||||
git commit -m "refactor(phase2): Extract auth + public blueprints
|
||||
|
||||
BREAKING CHANGE: All url_for() calls updated to use blueprint prefixes:
|
||||
- url_for('index') -> url_for('public.index')
|
||||
- url_for('login') -> url_for('auth.login')
|
||||
- url_for('register') -> url_for('auth.register')
|
||||
- url_for('dashboard') -> url_for('account.dashboard')
|
||||
|
||||
~125 files changed atomically."
|
||||
|
||||
# 2. Push
|
||||
git push origin feature/phase2-auth-public
|
||||
git checkout master
|
||||
git merge feature/phase2-auth-public
|
||||
git push origin master && git push inpi master
|
||||
|
||||
# 3. Deploy
|
||||
ssh maciejpi@10.22.68.249 "cd /var/www/nordabiznes && sudo -u www-data git pull && sudo systemctl restart nordabiznes"
|
||||
|
||||
# 4. Weryfikacja (natychmiast!)
|
||||
curl -sI https://nordabiznes.pl/ | head -3
|
||||
curl -sI https://nordabiznes.pl/login | head -3
|
||||
curl -sI https://nordabiznes.pl/dashboard | head -3
|
||||
```
|
||||
|
||||
### Etap 6: Rollback (jeśli potrzebny)
|
||||
|
||||
```bash
|
||||
# Natychmiastowy rollback
|
||||
git revert HEAD
|
||||
git push origin master && git push inpi master
|
||||
ssh maciejpi@10.22.68.249 "cd /var/www/nordabiznes && sudo -u www-data git pull && sudo systemctl restart nordabiznes"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist przed wdrożeniem
|
||||
|
||||
### Przygotowanie
|
||||
- [ ] Nowy branch utworzony
|
||||
- [ ] Blueprinty auth i public stworzone
|
||||
- [ ] Wszystkie routes przeniesione
|
||||
- [ ] Wszystkie url_for zmienione w app.py
|
||||
- [ ] Wszystkie url_for zmienione w szablonach
|
||||
- [ ] utils/decorators.py sprawdzony
|
||||
|
||||
### Testy lokalne
|
||||
- [ ] Aplikacja uruchamia się bez błędów
|
||||
- [ ] Strona główna działa
|
||||
- [ ] Logowanie działa
|
||||
- [ ] Dashboard działa
|
||||
- [ ] Wylogowanie działa
|
||||
- [ ] Rejestracja działa
|
||||
- [ ] Menu nawigacji działa
|
||||
- [ ] Istniejące blueprinty działają
|
||||
|
||||
### Przed wdrożeniem
|
||||
- [ ] Backup bazy danych (opcjonalnie)
|
||||
- [ ] Czas wdrożenia: niska aktywność użytkowników
|
||||
- [ ] Osoba monitorująca: dostępna
|
||||
|
||||
### Po wdrożeniu
|
||||
- [ ] Health check OK
|
||||
- [ ] Strona główna OK
|
||||
- [ ] Logowanie OK
|
||||
- [ ] Brak błędów w logach
|
||||
|
||||
---
|
||||
|
||||
## Alternatywa: Endpoint override (bezpieczniejsza?)
|
||||
|
||||
Zamiast zmieniać 125 miejsc, można użyć endpoint override:
|
||||
|
||||
```python
|
||||
# blueprints/public/__init__.py
|
||||
bp = Blueprint('public', __name__)
|
||||
|
||||
# blueprints/public/routes.py
|
||||
@bp.route('/', endpoint='index') # Endpoint = 'index' (bez prefiksu!)
|
||||
def index():
|
||||
...
|
||||
```
|
||||
|
||||
**Zalety:**
|
||||
- `url_for('index')` dalej działa ✅
|
||||
- Nie trzeba zmieniać szablonów ✅
|
||||
- Mniej ryzykowne ✅
|
||||
|
||||
**Wady:**
|
||||
- `utils/decorators.py` używa `url_for('public.index')` - trzeba zmienić ❌
|
||||
- Niespójna konwencja nazewnictwa ❌
|
||||
- Trudniejsze debugowanie ❌
|
||||
|
||||
### Decyzja
|
||||
|
||||
**REKOMENDACJA:** Użyć endpoint override dla pierwszego wdrożenia, potem stopniowo migrować na pełne nazwy.
|
||||
|
||||
```python
|
||||
# Faza 2a: Endpoint override (bezpieczne)
|
||||
@bp.route('/', endpoint='index')
|
||||
@bp.route('/login', endpoint='login')
|
||||
|
||||
# Faza 2b: Stopniowa zmiana szablonów (później)
|
||||
# url_for('index') -> url_for('public.index')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ REKOMENDOWANA STRATEGIA: Dual Endpoints (Alias Bridge)
|
||||
|
||||
### Dowód koncepcji (przetestowane!)
|
||||
|
||||
```python
|
||||
# Flask pozwala zarejestrować ten sam URL pod dwoma nazwami:
|
||||
app.register_blueprint(auth_bp) # Rejestruje /login -> auth.login
|
||||
|
||||
# Dodanie aliasu dla kompatybilności wstecznej:
|
||||
app.add_url_rule('/login', 'login', app.view_functions['auth.login'])
|
||||
|
||||
# Wynik:
|
||||
# /login -> auth.login (nowy)
|
||||
# /login -> login (stary alias)
|
||||
|
||||
# Oba url_for działają:
|
||||
# url_for('login') = /login ✓
|
||||
# url_for('auth.login') = /login ✓
|
||||
```
|
||||
|
||||
### Plan wdrożenia (3 podfazy)
|
||||
|
||||
#### Faza 2a: Blueprinty + Aliasy (BEZPIECZNE)
|
||||
|
||||
```python
|
||||
# blueprints/__init__.py
|
||||
def register_blueprints(app):
|
||||
from blueprints.auth import bp as auth_bp
|
||||
from blueprints.public import bp as public_bp
|
||||
|
||||
app.register_blueprint(auth_bp)
|
||||
app.register_blueprint(public_bp)
|
||||
|
||||
# ALIASY dla kompatybilności wstecznej
|
||||
# Szablony dalej używają url_for('login'), url_for('index'), etc.
|
||||
_create_endpoint_aliases(app, {
|
||||
# stara_nazwa: nowa_nazwa
|
||||
'index': 'public.index',
|
||||
'login': 'auth.login',
|
||||
'logout': 'auth.logout',
|
||||
'register': 'auth.register',
|
||||
'dashboard': 'public.dashboard', # lub account.dashboard
|
||||
'search': 'public.search',
|
||||
'company_detail': 'public.company_detail',
|
||||
'company_detail_by_slug': 'public.company_detail_by_slug',
|
||||
# ... wszystkie inne
|
||||
})
|
||||
|
||||
def _create_endpoint_aliases(app, aliases):
|
||||
"""Tworzy aliasy dla starych nazw endpointów."""
|
||||
for old_name, new_name in aliases.items():
|
||||
if new_name in app.view_functions:
|
||||
# Znajdź URL dla nowego endpointu
|
||||
for rule in app.url_map.iter_rules():
|
||||
if rule.endpoint == new_name:
|
||||
app.add_url_rule(
|
||||
rule.rule,
|
||||
old_name,
|
||||
app.view_functions[new_name],
|
||||
methods=rule.methods
|
||||
)
|
||||
break
|
||||
```
|
||||
|
||||
**Efekt Fazy 2a:**
|
||||
- ✅ Blueprinty działają
|
||||
- ✅ `url_for('login')` działa (alias)
|
||||
- ✅ `url_for('auth.login')` działa (nowy)
|
||||
- ✅ Szablony NIE wymagają zmian
|
||||
- ✅ Zero ryzyka awarii
|
||||
|
||||
#### Faza 2b: Stopniowa migracja szablonów
|
||||
|
||||
```bash
|
||||
# Można zmieniać po jednym szablonie
|
||||
# Każda zmiana = osobny commit
|
||||
|
||||
# Przykład:
|
||||
sed -i '' "s/url_for('login')/url_for('auth.login')/g" templates/base.html
|
||||
git commit -m "refactor: Migrate url_for in base.html"
|
||||
```
|
||||
|
||||
**Efekt Fazy 2b:**
|
||||
- ✅ Szablony używają nowych nazw
|
||||
- ✅ Aliasy dalej działają (dla nieprzemigrowanych)
|
||||
- ✅ Można robić stopniowo
|
||||
|
||||
#### Faza 2c: Usunięcie aliasów (cleanup)
|
||||
|
||||
```python
|
||||
# Po pełnej migracji szablonów:
|
||||
# Usunąć _create_endpoint_aliases()
|
||||
```
|
||||
|
||||
**Efekt Fazy 2c:**
|
||||
- ✅ Czysty kod
|
||||
- ✅ Tylko nowe nazwy endpointów
|
||||
- ✅ Pełna migracja zakończona
|
||||
|
||||
---
|
||||
|
||||
## Podsumowanie strategii
|
||||
|
||||
| Faza | Opis | Ryzyko | Szablony |
|
||||
|------|------|--------|----------|
|
||||
| **2a** | Blueprinty + aliasy | 🟢 Niskie | Bez zmian |
|
||||
| **2b** | Migracja szablonów | 🟢 Niskie | Stopniowo |
|
||||
| **2c** | Usunięcie aliasów | 🟢 Niskie | Po migracji |
|
||||
|
||||
### Porównanie z Big Bang
|
||||
|
||||
| Aspekt | Big Bang | Alias Bridge |
|
||||
|--------|----------|--------------|
|
||||
| Zmiany na raz | ~125 | ~20 (tylko blueprinty) |
|
||||
| Ryzyko awarii | 🔴 Wysokie | 🟢 Niskie |
|
||||
| Rollback | Trudny | Łatwy |
|
||||
| Testowanie | Wszystko naraz | Etapami |
|
||||
| Czas wdrożenia | 1 dzień | 3-5 dni (bezpiecznie) |
|
||||
|
||||
---
|
||||
|
||||
## Checklist Fazy 2a
|
||||
|
||||
### Przygotowanie
|
||||
- [ ] Stworzyć `blueprints/auth/__init__.py`
|
||||
- [ ] Stworzyć `blueprints/auth/routes.py`
|
||||
- [ ] Stworzyć `blueprints/public/__init__.py`
|
||||
- [ ] Stworzyć `blueprints/public/routes.py`
|
||||
- [ ] Przenieść routes z app.py do blueprintów
|
||||
- [ ] Dodać funkcję `_create_endpoint_aliases()`
|
||||
- [ ] Zaktualizować `blueprints/__init__.py`
|
||||
|
||||
### Testy lokalne
|
||||
- [ ] Aplikacja uruchamia się
|
||||
- [ ] `url_for('index')` działa
|
||||
- [ ] `url_for('public.index')` działa
|
||||
- [ ] Logowanie działa
|
||||
- [ ] Strona główna działa
|
||||
- [ ] Istniejące blueprinty działają
|
||||
|
||||
### Wdrożenie
|
||||
- [ ] Commit + push
|
||||
- [ ] Deploy na produkcję
|
||||
- [ ] Weryfikacja health check
|
||||
- [ ] Weryfikacja logowania
|
||||
|
||||
---
|
||||
|
||||
**Ostatnia aktualizacja:** 2026-01-31
|
||||
**Status:** REKOMENDOWANA STRATEGIA - Alias Bridge
|
||||
@ -451,7 +451,27 @@ nordabiz/
|
||||
| 10 | cleanup (usunięcie duplikatów z app.py) | 2h | Wszystkie fazy | ⏳ |
|
||||
| **RAZEM** | | **~39h** | | |
|
||||
|
||||
**UWAGA:** Faza 2 jest krytyczna - `auth` i `public` muszą być wdrożone RAZEM, bo `utils/decorators.py` wymaga obu blueprintów.
|
||||
**⚠️ UWAGA:** Faza 2 jest krytyczna - `auth` i `public` muszą być wdrożone RAZEM!
|
||||
|
||||
**Szczegółowa strategia:** [`docs/MODULAR_MONOLITH_DEPLOYMENT_STRATEGY.md`](MODULAR_MONOLITH_DEPLOYMENT_STRATEGY.md)
|
||||
|
||||
### Strategia "Alias Bridge" (bezpieczna)
|
||||
|
||||
| Podfaza | Opis | Ryzyko |
|
||||
|---------|------|--------|
|
||||
| **2a** | Blueprinty + aliasy dla starych nazw | 🟢 Niskie |
|
||||
| **2b** | Stopniowa migracja szablonów | 🟢 Niskie |
|
||||
| **2c** | Usunięcie aliasów | 🟢 Niskie |
|
||||
|
||||
**Kluczowa technika:** Rejestracja tego samego URL pod dwoma nazwami
|
||||
```python
|
||||
# Po rejestracji blueprintu:
|
||||
app.add_url_rule('/login', 'login', app.view_functions['auth.login'])
|
||||
|
||||
# Efekt:
|
||||
url_for('login') → /login ✓ (stary kod działa)
|
||||
url_for('auth.login') → /login ✓ (nowy kod działa)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user