201 lines
6.4 KiB
Python
201 lines
6.4 KiB
Python
"""
|
|
Flask application factory and initialization
|
|
"""
|
|
from flask import Flask, request, jsonify, render_template
|
|
import os
|
|
import logging
|
|
from logging.handlers import RotatingFileHandler
|
|
from config.config import get_config
|
|
|
|
def create_app(config_name=None):
|
|
"""Application factory pattern"""
|
|
# Get the project root directory (parent of app directory)
|
|
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
template_dir = os.path.join(project_root, 'templates')
|
|
static_dir = os.path.join(project_root, 'static')
|
|
|
|
app = Flask(__name__, template_folder=template_dir, static_folder=static_dir)
|
|
|
|
# Load configuration
|
|
config_class = get_config(config_name)
|
|
app.config.from_object(config_class)
|
|
|
|
# Ensure required directories exist
|
|
_ensure_directories()
|
|
|
|
# Setup logging
|
|
_setup_logging(app)
|
|
|
|
# Register blueprints
|
|
_register_blueprints(app)
|
|
|
|
# Register error handlers
|
|
_register_error_handlers(app)
|
|
|
|
# Add context processors
|
|
_register_context_processors(app)
|
|
|
|
return app
|
|
|
|
def _ensure_directories():
|
|
"""Ensure required directories exist"""
|
|
directories = [
|
|
'data',
|
|
'data/uploads',
|
|
'data/backups',
|
|
'logs',
|
|
'ansible/inventory',
|
|
'ansible/playbooks',
|
|
'ansible/roles'
|
|
]
|
|
|
|
for directory in directories:
|
|
os.makedirs(directory, exist_ok=True)
|
|
|
|
def _setup_logging(app):
|
|
"""Setup application logging"""
|
|
if not app.debug and not app.testing:
|
|
# File logging for production
|
|
if app.config.get('LOG_FILE'):
|
|
if not os.path.exists('logs'):
|
|
os.mkdir('logs')
|
|
|
|
file_handler = RotatingFileHandler(
|
|
app.config['LOG_FILE'],
|
|
maxBytes=10240000, # 10MB
|
|
backupCount=10
|
|
)
|
|
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)
|
|
|
|
app.logger.setLevel(logging.INFO)
|
|
app.logger.info('Enhanced monitoring server startup')
|
|
|
|
def _register_blueprints(app):
|
|
"""Register all blueprints"""
|
|
|
|
# Import blueprints here to avoid circular imports
|
|
from app.api.logs import logs_bp
|
|
from app.api.ansible import ansible_bp
|
|
from app.api.wmt import wmt_api_bp
|
|
from app.web.main import main_bp
|
|
from app.web.ansible import ansible_web_bp
|
|
from app.web.wmt import wmt_web_bp
|
|
|
|
app.register_blueprint(logs_bp)
|
|
app.register_blueprint(ansible_bp)
|
|
app.register_blueprint(wmt_api_bp)
|
|
app.register_blueprint(main_bp)
|
|
app.register_blueprint(ansible_web_bp)
|
|
app.register_blueprint(wmt_web_bp)
|
|
|
|
# Add compatibility routes for old clients
|
|
@app.route('/logs', methods=['POST'])
|
|
def compatibility_logs():
|
|
"""Compatibility endpoint for old prezenta clients"""
|
|
from flask import request, redirect, url_for
|
|
import re
|
|
|
|
# Forward the request to the new API endpoint
|
|
# Import inside function to avoid circular imports
|
|
from app.api.logs import submit_log
|
|
return submit_log()
|
|
|
|
def _register_error_handlers(app):
|
|
"""Register error handlers"""
|
|
|
|
@app.errorhandler(400)
|
|
def bad_request(error):
|
|
if request.is_json:
|
|
return jsonify({
|
|
'error': 'Bad request',
|
|
'message': 'The request could not be understood by the server'
|
|
}), 400
|
|
return render_template('errors/400.html'), 400
|
|
|
|
@app.errorhandler(401)
|
|
def unauthorized(error):
|
|
if request.is_json:
|
|
return jsonify({
|
|
'error': 'Unauthorized',
|
|
'message': 'Authentication required'
|
|
}), 401
|
|
return render_template('errors/401.html'), 401
|
|
|
|
@app.errorhandler(403)
|
|
def forbidden(error):
|
|
if request.is_json:
|
|
return jsonify({
|
|
'error': 'Forbidden',
|
|
'message': 'Insufficient permissions'
|
|
}), 403
|
|
return render_template('errors/403.html'), 403
|
|
|
|
@app.errorhandler(404)
|
|
def not_found(error):
|
|
if request.is_json:
|
|
return jsonify({
|
|
'error': 'Not found',
|
|
'message': 'The requested resource was not found'
|
|
}), 404
|
|
try:
|
|
return render_template('errors/404.html'), 404
|
|
except Exception as e:
|
|
# Fallback if template cannot be loaded
|
|
app.logger.error(f'Error loading 404 template: {e}')
|
|
return '''
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head><title>404 - Page Not Found</title></head>
|
|
<body>
|
|
<h1>404 - Page Not Found</h1>
|
|
<p>The requested resource was not found.</p>
|
|
<a href="/">← Back to Dashboard</a>
|
|
</body>
|
|
</html>
|
|
''', 404
|
|
|
|
@app.errorhandler(500)
|
|
def internal_error(error):
|
|
app.logger.error(f'Internal server error: {error}')
|
|
if request.is_json:
|
|
return jsonify({
|
|
'error': 'Internal server error',
|
|
'message': 'An unexpected error occurred'
|
|
}), 500
|
|
try:
|
|
return render_template('errors/500.html'), 500
|
|
except Exception as e:
|
|
app.logger.error(f'Error loading 500 template: {e}')
|
|
return '''
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head><title>500 - Internal Server Error</title></head>
|
|
<body>
|
|
<h1>500 - Internal Server Error</h1>
|
|
<p>An unexpected error occurred.</p>
|
|
<a href="/">← Back to Dashboard</a>
|
|
</body>
|
|
</html>
|
|
''', 500
|
|
|
|
def _register_context_processors(app):
|
|
"""Register template context processors"""
|
|
|
|
@app.context_processor
|
|
def inject_config():
|
|
from app.models import WMTUpdateRequest
|
|
from config.database_config import get_db
|
|
try:
|
|
with get_db().get_session() as session:
|
|
pending_wmt_count = session.query(WMTUpdateRequest).filter_by(status='pending').count()
|
|
except Exception:
|
|
pending_wmt_count = 0
|
|
return {
|
|
'app_name': 'Enhanced Server Monitoring',
|
|
'app_version': '2.0.0',
|
|
'pending_wmt_count': pending_wmt_count,
|
|
} |