nowe funkcje (zrealizowane) i poprawki w detekcji ip

This commit is contained in:
Mateusz Gruszczyński 2025-03-26 11:04:21 +01:00
parent 60754ec23b
commit b3c9aa1586
8 changed files with 151 additions and 35 deletions

View File

@ -1 +1,3 @@
ALTER TABLE global_settings ADD COLUMN allowed_login_hosts TEXT;
ALTER TABLE zbiorka ADD COLUMN zrealizowana BOOLEAN DEFAULT 0;

44
app.py
View File

@ -43,6 +43,7 @@ class Zbiorka(db.Model):
ukryta = db.Column(db.Boolean, default=False)
ukryj_kwote = db.Column(db.Boolean, default=False)
wplaty = db.relationship('Wplata', backref='zbiorka', lazy=True, order_by='Wplata.data.desc()')
zrealizowana = db.Column(db.Boolean, default=False)
class Wplata(db.Model):
id = db.Column(db.Integer, primary_key=True)
@ -91,7 +92,12 @@ def markdown_filter(text):
@app.route('/')
def index():
zbiorki = Zbiorka.query.filter_by(ukryta=False).all()
zbiorki = Zbiorka.query.filter_by(ukryta=False, zrealizowana=False).all()
return render_template('index.html', zbiorki=zbiorki)
@app.route('/zbiorki_zrealizowane')
def zbiorki_zrealizowane():
zbiorki = Zbiorka.query.filter_by(zrealizowana=True).all()
return render_template('index.html', zbiorki=zbiorki)
@app.errorhandler(404)
@ -106,6 +112,21 @@ def zbiorka(zbiorka_id):
abort(404)
return render_template('zbiorka.html', zbiorka=zb)
def get_real_ip():
# Sprawdź, czy żądanie pochodzi przez Cloudflare
if "CF-Connecting-IP" in request.headers:
return request.headers.get("CF-Connecting-IP")
# Następnie sprawdź nagłówek X-Real-IP
elif "X-Real-IP" in request.headers:
return request.headers.get("X-Real-IP")
# Jeśli jest nagłówek X-Forwarded-For, pobierz pierwszy adres na liście
elif "X-Forwarded-For" in request.headers:
forwarded_for = request.headers.get("X-Forwarded-For").split(",")
return forwarded_for[0].strip()
# W przeciwnym wypadku użyj standardowego remote_addr
return request.remote_addr
# TRASY LOGOWANIA I REJESTRACJI
@app.route('/login', methods=['GET', 'POST'])
@ -117,7 +138,8 @@ def login():
allowed_hosts_str = settings.allowed_login_hosts
# Sprawdzenie, czy adres IP klienta jest dozwolony
if not is_allowed_ip(request.remote_addr, allowed_hosts_str):
client_ip = get_real_ip()
if not is_allowed_ip(client_ip, allowed_hosts_str):
flash('Dostęp do endpointu /login jest zablokowany dla Twojego adresu IP', 'danger')
return redirect(url_for('index'))
@ -166,12 +188,13 @@ def register():
@app.route('/admin')
@login_required
def admin_dashboard():
# Tylko użytkownik z flagą is_admin ma dostęp do pełnych funkcji panelu
if not current_user.is_admin:
flash('Brak uprawnień do panelu administracyjnego', 'danger')
return redirect(url_for('index'))
zbiorki = Zbiorka.query.all()
return render_template('admin/dashboard.html', zbiorki=zbiorki)
active_zbiorki = Zbiorka.query.filter_by(zrealizowana=False).all()
completed_zbiorki = Zbiorka.query.filter_by(zrealizowana=True).all()
return render_template('admin/dashboard.html', active_zbiorki=active_zbiorki,
completed_zbiorki=completed_zbiorki)
@app.route('/admin/zbiorka/dodaj', methods=['GET', 'POST'])
@login_required
@ -346,6 +369,17 @@ def admin_settings():
return render_template('admin/settings.html', settings=settings)
@app.route('/admin/zbiorka/oznacz/<int:zbiorka_id>', methods=['POST'])
@login_required
def oznacz_zbiorka(zbiorka_id):
if not current_user.is_admin:
flash('Brak uprawnień do wykonania tej operacji', 'danger')
return redirect(url_for('index'))
zb = Zbiorka.query.get_or_404(zbiorka_id)
zb.zrealizowana = True
db.session.commit()
flash('Zbiórka została oznaczona jako zrealizowana', 'success')
return redirect(url_for('admin_dashboard'))
@app.route('/robots.txt')
def robots():

