from flask import Flask, request, jsonify from flask_cors import CORS from flask_migrate import Migrate import hashlib import redis import os from app.config import Config from app.models import db, Domain, DNSRecord, CloudflareAccount, User, Customer from app.services.cloudflare_service import CloudflareService # Import blueprints from app.routes.auth import auth_bp from app.routes.admin import admin_bp from app.routes.dns import dns_bp from app.routes.customer import customer_bp app = Flask(__name__) app.config.from_object(Config) # Set ENCRYPTION_KEY environment variable if Config.ENCRYPTION_KEY: os.environ['ENCRYPTION_KEY'] = Config.ENCRYPTION_KEY # Extensions CORS(app) db.init_app(app) migrate = Migrate(app, db) # Register blueprints app.register_blueprint(auth_bp) app.register_blueprint(admin_bp) app.register_blueprint(dns_bp) app.register_blueprint(customer_bp) # Redis redis_client = redis.from_url(Config.REDIS_URL) # Helper Functions def select_lb_ip(domain: str) -> str: """Domain için load balancer IP seç (hash-based)""" hash_value = int(hashlib.md5(domain.encode()).hexdigest(), 16) index = hash_value % len(Config.LB_IPS) return Config.LB_IPS[index] # Routes @app.route('/health', methods=['GET']) def health(): """Health check""" return jsonify({"status": "ok", "service": "hosting-platform-api"}) @app.route('/api/dns/validate-token', methods=['POST']) def validate_cf_token(): """Cloudflare API token doğrula""" data = request.json domain = data.get('domain') cf_token = data.get('cf_token') if not domain or not cf_token: return jsonify({"error": "domain ve cf_token gerekli"}), 400 cf_service = CloudflareService(cf_token) result = cf_service.validate_token_and_get_zone(domain) return jsonify(result) @app.route('/api/dns/preview-changes', methods=['POST']) def preview_changes(): """DNS değişiklik önizlemesi""" data = request.json domain = data.get('domain') zone_id = data.get('zone_id') cf_token = data.get('cf_token') if not all([domain, zone_id, cf_token]): return jsonify({"error": "domain, zone_id ve cf_token gerekli"}), 400 # Load balancer IP seç new_ip = select_lb_ip(domain) cf_service = CloudflareService(cf_token) preview = cf_service.generate_dns_preview(domain, zone_id, new_ip) return jsonify(preview) @app.route('/api/dns/apply-changes', methods=['POST']) def apply_changes(): """DNS değişikliklerini uygula""" data = request.json domain = data.get('domain') zone_id = data.get('zone_id') cf_token = data.get('cf_token') preview = data.get('preview') proxy_enabled = data.get('proxy_enabled', True) customer_id = data.get('customer_id', 1) # Test için if not all([domain, zone_id, cf_token, preview]): return jsonify({"error": "Eksik parametreler"}), 400 cf_service = CloudflareService(cf_token) # DNS değişikliklerini uygula result = cf_service.apply_dns_changes(zone_id, preview, proxy_enabled) if result["status"] == "success": # SSL yapılandır ssl_config = cf_service.configure_ssl(zone_id) # Veritabanına kaydet domain_obj = Domain.query.filter_by(domain_name=domain).first() if not domain_obj: domain_obj = Domain( domain_name=domain, customer_id=customer_id, use_cloudflare=True, cf_zone_id=zone_id, cf_proxy_enabled=proxy_enabled, lb_ip=preview["new_ip"], status="active", dns_configured=True, ssl_configured=len(ssl_config["errors"]) == 0 ) db.session.add(domain_obj) else: domain_obj.cf_zone_id = zone_id domain_obj.cf_proxy_enabled = proxy_enabled domain_obj.lb_ip = preview["new_ip"] domain_obj.status = "active" domain_obj.dns_configured = True domain_obj.ssl_configured = len(ssl_config["errors"]) == 0 db.session.commit() return jsonify({ "status": "success", "dns_result": result, "ssl_config": ssl_config, "domain_id": domain_obj.id }) return jsonify(result), 500 @app.route('/api/domains', methods=['GET']) def list_domains(): """Domain listesi""" customer_id = request.args.get('customer_id', 1, type=int) domains = Domain.query.filter_by(customer_id=customer_id).all() return jsonify([d.to_dict() for d in domains]) @app.route('/api/domains/', methods=['GET']) def get_domain(domain_id): """Domain detayı""" domain = Domain.query.get_or_404(domain_id) return jsonify(domain.to_dict()) @app.route('/webhook/deploy', methods=['POST']) def webhook_deploy(): """Gitea webhook for auto-deployment""" import subprocess import os from datetime import datetime # Get webhook data data = request.json or {} # Log webhook event repo_name = data.get('repository', {}).get('name', 'unknown') pusher = data.get('pusher', {}).get('username', 'unknown') print(f"📥 Webhook received from {repo_name} by {pusher} at {datetime.now()}") # Trigger deployment script in background try: # Run deployment script asynchronously process = subprocess.Popen( ['/opt/hosting-platform/deploy-local.sh'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # Don't wait for completion, return immediately return jsonify({ "status": "success", "message": "Deployment triggered successfully", "repository": repo_name, "pusher": pusher, "timestamp": datetime.now().isoformat(), "note": "Check /var/log/auto-deploy.log for deployment progress" }), 202 # 202 Accepted except FileNotFoundError: return jsonify({ "status": "error", "message": "Deployment script not found at /opt/hosting-platform/deploy-local.sh" }), 500 except Exception as e: return jsonify({ "status": "error", "message": f"Failed to trigger deployment: {str(e)}" }), 500 if __name__ == '__main__': with app.app_context(): db.create_all() app.run(host=Config.API_HOST, port=Config.API_PORT, debug=True)