BBFbyOpus

This commit is contained in:
Artem Kokos
2026-04-01 22:51:24 +07:00
parent 732313a61c
commit b6b25fa2a1
11 changed files with 160 additions and 72 deletions

View File

@@ -8,6 +8,10 @@ from typing import List, Dict
logger = logging.getLogger(__name__)
# Минимальный допустимый prefixlen (больше число = меньше сеть)
# /16 = 65534 хоста, /8 = 16M хостов -- слишком много
MIN_PREFIX_LEN = 16
class DiscoveryService:
def __init__(self, port: int = 38899):
@@ -23,9 +27,25 @@ class DiscoveryService:
"""
env_network = os.getenv("SCAN_NETWORK")
if env_network:
return [s.strip() for s in env_network.split(",")]
subnets = []
for s in env_network.split(","):
s = s.strip()
try:
net = ipaddress.IPv4Network(s, strict=False)
if net.prefixlen < MIN_PREFIX_LEN:
logger.warning(
f"Подсеть {s} слишком большая (/{net.prefixlen}), "
f"ограничиваю до /{MIN_PREFIX_LEN}"
)
net = ipaddress.IPv4Network(
f"{net.network_address}/{MIN_PREFIX_LEN}", strict=False
)
subnets.append(str(net))
except ValueError as e:
logger.error(f"Неверный формат подсети {s}: {e}")
return subnets if subnets else ["192.168.1.0/24"]
# Автоопределение (твой старый метод)
# Автоопределение
try:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
# Коннект не создает трафика, но заставляет ОС выбрать нужный интерфейс
@@ -49,7 +69,7 @@ class DiscoveryService:
loop = asyncio.get_running_loop()
message = json.dumps(self.discover_msg).encode()
logger.debug(f"🚀 Начинаю сканирование сетей: {', '.join(subnets)}...")
logger.debug(f"Начинаю сканирование сетей: {', '.join(subnets)}...")
# Рассылаем запросы по всем целевым сетям
for subnet in subnets:
@@ -61,7 +81,7 @@ class DiscoveryService:
except Exception:
continue
except ValueError as e:
logger.error(f"Неверный формат подсети {subnet}: {e}")
logger.error(f"Неверный формат подсети {subnet}: {e}")
# Собираем ответы
start_time = loop.time()
@@ -107,9 +127,7 @@ class DiscoveryService:
found_devices = await self.scan_network()
for dev_data in found_devices:
state_manager.update_device(dev_data)
logger.info(
f"📡 Discovery: онлайн {len(state_manager.devices)} устройств"
)
logger.info(f"Discovery: онлайн {len(state_manager.devices)} устройств")
except Exception as e:
logger.error(f"Discovery background error: {e}")
logger.error(f"Discovery background error: {e}")
await asyncio.sleep(interval)

View File

@@ -1,11 +1,14 @@
import os
import logging
import pytz
from datetime import datetime
from datetime import datetime, timedelta
from dotenv import load_dotenv
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from app.core.database import sync_engine
from apscheduler.triggers.cron import CronTrigger
from sqlalchemy import delete
from app.core.database import sync_engine, async_session
from app.models.event_log import EventLog
from app.drivers.wiz import WizDriver
load_dotenv()
@@ -14,6 +17,8 @@ logger = logging.getLogger(__name__)
TZ_NAME = os.getenv("APP_TIMEZONE", "Asia/Novosibirsk")
app_tz = pytz.timezone(TZ_NAME)
RETENTION_DAYS = int(os.getenv("EVENT_LOG_RETENTION_DAYS", "30"))
jobstores = {"default": SQLAlchemyJobStore(engine=sync_engine)}
scheduler = AsyncIOScheduler(jobstores=jobstores, timezone=app_tz)
@@ -25,10 +30,34 @@ async def execute_lamp_command(ip: str, params: dict):
"""
driver = WizDriver()
await driver.set_pilot(ip, params)
logger.info(f"Сработало расписание для {ip}: {params}")
logger.info(f"Сработало расписание для {ip}: {params}")
async def cleanup_old_events():
"""Удаляет записи event_log старше RETENTION_DAYS."""
cutoff = (datetime.now() - timedelta(days=RETENTION_DAYS)).isoformat()
async with async_session() as session:
result = await session.execute(
delete(EventLog).where(EventLog.timestamp < cutoff)
)
await session.commit()
if result.rowcount:
logger.info(
f"Очистка лога: удалено {result.rowcount} записей старше {RETENTION_DAYS} дней"
)
async def start_scheduler():
if not scheduler.running:
scheduler.start()
logger.info(f"🚀 Планировщик запущен. Таймзона: {TZ_NAME}")
# Очистка лога -- раз в сутки в 03:00
scheduler.add_job(
cleanup_old_events,
CronTrigger(hour=3, minute=0, timezone=app_tz),
id="cleanup_event_log",
name="Очистка старых событий",
replace_existing=True,
)
logger.info(f"Планировщик запущен. Таймзона: {TZ_NAME}")