| 
1 |  | -from flask import Flask, render_template, request, jsonify, redirect, url_for, send_from_directory  | 
 | 1 | +from flask import Flask, render_template, request, jsonify, redirect, url_for  | 
2 | 2 | from flask_login import LoginManager, login_required, current_user  | 
3 |  | -from datetime import datetime  | 
4 | 3 | from app.models import db, User  | 
5 |  | -from app.auth import auth_bp  | 
6 |  | -from app.admin import admin_bp  | 
7 |  | -from app.settings import settings_bp  | 
8 |  | -from app.directadmin_api import DirectAdminAPI  | 
9 | 4 | from app.config import Config  | 
10 |  | -import os  | 
11 |  | -import traceback  | 
 | 5 | +from app.directadmin_api import DirectAdminAPI  | 
12 | 6 | 
 
  | 
13 | 7 | def create_app():  | 
14 | 8 |     app = Flask(__name__,   | 
15 |  | -                static_folder='../static',  | 
16 |  | -                template_folder='templates')  | 
17 |  | -    app.config.from_object(Config)  | 
18 |  | - | 
19 |  | -    # Ensure data directory exists  | 
20 |  | -    data_dir = '/app/data' if os.path.exists('/app') else './data'  | 
21 |  | -    os.makedirs(data_dir, exist_ok=True)  | 
 | 9 | +                template_folder='templates',  | 
 | 10 | +                static_folder='../static')  | 
22 | 11 | 
 
  | 
23 |  | -    # Update database path  | 
24 |  | -    if 'sqlite' in app.config.get('SQLALCHEMY_DATABASE_URI', ''):  | 
25 |  | -        app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{data_dir}/users.db'  | 
 | 12 | +    app.config.from_object(Config)  | 
26 | 13 | 
 
  | 
 | 14 | +    # Initialize extensions  | 
27 | 15 |     db.init_app(app)  | 
28 | 16 | 
 
  | 
29 | 17 |     login_manager = LoginManager()  | 
30 | 18 |     login_manager.init_app(app)  | 
31 | 19 |     login_manager.login_view = 'auth.login'  | 
32 | 20 | 
 
  | 
33 |  | -    # Important: Make login manager return JSON for API routes  | 
34 |  | -    @login_manager.unauthorized_handler  | 
35 |  | -    def unauthorized():  | 
36 |  | -        if request.path.startswith('/api/') or request.path.startswith('/settings/api/'):  | 
37 |  | -            return jsonify({'error': 'Authentication required'}), 401  | 
38 |  | -        return redirect(url_for('auth.login'))  | 
39 |  | - | 
40 | 21 |     @login_manager.user_loader  | 
41 | 22 |     def load_user(user_id):  | 
42 | 23 |         return User.query.get(int(user_id))  | 
43 | 24 | 
 
  | 
44 |  | -    # NOW we can use @app decorators - app exists here!  | 
45 |  | -    @app.before_request  | 
46 |  | -    def check_session():  | 
47 |  | -        """Ensure session is valid for API routes"""  | 
48 |  | -        if request.path.startswith(('/api/', '/settings/api/', '/admin/api/')):  | 
49 |  | -            if not current_user.is_authenticated:  | 
50 |  | -                return jsonify({'error': 'Authentication required', 'redirect': '/login'}), 401  | 
51 |  | - | 
52 |  | -    # Error handlers for JSON responses  | 
53 |  | -    @app.errorhandler(404)  | 
54 |  | -    def not_found(error):  | 
55 |  | -        if request.path.startswith('/api/') or request.path.startswith('/settings/api/'):  | 
56 |  | -            return jsonify({'error': 'Not found'}), 404  | 
57 |  | -        return render_template('404.html'), 404  | 
58 |  | - | 
59 |  | -    @app.errorhandler(500)  | 
60 |  | -    def internal_error(error):  | 
61 |  | -        db.session.rollback()  | 
62 |  | -        if request.path.startswith('/api/') or request.path.startswith('/settings/api/'):  | 
63 |  | -            return jsonify({'error': 'Internal server error'}), 500  | 
64 |  | -        return render_template('500.html'), 500  | 
 | 25 | +    # Register blueprints  | 
 | 26 | +    from app.auth import auth_bp  | 
 | 27 | +    from app.admin import admin_bp  | 
 | 28 | +    from app.settings import settings_bp  | 
