zmiany ux i kodowe

This commit is contained in:
Mateusz Gruszczyński
2025-09-23 10:25:11 +02:00
parent 1a423a8b92
commit bbe318f995
12 changed files with 205 additions and 71 deletions

114
app.py
View File

@@ -1,3 +1,7 @@
import markdown as md
import hashlib, os
import re
import socket
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from flask_login import (
@@ -11,29 +15,42 @@ from flask_login import (
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime, timezone
from markupsafe import Markup
from sqlalchemy import event, Numeric
from sqlalchemy import event, Numeric, select
from sqlalchemy.engine import Engine
from decimal import Decimal, InvalidOperation
import markdown as md
from flask import request, flash, abort
import os
import re
import socket
try:
from zoneinfo import ZoneInfo # Python 3.9+
except ImportError:
from backports.zoneinfo import ZoneInfo
def build_fingerprint(paths):
h = hashlib.sha256()
for base in paths:
if not os.path.exists(base):
continue
for root, _, files in os.walk(base):
for f in sorted(files):
p = os.path.join(root, f)
try:
with open(p, "rb") as fh:
h.update(fh.read())
except Exception:
continue
return h.hexdigest()[:8]
APP_VERSION = f"{datetime.now():%Y.%m.%d}+{build_fingerprint(['templates','static','app.py'])}"
app = Flask(__name__)
app.config['APP_VERSION'] = APP_VERSION
# Ładujemy konfigurację z pliku config.py
app.config.from_object("config.Config")
db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = "zaloguj"
try:
from zoneinfo import ZoneInfo # Python 3.9+
except ImportError:
from backports.zoneinfo import ZoneInfo
LOCAL_TZ = ZoneInfo("Europe/Warsaw")
@@ -143,7 +160,7 @@ class GlobalSettings(db.Model):
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
return db.session.get(User, int(user_id))
@event.listens_for(Engine, "connect")
@@ -235,6 +252,11 @@ def inject_globals():
}
@app.context_processor
def inject_version():
return {'APP_VERSION': app.config['APP_VERSION']}
# TRASY PUBLICZNE
@app.route("/")
def index():
@@ -255,7 +277,9 @@ def page_not_found(e):
@app.route("/zbiorka/<int:zbiorka_id>")
def zbiorka(zbiorka_id):
zb = Zbiorka.query.get_or_404(zbiorka_id)
zb = db.session.get(Zbiorka, zbiorka_id)
if zb is None:
abort(404)
if zb.ukryta and (not current_user.is_authenticated or not current_user.is_admin):
abort(404)
@@ -364,7 +388,9 @@ def formularz_zbiorek(zbiorka_id=None):
# Tryb
is_edit = zbiorka_id is not None
zb = Zbiorka.query.get_or_404(zbiorka_id) if is_edit else None
zb = db.session.get(Zbiorka, zbiorka_id)
if zb is None:
abort(404)
global_settings = GlobalSettings.query.first()
if request.method == "POST":
@@ -498,7 +524,9 @@ def dodaj_wplate(zbiorka_id):
flash("Brak uprawnień", "danger")
return redirect(url_for("index"))
zb = Zbiorka.query.get_or_404(zbiorka_id)
zb = db.session.get(Zbiorka, zbiorka_id) if is_edit else None
if is_edit and not zb:
abort(404)
if request.method == "POST":
try:
@@ -527,7 +555,9 @@ def usun_zbiorka(zbiorka_id):
if not current_user.is_admin:
flash("Brak uprawnień", "danger")
return redirect(url_for("index"))
zb = Zbiorka.query.get_or_404(zbiorka_id)
zb = db.session.get(Zbiorka, zbiorka_id)
if zb is None:
abort(404)
db.session.delete(zb)
db.session.commit()
flash("Zbiórka została usunięta", "success")
@@ -540,7 +570,9 @@ def edytuj_stan(zbiorka_id):
if not current_user.is_admin:
flash("Brak uprawnień", "danger")
return redirect(url_for("index"))
zb = Zbiorka.query.get_or_404(zbiorka_id)
zb = db.session.get(Zbiorka, zbiorka_id)
if zb is None:
abort(404)
if request.method == "POST":
try:
nowy_stan = Decimal(request.form.get("stan", "").replace(",", "."))
@@ -560,7 +592,9 @@ def zmien_widzialnosc(zbiorka_id):
if not current_user.is_admin:
flash("Brak uprawnień", "danger")
return redirect(url_for("index"))
zb = Zbiorka.query.get_or_404(zbiorka_id)
zb = db.session.get(Zbiorka, zbiorka_id)
if zb is None:
abort(404)
zb.ukryta = not zb.ukryta
db.session.commit()
flash("Zbiórka została " + ("ukryta" if zb.ukryta else "przywrócona"), "success")
@@ -579,7 +613,6 @@ def create_admin_account():
@app.after_request
def apply_headers(response):
# --- STATIC: jak wcześniej ---
if request.path.startswith("/static/"):
response.headers.pop("Content-Disposition", None)
response.headers["Vary"] = "Accept-Encoding"
@@ -616,7 +649,7 @@ def apply_headers(response):
response.headers.pop("Vary", None)
else:
response.headers["Vary"] = "Cookie, Accept-Encoding"
default_cache = app.config.get("CACHE_CONTROL_HEADER") or "private, max-age=0"
default_cache = app.config.get("CACHE_CONTROL_HEADER") or "private, no-store"
response.headers["Cache-Control"] = default_cache
if (
@@ -694,7 +727,9 @@ def dodaj_wydatek(zbiorka_id):
flash("Brak uprawnień", "danger")
return redirect(url_for("index"))
zb = Zbiorka.query.get_or_404(zbiorka_id)
zb = db.session.get(Zbiorka, zbiorka_id)
if zb is None:
abort(404)
if request.method == "POST":
try:
@@ -734,7 +769,9 @@ def oznacz_zbiorka(zbiorka_id):
flash("Brak uprawnień do wykonania tej operacji", "danger")
return redirect(url_for("index"))
zb = Zbiorka.query.get_or_404(zbiorka_id)
zb = db.session.get(Zbiorka, zbiorka_id)
if zb is None:
abort(404)
if "niezrealizowana" in request.path:
zb.zrealizowana = False
@@ -762,7 +799,9 @@ def robots():
def transakcje_zbiorki(zbiorka_id):
if not current_user.is_admin:
flash("Brak uprawnień", "danger"); return redirect(url_for("index"))
zb = Zbiorka.query.get_or_404(zbiorka_id)
zb = db.session.get(Zbiorka, zbiorka_id)
if zb is None:
abort(404)
aktywnosci = (
[{"typ": "wpłata", "id": w.id, "kwota": w.kwota, "opis": w.opis, "data": w.data} for w in zb.wplaty] +
[{"typ": "wydatek","id": x.id, "kwota": x.kwota,"opis": x.opis,"data": x.data} for x in zb.wydatki]
@@ -776,7 +815,9 @@ def transakcje_zbiorki(zbiorka_id):
def zapisz_wplate(wplata_id):
if not current_user.is_admin:
flash("Brak uprawnień", "danger"); return redirect(url_for("index"))
w = Wplata.query.get_or_404(wplata_id)
w = db.session.get(Wplata, wplata_id)
if w is None:
abort(404)
zb = w.zbiorka
try:
nowa_kwota = Decimal(request.form.get("kwota", "").replace(",", "."))
@@ -800,7 +841,9 @@ def zapisz_wplate(wplata_id):
def usun_wplate(wplata_id):
if not current_user.is_admin:
flash("Brak uprawnień", "danger"); return redirect(url_for("index"))
w = Wplata.query.get_or_404(wplata_id)
w = db.session.get(Wplata, wplata_id)
if w is None:
abort(404)
zb = w.zbiorka
zb.stan -= w.kwota
db.session.delete(w)
@@ -814,7 +857,9 @@ def usun_wplate(wplata_id):
def zapisz_wydatek(wydatek_id):
if not current_user.is_admin:
flash("Brak uprawnień", "danger"); return redirect(url_for("index"))
x = Wydatek.query.get_or_404(wydatek_id)
x = db.session.get(Wydatek, wydatek_id)
if x is None:
abort(404)
zb = x.zbiorka
try:
nowa_kwota = Decimal(request.form.get("kwota", "").replace(",", "."))
@@ -839,7 +884,9 @@ def zapisz_wydatek(wydatek_id):
def usun_wydatek(wydatek_id):
if not current_user.is_admin:
flash("Brak uprawnień", "danger"); return redirect(url_for("index"))
x = Wydatek.query.get_or_404(wydatek_id)
x = db.session.get(Wydatek, wydatek_id)
if x is None:
abort(404)
zb = x.zbiorka
zb.stan += x.kwota
db.session.delete(x)
@@ -867,9 +914,16 @@ if __name__ == "__main__":
with app.app_context():
db.create_all()
# Tworzenie konta głównego admina, jeśli nie istnieje
if not User.query.filter_by(is_admin=True).first():
main_admin = User(username=app.config["MAIN_ADMIN_USERNAME"], is_admin=True)
stmt = select(User).filter_by(is_admin=True)
admin = db.session.execute(stmt).scalars().first()
if not admin:
main_admin = User(
username=app.config["MAIN_ADMIN_USERNAME"],
is_admin=True
)
main_admin.set_password(app.config["MAIN_ADMIN_PASSWORD"])
db.session.add(main_admin)
db.session.commit()
app.run(debug=True)