Phase 1 of app.py refactoring - reducing from ~14,455 to ~13,699 lines.
New structure:
- blueprints/reports/ - 4 routes (/raporty/*)
- blueprints/community/contacts/ - 6 routes (/kontakty/*)
- blueprints/community/classifieds/ - 4 routes (/tablica/*)
- blueprints/community/calendar/ - 3 routes (/kalendarz/*)
- utils/ - decorators, helpers, notifications, analytics
- extensions.py - Flask extensions (csrf, login_manager, limiter)
- config.py - environment configurations
Updated templates with blueprint-prefixed url_for() calls.
⚠️ DO NOT DEPLOY before presentation on 2026-01-30 19:00
Tested on DEV: all endpoints working correctly.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
90 lines
2.4 KiB
Python
90 lines
2.4 KiB
Python
"""
|
|
Custom Decorators
|
|
=================
|
|
|
|
Reusable decorators for access control and validation.
|
|
"""
|
|
|
|
from functools import wraps
|
|
from flask import abort, flash, redirect, url_for
|
|
from flask_login import current_user
|
|
|
|
|
|
def admin_required(f):
|
|
"""
|
|
Decorator that requires user to be logged in AND be an admin.
|
|
|
|
Usage:
|
|
@bp.route('/admin/users')
|
|
@login_required
|
|
@admin_required
|
|
def admin_users():
|
|
...
|
|
|
|
Note: Always use @login_required BEFORE @admin_required
|
|
"""
|
|
@wraps(f)
|
|
def decorated_function(*args, **kwargs):
|
|
if not current_user.is_authenticated:
|
|
return redirect(url_for('auth.login'))
|
|
if not current_user.is_admin:
|
|
flash('Brak uprawnień administratora.', 'error')
|
|
return redirect(url_for('public.index'))
|
|
return f(*args, **kwargs)
|
|
return decorated_function
|
|
|
|
|
|
def verified_required(f):
|
|
"""
|
|
Decorator that requires user to have verified email.
|
|
|
|
Usage:
|
|
@bp.route('/forum/new')
|
|
@login_required
|
|
@verified_required
|
|
def new_topic():
|
|
...
|
|
"""
|
|
@wraps(f)
|
|
def decorated_function(*args, **kwargs):
|
|
if not current_user.is_authenticated:
|
|
return redirect(url_for('auth.login'))
|
|
if not current_user.is_verified:
|
|
flash('Musisz zweryfikować swój email, aby wykonać tę akcję.', 'warning')
|
|
return redirect(url_for('auth.resend_verification'))
|
|
return f(*args, **kwargs)
|
|
return decorated_function
|
|
|
|
|
|
def company_owner_or_admin(f):
|
|
"""
|
|
Decorator for routes that accept company_id.
|
|
Allows access only if user is admin OR owns the company.
|
|
|
|
Usage:
|
|
@bp.route('/company/<int:company_id>/edit')
|
|
@login_required
|
|
@company_owner_or_admin
|
|
def edit_company(company_id):
|
|
...
|
|
"""
|
|
@wraps(f)
|
|
def decorated_function(*args, **kwargs):
|
|
if not current_user.is_authenticated:
|
|
return redirect(url_for('auth.login'))
|
|
|
|
company_id = kwargs.get('company_id')
|
|
if company_id is None:
|
|
abort(400)
|
|
|
|
if current_user.is_admin:
|
|
return f(*args, **kwargs)
|
|
|
|
if current_user.company_id == company_id:
|
|
return f(*args, **kwargs)
|
|
|
|
flash('Nie masz uprawnień do tej firmy.', 'error')
|
|
return redirect(url_for('public.index'))
|
|
|
|
return decorated_function
|