17 Commits

Author SHA1 Message Date
Mateusz Gruszczyński
9193a94c0d zmrestore old login 2025-07-11 13:20:13 +02:00
Mateusz Gruszczyński
a35adf6101 zmiana w base.hmtl i formuarzy logowania 2025-07-11 13:10:10 +02:00
Mateusz Gruszczyński
3069f38623 fix in css 2025-07-11 12:49:03 +02:00
Mateusz Gruszczyński
194287aa39 poprawki w autoryzacji 2025-07-11 11:35:57 +02:00
Mateusz Gruszczyński
96546a3a9d poprawki w autoryzacji 2025-07-11 11:34:27 +02:00
Mateusz Gruszczyński
6d197eb1fe poprawki w autoryzacji 2025-07-11 11:30:27 +02:00
Mateusz Gruszczyński
7134de3e1f poprawki w autoryzacji 2025-07-11 11:24:06 +02:00
Mateusz Gruszczyński
add29fbb30 poprawki w autoryzacji 2025-07-11 11:21:17 +02:00
Mateusz Gruszczyński
18c34d8093 poprawki w autoryzacji 2025-07-11 11:02:43 +02:00
Mateusz Gruszczyński
7aa5c43c5a poprawki w autoryzacji 2025-07-11 10:56:57 +02:00
Mateusz Gruszczyński
7786310de3 poprawki w autoryzacji 2025-07-11 10:38:24 +02:00
Mateusz Gruszczyński
d91a46bf22 decydowanie o zyciu cookie 2025-07-11 00:03:09 +02:00
Mateusz Gruszczyński
153d50f875 decydowanie o zyciu cookie 2025-07-10 23:58:11 +02:00
Mateusz Gruszczyński
5e3146aa6a decydowanie o zyciu cookie 2025-07-10 23:57:27 +02:00
Mateusz Gruszczyński
404cc7a9bf fix serwowanie toasts.js i error handlery 2025-07-10 23:18:19 +02:00
Mateusz Gruszczyński
120b08efd0 fix serwowanie toasts.js i error handlery 2025-07-10 23:11:35 +02:00
Mateusz Gruszczyński
c219cd2691 fix serwowanie toasts.js i error handlery 2025-07-10 23:06:43 +02:00
9 changed files with 68 additions and 38 deletions

View File

@@ -14,4 +14,7 @@ DEFAULT_ADMIN_PASSWORD=admin123
# Katalog wgrywanych plików
UPLOAD_FOLDER=uploads
AUTHORIZED_COOKIE_VALUE=twoj_wlasny_hash
AUTHORIZED_COOKIE_VALUE=twoj_wlasny_hash
# czas zycia cookie
AUTH_COOKIE_MAX_AGE=86400

73
app.py
View File

