feat: Auto-create Cloudflare zone on domain creation

- Add create_zone() method to CloudflareService
- Fetch CF account API token from Admin Panel
- Create zone in Cloudflare when using company account
- Return nameservers in domain creation response
- Handle zone creation errors gracefully
This commit is contained in:
oguz ozturk 2026-01-12 17:29:05 +03:00
parent 0b2920e43f
commit 61dc963b28
2 changed files with 153 additions and 22 deletions

View File

@ -3,7 +3,7 @@ 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.domain import db, Domain, DNSRecord
from app.models.user import Customer
from app.services.auth_service import token_required
from app.services.admin_api_service import AdminAPIService
@ -25,14 +25,34 @@ def get_domains(current_user):
# Get domains with customer isolation
domains = Domain.query.filter_by(customer_id=customer.id).all()
# Fetch CF account names from Admin Panel (batch)
cf_account_names = {}
if domains:
# Get unique CF account IDs
cf_account_ids = set(
d.cf_account_id for d in domains
if d.cf_account_type == 'company' and d.cf_account_id
)
if cf_account_ids:
admin_api = AdminAPIService()
accounts_result = admin_api.get_available_cf_accounts()
if accounts_result['status'] == 'success':
for acc in accounts_result.get('accounts', []):
cf_account_names[acc['id']] = acc['name']
# 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
if domain.cf_account_type == 'company' and domain.cf_account_id:
domain_dict['cf_account_name'] = cf_account_names.get(
domain.cf_account_id,
f'CF Account #{domain.cf_account_id}'
)
else:
domain_dict['cf_account_name'] = 'Own Account'
@ -71,9 +91,17 @@ def get_domain(current_user, domain_id):
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 CF account info from Admin Panel
if domain.cf_account_type == 'company' and domain.cf_account_id:
admin_api = AdminAPIService()
account_result = admin_api.get_cf_account(domain.cf_account_id)
if account_result['status'] == 'success':
domain_dict['cf_account_name'] = account_result['account']['name']
else:
domain_dict['cf_account_name'] = f'CF Account #{domain.cf_account_id}'
else:
domain_dict['cf_account_name'] = 'Own Account'
# Add DNS records
domain_dict['dns_records'] = [record.to_dict() for record in domain.dns_records]
@ -162,7 +190,41 @@ def create_domain(current_user):
'error': f'Cloudflare account is full ({cf_account_data.get("max_domains", 0)} domains max)'
}), 400
# Create domain
# Create zone in Cloudflare if using company account
cf_zone_id = None
cf_nameservers = []
cf_api_token = None
if cf_account_type == 'company' and cf_account_id:
# Get CF account with API token from Admin Panel
admin_api = AdminAPIService()
account_result = admin_api.get_cf_account(cf_account_id)
if account_result['status'] != 'success':
return jsonify({
'error': f"Failed to get CF account: {account_result.get('error')}"
}), 500
cf_api_token = account_result['account'].get('api_token')
if not cf_api_token:
return jsonify({
'error': 'CF account API token not found'
}), 500
# Create zone in Cloudflare
from app.services.cloudflare_service import CloudflareService
cf_service = CloudflareService(cf_api_token)
zone_result = cf_service.create_zone(domain_name)
if zone_result['status'] != 'success':
return jsonify({
'error': f"Failed to create Cloudflare zone: {zone_result.get('message', 'Unknown error')}"
}), 500
cf_zone_id = zone_result['zone_id']
cf_nameservers = zone_result.get('nameservers', [])
# Create domain in database
domain = Domain(
domain_name=domain_name,
customer_id=customer.id,
@ -171,7 +233,7 @@ def create_domain(current_user):
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_zone_id=cf_zone_id,
cf_proxy_enabled=data.get('cf_proxy_enabled', True),
status='pending'
)
@ -183,13 +245,21 @@ def create_domain(current_user):
db.session.add(domain)
db.session.commit()
# Note: CF account domain count is managed in Admin Panel database
# We don't update it here since we're using separate databases
# Increment domain count in Admin Panel if using company account
if cf_account_type == 'company' and cf_account_id:
admin_api = AdminAPIService()
increment_result = admin_api.increment_domain_count(cf_account_id)
if increment_result['status'] != 'success':
# Log warning but don't fail the request
# Domain is already created in customer DB
print(f"Warning: Failed to increment domain count in Admin Panel: {increment_result.get('error')}")
return jsonify({
'message': 'Domain created successfully',
'domain': domain.to_dict(),
'cf_account_name': cf_account_data.get('name') if cf_account_data else None
'cf_account_name': cf_account_data.get('name') if cf_account_data else None,
'nameservers': cf_nameservers
}), 201
except Exception as e:
@ -258,12 +328,24 @@ def delete_domain(current_user, domain_id):
if not domain:
return jsonify({'error': 'Domain not found'}), 404
# Note: CF account domain count is managed in Admin Panel database
# We don't update it here since we're using separate databases
# Store CF account info before deletion
cf_account_id = domain.cf_account_id
cf_account_type = domain.cf_account_type
# Delete domain
db.session.delete(domain)
db.session.commit()
# Decrement domain count in Admin Panel if using company account
if cf_account_type == 'company' and cf_account_id:
admin_api = AdminAPIService()
decrement_result = admin_api.decrement_domain_count(cf_account_id)
if decrement_result['status'] != 'success':
# Log warning but don't fail the request
# Domain is already deleted from customer DB
print(f"Warning: Failed to decrement domain count in Admin Panel: {decrement_result.get('error')}")
return jsonify({'message': 'Domain deleted successfully'}), 200
except Exception as e:

View File

@ -10,6 +10,55 @@ class CloudflareService:
self.cf = CloudFlare.CloudFlare(token=api_token)
self.api_token = api_token
def create_zone(self, domain: str) -> Dict:
"""
Create a new zone in Cloudflare
Returns zone_id and nameservers
"""
try:
# Create zone
zone_data = {
'name': domain,
'jump_start': True # Auto-scan DNS records
}
zone = self.cf.zones.post(data=zone_data)
return {
'status': 'success',
'zone_id': zone['id'],
'zone_name': zone['name'],
'nameservers': zone.get('name_servers', []),
'zone_status': zone.get('status', 'pending'),
'created_on': zone.get('created_on')
}
except CloudFlare.exceptions.CloudFlareAPIError as e:
error_code = e.code if hasattr(e, 'code') else 0
error_message = str(e)
# Handle specific errors
if error_code == 1061: # Zone already exists
return {
'status': 'error',
'error': 'zone_exists',
'message': f'Zone {domain} already exists in this Cloudflare account'
}
return {
'status': 'error',
'error': 'cloudflare_api_error',
'message': f'Cloudflare API error: {error_message}',
'code': error_code
}
except Exception as e:
return {
'status': 'error',
'error': 'unexpected_error',
'message': f'Unexpected error: {str(e)}'
}
def validate_token_and_get_zone(self, domain: str) -> Dict:
"""
API token doğrula ve zone bilgilerini al