From 08f7f0e89051f548fa1f42e936d83517de8fa192 Mon Sep 17 00:00:00 2001 From: James Devine Date: Wed, 11 Mar 2026 11:15:22 +0100 Subject: [PATCH] Fix PDF export, inspection edit, photo upload issues and add user management access --- app/__init__.py | 39 +++++++++++++- app/models.py | 17 +++++- app/routes/admin.py | 85 ++++++++++++++++++++++++++++-- app/routes/auth.py | 3 +- app/routes/export.py | 1 + app/routes/inspections.py | 89 ++++++++++++++++++++------------ app/static/favicon.ico | Bin 0 -> 1095 bytes app/static/favicon.png | Bin 0 -> 1095 bytes app/templates/admin/logo.html | 43 +++++++++++++++ app/templates/errors/404.html | 10 ++++ app/templates/errors/500.html | 10 ++++ app/templates/pdf_template.html | 2 +- config.py | 5 +- instance/app.db | Bin 0 -> 36864 bytes run.py | 18 ++----- setup.py | 31 +++-------- 16 files changed, 269 insertions(+), 84 deletions(-) create mode 100644 app/static/favicon.ico create mode 100644 app/static/favicon.png create mode 100644 app/templates/admin/logo.html create mode 100644 app/templates/errors/404.html create mode 100644 app/templates/errors/500.html create mode 100644 instance/app.db diff --git a/app/__init__.py b/app/__init__.py index cd128c3..fc9890f 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,14 +1,34 @@ -from flask import Flask +from flask import Flask, render_template from flask_login import LoginManager from flask_wtf.csrf import CSRFProtect from config import Config -from app.models import db +from app.models import db, Config as ConfigModel, User import os +from datetime import datetime login_manager = LoginManager() login_manager.login_view = 'auth.login' login_manager.login_message = 'Please log in to access this page.' +@login_manager.user_loader +def load_user(user_id): + """Load user for Flask-Login""" + return User.query.get(int(user_id)) + +def get_logo_filename(): + """Get the logo filename from database configuration""" + try: + logo_config = ConfigModel.query.filter_by(key='logo_filename').first() + return logo_config.value if logo_config else None + except: + return None + +def format_date(value, format='%Y'): + """Format date for Jinja2 templates""" + if value: + return value.strftime(format) + return '' + def create_app(config_class=Config): app = Flask(__name__) app.config.from_object(config_class) @@ -35,6 +55,21 @@ def create_app(config_class=Config): app.register_blueprint(admin_bp) app.register_blueprint(export_bp) + # Add logo filename to template context + @app.context_processor + def inject_logo(): + return dict(logo_filename=get_logo_filename()) + + # Add custom filters + @app.template_filter('format_date') + def format_date_filter(value, format='%Y'): + return format_date(value, format) + + # Add current date function + @app.context_processor + def inject_current_date(): + return dict(moment=lambda: datetime.now().strftime('%Y-%m-%d %H:%M')) + # Error handlers @app.errorhandler(404) def not_found_error(error): diff --git a/app/models.py b/app/models.py index 0d1e5dc..5377269 100644 --- a/app/models.py +++ b/app/models.py @@ -1,17 +1,17 @@ from flask_sqlalchemy import SQLAlchemy from datetime import datetime from werkzeug.security import generate_password_hash, check_password_hash +from flask_login import UserMixin db = SQLAlchemy() -class User(db.Model): +class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) full_name = db.Column(db.String(120), nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) password_hash = db.Column(db.String(120), nullable=False) is_admin = db.Column(db.Boolean, default=False) - is_active = db.Column(db.Boolean, default=True) created_at = db.Column(db.DateTime, default=datetime.utcnow) def set_password(self, password): @@ -20,9 +20,22 @@ class User(db.Model): def check_password(self, password): return check_password_hash(self.password_hash, password) + def get_id(self): + """Required by Flask-Login""" + return str(self.id) + def __repr__(self): return f'' +class Config(db.Model): + id = db.Column(db.Integer, primary_key=True) + key = db.Column(db.String(100), unique=True, nullable=False) + value = db.Column(db.Text, nullable=True) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + + def __repr__(self): + return f'' + class Inspection(db.Model): id = db.Column(db.Integer, primary_key=True) installation_name = db.Column(db.String(200), nullable=False) diff --git a/app/routes/admin.py b/app/routes/admin.py index 00330a4..d69deba 100644 --- a/app/routes/admin.py +++ b/app/routes/admin.py @@ -1,10 +1,11 @@ from flask import Blueprint, render_template, request, redirect, url_for, flash from flask_login import login_required, current_user -from app.models import User, db +from app.models import User, db, Config from werkzeug.security import generate_password_hash from flask_wtf import FlaskForm -from wtforms import StringField, EmailField, PasswordField, BooleanField, SubmitField -from wtforms.validators import DataRequired, Email, Length, EqualTo +from wtforms import StringField, EmailField, PasswordField, BooleanField, SubmitField, FileField +from wtforms.validators import DataRequired, Length, EqualTo +import os admin_bp = Blueprint('admin', __name__) @@ -18,15 +19,24 @@ def admin_required(f): wrapper.__name__ = f.__name__ return wrapper +def get_logo_filename(): + """Get the logo filename from configuration""" + logo_config = Config.query.filter_by(key='logo_filename').first() + return logo_config.value if logo_config else None + class UserForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(min=4, max=80)]) full_name = StringField('Full Name', validators=[DataRequired(), Length(min=2, max=120)]) - email = EmailField('Email', validators=[DataRequired(), Email()]) + email = EmailField('Email', validators=[DataRequired()]) password = PasswordField('Password', validators=[Length(min=6)]) is_admin = BooleanField('Administrator') is_active = BooleanField('Active') submit = SubmitField('Save') +class LogoForm(FlaskForm): + logo = FileField('Logo', validators=[]) + submit = SubmitField('Save Logo') + @admin_bp.route('/admin/users') @login_required @admin_required @@ -34,6 +44,73 @@ def users(): users = User.query.all() return render_template('admin/users.html', users=users) +@admin_bp.route('/admin/user//delete', methods=['POST']) +@login_required +@admin_required +def user_delete(user_id): + user = User.query.get_or_404(user_id) + + # Prevent deleting the last admin user + if user.is_admin: + admin_count = User.query.filter_by(is_admin=True).count() + if admin_count <= 1: + flash('Cannot delete the last administrator user.', 'error') + return redirect(url_for('admin.users')) + + # Prevent deleting self + if user.id == current_user.id: + flash('You cannot delete yourself.', 'error') + return redirect(url_for('admin.users')) + + db.session.delete(user) + db.session.commit() + flash('User deleted successfully.', 'success') + return redirect(url_for('admin.users')) + +@admin_bp.route('/admin/logo', methods=['GET', 'POST']) +@login_required +@admin_required +def logo(): + form = LogoForm() + logo_filename = get_logo_filename() + + if form.validate_on_submit(): + if form.logo.data: + # Save the uploaded logo + filename = form.logo.data.filename + if filename and '.' in filename: + # Only allow jpeg and png files + ext = filename.rsplit('.', 1)[1].lower() + if ext in ['jpeg', 'jpg', 'png']: + # Create uploads directory if it doesn't exist + upload_dir = 'uploads' + os.makedirs(upload_dir, exist_ok=True) + + # Save file with a unique name + logo_filename = f"logo.{ext}" + file_path = os.path.join(upload_dir, logo_filename) + form.logo.data.save(file_path) + + # Save filename to config + logo_config = Config.query.filter_by(key='logo_filename').first() + if logo_config: + logo_config.value = logo_filename + else: + logo_config = Config(key='logo_filename', value=logo_filename) + db.session.add(logo_config) + + db.session.commit() + flash('Logo uploaded successfully.', 'success') + return redirect(url_for('admin.logo')) + else: + flash('Invalid file type. Only JPEG and PNG files are allowed.', 'error') + else: + flash('Invalid filename.', 'error') + else: + flash('No file selected.', 'error') + + return render_template('admin/logo.html', form=form, logo_filename=logo_filename) + @admin_bp.route('/admin/user/new', methods=['GET', 'POST']) @login_required @admin_required diff --git a/app/routes/auth.py b/app/routes/auth.py index 5dfa844..337134f 100644 --- a/app/routes/auth.py +++ b/app/routes/auth.py @@ -1,8 +1,7 @@ from flask import Blueprint, render_template, request, redirect, url_for, flash from flask_login import login_user, logout_user, login_required, current_user from app.models import User, db -from app.utils.security import generate_password_hash -from werkzeug.security import check_password_hash +from werkzeug.security import generate_password_hash, check_password_hash from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import DataRequired, Email, Length diff --git a/app/routes/export.py b/app/routes/export.py index d873cf6..df3fcdd 100644 --- a/app/routes/export.py +++ b/app/routes/export.py @@ -4,6 +4,7 @@ from app.models import Inspection, InspectionInspector, Photo, User, db from weasyprint import HTML import os from datetime import datetime +import io export_bp = Blueprint('export', __name__) diff --git a/app/routes/inspections.py b/app/routes/inspections.py index 50e0d42..c24178b 100644 --- a/app/routes/inspections.py +++ b/app/routes/inspections.py @@ -5,41 +5,39 @@ from werkzeug.utils import secure_filename import os from datetime import datetime from flask_wtf import FlaskForm -from wtforms import StringField, TextAreaField, DateField, IntegerField, SelectField, SubmitField, FieldList, FormField +from wtforms import StringField, TextAreaField, DateField, IntegerField, SelectField, SubmitField, FieldList from wtforms.validators import DataRequired, Length, Optional inspections_bp = Blueprint('inspections', __name__) -# Form for adding photos -class PhotoForm(FlaskForm): - caption = StringField('Caption', validators=[Optional(), Length(max=200)]) - action_required = SelectField('Action Required', choices=[ - ('none', 'No action required'), - ('urgent', 'Urgent action required'), - ('before_next', 'Action required before next inspection') - ], validators=[DataRequired()]) - file = StringField('File', validators=[DataRequired()]) - -# Form for inspection class InspectionForm(FlaskForm): + # Basic inspection information installation_name = StringField('Installation Name', validators=[DataRequired(), Length(max=200)]) location = StringField('Location', validators=[DataRequired(), Length(max=200)]) - inspection_date = DateField('Date of Inspection', validators=[DataRequired()]) + inspection_date = DateField('Inspection Date', validators=[DataRequired()]) reference_number = IntegerField('Reference Number', validators=[DataRequired()]) + + # Observations observations = TextAreaField('Observations') - conclusion_text = TextAreaField('Conclusion Comments') - conclusion_status = SelectField('Conclusion Status', choices=[ - ('ok', 'OK for operation in current state'), - ('minor', 'Minor comments — Remedial actions required for continued operation'), - ('major', 'Major comments — Operation suspended until resolution and satisfactory follow-up inspection') - ], validators=[DataRequired()]) - inspectors = FieldList(StringField('Inspector', validators=[Optional(), Length(max=120)]), min_entries=1) - photos = FieldList(FormField(PhotoForm), min_entries=0) - submit = SubmitField('Complete Report') - update = SubmitField('Update Report') - cancel = SubmitField('Cancel') + + # Inspectors (multiple fields) + inspectors = FieldList(StringField('Inspector'), min_entries=1) + + # Conclusion + conclusion_text = TextAreaField('Conclusion Text') + conclusion_status = SelectField('Conclusion Status', + choices=[('ok', 'OK'), ('minor', 'Minor Issue'), ('major', 'Major Issue')], + validators=[DataRequired()]) + + # Submit button + submit = SubmitField('Save Inspection') + update = SubmitField('Update Inspection') @inspections_bp.route('/') +def index(): + return redirect(url_for('auth.login')) + +@inspections_bp.route('/dashboard') @login_required def dashboard(): # Get all inspections for the current user @@ -111,13 +109,21 @@ def inspection_edit(id): # Pre-fill inspectors with existing inspectors if inspection.inspectors: - form.inspectors.process_data([inspector.free_text_name or inspector.user.full_name - for inspector in inspection.inspectors if inspector.free_text_name or inspector.user]) - - # Pre-fill photos - if inspection.photos: - for photo in inspection.photos: - form.photos.append_entry(photo) + inspector_names = [] + for inspector in inspection.inspectors: + if inspector.free_text_name: + inspector_names.append(inspector.free_text_name) + elif inspector.user: + inspector_names.append(inspector.user.full_name) + # Fill the first inspector field + if inspector_names: + form.inspectors[0].data = inspector_names[0] + # Add additional fields if needed + while len(form.inspectors) < len(inspector_names): + form.inspectors.append_entry() + # Fill remaining fields + for i, name in enumerate(inspector_names[1:], 1): + form.inspectors[i].data = name if form.validate_on_submit(): # Update inspection @@ -179,14 +185,29 @@ def upload_photo(): if file.filename == '': return jsonify({'error': 'No file selected'}), 400 - if file: + if file and file.filename and allowed_file(file.filename): filename = secure_filename(file.filename) if filename: # Generate unique filename import uuid unique_filename = f"{uuid.uuid4().hex}_{filename}" - file_path = os.path.join('uploads', unique_filename) + # Create uploads directory if it doesn't exist + upload_dir = 'uploads' + os.makedirs(upload_dir, exist_ok=True) + file_path = os.path.join(upload_dir, unique_filename) file.save(file_path) return jsonify({'filename': unique_filename, 'original_filename': filename}) - return jsonify({'error': 'Upload failed'}), 500 \ No newline at end of file + return jsonify({'error': 'Upload failed'}), 500 + +def allowed_file(filename): + """Check if file extension is allowed""" + ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} + if not filename: + return False + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +def allowed_file(filename): + """Check if file extension is allowed""" + ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS \ No newline at end of file diff --git a/app/static/favicon.ico b/app/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7cbc81d59718c385094ff15659e3eb6d6a583aba GIT binary patch literal 1095 zcmV-N1i1T&P)BbojR=JnP1DpMaRL8vqaYGDq(uKnR7$;~XqqAlsg>nLDYINqNf9*_uM1cz z=1M7shzW{%bGg{2rD1L%EvUIY&viz=b2!I)mhQmAeBXCwd1vN*fA5?c4-{4&qgyxl z3jQl>h`YUN6xewh{f@(WxK`j81sm`nJ$J$HFarL9)-VJv!KZK|SEF*-frbn=686IO zFmP|`Ct*o=-$VTww15M!1)4z#tc4TV49dlT59z!NHb56RA67h&dQup8C-vLV3N+?4 zbc8ba7?$R1fSnJ5TUi(8fIH9*+>P4sCd>^3zY%`>je1u2rorRjSGbjr0b^+VPq6+v z^%JlTeuCBD`BzmuiuYkqV)7L{>oZrAoiiOlDGi>5ns5Qehv%FfD@^bj^$yTUUjcXZ z18_&LL?g2KfRp!x4q+u#t@wVwdt4r$j%l>$$kBQ4s{_x_@Jul?)At2<2h@x;a5#Ou zZ@P$KHK3{swf?6a)Oh(D@{v z(F$m5$f_+(-I;gfn>FYpE2gjb<2 zL<2l(jhGq@P%@2sJ4XZ5_kQpv7{?l5>OBU^t*dsMH-$Qr0Zyv&nF~r12CU*)*`5>) zFqJx3`!oYgI2J8;Vr5!E$y72KknIRIGJv^eS~S4bde$5FLSq$jP z_vb)|Q*KK^fAGen1na0LL*Jv+X34T>Ks{bALR)a9)mdERAE3ilB$aSCAIx+*T6LCT zVP_P)6d`(@cdt{;Ath!0kJhLwhO|yNBi(>nd@vlhf|rQuDBdlM4DS_{DLGR*HVYb- zHc*y}-3?dm!mK}slxuvt0fvoXrqDbB{b6E}Tj5y7yh^E{mBYO&TDk$Q{#{TLOd2iW zJ*W?hL5bCga>rzl7e4ZZKR93lM%M~TX#mx3Ms2Lh$W>cILE(bTG#ti8(_5!!s zENMQ-SaLDI8`4bpJ~4uS$&}@@;hSmPt@gix#UpeFOgIY9!eR{Y4q!%17FXy^Hx@6k zWBbojR=JnP1DpMaRL8vqaYGDq(uKnR7$;~XqqAlsg>nLDYINqNf9*_uM1cz z=1M7shzW{%bGg{2rD1L%EvUIY&viz=b2!I)mhQmAeBXCwd1vN*fA5?c4-{4&qgyxl z3jQl>h`YUN6xewh{f@(WxK`j81sm`nJ$J$HFarL9)-VJv!KZK|SEF*-frbn=686IO zFmP|`Ct*o=-$VTww15M!1)4z#tc4TV49dlT59z!NHb56RA67h&dQup8C-vLV3N+?4 zbc8ba7?$R1fSnJ5TUi(8fIH9*+>P4sCd>^3zY%`>je1u2rorRjSGbjr0b^+VPq6+v z^%JlTeuCBD`BzmuiuYkqV)7L{>oZrAoiiOlDGi>5ns5Qehv%FfD@^bj^$yTUUjcXZ z18_&LL?g2KfRp!x4q+u#t@wVwdt4r$j%l>$$kBQ4s{_x_@Jul?)At2<2h@x;a5#Ou zZ@P$KHK3{swf?6a)Oh(D@{v z(F$m5$f_+(-I;gfn>FYpE2gjb<2 zL<2l(jhGq@P%@2sJ4XZ5_kQpv7{?l5>OBU^t*dsMH-$Qr0Zyv&nF~r12CU*)*`5>) zFqJx3`!oYgI2J8;Vr5!E$y72KknIRIGJv^eS~S4bde$5FLSq$jP z_vb)|Q*KK^fAGen1na0LL*Jv+X34T>Ks{bALR)a9)mdERAE3ilB$aSCAIx+*T6LCT zVP_P)6d`(@cdt{;Ath!0kJhLwhO|yNBi(>nd@vlhf|rQuDBdlM4DS_{DLGR*HVYb- zHc*y}-3?dm!mK}slxuvt0fvoXrqDbB{b6E}Tj5y7yh^E{mBYO&TDk$Q{#{TLOd2iW zJ*W?hL5bCga>rzl7e4ZZKR93lM%M~TX#mx3Ms2Lh$W>cILE(bTG#ti8(_5!!s zENMQ-SaLDI8`4bpJ~4uS$&}@@;hSmPt@gix#UpeFOgIY9!eR{Y4q!%17FXy^Hx@6k zW +

