Merge pull request #9 from pingud98/continuous-claude/iteration-1/2026-03-10-ef846925
Fix PDF export, inspection edit, photo upload issues and add user man…
This commit is contained in:
commit
d05097a283
16 changed files with 269 additions and 84 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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'<User {self.username}>'
|
||||
|
||||
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'<Config {self.key}>'
|
||||
|
||||
class Inspection(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
installation_name = db.Column(db.String(200), nullable=False)
|
||||
|
|
|
|||
|
|
@ -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/<int:user_id>/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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
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
|
||||
BIN
app/static/favicon.ico
Normal file
BIN
app/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/static/favicon.png
Normal file
BIN
app/static/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
43
app/templates/admin/logo.html
Normal file
43
app/templates/admin/logo.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Logo Configuration{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Logo Configuration</h2>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'danger' if category == 'error' else category }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="form-group">
|
||||
{{ form.logo.label(class="form-label") }}
|
||||
{{ form.logo(class="form-control") }}
|
||||
<small class="form-text text-muted">Upload a JPEG or PNG logo (max 10MB)</small>
|
||||
</div>
|
||||
{{ form.submit(class="btn btn-primary") }}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h4>Current Logo</h4>
|
||||
{% if logo_filename %}
|
||||
<img src="{{ url_for('static', filename='uploads/' + logo_filename) }}"
|
||||
alt="Current Logo"
|
||||
style="max-width: 200px; max-height: 100px;">
|
||||
<p>Current logo: {{ logo_filename }}</p>
|
||||
{% else %}
|
||||
<p>No logo uploaded yet.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
10
app/templates/errors/404.html
Normal file
10
app/templates/errors/404.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Page Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>404 - Page Not Found</h1>
|
||||
<p>The page you are looking for does not exist.</p>
|
||||
</body>
|
||||
</html>
|
||||
10
app/templates/errors/500.html
Normal file
10
app/templates/errors/500.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Server Error</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Server Error (500)</h1>
|
||||
<p>Something went wrong on our end. Please try again later.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
<body>
|
||||
<div class="header">
|
||||
<h1>Inspection Report</h1>
|
||||
<p>Reference: {{ inspection.reference_number }} | Version: {{ inspection.version }} | Generated: {{ moment().format('YYYY-MM-DD HH:mm') }}</p>
|
||||
<p>Reference: {{ inspection.reference_number }} | Version: {{ inspection.version }} | Generated: {{ moment() }}</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
|
|
|
|||
|
|
@ -15,3 +15,6 @@ 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')
|
||||
|
||||
# Logo configuration
|
||||
LOGO_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'uploads', 'logo.png')
|
||||
BIN
instance/app.db
Normal file
BIN
instance/app.db
Normal file
Binary file not shown.
18
run.py
18
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')
|
||||
# Run with debug mode for development
|
||||
app.run(debug=True, host='0.0.0.0', port=5000, ssl_context='adhoc')
|
||||
31
setup.py
31
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
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue