From 94a35da86ee7f46203466e43c1b9b7c15b7fe19a Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 13 Apr 2026 17:13:55 -0400 Subject: [PATCH] =?UTF-8?q?refactor:=20update=20imports=20fabledscryer=20?= =?UTF-8?q?=E2=86=92=20roundtable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- roundtable/alerts/routes.py | 12 +++--- roundtable/ansible/executor.py | 4 +- roundtable/ansible/routes.py | 10 ++--- roundtable/ansible/sources.py | 4 +- roundtable/app.py | 2 +- roundtable/audit/routes.py | 6 +-- roundtable/auth/ldap_auth.py | 2 +- roundtable/auth/middleware.py | 2 +- roundtable/auth/oidc.py | 2 +- roundtable/auth/routes.py | 18 ++++---- roundtable/config.py | 2 +- roundtable/core/alerts.py | 6 +-- roundtable/core/audit.py | 4 +- roundtable/core/cleanup.py | 6 +-- roundtable/core/migration_runner.py | 2 +- roundtable/core/notifications.py | 2 +- roundtable/core/plugin_index.py | 2 +- roundtable/core/plugin_manager.py | 28 ++++++------- roundtable/core/reports.py | 16 +++---- roundtable/core/settings.py | 8 ++-- roundtable/core/time_range.py | 2 +- roundtable/core/widgets.py | 2 +- roundtable/dashboard/routes.py | 12 +++--- roundtable/dns/routes.py | 8 ++-- roundtable/hosts/routes.py | 10 ++--- roundtable/migrations/env.py | 4 +- roundtable/models/settings.py | 2 +- roundtable/monitors/dns.py | 6 +-- roundtable/monitors/ping.py | 6 +-- roundtable/ping/routes.py | 12 +++--- roundtable/settings/__init__.py | 2 +- roundtable/settings/routes.py | 42 +++++++++---------- roundtable/templates/settings/ansible.html | 2 +- roundtable/templates/settings/auth.html | 10 ++--- roundtable/templates/settings/general.html | 2 +- roundtable/templates/settings/index.html | 2 +- .../templates/settings/notifications.html | 2 +- .../templates/settings/plugin_detail.html | 2 +- roundtable/templates/settings/plugins.html | 2 +- roundtable/templates/settings/reports.html | 2 +- tests/conftest.py | 2 +- tests/test_app.py | 2 +- tests/test_auth.py | 12 +++--- tests/test_config.py | 6 +-- tests/test_models.py | 2 +- 45 files changed, 147 insertions(+), 147 deletions(-) diff --git a/roundtable/alerts/routes.py b/roundtable/alerts/routes.py index db025c7..5831a42 100644 --- a/roundtable/alerts/routes.py +++ b/roundtable/alerts/routes.py @@ -1,13 +1,13 @@ from __future__ import annotations from datetime import datetime, timezone from quart import Blueprint, render_template, request, redirect, url_for, current_app, session -from fabledscryer.core.audit import log_audit +from roundtable.core.audit import log_audit from sqlalchemy import select -from fabledscryer.auth.middleware import require_role -from fabledscryer.models.alerts import AlertRule, AlertState, AlertStateEnum, AlertOperator, MaintenanceWindow -from fabledscryer.models.hosts import Host -from fabledscryer.models.metrics import PluginMetric -from fabledscryer.models.users import UserRole +from roundtable.auth.middleware import require_role +from roundtable.models.alerts import AlertRule, AlertState, AlertStateEnum, AlertOperator, MaintenanceWindow +from roundtable.models.hosts import Host +from roundtable.models.metrics import PluginMetric +from roundtable.models.users import UserRole alerts_bp = Blueprint("alerts", __name__, url_prefix="/alerts") diff --git a/roundtable/ansible/executor.py b/roundtable/ansible/executor.py index 281d8c3..a7f1eaf 100644 --- a/roundtable/ansible/executor.py +++ b/roundtable/ansible/executor.py @@ -1,4 +1,4 @@ -# fabledscryer/ansible/executor.py +# roundtable/ansible/executor.py from __future__ import annotations import asyncio import logging @@ -77,7 +77,7 @@ async def start_run( async def _flush(final_status: str | None = None) -> None: nonlocal lines_since_flush, last_flush_at from sqlalchemy import update - from fabledscryer.models.ansible import AnsibleRun, AnsibleRunStatus + from roundtable.models.ansible import AnsibleRun, AnsibleRunStatus values: dict = {"output": db_output} if final_status is not None: diff --git a/roundtable/ansible/routes.py b/roundtable/ansible/routes.py index 8624add..273f0b2 100644 --- a/roundtable/ansible/routes.py +++ b/roundtable/ansible/routes.py @@ -1,4 +1,4 @@ -# fabledscryer/ansible/routes.py +# roundtable/ansible/routes.py from __future__ import annotations import asyncio import uuid @@ -11,10 +11,10 @@ from quart import ( ) from sqlalchemy import select, update -from fabledscryer.ansible import executor, sources as src_module -from fabledscryer.auth.middleware import require_role -from fabledscryer.models.ansible import AnsibleRun, AnsibleRunStatus -from fabledscryer.models.users import UserRole +from roundtable.ansible import executor, sources as src_module +from roundtable.auth.middleware import require_role +from roundtable.models.ansible import AnsibleRun, AnsibleRunStatus +from roundtable.models.users import UserRole ansible_bp = Blueprint("ansible", __name__, url_prefix="/ansible") diff --git a/roundtable/ansible/sources.py b/roundtable/ansible/sources.py index 40002ae..8d1a040 100644 --- a/roundtable/ansible/sources.py +++ b/roundtable/ansible/sources.py @@ -17,7 +17,7 @@ def get_sources(ansible_cfg: dict) -> list[dict]: Config structure in config.yaml:: ansible: - cache_dir: /var/cache/fabledscryer/ansible + cache_dir: /var/cache/roundtable/ansible sources: - name: my-playbooks type: local @@ -29,7 +29,7 @@ def get_sources(ansible_cfg: dict) -> list[dict]: pull_interval_seconds: 3600 """ sources = ansible_cfg.get("sources", []) - cache_dir = ansible_cfg.get("cache_dir", "/var/cache/fabledscryer/ansible") + cache_dir = ansible_cfg.get("cache_dir", "/var/cache/roundtable/ansible") result = [] for src in sources: src_type = src.get("type", "local") diff --git a/roundtable/app.py b/roundtable/app.py index 397bb02..2e75829 100644 --- a/roundtable/app.py +++ b/roundtable/app.py @@ -1,4 +1,4 @@ -# fabledscryer/app.py +# roundtable/app.py from __future__ import annotations import asyncio from pathlib import Path diff --git a/roundtable/audit/routes.py b/roundtable/audit/routes.py index a10eab4..fe7e755 100644 --- a/roundtable/audit/routes.py +++ b/roundtable/audit/routes.py @@ -2,9 +2,9 @@ from __future__ import annotations import json from quart import Blueprint, render_template, request, current_app from sqlalchemy import select -from fabledscryer.auth.middleware import require_role -from fabledscryer.models.audit import AuditEvent -from fabledscryer.models.users import UserRole +from roundtable.auth.middleware import require_role +from roundtable.models.audit import AuditEvent +from roundtable.models.users import UserRole audit_bp = Blueprint("audit", __name__, url_prefix="/audit") diff --git a/roundtable/auth/ldap_auth.py b/roundtable/auth/ldap_auth.py index 098229f..e64b93e 100644 --- a/roundtable/auth/ldap_auth.py +++ b/roundtable/auth/ldap_auth.py @@ -1,4 +1,4 @@ -"""fabledscryer/auth/ldap_auth.py +"""roundtable/auth/ldap_auth.py LDAP authentication helper. Requires the optional `ldap3` package. Falls back gracefully if ldap3 is not installed. diff --git a/roundtable/auth/middleware.py b/roundtable/auth/middleware.py index 4af4103..17e9579 100644 --- a/roundtable/auth/middleware.py +++ b/roundtable/auth/middleware.py @@ -1,7 +1,7 @@ from __future__ import annotations import functools from quart import session, redirect, url_for, abort, g -from fabledscryer.models.users import UserRole +from roundtable.models.users import UserRole _ROLE_ORDER = [UserRole.viewer, UserRole.operator, UserRole.admin] diff --git a/roundtable/auth/oidc.py b/roundtable/auth/oidc.py index 3cacf4f..0d1aac8 100644 --- a/roundtable/auth/oidc.py +++ b/roundtable/auth/oidc.py @@ -1,4 +1,4 @@ -"""fabledscryer/auth/oidc.py +"""roundtable/auth/oidc.py OIDC Authorization Code flow helpers using httpx. No extra dependencies — works with any OIDC provider (Authentik, Keycloak, etc.). diff --git a/roundtable/auth/routes.py b/roundtable/auth/routes.py index ba7d299..49de43a 100644 --- a/roundtable/auth/routes.py +++ b/roundtable/auth/routes.py @@ -2,8 +2,8 @@ from __future__ import annotations import bcrypt from quart import Blueprint, render_template, request, redirect, url_for, session from sqlalchemy import select, func -from fabledscryer.auth.middleware import login_user, logout_user -from fabledscryer.models.users import User, UserRole +from roundtable.auth.middleware import login_user, logout_user +from roundtable.models.users import User, UserRole auth_bp = Blueprint("auth", __name__) @@ -15,7 +15,7 @@ async def _provision_external_user(app, username: str, email: str, role_str: str Their role is updated on every login to reflect current IdP group membership. """ import secrets - from fabledscryer.models.users import UserRole + from roundtable.models.users import UserRole async with app.db_sessionmaker() as db: result = await db.execute(select(User).where(User.username == username)) user = result.scalar_one_or_none() @@ -60,7 +60,7 @@ async def login(): @auth_bp.post("/login") async def login_post(): from quart import current_app - from fabledscryer.core.audit import log_audit + from roundtable.core.audit import log_audit form = await request.form username = form.get("username", "").strip() password_str = form.get("password", "") @@ -77,7 +77,7 @@ async def login_post(): # Try LDAP fallback if enabled ldap_cfg = current_app.config.get("LDAP", {}) if ldap_cfg.get("enabled"): - from fabledscryer.auth.ldap_auth import ldap_authenticate + from roundtable.auth.ldap_auth import ldap_authenticate ldap_info = await ldap_authenticate(ldap_cfg, username, password_str) if ldap_info: user = await _provision_external_user( @@ -99,7 +99,7 @@ async def login_post(): @auth_bp.get("/login/oidc") async def login_oidc(): from quart import current_app - from fabledscryer.auth.oidc import get_discovery, build_authorize_url, generate_state + from roundtable.auth.oidc import get_discovery, build_authorize_url, generate_state oidc_cfg = current_app.config.get("OIDC", {}) if not oidc_cfg.get("enabled") or not oidc_cfg.get("discovery_url"): return redirect(url_for("auth.login")) @@ -123,8 +123,8 @@ async def login_oidc(): @auth_bp.get("/login/oidc/callback") async def login_oidc_callback(): from quart import current_app - from fabledscryer.auth.oidc import get_discovery, exchange_code, get_userinfo, map_role - from fabledscryer.core.audit import log_audit + from roundtable.auth.oidc import get_discovery, exchange_code, get_userinfo, map_role + from roundtable.core.audit import log_audit oidc_cfg = current_app.config.get("OIDC", {}) error = request.args.get("error") @@ -190,7 +190,7 @@ async def setup(): @auth_bp.post("/setup") async def setup_post(): from quart import current_app - from fabledscryer.core.audit import log_audit + from roundtable.core.audit import log_audit if await get_user_count(current_app) > 0: return redirect(url_for("auth.login")) form = await request.form diff --git a/roundtable/config.py b/roundtable/config.py index acc8d03..f6a5e63 100644 --- a/roundtable/config.py +++ b/roundtable/config.py @@ -1,4 +1,4 @@ -# fabledscryer/config.py +# roundtable/config.py from __future__ import annotations import logging import os diff --git a/roundtable/core/alerts.py b/roundtable/core/alerts.py index 0cc3dcd..d77516f 100644 --- a/roundtable/core/alerts.py +++ b/roundtable/core/alerts.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING from sqlalchemy import and_, or_, select from sqlalchemy.ext.asyncio import AsyncSession -from fabledscryer.models.alerts import ( +from roundtable.models.alerts import ( AlertEvent, AlertOperator, AlertRule, @@ -16,7 +16,7 @@ from fabledscryer.models.alerts import ( AlertStateEnum, MaintenanceWindow, ) -from fabledscryer.models.metrics import PluginMetric +from roundtable.models.metrics import PluginMetric if TYPE_CHECKING: from quart import Quart @@ -226,7 +226,7 @@ async def _dispatch_notification( event_id: str, ) -> None: """Run after the transaction commits. Sends notifications and updates the event row.""" - from fabledscryer.core.notifications import dispatch_notifications + from roundtable.core.notifications import dispatch_notifications alert_data = { "rule_name": rule.name, diff --git a/roundtable/core/audit.py b/roundtable/core/audit.py index 63ad5e2..9342666 100644 --- a/roundtable/core/audit.py +++ b/roundtable/core/audit.py @@ -1,4 +1,4 @@ -"""fabledscryer/core/audit.py +"""roundtable/core/audit.py Helpers for writing audit log entries. Each call opens its own DB session so audit events are committed independently of the calling @@ -23,7 +23,7 @@ async def log_audit( detail: dict | None = None, ) -> None: """Write one audit event. Never raises — failures are logged and swallowed.""" - from fabledscryer.models.audit import AuditEvent + from roundtable.models.audit import AuditEvent try: async with app.db_sessionmaker() as db: async with db.begin(): diff --git a/roundtable/core/cleanup.py b/roundtable/core/cleanup.py index 7222d81..55329e9 100644 --- a/roundtable/core/cleanup.py +++ b/roundtable/core/cleanup.py @@ -5,9 +5,9 @@ from typing import TYPE_CHECKING from sqlalchemy import delete -from fabledscryer.models.monitors import DnsResult, PingResult -from fabledscryer.models.metrics import PluginMetric -from fabledscryer.models.ansible import AnsibleRun +from roundtable.models.monitors import DnsResult, PingResult +from roundtable.models.metrics import PluginMetric +from roundtable.models.ansible import AnsibleRun if TYPE_CHECKING: from quart import Quart diff --git a/roundtable/core/migration_runner.py b/roundtable/core/migration_runner.py index 1b586b0..2a030b7 100644 --- a/roundtable/core/migration_runner.py +++ b/roundtable/core/migration_runner.py @@ -1,4 +1,4 @@ -# fabledscryer/core/migration_runner.py +# roundtable/core/migration_runner.py from __future__ import annotations import logging from pathlib import Path diff --git a/roundtable/core/notifications.py b/roundtable/core/notifications.py index c08b1ac..932ba14 100644 --- a/roundtable/core/notifications.py +++ b/roundtable/core/notifications.py @@ -34,7 +34,7 @@ async def _send_email(cfg: dict, alert: dict) -> dict: try: msg = EmailMessage() msg["Subject"] = f"[Fabled Scryer] {alert['state']} — {alert['rule_name']}" - msg["From"] = cfg.get("username", "fabledscryer@localhost") + msg["From"] = cfg.get("username", "roundtable@localhost") msg["To"] = ", ".join(cfg["recipients"]) msg.set_content( f"Alert: {alert['state']}\n" diff --git a/roundtable/core/plugin_index.py b/roundtable/core/plugin_index.py index fc292e8..82e52ee 100644 --- a/roundtable/core/plugin_index.py +++ b/roundtable/core/plugin_index.py @@ -1,4 +1,4 @@ -# fabledscryer/core/plugin_index.py +# roundtable/core/plugin_index.py """Remote plugin catalog: fetch, parse, and cache the plugin index.yaml.""" from __future__ import annotations import logging diff --git a/roundtable/core/plugin_manager.py b/roundtable/core/plugin_manager.py index 7ea0127..4f87168 100644 --- a/roundtable/core/plugin_manager.py +++ b/roundtable/core/plugin_manager.py @@ -1,4 +1,4 @@ -# fabledscryer/core/plugin_manager.py +# roundtable/core/plugin_manager.py from __future__ import annotations import hashlib import importlib @@ -41,7 +41,7 @@ def _import_plugin(name: str, plugin_path: Path): """ import importlib.util - module_key = f"_fabledscryer_plugin_{name}" + module_key = f"_roundtable_plugin_{name}" if module_key in sys.modules: return sys.modules[module_key] @@ -78,7 +78,7 @@ def load_plugins(app: "Quart") -> None: 7. Register blueprint at /plugins// if get_blueprint() exists 8. Append get_scheduled_tasks() results to app._task_registry """ - import fabledscryer + import roundtable plugin_dir = Path(app.config["PLUGIN_DIR"]) plugins_cfg: dict = app.config["PLUGINS"] @@ -126,13 +126,13 @@ def load_plugins(app: "Quart") -> None: min_ver = meta.get("min_app_version") if min_ver: try: - if Version(fabledscryer.__version__) < Version(min_ver): + if Version(roundtable.__version__) < Version(min_ver): _FAILED_PLUGINS[name] = ( - f"Requires fabledscryer>={min_ver}, running {fabledscryer.__version__}" + f"Requires roundtable>={min_ver}, running {roundtable.__version__}" ) logger.error( - "Plugin %r: requires fabledscryer>=%s but running %s, skipping", - name, min_ver, fabledscryer.__version__, + "Plugin %r: requires roundtable>=%s but running %s, skipping", + name, min_ver, roundtable.__version__, ) continue except Exception as exc: @@ -267,7 +267,7 @@ async def download_and_install_plugin( # Detect a single top-level directory to strip. Handles: # name/ (clean plugin zip) # name-v1.0.0/ (GitHub release tag archive) - # fabledscryer-plugins-main/name/ (whole-repo archive) + # roundtable-plugins-main/name/ (whole-repo archive) # Strategy: if there is exactly one top-level item and it is a # directory whose name starts with the plugin name (case-insensitive), # strip it. Otherwise extract flat into dest. @@ -302,7 +302,7 @@ async def download_and_install_plugin( try: mdir = plugin_dir / name / "migrations" if mdir.exists(): - from fabledscryer.core.migration_runner import ( + from roundtable.core.migration_runner import ( run_plugin_migrations, discover_all_plugin_migration_dirs, ) @@ -325,7 +325,7 @@ def hot_reload_plugin(app: "Quart", name: str) -> tuple[bool, str]: Returns (success, message). """ - import fabledscryer + import roundtable if name in _LOADED_PLUGINS: return False, "Plugin already loaded — restart required to apply updates" @@ -352,10 +352,10 @@ def hot_reload_plugin(app: "Quart", name: str) -> tuple[bool, str]: min_ver = meta.get("min_app_version") if min_ver: try: - if Version(fabledscryer.__version__) < Version(min_ver): + if Version(roundtable.__version__) < Version(min_ver): return False, ( - f"Plugin requires fabledscryer>={min_ver} " - f"but running {fabledscryer.__version__}" + f"Plugin requires roundtable>={min_ver} " + f"but running {roundtable.__version__}" ) except Exception: return False, f"Invalid min_app_version: {min_ver!r}" @@ -381,7 +381,7 @@ def hot_reload_plugin(app: "Quart", name: str) -> tuple[bool, str]: mdir = plugin_path / "migrations" if mdir.exists(): try: - from fabledscryer.core.migration_runner import ( + from roundtable.core.migration_runner import ( run_plugin_migrations, discover_all_plugin_migration_dirs, ) diff --git a/roundtable/core/reports.py b/roundtable/core/reports.py index 4efe5ea..26dc6fd 100644 --- a/roundtable/core/reports.py +++ b/roundtable/core/reports.py @@ -1,4 +1,4 @@ -"""fabledscryer/core/reports.py +"""roundtable/core/reports.py Weekly digest report: uptime, active alerts, alert events, top metrics. Called by the scheduler (hourly check) or triggered manually from Settings. @@ -13,10 +13,10 @@ from email.message import EmailMessage from sqlalchemy import and_, case, func, select -from fabledscryer.models.alerts import AlertRule, AlertState, AlertStateEnum, AlertEvent -from fabledscryer.models.hosts import Host -from fabledscryer.models.metrics import PluginMetric -from fabledscryer.models.monitors import PingResult, PingStatus +from roundtable.models.alerts import AlertRule, AlertState, AlertStateEnum, AlertEvent +from roundtable.models.hosts import Host +from roundtable.models.metrics import PluginMetric +from roundtable.models.monitors import PingResult, PingStatus logger = logging.getLogger(__name__) @@ -226,13 +226,13 @@ async def send_report(app) -> tuple[bool, str]: msg = EmailMessage() msg["Subject"] = subject - msg["From"] = smtp_cfg.get("username", "fabledscryer@localhost") + msg["From"] = smtp_cfg.get("username", "roundtable@localhost") msg["To"] = ", ".join(smtp_cfg["recipients"]) msg.set_content(body) try: loop = asyncio.get_running_loop() - from fabledscryer.core.notifications import _smtp_send_sync + from roundtable.core.notifications import _smtp_send_sync await loop.run_in_executor(None, _smtp_send_sync, smtp_cfg, msg) logger.info("Weekly report sent to %s", smtp_cfg["recipients"]) return True, f"Report sent to {', '.join(smtp_cfg['recipients'])}." @@ -243,7 +243,7 @@ async def send_report(app) -> tuple[bool, str]: async def check_and_send(app) -> None: """Hourly scheduler hook: send if day/hour match and not sent recently.""" - from fabledscryer.core.settings import get_setting, set_setting + from roundtable.core.settings import get_setting, set_setting async with app.db_sessionmaker() as db: enabled = await get_setting(db, "reports.enabled") diff --git a/roundtable/core/settings.py b/roundtable/core/settings.py index 25a954c..ea7944d 100644 --- a/roundtable/core/settings.py +++ b/roundtable/core/settings.py @@ -1,15 +1,15 @@ -# fabledscryer/core/settings.py +# roundtable/core/settings.py """DB-backed application settings. Keys use dotted notation (e.g. "smtp.host"). Values are JSON-encoded in the DB. Usage in create_app() (before event loop): - from fabledscryer.core.settings import load_settings_sync + from roundtable.core.settings import load_settings_sync settings = load_settings_sync(db_url) Usage at runtime (inside async handlers): - from fabledscryer.core.settings import get_setting, set_setting + from roundtable.core.settings import get_setting, set_setting async with app.db_sessionmaker() as session: value = await get_setting(session, "smtp.host") await set_setting(session, "smtp.host", "mail.example.com") @@ -24,7 +24,7 @@ from typing import Any from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from fabledscryer.models.settings import AppSetting +from roundtable.models.settings import AppSetting logger = logging.getLogger(__name__) diff --git a/roundtable/core/time_range.py b/roundtable/core/time_range.py index 2884cc1..ad984f3 100644 --- a/roundtable/core/time_range.py +++ b/roundtable/core/time_range.py @@ -1,4 +1,4 @@ -# fabledscryer/core/time_range.py +# roundtable/core/time_range.py """Shared time-range utilities for scoping queries and sparkline data.""" from __future__ import annotations from datetime import datetime, timedelta, timezone diff --git a/roundtable/core/widgets.py b/roundtable/core/widgets.py index daf9adc..24ffc2f 100644 --- a/roundtable/core/widgets.py +++ b/roundtable/core/widgets.py @@ -1,4 +1,4 @@ -# fabledscryer/core/widgets.py +# roundtable/core/widgets.py # # Central widget registry. Each entry describes one widget type that can be # placed on a dashboard. Plugin widgets declare which plugin must be enabled. diff --git a/roundtable/dashboard/routes.py b/roundtable/dashboard/routes.py index faeba86..b5ccfb0 100644 --- a/roundtable/dashboard/routes.py +++ b/roundtable/dashboard/routes.py @@ -6,12 +6,12 @@ from datetime import datetime, timezone, timedelta from urllib.parse import urlencode from quart import Blueprint, render_template, current_app, abort, redirect, request, session from sqlalchemy import select, func, update -from fabledscryer.auth.middleware import require_role, current_user_id -from fabledscryer.models.users import UserRole -from fabledscryer.models.hosts import Host -from fabledscryer.models.monitors import PingResult, DnsResult -from fabledscryer.models.dashboard import Dashboard, DashboardWidget, DashboardShareToken -from fabledscryer.core.widgets import get_available_widgets, WIDGET_REGISTRY +from roundtable.auth.middleware import require_role, current_user_id +from roundtable.models.users import UserRole +from roundtable.models.hosts import Host +from roundtable.models.monitors import PingResult, DnsResult +from roundtable.models.dashboard import Dashboard, DashboardWidget, DashboardShareToken +from roundtable.core.widgets import get_available_widgets, WIDGET_REGISTRY dashboard_bp = Blueprint("dashboard", __name__) diff --git a/roundtable/dns/routes.py b/roundtable/dns/routes.py index c8e5199..04b9ca7 100644 --- a/roundtable/dns/routes.py +++ b/roundtable/dns/routes.py @@ -1,10 +1,10 @@ from __future__ import annotations from quart import Blueprint, current_app, render_template from sqlalchemy import select, func -from fabledscryer.auth.middleware import require_role -from fabledscryer.models.users import UserRole -from fabledscryer.models.hosts import Host -from fabledscryer.models.monitors import DnsResult +from roundtable.auth.middleware import require_role +from roundtable.models.users import UserRole +from roundtable.models.hosts import Host +from roundtable.models.monitors import DnsResult dns_bp = Blueprint("dns", __name__, url_prefix="/dns") diff --git a/roundtable/hosts/routes.py b/roundtable/hosts/routes.py index fdb5dc2..aa1af6c 100644 --- a/roundtable/hosts/routes.py +++ b/roundtable/hosts/routes.py @@ -2,11 +2,11 @@ from __future__ import annotations from datetime import datetime, timedelta, timezone from quart import Blueprint, render_template, request, redirect, url_for, current_app, session from sqlalchemy import and_, case, select, func -from fabledscryer.auth.middleware import require_role -from fabledscryer.core.audit import log_audit -from fabledscryer.models.hosts import Host, ProbeType -from fabledscryer.models.monitors import PingResult, DnsResult, PingStatus -from fabledscryer.models.users import UserRole +from roundtable.auth.middleware import require_role +from roundtable.core.audit import log_audit +from roundtable.models.hosts import Host, ProbeType +from roundtable.models.monitors import PingResult, DnsResult, PingStatus +from roundtable.models.users import UserRole hosts_bp = Blueprint("hosts", __name__, url_prefix="/hosts") diff --git a/roundtable/migrations/env.py b/roundtable/migrations/env.py index 5853d97..583f994 100644 --- a/roundtable/migrations/env.py +++ b/roundtable/migrations/env.py @@ -4,8 +4,8 @@ from sqlalchemy import pool from sqlalchemy.engine import Connection from sqlalchemy.ext.asyncio import async_engine_from_config from alembic import context -from fabledscryer.models.base import Base -import fabledscryer.models # noqa: F401 — registers all models in metadata +from roundtable.models.base import Base +import roundtable.models # noqa: F401 — registers all models in metadata config = context.config if config.config_file_name is not None: diff --git a/roundtable/models/settings.py b/roundtable/models/settings.py index 54c0997..02c014d 100644 --- a/roundtable/models/settings.py +++ b/roundtable/models/settings.py @@ -2,7 +2,7 @@ from __future__ import annotations from datetime import datetime, timezone from sqlalchemy import DateTime, String, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledscryer.models.base import Base +from roundtable.models.base import Base class AppSetting(Base): diff --git a/roundtable/monitors/dns.py b/roundtable/monitors/dns.py index 58b8f77..7f123db 100644 --- a/roundtable/monitors/dns.py +++ b/roundtable/monitors/dns.py @@ -6,9 +6,9 @@ import socket from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from fabledscryer.core.alerts import record_metric -from fabledscryer.models.hosts import Host -from fabledscryer.models.monitors import DnsResult, DnsStatus +from roundtable.core.alerts import record_metric +from roundtable.models.hosts import Host +from roundtable.models.monitors import DnsResult, DnsStatus logger = logging.getLogger(__name__) diff --git a/roundtable/monitors/ping.py b/roundtable/monitors/ping.py index aaf2e21..13e4007 100644 --- a/roundtable/monitors/ping.py +++ b/roundtable/monitors/ping.py @@ -5,9 +5,9 @@ import time from sqlalchemy.ext.asyncio import AsyncSession -from fabledscryer.core.alerts import record_metric -from fabledscryer.models.hosts import Host, ProbeType -from fabledscryer.models.monitors import PingResult, PingStatus +from roundtable.core.alerts import record_metric +from roundtable.models.hosts import Host, ProbeType +from roundtable.models.monitors import PingResult, PingStatus logger = logging.getLogger(__name__) diff --git a/roundtable/ping/routes.py b/roundtable/ping/routes.py index 4b79e51..6e6021e 100644 --- a/roundtable/ping/routes.py +++ b/roundtable/ping/routes.py @@ -2,12 +2,12 @@ from __future__ import annotations import logging from quart import Blueprint, current_app, render_template, request, redirect, url_for from sqlalchemy import select, func, case -from fabledscryer.auth.middleware import require_role -from fabledscryer.models.users import UserRole -from fabledscryer.models.hosts import Host -from fabledscryer.models.monitors import PingResult -from fabledscryer.core.settings import get_all_settings, set_setting, DEFAULTS -from fabledscryer.core.time_range import parse_range, DEFAULT_RANGE +from roundtable.auth.middleware import require_role +from roundtable.models.users import UserRole +from roundtable.models.hosts import Host +from roundtable.models.monitors import PingResult +from roundtable.core.settings import get_all_settings, set_setting, DEFAULTS +from roundtable.core.time_range import parse_range, DEFAULT_RANGE ping_bp = Blueprint("ping", __name__, url_prefix="/ping") logger = logging.getLogger(__name__) diff --git a/roundtable/settings/__init__.py b/roundtable/settings/__init__.py index c2e9ca5..0acad26 100644 --- a/roundtable/settings/__init__.py +++ b/roundtable/settings/__init__.py @@ -1 +1 @@ -# fabledscryer/settings/__init__.py +# roundtable/settings/__init__.py diff --git a/roundtable/settings/routes.py b/roundtable/settings/routes.py index b9a5c4f..d1f069e 100644 --- a/roundtable/settings/routes.py +++ b/roundtable/settings/routes.py @@ -1,14 +1,14 @@ -# fabledscryer/settings/routes.py +# roundtable/settings/routes.py from __future__ import annotations import json import logging from pathlib import Path from quart import Blueprint, current_app, render_template, request, redirect, url_for, session -from fabledscryer.auth.middleware import require_role -from fabledscryer.core.audit import log_audit -from fabledscryer.models.users import UserRole -from fabledscryer.core.settings import ( +from roundtable.auth.middleware import require_role +from roundtable.core.audit import log_audit +from roundtable.models.users import UserRole +from roundtable.core.settings import ( DEFAULTS, get_all_settings, set_setting, to_smtp_cfg, to_webhook_cfg, to_ansible_cfg, to_plugins_cfg, to_oidc_cfg, to_ldap_cfg, @@ -232,12 +232,12 @@ async def ansible_sync_source(idx: int): source = sources[idx] if source.get("type") != "git": return 'Not a git source' - from fabledscryer.ansible.sources import git_pull - from fabledscryer.core.settings import to_ansible_cfg + from roundtable.ansible.sources import git_pull + from roundtable.core.settings import to_ansible_cfg async with current_app.db_sessionmaker() as db: settings = await get_all_settings(db) ansible_cfg = to_ansible_cfg(settings) - from fabledscryer.ansible.sources import get_sources + from roundtable.ansible.sources import get_sources resolved = next((s for s in get_sources(ansible_cfg) if s["name"] == source["name"]), None) if resolved is None: return 'Could not resolve source path' @@ -295,7 +295,7 @@ async def save_auth_settings(): await log_audit(current_app, session.get("user_id"), session.get("username", ""), "settings.saved", detail={"section": "auth"}) # Clear OIDC discovery cache so new settings take effect - from fabledscryer.auth.oidc import _discovery_cache + from roundtable.auth.oidc import _discovery_cache _discovery_cache.clear() return redirect(url_for("settings.auth_settings")) @@ -327,7 +327,7 @@ async def save_reports(): @settings_bp.post("/reports/send-now/") @require_role(UserRole.admin) async def send_report_now(): - from fabledscryer.core.reports import send_report + from roundtable.core.reports import send_report async with current_app.db_sessionmaker() as db: settings = await get_all_settings(db) ok, message = await send_report(current_app._get_current_object()) @@ -442,7 +442,7 @@ async def plugin_detail(name: str): if plugin is None: return redirect(url_for("settings.plugins")) _merge_plugin_config([plugin], to_plugins_cfg(settings)) - from fabledscryer.core.plugin_manager import _LOADED_PLUGINS, get_plugin_failures + from roundtable.core.plugin_manager import _LOADED_PLUGINS, get_plugin_failures return await render_template( "settings/plugin_detail.html", plugin=plugin, @@ -471,7 +471,7 @@ async def save_plugin_detail(name: str): await set_setting(db, f"plugin.{name}", plugin_cfg) await _reload_app_config() - from fabledscryer.core.plugin_index import clear_catalog_cache + from roundtable.core.plugin_index import clear_catalog_cache clear_catalog_cache() new_enabled = plugin_cfg.get("enabled", False) @@ -480,7 +480,7 @@ async def save_plugin_detail(name: str): await log_audit(current_app, session.get("user_id"), session.get("username", ""), action, entity_type="plugin", entity_id=name) if new_enabled and not old_enabled: - from fabledscryer.core.plugin_manager import hot_reload_plugin + from roundtable.core.plugin_manager import hot_reload_plugin hot_reload_plugin(current_app._get_current_object(), name) if name == "ups": @@ -505,7 +505,7 @@ async def plugin_repos_add(): async with current_app.db_sessionmaker() as db: async with db.begin(): await _save_plugin_repos(db, repos) - from fabledscryer.core.plugin_index import clear_catalog_cache + from roundtable.core.plugin_index import clear_catalog_cache clear_catalog_cache() return await _plugin_repos_partial({"plugins.repositories": repos}) @@ -521,7 +521,7 @@ async def plugin_repos_remove(idx: int): async with current_app.db_sessionmaker() as db: async with db.begin(): await _save_plugin_repos(db, repos) - from fabledscryer.core.plugin_index import clear_catalog_cache + from roundtable.core.plugin_index import clear_catalog_cache clear_catalog_cache() return await _plugin_repos_partial({"plugins.repositories": repos}) @@ -541,7 +541,7 @@ async def plugin_repos_save(idx: int): async with current_app.db_sessionmaker() as db: async with db.begin(): await _save_plugin_repos(db, repos) - from fabledscryer.core.plugin_index import clear_catalog_cache + from roundtable.core.plugin_index import clear_catalog_cache clear_catalog_cache() return await _plugin_repos_partial({"plugins.repositories": repos}) @@ -595,8 +595,8 @@ async def plugins_catalog(): error="No plugin repositories configured.", ) - from fabledscryer.core.plugin_index import fetch_catalog - from fabledscryer.core.plugin_manager import _LOADED_PLUGINS + from roundtable.core.plugin_index import fetch_catalog + from roundtable.core.plugin_manager import _LOADED_PLUGINS force = request.args.get("refresh") == "1" try: @@ -637,7 +637,7 @@ async def install_plugin(name: str): message="No download URL provided.", ) - from fabledscryer.core.plugin_manager import download_and_install_plugin, hot_reload_plugin + from roundtable.core.plugin_manager import download_and_install_plugin, hot_reload_plugin ok, msg = await download_and_install_plugin( current_app._get_current_object(), name, download_url, checksum @@ -671,7 +671,7 @@ async def install_plugin(name: str): @require_role(UserRole.admin) async def reload_plugin(name: str): """Hot-reload a plugin that was installed but not yet active.""" - from fabledscryer.core.plugin_manager import hot_reload_plugin + from roundtable.core.plugin_manager import hot_reload_plugin ok, msg = hot_reload_plugin(current_app._get_current_object(), name) return await render_template( "settings/_install_result.html", @@ -687,7 +687,7 @@ async def reload_plugin(name: str): async def restart_app_route(): """Trigger an in-app restart (replaces the process via os.execv).""" import asyncio - from fabledscryer.core.plugin_manager import restart_app + from roundtable.core.plugin_manager import restart_app # Schedule the restart slightly after we return the response async def _delayed_restart(): diff --git a/roundtable/templates/settings/ansible.html b/roundtable/templates/settings/ansible.html index 2660902..d549416 100644 --- a/roundtable/templates/settings/ansible.html +++ b/roundtable/templates/settings/ansible.html @@ -1,4 +1,4 @@ -{# fabledscryer/templates/settings/ansible.html #} +{# roundtable/templates/settings/ansible.html #} {% extends "base.html" %} {% block title %}Settings — Ansible — Fabled Scryer{% endblock %} {% block content %} diff --git a/roundtable/templates/settings/auth.html b/roundtable/templates/settings/auth.html index a40d7d0..1e4a533 100644 --- a/roundtable/templates/settings/auth.html +++ b/roundtable/templates/settings/auth.html @@ -1,4 +1,4 @@ -{# fabledscryer/templates/settings/auth.html #} +{# roundtable/templates/settings/auth.html #} {% extends "base.html" %} {% block title %}Settings — Auth — Fabled Scryer{% endblock %} {% block content %} @@ -25,7 +25,7 @@
+ placeholder="https://auth.example.com/application/o/roundtable/.well-known/openid-configuration">

