82 lines
3.1 KiB
Python
82 lines
3.1 KiB
Python
"""
|
|
PluginLoader — registers action plugins at startup and syncs them with the DB.
|
|
|
|
Startup flow:
|
|
1. For each builtin action, INSERT into `plugins` table (skip if already exists).
|
|
This preserves the `enabled` flag set by admins.
|
|
2. Load the list of enabled plugin names from DB.
|
|
3. Register only enabled actions in the registry.
|
|
|
|
Hot enable/disable (via Admin API):
|
|
- Disable: set DB.enabled=False, call action_registry.unregister(name)
|
|
- Enable: set DB.enabled=True, call action_registry.register(BUILTIN_ACTIONS[name])
|
|
"""
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
|
|
|
from app.models.plugin import Plugin
|
|
from app.plugins.base import BaseAction
|
|
from app.plugins.builtin.camera.get_status import CameraGetStatusAction
|
|
from app.plugins.builtin.camera.set_ntp import CameraSetNTPAction
|
|
from app.plugins.builtin.common.ping import PingAction
|
|
from app.plugins.builtin.mikrotik.get_config import MikrotikGetConfigAction
|
|
from app.plugins.builtin.mikrotik.get_ntp import MikrotikGetNTPAction
|
|
from app.plugins.builtin.mikrotik.set_ntp import MikrotikSetNTPAction
|
|
from app.plugins.builtin.ssh.debian_system_info import DebianSystemInfoAction
|
|
from app.plugins.builtin.ssh.get_file import GetFileAction
|
|
from app.plugins.builtin.ssh.get_timedatectl import GetTimedatectlAction
|
|
from app.plugins.builtin.ssh.ssh_command import SSHCommandAction
|
|
from app.plugins.registry import action_registry
|
|
from app.services.database import AsyncSessionLocal
|
|
|
|
# Public dict — used by Admin router for hot enable/disable
|
|
BUILTIN_ACTIONS: dict[str, BaseAction] = {
|
|
a.name: a
|
|
for a in [
|
|
PingAction(),
|
|
SSHCommandAction(),
|
|
DebianSystemInfoAction(),
|
|
GetTimedatectlAction(),
|
|
GetFileAction(),
|
|
MikrotikGetConfigAction(),
|
|
MikrotikGetNTPAction(),
|
|
MikrotikSetNTPAction(),
|
|
CameraGetStatusAction(),
|
|
CameraSetNTPAction(),
|
|
]
|
|
}
|
|
|
|
|
|
async def load_plugins() -> None:
|
|
now = datetime.now(timezone.utc)
|
|
|
|
async with AsyncSessionLocal() as db:
|
|
async with db.begin():
|
|
for action in BUILTIN_ACTIONS.values():
|
|
stmt = (
|
|
pg_insert(Plugin)
|
|
.values(
|
|
name=action.name,
|
|
display_name=action.display_name,
|
|
description=(action.__class__.__doc__ or "").strip() or None,
|
|
enabled=True,
|
|
registered_at=now,
|
|
)
|
|
.on_conflict_do_nothing(index_elements=["name"])
|
|
)
|
|
await db.execute(stmt)
|
|
|
|
# Load enabled plugins from DB
|
|
async with AsyncSessionLocal() as db:
|
|
result = await db.execute(select(Plugin.name).where(Plugin.enabled == True))
|
|
enabled_names = {row[0] for row in result.all()}
|
|
|
|
# Register only enabled actions
|
|
for name, action in BUILTIN_ACTIONS.items():
|
|
if name in enabled_names:
|
|
action_registry.register(action)
|
|
|
|
print(f"[plugins] Registered {len(enabled_names)}/{len(BUILTIN_ACTIONS)} plugin(s)")
|