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

486 lines
15 KiB
Python
Raw Normal View History

2026-01-11 14:38:39 +00:00
"""
Admin routes - Cloudflare hesap yönetimi ve Customer yönetimi
2026-01-11 14:38:39 +00:00
"""
from flask import Blueprint, request, jsonify
from app.models.domain import db, CloudflareAccount, Domain
from app.models.user import User, Customer
2026-01-11 14:38:39 +00:00
from app.services.cloudflare_service import CloudflareService
from sqlalchemy import func
2026-01-11 14:38:39 +00:00
admin_bp = Blueprint('admin', __name__, url_prefix='/api/admin')
@admin_bp.route('/cf-accounts', methods=['GET'])
def list_cf_accounts():
"""Tüm Cloudflare hesaplarını listele"""
try:
accounts = CloudflareAccount.query.filter_by(is_active=True).all()
return jsonify({
"status": "success",
"accounts": [acc.to_dict(include_token=False) for acc in accounts],
"count": len(accounts)
})
except Exception as e:
return jsonify({
"status": "error",
"message": f"Hesaplar listelenirken hata: {str(e)}"
}), 500
@admin_bp.route('/cf-accounts', methods=['POST'])
def create_cf_account():
"""Yeni Cloudflare hesabı ekle"""
try:
data = request.json
# Validasyon
required_fields = ['name', 'email', 'api_token']
for field in required_fields:
if not data.get(field):
return jsonify({
"status": "error",
"message": f"'{field}' alanı gerekli"
}), 400
# Token'ı doğrula
cf_service = CloudflareService(data['api_token'])
# Basit bir API çağrısı yaparak token'ı test et
try:
zones = cf_service.cf.zones.get(params={'per_page': 1})
# Token geçerli
except Exception as e:
return jsonify({
"status": "error",
"message": f"Cloudflare API token geçersiz: {str(e)}"
}), 400
# Aynı isimde hesap var mı kontrol et
existing = CloudflareAccount.query.filter_by(name=data['name']).first()
if existing:
return jsonify({
"status": "error",
"message": f"'{data['name']}' isimli hesap zaten mevcut"
}), 400
# Yeni hesap oluştur
account = CloudflareAccount(
name=data['name'],
email=data['email'],
max_domains=data.get('max_domains', 100),
notes=data.get('notes', ''),
is_active=True
)
# Token'ı şifrele ve kaydet
account.set_api_token(data['api_token'])
db.session.add(account)
db.session.commit()
return jsonify({
"status": "success",
"message": "Cloudflare hesabı başarıyla eklendi",
"account": account.to_dict(include_token=False)
}), 201
except Exception as e:
db.session.rollback()
return jsonify({
"status": "error",
"message": f"Hesap eklenirken hata: {str(e)}"
}), 500
@admin_bp.route('/cf-accounts/<int:account_id>', methods=['GET'])
def get_cf_account(account_id):
"""Belirli bir Cloudflare hesabını getir"""
try:
account = CloudflareAccount.query.get(account_id)
if not account:
return jsonify({
"status": "error",
"message": "Hesap bulunamadı"
}), 404
# include_token parametresi ile token'ı da döndürebiliriz (sadece admin için)
include_token = request.args.get('include_token', 'false').lower() == 'true'
return jsonify({
"status": "success",
"account": account.to_dict(include_token=include_token)
})
except Exception as e:
return jsonify({
"status": "error",
"message": f"Hesap getirilirken hata: {str(e)}"
}), 500
@admin_bp.route('/cf-accounts/<int:account_id>', methods=['PUT'])
def update_cf_account(account_id):
"""Cloudflare hesabını güncelle"""
try:
account = CloudflareAccount.query.get(account_id)
if not account:
return jsonify({
"status": "error",
"message": "Hesap bulunamadı"
}), 404
data = request.json
# Güncellenebilir alanlar
if 'name' in data:
# Aynı isimde başka hesap var mı?
existing = CloudflareAccount.query.filter(
CloudflareAccount.name == data['name'],
CloudflareAccount.id != account_id
).first()
if existing:
return jsonify({
"status": "error",
"message": f"'{data['name']}' isimli hesap zaten mevcut"
}), 400
account.name = data['name']
if 'email' in data:
account.email = data['email']
if 'api_token' in data:
# Yeni token'ı doğrula
cf_service = CloudflareService(data['api_token'])
try:
zones = cf_service.cf.zones.get(params={'per_page': 1})
except Exception as e:
return jsonify({
"status": "error",
"message": f"Cloudflare API token geçersiz: {str(e)}"
}), 400
account.set_api_token(data['api_token'])
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']
db.session.commit()
return jsonify({
"status": "success",
"message": "Hesap başarıyla güncellendi",
"account": account.to_dict(include_token=False)
})
except Exception as e:
db.session.rollback()
return jsonify({
"status": "error",
"message": f"Hesap güncellenirken hata: {str(e)}"
}), 500
@admin_bp.route('/cf-accounts/<int:account_id>', methods=['DELETE'])
def delete_cf_account(account_id):
"""Cloudflare hesabını sil (soft delete)"""
try:
account = CloudflareAccount.query.get(account_id)
if not account:
return jsonify({
"status": "error",
"message": "Hesap bulunamadı"
}), 404
# Bu hesabı kullanan domain var mı kontrol et
if account.current_domain_count > 0:
return jsonify({
"status": "error",
"message": f"Bu hesap {account.current_domain_count} domain tarafından kullanılıyor. Önce domain'leri başka hesaba taşıyın."
}), 400
# Soft delete (is_active = False)
account.is_active = False
db.session.commit()
return jsonify({
"status": "success",
"message": "Hesap başarıyla devre dışı bırakıldı"
})
except Exception as e:
db.session.rollback()
return jsonify({
"status": "error",
"message": f"Hesap silinirken hata: {str(e)}"
}), 500
@admin_bp.route('/cf-accounts/<int:account_id>/test', methods=['POST'])
def test_cf_account(account_id):
"""Cloudflare hesabının API bağlantısını test et"""
try:
account = CloudflareAccount.query.get(account_id)
if not account:
return jsonify({
"status": "error",
"message": "Hesap bulunamadı"
}), 404
# API token'ı al
api_token = account.get_api_token()
# Cloudflare API'ye bağlan
cf_service = CloudflareService(api_token)
try:
# Zone listesini al (test için)
zones = cf_service.cf.zones.get(params={'per_page': 5})
return jsonify({
"status": "success",
"message": "✅ Cloudflare API bağlantısı başarılı",
"zone_count": len(zones),
"sample_zones": [
{"name": z["name"], "status": z["status"]}
for z in zones[:3]
]
})
except Exception as e:
return jsonify({
"status": "error",
"message": f"❌ Cloudflare API bağlantı hatası: {str(e)}"
}), 400
except Exception as e:
return jsonify({
"status": "error",
"message": f"Test sırasında hata: {str(e)}"
}), 500
# ============================================
# CUSTOMER MANAGEMENT ENDPOINTS
# ============================================
@admin_bp.route('/customers', methods=['GET'])
def list_customers():
"""Tüm müşterileri listele"""
try:
# Query parameters
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 20, type=int)
search = request.args.get('search', '')
# Base query
query = db.session.query(User, Customer).join(Customer, User.id == Customer.user_id)
# Search filter
if search:
query = query.filter(
db.or_(
User.email.ilike(f'%{search}%'),
User.full_name.ilike(f'%{search}%'),
Customer.company_name.ilike(f'%{search}%')
)
)
# Pagination
total = query.count()
results = query.offset((page - 1) * per_page).limit(per_page).all()
# Format response
customers = []
for user, customer in results:
# Get domain count
domain_count = Domain.query.filter_by(customer_id=customer.id).count()
customer_data = {
**user.to_dict(),
**customer.to_dict(),
'domain_count': domain_count
}
customers.append(customer_data)
return jsonify({
"status": "success",
"customers": customers,
"pagination": {
"page": page,
"per_page": per_page,
"total": total,
"pages": (total + per_page - 1) // per_page
}
})
except Exception as e:
return jsonify({
"status": "error",
"message": f"Müşteriler listelenirken hata: {str(e)}"
}), 500
@admin_bp.route('/customers/<int:customer_id>', methods=['GET'])
def get_customer(customer_id):
"""Belirli bir müşteriyi getir"""
try:
customer = Customer.query.get(customer_id)
if not customer:
return jsonify({
"status": "error",
"message": "Müşteri bulunamadı"
}), 404
user = User.query.get(customer.user_id)
# Get domains
domains = Domain.query.filter_by(customer_id=customer.id).all()
return jsonify({
"status": "success",
"customer": {
**user.to_dict(),
**customer.to_dict(),
'domains': [d.to_dict() for d in domains]
}
})
except Exception as e:
return jsonify({
"status": "error",
"message": f"Müşteri getirilirken hata: {str(e)}"
}), 500
@admin_bp.route('/customers/<int:customer_id>/plan', methods=['PUT'])
def update_customer_plan(customer_id):
"""Müşterinin planını güncelle"""
try:
data = request.json
customer = Customer.query.get(customer_id)
if not customer:
return jsonify({
"status": "error",
"message": "Müşteri bulunamadı"
}), 404
# Update plan
if 'subscription_plan' in data:
customer.subscription_plan = data['subscription_plan']
if 'max_domains' in data:
customer.max_domains = data['max_domains']
if 'max_containers' in data:
customer.max_containers = data['max_containers']
if 'subscription_status' in data:
customer.subscription_status = data['subscription_status']
db.session.commit()
return jsonify({
"status": "success",
"message": "Plan başarıyla güncellendi",
"customer": customer.to_dict()
})
except Exception as e:
db.session.rollback()
return jsonify({
"status": "error",
"message": f"Plan güncellenirken hata: {str(e)}"
}), 500
@admin_bp.route('/customers/<int:customer_id>/status', methods=['PUT'])
def update_customer_status(customer_id):
"""Müşteri durumunu güncelle (aktif/pasif)"""
try:
data = request.json
customer = Customer.query.get(customer_id)
if not customer:
return jsonify({
"status": "error",
"message": "Müşteri bulunamadı"
}), 404
user = User.query.get(customer.user_id)
if 'is_active' in data:
user.is_active = data['is_active']
if 'subscription_status' in data:
customer.subscription_status = data['subscription_status']
db.session.commit()
return jsonify({
"status": "success",
"message": "Durum başarıyla güncellendi"
})
except Exception as e:
db.session.rollback()
return jsonify({
"status": "error",
"message": f"Durum güncellenirken hata: {str(e)}"
}), 500
@admin_bp.route('/stats', methods=['GET'])
def get_admin_stats():
"""Admin dashboard istatistikleri"""
try:
total_customers = Customer.query.count()
active_customers = db.session.query(Customer).join(User).filter(User.is_active == True).count()
total_domains = Domain.query.count()
active_domains = Domain.query.filter_by(status='active').count()
# CF accounts stats
total_cf_accounts = CloudflareAccount.query.filter_by(is_active=True).count()
# Subscription breakdown
subscription_stats = db.session.query(
Customer.subscription_plan,
func.count(Customer.id)
).group_by(Customer.subscription_plan).all()
return jsonify({
"status": "success",
"stats": {
"customers": {
"total": total_customers,
"active": active_customers
},
"domains": {
"total": total_domains,
"active": active_domains
},
"cf_accounts": total_cf_accounts,
"subscriptions": {plan: count for plan, count in subscription_stats}
}
})
except Exception as e:
return jsonify({
"status": "error",
"message": f"İstatistikler alınırken hata: {str(e)}"
}), 500