@@ -34,6 +34,7 @@ DEFAULT_ADMIN_PASSWORD = app.config.get('DEFAULT_ADMIN_PASSWORD', 'admin123')
UPLOAD_FOLDER = app.config.get('UPLOAD_FOLDER', 'uploads')
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'}
AUTHORIZED_COOKIE_VALUE = app.config.get('AUTHORIZED_COOKIE_VALUE', '80d31cdfe63539c9')
AUTH_COOKIE_MAX_AGE = app.config.get('AUTH_COOKIE_MAX_AGE', 86400)
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
@@ -250,30 +251,52 @@ def inject_time():
def inject_has_authorized_cookie():
return {'has_authorized_cookie': 'authorized' in request.cookies}
@app.context_processor
def inject_is_blocked():
ip = request.access_route[0]
return {'is_blocked': is_ip_blocked(ip)}
@app.before_request
def require_system_password():
if 'authorized' not in request.cookies \
and request.endpoint != 'system_auth' \
and not request.endpoint.startswith('login') \
and request.endpoint != 'favicon':
endpoint = request.endpoint
if request.endpoint == 'static_bp.serve_js':
# Wyjątki: lib js/css zawsze przepuszczamy
if endpoint in ('static_bp.serve_js_lib', 'static_bp.serve_css_lib'):
return
ip = request.access_route[0]
if is_ip_blocked(ip):
abort(403)
if endpoint is None:
return
if endpoint == 'system_auth':
return
if 'authorized' not in request.cookies and not endpoint.startswith('login') and endpoint != 'favicon':
# Dla serve_js przepuszczamy tylko toasts.js
if endpoint == 'static_bp.serve_js':
requested_file = request.view_args.get("filename", "")
if requested_file == "toasts.js":
return
if requested_file.endswith(".js"):
return redirect(url_for('system_auth', next=request.url))
else:
return
return
if request.endpoint.startswith('static_bp.'):
# Blokujemy pozostałe static_bp
if endpoint.startswith('static_bp.'):
return
if request.path == '/':
return redirect(url_for('system_auth'))
else:
from urllib.parse import urlparse, urlunparse
parsed = urlparse(request.url)
fixed_url = urlunparse(parsed._replace(netloc=request.host))
return redirect(url_for('system_auth', next=fixed_url))
from urllib.parse import urlparse, urlunparse
parsed = urlparse(request.url)
fixed_url = urlunparse(parsed._replace(netloc=request.host))
return redirect(url_for('system_auth', next=fixed_url))
@app.template_filter('filemtime')
def file_mtime_filter(path):
@@ -313,15 +336,6 @@ def forbidden(e):
message="Nie masz uprawnień do wyświetlenia tej strony."
), 403
@app.errorhandler(500)
def internal_error(e):
return render_template(
'errors.html',
code=500,
title="Błąd serwera",
message="Wystąpił nieoczekiwany błąd. Spróbuj ponownie później."
), 500
@app.route('/favicon.ico')
def favicon_ico():
return redirect(url_for('static', filename='favicon.svg'))
@@ -366,6 +380,9 @@ def main_page():
return render_template("main.html", user_lists=user_lists, public_lists=public_lists, archived_lists=archived_lists)
from flask import request, redirect, url_for, flash, render_template, make_response
# ... inne importy ...
@app.route('/system-auth', methods=['GET', 'POST'])
def system_auth():
if current_user.is_authenticated or request.cookies.get('authorized') == AUTHORIZED_COOKIE_VALUE:
@@ -383,7 +400,8 @@ def system_auth():
if request.form['password'] == SYSTEM_PASSWORD:
reset_failed_attempts(ip)
resp = redirect(next_page)
resp.set_cookie('authorized', AUTHORIZED_COOKIE_VALUE)
max_age = app.config.get('AUTH_COOKIE_MAX_AGE', 86400)
resp.set_cookie('authorized', AUTHORIZED_COOKIE_VALUE, max_age=max_age)
return resp
else:
register_failed_attempt(ip)
@@ -456,10 +474,13 @@ def toggle_visibility(list_id):
return redirect(url_for('main_page'))
from sqlalchemy import func
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
user = User.query.filter_by(username=request.form['username']).first()
username_input = request.form['username'].lower()
user = User.query.filter(func.lower(User.username) == username_input).first()
if user and check_password_hash(user.password_hash, request.form['password']):
login_user(user)
flash('Zalogowano pomyślnie', 'success')
@@ -759,14 +780,14 @@ def delete_list(list_id):
@login_required
@admin_required
def add_user():
username = request.form['username']
username = request.form['username'].lower()
password = request.form['password']
if not username or not password:
flash('Wypełnij wszystkie pola', 'danger')
return redirect(url_for('list_users'))
if User.query.filter_by(username=username).first():
if User.query.filter(func.lower(User.username) == username).first():
flash('Użytkownik o takiej nazwie już istnieje', 'warning')
return redirect(url_for('list_users'))

View File

@@ -9,3 +9,4 @@ class Config:
DEFAULT_ADMIN_PASSWORD = os.environ.get('DEFAULT_ADMIN_PASSWORD', 'admin123')
UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER', 'uploads')
AUTHORIZED_COOKIE_VALUE = os.environ.get('AUTHORIZED_COOKIE_VALUE', 'cookievalue')
AUTH_COOKIE_MAX_AGE = int(os.environ.get('AUTH_COOKIE_MAX_AGE', 86400))

View File