65 | 29 | 
 
  | 
66 | 30 |     app.register_blueprint(auth_bp)  | 
67 | 31 |     app.register_blueprint(admin_bp)  | 
68 | 32 |     app.register_blueprint(settings_bp)  | 
69 | 33 | 
 
  | 
70 |  | -    # Initialize database  | 
71 |  | -    with app.app_context():  | 
72 |  | -        try:  | 
73 |  | -            db.create_all()  | 
74 |  | -            # Create default admin user if none exists  | 
75 |  | -            if User.query.count() == 0:  | 
76 |  | -                user = User(username='admin', is_admin=True)  | 
77 |  | -                user.set_password('changeme')  | 
78 |  | -                db.session.add(user)  | 
79 |  | -                db.session.commit()  | 
80 |  | -                print("Created default admin user")  | 
81 |  | -        except Exception as e:  | 
82 |  | -            print(f"Database initialization error: {e}")  | 
83 |  | - | 
84 |  | -    @app.route('/static/<path:filename>')  | 
85 |  | -    def serve_static(filename):  | 
86 |  | -        return send_from_directory(app.static_folder, filename)  | 
87 |  | - | 
 | 34 | +    # Main routes  | 
88 | 35 |     @app.route('/')  | 
89 | 36 |     @login_required  | 
90 | 37 |     def index():  | 
91 |  | -        # Check if user has configured DirectAdmin settings  | 
92 |  | -        if not current_user.has_da_config():  | 
93 |  | -            return redirect(url_for('settings.index'))  | 
94 | 38 |         return redirect(url_for('dashboard'))  | 
95 | 39 | 
 
  | 
96 | 40 |     @app.route('/dashboard')  | 
97 | 41 |     @login_required  | 
98 | 42 |     def dashboard():  | 
99 |  | -        # Check if user has configured DirectAdmin settings  | 
100 | 43 |         if not current_user.has_da_config():  | 
101 |  | -            # Import flash here to avoid circular imports  | 
102 |  | -            from flask import flash  | 
103 |  | -            flash('Please configure your DirectAdmin settings first.', 'warning')  | 
104 | 44 |             return redirect(url_for('settings.index'))  | 
 | 45 | +        return render_template('dashboard.html')  | 
105 | 46 | 
 
  | 
106 |  | -        # Update last login  | 
107 |  | -        current_user.last_login = datetime.utcnow()  | 
108 |  | -        db.session.commit()  | 
109 |  | -        return render_template('dashboard.html', domain=current_user.da_domain)  | 
110 |  | - | 
111 |  | -    @app.route('/health')  | 
112 |  | -    def health_check():  | 
113 |  | -        """Health check endpoint for Docker"""  | 
114 |  | -        return jsonify({  | 
115 |  | -            'status': 'healthy',  | 
116 |  | -            'version': '1.0.0',  | 
117 |  | -            'database': 'connected' if db.engine else 'disconnected'  | 
118 |  | -        })  | 
119 |  | - | 
120 |  | -    @app.route('/api/email-accounts')  | 
 | 47 | +    @app.route('/api/forwarders', methods=['GET'])  | 
121 | 48 |     @login_required  | 
122 |  | -    def get_email_accounts():  | 
 | 49 | +    def get_forwarders():  | 
 | 50 | +        """Get all forwarders - NO EXTRA ARGUMENTS"""  | 
123 | 51 |         if not current_user.has_da_config():  | 
124 | 52 |             return jsonify({'error': 'DirectAdmin not configured'}), 400  | 
125 | 53 | 
 
  | 
