from flask import Blueprint, render_template, request, jsonify, make_response, flash, redirect, url_for from flask_login import login_required, current_user from app import db from app.models import Folder, User from app.imap_service import IMAPService import uuid import logging main = Blueprint('main', __name__) @main.route('/') @login_required def index(): # Get folders for the current authenticated user folders = Folder.query.filter_by(user_id=current_user.id).all() return render_template('index.html', folders=folders) @main.route('/api/folders/new', methods=['GET']) @login_required def new_folder_modal(): # Return the add folder modal response = make_response(render_template('partials/folder_modal.html')) response.headers['HX-Trigger'] = 'open-modal' return response @main.route('/api/folders', methods=['POST']) @login_required def add_folder(): try: # Get form data instead of JSON name = request.form.get('name') rule_text = request.form.get('rule_text') priority = request.form.get('priority') # Server-side validation errors = {} if not name or not name.strip(): errors['name'] = 'Folder name is required' elif len(name.strip()) < 3: errors['name'] = 'Folder name must be at least 3 characters' elif len(name.strip()) > 50: errors['name'] = 'Folder name must be less than 50 characters' if not rule_text or not rule_text.strip(): errors['rule_text'] = 'Rule text is required' elif len(rule_text.strip()) < 10: errors['rule_text'] = 'Rule text must be at least 10 characters' elif len(rule_text.strip()) > 200: errors['rule_text'] = 'Rule text must be less than 200 characters' # If there are validation errors, return the modal with errors if errors: response = make_response(render_template('partials/folder_modal.html', errors=errors, name=name, rule_text=rule_text, priority=priority)) response.headers['HX-Retarget'] = '#folder-modal' response.headers['HX-Reswap'] = 'outerHTML' return response # Create new folder for the current user folder = Folder( user_id=current_user.id, name=name.strip(), rule_text=rule_text.strip(), priority=int(priority) if priority else 0 ) db.session.add(folder) db.session.commit() # Get updated list of folders for the current user folders = Folder.query.filter_by(user_id=current_user.id).all() # Return the updated folders list HTML response = make_response(render_template('partials/folders_list.html', folders=folders)) response.headers['HX-Trigger'] = 'close-modal' response.status_code = 201 return response except Exception as e: # Print unhandled exceptions to the console as required logging.exception("Error adding folder: %s", e) db.session.rollback() # Return error in modal errors = {'general': 'An unexpected error occurred. Please try again.'} response = make_response(render_template('partials/folder_modal.html', errors=errors, name=name, rule_text=rule_text, priority=priority)) response.headers['HX-Retarget'] = '#folder-modal' response.headers['HX-Reswap'] = 'outerHTML' return response @main.route('/api/folders/', methods=['DELETE']) @login_required def delete_folder(folder_id): try: # Find the folder by ID and ensure it belongs to the current user folder = Folder.query.filter_by(id=folder_id, user_id=current_user.id).first() if not folder: # Folder not found folders = Folder.query.filter_by(user_id=current_user.id).all() return render_template('partials/folders_list.html', folders=folders) # Delete the folder db.session.delete(folder) db.session.commit() # Get updated list of folders for the current user folders = Folder.query.filter_by(user_id=current_user.id).all() # Return the updated folders list HTML return render_template('partials/folders_list.html', folders=folders) except Exception as e: # Print unhandled exceptions to the console as required logging.exception("Error deleting folder: %s", e) db.session.rollback() # Return the folders list unchanged folders = Folder.query.filter_by(user_id=current_user.id).all() return render_template('partials/folders_list.html', folders=folders) @main.route('/api/folders//edit', methods=['GET']) @login_required def edit_folder_modal(folder_id): try: # Find the folder by ID and ensure it belongs to the current user folder = Folder.query.filter_by(id=folder_id, user_id=current_user.id).first() if not folder: return jsonify({'error': 'Folder not found'}), 404 # Return the edit folder modal with folder data response = make_response(render_template('partials/folder_modal.html', folder=folder)) response.headers['HX-Trigger'] = 'open-modal' return response except Exception as e: # Print unhandled exceptions to the console as required logging.exception("Error getting folder for edit: %s", e) return jsonify({'error': 'Error retrieving folder'}), 500 @main.route('/api/folders/', methods=['PUT']) @login_required def update_folder(folder_id): try: # Find the folder by ID and ensure it belongs to the current user folder = Folder.query.filter_by(id=folder_id, user_id=current_user.id).first() if not folder: # Folder not found folders = Folder.query.filter_by(user_id=current_user.id).all() return render_template('partials/folders_list.html', folders=folders) # Get form data name = request.form.get('name') rule_text = request.form.get('rule_text') priority = request.form.get('priority') # Server-side validation errors = {} if not name or not name.strip(): errors['name'] = 'Folder name is required' elif len(name.strip()) < 3: errors['name'] = 'Folder name must be at least 3 characters' elif len(name.strip()) > 50: errors['name'] = 'Folder name must be less than 50 characters' if not rule_text or not rule_text.strip(): errors['rule_text'] = 'Rule text is required' elif len(rule_text.strip()) < 10: errors['rule_text'] = 'Rule text must be at least 10 characters' elif len(rule_text.strip()) > 200: errors['rule_text'] = 'Rule text must be less than 200 characters' # If there are validation errors, return the modal with errors if errors: response = make_response(render_template('partials/folder_modal.html', folder=folder, errors=errors, name=name, rule_text=rule_text, priority=priority)) response.headers['HX-Retarget'] = '#folder-modal' response.headers['HX-Reswap'] = 'outerHTML' return response # Update folder folder.name = name.strip() folder.rule_text = rule_text.strip() folder.priority = int(priority) if priority else 0 db.session.commit() # Get updated list of folders for the current user folders = Folder.query.filter_by(user_id=current_user.id).all() response = make_response(render_template('partials/folders_list.html', folders=folders)) response.headers['HX-Trigger'] = 'close-modal' return response except Exception as e: # Print unhandled exceptions to the console as required logging.exception("Error updating folder: %s", e) db.session.rollback() # Return error in modal errors = {'general': 'An unexpected error occurred. Please try again.'} response = make_response(render_template('partials/folder_modal.html', folder=folder, errors=errors, name=name, rule_text=rule_text, priority=priority)) response.headers['HX-Retarget'] = '#folder-modal' response.headers['HX-Reswap'] = 'outerHTML' return response @main.route('/api/imap/config', methods=['GET']) @login_required def imap_config_modal(): """Return the IMAP configuration modal.""" # Pass existing IMAP config to the template if it exists response = make_response(render_template('partials/imap_config_modal.html', server=current_user.imap_config.get('server') if current_user.imap_config else None, port=current_user.imap_config.get('port') if current_user.imap_config else None, username=current_user.imap_config.get('username') if current_user.imap_config else None, use_ssl=current_user.imap_config.get('use_ssl', True) if current_user.imap_config else True)) response.headers['HX-Trigger'] = 'open-modal' return response @main.route('/api/imap/test', methods=['POST']) @login_required def test_imap_connection(): """Test IMAP connection with provided configuration.""" print("HELLO") try: # Get form data server = request.form.get('server') port = request.form.get('port') username = request.form.get('username') password = request.form.get('password') use_ssl = request.form.get('use_ssl') == 'on' # Validate required fields errors = {} if not server: errors['server'] = 'Server is required' if not port: errors['port'] = 'Port is required' elif not port.isdigit(): errors['port'] = 'Port must be a number' if not username: errors['username'] = 'Username is required' if not password: errors['password'] = 'Password is required' if errors: response = make_response(render_template('partials/imap_config_modal.html', errors=errors, server=server, port=port, username=username, use_ssl=use_ssl)) response.headers['HX-Retarget'] = '#imap-modal' response.headers['HX-Reswap'] = 'outerHTML' return response # Store configuration temporarily for testing test_config = { 'server': server, 'port': int(port), 'username': username, 'password': password, 'use_ssl': False, 'use_tls': False, 'connection_timeout': 30 } # Test connection temp_user = type('User', (), {'imap_config': test_config})() imap_service = IMAPService(temp_user) print(temp_user, test_config) success, message = imap_service.test_connection() if success: # Save configuration to user's profile current_user.imap_config = test_config db.session.commit() response = make_response(render_template('partials/imap_config_modal.html', success=True, message=message)) response.headers['HX-Retarget'] = '#imap-modal' response.headers['HX-Reswap'] = 'outerHTML' else: print(message) response = make_response(render_template('partials/imap_config_modal.html', errors={'general': message}, server=server, port=port, username=username, use_ssl=use_ssl)) response.headers['HX-Retarget'] = '#imap-modal' response.headers['HX-Reswap'] = 'outerHTML' return response except Exception as e: logging.exception("Error testing IMAP connection: %s", e) print(e) errors = {'general': 'An unexpected error occurred. Please try again.'} response = make_response(render_template('partials/imap_config_modal.html', errors=errors, server=server, port=port, username=username, use_ssl=use_ssl)) response.headers['HX-Retarget'] = '#imap-modal' response.headers['HX-Reswap'] = 'outerHTML' return response @main.route('/api/imap/sync', methods=['POST']) @login_required def sync_imap_folders(): """Sync folders from IMAP server.""" try: if not current_user.imap_config: return jsonify({'error': 'No IMAP configuration found. Please configure IMAP first.'}), 400 # Test connection first imap_service = IMAPService(current_user) success, message = imap_service.sync_folders() if success: # Get updated list of folders folders = Folder.query.filter_by(user_id=current_user.id).all() return render_template('partials/folders_list.html', folders=folders) else: return jsonify({'error': message}), 400 except Exception as e: logging.exception("Error syncing IMAP folders: %s", e) print(e) return jsonify({'error': 'An unexpected error occurred. Please try again.'}), 500