@@ -13,5 +13,6 @@ services:
- DEFAULT_ADMIN_PASSWORD=${DEFAULT_ADMIN_PASSWORD}
- UPLOAD_FOLDER=${UPLOAD_FOLDER}
- AUTHORIZED_COOKIE_VALUE=${AUTHORIZED_COOKIE_VALUE}
- AUTH_COOKIE_MAX_AGE=${AUTH_COOKIE_MAX_AGE}
volumes:
- .:/app

View File

@@ -8,5 +8,5 @@ function showToast(message, type = 'primary') {
toast.innerHTML = `<div class="d-flex"><div class="toast-body">${message}</div></div>`;
toastContainer.appendChild(toast);
setTimeout(() => { toast.remove(); }, 4000);
setTimeout(() => { toast.remove(); }, 2000);
}

View File

@@ -5,10 +5,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Live Lista Zakupów{% endblock %}</title>
<link rel="icon" type="image/svg+xml" href="{{ url_for('favicon') }}">
{% if not is_blocked %}
<link href="{{ url_for('static_bp.serve_css', filename='style.css') }}" rel="stylesheet">
<link href="{{ url_for('static_bp.serve_css_lib', filename='bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static_bp.serve_css_lib', filename='glightbox.min.css') }}" rel="stylesheet">
{% endif %}
<link href="{{ url_for('static_bp.serve_css_lib', filename='bootstrap.min.css') }}" rel="stylesheet">
</head>
<body class="bg-dark text-white">
@@ -18,7 +19,7 @@
🛒 <span class="text-warning">Lista</span> Zakupów
</a>
{% if has_authorized_cookie %}
{% if has_authorized_cookie and not is_blocked %}
{% if current_user.is_authenticated %}
<div class="d-flex justify-content-center align-items-center text-white small flex-wrap text-center">
<span class="me-1">Zalogowany:</span>
@@ -32,9 +33,9 @@
{% endif %}
{% endif %}
{% if not is_blocked %}
<div class="d-flex align-items-center gap-2">
{% if request.endpoint != 'system_auth' %}
{% if request.endpoint and request.endpoint != 'system_auth' %}
{% if current_user.is_authenticated and current_user.is_admin %}
<a href="{{ url_for('admin_panel') }}" class="btn btn-outline-warning btn-sm">⚙️ Panel admina</a>
{% endif %}
@@ -45,6 +46,8 @@
{% endif %}
{% endif %}
</div>
{% endif %}
</div>
</nav>
@@ -54,6 +57,8 @@
<div id="toast-container" class="toast-container position-fixed bottom-0 end-0 p-3"></div>
<script src="{{ url_for('static_bp.serve_js_lib', filename='bootstrap.bundle.min.js') }}"></script>
{% if not is_blocked %}
<script>
document.addEventListener('DOMContentLoaded', function() {
{% with messages = get_flashed_messages(with_categories=true) %}
@@ -69,9 +74,8 @@
});
</script>
<script src="{{ url_for('static_bp.serve_js_lib', filename='glightbox.min.js') }}"></script>
<script src="{{ url_for('static_bp.serve_js_lib', filename='bootstrap.bundle.min.js') }}"></script>
<script src="{{ url_for('static_bp.serve_js_lib', filename='socket.io.min.js') }}"></script>
{% if request.endpoint != 'system_auth' %}
{% if request.endpoint != 'system_auth' %}
<script src="{{ url_for('static_bp.serve_js', filename='functions.js') }}"></script>
<script src="{{ url_for('static_bp.serve_js', filename='live.js') }}"></script>
<script src="{{ url_for('static_bp.serve_js', filename='sockets.js') }}"></script>
@@ -82,6 +86,7 @@
selector: '.glightbox'
});
</script>
{% endif %}
{% block scripts %}{% endblock %}

View File

@@ -4,7 +4,6 @@
<div class="d-flex justify-content-between align-items-center flex-wrap mb-4">
<h2 class="mb-2">{{ code }} — {{ title }}</h2>
<a href="{{ url_for('main_page') }}" class="btn btn-outline-secondary">← Powrót na stronę główną</a>
</div>
<div class="card bg-dark text-white">

View File

@@ -19,4 +19,4 @@
</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -25,4 +25,4 @@ document.addEventListener('DOMContentLoaded', function() {
</script>
{% endblock %}
{% endblock %}
{% endblock %}