working folder creation
This commit is contained in:
parent
ea3e92b8b7
commit
b9a82af12f
11 changed files with 2791 additions and 1552 deletions
221
app/__init__.py
221
app/__init__.py
|
@ -5,6 +5,7 @@ from config import Config
|
|||
import os
|
||||
from datetime import datetime
|
||||
import sqlite3
|
||||
import logging
|
||||
|
||||
# Initialize extensions
|
||||
db = SQLAlchemy()
|
||||
|
@ -13,61 +14,185 @@ login_manager.login_view = 'auth.login'
|
|||
login_manager.login_message_category = 'info'
|
||||
|
||||
def initialize_database(app):
|
||||
"""Create database tables if they don't exist"""
|
||||
"""Create and initialize database tables if they don't exist"""
|
||||
with app.app_context():
|
||||
app.logger.info("Initializing database...")
|
||||
try:
|
||||
# Create all tables
|
||||
# Create all tables (this is safe to call even if tables exist)
|
||||
db.create_all()
|
||||
app.logger.info("Database tables created successfully")
|
||||
|
||||
# Check if we need to add the Share and Download models
|
||||
inspector = db.inspect(db.engine)
|
||||
if not inspector.has_table('share'):
|
||||
app.logger.info("Creating Share table...")
|
||||
# Import models to ensure they're registered with SQLAlchemy
|
||||
from app.models import Share
|
||||
if not inspector.has_table('share'):
|
||||
# Create the Share table
|
||||
Share.__table__.create(db.engine)
|
||||
|
||||
if not inspector.has_table('download'):
|
||||
app.logger.info("Creating Download table...")
|
||||
from app.models import Download
|
||||
if not inspector.has_table('download'):
|
||||
# Create the Download table
|
||||
Download.__table__.create(db.engine)
|
||||
|
||||
# Check for existing users - create admin if none
|
||||
from app.models import User
|
||||
if User.query.count() == 0:
|
||||
app.logger.info("No users found, creating default admin user...")
|
||||
admin = User(username='admin', email='admin@example.com')
|
||||
admin.set_password('adminpassword')
|
||||
db.session.add(admin)
|
||||
db.session.commit()
|
||||
app.logger.info("Default admin user created")
|
||||
|
||||
app.logger.info("Database initialization complete")
|
||||
except Exception as e:
|
||||
app.logger.error(f"Error creating database tables: {str(e)}")
|
||||
app.logger.error(f"Error initializing database: {str(e)}")
|
||||
# Don't raise the exception to prevent app startup failure
|
||||
# But log it for debugging purposes
|
||||
|
||||
def run_migrations(app):
|
||||
"""Apply any necessary database migrations"""
|
||||
db_path = app.config['SQLALCHEMY_DATABASE_URI'].replace('sqlite:///', '')
|
||||
|
||||
if not os.path.exists(db_path):
|
||||
app.logger.info(f"Database file does not exist: {db_path}")
|
||||
return
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Check if storage_name column exists in file table
|
||||
cursor.execute("PRAGMA table_info(file)")
|
||||
columns = [column[1] for column in cursor.fetchall()]
|
||||
|
||||
if 'storage_name' not in columns:
|
||||
app.logger.info("Adding storage_name column to file table")
|
||||
cursor.execute("ALTER TABLE file ADD COLUMN storage_name TEXT")
|
||||
"""Apply any necessary database migrations automatically"""
|
||||
with app.app_context():
|
||||
try:
|
||||
app.logger.info("Running database migrations...")
|
||||
# Get database path
|
||||
db_path = app.config['SQLALCHEMY_DATABASE_URI'].replace('sqlite:///', '')
|
||||
|
||||
# Update existing records to use filename as storage_name
|
||||
cursor.execute("UPDATE file SET storage_name = name WHERE storage_name IS NULL AND is_folder = 0")
|
||||
conn.commit()
|
||||
|
||||
conn.close()
|
||||
app.logger.info("Database migrations completed successfully")
|
||||
except sqlite3.OperationalError as e:
|
||||
if "no such table: file" in str(e):
|
||||
app.logger.info("File table doesn't exist yet, will be created with db.create_all()")
|
||||
else:
|
||||
app.logger.error(f"Error during migration: {str(e)}")
|
||||
except Exception as e:
|
||||
app.logger.error(f"Error during migration: {str(e)}")
|
||||
# Check if we're using SQLite and if the database file exists
|
||||
if db_path.startswith('/'): # Absolute path
|
||||
if not os.path.exists(db_path):
|
||||
app.logger.info(f"Database file does not exist: {db_path}")
|
||||
return
|
||||
|
||||
# Use SQLAlchemy to check and add missing columns
|
||||
inspector = db.inspect(db.engine)
|
||||
|
||||
# Check for 'file' table columns
|
||||
if inspector.has_table('file'):
|
||||
columns = [col['name'] for col in inspector.get_columns('file')]
|
||||
|
||||
# Add storage_name column if it doesn't exist
|
||||
if 'storage_name' not in columns:
|
||||
app.logger.info("Adding storage_name column to file table")
|
||||
if db.engine.name == 'sqlite':
|
||||
# For SQLite, use direct SQL as it doesn't support ALTER TABLE ADD COLUMN with default
|
||||
db.session.execute('ALTER TABLE file ADD COLUMN storage_name TEXT')
|
||||
# Update existing records
|
||||
db.session.execute('UPDATE file SET storage_name = uuid() WHERE storage_name IS NULL AND is_folder = 0')
|
||||
db.session.commit()
|
||||
|
||||
# Check for user table columns
|
||||
if inspector.has_table('user'):
|
||||
columns = [col['name'] for col in inspector.get_columns('user')]
|
||||
|
||||
# Add last_login column if it doesn't exist
|
||||
if 'last_login' not in columns:
|
||||
app.logger.info("Adding last_login column to user table")
|
||||
if db.engine.name == 'sqlite':
|
||||
db.session.execute('ALTER TABLE user ADD COLUMN last_login DATETIME')
|
||||
db.session.commit()
|
||||
|
||||
app.logger.info("Database migrations complete")
|
||||
except Exception as e:
|
||||
app.logger.error(f"Error during database migration: {str(e)}")
|
||||
# Log error but don't crash the app
|
||||
|
||||
def get_file_icon(mime_type, filename):
|
||||
"""Return Font Awesome icon class based on file type"""
|
||||
if mime_type:
|
||||
if mime_type.startswith('image/'):
|
||||
return 'fa-file-image'
|
||||
elif mime_type.startswith('video/'):
|
||||
return 'fa-file-video'
|
||||
elif mime_type.startswith('audio/'):
|
||||
return 'fa-file-audio'
|
||||
elif mime_type.startswith('text/'):
|
||||
return 'fa-file-alt'
|
||||
elif mime_type.startswith('application/pdf'):
|
||||
return 'fa-file-pdf'
|
||||
elif 'spreadsheet' in mime_type or 'excel' in mime_type:
|
||||
return 'fa-file-excel'
|
||||
elif 'presentation' in mime_type or 'powerpoint' in mime_type:
|
||||
return 'fa-file-powerpoint'
|
||||
elif 'document' in mime_type or 'word' in mime_type:
|
||||
return 'fa-file-word'
|
||||
|
||||
# Check by extension
|
||||
ext = os.path.splitext(filename)[1].lower()[1:]
|
||||
if ext in ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp']:
|
||||
return 'fa-file-image'
|
||||
elif ext in ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv']:
|
||||
return 'fa-file-video'
|
||||
elif ext in ['mp3', 'wav', 'ogg', 'flac', 'm4a']:
|
||||
return 'fa-file-audio'
|
||||
elif ext in ['doc', 'docx', 'odt']:
|
||||
return 'fa-file-word'
|
||||
elif ext in ['xls', 'xlsx', 'ods', 'csv']:
|
||||
return 'fa-file-excel'
|
||||
elif ext in ['ppt', 'pptx', 'odp']:
|
||||
return 'fa-file-powerpoint'
|
||||
elif ext == 'pdf':
|
||||
return 'fa-file-pdf'
|
||||
elif ext in ['zip', 'rar', '7z', 'tar', 'gz']:
|
||||
return 'fa-file-archive'
|
||||
elif ext in ['txt', 'rtf', 'md']:
|
||||
return 'fa-file-alt'
|
||||
elif ext in ['html', 'css', 'js', 'py', 'java', 'php', 'c', 'cpp', 'json', 'xml']:
|
||||
return 'fa-file-code'
|
||||
|
||||
return 'fa-file'
|
||||
|
||||
def format_file_size(size):
|
||||
"""Format file size in bytes to human-readable format"""
|
||||
if not size:
|
||||
return "0 B"
|
||||
|
||||
size_names = ("B", "KB", "MB", "GB", "TB")
|
||||
i = 0
|
||||
while size >= 1024 and i < len(size_names) - 1:
|
||||
size /= 1024
|
||||
i += 1
|
||||
return f"{size:.1f} {size_names[i]}"
|
||||
|
||||
def create_app(config_class=Config):
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(config_class)
|
||||
|
||||
# Configure logging
|
||||
if not app.debug:
|
||||
# Set up file handler
|
||||
if not os.path.exists('logs'):
|
||||
os.mkdir('logs')
|
||||
file_handler = logging.FileHandler('logs/flask_files.log')
|
||||
file_handler.setFormatter(logging.Formatter(
|
||||
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
|
||||
))
|
||||
file_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(file_handler)
|
||||
|
||||
# Set log level
|
||||
app.logger.setLevel(logging.INFO)
|
||||
app.logger.info('Flask Files startup')
|
||||
|
||||
# Initialize extensions
|
||||
db.init_app(app)
|
||||
login_manager.init_app(app)
|
||||
|
||||
# Initialize the upload folder
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
||||
upload_folder = app.config.get('UPLOAD_FOLDER', 'uploads')
|
||||
if not os.path.isabs(upload_folder):
|
||||
# If it's a relative path, make it relative to the app instance folder
|
||||
upload_folder = os.path.join(app.instance_path, upload_folder)
|
||||
app.config['UPLOAD_FOLDER'] = upload_folder
|
||||
|
||||
# Auto initialize database if it doesn't exist
|
||||
os.makedirs(upload_folder, exist_ok=True)
|
||||
app.logger.info(f"Upload folder initialized at: {upload_folder}")
|
||||
|
||||
# Auto initialize database and run migrations on startup
|
||||
with app.app_context():
|
||||
initialize_database(app)
|
||||
run_migrations(app)
|
||||
|
@ -83,9 +208,27 @@ def create_app(config_class=Config):
|
|||
|
||||
# Add context processor for template variables
|
||||
@app.context_processor
|
||||
def inject_now():
|
||||
return {'now': datetime.now()}
|
||||
def inject_global_variables():
|
||||
return {
|
||||
'now': datetime.now(),
|
||||
'file_icon': get_file_icon,
|
||||
'format_size': format_file_size,
|
||||
'app_version': '1.0.0', # Add version number for caching
|
||||
}
|
||||
|
||||
# Handle 404 errors
|
||||
@app.errorhandler(404)
|
||||
def not_found_error(error):
|
||||
return render_template('errors/404.html'), 404
|
||||
|
||||
# Handle 500 errors
|
||||
@app.errorhandler(500)
|
||||
def internal_error(error):
|
||||
db.session.rollback() # Rollback any failed database transactions
|
||||
return render_template('errors/500.html'), 500
|
||||
|
||||
return app
|
||||
|
||||
# Import must come after create_app to avoid circular imports
|
||||
from flask import render_template # For error handlers
|
||||
from app import models
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue