refactor: update imports fabledscryer → roundtable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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")
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# fabledscryer/app.py
|
||||
# roundtable/app.py
|
||||
from __future__ import annotations
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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.).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# fabledscryer/config.py
|
||||
# roundtable/config.py
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
import os
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# fabledscryer/core/migration_runner.py
|
||||
# roundtable/core/migration_runner.py
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/<name>/ 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,
|
||||
)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -1 +1 @@
|
||||
# fabledscryer/settings/__init__.py
|
||||
# roundtable/settings/__init__.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 '<span style="color:var(--text-muted);font-size:0.8rem;">Not a git source</span>'
|
||||
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 '<span style="color:var(--red);font-size:0.8rem;">Could not resolve source path</span>'
|
||||
@@ -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():
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 @@
|
||||
<div class="form-group">
|
||||
<label>Discovery URL</label>
|
||||
<input type="text" name="oidc.discovery_url" value="{{ settings['oidc.discovery_url'] }}"
|
||||
placeholder="https://auth.example.com/application/o/fabledscryer/.well-known/openid-configuration">
|
||||
placeholder="https://auth.example.com/application/o/roundtable/.well-known/openid-configuration">
|
||||
<p style="font-size:0.75rem;color:var(--text-muted);margin-top:0.2rem;">
|
||||
Authentik: <code>https://<host>/application/o/<slug>/.well-known/openid-configuration</code>
|
||||
</p>
|
||||
@@ -72,13 +72,13 @@
|
||||
<div class="form-group">
|
||||
<label>Admin group name</label>
|
||||
<input type="text" name="oidc.admin_group" value="{{ settings['oidc.admin_group'] }}"
|
||||
placeholder="fabledscryer-admins">
|
||||
placeholder="roundtable-admins">
|
||||
<p style="font-size:0.75rem;color:var(--text-muted);margin-top:0.2rem;">Members → admin role</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Operator group name</label>
|
||||
<input type="text" name="oidc.operator_group" value="{{ settings['oidc.operator_group'] }}"
|
||||
placeholder="fabledscryer-operators">
|
||||
placeholder="roundtable-operators">
|
||||
<p style="font-size:0.75rem;color:var(--text-muted);margin-top:0.2rem;">Members → operator role; others → viewer</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -119,7 +119,7 @@
|
||||
<div class="form-group">
|
||||
<label>Service account bind DN</label>
|
||||
<input type="text" name="ldap.bind_dn" value="{{ settings['ldap.bind_dn'] }}"
|
||||
placeholder="cn=svc-fabledscryer,ou=service,dc=example,dc=com">
|
||||
placeholder="cn=svc-roundtable,ou=service,dc=example,dc=com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Bind password <span style="color:var(--text-dim);font-size:0.8rem;">(blank = keep current)</span></label>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import pytest
|
||||
from fabledscryer.app import create_app
|
||||
from roundtable.app import create_app
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
+6
-6
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user