mirror of
https://github.com/infinition/Bjorn.git
synced 2026-03-19 02:00:24 +00:00
feat: Implement package management utilities with JSON endpoints for listing and uninstalling packages feat: Create plugin management utilities with endpoints for listing, configuring, and installing plugins feat: Develop schedule and trigger management utilities with CRUD operations for schedules and triggers
138 lines
5.4 KiB
Python
138 lines
5.4 KiB
Python
"""plugins.py - Plugin configuration and hook tracking operations."""
|
|
|
|
import json
|
|
import logging
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from logger import Logger
|
|
|
|
logger = Logger(name="db_utils.plugins", level=logging.DEBUG)
|
|
|
|
|
|
class PluginOps:
|
|
"""Plugin configuration and hook registration operations."""
|
|
|
|
def __init__(self, base):
|
|
self.base = base
|
|
|
|
def create_tables(self):
|
|
"""Create plugin_configs and plugin_hooks tables."""
|
|
|
|
self.base.execute("""
|
|
CREATE TABLE IF NOT EXISTS plugin_configs (
|
|
plugin_id TEXT PRIMARY KEY,
|
|
enabled INTEGER DEFAULT 1,
|
|
config_json TEXT DEFAULT '{}',
|
|
meta_json TEXT DEFAULT '{}',
|
|
installed_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
""")
|
|
|
|
self.base.execute("""
|
|
CREATE TABLE IF NOT EXISTS plugin_hooks (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
plugin_id TEXT NOT NULL,
|
|
hook_name TEXT NOT NULL,
|
|
UNIQUE(plugin_id, hook_name),
|
|
FOREIGN KEY (plugin_id) REFERENCES plugin_configs(plugin_id)
|
|
ON DELETE CASCADE
|
|
);
|
|
""")
|
|
|
|
self.base.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_plugin_hooks_hook "
|
|
"ON plugin_hooks(hook_name);"
|
|
)
|
|
|
|
logger.debug("Plugin tables created/verified")
|
|
|
|
# ── Config CRUD ──────────────────────────────────────────────────
|
|
|
|
def get_plugin_config(self, plugin_id: str) -> Optional[Dict[str, Any]]:
|
|
"""Get plugin config row. Returns dict with parsed config_json and meta."""
|
|
row = self.base.query_one(
|
|
"SELECT * FROM plugin_configs WHERE plugin_id=?;", (plugin_id,)
|
|
)
|
|
if row:
|
|
try:
|
|
row["config"] = json.loads(row.get("config_json") or "{}")
|
|
except Exception:
|
|
row["config"] = {}
|
|
try:
|
|
row["meta"] = json.loads(row.get("meta_json") or "{}")
|
|
except Exception:
|
|
row["meta"] = {}
|
|
return row
|
|
|
|
def save_plugin_config(self, plugin_id: str, config: dict) -> None:
|
|
"""Update config_json for a plugin."""
|
|
self.base.execute("""
|
|
UPDATE plugin_configs
|
|
SET config_json = ?, updated_at = CURRENT_TIMESTAMP
|
|
WHERE plugin_id = ?;
|
|
""", (json.dumps(config, ensure_ascii=False), plugin_id))
|
|
|
|
def upsert_plugin(self, plugin_id: str, enabled: int, config: dict, meta: dict) -> None:
|
|
"""Insert or update a plugin record."""
|
|
self.base.execute("""
|
|
INSERT INTO plugin_configs (plugin_id, enabled, config_json, meta_json)
|
|
VALUES (?, ?, ?, ?)
|
|
ON CONFLICT(plugin_id) DO UPDATE SET
|
|
enabled = excluded.enabled,
|
|
meta_json = excluded.meta_json,
|
|
updated_at = CURRENT_TIMESTAMP;
|
|
""", (plugin_id, enabled, json.dumps(config, ensure_ascii=False),
|
|
json.dumps(meta, ensure_ascii=False)))
|
|
|
|
def delete_plugin(self, plugin_id: str) -> None:
|
|
"""Delete plugin and its hooks (CASCADE)."""
|
|
self.base.execute("DELETE FROM plugin_configs WHERE plugin_id=?;", (plugin_id,))
|
|
|
|
def list_plugins(self) -> List[Dict[str, Any]]:
|
|
"""List all registered plugins."""
|
|
rows = self.base.query("SELECT * FROM plugin_configs ORDER BY plugin_id;")
|
|
for r in rows:
|
|
try:
|
|
r["config"] = json.loads(r.get("config_json") or "{}")
|
|
except Exception:
|
|
r["config"] = {}
|
|
try:
|
|
r["meta"] = json.loads(r.get("meta_json") or "{}")
|
|
except Exception:
|
|
r["meta"] = {}
|
|
return rows
|
|
|
|
def set_plugin_enabled(self, plugin_id: str, enabled: bool) -> None:
|
|
"""Toggle plugin enabled state."""
|
|
self.base.execute(
|
|
"UPDATE plugin_configs SET enabled=?, updated_at=CURRENT_TIMESTAMP WHERE plugin_id=?;",
|
|
(1 if enabled else 0, plugin_id)
|
|
)
|
|
|
|
# ── Hook CRUD ────────────────────────────────────────────────────
|
|
|
|
def set_plugin_hooks(self, plugin_id: str, hooks: List[str]) -> None:
|
|
"""Replace all hooks for a plugin."""
|
|
with self.base.transaction():
|
|
self.base.execute("DELETE FROM plugin_hooks WHERE plugin_id=?;", (plugin_id,))
|
|
for h in hooks:
|
|
self.base.execute(
|
|
"INSERT OR IGNORE INTO plugin_hooks(plugin_id, hook_name) VALUES(?,?);",
|
|
(plugin_id, h)
|
|
)
|
|
|
|
def get_hooks_for_event(self, hook_name: str) -> List[str]:
|
|
"""Get all plugin_ids subscribed to a given hook."""
|
|
rows = self.base.query(
|
|
"SELECT plugin_id FROM plugin_hooks WHERE hook_name=?;", (hook_name,)
|
|
)
|
|
return [r["plugin_id"] for r in rows]
|
|
|
|
def get_hooks_for_plugin(self, plugin_id: str) -> List[str]:
|
|
"""Get all hooks a plugin subscribes to."""
|
|
rows = self.base.query(
|
|
"SELECT hook_name FROM plugin_hooks WHERE plugin_id=?;", (plugin_id,)
|
|
)
|
|
return [r["hook_name"] for r in rows]
|