初始化1
This commit is contained in:
619
nginx-python-api/app.py
Normal file
619
nginx-python-api/app.py
Normal file
@@ -0,0 +1,619 @@
|
||||
"""
|
||||
Nginx域名转发管理 - Python API 主入口
|
||||
部署在 Nginx 服务器 (10.1.1.160:5000) 上
|
||||
直接操作本机 Nginx 配置和证书
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
from flask import Flask, request, g
|
||||
from services.nginx_service import NginxService
|
||||
from services.ssl_service import SSLService
|
||||
from services.dns_service import DnsService
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['JSON_AS_ASCII'] = False # 中文不转 Unicode
|
||||
|
||||
# ════════════════════════════════════════════
|
||||
# 网关用户信息中间件
|
||||
# ════════════════════════════════════════════
|
||||
USER_HEADERS = [
|
||||
'X-User-Id', 'X-User-Name', 'X-User-Account',
|
||||
'X-User-Email', 'X-User-Post', 'X-User-Roles',
|
||||
'X-System-Id', 'X-System-Code', 'X-System-Name',
|
||||
]
|
||||
|
||||
|
||||
@app.before_request
|
||||
def user_context_middleware():
|
||||
"""从网关注入的请求头提取用户信息,存到 Flask g 对象"""
|
||||
for h in USER_HEADERS:
|
||||
val = request.headers.get(h)
|
||||
if val:
|
||||
setattr(g, h.replace('-', '_').lower(), val)
|
||||
|
||||
# 初始化服务
|
||||
nginx_conf_dir = os.environ.get('NGINX_CONF_DIR', '/etc/nginx/conf.d')
|
||||
nginx_ssl_dir = os.environ.get('NGINX_SSL_DIR', '/etc/nginx/ssl')
|
||||
ca_dir = os.environ.get('CA_DIR', '/etc/nginx/ssl/ca')
|
||||
|
||||
nginx_service = NginxService(nginx_conf_dir, nginx_ssl_dir)
|
||||
ssl_service = SSLService(ca_dir, nginx_ssl_dir)
|
||||
dns_service = DnsService()
|
||||
|
||||
# 启动时初始化内部 CA(gunicorn 模式下也必须执行)
|
||||
ssl_service.init_ca()
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# API 路由
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
|
||||
@app.route('/api/nginx/health', methods=['GET'])
|
||||
def health_check():
|
||||
"""健康检查"""
|
||||
return {
|
||||
'code': 0,
|
||||
'message': 'Nginx Manager API is running',
|
||||
'data': {
|
||||
'nginx_installed': os.path.exists('/usr/sbin/nginx') or os.path.exists('/usr/bin/nginx'),
|
||||
'conf_dir': nginx_conf_dir,
|
||||
'ssl_dir': nginx_ssl_dir
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules', methods=['GET'])
|
||||
def list_rules():
|
||||
"""获取所有转发规则列表"""
|
||||
try:
|
||||
rules = nginx_service.list_rules()
|
||||
return {'code': 0, 'message': 'success', 'data': rules}
|
||||
except Exception as e:
|
||||
logger.error(f"获取规则列表失败: {e}")
|
||||
return {'code': -1, 'message': str(e), 'data': []}
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules', methods=['POST'])
|
||||
def add_rule():
|
||||
"""新增转发规则"""
|
||||
try:
|
||||
body = request.get_json(force=True)
|
||||
domain = body.get('domain', '').strip()
|
||||
protocol = body.get('protocol', '').strip().lower()
|
||||
target = body.get('target', '').strip()
|
||||
|
||||
# 参数校验
|
||||
if not domain:
|
||||
return {'code': -1, 'message': '域名不能为空'}, 400
|
||||
if protocol not in ('http', 'https'):
|
||||
return {'code': -1, 'message': '协议类型必须为 http 或 https'}, 400
|
||||
if not target.startswith('http://') and not target.startswith('https://'):
|
||||
return {'code': -1, 'message': '转发目标必须以 http:// 或 https:// 开头'}, 400
|
||||
|
||||
# 系统保护域名不允许手动创建(zgitm.com / www.zgitm.com)
|
||||
if ssl_service.is_system_domain(domain):
|
||||
return {'code': -1, 'message': f'{domain} 是系统保护域名,不能手动创建'}, 400
|
||||
|
||||
logger.info(f"新增规则: domain={domain}, protocol={protocol}, target={target}")
|
||||
|
||||
# 检查是否已存在
|
||||
existing = nginx_service.find_rule(domain)
|
||||
if existing:
|
||||
return {'code': -1, 'message': f'域名 {domain} 的规则已存在'}, 409
|
||||
|
||||
# HTTPS证书签发
|
||||
ssl_cert = None
|
||||
ssl_key = None
|
||||
if protocol == 'https':
|
||||
if ssl_service.is_zgitm_domain(domain):
|
||||
# zgitm.com 子域名 → Let's Encrypt DNS-01
|
||||
ssl_cert, ssl_key = ssl_service.ensure_le_certificate(domain)
|
||||
if not ssl_cert or not ssl_key:
|
||||
return {'code': -1, 'message': f'{domain} Let\'s Encrypt 证书签发失败,请查看服务器日志'}, 500
|
||||
else:
|
||||
# 其他域名 → 内部 CA
|
||||
ssl_cert, ssl_key = ssl_service.ensure_certificate(domain)
|
||||
|
||||
# 创建 Nginx 配置文件
|
||||
config_file = nginx_service.create_config(domain, protocol, target, ssl_cert, ssl_key)
|
||||
|
||||
# 检查 Nginx 配置语法
|
||||
valid, error_msg = nginx_service.check_config()
|
||||
if not valid:
|
||||
# 回滚:删除刚创建的配置文件
|
||||
nginx_service.delete_config(domain)
|
||||
logger.error(f"Nginx 配置语法错误: {error_msg}")
|
||||
return {'code': -1, 'message': f'Nginx 配置语法检查失败: {error_msg}'}, 400
|
||||
|
||||
return {
|
||||
'code': 0,
|
||||
'message': '规则创建成功',
|
||||
'data': {
|
||||
'domain': domain,
|
||||
'protocol': protocol,
|
||||
'target': target,
|
||||
'config_file': config_file,
|
||||
'ssl_cert': ssl_cert,
|
||||
'ssl_key': ssl_key
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"新增规则失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules/<domain>', methods=['GET'])
|
||||
def get_rule(domain):
|
||||
"""获取单个规则详情"""
|
||||
try:
|
||||
rule = nginx_service.find_rule(domain)
|
||||
if rule:
|
||||
return {'code': 0, 'message': 'success', 'data': rule}
|
||||
return {'code': -1, 'message': f'域名 {domain} 的规则不存在'}, 404
|
||||
except Exception as e:
|
||||
logger.error(f"获取规则详情失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules/<domain>', methods=['PUT'])
|
||||
def update_rule(domain):
|
||||
"""修改转发规则"""
|
||||
try:
|
||||
body = request.get_json(force=True)
|
||||
new_domain = body.get('domain', '').strip()
|
||||
new_protocol = body.get('protocol', '').strip().lower()
|
||||
new_target = body.get('target', '').strip()
|
||||
|
||||
if not new_domain:
|
||||
return {'code': -1, 'message': '域名不能为空'}, 400
|
||||
if new_protocol not in ('http', 'https'):
|
||||
return {'code': -1, 'message': '协议类型必须为 http 或 https'}, 400
|
||||
if not new_target.startswith('http://') and not new_target.startswith('https://'):
|
||||
return {'code': -1, 'message': '转发目标必须以 http:// 或 https:// 开头'}, 400
|
||||
|
||||
logger.info(f"修改规则: domain={domain} -> new_domain={new_domain}")
|
||||
|
||||
# 检查原规则是否存在
|
||||
old_rule = nginx_service.find_rule(domain)
|
||||
if not old_rule:
|
||||
return {'code': -1, 'message': f'域名 {domain} 的规则不存在'}, 404
|
||||
|
||||
# 删除旧配置
|
||||
nginx_service.delete_config(domain)
|
||||
|
||||
# HTTPS证书签发
|
||||
ssl_cert = None
|
||||
ssl_key = None
|
||||
if new_protocol == 'https':
|
||||
if ssl_service.is_zgitm_domain(new_domain):
|
||||
ssl_cert, ssl_key = ssl_service.ensure_le_certificate(new_domain)
|
||||
if not ssl_cert or not ssl_key:
|
||||
return {'code': -1, 'message': f'{new_domain} Let\'s Encrypt 证书签发失败'}, 500
|
||||
else:
|
||||
ssl_cert, ssl_key = ssl_service.ensure_certificate(new_domain)
|
||||
|
||||
# 创建新配置
|
||||
config_file = nginx_service.create_config(new_domain, new_protocol, new_target, ssl_cert, ssl_key)
|
||||
|
||||
# 语法检查
|
||||
valid, error_msg = nginx_service.check_config()
|
||||
if not valid:
|
||||
nginx_service.delete_config(new_domain)
|
||||
# 恢复旧配置
|
||||
old_proto = old_rule.get('protocol', 'http')
|
||||
old_cert = old_rule.get('ssl_cert')
|
||||
old_key = old_rule.get('ssl_key')
|
||||
nginx_service.create_config(domain, old_proto, old_rule.get('target', ''), old_cert, old_key)
|
||||
logger.error(f"Nginx 配置语法错误: {error_msg}")
|
||||
return {'code': -1, 'message': f'Nginx 配置语法检查失败: {error_msg}'}, 400
|
||||
|
||||
return {
|
||||
'code': 0,
|
||||
'message': '规则修改成功',
|
||||
'data': {
|
||||
'domain': new_domain,
|
||||
'protocol': new_protocol,
|
||||
'target': new_target,
|
||||
'config_file': config_file,
|
||||
'ssl_cert': ssl_cert,
|
||||
'ssl_key': ssl_key
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"修改规则失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules/<domain>', methods=['DELETE'])
|
||||
def delete_rule(domain):
|
||||
"""删除转发规则"""
|
||||
try:
|
||||
logger.info(f"删除规则: domain={domain}")
|
||||
|
||||
rule = nginx_service.find_rule(domain)
|
||||
|
||||
# 删除配置文件(如果存在)
|
||||
deleted = nginx_service.delete_config(domain)
|
||||
|
||||
# 文件不存在但数据库有记录 → 允许继续(清理脏数据)
|
||||
if not rule and not deleted:
|
||||
logger.info(f"域名 {domain} 的规则文件不存在,跳过删除")
|
||||
|
||||
# 检查语法(删除后)
|
||||
valid, error_msg = nginx_service.check_config()
|
||||
if not valid:
|
||||
logger.warning(f"删除后 Nginx 配置语法错误: {error_msg}")
|
||||
|
||||
return {'code': 0, 'message': '规则已删除', 'data': {'domain': domain}}
|
||||
except Exception as e:
|
||||
logger.error(f"删除规则失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 配置文件查看/编辑接口
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules/<domain>/config', methods=['GET'])
|
||||
def get_config(domain):
|
||||
"""获取指定域名的 Nginx 配置文件原始内容"""
|
||||
try:
|
||||
import os as _os
|
||||
config_path = _os.path.join(nginx_conf_dir, f'{domain}.conf')
|
||||
if not _os.path.exists(config_path):
|
||||
return {'code': -1, 'message': f'{domain} 的配置文件不存在', 'data': None}, 404
|
||||
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
return {
|
||||
'code': 0,
|
||||
'message': 'success',
|
||||
'data': {
|
||||
'domain': domain,
|
||||
'config_file': config_path,
|
||||
'content': content
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"获取配置文件失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules/<domain>/config', methods=['PUT'])
|
||||
def update_config(domain):
|
||||
"""直接写入 Nginx 配置文件(会进行语法检查,失败自动回滚)"""
|
||||
try:
|
||||
body = request.get_json(force=True)
|
||||
new_content = body.get('content', '')
|
||||
|
||||
if not new_content.strip():
|
||||
return {'code': -1, 'message': '配置内容不能为空'}, 400
|
||||
|
||||
import os as _os
|
||||
config_path = _os.path.join(nginx_conf_dir, f'{domain}.conf')
|
||||
old_content = None
|
||||
old_exists = _os.path.exists(config_path)
|
||||
|
||||
# 备份旧内容(如果存在)
|
||||
if old_exists:
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
old_content = f.read()
|
||||
|
||||
# 写入新内容
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
logger.info(f"已写入配置文件: {config_path}")
|
||||
|
||||
# 语法检查
|
||||
valid, error_msg = nginx_service.check_config()
|
||||
if not valid:
|
||||
# 回滚
|
||||
if old_exists:
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
f.write(old_content)
|
||||
else:
|
||||
_os.remove(config_path)
|
||||
logger.error(f"Nginx 配置语法错误,已回滚: {error_msg}")
|
||||
return {'code': -1, 'message': f'配置语法检查失败,已自动回滚: {error_msg}'}, 400
|
||||
|
||||
# 重载 Nginx
|
||||
nginx_service.reload()
|
||||
logger.info(f"Nginx 配置已重载: {domain}")
|
||||
|
||||
return {
|
||||
'code': 0,
|
||||
'message': '配置已保存并重载生效',
|
||||
'data': {'domain': domain, 'config_file': config_path}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"更新配置文件失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules/<domain>/ssl', methods=['GET'])
|
||||
def get_ssl_files(domain):
|
||||
"""获取指定域名的 SSL 证书和密钥文件内容(仅查看,不可编辑)"""
|
||||
try:
|
||||
import os as _os
|
||||
cert_path = _os.path.join(nginx_ssl_dir, f'{domain}.pem')
|
||||
key_path = _os.path.join(nginx_ssl_dir, f'{domain}.key')
|
||||
|
||||
cert_content = None
|
||||
key_content = None
|
||||
|
||||
if _os.path.exists(cert_path):
|
||||
with open(cert_path, 'r', encoding='utf-8') as f:
|
||||
cert_content = f.read()
|
||||
if _os.path.exists(key_path):
|
||||
with open(key_path, 'r', encoding='utf-8') as f:
|
||||
key_content = f.read()
|
||||
|
||||
if not cert_content and not key_content:
|
||||
return {'code': -1, 'message': f'{domain} 没有 SSL 证书文件', 'data': None}, 404
|
||||
|
||||
return {
|
||||
'code': 0,
|
||||
'message': 'success',
|
||||
'data': {
|
||||
'domain': domain,
|
||||
'cert_path': cert_path,
|
||||
'cert_content': cert_content,
|
||||
'key_path': key_path,
|
||||
'key_content': key_content,
|
||||
'has_cert': bool(cert_content),
|
||||
'has_key': bool(key_content)
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"获取SSL证书文件失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 内容压缩(gzip)接口
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules/<domain>/gzip', methods=['GET'])
|
||||
def get_gzip_config(domain):
|
||||
"""获取指定域名的 gzip 压缩配置"""
|
||||
try:
|
||||
gzip_config = nginx_service.parse_gzip_config(domain)
|
||||
return {'code': 0, 'message': 'success', 'data': gzip_config}
|
||||
except Exception as e:
|
||||
logger.error(f"获取gzip配置失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules/<domain>/gzip', methods=['PUT'])
|
||||
def update_gzip_config(domain):
|
||||
"""更新指定域名的 gzip 压缩配置"""
|
||||
try:
|
||||
body = request.get_json(force=True)
|
||||
enabled = body.get('enabled', False)
|
||||
types = body.get('types', [])
|
||||
comp_level = body.get('comp_level', 6)
|
||||
min_length = body.get('min_length', '1000')
|
||||
|
||||
# 参数校验
|
||||
if enabled and not isinstance(types, list):
|
||||
return {'code': -1, 'message': 'types 必须是数组'}, 400
|
||||
if enabled and comp_level not in range(1, 10):
|
||||
return {'code': -1, 'message': '压缩级别必须在 1-9 之间'}, 400
|
||||
if enabled and not min_length:
|
||||
return {'code': -1, 'message': '最小压缩长度不能为空'}, 400
|
||||
|
||||
gzip_config = {
|
||||
'enabled': enabled,
|
||||
'types': types,
|
||||
'comp_level': comp_level,
|
||||
'min_length': min_length
|
||||
}
|
||||
|
||||
success, message = nginx_service.set_gzip_config(domain, gzip_config)
|
||||
if success:
|
||||
return {'code': 0, 'message': message, 'data': gzip_config}
|
||||
return {'code': -1, 'message': message}, 400
|
||||
except Exception as e:
|
||||
logger.error(f"更新gzip配置失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 域名日志接口
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
|
||||
@app.route('/api/nginx/rules/<domain>/logs', methods=['GET'])
|
||||
def get_domain_logs(domain):
|
||||
"""获取指定域名的 Nginx 访问日志和错误日志"""
|
||||
try:
|
||||
lines = request.args.get('lines', 200, type=int)
|
||||
# 限制最大行数
|
||||
lines = min(lines, 1000)
|
||||
logs = nginx_service.get_domain_logs(domain, lines=lines)
|
||||
return {'code': 0, 'message': 'success', 'data': logs}
|
||||
except Exception as e:
|
||||
logger.error(f"获取域名日志失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/reload', methods=['POST'])
|
||||
def reload_nginx():
|
||||
"""重载 Nginx 配置"""
|
||||
try:
|
||||
logger.info("重载 Nginx 配置")
|
||||
success, message = nginx_service.reload()
|
||||
if success:
|
||||
return {'code': 0, 'message': message}
|
||||
return {'code': -1, 'message': message}, 500
|
||||
except Exception as e:
|
||||
logger.error(f"重载 Nginx 失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 证书管理接口(Let's Encrypt)
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
@app.route('/api/nginx/certs', methods=['GET'])
|
||||
def list_certs():
|
||||
"""获取所有 SSL 证书信息列表(Let's Encrypt + 内部 CA)"""
|
||||
try:
|
||||
certs = []
|
||||
# 扫描 SSL 目录中所有的 .pem 证书文件
|
||||
import glob as _glob
|
||||
# 要排除的文件(CA 根证书等)
|
||||
exclude_files = {'ca.pem', 'ca.crt'}
|
||||
for cert_file in _glob.glob(f'{nginx_ssl_dir}/*.pem'):
|
||||
filename = os.path.basename(cert_file)
|
||||
if filename in exclude_files:
|
||||
continue
|
||||
domain = filename.replace('.pem', '')
|
||||
info = ssl_service.get_le_cert_info(domain)
|
||||
certs.append(info)
|
||||
return {'code': 0, 'message': 'success', 'data': certs}
|
||||
except Exception as e:
|
||||
logger.error(f"获取证书列表失败: {e}")
|
||||
return {'code': -1, 'message': str(e), 'data': []}
|
||||
|
||||
|
||||
@app.route('/api/nginx/certs/<domain>', methods=['GET'])
|
||||
def get_cert_info(domain):
|
||||
"""获取单个 SSL 证书详情(Let's Encrypt + 内部 CA)"""
|
||||
try:
|
||||
info = ssl_service.get_le_cert_info(domain)
|
||||
return {'code': 0, 'message': 'success', 'data': info}
|
||||
except Exception as e:
|
||||
logger.error(f"获取证书信息失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/certs/<domain>/issue', methods=['POST'])
|
||||
def issue_cert(domain):
|
||||
"""首次签发或重新签发 Let's Encrypt 证书"""
|
||||
try:
|
||||
if not ssl_service.is_zgitm_domain(domain):
|
||||
return {'code': -1, 'message': f'{domain} 不是 zgitm.com 域名'}, 400
|
||||
|
||||
logger.info(f"签发 LE 证书: {domain}")
|
||||
cert_path, key_path = ssl_service.ensure_le_certificate(domain)
|
||||
|
||||
if cert_path and key_path:
|
||||
# 获取最新证书信息
|
||||
info = ssl_service.get_le_cert_info(domain)
|
||||
return {'code': 0, 'message': f'{domain} 证书签发成功', 'data': info}
|
||||
else:
|
||||
return {'code': -1, 'message': f'{domain} 证书签发失败,请检查阿里云 AccessKey 和网络'}, 500
|
||||
except Exception as e:
|
||||
logger.error(f"签发证书失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/certs/<domain>/renew', methods=['POST'])
|
||||
def renew_cert(domain):
|
||||
"""手动续期 Let's Encrypt 证书"""
|
||||
try:
|
||||
if not ssl_service.is_zgitm_domain(domain):
|
||||
return {'code': -1, 'message': f'{domain} 不是 zgitm.com 域名'}, 400
|
||||
|
||||
logger.info(f"手动续期 LE 证书: {domain}")
|
||||
result = ssl_service.renew_le_certificate(domain)
|
||||
|
||||
if result['success']:
|
||||
return {'code': 0, 'message': result['message'], 'data': {'log': result['log']}}
|
||||
else:
|
||||
return {'code': -1, 'message': result['message'], 'data': {'log': result['log']}}, 500
|
||||
except Exception as e:
|
||||
logger.error(f"续期证书失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# DNS 解析管理接口
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
@app.route('/api/nginx/dns/records', methods=['GET'])
|
||||
def list_dns_records():
|
||||
"""获取 DNS 解析记录列表"""
|
||||
try:
|
||||
zone = request.args.get('zone', ssl_service.get_le_root_domain())
|
||||
rr = request.args.get('rr', None)
|
||||
rtype = request.args.get('type', None)
|
||||
records = dns_service.list_records(zone, rr_keyword=rr, record_type=rtype)
|
||||
return {'code': 0, 'message': 'success', 'data': records}
|
||||
except Exception as e:
|
||||
logger.error(f"获取DNS记录失败: {e}")
|
||||
return {'code': -1, 'message': str(e), 'data': []}
|
||||
|
||||
|
||||
@app.route('/api/nginx/dns/records', methods=['POST'])
|
||||
def add_dns_record():
|
||||
"""添加 DNS 解析记录"""
|
||||
try:
|
||||
body = request.get_json(force=True)
|
||||
zone = body.get('zone', ssl_service.get_le_root_domain())
|
||||
rr = body.get('rr', '').strip()
|
||||
record_type = body.get('type', 'A').upper()
|
||||
value = body.get('value', '').strip()
|
||||
ttl = body.get('ttl', 600)
|
||||
|
||||
if not rr:
|
||||
return {'code': -1, 'message': '主机记录(rr)不能为空'}, 400
|
||||
if not value:
|
||||
return {'code': -1, 'message': '记录值(value)不能为空'}, 400
|
||||
|
||||
result = dns_service.add_record(zone, rr, record_type, value, ttl)
|
||||
return {'code': 0, 'message': f'{result["full_domain"]} 记录已添加', 'data': result}
|
||||
except Exception as e:
|
||||
logger.error(f"添加DNS记录失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/dns/records/<record_id>', methods=['DELETE'])
|
||||
def delete_dns_record(record_id):
|
||||
"""删除 DNS 解析记录"""
|
||||
try:
|
||||
dns_service.delete_record(record_id)
|
||||
return {'code': 0, 'message': 'DNS记录已删除'}
|
||||
except Exception as e:
|
||||
logger.error(f"删除DNS记录失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
@app.route('/api/nginx/dns/records/<record_id>', methods=['PUT'])
|
||||
def update_dns_record(record_id):
|
||||
"""修改 DNS 解析记录"""
|
||||
try:
|
||||
body = request.get_json(force=True)
|
||||
rr = body.get('rr', '@')
|
||||
record_type = body.get('type', 'A').upper()
|
||||
value = body.get('value', '').strip()
|
||||
ttl = body.get('ttl', 600)
|
||||
|
||||
if not value:
|
||||
return {'code': -1, 'message': '记录值(value)不能为空'}, 400
|
||||
|
||||
dns_service.update_record(record_id, rr, record_type, value, ttl)
|
||||
return {'code': 0, 'message': 'DNS记录已更新'}
|
||||
except Exception as e:
|
||||
logger.error(f"修改DNS记录失败: {e}")
|
||||
return {'code': -1, 'message': str(e)}, 500
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 启动入口
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger.info("Nginx Manager API 启动在端口 5000")
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||
Reference in New Issue
Block a user