Authentik: https://<host>/application/o/<slug>/.well-known/openid-configuration

@@ -72,13 +72,13 @@
+ placeholder="roundtable-admins">

Members → admin role

+ placeholder="roundtable-operators">

Members → operator role; others → viewer

@@ -119,7 +119,7 @@
+ placeholder="cn=svc-roundtable,ou=service,dc=example,dc=com">
diff --git a/roundtable/templates/settings/general.html b/roundtable/templates/settings/general.html index c3043ae..611f22a 100644 --- a/roundtable/templates/settings/general.html +++ b/roundtable/templates/settings/general.html @@ -1,4 +1,4 @@ -{# fabledscryer/templates/settings/general.html #} +{# roundtable/templates/settings/general.html #} {% extends "base.html" %} {% block title %}Settings — General — Fabled Scryer{% endblock %} {% block content %} diff --git a/roundtable/templates/settings/index.html b/roundtable/templates/settings/index.html index 1286f8c..858dc86 100644 --- a/roundtable/templates/settings/index.html +++ b/roundtable/templates/settings/index.html @@ -1,4 +1,4 @@ -{# fabledscryer/templates/settings/index.html #} +{# roundtable/templates/settings/index.html #} {% extends "base.html" %} {% block title %}Settings — Fabled Scryer{% endblock %} {% block content %} diff --git a/roundtable/templates/settings/notifications.html b/roundtable/templates/settings/notifications.html index 1625ff5..3447b7a 100644 --- a/roundtable/templates/settings/notifications.html +++ b/roundtable/templates/settings/notifications.html @@ -1,4 +1,4 @@ -{# fabledscryer/templates/settings/notifications.html #} +{# roundtable/templates/settings/notifications.html #} {% extends "base.html" %} {% block title %}Settings — Notifications — Fabled Scryer{% endblock %} {% block content %} diff --git a/roundtable/templates/settings/plugin_detail.html b/roundtable/templates/settings/plugin_detail.html index f1c88a3..90cb70d 100644 --- a/roundtable/templates/settings/plugin_detail.html +++ b/roundtable/templates/settings/plugin_detail.html @@ -1,4 +1,4 @@ -{# fabledscryer/templates/settings/plugin_detail.html #} +{# roundtable/templates/settings/plugin_detail.html #} {% extends "base.html" %} {% block title %}{{ plugin.get('name', plugin._dir) }} Settings — Fabled Scryer{% endblock %} {% block content %} diff --git a/roundtable/templates/settings/plugins.html b/roundtable/templates/settings/plugins.html index 990ba53..de7d14c 100644 --- a/roundtable/templates/settings/plugins.html +++ b/roundtable/templates/settings/plugins.html @@ -1,4 +1,4 @@ -{# fabledscryer/templates/settings/plugins.html #} +{# roundtable/templates/settings/plugins.html #} {% extends "base.html" %} {% block title %}Settings — Plugins — Fabled Scryer{% endblock %} {% block content %} diff --git a/roundtable/templates/settings/reports.html b/roundtable/templates/settings/reports.html index 7615a6c..a52ae5e 100644 --- a/roundtable/templates/settings/reports.html +++ b/roundtable/templates/settings/reports.html @@ -1,4 +1,4 @@ -{# fabledscryer/templates/settings/reports.html #} +{# roundtable/templates/settings/reports.html #} {% extends "base.html" %} {% block title %}Settings — Reports — Fabled Scryer{% endblock %} {% block content %} diff --git a/tests/conftest.py b/tests/conftest.py index 6459198..1e68dd0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,7 @@ import pytest import pytest_asyncio from pathlib import Path import textwrap -from fabledscryer.app import create_app +from roundtable.app import create_app @pytest.fixture diff --git a/tests/test_app.py b/tests/test_app.py index 219ec5e..006f674 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,5 +1,5 @@ import pytest -from fabledscryer.app import create_app +from roundtable.app import create_app @pytest.mark.asyncio diff --git a/tests/test_auth.py b/tests/test_auth.py index 876e318..f6933d4 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -1,7 +1,7 @@ import pytest import bcrypt from unittest.mock import AsyncMock, patch -from fabledscryer.models.users import User, UserRole +from roundtable.models.users import User, UserRole def make_user(role=UserRole.admin): @@ -23,7 +23,7 @@ async def test_login_page_returns_200(client): @pytest.mark.asyncio async def test_login_redirects_to_setup_when_no_users(client, app): - with patch("fabledscryer.auth.routes.get_user_count", new=AsyncMock(return_value=0)): + with patch("roundtable.auth.routes.get_user_count", new=AsyncMock(return_value=0)): response = await client.get("/login") assert response.status_code == 302 assert "/setup" in response.headers["Location"] @@ -32,8 +32,8 @@ async def test_login_redirects_to_setup_when_no_users(client, app): @pytest.mark.asyncio async def test_login_success_redirects_to_dashboard(client, app): user = make_user() - with patch("fabledscryer.auth.routes.get_user_count", new=AsyncMock(return_value=1)), \ - patch("fabledscryer.auth.routes.get_user_by_username", new=AsyncMock(return_value=user)): + with patch("roundtable.auth.routes.get_user_count", new=AsyncMock(return_value=1)), \ + patch("roundtable.auth.routes.get_user_by_username", new=AsyncMock(return_value=user)): response = await client.post("/login", form={"username": "admin", "password": "password"}) assert response.status_code == 302 assert "/" in response.headers["Location"] @@ -42,8 +42,8 @@ async def test_login_success_redirects_to_dashboard(client, app): @pytest.mark.asyncio async def test_login_failure_returns_400(client, app): user = make_user() - with patch("fabledscryer.auth.routes.get_user_count", new=AsyncMock(return_value=1)), \ - patch("fabledscryer.auth.routes.get_user_by_username", new=AsyncMock(return_value=user)): + with patch("roundtable.auth.routes.get_user_count", new=AsyncMock(return_value=1)), \ + patch("roundtable.auth.routes.get_user_by_username", new=AsyncMock(return_value=user)): response = await client.post("/login", form={"username": "admin", "password": "wrong"}) assert response.status_code == 400 diff --git a/tests/test_config.py b/tests/test_config.py index 31fcf20..ba5a019 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,18 +1,18 @@ import os import textwrap import pytest -from fabledscryer.config import load_bootstrap +from roundtable.config import load_bootstrap def test_load_bootstrap_from_yaml(tmp_path): cfg_file = tmp_path / "config.yaml" cfg_file.write_text(textwrap.dedent("""\ database: - url: postgresql+asyncpg://user:pass@localhost/fabledscryer + url: postgresql+asyncpg://user:pass@localhost/roundtable secret_key: test-secret """)) cfg = load_bootstrap(cfg_file) - assert cfg["database_url"] == "postgresql+asyncpg://user:pass@localhost/fabledscryer" + assert cfg["database_url"] == "postgresql+asyncpg://user:pass@localhost/roundtable" assert cfg["secret_key"] == "test-secret" diff --git a/tests/test_models.py b/tests/test_models.py index f09c952..fbed0c7 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,5 +1,5 @@ import pytest -from fabledscryer.models.users import User, UserRole +from roundtable.models.users import User, UserRole def test_user_model_fields():