feat:自动更换ip+流量监控
This commit is contained in:
152
services/ip_change.py
Normal file
152
services/ip_change.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""IP 更换核心逻辑"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from database import Config, ProxyMachine, get_session, ensure_singleton_config
|
||||
from services.aws_eip import create_ec2_client, is_valid_instance_id, rotate_elastic_ip
|
||||
from services.lightsail_static_ip import (
|
||||
create_lightsail_client,
|
||||
is_valid_lightsail_instance_name,
|
||||
rotate_lightsail_static_ip,
|
||||
)
|
||||
from services.cloudflare_dns import CloudflareAuth, upsert_a_record
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_cf_auth(config: Config) -> CloudflareAuth:
|
||||
auth_type = (config.cloudflare_auth_type or "").strip() or "api_token"
|
||||
if auth_type == "api_token":
|
||||
return CloudflareAuth(auth_type="api_token", api_token=config.cf_api_token)
|
||||
return CloudflareAuth(
|
||||
auth_type="global_key",
|
||||
email=config.cf_email,
|
||||
api_key=config.cf_api_key,
|
||||
)
|
||||
|
||||
|
||||
def change_ip_for_machine(machine: ProxyMachine, config: Config) -> dict[str, str]:
|
||||
"""为指定机器更换 IP 并更新 DNS"""
|
||||
|
||||
if not machine.enabled:
|
||||
raise ValueError(f"机器 {machine.name} 已被禁用")
|
||||
|
||||
aws_service = (machine.aws_service or "ec2").strip().lower()
|
||||
|
||||
# 执行 IP 轮换
|
||||
if aws_service == "lightsail":
|
||||
if not is_valid_lightsail_instance_name(machine.aws_instance_id):
|
||||
raise ValueError(
|
||||
f"Lightsail 实例名格式不正确:{machine.aws_instance_id}"
|
||||
)
|
||||
lightsail = create_lightsail_client(
|
||||
region=machine.aws_region,
|
||||
aws_access_key=config.aws_access_key,
|
||||
aws_secret_key=config.aws_secret_key,
|
||||
)
|
||||
logger.info(
|
||||
"Rotating Lightsail Static IP for instance %s (%s)",
|
||||
machine.aws_instance_id,
|
||||
machine.name,
|
||||
)
|
||||
aws_result = rotate_lightsail_static_ip(
|
||||
lightsail,
|
||||
instance_name=machine.aws_instance_id,
|
||||
release_old=bool(config.release_old_eip),
|
||||
)
|
||||
else:
|
||||
if not is_valid_instance_id(machine.aws_instance_id):
|
||||
raise ValueError(
|
||||
f"EC2 Instance ID 格式不正确:{machine.aws_instance_id}(应类似 i-xxxxxxxxxxxxxxxxx)"
|
||||
)
|
||||
ec2 = create_ec2_client(
|
||||
region=machine.aws_region,
|
||||
aws_access_key=config.aws_access_key,
|
||||
aws_secret_key=config.aws_secret_key,
|
||||
)
|
||||
logger.info(
|
||||
"Rotating Elastic IP for instance %s (%s)",
|
||||
machine.aws_instance_id,
|
||||
machine.name,
|
||||
)
|
||||
aws_result = rotate_elastic_ip(
|
||||
ec2,
|
||||
instance_id=machine.aws_instance_id,
|
||||
release_old=bool(config.release_old_eip),
|
||||
)
|
||||
|
||||
public_ip = aws_result.get("public_ip")
|
||||
if not public_ip:
|
||||
raise RuntimeError("AWS 未返回新的 Public IP")
|
||||
|
||||
message = f"IP 已更换为 {public_ip}"
|
||||
|
||||
# 如果机器配置了域名,更新 DNS
|
||||
if machine.cf_zone_id and machine.cf_record_name:
|
||||
logger.info("Updating Cloudflare A record %s -> %s", machine.cf_record_name, public_ip)
|
||||
record = upsert_a_record(
|
||||
zone_id=machine.cf_zone_id,
|
||||
record_name=machine.cf_record_name,
|
||||
ip=public_ip,
|
||||
proxied=bool(machine.cf_proxied),
|
||||
record_id=machine.cf_record_id,
|
||||
auth=_get_cf_auth(config),
|
||||
)
|
||||
record_id = record.get("id")
|
||||
if record_id:
|
||||
machine.cf_record_id = record_id
|
||||
message = f"已更新 {machine.cf_record_name} -> {public_ip}"
|
||||
|
||||
return {
|
||||
"public_ip": public_ip,
|
||||
"message": message,
|
||||
}
|
||||
|
||||
|
||||
def run_ip_change_for_machine(machine_id: int) -> dict:
|
||||
"""为指定机器执行一次 IP 更换"""
|
||||
session = get_session()
|
||||
try:
|
||||
machine = session.query(ProxyMachine).filter_by(id=machine_id).first()
|
||||
if not machine:
|
||||
return {"ok": False, "message": "机器不存在"}
|
||||
|
||||
config = ensure_singleton_config(session)
|
||||
started_at = datetime.now(timezone.utc)
|
||||
|
||||
try:
|
||||
result = change_ip_for_machine(machine, config)
|
||||
machine.last_run_at = started_at
|
||||
machine.last_success = True
|
||||
machine.current_ip = result.get("public_ip")
|
||||
machine.last_message = result.get("message") or "OK"
|
||||
session.add(machine)
|
||||
session.commit()
|
||||
logger.info("IP change success for %s: %s", machine.name, machine.current_ip)
|
||||
return {"ok": True, "machine_name": machine.name, **result}
|
||||
except Exception as exc:
|
||||
logger.exception("IP change failed for %s", machine.name)
|
||||
machine.last_run_at = started_at
|
||||
machine.last_success = False
|
||||
machine.last_message = str(exc)
|
||||
session.add(machine)
|
||||
session.commit()
|
||||
return {"ok": False, "machine_name": machine.name, "message": str(exc)}
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
# 兼容旧接口
|
||||
def run_ip_change() -> dict:
|
||||
"""执行一次 IP 更换(兼容旧接口)"""
|
||||
session = get_session()
|
||||
try:
|
||||
# 获取第一台启用的机器
|
||||
machine = session.query(ProxyMachine).filter_by(enabled=True).first()
|
||||
if not machine:
|
||||
return {"ok": False, "message": "没有可用的机器"}
|
||||
return run_ip_change_for_machine(machine.id)
|
||||
finally:
|
||||
session.close()
|
||||
Reference in New Issue
Block a user