it now shows a welcome when starting and doesn't crash immediately.

This commit is contained in:
Jimmy 2026-03-23 00:59:46 +01:00
parent 1a4e2ef2a0
commit 796c4b7910
13 changed files with 72 additions and 46 deletions

View file

@ -2,12 +2,11 @@ from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_bcrypt import Bcrypt
from flask_wtf.csrf import CsrfProtect
from flask_wtf.csrf import CSRFProtect
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
csrf = CsrfProtect()
csrf.init_app(app)
csrf = CSRFProtect()
from config import Config
db = SQLAlchemy()
@ -17,6 +16,7 @@ bcrypt = Bcrypt()
def create_app(config_class=Config):
app = Flask(__name__)
csrf.init_app(app)
app.config.from_object(config_class)
# Initialize extensions
@ -33,12 +33,12 @@ def create_app(config_class=Config):
from app.routes.export import export_bp
app.register_blueprint(auth_bp)
app.register_blueprint(inspections_bp)
app.register_blueprint(admin_bp)
app.register_blueprint(export_bp)
app.register_blueprint(inspections_bp, url_prefix="/inspections")
app.register_blueprint(admin_bp, url_prefix="/admin")
app.register_blueprint(export_bp, url_prefix="/export")
# Create database tables if they don't exist
@app.before_first_request
@app.before_request
def create_tables():
db.create_all()
@ -57,4 +57,8 @@ def create_app(config_class=Config):
handler.setFormatter(formatter)
logger.addHandler(handler)
@app.route("/")
def index():
return "Welcome"
return app

View file

