remote_service/main.py
ZZY dc052329cb refactor(main): 重构主要逻辑并添加 Certbot 支持
- 移除 Docker 网络检查逻辑
- 添加 Certbot 配置和注册
- 优化错误处理和日志记录
- 重构 Nginx 配置生成和重载逻辑
2025-05-25 16:12:41 +08:00

166 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""main"""
import argparse
from pathlib import Path
import yaml
from src.certbot import CertbotConfigurator, parse_certbot, register_certbot
from src.logger import get_logger, run_cmd_with_log
from src.nginx import NginxConfig, NginxConfigurator
logger = get_logger(__name__)
# 配置常量
FILE_PATH = Path(__file__).parent
NETWORK_NAME = "nginx-net"
def validate_docker_network():
"""检查Docker网络是否存在"""
result = run_cmd_with_log(["docker", "network", "inspect", NETWORK_NAME], logger)
if result.returncode != 0:
logger.error("Docker网络 %s 不存在", NETWORK_NAME)
return False
return True
def parse_compose_config(file_path: Path) -> list[NginxConfig]:
"""解析Docker Compose文件"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
services:dict = config['services']
# networks = config['networks'][NETWORK_NAME]
# if not networks['external']:
# raise ValueError("network 必须为external")
nginx_configs:list[NginxConfig] = []
for name, service in services.items():
try:
labels = service.get('labels')
if labels is not None:
n = labels.get('nginx_prefix_name')
if n is not None:
name = n
conf = NginxConfig(
name = f"{name}.zzyxyz.com",
host = service['container_name'],
port = service['expose'][0]
)
if len(service['expose']) != 1:
raise ValueError("expose 必须为一项")
networks = service.get('networks')
# TODO
if networks is None or networks[0] != NETWORK_NAME:
raise ValueError(f"networks 需要设置为{NETWORK_NAME}")
# required_fields = ['com.lingma.nginx.domain', 'com.lingma.nginx.port']
# for field in required_fields:
# if field not in labels:
# raise ValueError(f"服务 {name} 缺少必要标签: {field}")
nginx_configs.append(conf)
except Exception as e:
logger.error("解析服务 %s 配置失败: %s", name, str(e))
continue
return nginx_configs
except FileNotFoundError:
logger.error("文件不存在: %s", file_path)
return False
except PermissionError:
logger.error("无权限访问文件: %s", file_path)
return False
except yaml.YAMLError as e:
logger.error("YAML解析错误: %s", str(e))
return False
def parse_yaml_config(file_path: Path) -> tuple[list[NginxConfig], list[NginxConfig]]:
"""解析conf.yml配置文件"""
with open(file_path, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
inner_configs = []
for compose_file in config['inner']['compose_file']:
inner_configs.extend(parse_compose_config(Path(compose_file)))
outer_configs = []
for domain, service_config in config['outter']['services'].items():
outer_configs.append(NginxConfig(
name=domain,
host=service_config.get('host', config['outter']['host']),
port=service_config['port'],
web_socket_proxy=service_config.get('web_socket_proxy', False)
))
return inner_configs, outer_configs
def main():
"""main"""
parser = argparse.ArgumentParser(description="Nginx自动化配置工具",
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('-i', '--input', type=str, default='conf.yml',
help='配置文件路径 (默认: conf.yml)')
parser.add_argument('-t', '--type', choices=['all', 'inner', 'outter'], default='all',
help='配置类型选择:\n'
'all - 同时处理内部和外部服务(默认)\n'
'inner - 仅处理Docker compose服务\n'
'outter - 仅处理外部服务')
parser.add_argument('--no-reload', action='store_true',
help='生成配置后禁用立即重载Nginx')
parser.add_argument('--rollback', action='store_true',
help='回滚到最近的有效配置')
parser.add_argument('--dry-run', action='store_true',
help='只生成配置不实际写入')
parser.add_argument('--verbose', action='store_true',
help='显示详细调试信息')
register_certbot(parser)
args = parser.parse_args()
parse_certbot(args)
# 初始化配置器
conf = NginxConfigurator()
# 回滚操作
if args.rollback:
if conf.rollback():
logger.info("回滚成功")
return
# 配置生成
try:
if not validate_docker_network():
logger.error("请先创建Docker网络,否则nginx将会失效: docker network create %s", NETWORK_NAME)
inner, outter = parse_yaml_config(Path(args.input))
# 根据类型选择配置
if args.type == 'inner':
configs = inner
elif args.type == 'outter':
configs = outter
else:
configs = inner + outter
logger.info("共生成 %d 项配置(内部:%d 外部:%d",
len(configs), len(inner), len(outter))
# 备份当前配置
conf.backup_config()
# 预览模式
if args.dry_run:
print(conf.gen_all_config(configs))
return
# 写入配置
if conf.gen_and_save_config(configs):
if not args.no_reload:
conf.safe_reload()
except Exception as e:
logger.critical("流程异常: %s", str(e), exc_info=args.verbose)
if __name__ == "__main__":
main()