126 | 54 |         try:  | 
127 | 55 |             api = DirectAdminAPI(  | 
128 | 56 |                 current_user.da_server,  | 
129 | 57 |                 current_user.da_username,  | 
130 |  | -                current_user.get_da_password()  | 
 | 58 | +                current_user.get_da_password(),  | 
 | 59 | +                current_user.da_domain  | 
131 | 60 |             )  | 
132 |  | -            accounts = api.get_email_accounts(current_user.da_domain)  | 
133 |  | -            return jsonify(accounts)  | 
 | 61 | + | 
 | 62 | +            # Call without extra arguments!  | 
 | 63 | +            forwarders = api.get_forwarders()  # ← No arguments here!  | 
 | 64 | + | 
 | 65 | +            return jsonify({'forwarders': forwarders})  | 
 | 66 | + | 
134 | 67 |         except Exception as e:  | 
135 |  | -            print(f"Error getting email accounts: {e}")  | 
 | 68 | +            print(f"Error getting forwarders: {e}")  | 
136 | 69 |             return jsonify({'error': str(e)}), 500  | 
137 | 70 | 
 
  | 
138 |  | -    @app.route('/api/forwarders')  | 
 | 71 | +    @app.route('/api/forwarders', methods=['POST'])  | 
139 | 72 |     @login_required  | 
140 |  | -    def get_forwarders():  | 
 | 73 | +    def create_forwarder():  | 
 | 74 | +        """Create a new forwarder"""  | 
141 | 75 |         if not current_user.has_da_config():  | 
142 | 76 |             return jsonify({'error': 'DirectAdmin not configured'}), 400  | 
143 | 77 | 
 
  | 
144 | 78 |         try:  | 
 | 79 | +            data = request.get_json()  | 
 | 80 | +            address = data.get('address')  | 
 | 81 | +            destination = data.get('destination')  | 
 | 82 | + | 
 | 83 | +            if not address or not destination:  | 
 | 84 | +                return jsonify({'error': 'Address and destination required'}), 400  | 
 | 85 | + | 
145 | 86 |             api = DirectAdminAPI(  | 
146 | 87 |                 current_user.da_server,  | 
147 | 88 |                 current_user.da_username,  | 
148 |  | -                current_user.get_da_password()  | 
 | 89 | +                current_user.get_da_password(),  | 
 | 90 | +                current_user.da_domain  | 
149 | 91 |             )  | 
150 |  | -            forwarders = api.get_forwarders(current_user.da_domain)  | 
151 |  | -            return jsonify(forwarders)  | 
 | 92 | + | 
 | 93 | +            success, message = api.create_forwarder(address, destination)  | 
 | 94 | + | 
 | 95 | +            if success:  | 
 | 96 | +                return jsonify({'success': True, 'message': message})  | 
 | 97 | +            else:  | 
 | 98 | +                return jsonify({'error': message}), 400  | 
 | 99 | + | 
152 | 100 |         except Exception as e:  | 
153 |  | -            print(f"Error getting forwarders: {e}")  | 
 | 101 | +            print(f"Error creating forwarder: {e}")  | 
154 | 102 |             return jsonify({'error': str(e)}), 500  | 
155 | 103 | 
 
  | 
156 |  | -    @app.route('/api/forwarders', methods=['POST'])  | 
 | 104 | +    @app.route('/api/forwarders', methods=['DELETE'])  | 
157 | 105 |     @login_required  | 
158 |  | -    def create_forwarder():  | 
 | 106 | +    def delete_forwarder():  | 
 | 107 | +        """Delete a forwarder"""  | 
159 | 108 |         if not current_user.has_da_config():  | 
160 | 109 |             return jsonify({'error': 'DirectAdmin not configured'}), 400  | 
161 | 110 | 
 
  | 
162 | 111 |         try:  | 
163 |  | -            data = request.json  | 
 | 112 | +            data = request.get_json()  | 
 | 113 | +            address = data.get('address')  | 
 | 114 | + | 
 | 115 | +            if not address:  | 
 | 116 | +                return jsonify({'error': 'Address required'}), 400  | 
 | 117 | + | 