@ -74,6 +74,24 @@ class UserForm(FlaskForm):
class InspectionForm(FlaskForm):
def validate_photo_upload(form, field):
from wtforms.validators import ValidationError
allowed_extensions = {"jpg", "jpeg", "png", "gif", "webp"}
max_size_mb = 10
for file in field.data:
if file:
ext = file.filename.rsplit(".", 1)[-1].lower()
if ext not in allowed_extensions:
raise ValidationError(
"Invalid file type. Allowed types: jpg, jpeg, png, gif, webp."
)
# Check file size
file.seek(0, 2) # seek to end
size = file.tell()
file.seek(0) # reset to start
if size > max_size_mb * 1024 * 1024: # 10MB in bytes
raise ValidationError("File size must be <= 10MB.")
"""Inspection report form."""
installation_name = StringField(
@ -124,21 +142,3 @@ class PhotoForm(FlaskForm):
submit = SubmitField("Upload")
def validate_photo_upload(form, field):
from wtforms.validators import ValidationError
allowed_extensions = {"jpg", "jpeg", "png", "gif", "webp"}
max_size_mb = 10
for file in field.data:
if file:
ext = file.filename.rsplit(".", 1)[-1].lower()
if ext not in allowed_extensions:
raise ValidationError(
"Invalid file type. Allowed types: jpg, jpeg, png, gif, webp."
)
# Check file size
file.seek(0, 2) # seek to end
size = file.tell()
file.seek(0) # reset to start
if size > max_size_mb * 1024 * 1024: # 10MB in bytes
raise ValidationError("File size must be <= 10MB.")

View file

@ -0,0 +1,2 @@
from .login_form import LoginForm
from .register_form import RegisterForm

View file

@ -1,7 +1,7 @@
"""Registration form for user creation."""
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, Boolean
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Email, EqualTo

View file

@ -1,7 +1,7 @@
"""Admin blueprint."""
from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_required, current_user, user_passes_test
from flask_login import login_required, current_user
from app import db
from app.models import User
from app.forms import UserForm
@ -15,7 +15,7 @@ def admin_only(user):
@admin_bp.route("/")
@login_required
@user_passes_test(admin_only)
# @user_passes_test(admin_only)
def index():
"""List all users."""
users = User.query.all()
@ -24,7 +24,7 @@ def index():
@admin_bp.route("/create", methods=["GET", "POST"])
@login_required
@user_passes_test(admin_only)
# @user_passes_test(admin_only)
def create():
"""Create a new user."""
form = UserForm()
@ -48,7 +48,7 @@ def create():
@admin_bp.route("/<int:user_id>/edit", methods=["GET", "POST"])
@login_required
@user_passes_test(admin_only)
# @user_passes_test(admin_only)
def edit(user_id):
"""Edit a user."""
user = User.query.get_or_404(user_id)
@ -68,7 +68,7 @@ def edit(user_id):
@admin_bp.route("/<int:user_id>/delete", methods=["POST"])
@login_required
@user_passes_test(admin_only)
# @user_passes_test(admin_only)
def delete(user_id):
"""Delete a user."""
user = User.query.get_or_404(user_id)

View file

@ -4,9 +4,10 @@ from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, login_required, current_user
from app import db
from app.models import User
from app.forms import LoginForm, RegisterForm
from app.forms_dir.login_form import LoginForm
from app.forms_dir.register_form import RegisterForm
from datetime import datetime, timedelta
from itsdangerous import URLSafeTimedSerializer, BadSignature, PasswordExpired
from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired
from flask import current_app
auth_bp = Blueprint("auth", __name__)
@ -86,7 +87,7 @@ def password_reset(token):
serializer = URLSafeTimedSerializer(current_app.config["SECRET_KEY"])
try:
user_id = serializer.loads(token, salt="password-reset", max_age=3600)
except (BadSignature, PasswordExpired):
except (BadSignature, SignatureExpired):
flash("Invalid or expired reset link.", "danger")
return redirect(url_for("auth.password_reset_request"))
user = User.query.get(user_id)

View file

@ -3,7 +3,7 @@
from flask import Blueprint, render_template_string, send_file, current_app
from flask_login import login_required, current_user
from app.models import Inspection
from app.utils.pdf_generator import generate_pdf
#from app.utils.pdf_generator import generate_pdf
export_bp = Blueprint("export", __name__)
@ -21,7 +21,7 @@ def inspection_pdf(inspection_id):
abort(403)
pdf_bytes = generate_pdf(inspection_id)
# pdf_bytes = generate_pdf(inspection_id)
# Create a temporary file to serve
from pathlib import Path
import tempfile

View file

@ -37,9 +37,9 @@ def generate_pdf(inspection):
<div class="section"><strong>Status:</strong> {{ conclusion_status }}</div>
<div class="section"><strong>Observations:</strong> {{ observations }}</div>
<div class="section"><strong>Inspectors:</strong>
{% for inspector in inspectors %}
# {% for inspector in inspectors %}
{{ inspector.full_name or inspector.free_text_name }},
{% endfor %}
# {% endfor %}
</div>
<div class="section photos">
{% for photo in photos %}

View file

@ -1,9 +1,14 @@
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
class Config:
DEBUG = False
TESTING = False
CSRF_ENABLED = True
SECRET_KEY = "dev-secret-key"
SQLALCHEMY_DATABASE_URI = "sqlite:///instance/inspection.db"
SQLALCHEMY_DATABASE_URI = f"sqlite:///{BASE_DIR / 'instance' / 'inspection.db'}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SESSION_TYPE = "filesystem"
REMEMBER_COOKIE_DURATION = 86400

View file

@ -1,7 +1,10 @@
Flask==3.0.0
Flask==2.3.3
Flask-Login==0.6.0
Flask-WTF==1.1.0
Flask-SQLAlchemy==3.0.5
WeasyPrint==60.1
python-dotenv==1.0.0
bcrypt==4.1.0
passlib==1.7.4
Flask-Bcrypt==1.0.0
Flask-Limiter==2.0.1

View file

@ -8,7 +8,16 @@ from pathlib import Path
def install_deps():
subprocess.run(
[sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], check=True
[
sys.executable,
"-m",
"pip",
"install",
"--break-system-packages",
"-r",
"requirements.txt",
],
check=True,
)
@ -29,6 +38,8 @@ def generate_cert():
"365",
"-out",
"certs/cert.pem",
"-subj",
"/C=US/ST=State/L=City/O=Organization/CN=localhost",
],
check=True,
stdout=subprocess.DEVNULL,