299 lines
11 KiB
Python
299 lines
11 KiB
Python
"""Domains API routes for AnchorHost."""
|
|
|
|
|
|
from domain_runtime import domain_manager
|
|
from domain_service import create_domain_record, delete_domain_record, get_domain_by_id, get_user_domains, link_domain_to_server, unlink_domain_from_server
|
|
from flask import Blueprint, jsonify, request
|
|
from runtime_config import (
|
|
logger,
|
|
)
|
|
from server_records import get_server_by_id
|
|
from user_service import authenticate_user, get_bearer_token, require_admin
|
|
from websocket_service import sync_user_domains_with_dns
|
|
|
|
__all__ = ['create_domain_api', 'delete_domain_api', 'fix_all_dnssec_api', 'get_domains_api', 'link_domain_api', 'sync_domains_api', 'test_domain_api', 'unlink_domain_api', 'verify_dnssec_api']
|
|
|
|
|
|
|
|
bp = Blueprint("domains", __name__)
|
|
|
|
|
|
@bp.route('/api/dnssec/fix-all', methods=['POST'])
|
|
def fix_all_dnssec_api():
|
|
"""Fix DNSSEC delegation for all user domains"""
|
|
try:
|
|
token = get_bearer_token()
|
|
user = authenticate_user(token)
|
|
|
|
# Only allow admin users
|
|
if not user or user.get('role') != 'admin':
|
|
return jsonify({'error': 'Admin access required'}), 403
|
|
|
|
if not domain_manager:
|
|
return jsonify({'error': 'Domain manager not available'}), 503
|
|
|
|
# Run the fix
|
|
domain_manager.fix_all_dnssec_delegations()
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': 'DNSSEC fix process initiated. Check logs for details.'
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error fixing DNSSEC: {e}")
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@bp.route('/api/dnssec/verify/<username>', methods=['GET'])
|
|
def verify_dnssec_api(username):
|
|
"""Verify DNSSEC delegation for a user"""
|
|
try:
|
|
token = get_bearer_token()
|
|
user = authenticate_user(token)
|
|
|
|
# Users can only check their own DNSSEC, admins can check any
|
|
if not user or (user.get('role') != 'admin' and user.get('username') != username):
|
|
return jsonify({'error': 'Unauthorized'}), 403
|
|
|
|
if not domain_manager:
|
|
return jsonify({'error': 'Domain manager not available'}), 503
|
|
|
|
result = domain_manager.setup_dnssec_delegation(username)
|
|
|
|
return jsonify(result), 200 if result.get('success') else 500
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error verifying DNSSEC: {e}")
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@bp.route('/api/domains', methods=['GET'])
|
|
def get_domains_api():
|
|
"""Get domains API endpoint"""
|
|
try:
|
|
token = get_bearer_token()
|
|
user = authenticate_user(token)
|
|
if not user:
|
|
return jsonify({'error': 'Unauthorized'}), 401
|
|
|
|
include_serverless = request.args.get('include_serverless', 'true').lower() == 'true'
|
|
domains = get_user_domains(user['id'], include_serverless)
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'domains': domains,
|
|
'count': len(domains)
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting domains: {e}")
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@bp.route('/api/domains/sync', methods=['POST'])
|
|
def sync_domains_api():
|
|
"""Sync domains between database and DNS"""
|
|
try:
|
|
token = get_bearer_token()
|
|
user = authenticate_user(token)
|
|
if not user:
|
|
return jsonify({'error': 'Unauthorized'}), 401
|
|
|
|
success = sync_user_domains_with_dns(user['id'])
|
|
|
|
if success:
|
|
domains = get_user_domains(user['id'], True)
|
|
return jsonify({
|
|
'success': True,
|
|
'message': 'Domains synced successfully',
|
|
'domains': domains,
|
|
'count': len(domains)
|
|
}), 200
|
|
else:
|
|
return jsonify({'error': 'Failed to sync domains'}), 500
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error syncing domains: {e}")
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@bp.route('/api/domains', methods=['POST'])
|
|
def create_domain_api():
|
|
"""Create domain API endpoint"""
|
|
try:
|
|
token = get_bearer_token()
|
|
user = authenticate_user(token)
|
|
if not user:
|
|
return jsonify({'error': 'Unauthorized'}), 401
|
|
|
|
if not domain_manager:
|
|
return jsonify({'error': 'Domain service not available'}), 503
|
|
|
|
data = request.get_json()
|
|
subdomain = data.get('subdomain')
|
|
server_id = data.get('serverId')
|
|
|
|
if not subdomain:
|
|
return jsonify({'error': 'Subdomain name required'}), 400
|
|
|
|
# Check if subdomain is available
|
|
availability_result = domain_manager.check_domain_availability(subdomain, user['username'])
|
|
if not availability_result.get('available', False):
|
|
return jsonify({'error': availability_result.get('message', 'Domain not available')}), 400
|
|
|
|
# Create the subdomain
|
|
result = domain_manager.create_subdomain(user['username'], subdomain)
|
|
|
|
if result['success'] and result.get('domain_data'):
|
|
# Save to database
|
|
domain_record = {
|
|
'user_id': user['id'],
|
|
'subdomain': result['domain_data']['subdomain'],
|
|
'full_domain': result['domain_data']['domain'],
|
|
'record_type': result['domain_data']['record_type'],
|
|
'ip_address': result['domain_data']['ip_address'],
|
|
'ttl': result['domain_data']['ttl'],
|
|
'server_id': server_id,
|
|
'created_at': result['domain_data']['created_at']
|
|
}
|
|
|
|
db_domain = create_domain_record(domain_record)
|
|
|
|
if db_domain:
|
|
return jsonify({
|
|
'success': True,
|
|
'domain': db_domain,
|
|
'message': result['message']
|
|
}), 201
|
|
else:
|
|
return jsonify({'error': 'Domain created in DNS but failed to save to database'}), 500
|
|
else:
|
|
return jsonify({'error': result.get('message', 'Failed to create domain')}), 500
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error creating domain: {e}")
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@bp.route('/api/domains/<domain_id>', methods=['DELETE'])
|
|
def delete_domain_api(domain_id):
|
|
"""Delete domain API endpoint"""
|
|
try:
|
|
token = get_bearer_token()
|
|
user = authenticate_user(token)
|
|
if not user:
|
|
return jsonify({'error': 'Unauthorized'}), 401
|
|
|
|
if not domain_manager:
|
|
return jsonify({'error': 'Domain service not available'}), 503
|
|
|
|
# Get domain from database
|
|
domain = get_domain_by_id(domain_id)
|
|
if not domain or domain['user_id'] != user['id']:
|
|
return jsonify({'error': 'Domain not found or access denied'}), 404
|
|
|
|
# Delete from DNS
|
|
result = domain_manager.delete_subdomain(user['username'], domain['subdomain'])
|
|
|
|
if result['success']:
|
|
# Delete from database
|
|
if delete_domain_record(domain_id):
|
|
return jsonify({
|
|
'success': True,
|
|
'message': result['message']
|
|
}), 200
|
|
else:
|
|
return jsonify({'error': 'Domain deleted from DNS but failed to delete from database'}), 500
|
|
else:
|
|
return jsonify({'error': result.get('message', 'Failed to delete domain')}), 500
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error deleting domain: {e}")
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@bp.route('/api/domains/<domain_id>/link', methods=['POST'])
|
|
def link_domain_api(domain_id):
|
|
"""Link domain to server API endpoint"""
|
|
try:
|
|
token = get_bearer_token()
|
|
user = authenticate_user(token)
|
|
if not user:
|
|
return jsonify({'error': 'Unauthorized'}), 401
|
|
|
|
data = request.get_json()
|
|
server_id = data.get('serverId')
|
|
|
|
if not server_id:
|
|
return jsonify({'error': 'Server ID required'}), 400
|
|
|
|
# Verify domain belongs to user
|
|
domain = get_domain_by_id(domain_id)
|
|
if not domain or domain['user_id'] != user['id']:
|
|
return jsonify({'error': 'Domain not found or access denied'}), 404
|
|
|
|
# Verify server belongs to user
|
|
server = get_server_by_id(server_id)
|
|
if not server or server['owner_id'] != user['id']:
|
|
return jsonify({'error': 'Server not found or access denied'}), 404
|
|
|
|
# Check if server is a website or VPS
|
|
if server['type'] not in ['website', 'vps']:
|
|
return jsonify({'error': 'Domain can only be linked to Website or VPS servers'}), 400
|
|
|
|
# Link domain to server (this will create IngressRoute)
|
|
if link_domain_to_server(domain_id, server_id):
|
|
return jsonify({
|
|
'success': True,
|
|
'message': f'Domain linked to server {server["name"]} with IngressRoute created'
|
|
}), 200
|
|
else:
|
|
return jsonify({'error': 'Failed to link domain'}), 500
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error linking domain: {e}")
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@bp.route('/api/domains/<domain_id>/unlink', methods=['POST'])
|
|
def unlink_domain_api(domain_id):
|
|
"""Unlink domain from server API endpoint"""
|
|
try:
|
|
token = get_bearer_token()
|
|
user = authenticate_user(token)
|
|
if not user:
|
|
return jsonify({'error': 'Unauthorized'}), 401
|
|
|
|
# Verify domain belongs to user
|
|
domain = get_domain_by_id(domain_id)
|
|
if not domain or domain['user_id'] != user['id']:
|
|
return jsonify({'error': 'Domain not found or access denied'}), 404
|
|
|
|
# Unlink domain from server (this will delete IngressRoute)
|
|
if unlink_domain_from_server(domain_id):
|
|
return jsonify({
|
|
'success': True,
|
|
'message': 'Domain unlinked from server and IngressRoute removed'
|
|
}), 200
|
|
else:
|
|
return jsonify({'error': 'Failed to unlink domain'}), 500
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error unlinking domain: {e}")
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@bp.route('/api/domains/test', methods=['GET'])
|
|
def test_domain_api():
|
|
"""Test domain API connection"""
|
|
if not require_admin():
|
|
return jsonify({'error': 'Unauthorized'}), 403
|
|
try:
|
|
if not domain_manager:
|
|
return jsonify({'success': False, 'message': 'Domain manager not initialized'}), 503
|
|
|
|
success = domain_manager.test_connection()
|
|
|
|
return jsonify({
|
|
'success': success,
|
|
'message': 'Domain API connection test completed',
|
|
'connected': success
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error testing domain API: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|