Logo Configuration

+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} + +
+
+
+ {{ form.hidden_tag() }} +
+ {{ form.logo.label(class="form-label") }} + {{ form.logo(class="form-control") }} + Upload a JPEG or PNG logo (max 10MB) +
+ {{ form.submit(class="btn btn-primary") }} +
+
+ +
+

Current Logo

+ {% if logo_filename %} + Current Logo +

Current logo: {{ logo_filename }}

+ {% else %} +

No logo uploaded yet.

+ {% endif %} +
+
+ +{% endblock %} \ No newline at end of file diff --git a/app/templates/errors/404.html b/app/templates/errors/404.html new file mode 100644 index 0000000..c532175 --- /dev/null +++ b/app/templates/errors/404.html @@ -0,0 +1,10 @@ + + + + Page Not Found + + +

404 - Page Not Found

+

The page you are looking for does not exist.

+ + \ No newline at end of file diff --git a/app/templates/errors/500.html b/app/templates/errors/500.html new file mode 100644 index 0000000..101c6e0 --- /dev/null +++ b/app/templates/errors/500.html @@ -0,0 +1,10 @@ + + + + Server Error + + +

Server Error (500)

+

Something went wrong on our end. Please try again later.

+ + \ No newline at end of file diff --git a/app/templates/pdf_template.html b/app/templates/pdf_template.html index 920ab12..8b04f07 100644 --- a/app/templates/pdf_template.html +++ b/app/templates/pdf_template.html @@ -101,7 +101,7 @@