164 | 118 |             api = DirectAdminAPI(  | 
165 | 119 |                 current_user.da_server,  | 
166 | 120 |                 current_user.da_username,  | 
167 |  | -                current_user.get_da_password()  | 
 | 121 | +                current_user.get_da_password(),  | 
 | 122 | +                current_user.da_domain  | 
168 | 123 |             )  | 
169 |  | -            success = api.create_forwarder(current_user.da_domain, data['alias'], data['destination'])  | 
170 |  | -            return jsonify({'success': success})  | 
 | 124 | + | 
 | 125 | +            success, message = api.delete_forwarder(address)  | 
 | 126 | + | 
 | 127 | +            if success:  | 
 | 128 | +                return jsonify({'success': True, 'message': message})  | 
 | 129 | +            else:  | 
 | 130 | +                return jsonify({'error': message}), 400  | 
 | 131 | + | 
171 | 132 |         except Exception as e:  | 
172 |  | -            print(f"Error creating forwarder: {e}")  | 
 | 133 | +            print(f"Error deleting forwarder: {e}")  | 
173 | 134 |             return jsonify({'error': str(e)}), 500  | 
174 | 135 | 
 
  | 
175 |  | -    @app.route('/api/forwarders/<alias>', methods=['DELETE'])  | 
 | 136 | +    @app.route('/api/email-accounts', methods=['GET'])  | 
176 | 137 |     @login_required  | 
177 |  | -    def delete_forwarder(alias):  | 
 | 138 | +    def get_email_accounts():  | 
 | 139 | +        """Get all email accounts - NO EXTRA ARGUMENTS"""  | 
178 | 140 |         if not current_user.has_da_config():  | 
179 | 141 |             return jsonify({'error': 'DirectAdmin not configured'}), 400  | 
180 | 142 | 
 
  | 
181 | 143 |         try:  | 
182 | 144 |             api = DirectAdminAPI(  | 
183 | 145 |                 current_user.da_server,  | 
184 | 146 |                 current_user.da_username,  | 
185 |  | -                current_user.get_da_password()  | 
 | 147 | +                current_user.get_da_password(),  | 
 | 148 | +                current_user.da_domain  | 
186 | 149 |             )  | 
187 |  | -            success = api.delete_forwarder(current_user.da_domain, alias)  | 
188 |  | -            return jsonify({'success': success})  | 
 | 150 | + | 
 | 151 | +            # Call without extra arguments!  | 
 | 152 | +            accounts = api.get_email_accounts()  # ← No arguments here!  | 
 | 153 | + | 
 | 154 | +            return jsonify({'accounts': accounts})  | 
 | 155 | + | 
189 | 156 |         except Exception as e:  | 
190 |  | -            print(f"Error deleting forwarder: {e}")  | 
 | 157 | +            print(f"Error getting email accounts: {e}")  | 
191 | 158 |             return jsonify({'error': str(e)}), 500  | 
192 | 159 | 
 
  | 
193 |  | -    return app  # Don't forget to return the app!  | 
 | 160 | +    # Error handlers  | 
 | 161 | +    @app.errorhandler(404)  | 
 | 162 | +    def not_found(error):  | 
 | 163 | +        return render_template('404.html'), 404  | 
194 | 164 | 
 
  | 
195 |  | -if __name__ == '__main__':  | 
196 |  | -    app = create_app()  | 
197 |  | -    app.run(debug=True, host='0.0.0.0')  | 
 | 165 | +    @app.errorhandler(500)  | 
 | 166 | +    def internal_error(error):  | 
 | 167 | +        db.session.rollback()  | 
 | 168 | +        return render_template('500.html'), 500  | 
 | 169 | + | 
 | 170 | +    # Create tables  | 
 | 171 | +    with app.app_context():  | 
 | 172 | +        db.create_all()  | 
 | 173 | + | 
 | 174 | +        # Create admin user if doesn't exist  | 
 | 175 | +        admin = User.query.filter_by(username='admin').first()  | 
 | 176 | +        if not admin:  | 
 | 177 | +            admin = User(username='admin', is_admin=True)  | 
 | 178 | +            admin.set_password('admin')  # Change this!  | 
 | 179 | +            db.session.add(admin)  | 
 | 180 | +            db.session.commit()  | 
 | 181 | +            print("Admin user created with password: admin")  | 
 | 182 | + | 
 | 183 | +    return app  | 
0 commit comments