feat: Integrate Admin Panel API for CF account management

- Add AdminAPIService to fetch CF accounts from Admin Panel
- Update customer routes to use Admin Panel API instead of local DB
- Add ADMIN_API_URL and ADMIN_API_INTERNAL_KEY config
- Remove local CF account count management (managed in Admin Panel)
- Support automatic CF account selection from Admin Panel
This commit is contained in:
oguz ozturk 2026-01-12 16:57:46 +03:00
parent 7f564c7638
commit c28642cc6e
3 changed files with 179 additions and 44 deletions

View File

@ -27,6 +27,10 @@ class Config:
# Encryption (for sensitive data like API tokens)
ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY")
# Admin Panel API (for fetching CF accounts)
ADMIN_API_URL = os.getenv("ADMIN_API_URL", "http://localhost:5001")
ADMIN_API_INTERNAL_KEY = os.getenv("ADMIN_API_INTERNAL_KEY", "internal-api-key-change-in-production")
# Cloudflare Platform Account (opsiyonel - deprecated, use database instead)
PLATFORM_CF_API_TOKEN = os.getenv("PLATFORM_CF_API_TOKEN")
PLATFORM_CF_ACCOUNT_ID = os.getenv("PLATFORM_CF_ACCOUNT_ID")

View File

@ -6,6 +6,7 @@ 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 app.services.admin_api_service import AdminAPIService
from datetime import datetime
customer_bp = Blueprint('customer', __name__, url_prefix='/api/customer')
@ -115,42 +116,50 @@ def create_domain(current_user):
# 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 = None
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:
# En az dolu, aktif ve verification için kullanılabilir hesabı seç
cf_account = CloudflareAccount.query.filter(
CloudflareAccount.is_active == True,
CloudflareAccount.use_for_verification == True,
CloudflareAccount.current_domain_count < CloudflareAccount.max_domains
).order_by(
CloudflareAccount.current_domain_count.asc()
).first()
accounts_result = admin_api.get_available_cf_accounts()
if not cf_account:
if accounts_result['status'] != 'success' or not accounts_result.get('accounts'):
return jsonify({
'error': 'No available Cloudflare account found. Please contact administrator.'
}), 404
cf_account_id = cf_account.id
# 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ı kullan
cf_account = CloudflareAccount.query.get(cf_account_id)
if not cf_account:
return jsonify({'error': 'Cloudflare account not found'}), 404
# Manuel seçim yapılmışsa o hesabı Admin Panel'den çek
account_result = admin_api.get_cf_account(cf_account_id)
if not cf_account.is_active:
return jsonify({'error': 'Cloudflare account is not active'}), 400
if account_result['status'] != 'success':
return jsonify({
'error': account_result.get('error', 'Cloudflare account not found')
}), 404
if not cf_account.use_for_verification:
return jsonify({'error': 'This Cloudflare account is not available for domain verification'}), 400
cf_account_data = account_result['account']
# Check CF account capacity
if cf_account.current_domain_count >= cf_account.max_domains:
# Validate account
if cf_account_data.get('is_full', True):
return jsonify({
'error': f'Cloudflare account is full ({cf_account.max_domains} domains max)'
'error': f'Cloudflare account is full ({cf_account_data.get("max_domains", 0)} domains max)'
}), 400
# Create domain
@ -172,16 +181,15 @@ def create_domain(current_user):
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()
# Note: CF account domain count is managed in Admin Panel database
# We don't update it here since we're using separate databases
return jsonify({
'message': 'Domain created successfully',
'domain': domain.to_dict()
'domain': domain.to_dict(),
'cf_account_name': cf_account_data.get('name') if cf_account_data else None
}), 201
except Exception as e:
@ -250,9 +258,8 @@ def delete_domain(current_user, domain_id):
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)
# Note: CF account domain count is managed in Admin Panel database
# We don't update it here since we're using separate databases
db.session.delete(domain)
db.session.commit()
@ -297,26 +304,30 @@ def get_domain_dns(current_user, domain_id):
@customer_bp.route('/cloudflare-accounts', methods=['GET'])
@token_required
def get_cloudflare_accounts(current_user):
"""Get available Cloudflare accounts (company accounts only)"""
"""Get available Cloudflare accounts from Admin Panel API"""
try:
# Get active company CF accounts
accounts = CloudflareAccount.query.filter_by(is_active=True).all()
# Fetch CF accounts from Admin Panel API
admin_api = AdminAPIService()
result = admin_api.get_available_cf_accounts()
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)
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,
'total': len(result)
'accounts': result.get('accounts', []),
'total': result.get('total', 0)
}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
return jsonify({
'error': str(e),
'accounts': [],
'total': 0
}), 500
@customer_bp.route('/stats', methods=['GET'])

View File

@ -0,0 +1,120 @@
"""
Admin Panel API Service
Handles communication with Admin Panel API to fetch CF accounts
"""
import requests
from typing import Dict, List, Optional
from app.config import Config
class AdminAPIService:
"""Service to communicate with Admin Panel API"""
def __init__(self):
self.base_url = Config.ADMIN_API_URL
self.api_key = Config.ADMIN_API_INTERNAL_KEY
self.headers = {
'X-Internal-API-Key': self.api_key,
'Content-Type': 'application/json'
}
def get_available_cf_accounts(self) -> Dict:
"""
Fetch available CF accounts from Admin Panel
Returns:
{
"status": "success" | "error",
"accounts": [...],
"total": int,
"error": str (if error)
}
"""
try:
url = f"{self.base_url}/api/cf-accounts/internal/available"
response = requests.get(url, headers=self.headers, timeout=10)
if response.status_code == 200:
return response.json()
else:
return {
'status': 'error',
'error': f'Admin API returned {response.status_code}: {response.text}',
'accounts': [],
'total': 0
}
except requests.exceptions.Timeout:
return {
'status': 'error',
'error': 'Admin API request timeout',
'accounts': [],
'total': 0
}
except requests.exceptions.ConnectionError:
return {
'status': 'error',
'error': 'Cannot connect to Admin API',
'accounts': [],
'total': 0
}
except Exception as e:
return {
'status': 'error',
'error': f'Admin API error: {str(e)}',
'accounts': [],
'total': 0
}
def get_cf_account(self, account_id: int) -> Dict:
"""
Fetch specific CF account with API token from Admin Panel
Args:
account_id: CF account ID
Returns:
{
"status": "success" | "error",
"account": {...},
"error": str (if error)
}
"""
try:
url = f"{self.base_url}/api/cf-accounts/internal/{account_id}"
response = requests.get(url, headers=self.headers, timeout=10)
if response.status_code == 200:
return response.json()
elif response.status_code == 404:
return {
'status': 'error',
'error': 'CF account not found'
}
elif response.status_code == 403:
return {
'status': 'error',
'error': 'CF account not available for verification'
}
else:
return {
'status': 'error',
'error': f'Admin API returned {response.status_code}: {response.text}'
}
except requests.exceptions.Timeout:
return {
'status': 'error',
'error': 'Admin API request timeout'
}
except requests.exceptions.ConnectionError:
return {
'status': 'error',
'error': 'Cannot connect to Admin API'
}
except Exception as e:
return {
'status': 'error',
'error': f'Admin API error: {str(e)}'
}