Initial commit: Backend API with Cloudflare integration
This commit is contained in:
commit
0593965305
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
ENV/
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Flask
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Database
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# Node (for frontend)
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Frontend build
|
||||||
|
frontend/dist/
|
||||||
|
frontend/build/
|
||||||
|
|
||||||
|
# SSH Keys
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
hosting_platform_temp
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
backend_main.py
|
||||||
|
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
# 🚀 Hosting Platform - Automated DNS & SSL Management
|
||||||
|
|
||||||
|
Modern, otomatik DNS ve SSL yönetim platformu. Cloudflare entegrasyonu ile domain'leri saniyeler içinde yapılandırın.
|
||||||
|
|
||||||
|
## 📋 Özellikler
|
||||||
|
|
||||||
|
- ✅ **Cloudflare Entegrasyonu**: API token ile otomatik DNS yönetimi
|
||||||
|
- ✅ **DNS Önizleme**: Değişiklikleri uygulamadan önce görüntüleyin
|
||||||
|
- ✅ **Otomatik SSL**: Cloudflare SSL/TLS yapılandırması
|
||||||
|
- ✅ **Load Balancer**: Hash-based IP dağıtımı
|
||||||
|
- ✅ **Modern UI**: React + Vite ile hızlı ve responsive arayüz
|
||||||
|
- ✅ **Auto-Deploy**: Git push ile otomatik deployment
|
||||||
|
- ✅ **PostgreSQL**: Güvenilir veri saklama
|
||||||
|
- ✅ **Redis**: Hızlı cache ve session yönetimi
|
||||||
|
|
||||||
|
## 🏗️ Mimari
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ Cloudflare CDN │
|
||||||
|
│ (SSL/TLS + DDoS Protection) │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ Nginx Reverse Proxy │
|
||||||
|
│ (176.96.129.77 - Load Balancer) │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌──────────────┴──────────────┐
|
||||||
|
▼ ▼
|
||||||
|
┌───────────────┐ ┌───────────────┐
|
||||||
|
│ Frontend │ │ Backend │
|
||||||
|
│ React + Vite │ │ Flask API │
|
||||||
|
│ Port 3001 │ │ Port 5000 │
|
||||||
|
└───────────────┘ └───────────────┘
|
||||||
|
│
|
||||||
|
┌────────────────┴────────────────┐
|
||||||
|
▼ ▼
|
||||||
|
┌──────────────┐ ┌──────────────┐
|
||||||
|
│ PostgreSQL │ │ Redis │
|
||||||
|
│ Port 5432 │ │ Port 6379 │
|
||||||
|
└──────────────┘ └──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ Teknolojiler
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- **Flask 3.0** - Modern Python web framework
|
||||||
|
- **SQLAlchemy 2.0** - ORM
|
||||||
|
- **PostgreSQL 16** - Database
|
||||||
|
- **Redis 7.0** - Cache & Sessions
|
||||||
|
- **Cloudflare API** - DNS & SSL management
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- **React 18** - UI library
|
||||||
|
- **Vite** - Build tool
|
||||||
|
- **TailwindCSS** - Styling
|
||||||
|
- **Axios** - HTTP client
|
||||||
|
|
||||||
|
### DevOps
|
||||||
|
- **Gitea** - Git repository
|
||||||
|
- **Nginx** - Reverse proxy
|
||||||
|
- **Supervisor** - Process management
|
||||||
|
- **Systemd** - Service management
|
||||||
|
|
||||||
|
## 📦 Kurulum
|
||||||
|
|
||||||
|
### Gereksinimler
|
||||||
|
- Ubuntu 22.04+
|
||||||
|
- Python 3.12+
|
||||||
|
- Node.js 18+
|
||||||
|
- PostgreSQL 16
|
||||||
|
- Redis 7.0
|
||||||
|
|
||||||
|
### Backend Kurulumu
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
cp .env.example .env
|
||||||
|
# .env dosyasını düzenleyin
|
||||||
|
|
||||||
|
# Database migration
|
||||||
|
flask db upgrade
|
||||||
|
|
||||||
|
# Başlatma
|
||||||
|
python app/main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Kurulumu
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Yapılandırma
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Database
|
||||||
|
DATABASE_URL=postgresql://user:pass@localhost:5432/hosting_db
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_URL=redis://localhost:6379/0
|
||||||
|
|
||||||
|
# API
|
||||||
|
API_HOST=0.0.0.0
|
||||||
|
API_PORT=5000
|
||||||
|
|
||||||
|
# Load Balancer IPs
|
||||||
|
LB_IPS=176.96.129.77,176.96.129.78,176.96.129.79
|
||||||
|
|
||||||
|
# Secret
|
||||||
|
SECRET_KEY=your-secret-key-here
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 API Endpoints
|
||||||
|
|
||||||
|
### Health Check
|
||||||
|
```bash
|
||||||
|
GET /health
|
||||||
|
```
|
||||||
|
|
||||||
|
### DNS Management
|
||||||
|
```bash
|
||||||
|
POST /api/dns/validate-token
|
||||||
|
POST /api/dns/preview-changes
|
||||||
|
POST /api/dns/apply-changes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Domain Management
|
||||||
|
```bash
|
||||||
|
GET /api/domains
|
||||||
|
GET /api/domains/<id>
|
||||||
|
POST /api/domains
|
||||||
|
PUT /api/domains/<id>
|
||||||
|
DELETE /api/domains/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 Lisans
|
||||||
|
|
||||||
|
MIT License - Detaylar için LICENSE dosyasına bakın.
|
||||||
|
|
||||||
|
## 👨💻 Geliştirici
|
||||||
|
|
||||||
|
Hosting Platform Team
|
||||||
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
# Flask
|
||||||
|
SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key-change-in-production")
|
||||||
|
|
||||||
|
# Database
|
||||||
|
SQLALCHEMY_DATABASE_URI = os.getenv(
|
||||||
|
"DATABASE_URL",
|
||||||
|
"postgresql://hosting:hosting_pass_2024@localhost:5432/hosting"
|
||||||
|
)
|
||||||
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
||||||
|
|
||||||
|
# Load Balancer IPs
|
||||||
|
LB_IPS = os.getenv("LB_IPS", "176.96.129.77").split(",")
|
||||||
|
|
||||||
|
# API
|
||||||
|
API_HOST = os.getenv("API_HOST", "0.0.0.0")
|
||||||
|
API_PORT = int(os.getenv("API_PORT", 5000))
|
||||||
|
|
||||||
|
# Cloudflare Platform Account (opsiyonel)
|
||||||
|
PLATFORM_CF_API_TOKEN = os.getenv("PLATFORM_CF_API_TOKEN")
|
||||||
|
PLATFORM_CF_ACCOUNT_ID = os.getenv("PLATFORM_CF_ACCOUNT_ID")
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from flask_cors import CORS
|
||||||
|
from flask_migrate import Migrate
|
||||||
|
import hashlib
|
||||||
|
import redis
|
||||||
|
|
||||||
|
from app.config import Config
|
||||||
|
from app.models.domain import db, Domain, DNSRecord
|
||||||
|
from app.services.cloudflare_service import CloudflareService
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object(Config)
|
||||||
|
|
||||||
|
# Extensions
|
||||||
|
CORS(app)
|
||||||
|
db.init_app(app)
|
||||||
|
migrate = Migrate(app, db)
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
redis_client = redis.from_url(Config.REDIS_URL)
|
||||||
|
|
||||||
|
|
||||||
|
# Helper Functions
|
||||||
|
def select_lb_ip(domain: str) -> str:
|
||||||
|
"""Domain için load balancer IP seç (hash-based)"""
|
||||||
|
hash_value = int(hashlib.md5(domain.encode()).hexdigest(), 16)
|
||||||
|
index = hash_value % len(Config.LB_IPS)
|
||||||
|
return Config.LB_IPS[index]
|
||||||
|
|
||||||
|
|
||||||
|
# Routes
|
||||||
|
@app.route('/health', methods=['GET'])
|
||||||
|
def health():
|
||||||
|
"""Health check"""
|
||||||
|
return jsonify({"status": "ok", "service": "hosting-platform-api"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/dns/validate-token', methods=['POST'])
|
||||||
|
def validate_cf_token():
|
||||||
|
"""Cloudflare API token doğrula"""
|
||||||
|
data = request.json
|
||||||
|
domain = data.get('domain')
|
||||||
|
cf_token = data.get('cf_token')
|
||||||
|
|
||||||
|
if not domain or not cf_token:
|
||||||
|
return jsonify({"error": "domain ve cf_token gerekli"}), 400
|
||||||
|
|
||||||
|
cf_service = CloudflareService(cf_token)
|
||||||
|
result = cf_service.validate_token_and_get_zone(domain)
|
||||||
|
|
||||||
|
return jsonify(result)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/dns/preview-changes', methods=['POST'])
|
||||||
|
def preview_changes():
|
||||||
|
"""DNS değişiklik önizlemesi"""
|
||||||
|
data = request.json
|
||||||
|
domain = data.get('domain')
|
||||||
|
zone_id = data.get('zone_id')
|
||||||
|
cf_token = data.get('cf_token')
|
||||||
|
|
||||||
|
if not all([domain, zone_id, cf_token]):
|
||||||
|
return jsonify({"error": "domain, zone_id ve cf_token gerekli"}), 400
|
||||||
|
|
||||||
|
# Load balancer IP seç
|
||||||
|
new_ip = select_lb_ip(domain)
|
||||||
|
|
||||||
|
cf_service = CloudflareService(cf_token)
|
||||||
|
preview = cf_service.generate_dns_preview(domain, zone_id, new_ip)
|
||||||
|
|
||||||
|
return jsonify(preview)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/dns/apply-changes', methods=['POST'])
|
||||||
|
def apply_changes():
|
||||||
|
"""DNS değişikliklerini uygula"""
|
||||||
|
data = request.json
|
||||||
|
domain = data.get('domain')
|
||||||
|
zone_id = data.get('zone_id')
|
||||||
|
cf_token = data.get('cf_token')
|
||||||
|
preview = data.get('preview')
|
||||||
|
proxy_enabled = data.get('proxy_enabled', True)
|
||||||
|
customer_id = data.get('customer_id', 1) # Test için
|
||||||
|
|
||||||
|
if not all([domain, zone_id, cf_token, preview]):
|
||||||
|
return jsonify({"error": "Eksik parametreler"}), 400
|
||||||
|
|
||||||
|
cf_service = CloudflareService(cf_token)
|
||||||
|
|
||||||
|
# DNS değişikliklerini uygula
|
||||||
|
result = cf_service.apply_dns_changes(zone_id, preview, proxy_enabled)
|
||||||
|
|
||||||
|
if result["status"] == "success":
|
||||||
|
# SSL yapılandır
|
||||||
|
ssl_config = cf_service.configure_ssl(zone_id)
|
||||||
|
|
||||||
|
# Veritabanına kaydet
|
||||||
|
domain_obj = Domain.query.filter_by(domain_name=domain).first()
|
||||||
|
if not domain_obj:
|
||||||
|
domain_obj = Domain(
|
||||||
|
domain_name=domain,
|
||||||
|
customer_id=customer_id,
|
||||||
|
use_cloudflare=True,
|
||||||
|
cf_zone_id=zone_id,
|
||||||
|
cf_proxy_enabled=proxy_enabled,
|
||||||
|
lb_ip=preview["new_ip"],
|
||||||
|
status="active",
|
||||||
|
dns_configured=True,
|
||||||
|
ssl_configured=len(ssl_config["errors"]) == 0
|
||||||
|
)
|
||||||
|
db.session.add(domain_obj)
|
||||||
|
else:
|
||||||
|
domain_obj.cf_zone_id = zone_id
|
||||||
|
domain_obj.cf_proxy_enabled = proxy_enabled
|
||||||
|
domain_obj.lb_ip = preview["new_ip"]
|
||||||
|
domain_obj.status = "active"
|
||||||
|
domain_obj.dns_configured = True
|
||||||
|
domain_obj.ssl_configured = len(ssl_config["errors"]) == 0
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"status": "success",
|
||||||
|
"dns_result": result,
|
||||||
|
"ssl_config": ssl_config,
|
||||||
|
"domain_id": domain_obj.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify(result), 500
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/domains', methods=['GET'])
|
||||||
|
def list_domains():
|
||||||
|
"""Domain listesi"""
|
||||||
|
customer_id = request.args.get('customer_id', 1, type=int)
|
||||||
|
domains = Domain.query.filter_by(customer_id=customer_id).all()
|
||||||
|
return jsonify([d.to_dict() for d in domains])
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/domains/<int:domain_id>', methods=['GET'])
|
||||||
|
def get_domain(domain_id):
|
||||||
|
"""Domain detayı"""
|
||||||
|
domain = Domain.query.get_or_404(domain_id)
|
||||||
|
return jsonify(domain.to_dict())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
with app.app_context():
|
||||||
|
db.create_all()
|
||||||
|
app.run(host=Config.API_HOST, port=Config.API_PORT, debug=True)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
class Domain(db.Model):
|
||||||
|
__tablename__ = "domains"
|
||||||
|
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
domain_name = db.Column(db.String(255), unique=True, nullable=False, index=True)
|
||||||
|
customer_id = db.Column(db.Integer, nullable=False, index=True)
|
||||||
|
|
||||||
|
# Cloudflare
|
||||||
|
use_cloudflare = db.Column(db.Boolean, default=True)
|
||||||
|
cf_zone_id = db.Column(db.String(255), nullable=True)
|
||||||
|
cf_api_token = db.Column(db.Text, nullable=True) # Encrypted
|
||||||
|
cf_proxy_enabled = db.Column(db.Boolean, default=True)
|
||||||
|
|
||||||
|
# DNS
|
||||||
|
current_ip = db.Column(db.String(45), nullable=True)
|
||||||
|
lb_ip = db.Column(db.String(45), nullable=True)
|
||||||
|
|
||||||
|
# Status
|
||||||
|
status = db.Column(db.String(50), default="pending") # pending, active, error
|
||||||
|
dns_configured = db.Column(db.Boolean, default=False)
|
||||||
|
ssl_configured = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
|
# Timestamps
|
||||||
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||||
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
dns_records = db.relationship("DNSRecord", backref="domain", lazy=True, cascade="all, delete-orphan")
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"domain_name": self.domain_name,
|
||||||
|
"customer_id": self.customer_id,
|
||||||
|
"use_cloudflare": self.use_cloudflare,
|
||||||
|
"cf_zone_id": self.cf_zone_id,
|
||||||
|
"cf_proxy_enabled": self.cf_proxy_enabled,
|
||||||
|
"current_ip": self.current_ip,
|
||||||
|
"lb_ip": self.lb_ip,
|
||||||
|
"status": self.status,
|
||||||
|
"dns_configured": self.dns_configured,
|
||||||
|
"ssl_configured": self.ssl_configured,
|
||||||
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||||
|
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DNSRecord(db.Model):
|
||||||
|
__tablename__ = "dns_records"
|
||||||
|
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
domain_id = db.Column(db.Integer, db.ForeignKey("domains.id"), nullable=False)
|
||||||
|
|
||||||
|
record_type = db.Column(db.String(10), nullable=False) # A, CNAME, MX, TXT, etc.
|
||||||
|
name = db.Column(db.String(255), nullable=False)
|
||||||
|
content = db.Column(db.Text, nullable=False)
|
||||||
|
ttl = db.Column(db.Integer, default=300)
|
||||||
|
proxied = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
|
# Cloudflare
|
||||||
|
cf_record_id = db.Column(db.String(255), nullable=True)
|
||||||
|
|
||||||
|
# Timestamps
|
||||||
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||||
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"domain_id": self.domain_id,
|
||||||
|
"record_type": self.record_type,
|
||||||
|
"name": self.name,
|
||||||
|
"content": self.content,
|
||||||
|
"ttl": self.ttl,
|
||||||
|
"proxied": self.proxied,
|
||||||
|
"cf_record_id": self.cf_record_id,
|
||||||
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||||
|
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,285 @@
|
||||||
|
import hashlib
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
import CloudFlare
|
||||||
|
|
||||||
|
|
||||||
|
class CloudflareService:
|
||||||
|
"""Cloudflare API işlemleri"""
|
||||||
|
|
||||||
|
def __init__(self, api_token: str):
|
||||||
|
self.cf = CloudFlare.CloudFlare(token=api_token)
|
||||||
|
self.api_token = api_token
|
||||||
|
|
||||||
|
def validate_token_and_get_zone(self, domain: str) -> Dict:
|
||||||
|
"""
|
||||||
|
API token doğrula ve zone bilgilerini al
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Zone ara
|
||||||
|
zones = self.cf.zones.get(params={"name": domain})
|
||||||
|
|
||||||
|
if not zones:
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"message": f"{domain} zone bulunamadı. Domain Cloudflare hesabınızda olduğundan emin olun."
|
||||||
|
}
|
||||||
|
|
||||||
|
zone = zones[0]
|
||||||
|
zone_id = zone["id"]
|
||||||
|
|
||||||
|
# Mevcut DNS kayıtlarını al
|
||||||
|
dns_records = self.cf.zones.dns_records.get(zone_id)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"zone_id": zone_id,
|
||||||
|
"zone_name": zone["name"],
|
||||||
|
"zone_status": zone["status"],
|
||||||
|
"nameservers": zone.get("name_servers", []),
|
||||||
|
"account_email": zone.get("account", {}).get("email", "N/A"),
|
||||||
|
"current_dns_records": [
|
||||||
|
{
|
||||||
|
"type": r["type"],
|
||||||
|
"name": r["name"],
|
||||||
|
"content": r["content"],
|
||||||
|
"proxied": r.get("proxied", False),
|
||||||
|
"ttl": r["ttl"],
|
||||||
|
"id": r["id"]
|
||||||
|
}
|
||||||
|
for r in dns_records
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
except CloudFlare.exceptions.CloudFlareAPIError as e:
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"message": f"Cloudflare API hatası: {str(e)}"
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"message": f"Beklenmeyen hata: {str(e)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_dns_preview(self, domain: str, zone_id: str, new_ip: str) -> Dict:
|
||||||
|
"""
|
||||||
|
DNS değişiklik önizlemesi oluştur
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Mevcut A kayıtlarını al
|
||||||
|
dns_records = self.cf.zones.dns_records.get(
|
||||||
|
zone_id,
|
||||||
|
params={"type": "A"}
|
||||||
|
)
|
||||||
|
|
||||||
|
current_root = None
|
||||||
|
current_www = None
|
||||||
|
|
||||||
|
for record in dns_records:
|
||||||
|
if record["name"] == domain:
|
||||||
|
current_root = record
|
||||||
|
elif record["name"] == f"www.{domain}":
|
||||||
|
current_www = record
|
||||||
|
|
||||||
|
# Önizleme oluştur
|
||||||
|
preview = {
|
||||||
|
"domain": domain,
|
||||||
|
"new_ip": new_ip,
|
||||||
|
"changes": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Root domain (@) değişikliği
|
||||||
|
if current_root:
|
||||||
|
preview["changes"].append({
|
||||||
|
"record_type": "A",
|
||||||
|
"name": "@",
|
||||||
|
"current": {
|
||||||
|
"value": current_root["content"],
|
||||||
|
"proxied": current_root.get("proxied", False),
|
||||||
|
"ttl": current_root["ttl"]
|
||||||
|
},
|
||||||
|
"new": {
|
||||||
|
"value": new_ip,
|
||||||
|
"proxied": current_root.get("proxied", True),
|
||||||
|
"ttl": "auto"
|
||||||
|
},
|
||||||
|
"action": "update",
|
||||||
|
"record_id": current_root["id"]
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
preview["changes"].append({
|
||||||
|
"record_type": "A",
|
||||||
|
"name": "@",
|
||||||
|
"current": None,
|
||||||
|
"new": {
|
||||||
|
"value": new_ip,
|
||||||
|
"proxied": True,
|
||||||
|
"ttl": "auto"
|
||||||
|
},
|
||||||
|
"action": "create"
|
||||||
|
})
|
||||||
|
|
||||||
|
# www subdomain değişikliği
|
||||||
|
if current_www:
|
||||||
|
preview["changes"].append({
|
||||||
|
"record_type": "A",
|
||||||
|
"name": "www",
|
||||||
|
"current": {
|
||||||
|
"value": current_www["content"],
|
||||||
|
"proxied": current_www.get("proxied", False),
|
||||||
|
"ttl": current_www["ttl"]
|
||||||
|
},
|
||||||
|
"new": {
|
||||||
|
"value": new_ip,
|
||||||
|
"proxied": current_www.get("proxied", True),
|
||||||
|
"ttl": "auto"
|
||||||
|
},
|
||||||
|
"action": "update",
|
||||||
|
"record_id": current_www["id"]
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
preview["changes"].append({
|
||||||
|
"record_type": "A",
|
||||||
|
"name": "www",
|
||||||
|
"current": None,
|
||||||
|
"new": {
|
||||||
|
"value": new_ip,
|
||||||
|
"proxied": True,
|
||||||
|
"ttl": "auto"
|
||||||
|
},
|
||||||
|
"action": "create"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Diğer kayıtlar (değişmeyecek)
|
||||||
|
all_records = self.cf.zones.dns_records.get(zone_id)
|
||||||
|
other_records = [
|
||||||
|
r for r in all_records
|
||||||
|
if r["type"] != "A" or (r["name"] != domain and r["name"] != f"www.{domain}")
|
||||||
|
]
|
||||||
|
|
||||||
|
preview["preserved_records"] = [
|
||||||
|
{
|
||||||
|
"type": r["type"],
|
||||||
|
"name": r["name"],
|
||||||
|
"content": r["content"]
|
||||||
|
}
|
||||||
|
for r in other_records[:10] # İlk 10 kayıt
|
||||||
|
]
|
||||||
|
|
||||||
|
preview["preserved_count"] = len(other_records)
|
||||||
|
|
||||||
|
return preview
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"message": f"Önizleme oluşturma hatası: {str(e)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply_dns_changes(self, zone_id: str, preview: Dict, proxy_enabled: bool = True) -> Dict:
|
||||||
|
"""
|
||||||
|
DNS değişikliklerini uygula
|
||||||
|
"""
|
||||||
|
results = {
|
||||||
|
"domain": preview["domain"],
|
||||||
|
"applied_changes": [],
|
||||||
|
"errors": []
|
||||||
|
}
|
||||||
|
|
||||||
|
for change in preview["changes"]:
|
||||||
|
try:
|
||||||
|
if change["action"] == "update":
|
||||||
|
# Mevcut kaydı güncelle
|
||||||
|
self.cf.zones.dns_records.patch(
|
||||||
|
zone_id,
|
||||||
|
change["record_id"],
|
||||||
|
data={
|
||||||
|
"type": "A",
|
||||||
|
"name": change["name"],
|
||||||
|
"content": change["new"]["value"],
|
||||||
|
"proxied": proxy_enabled,
|
||||||
|
"ttl": 1 if proxy_enabled else 300
|
||||||
|
}
|
||||||
|
)
|
||||||
|
results["applied_changes"].append({
|
||||||
|
"name": change["name"],
|
||||||
|
"action": "updated",
|
||||||
|
"new_value": change["new"]["value"]
|
||||||
|
})
|
||||||
|
|
||||||
|
elif change["action"] == "create":
|
||||||
|
# Yeni kayıt oluştur
|
||||||
|
self.cf.zones.dns_records.post(
|
||||||
|
zone_id,
|
||||||
|
data={
|
||||||
|
"type": "A",
|
||||||
|
"name": change["name"],
|
||||||
|
"content": change["new"]["value"],
|
||||||
|
"proxied": proxy_enabled,
|
||||||
|
"ttl": 1 if proxy_enabled else 300
|
||||||
|
}
|
||||||
|
)
|
||||||
|
results["applied_changes"].append({
|
||||||
|
"name": change["name"],
|
||||||
|
"action": "created",
|
||||||
|
"new_value": change["new"]["value"]
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
results["errors"].append({
|
||||||
|
"name": change["name"],
|
||||||
|
"error": str(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
if results["errors"]:
|
||||||
|
results["status"] = "partial"
|
||||||
|
else:
|
||||||
|
results["status"] = "success"
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def configure_ssl(self, zone_id: str) -> Dict:
|
||||||
|
"""
|
||||||
|
Cloudflare SSL ayarlarını yapılandır
|
||||||
|
"""
|
||||||
|
ssl_config = {
|
||||||
|
"steps": [],
|
||||||
|
"errors": []
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1. SSL/TLS Mode: Full (strict)
|
||||||
|
self.cf.zones.settings.ssl.patch(zone_id, data={"value": "full"})
|
||||||
|
ssl_config["steps"].append({"name": "ssl_mode", "status": "success", "value": "full"})
|
||||||
|
except Exception as e:
|
||||||
|
ssl_config["errors"].append({"step": "ssl_mode", "error": str(e)})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 2. Always Use HTTPS
|
||||||
|
self.cf.zones.settings.always_use_https.patch(zone_id, data={"value": "on"})
|
||||||
|
ssl_config["steps"].append({"name": "always_https", "status": "success"})
|
||||||
|
except Exception as e:
|
||||||
|
ssl_config["errors"].append({"step": "always_https", "error": str(e)})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 3. Automatic HTTPS Rewrites
|
||||||
|
self.cf.zones.settings.automatic_https_rewrites.patch(zone_id, data={"value": "on"})
|
||||||
|
ssl_config["steps"].append({"name": "auto_https_rewrites", "status": "success"})
|
||||||
|
except Exception as e:
|
||||||
|
ssl_config["errors"].append({"step": "auto_https_rewrites", "error": str(e)})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 4. Minimum TLS Version
|
||||||
|
self.cf.zones.settings.min_tls_version.patch(zone_id, data={"value": "1.2"})
|
||||||
|
ssl_config["steps"].append({"name": "min_tls", "status": "success", "value": "1.2"})
|
||||||
|
except Exception as e:
|
||||||
|
ssl_config["errors"].append({"step": "min_tls", "error": str(e)})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 5. TLS 1.3
|
||||||
|
self.cf.zones.settings.tls_1_3.patch(zone_id, data={"value": "on"})
|
||||||
|
ssl_config["steps"].append({"name": "tls_1_3", "status": "success"})
|
||||||
|
except Exception as e:
|
||||||
|
ssl_config["errors"].append({"step": "tls_1_3", "error": str(e)})
|
||||||
|
|
||||||
|
return ssl_config
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Web Framework
|
||||||
|
Flask==3.0.0
|
||||||
|
Flask-CORS==4.0.0
|
||||||
|
Flask-SQLAlchemy==3.1.1
|
||||||
|
Flask-Migrate==4.0.5
|
||||||
|
|
||||||
|
# Database
|
||||||
|
psycopg2-binary==2.9.9
|
||||||
|
SQLAlchemy==2.0.23
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
redis==5.0.1
|
||||||
|
|
||||||
|
# Cloudflare
|
||||||
|
cloudflare==2.19.4
|
||||||
|
requests==2.31.0
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
python-dotenv==1.0.0
|
||||||
|
pydantic==2.5.2
|
||||||
|
python-dateutil==2.8.2
|
||||||
|
|
||||||
|
# Security
|
||||||
|
cryptography==41.0.7
|
||||||
|
|
||||||
|
# Development
|
||||||
|
pytest==7.4.3
|
||||||
|
pytest-cov==4.1.0
|
||||||
|
black==23.12.1
|
||||||
|
flake8==6.1.0
|
||||||
Loading…
Reference in New Issue