3
emergency_access.txt Normal file
View File

@ -0,0 +1,3 @@
Jeśli ten plik istwnieje w katalogu apliakcji, to wylacza zebzpieczenie logowania do panelu admina z ograniczeniem IP.
Musi miec rozszerzenie .txt

View File

@ -2,13 +2,66 @@
{% block title %}Panel Admina{% endblock %}
{% block content %}
<div class="container my-4">
<h3 class="mb-4">Panel Admina</h3>
<div class="mb-3">
<h2 class="mb-4">Panel Admina</h2>
<div class="mb-3">
<a href="{{ url_for('dodaj_zbiorka') }}" class="btn btn-success">Dodaj zbiórkę</a>
<a href="{{ url_for('admin_settings') }}" class="btn btn-primary">Ustawienia globalne</a>
<a href="{{ url_for('admin_settings') }}" class="btn btn-primary">Ustawienia</a>
</div>
<!-- Tabela zbiórek aktywnych -->
<h4>Aktywne zbiórki</h4>
<div class="table-responsive mb-5">
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Nazwa</th>
<th>Widoczność</th>
<th>Opcje</th>
</tr>
</thead>
<tbody>
{% for z in active_zbiorki %}
<tr>
<td>{{ z.id }}</td>
<td>{{ z.nazwa }}</td>
<td>
{% if z.ukryta %}
<span class="badge bg-secondary">Ukryta</span>
{% else %}
<span class="badge bg-success">Widoczna</span>
{% endif %}
</td>
<td>
<a href="{{ url_for('edytuj_zbiorka', zbiorka_id=z.id) }}" class="btn btn-primary btn-sm">Edytuj</a>
<a href="{{ url_for('admin_dodaj_wplate', zbiorka_id=z.id) }}" class="btn btn-warning btn-sm">Dodaj wpłatę</a>
<a href="{{ url_for('edytuj_stan', zbiorka_id=z.id) }}" class="btn btn-info btn-sm">Edytuj stan</a>
<!-- Przycisk do oznaczenia jako zrealizowana -->
<form action="{{ url_for('oznacz_zbiorka', zbiorka_id=z.id) }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-warning btn-sm">Oznacz jako zrealizowana</button>
</form>
<form action="{{ url_for('toggle_visibility', zbiorka_id=z.id) }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-secondary btn-sm">
{% if z.ukryta %} Pokaż {% else %} Ukryj {% endif %}
</button>
</form>
<form action="{{ url_for('usun_zbiorka', zbiorka_id=z.id) }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Czy na pewno chcesz usunąć tę zbiórkę?');">Usuń</button>
</form>
</td>
</tr>
{% else %}
<tr>
<td colspan="4" class="text-center">Brak aktywnych zbiórek</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Tabela zbiórek zrealizowanych -->
<h4>Zrealizowane zbiórki</h4>
<div class="table-responsive">
<table class="table table-dark table-striped table-hover">
<thead>
@ -20,7 +73,7 @@
</tr>
</thead>
<tbody>
{% for z in zbiorki %}
{% for z in completed_zbiorki %}
<tr>
<td>{{ z.id }}</td>
<td>{{ z.nazwa }}</td>
@ -47,7 +100,7 @@
</tr>
{% else %}
<tr>
<td colspan="4" class="text-center">Brak zbiórek</td>
<td colspan="4" class="text-center">Brak zbiórek zrealizowanych</td>
</tr>
{% endfor %}
</tbody>

