""" Customer Routes - Domain Management Customer-specific endpoints with isolation """ from flask import Blueprint, request, jsonify from app.models.domain import db, Domain, DNSRecord, CloudflareAccount from app.models.user import Customer from app.services.auth_service import token_required from datetime import datetime customer_bp = Blueprint('customer', __name__, url_prefix='/api/customer') @customer_bp.route('/domains', methods=['GET']) @token_required def get_domains(current_user): """Get all domains for the current customer""" try: # Get customer customer = current_user.customer if not customer: return jsonify({'error': 'Customer profile not found'}), 404 # Get domains with customer isolation domains = Domain.query.filter_by(customer_id=customer.id).all() # Add CF account info result = [] for domain in domains: domain_dict = domain.to_dict() # Add CF account name if using company account if domain.cf_account_type == 'company' and domain.cf_account: domain_dict['cf_account_name'] = domain.cf_account.name else: domain_dict['cf_account_name'] = 'Own Account' # Add DNS record count domain_dict['dns_record_count'] = len(domain.dns_records) result.append(domain_dict) return jsonify({ 'domains': result, 'total': len(result), 'limit': customer.max_domains }), 200 except Exception as e: return jsonify({'error': str(e)}), 500 @customer_bp.route('/domains/', methods=['GET']) @token_required def get_domain(current_user, domain_id): """Get specific domain details""" try: customer = current_user.customer if not customer: return jsonify({'error': 'Customer profile not found'}), 404 # Get domain with customer isolation domain = Domain.query.filter_by( id=domain_id, customer_id=customer.id ).first() if not domain: return jsonify({'error': 'Domain not found'}), 404 domain_dict = domain.to_dict() # Add CF account info if domain.cf_account_type == 'company' and domain.cf_account: domain_dict['cf_account_name'] = domain.cf_account.name # Add DNS records domain_dict['dns_records'] = [record.to_dict() for record in domain.dns_records] return jsonify(domain_dict), 200 except Exception as e: return jsonify({'error': str(e)}), 500 @customer_bp.route('/domains', methods=['POST']) @token_required def create_domain(current_user): """Create a new domain""" try: customer = current_user.customer if not customer: return jsonify({'error': 'Customer profile not found'}), 404 data = request.get_json() # Validate required fields if not data.get('domain_name'): return jsonify({'error': 'domain_name is required'}), 400 domain_name = data['domain_name'].lower().strip() # Check domain limit current_count = Domain.query.filter_by(customer_id=customer.id).count() if current_count >= customer.max_domains: return jsonify({ 'error': f'Domain limit reached. Maximum {customer.max_domains} domains allowed.' }), 403 # Check if domain already exists existing = Domain.query.filter_by(domain_name=domain_name).first() if existing: return jsonify({'error': 'Domain already exists'}), 409 # Validate CF account if using company account cf_account_id = data.get('cf_account_id') cf_account_type = data.get('cf_account_type', 'company') if cf_account_type == 'company': if not cf_account_id: return jsonify({'error': 'cf_account_id is required for company account'}), 400 cf_account = CloudflareAccount.query.get(cf_account_id) if not cf_account: return jsonify({'error': 'Cloudflare account not found'}), 404 if not cf_account.is_active: return jsonify({'error': 'Cloudflare account is not active'}), 400 # Check CF account capacity if cf_account.current_domain_count >= cf_account.max_domains: return jsonify({ 'error': f'Cloudflare account is full ({cf_account.max_domains} domains max)' }), 400 # Create domain domain = Domain( domain_name=domain_name, customer_id=customer.id, created_by=current_user.id, project_name=data.get('project_name'), use_cloudflare=data.get('use_cloudflare', True), cf_account_type=cf_account_type, cf_account_id=cf_account_id if cf_account_type == 'company' else None, cf_zone_id=data.get('cf_zone_id'), cf_proxy_enabled=data.get('cf_proxy_enabled', True), status='pending' ) # If using own CF account, save encrypted token if cf_account_type == 'own' and data.get('cf_api_token'): domain.set_cf_api_token(data['cf_api_token']) db.session.add(domain) # Update CF account domain count if using company account if cf_account_type == 'company' and cf_account: cf_account.current_domain_count += 1 db.session.commit() return jsonify({ 'message': 'Domain created successfully', 'domain': domain.to_dict() }), 201 except Exception as e: db.session.rollback() return jsonify({'error': str(e)}), 500 @customer_bp.route('/domains/', methods=['PUT']) @token_required def update_domain(current_user, domain_id): """Update domain""" try: customer = current_user.customer if not customer: return jsonify({'error': 'Customer profile not found'}), 404 # Get domain with customer isolation domain = Domain.query.filter_by( id=domain_id, customer_id=customer.id ).first() if not domain: return jsonify({'error': 'Domain not found'}), 404 data = request.get_json() # Update allowed fields if 'project_name' in data: domain.project_name = data['project_name'] if 'cf_proxy_enabled' in data: domain.cf_proxy_enabled = data['cf_proxy_enabled'] if 'status' in data and data['status'] in ['pending', 'active', 'suspended', 'error']: domain.status = data['status'] domain.updated_at = datetime.utcnow() db.session.commit() return jsonify({ 'message': 'Domain updated successfully', 'domain': domain.to_dict() }), 200 except Exception as e: db.session.rollback() return jsonify({'error': str(e)}), 500 @customer_bp.route('/domains/', methods=['DELETE']) @token_required def delete_domain(current_user, domain_id): """Delete domain""" try: customer = current_user.customer if not customer: return jsonify({'error': 'Customer profile not found'}), 404 # Get domain with customer isolation domain = Domain.query.filter_by( id=domain_id, customer_id=customer.id ).first() if not domain: return jsonify({'error': 'Domain not found'}), 404 # Update CF account count if using company account if domain.cf_account_type == 'company' and domain.cf_account: domain.cf_account.current_domain_count = max(0, domain.cf_account.current_domain_count - 1) db.session.delete(domain) db.session.commit() return jsonify({'message': 'Domain deleted successfully'}), 200 except Exception as e: db.session.rollback() return jsonify({'error': str(e)}), 500 @customer_bp.route('/domains//dns', methods=['GET']) @token_required def get_domain_dns(current_user, domain_id): """Get DNS records for a domain""" try: customer = current_user.customer if not customer: return jsonify({'error': 'Customer profile not found'}), 404 # Get domain with customer isolation domain = Domain.query.filter_by( id=domain_id, customer_id=customer.id ).first() if not domain: return jsonify({'error': 'Domain not found'}), 404 records = [record.to_dict() for record in domain.dns_records] return jsonify({ 'domain': domain.domain_name, 'records': records, 'total': len(records) }), 200 except Exception as e: return jsonify({'error': str(e)}), 500 @customer_bp.route('/cloudflare-accounts', methods=['GET']) @token_required def get_cloudflare_accounts(current_user): """Get available Cloudflare accounts (company accounts only)""" try: # Get active company CF accounts accounts = CloudflareAccount.query.filter_by(is_active=True).all() result = [] for account in accounts: account_dict = account.to_dict(include_token=False) # Calculate available capacity account_dict['available_capacity'] = account.max_domains - account.current_domain_count account_dict['is_full'] = account.current_domain_count >= account.max_domains result.append(account_dict) return jsonify({ 'accounts': result, 'total': len(result) }), 200 except Exception as e: return jsonify({'error': str(e)}), 500 @customer_bp.route('/stats', methods=['GET']) @token_required def get_customer_stats(current_user): """Get customer statistics""" try: customer = current_user.customer if not customer: return jsonify({'error': 'Customer profile not found'}), 404 # Count domains by status total_domains = Domain.query.filter_by(customer_id=customer.id).count() active_domains = Domain.query.filter_by(customer_id=customer.id, status='active').count() pending_domains = Domain.query.filter_by(customer_id=customer.id, status='pending').count() return jsonify({ 'total_domains': total_domains, 'active_domains': active_domains, 'pending_domains': pending_domains, 'max_domains': customer.max_domains, 'available_slots': customer.max_domains - total_domains, 'subscription_plan': customer.subscription_plan }), 200 except Exception as e: return jsonify({'error': str(e)}), 500