admin-panel/backend/app/routes/cf_accounts.py

257 lines
8.1 KiB
Python
Raw Normal View History

"""
Cloudflare Accounts Management Routes
"""
from flask import Blueprint, request, jsonify
from app.models import db, CloudflareAccount, AuditLog
from app.routes.auth import token_required
from app.config import Config
from functools import wraps
cf_accounts_bp = Blueprint('cf_accounts', __name__)
def internal_api_required(f):
"""Decorator for internal API endpoints (requires internal API key)"""
@wraps(f)
def decorated_function(*args, **kwargs):
api_key = request.headers.get('X-Internal-API-Key')
if not api_key or api_key != Config.CUSTOMER_API_INTERNAL_KEY:
return jsonify({'error': 'Unauthorized - Invalid internal API key'}), 401
return f(*args, **kwargs)
return decorated_function
@cf_accounts_bp.route('', methods=['GET'])
@token_required
def get_cf_accounts(current_admin):
"""Get all CF accounts"""
try:
accounts = CloudflareAccount.query.order_by(CloudflareAccount.created_at.desc()).all()
return jsonify({
'status': 'success',
'accounts': [acc.to_dict() for acc in accounts]
}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@cf_accounts_bp.route('/<int:account_id>', methods=['GET'])
@token_required
def get_cf_account(current_admin, account_id):
"""Get single CF account"""
try:
account = CloudflareAccount.query.get(account_id)
if not account:
return jsonify({'error': 'Account not found'}), 404
return jsonify({
'status': 'success',
'account': account.to_dict(include_token=True)
}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@cf_accounts_bp.route('', methods=['POST'])
@token_required
def create_cf_account(current_admin):
"""Create new CF account"""
try:
data = request.get_json()
required = ['name', 'email', 'api_token']
for field in required:
if not data.get(field):
return jsonify({'error': f'{field} is required'}), 400
account = CloudflareAccount(
name=data['name'],
email=data['email'],
api_token=data['api_token'], # TODO: Encrypt this
max_domains=data.get('max_domains', 100),
notes=data.get('notes'),
is_active=data.get('is_active', True),
use_for_verification=data.get('use_for_verification', True)
)
db.session.add(account)
db.session.commit()
# Log action
log = AuditLog(
admin_id=current_admin.id,
action='create_cf_account',
resource_type='cf_account',
resource_id=account.id,
details={'account_name': account.name},
ip_address=request.remote_addr
)
db.session.add(log)
db.session.commit()
return jsonify({
'status': 'success',
'message': 'CF account created successfully',
'account': account.to_dict()
}), 201
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@cf_accounts_bp.route('/<int:account_id>', methods=['PUT'])
@token_required
def update_cf_account(current_admin, account_id):
"""Update CF account"""
try:
account = CloudflareAccount.query.get(account_id)
if not account:
return jsonify({'error': 'Account not found'}), 404
data = request.get_json()
if 'name' in data:
account.name = data['name']
if 'email' in data:
account.email = data['email']
if 'api_token' in data and data['api_token']:
account.api_token = data['api_token'] # TODO: Encrypt
if 'max_domains' in data:
account.max_domains = data['max_domains']
if 'notes' in data:
account.notes = data['notes']
if 'is_active' in data:
account.is_active = data['is_active']
if 'use_for_verification' in data:
account.use_for_verification = data['use_for_verification']
db.session.commit()
# Log action
log = AuditLog(
admin_id=current_admin.id,
action='update_cf_account',
resource_type='cf_account',
resource_id=account.id,
details={'account_name': account.name},
ip_address=request.remote_addr
)
db.session.add(log)
db.session.commit()
return jsonify({
'status': 'success',
'message': 'CF account updated successfully',
'account': account.to_dict()
}), 200
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@cf_accounts_bp.route('/<int:account_id>', methods=['DELETE'])
@token_required
def delete_cf_account(current_admin, account_id):
"""Delete CF account"""
try:
account = CloudflareAccount.query.get(account_id)
if not account:
return jsonify({'error': 'Account not found'}), 404
if account.current_domains > 0:
return jsonify({'error': 'Cannot delete account with active domains'}), 400
account_name = account.name
db.session.delete(account)
db.session.commit()
# Log action
log = AuditLog(
admin_id=current_admin.id,
action='delete_cf_account',
resource_type='cf_account',
resource_id=account_id,
details={'account_name': account_name},
ip_address=request.remote_addr
)
db.session.add(log)
db.session.commit()
return jsonify({
'status': 'success',
'message': 'CF account deleted successfully'
}), 200
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
# Internal API Endpoints (for Customer Panel)
@cf_accounts_bp.route('/internal/available', methods=['GET'])
@internal_api_required
def get_available_cf_accounts_internal():
"""
Internal API endpoint for Customer Panel to fetch available CF accounts
Requires X-Internal-API-Key header
"""
try:
# Get active CF accounts that are enabled for verification
accounts = CloudflareAccount.query.filter_by(
is_active=True,
use_for_verification=True
).order_by(CloudflareAccount.created_at.desc()).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_domains
account_dict['is_full'] = account.current_domains >= account.max_domains
result.append(account_dict)
return jsonify({
'status': 'success',
'accounts': result,
'total': len(result)
}), 200
except Exception as e:
return jsonify({
'status': 'error',
'error': str(e)
}), 500
@cf_accounts_bp.route('/internal/<int:account_id>', methods=['GET'])
@internal_api_required
def get_cf_account_internal(account_id):
"""
Internal API endpoint to get specific CF account with API token
Requires X-Internal-API-Key header
"""
try:
account = CloudflareAccount.query.get(account_id)
if not account:
return jsonify({
'status': 'error',
'error': 'Account not found'
}), 404
if not account.is_active or not account.use_for_verification:
return jsonify({
'status': 'error',
'error': 'Account not available for verification'
}), 403
# Return account with API token (for internal use)
account_dict = account.to_dict(include_token=True)
return jsonify({
'status': 'success',
'account': account_dict
}), 200
except Exception as e:
return jsonify({
'status': 'error',
'error': str(e)
}), 500