View File

@ -34,8 +34,15 @@
<input type="checkbox" class="form-check-input" id="ukryj_kwote" name="ukryj_kwote" {% if zbiorka.ukryj_kwote %}checked{% endif %}>
<label class="form-check-label" for="ukryj_kwote">Ukryj kwoty (cel i stan)</label>
</div>
<button type="submit" class="btn btn-primary">Zaktualizuj zbiórkę</button>
<button type="button" class="btn btn-primary" id="ustaw-globalne">Ustaw globalne</button>
<form action="{{ url_for('oznacz_zbiorka', zbiorka_id=zbiorka.id) }}" method="post" style="display:inline;">
<button type="submit" class="btn btn-warning">Oznacz jako zrealizowana</button>
</form>
</form>
</div>
</div>

View File

@ -2,12 +2,13 @@
{% block title %}Ustawienia globalne{% endblock %}
{% block content %}
<div class="container my-4">
<div class="card shadow-sm">
<form method="post">
<!-- Blok ustawień konta -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-primary text-white">
<h3 class="card-title mb-0">Ustawienia globalne</h3>
<h3 class="card-title mb-0">Ustawienia konta</h3>
</div>
<div class="card-body">
<form method="post">
<div class="mb-3">
<label for="numer_konta" class="form-label">Globalny numer konta</label>
<input type="text" class="form-control" id="numer_konta" name="numer_konta" value="{{ settings.numer_konta if settings else '' }}" required>
@ -16,14 +17,24 @@
<label for="numer_telefonu_blik" class="form-label">Globalny numer telefonu BLIK</label>
<input type="text" class="form-control" id="numer_telefonu_blik" name="numer_telefonu_blik" value="{{ settings.numer_telefonu_blik if settings else '' }}" required>
</div>
</div>
</div>
<!-- Blok dozwolonych adresów IP -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-secondary text-white">
<h3 class="card-title mb-0">Dozwolone adresy IP</h3>
</div>
<div class="card-body">
<div class="mb-3">
<label for="allowed_login_hosts" class="form-label">Dozwolone hosty logowania</label>
<textarea class="form-control" id="allowed_login_hosts" name="allowed_login_hosts" rows="4" placeholder="Podaj adresy IP lub nazwy domen oddzielone przecinkami lub nowymi liniami">{{ settings.allowed_login_hosts if settings and settings.allowed_login_hosts else '' }}</textarea>
</div>
</div>
</div>
<div class="d-flex justify-content-between">
<button type="submit" class="btn btn-primary">Zapisz ustawienia</button>
<a href="{{ url_for('admin_dashboard') }}" class="btn btn-secondary">Powrót</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -15,6 +15,8 @@
<a class="navbar-brand" href="{{ url_for('index') }}">Zbiórki unitraklub.pl</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link" href="{{ url_for('index') }}">Aktualne zbiórki</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('zbiorki_zrealizowane') }}">Zrealizowane zbiórki</a></li>
{% if current_user.is_authenticated %}
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin_dashboard') }}">Panel Admina</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('logout') }}">Wyloguj</a></li>

View File

@ -1,7 +1,11 @@
{% extends 'base.html' %}
{% block title %}Aktywne zbiórki{% endblock %}
{% block title %}{% if request.path == url_for('zbiorki_zrealizowane') %}Zrealizowane zbiórki{% else %}Aktualnie aktywne zbiórki{% endif %}{% endblock %}
{% block content %}
{% if request.path == url_for('zbiorki_zrealizowane') %}
<h2 class="mb-4">Zrealizowane zbiórki</h2>
{% else %}
<h2 class="mb-4">Aktualnie aktywne zbiórki</h2>
{% endif %}
<div class="row">
{% for z in zbiorki %}
<div class="col-sm-12 col-md-6 col-lg-4 mb-4">