hosting-platform/backend/app/routes/customer.py

442 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Customer Routes - Domain Management
Customer-specific endpoints with isolation
"""
from flask import Blueprint, request, jsonify
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
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()
# 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_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'
# 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/<int:domain_id>', 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 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]
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')
cf_account_data = None
if cf_account_type == 'company':
# Fetch available CF accounts from Admin Panel
admin_api = AdminAPIService()
# Otomatik hesap seçimi - cf_account_id verilmemişse en uygun hesabı seç
if not cf_account_id:
accounts_result = admin_api.get_available_cf_accounts()
if accounts_result['status'] != 'success' or not accounts_result.get('accounts'):
return jsonify({
'error': 'No available Cloudflare account found. Please contact administrator.'
}), 404
# En az dolu hesabı seç
available_accounts = [
acc for acc in accounts_result['accounts']
if not acc.get('is_full', True)
]
if not available_accounts:
return jsonify({
'error': 'All Cloudflare accounts are full. Please contact administrator.'
}), 404
# En az dolu olanı seç
cf_account_data = min(available_accounts, key=lambda x: x.get('current_domains', 0))
cf_account_id = cf_account_data['id']
else:
# Manuel seçim yapılmışsa o hesabı Admin Panel'den çek
account_result = admin_api.get_cf_account(cf_account_id)
if account_result['status'] != 'success':
return jsonify({
'error': account_result.get('error', 'Cloudflare account not found')
}), 404
cf_account_data = account_result['account']
# Validate account
if cf_account_data.get('is_full', True):
return jsonify({
'error': f'Cloudflare account is full ({cf_account_data.get("max_domains", 0)} domains max)'
}), 400
# 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,
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=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)
db.session.commit()
# 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,
'nameservers': cf_nameservers
}), 201
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@customer_bp.route('/domains/<int:domain_id>', 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/<int:domain_id>', 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
# 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:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@customer_bp.route('/domains/<int:domain_id>/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 from Admin Panel API"""
try:
# Fetch CF accounts from Admin Panel API
admin_api = AdminAPIService()
result = admin_api.get_available_cf_accounts()
if result['status'] == 'error':
return jsonify({
'error': result.get('error', 'Failed to fetch CF accounts from Admin Panel'),
'accounts': [],
'total': 0
}), 500
return jsonify({
'accounts': result.get('accounts', []),
'total': result.get('total', 0)
}), 200
except Exception as e:
return jsonify({
'error': str(e),
'accounts': [],
'total': 0
}), 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