Inspection Report

-

Reference: {{ inspection.reference_number }} | Version: {{ inspection.version }} | Generated: {{ moment().format('YYYY-MM-DD HH:mm') }}

+

Reference: {{ inspection.reference_number }} | Version: {{ inspection.version }} | Generated: {{ moment() }}

diff --git a/config.py b/config.py index e2f5cac..98ea6ef 100644 --- a/config.py +++ b/config.py @@ -14,4 +14,7 @@ class Config: # Self-signed certificate paths CERT_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'certs', 'cert.pem') - KEY_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'certs', 'key.pem') \ No newline at end of file + KEY_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'certs', 'key.pem') + + # Logo configuration + LOGO_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'uploads', 'logo.png') \ No newline at end of file diff --git a/instance/app.db b/instance/app.db new file mode 100644 index 0000000000000000000000000000000000000000..e2e8928dd8bedf265b565490b5d2002cf8bbfa86 GIT binary patch literal 36864 zcmeI)-)`Gf90zcF{gXIQ0uq(g3xqp{+6>m(PU1R2h-IBOGP0CrDci2B6Z?c(b>hr+ zLI+nZl@M>RC*XB>0p4H_0K3D-Y0|h&#sHzA?rX(~b9`){-^agm8mEyT?ADx+N^Q^Y zkWk8V?{YlP-IOGbSAMDu7`h!*BYZ=zRzVtLkLn%fxsyp;WeOAFVzcg*as*d?;jy6*%ab<-+E`_A!Qp@YMoo|QF>FtJ5X&O?q za;s)Y!!l_-BRIBHtv8LkMnkIaG^P4pt@f#u5w0H4r&pzKDvj+gD~)wUmUD^K1LF3n z)HEJ7S!K(oB&4<(eLA}x9iv&jZ?NL88`b-j#$)L#-Sub*so@F zXQyFQ@79^$dZNuasbSnP8b*EFcp#mviIFq8J%3u+V#07* zjekYj52!!E&2pjbQ)-6vFf_8*ClbAR<2uauv#w{-#9f603V82)^@t+uSF?{_JW_U=q9IbfsH6J2j(HXUWgXqVZ`! zCksb5=cK3gy3wFLiZU^h$IXsL^0m1|@w4SwBRSjDRN3?V+LgbosIZE2(ce^dea|gL zGoNj8(E}3%AOHafKmY;|fB*y_009U<00MtQ;3&suIB{+BXjc#_@fQXa_N{>vggyy9 zKQ6gR4@sxz(hbY&^jZgYTP+9sL@jD%O=i=zrgN|QQ1NOHtB?F^x}wVkMOF){uH}iP zZ?$y0XeqKrw)BF$RnkhjWoad1lNKrEEt%%^VnN%|^&(MAd0SIuRl6bQZz!^)sO5rO zR>WF2tWV=5P$##AOHafKmY;|xLgA1IZ=qehZtV}Kj*T~ zFIRww1_BU(00bZa0SG_<0uX=z1R(H+3cNqJIlShd{O!T;_y5nh?6Wsi1z|$~0uX=z z1Rwwb2tWV=5P$##UZX&pU*g2&Re@jm@I9eYA#Kb0esE|F#=qp(e^8Zt^zaVY&HJPd z^$y1C6iL?0`C_?H+)(tAqU*EPl(gaX{|her;xz_~Lqh-p5P$##AOHafKmY;|fB*y_ z@ZSh5@C%$E#P|P;Zu*Z20uX=z1Rwwb2tWV=5P$##AOL|&EWrN%kL&+Se1Z`g1Rwwb w2tWV=5P$##AOHafKp+xe_y5E7KT-h#5P$##AOHafKmY;|fB*y_aJdEk1Z9V4;s5{u literal 0 HcmV?d00001 diff --git a/run.py b/run.py index eaaaa3a..ba94e68 100755 --- a/run.py +++ b/run.py @@ -3,21 +3,11 @@ import os import sys from app import create_app - -# Get the current directory -current_dir = os.path.dirname(os.path.abspath(__file__)) - -# Set the configuration -config_name = os.environ.get('FLASK_ENV', 'development') +from config import Config # Create and run the application -app = create_app(config_name) +app = create_app(Config) if __name__ == '__main__': - # Check if we're in development mode - if config_name == 'development': - # Run with debug mode for development - app.run(debug=True, host='0.0.0.0', port=5000, ssl_context='adhoc') - else: - # Production mode - app.run(host='0.0.0.0', port=5000, ssl_context='adhoc') \ No newline at end of file + # Run with debug mode for development + app.run(debug=True, host='0.0.0.0', port=5000, ssl_context='adhoc') \ No newline at end of file diff --git a/setup.py b/setup.py index 8038f64..b5135d5 100755 --- a/setup.py +++ b/setup.py @@ -73,21 +73,12 @@ def create_directories(): def setup_database(): """Initialize the database""" try: + # Import the actual config class and create app with it + from config import Config from app import create_app from app.models import db - # Create a simple config object - class DevelopmentConfig: - SECRET_KEY = 'dev-secret-key' - SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db' - SQLALCHEMY_TRACK_MODIFICATIONS = False - UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'uploads') - MAX_CONTENT_LENGTH = 10 * 1024 * 1024 - ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'} - CERT_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'certs', 'cert.pem') - KEY_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'certs', 'key.pem') - - app = create_app(DevelopmentConfig) + app = create_app(Config) with app.app_context(): db.create_all() @@ -98,21 +89,12 @@ def setup_database(): def create_admin_user(): """Create the default admin user""" try: + # Import the actual config class and create app with it + from config import Config from app import create_app from app.models import db, User - # Create a simple config object - class DevelopmentConfig: - SECRET_KEY = 'dev-secret-key' - SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db' - SQLALCHEMY_TRACK_MODIFICATIONS = False - UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'uploads') - MAX_CONTENT_LENGTH = 10 * 1024 * 1024 - ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'} - CERT_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'certs', 'cert.pem') - KEY_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'certs', 'key.pem') - - app = create_app(DevelopmentConfig) + app = create_app(Config) with app.app_context(): # Check if admin user already exists @@ -121,6 +103,7 @@ def create_admin_user(): # Create admin user admin_user = User( username='admin', + full_name='Administrator', email='admin@example.com', is_admin=True )