from flask import Flask, render_template, request, jsonify, redirect, url_for, session from functools import wraps import malias_wrapper as malias_w import os app = Flask(__name__) app.secret_key = os.urandom(24) # Secret key for session management # Initialize database on startup malias_w.init_database() def login_required(f): """Decorator to require login for routes""" @wraps(f) def decorated_function(*args, **kwargs): if not session.get('logged_in'): return jsonify({'status': 'error', 'message': 'Not authenticated', 'redirect': '/login'}), 401 return f(*args, **kwargs) return decorated_function @app.route('/login', methods=['GET', 'POST']) def login(): """Login page""" if request.method == 'POST': password = request.json.get('password', '') if malias_w.verify_password(password): session['logged_in'] = True return jsonify({'status': 'success', 'message': 'Login successful'}) else: return jsonify({'status': 'error', 'message': 'Invalid password'}) # Check if already logged in if session.get('logged_in'): return redirect(url_for('index')) return render_template('login.html') @app.route('/logout') def logout(): """Logout""" session.pop('logged_in', None) return redirect(url_for('login')) @app.route('/') def index(): """Main page - requires login""" if not session.get('logged_in'): return redirect(url_for('login')) return render_template('index.html') @app.route('/list_aliases', methods=['POST']) @login_required def list_aliases(): """List all aliases from Mailcow with pagination""" try: connection = malias_w.get_settings_from_db() if not connection: return jsonify({'status': 'error', 'message': 'Please configure Mailcow server first'}) # Get page number from request, default to 1 page = request.json.get('page', 1) if request.json else 1 result = malias_w.get_all_aliases(page=page, per_page=20) return jsonify({'status': 'success', 'data': result}) except Exception as e: return jsonify({'status': 'error', 'message': f"Error: {str(e)}"}) @app.route('/sync_aliases', methods=['POST']) @login_required def sync_aliases(): """Sync aliases from server to local DB""" try: count = malias_w.update_aliases() result = f"Aliases synchronized successfully! Added {count} new aliases to local DB." return jsonify({'status': 'success', 'message': result}) except Exception as e: return jsonify({'status': 'error', 'message': f"Error syncing: {str(e)}"}) @app.route('/get_domains', methods=['POST']) @login_required def get_domains(): """Get all mail domains""" try: domains = malias_w.get_domains() domain_list = [d['domain_name'] for d in domains] result = f"Domains: {', '.join(domain_list)}" return jsonify({'status': 'success', 'message': result, 'domains': domain_list}) except Exception as e: return jsonify({'status': 'error', 'message': f"Error: {str(e)}"}) @app.route('/create_alias', methods=['POST']) @login_required def create_alias(): """Create a new alias""" try: data = request.json alias = data.get('alias', '') goto = data.get('goto', '') if not alias or not goto: return jsonify({'status': 'error', 'message': 'Both alias and destination are required'}) malias_w.create_alias(alias, goto) result = f"Alias {alias} created successfully for {goto}" return jsonify({'status': 'success', 'message': result}) except Exception as e: return jsonify({'status': 'error', 'message': f"Error creating alias: {str(e)}"}) @app.route('/delete_alias', methods=['POST']) @login_required def delete_alias_route(): """Delete an alias""" try: data = request.json alias = data.get('alias', '') if not alias: return jsonify({'status': 'error', 'message': 'Alias is required'}) malias_w.delete_alias(alias) result = f"Alias {alias} deleted successfully" return jsonify({'status': 'success', 'message': result}) except Exception as e: return jsonify({'status': 'error', 'message': f"Error deleting alias: {str(e)}"}) @app.route('/create_timed_alias', methods=['POST']) @login_required def create_timed_alias(): """Create a time-limited alias""" try: data = request.json username = data.get('username', '') domain = data.get('domain', '') if not username or not domain: return jsonify({'status': 'error', 'message': 'Both username and domain are required'}) malias_w.create_timed_alias(username, domain) result = f"Timed alias created for {username} on domain {domain}" return jsonify({'status': 'success', 'message': result}) except Exception as e: return jsonify({'status': 'error', 'message': f"Error: {str(e)}"}) @app.route('/search', methods=['POST']) @login_required def search(): """Search for aliases""" query = request.json.get('query', '') if not query: return jsonify({'status': 'error', 'message': 'No search query provided'}) try: results = malias_w.search_aliases(query) if results: result_list = [f"{alias} → {goto}" for alias, goto in results] message = f"Found {len(results)} alias(es):\n" + "\n".join(result_list) else: message = f"No aliases found matching '{query}'" return jsonify({'status': 'success', 'message': message, 'query': query}) except Exception as e: return jsonify({'status': 'error', 'message': f"Search error: {str(e)}"}) @app.route('/config', methods=['GET', 'POST']) @login_required def config_page(): """Configuration management for Mailcow connection""" if request.method == 'POST': mailcow_server = request.json.get('mailcow_server', '') mailcow_api_key = request.json.get('mailcow_api_key', '') if mailcow_server and mailcow_api_key: malias_w.set_connection_info(mailcow_server, mailcow_api_key) return jsonify({'status': 'success', 'message': 'Mailcow configuration saved successfully'}) else: return jsonify({'status': 'error', 'message': 'Server and API key are required'}) # GET request - return current config try: current_config = malias_w.get_config() return jsonify(current_config) except Exception as e: return jsonify({'mailcow_server': '', 'mailcow_api_key': ''}) @app.route('/change_password', methods=['POST']) @login_required def change_password_route(): """Change password""" try: data = request.json old_password = data.get('old_password', '') new_password = data.get('new_password', '') confirm_password = data.get('confirm_password', '') if not old_password or not new_password or not confirm_password: return jsonify({'status': 'error', 'message': 'All fields are required'}) if new_password != confirm_password: return jsonify({'status': 'error', 'message': 'New passwords do not match'}) if len(new_password) < 6: return jsonify({'status': 'error', 'message': 'Password must be at least 6 characters'}) malias_w.change_password(old_password, new_password) return jsonify({'status': 'success', 'message': 'Password changed successfully'}) except Exception as e: return jsonify({'status': 'error', 'message': str(e)}) if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5172)