docs: roundtable rename sweep
Sweeps README, docs/, example configs, code docstrings, report and notification subject lines, plugin catalog URL default, and stray comments in Dockerfile/entrypoint.sh/.gitignore. Adds a legacy note to docs/core/configuration.md explaining the FABLEDSCRYER_* fallback. docker-compose DB credentials intentionally left as fabledscryer to preserve the existing pgdata volume.
This commit is contained in:
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
# Copy to .env for Docker / local development secrets
|
# Copy to .env for Docker / local development secrets
|
||||||
# These override values in config.yaml
|
# These override values in config.yaml
|
||||||
|
|
||||||
FABLEDSCRYER_SECRET_KEY=change-me
|
ROUNDTABLE_SECRET_KEY=change-me
|
||||||
FABLEDSCRYER_DATABASE__URL=postgresql+asyncpg://fabledscryer:password@localhost/fabledscryer
|
ROUNDTABLE_DATABASE__URL=postgresql+asyncpg://roundtable:password@localhost/roundtable
|
||||||
FABLEDSCRYER_SMTP__PASSWORD=
|
ROUNDTABLE_SMTP__PASSWORD=
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# Planning files
|
# Planning files
|
||||||
docs/superpowers/
|
docs/superpowers/
|
||||||
|
|
||||||
# Plugin directory — managed as a separate repo (bvandeusen/fabledscryer-plugins)
|
# Plugin directory — managed as a separate repo (bvandeusen/roundtable-plugins)
|
||||||
# PLUGIN_DIR in config.yaml points here at runtime
|
# PLUGIN_DIR in config.yaml points here at runtime
|
||||||
/plugins/
|
/plugins/
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ RUN chmod +x /entrypoint.sh
|
|||||||
# Runtime directories — owned by app user.
|
# Runtime directories — owned by app user.
|
||||||
# The container runs as root so the entrypoint can start NUT daemons (USB
|
# The container runs as root so the entrypoint can start NUT daemons (USB
|
||||||
# access requires root); it drops to 'app' (uid 1000) via gosu before
|
# access requires root); it drops to 'app' (uid 1000) via gosu before
|
||||||
# launching fabledscryer.
|
# launching roundtable.
|
||||||
RUN useradd -m -u 1000 app \
|
RUN useradd -m -u 1000 app \
|
||||||
&& mkdir -p /data/playbook_cache /data/plugins \
|
&& mkdir -p /data/playbook_cache /data/plugins \
|
||||||
&& chown -R app:app /data /app
|
&& chown -R app:app /data /app
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Fabled Scryer
|
# Roundtable
|
||||||
|
|
||||||
A self-hosted network monitoring and infrastructure management hub for home servers. Fabled Scryer gives you a single pane of glass over your hosts, services, and automation — with live-updating dashboards, alerting, and Ansible integration.
|
A self-hosted network monitoring and infrastructure management hub for home servers. Roundtable gives you a single pane of glass over your hosts, services, and automation — with live-updating dashboards, alerting, and Ansible integration.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ No JavaScript framework. No build step. No external workers or message brokers.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
# Set FABLEDSCRYER_DATABASE_URL in .env
|
# Set ROUNDTABLE_DATABASE_URL in .env
|
||||||
|
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
@@ -41,7 +41,7 @@ pip install -e .
|
|||||||
cp config.example.yaml config.yaml
|
cp config.example.yaml config.yaml
|
||||||
# Edit config.yaml — set database.url at minimum
|
# Edit config.yaml — set database.url at minimum
|
||||||
|
|
||||||
fabledscryer --host 0.0.0.0 --port 5000
|
roundtable --host 0.0.0.0 --port 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
ICMP ping requires `CAP_NET_RAW` or the `setuid` ping binary. TCP mode (the default) needs no elevated privileges.
|
ICMP ping requires `CAP_NET_RAW` or the `setuid` ping binary. TCP mode (the default) needs no elevated privileges.
|
||||||
@@ -54,7 +54,7 @@ Only two things must be set to run the app:
|
|||||||
|
|
||||||
| What | How |
|
| What | How |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Database URL | `FABLEDSCRYER_DATABASE_URL` env var or `database.url` in `config.yaml` |
|
| Database URL | `ROUNDTABLE_DATABASE_URL` env var or `database.url` in `config.yaml` |
|
||||||
| Secret key | Auto-generated on first run and saved to `/data/secret.key` |
|
| Secret key | Auto-generated on first run and saved to `/data/secret.key` |
|
||||||
|
|
||||||
Everything else — SMTP, webhooks, monitor intervals, Ansible sources, plugin settings — is configured through the web UI at `/settings/`.
|
Everything else — SMTP, webhooks, monitor intervals, Ansible sources, plugin settings — is configured through the web UI at `/settings/`.
|
||||||
|
|||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
# Fabled Scryer — Bootstrap Configuration Example
|
# Roundtable — Bootstrap Configuration Example
|
||||||
#
|
#
|
||||||
# The only REQUIRED setting is the database URL.
|
# The only REQUIRED setting is the database URL.
|
||||||
# Set it via env var (recommended for Docker):
|
# Set it via env var (recommended for Docker):
|
||||||
#
|
#
|
||||||
# FABLEDSCRYER_DATABASE_URL=postgresql+asyncpg://user:pass@host/db
|
# ROUNDTABLE_DATABASE_URL=postgresql+asyncpg://user:pass@host/db
|
||||||
#
|
#
|
||||||
# All other settings (SMTP, webhook, ansible, plugins, retention)
|
# All other settings (SMTP, webhook, ansible, plugins, retention)
|
||||||
# are stored in the database and managed via the Settings UI at /settings/.
|
# are stored in the database and managed via the Settings UI at /settings/.
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
# config.yaml is optional. Use it only if you prefer file-based bootstrap.
|
# config.yaml is optional. Use it only if you prefer file-based bootstrap.
|
||||||
|
|
||||||
database:
|
database:
|
||||||
url: "postgresql+asyncpg://fabledscryer:password@localhost/fabledscryer"
|
url: "postgresql+asyncpg://roundtable:password@localhost/roundtable"
|
||||||
|
|
||||||
# Optional: override the auto-generated secret key.
|
# Optional: override the auto-generated secret key.
|
||||||
# If not set, a key is auto-generated on first run and saved to /data/secret.key.
|
# If not set, a key is auto-generated on first run and saved to /data/secret.key.
|
||||||
|
|||||||
+13
-13
@@ -1,12 +1,12 @@
|
|||||||
# Architecture
|
# Architecture
|
||||||
|
|
||||||
Fabled Scryer is a single Quart (async Python) process. There are no separate worker processes, no message brokers, and no build pipeline for the frontend. All monitoring, scheduling, and request handling runs on a single asyncio event loop.
|
Roundtable is a single Quart (async Python) process. There are no separate worker processes, no message brokers, and no build pipeline for the frontend. All monitoring, scheduling, and request handling runs on a single asyncio event loop.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Startup Sequence
|
## Startup Sequence
|
||||||
|
|
||||||
`create_app()` in `fabledscryer/app.py` runs these steps synchronously before the event loop starts:
|
`create_app()` in `roundtable/app.py` runs these steps synchronously before the event loop starts:
|
||||||
|
|
||||||
| Step | What happens |
|
| Step | What happens |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -32,14 +32,14 @@ All routes are Quart Blueprints registered in `app.py`:
|
|||||||
|
|
||||||
| Prefix | Blueprint | Module |
|
| Prefix | Blueprint | Module |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `/auth/` | `auth_bp` | `fabledscryer/auth/routes.py` |
|
| `/auth/` | `auth_bp` | `roundtable/auth/routes.py` |
|
||||||
| `/` | `dashboard_bp` | `fabledscryer/dashboard/routes.py` |
|
| `/` | `dashboard_bp` | `roundtable/dashboard/routes.py` |
|
||||||
| `/hosts/` | `hosts_bp` | `fabledscryer/hosts/routes.py` |
|
| `/hosts/` | `hosts_bp` | `roundtable/hosts/routes.py` |
|
||||||
| `/ping/` | `ping_bp` | `fabledscryer/ping/routes.py` |
|
| `/ping/` | `ping_bp` | `roundtable/ping/routes.py` |
|
||||||
| `/dns/` | `dns_bp` | `fabledscryer/dns/routes.py` |
|
| `/dns/` | `dns_bp` | `roundtable/dns/routes.py` |
|
||||||
| `/alerts/` | `alerts_bp` | `fabledscryer/alerts/routes.py` |
|
| `/alerts/` | `alerts_bp` | `roundtable/alerts/routes.py` |
|
||||||
| `/ansible/` | `ansible_bp` | `fabledscryer/ansible/routes.py` |
|
| `/ansible/` | `ansible_bp` | `roundtable/ansible/routes.py` |
|
||||||
| `/settings/` | `settings_bp` | `fabledscryer/settings/routes.py` |
|
| `/settings/` | `settings_bp` | `roundtable/settings/routes.py` |
|
||||||
| `/plugins/<name>/` | plugin blueprint | `plugins/<name>/routes.py` |
|
| `/plugins/<name>/` | plugin blueprint | `plugins/<name>/routes.py` |
|
||||||
|
|
||||||
Plugin blueprints are mounted automatically by `load_plugins()` using the plugin directory name as the URL prefix.
|
Plugin blueprints are mounted automatically by `load_plugins()` using the plugin directory name as the URL prefix.
|
||||||
@@ -48,7 +48,7 @@ Plugin blueprints are mounted automatically by `load_plugins()` using the plugin
|
|||||||
|
|
||||||
## Scheduler
|
## Scheduler
|
||||||
|
|
||||||
`fabledscryer/core/scheduler.py` exports two things:
|
`roundtable/core/scheduler.py` exports two things:
|
||||||
|
|
||||||
- `ScheduledTask` dataclass — holds a `name`, `coro_factory` (zero-argument callable returning a coroutine), `interval_seconds`, and optional `run_on_startup` flag
|
- `ScheduledTask` dataclass — holds a `name`, `coro_factory` (zero-argument callable returning a coroutine), `interval_seconds`, and optional `run_on_startup` flag
|
||||||
- `start_scheduler(tasks)` — async function that loops every second, calling `asyncio.create_task()` for each task whose interval has elapsed
|
- `start_scheduler(tasks)` — async function that loops every second, calling `asyncio.create_task()` for each task whose interval has elapsed
|
||||||
@@ -93,9 +93,9 @@ This means the only file you must touch to get the app running is the database U
|
|||||||
|
|
||||||
No JavaScript framework, no build step. The frontend is:
|
No JavaScript framework, no build step. The frontend is:
|
||||||
|
|
||||||
- **Jinja2 templates** rendered server-side (`fabledscryer/templates/`)
|
- **Jinja2 templates** rendered server-side (`roundtable/templates/`)
|
||||||
- **HTMX** for live-updating fragments (dashboard widgets, ping pills, DNS status)
|
- **HTMX** for live-updating fragments (dashboard widgets, ping pills, DNS status)
|
||||||
- A single CSS design system in `fabledscryer/templates/base.html` using CSS custom properties
|
- A single CSS design system in `roundtable/templates/base.html` using CSS custom properties
|
||||||
|
|
||||||
Live-updating widgets use HTMX polling:
|
Live-updating widgets use HTMX polling:
|
||||||
```html
|
```html
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ When any monitor or plugin calls `record_metric()`:
|
|||||||
4. If a state transition occurs, an `AlertEvent` row is written
|
4. If a state transition occurs, an `AlertEvent` row is written
|
||||||
5. Notification I/O is deferred outside the transaction via `asyncio.create_task()`
|
5. Notification I/O is deferred outside the transaction via `asyncio.create_task()`
|
||||||
|
|
||||||
**Key function:** `fabledscryer/core/alerts.py` → `record_metric(session, source_module, resource_name, metric_name, value)`
|
**Key function:** `roundtable/core/alerts.py` → `record_metric(session, source_module, resource_name, metric_name, value)`
|
||||||
|
|
||||||
`record_metric()` must always be called inside an active transaction:
|
`record_metric()` must always be called inside an active transaction:
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ Webhook is skipped if `webhook.url` is empty.
|
|||||||
|
|
||||||
## Data Models
|
## Data Models
|
||||||
|
|
||||||
Defined in `fabledscryer/models/alerts.py`:
|
Defined in `roundtable/models/alerts.py`:
|
||||||
|
|
||||||
- **`alert_rules`** — one row per configured rule
|
- **`alert_rules`** — one row per configured rule
|
||||||
- **`alert_states`** — one row per rule, tracks current state and consecutive failure count
|
- **`alert_states`** — one row per rule, tracks current state and consecutive failure count
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Ansible Integration
|
# Ansible Integration
|
||||||
|
|
||||||
Fabled Scryer can browse, trigger, and stream output from Ansible playbooks directly from the web UI. Runs execute as asyncio tasks inside the same process — no Celery, no external workers.
|
Roundtable can browse, trigger, and stream output from Ansible playbooks directly from the web UI. Runs execute as asyncio tasks inside the same process — no Celery, no external workers.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ Output stored in the DB is capped at 1 MB. If truncated, `[output truncated]` is
|
|||||||
|
|
||||||
## Data Model
|
## Data Model
|
||||||
|
|
||||||
`ansible_runs` table (defined in `fabledscryer/models/ansible.py`):
|
`ansible_runs` table (defined in `roundtable/models/ansible.py`):
|
||||||
|
|
||||||
| Column | Type | Description |
|
| Column | Type | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -137,7 +137,7 @@ Runs older than `data.retention_days` (default 90) are pruned by the `data_clean
|
|||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `fabledscryer/ansible/sources.py` | Source discovery, git pull logic |
|
| `roundtable/ansible/sources.py` | Source discovery, git pull logic |
|
||||||
| `fabledscryer/ansible/executor.py` | Subprocess execution and output streaming |
|
| `roundtable/ansible/executor.py` | Subprocess execution and output streaming |
|
||||||
| `fabledscryer/ansible/routes.py` | HTTP routes (browse, trigger, stream, history) |
|
| `roundtable/ansible/routes.py` | HTTP routes (browse, trigger, stream, history) |
|
||||||
| `fabledscryer/models/ansible.py` | `AnsibleRun` model |
|
| `roundtable/models/ansible.py` | `AnsibleRun` model |
|
||||||
|
|||||||
+12
-10
@@ -1,6 +1,6 @@
|
|||||||
# Configuration
|
# Configuration
|
||||||
|
|
||||||
Fabled Scryer uses a two-layer configuration system. Only the bare minimum needed to boot lives in files or environment variables. Everything else is stored in the database and managed through the Settings UI.
|
Roundtable uses a two-layer configuration system. Only the bare minimum needed to boot lives in files or environment variables. Everything else is stored in the database and managed through the Settings UI.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -10,29 +10,31 @@ These three values are read at startup from `config.yaml` and/or environment var
|
|||||||
|
|
||||||
| Key | Env var | Default | Description |
|
| Key | Env var | Default | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `database.url` | `FABLEDSCRYER_DATABASE_URL` | — | PostgreSQL async URL. **Required.** |
|
| `database.url` | `ROUNDTABLE_DATABASE_URL` | — | PostgreSQL async URL. **Required.** |
|
||||||
| `secret_key` | `FABLEDSCRYER_SECRET_KEY` | auto-generated | Flask/Quart session signing key. Auto-generated and saved to `/data/secret.key` if not set. |
|
| `secret_key` | `ROUNDTABLE_SECRET_KEY` | auto-generated | Flask/Quart session signing key. Auto-generated and saved to `/data/secret.key` if not set. |
|
||||||
| `plugin_dir` | `FABLEDSCRYER_PLUGIN_DIR` | `plugins` | Path to the plugins directory. |
|
| `plugin_dir` | `ROUNDTABLE_PLUGIN_DIR` | `plugins` | Path to the plugins directory. |
|
||||||
|
|
||||||
**Resolution order for `database_url`:** env var `FABLEDSCRYER_DATABASE_URL` → env var `FABLEDSCRYER_DATABASE__URL` (legacy double-underscore) → `database.url` in `config.yaml`.
|
**Resolution order for `database_url`:** env var `ROUNDTABLE_DATABASE_URL` → env var `ROUNDTABLE_DATABASE__URL` (legacy double-underscore) → `database.url` in `config.yaml`.
|
||||||
|
|
||||||
**Resolution order for `secret_key`:** env var `FABLEDSCRYER_SECRET_KEY` → `secret_key` in `config.yaml` → `/data/secret.key` file → auto-generate and write to `/data/secret.key`.
|
**Resolution order for `secret_key`:** env var `ROUNDTABLE_SECRET_KEY` → `secret_key` in `config.yaml` → `/data/secret.key` file → auto-generate and write to `/data/secret.key`.
|
||||||
|
|
||||||
### Minimal config.yaml
|
### Minimal config.yaml
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
database:
|
database:
|
||||||
url: "postgresql+asyncpg://user:password@localhost/fabledscryer"
|
url: "postgresql+asyncpg://user:password@localhost/roundtable"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Minimal env-only setup (Docker)
|
### Minimal env-only setup (Docker)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
FABLEDSCRYER_DATABASE_URL=postgresql+asyncpg://user:password@db/fabledscryer
|
ROUNDTABLE_DATABASE_URL=postgresql+asyncpg://user:password@db/roundtable
|
||||||
```
|
```
|
||||||
|
|
||||||
A `.env` file is loaded automatically if present.
|
A `.env` file is loaded automatically if present.
|
||||||
|
|
||||||
|
> **Legacy env vars:** `FABLEDSCRYER_*` env vars are still accepted as a fallback for existing deployments. They will be removed in a future release — migrate to `ROUNDTABLE_*` when convenient.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## App Settings (Database-backed)
|
## App Settings (Database-backed)
|
||||||
@@ -66,7 +68,7 @@ Default webhook template:
|
|||||||
### Reading and Writing Settings in Code
|
### Reading and Writing Settings in Code
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from fabledscryer.core.settings import get_setting, set_setting
|
from roundtable.core.settings import get_setting, set_setting
|
||||||
|
|
||||||
# Read
|
# Read
|
||||||
async with current_app.db_sessionmaker() as session:
|
async with current_app.db_sessionmaker() as session:
|
||||||
@@ -82,7 +84,7 @@ async with current_app.db_sessionmaker() as session:
|
|||||||
|
|
||||||
### The DEFAULTS Dict
|
### The DEFAULTS Dict
|
||||||
|
|
||||||
`fabledscryer/core/settings.py` contains the `DEFAULTS` dict — the canonical list of all recognised settings and their default values. Add new settings here to make them recognised by `get_all_settings()` and the settings UI.
|
`roundtable/core/settings.py` contains the `DEFAULTS` dict — the canonical list of all recognised settings and their default values. Add new settings here to make them recognised by `get_all_settings()` and the settings UI.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# Core Monitors
|
# Core Monitors
|
||||||
|
|
||||||
Fabled Scryer ships two built-in monitors: Ping and DNS. Both run as asyncio scheduled tasks on the same event loop as the web server, with no separate processes.
|
Roundtable ships two built-in monitors: Ping and DNS. Both run as asyncio scheduled tasks on the same event loop as the web server, with no separate processes.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Ping Monitor
|
## Ping Monitor
|
||||||
|
|
||||||
**Source:** `fabledscryer/monitors/ping.py`
|
**Source:** `roundtable/monitors/ping.py`
|
||||||
**Scheduler task:** `ping_monitor` in `fabledscryer/app.py`
|
**Scheduler task:** `ping_monitor` in `roundtable/app.py`
|
||||||
**Interval:** `monitors.poll_interval_seconds` (default 60s), runs on startup
|
**Interval:** `monitors.poll_interval_seconds` (default 60s), runs on startup
|
||||||
|
|
||||||
### How It Works
|
### How It Works
|
||||||
@@ -29,7 +29,7 @@ Each probe writes a `PingResult` row and calls `record_metric()`:
|
|||||||
|
|
||||||
### Data Model
|
### Data Model
|
||||||
|
|
||||||
`ping_results` table (defined in `fabledscryer/models/monitors.py`):
|
`ping_results` table (defined in `roundtable/models/monitors.py`):
|
||||||
|
|
||||||
| Column | Type | Description |
|
| Column | Type | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -51,8 +51,8 @@ Old rows are pruned by the `data_cleanup` task (default: 90 days).
|
|||||||
|
|
||||||
## DNS Monitor
|
## DNS Monitor
|
||||||
|
|
||||||
**Source:** `fabledscryer/monitors/dns.py`
|
**Source:** `roundtable/monitors/dns.py`
|
||||||
**Scheduler task:** `dns_monitor` in `fabledscryer/app.py`
|
**Scheduler task:** `dns_monitor` in `roundtable/app.py`
|
||||||
**Interval:** `monitors.poll_interval_seconds` (default 60s), runs on startup
|
**Interval:** `monitors.poll_interval_seconds` (default 60s), runs on startup
|
||||||
|
|
||||||
### How It Works
|
### How It Works
|
||||||
@@ -70,7 +70,7 @@ Each check writes a `DnsResult` row and calls `record_metric()`:
|
|||||||
|
|
||||||
### Data Model
|
### Data Model
|
||||||
|
|
||||||
`dns_results` table (defined in `fabledscryer/models/monitors.py`):
|
`dns_results` table (defined in `roundtable/models/monitors.py`):
|
||||||
|
|
||||||
| Column | Type | Description |
|
| Column | Type | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# fabledscryer-plugins / index.yaml
|
# roundtable-plugins / index.yaml
|
||||||
#
|
#
|
||||||
# This file is the catalog index for the fabledscryer plugin repository.
|
# This file is the catalog index for the roundtable plugin repository.
|
||||||
# It is fetched by the app's Settings → Plugins → Plugin Catalog UI.
|
# It is fetched by the app's Settings → Plugins → Plugin Catalog UI.
|
||||||
#
|
#
|
||||||
# Fabled Scryer reads this file from:
|
# Roundtable reads this file from:
|
||||||
# https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/raw/branch/main/index.yaml
|
# https://git.fabledsword.com/bvandeusen/Roundtable-plugins/raw/branch/main/index.yaml
|
||||||
#
|
#
|
||||||
# After adding or updating a plugin entry, commit and push — the change is
|
# After adding or updating a plugin entry, commit and push — the change is
|
||||||
# live immediately for anyone whose app fetches the catalog (cache TTL: 5 min).
|
# live immediately for anyone whose app fetches the catalog (cache TTL: 5 min).
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
#
|
#
|
||||||
# Download URL conventions:
|
# Download URL conventions:
|
||||||
# Gitea release assets (recommended):
|
# Gitea release assets (recommended):
|
||||||
# https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/releases/download/traefik-v1.0.0/traefik.zip
|
# https://git.fabledsword.com/bvandeusen/Roundtable-plugins/releases/download/traefik-v1.0.0/traefik.zip
|
||||||
# Upload the zip as a release attachment in Gitea; paste the URL here.
|
# Upload the zip as a release attachment in Gitea; paste the URL here.
|
||||||
# Source archive tarballs work too but release assets are preferred (smaller, plugin-only).
|
# Source archive tarballs work too but release assets are preferred (smaller, plugin-only).
|
||||||
|
|
||||||
@@ -37,12 +37,12 @@ plugins:
|
|||||||
- name: http
|
- name: http
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
description: "Synthetic HTTP endpoint monitoring — status code, response time, content match, TLS expiry"
|
description: "Synthetic HTTP endpoint monitoring — status code, response time, content match, TLS expiry"
|
||||||
author: "FabledScryer"
|
author: "Roundtable"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
min_app_version: "0.1.0"
|
min_app_version: "0.1.0"
|
||||||
repository_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins"
|
repository_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins"
|
||||||
homepage: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/src/branch/main/http"
|
homepage: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/src/branch/main/http"
|
||||||
download_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/releases/download/http-v1.0.0/http.zip"
|
download_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/releases/download/http-v1.0.0/http.zip"
|
||||||
checksum_sha256: ""
|
checksum_sha256: ""
|
||||||
tags:
|
tags:
|
||||||
- monitoring
|
- monitoring
|
||||||
@@ -53,12 +53,12 @@ plugins:
|
|||||||
- name: docker
|
- name: docker
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
description: "Docker container status, resource usage, and restart tracking via Docker socket"
|
description: "Docker container status, resource usage, and restart tracking via Docker socket"
|
||||||
author: "FabledScryer"
|
author: "Roundtable"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
min_app_version: "0.1.0"
|
min_app_version: "0.1.0"
|
||||||
repository_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins"
|
repository_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins"
|
||||||
homepage: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/src/branch/main/docker"
|
homepage: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/src/branch/main/docker"
|
||||||
download_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/releases/download/docker-v1.0.0/docker.zip"
|
download_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/releases/download/docker-v1.0.0/docker.zip"
|
||||||
checksum_sha256: ""
|
checksum_sha256: ""
|
||||||
tags:
|
tags:
|
||||||
- containers
|
- containers
|
||||||
@@ -68,12 +68,12 @@ plugins:
|
|||||||
- name: traefik
|
- name: traefik
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
description: "Traefik reverse proxy metrics and access log integration"
|
description: "Traefik reverse proxy metrics and access log integration"
|
||||||
author: "FabledScryer"
|
author: "Roundtable"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
min_app_version: "0.1.0"
|
min_app_version: "0.1.0"
|
||||||
repository_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins"
|
repository_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins"
|
||||||
homepage: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/src/branch/main/traefik"
|
homepage: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/src/branch/main/traefik"
|
||||||
download_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/releases/download/traefik-v1.0.0/traefik.zip"
|
download_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/releases/download/traefik-v1.0.0/traefik.zip"
|
||||||
checksum_sha256: "" # fill in after running: sha256sum traefik.zip
|
checksum_sha256: "" # fill in after running: sha256sum traefik.zip
|
||||||
tags:
|
tags:
|
||||||
- proxy
|
- proxy
|
||||||
@@ -83,12 +83,12 @@ plugins:
|
|||||||
- name: unifi
|
- name: unifi
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
description: "UniFi Network controller integration — WAN health, devices, clients, DPI"
|
description: "UniFi Network controller integration — WAN health, devices, clients, DPI"
|
||||||
author: "FabledScryer"
|
author: "Roundtable"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
min_app_version: "0.1.0"
|
min_app_version: "0.1.0"
|
||||||
repository_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins"
|
repository_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins"
|
||||||
homepage: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/src/branch/main/unifi"
|
homepage: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/src/branch/main/unifi"
|
||||||
download_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/releases/download/unifi-v1.0.0/unifi.zip"
|
download_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/releases/download/unifi-v1.0.0/unifi.zip"
|
||||||
checksum_sha256: ""
|
checksum_sha256: ""
|
||||||
tags:
|
tags:
|
||||||
- network
|
- network
|
||||||
@@ -98,12 +98,12 @@ plugins:
|
|||||||
- name: ups
|
- name: ups
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
description: "UPS monitoring via NUT (Network UPS Tools) with Ansible shutdown automation"
|
description: "UPS monitoring via NUT (Network UPS Tools) with Ansible shutdown automation"
|
||||||
author: "FabledScryer"
|
author: "Roundtable"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
min_app_version: "0.1.0"
|
min_app_version: "0.1.0"
|
||||||
repository_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins"
|
repository_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins"
|
||||||
homepage: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/src/branch/main/ups"
|
homepage: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/src/branch/main/ups"
|
||||||
download_url: "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/releases/download/ups-v1.0.0/ups.zip"
|
download_url: "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/releases/download/ups-v1.0.0/ups.zip"
|
||||||
checksum_sha256: ""
|
checksum_sha256: ""
|
||||||
tags:
|
tags:
|
||||||
- ups
|
- ups
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Plugin System Overview
|
# Plugin System Overview
|
||||||
|
|
||||||
Plugins extend Fabled Scryer with new data sources, UI pages, scheduled tasks, and dashboard widgets. Only enabled plugins are imported — disabled or unlisted plugins have zero runtime overhead.
|
Plugins extend Roundtable with new data sources, UI pages, scheduled tasks, and dashboard widgets. Only enabled plugins are imported — disabled or unlisted plugins have zero runtime overhead.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## How Plugins Are Loaded
|
## How Plugins Are Loaded
|
||||||
|
|
||||||
Plugin loading happens in step 9 of `create_app()`, after all core blueprints and tasks are registered. The entrypoint is `load_plugins(app)` in `fabledscryer/core/plugin_manager.py`.
|
Plugin loading happens in step 9 of `create_app()`, after all core blueprints and tasks are registered. The entrypoint is `load_plugins(app)` in `roundtable/core/plugin_manager.py`.
|
||||||
|
|
||||||
For each plugin listed as `enabled: true` in the `PLUGINS` config:
|
For each plugin listed as `enabled: true` in the `PLUGINS` config:
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ description: "Does a thing"
|
|||||||
|
|
||||||
# Optional
|
# Optional
|
||||||
author: "Your Name"
|
author: "Your Name"
|
||||||
min_app_version: "0.1.0" # Minimum Fabled Scryer version required
|
min_app_version: "0.1.0" # Minimum Roundtable version required
|
||||||
|
|
||||||
# Default config — merged with user overrides at runtime
|
# Default config — merged with user overrides at runtime
|
||||||
# Access at runtime via: app.config["PLUGINS"]["myplugin"]["my_setting"]
|
# Access at runtime via: app.config["PLUGINS"]["myplugin"]["my_setting"]
|
||||||
@@ -90,7 +90,7 @@ def setup(app):
|
|||||||
Returns a list of `ScheduledTask` objects. Return `[]` if the plugin has no background tasks. Called after `setup()`, so any app references set in `setup()` are available.
|
Returns a list of `ScheduledTask` objects. Return `[]` if the plugin has no background tasks. Called after `setup()`, so any app references set in `setup()` are available.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from fabledscryer.core.scheduler import ScheduledTask
|
from roundtable.core.scheduler import ScheduledTask
|
||||||
|
|
||||||
def get_scheduled_tasks():
|
def get_scheduled_tasks():
|
||||||
app = _app
|
app = _app
|
||||||
@@ -154,7 +154,7 @@ A plugin can contribute a dashboard widget by:
|
|||||||
1. Adding a `GET /widget` route to its blueprint that returns an HTMX HTML fragment
|
1. Adding a `GET /widget` route to its blueprint that returns an HTMX HTML fragment
|
||||||
2. The dashboard template polling that endpoint with HTMX
|
2. The dashboard template polling that endpoint with HTMX
|
||||||
|
|
||||||
The dashboard (`fabledscryer/templates/dashboard/index.html`) currently includes the Traefik widget conditionally based on `traefik_enabled`. To add a new plugin widget, the dashboard route and template both need to be updated to detect and render the new widget. See the Traefik widget implementation as a reference.
|
The dashboard (`roundtable/templates/dashboard/index.html`) currently includes the Traefik widget conditionally based on `traefik_enabled`. To add a new plugin widget, the dashboard route and template both need to be updated to detect and render the new widget. See the Traefik widget implementation as a reference.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ config:
|
|||||||
|
|
||||||
## Step 3: Define Models (if needed)
|
## Step 3: Define Models (if needed)
|
||||||
|
|
||||||
If your plugin stores data, define SQLAlchemy models using the shared `Base` from `fabledscryer.models.base`.
|
If your plugin stores data, define SQLAlchemy models using the shared `Base` from `roundtable.models.base`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# plugins/myplugin/models.py
|
# plugins/myplugin/models.py
|
||||||
@@ -55,7 +55,7 @@ import uuid
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from sqlalchemy import String, Float, DateTime
|
from sqlalchemy import String, Float, DateTime
|
||||||
from sqlalchemy.orm import Mapped, mapped_column
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
from fabledscryer.models.base import Base
|
from roundtable.models.base import Base
|
||||||
|
|
||||||
|
|
||||||
class MyPluginMetric(Base):
|
class MyPluginMetric(Base):
|
||||||
@@ -79,7 +79,7 @@ Generate the initial migration:
|
|||||||
# From the project root
|
# From the project root
|
||||||
alembic --config alembic.ini revision \
|
alembic --config alembic.ini revision \
|
||||||
--autogenerate \
|
--autogenerate \
|
||||||
--head=fabledscryer@head \
|
--head=roundtable@head \
|
||||||
--branch-label=myplugin \
|
--branch-label=myplugin \
|
||||||
-m "myplugin initial"
|
-m "myplugin initial"
|
||||||
```
|
```
|
||||||
@@ -106,8 +106,8 @@ Keep task logic in a separate file so `__init__.py` stays clean.
|
|||||||
# plugins/myplugin/scheduler.py
|
# plugins/myplugin/scheduler.py
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
from fabledscryer.core.scheduler import ScheduledTask
|
from roundtable.core.scheduler import ScheduledTask
|
||||||
from fabledscryer.core.alerts import record_metric
|
from roundtable.core.alerts import record_metric
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -174,8 +174,8 @@ async def _fetch_value(url: str) -> float:
|
|||||||
```python
|
```python
|
||||||
# plugins/myplugin/routes.py
|
# plugins/myplugin/routes.py
|
||||||
from quart import Blueprint, current_app, render_template
|
from quart import Blueprint, current_app, render_template
|
||||||
from fabledscryer.auth.middleware import require_role
|
from roundtable.auth.middleware import require_role
|
||||||
from fabledscryer.models.users import UserRole
|
from roundtable.models.users import UserRole
|
||||||
from .models import MyPluginMetric
|
from .models import MyPluginMetric
|
||||||
|
|
||||||
myplugin_bp = Blueprint("myplugin", __name__, template_folder="templates")
|
myplugin_bp = Blueprint("myplugin", __name__, template_folder="templates")
|
||||||
@@ -215,7 +215,7 @@ Templates live in `plugins/myplugin/templates/myplugin/` (the extra nesting avoi
|
|||||||
```html
|
```html
|
||||||
{# plugins/myplugin/templates/myplugin/index.html #}
|
{# plugins/myplugin/templates/myplugin/index.html #}
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}My Plugin — Fabled Scryer{% endblock %}
|
{% block title %}My Plugin — Roundtable{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="page-title">My Plugin</div>
|
<div class="page-title">My Plugin</div>
|
||||||
{% for row in rows %}
|
{% for row in rows %}
|
||||||
@@ -283,7 +283,7 @@ On next startup, the plugin will be loaded, its migrations applied, and its blue
|
|||||||
`record_metric()` is how plugins feed data into the alert pipeline. Any metric you write here can be the target of an alert rule created in the UI.
|
`record_metric()` is how plugins feed data into the alert pipeline. Any metric you write here can be the target of an alert rule created in the UI.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from fabledscryer.core.alerts import record_metric
|
from roundtable.core.alerts import record_metric
|
||||||
|
|
||||||
# Must be inside an active transaction
|
# Must be inside an active transaction
|
||||||
async with session.begin():
|
async with session.begin():
|
||||||
@@ -302,11 +302,11 @@ async with session.begin():
|
|||||||
|
|
||||||
## Auth in Routes
|
## Auth in Routes
|
||||||
|
|
||||||
Use the `@require_role` decorator from `fabledscryer.auth.middleware`:
|
Use the `@require_role` decorator from `roundtable.auth.middleware`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from fabledscryer.auth.middleware import require_role
|
from roundtable.auth.middleware import require_role
|
||||||
from fabledscryer.models.users import UserRole
|
from roundtable.models.users import UserRole
|
||||||
|
|
||||||
@myplugin_bp.get("/admin-only")
|
@myplugin_bp.get("/admin-only")
|
||||||
@require_role(UserRole.admin)
|
@require_role(UserRole.admin)
|
||||||
@@ -325,13 +325,13 @@ Role hierarchy: `admin > operator > viewer`. Requiring `viewer` grants access to
|
|||||||
|
|
||||||
## Publishing to the Catalog
|
## Publishing to the Catalog
|
||||||
|
|
||||||
The official plugin catalog is hosted at `https://git.fabledsword.com/bvandeusen/FabledScryer-plugins`. Anyone can submit a plugin by opening a pull request — first-party and third-party plugins are treated identically by the catalog system.
|
The official plugin catalog is hosted at `https://git.fabledsword.com/bvandeusen/Roundtable-plugins`. Anyone can submit a plugin by opening a pull request — first-party and third-party plugins are treated identically by the catalog system.
|
||||||
|
|
||||||
### Repo layout
|
### Repo layout
|
||||||
|
|
||||||
```
|
```
|
||||||
fabledscryer-plugins/
|
roundtable-plugins/
|
||||||
├── index.yaml ← catalog index — the only file Fabled Scryer fetches
|
├── index.yaml ← catalog index — the only file Roundtable fetches
|
||||||
├── myplugin/
|
├── myplugin/
|
||||||
│ ├── plugin.yaml
|
│ ├── plugin.yaml
|
||||||
│ ├── __init__.py
|
│ ├── __init__.py
|
||||||
@@ -381,7 +381,7 @@ myplugin.zip
|
|||||||
Generate the zip and its checksum:
|
Generate the zip and its checksum:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd fabledscryer-plugins
|
cd roundtable-plugins
|
||||||
zip -r myplugin.zip myplugin/
|
zip -r myplugin.zip myplugin/
|
||||||
sha256sum myplugin.zip # paste this into index.yaml checksum_sha256
|
sha256sum myplugin.zip # paste this into index.yaml checksum_sha256
|
||||||
```
|
```
|
||||||
|
|||||||
+59
-59
@@ -8,15 +8,15 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| What | File | Function / Class |
|
| What | File | Function / Class |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| App factory | `fabledscryer/app.py` | `create_app()` |
|
| App factory | `roundtable/app.py` | `create_app()` |
|
||||||
| CLI entry point | `fabledscryer/cli.py` | `main()` |
|
| CLI entry point | `roundtable/cli.py` | `main()` |
|
||||||
| Bootstrap config loading | `fabledscryer/config.py` | `load_bootstrap()` |
|
| Bootstrap config loading | `roundtable/config.py` | `load_bootstrap()` |
|
||||||
| Secret key resolution | `fabledscryer/config.py` | `_resolve_secret_key()` |
|
| Secret key resolution | `roundtable/config.py` | `_resolve_secret_key()` |
|
||||||
| DB engine init | `fabledscryer/database.py` | `init_db()` |
|
| DB engine init | `roundtable/database.py` | `init_db()` |
|
||||||
| Core migrations | `fabledscryer/core/migration_runner.py` | `run_core_migrations()` |
|
| Core migrations | `roundtable/core/migration_runner.py` | `run_core_migrations()` |
|
||||||
| Plugin migrations | `fabledscryer/core/migration_runner.py` | `run_plugin_migrations()` |
|
| Plugin migrations | `roundtable/core/migration_runner.py` | `run_plugin_migrations()` |
|
||||||
| Core task registration | `fabledscryer/app.py` | `_register_core_tasks()` |
|
| Core task registration | `roundtable/app.py` | `_register_core_tasks()` |
|
||||||
| Scheduler loop | `fabledscryer/core/scheduler.py` | `start_scheduler()` |
|
| Scheduler loop | `roundtable/core/scheduler.py` | `start_scheduler()` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,15 +24,15 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| What | File | Function |
|
| What | File | Function |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| All defaults | `fabledscryer/core/settings.py` | `DEFAULTS` dict |
|
| All defaults | `roundtable/core/settings.py` | `DEFAULTS` dict |
|
||||||
| Read a setting | `fabledscryer/core/settings.py` | `get_setting(session, key)` |
|
| Read a setting | `roundtable/core/settings.py` | `get_setting(session, key)` |
|
||||||
| Write a setting | `fabledscryer/core/settings.py` | `set_setting(session, key, value)` |
|
| Write a setting | `roundtable/core/settings.py` | `set_setting(session, key, value)` |
|
||||||
| Read all settings | `fabledscryer/core/settings.py` | `get_all_settings(session)` |
|
| Read all settings | `roundtable/core/settings.py` | `get_all_settings(session)` |
|
||||||
| Sync load at startup | `fabledscryer/core/settings.py` | `load_settings_sync(db_url)` |
|
| Sync load at startup | `roundtable/core/settings.py` | `load_settings_sync(db_url)` |
|
||||||
| Extract SMTP dict | `fabledscryer/core/settings.py` | `to_smtp_cfg(settings)` |
|
| Extract SMTP dict | `roundtable/core/settings.py` | `to_smtp_cfg(settings)` |
|
||||||
| Extract webhook dict | `fabledscryer/core/settings.py` | `to_webhook_cfg(settings)` |
|
| Extract webhook dict | `roundtable/core/settings.py` | `to_webhook_cfg(settings)` |
|
||||||
| Extract Ansible dict | `fabledscryer/core/settings.py` | `to_ansible_cfg(settings)` |
|
| Extract Ansible dict | `roundtable/core/settings.py` | `to_ansible_cfg(settings)` |
|
||||||
| Extract plugins dict | `fabledscryer/core/settings.py` | `to_plugins_cfg(settings)` |
|
| Extract plugins dict | `roundtable/core/settings.py` | `to_plugins_cfg(settings)` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -40,9 +40,9 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| What | File | Function |
|
| What | File | Function |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Plugin loading | `fabledscryer/core/plugin_manager.py` | `load_plugins(app)` |
|
| Plugin loading | `roundtable/core/plugin_manager.py` | `load_plugins(app)` |
|
||||||
| ScheduledTask dataclass | `fabledscryer/core/scheduler.py` | `ScheduledTask` |
|
| ScheduledTask dataclass | `roundtable/core/scheduler.py` | `ScheduledTask` |
|
||||||
| Task runner | `fabledscryer/core/scheduler.py` | `start_scheduler(tasks)` |
|
| Task runner | `roundtable/core/scheduler.py` | `start_scheduler(tasks)` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -50,11 +50,11 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| What | File | Function |
|
| What | File | Function |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Write metric + evaluate alerts | `fabledscryer/core/alerts.py` | `record_metric(session, source_module, resource_name, metric_name, value)` |
|
| Write metric + evaluate alerts | `roundtable/core/alerts.py` | `record_metric(session, source_module, resource_name, metric_name, value)` |
|
||||||
| Init alert pipeline | `fabledscryer/core/alerts.py` | `init_alerts(app)` |
|
| Init alert pipeline | `roundtable/core/alerts.py` | `init_alerts(app)` |
|
||||||
| Rule evaluation | `fabledscryer/core/alerts.py` | `_evaluate_rule()` (internal) |
|
| Rule evaluation | `roundtable/core/alerts.py` | `_evaluate_rule()` (internal) |
|
||||||
| Notification dispatch | `fabledscryer/core/alerts.py` | `_dispatch_notification()` (internal) |
|
| Notification dispatch | `roundtable/core/alerts.py` | `_dispatch_notification()` (internal) |
|
||||||
| Email + webhook send | `fabledscryer/core/notifications.py` | `dispatch_notifications()` |
|
| Email + webhook send | `roundtable/core/notifications.py` | `dispatch_notifications()` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -62,9 +62,9 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| What | File | Function |
|
| What | File | Function |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Ping a host | `fabledscryer/monitors/ping.py` | `ping_check(host, session)` |
|
| Ping a host | `roundtable/monitors/ping.py` | `ping_check(host, session)` |
|
||||||
| DNS check a host | `fabledscryer/monitors/dns.py` | `dns_check(host, session)` |
|
| DNS check a host | `roundtable/monitors/dns.py` | `dns_check(host, session)` |
|
||||||
| Data cleanup | `fabledscryer/core/cleanup.py` | `run_cleanup(app)` |
|
| Data cleanup | `roundtable/core/cleanup.py` | `run_cleanup(app)` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -72,9 +72,9 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| What | File | Function / Class |
|
| What | File | Function / Class |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Role-based access decorator | `fabledscryer/auth/middleware.py` | `@require_role(UserRole.X)` |
|
| Role-based access decorator | `roundtable/auth/middleware.py` | `@require_role(UserRole.X)` |
|
||||||
| Login / session handling | `fabledscryer/auth/middleware.py` | `login_user()`, `logout_user()` |
|
| Login / session handling | `roundtable/auth/middleware.py` | `login_user()`, `logout_user()` |
|
||||||
| User count (for first-run) | `fabledscryer/auth/middleware.py` | `get_user_count(app)` |
|
| User count (for first-run) | `roundtable/auth/middleware.py` | `get_user_count(app)` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -82,18 +82,18 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| Model | File | Table |
|
| Model | File | Table |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `Host` | `fabledscryer/models/hosts.py` | `hosts` |
|
| `Host` | `roundtable/models/hosts.py` | `hosts` |
|
||||||
| `PingResult` | `fabledscryer/models/monitors.py` | `ping_results` |
|
| `PingResult` | `roundtable/models/monitors.py` | `ping_results` |
|
||||||
| `DnsResult` | `fabledscryer/models/monitors.py` | `dns_results` |
|
| `DnsResult` | `roundtable/models/monitors.py` | `dns_results` |
|
||||||
| `AlertRule` | `fabledscryer/models/alerts.py` | `alert_rules` |
|
| `AlertRule` | `roundtable/models/alerts.py` | `alert_rules` |
|
||||||
| `AlertState` | `fabledscryer/models/alerts.py` | `alert_states` |
|
| `AlertState` | `roundtable/models/alerts.py` | `alert_states` |
|
||||||
| `AlertEvent` | `fabledscryer/models/alerts.py` | `alert_events` |
|
| `AlertEvent` | `roundtable/models/alerts.py` | `alert_events` |
|
||||||
| `PluginMetric` | `fabledscryer/models/metrics.py` | `plugin_metrics` |
|
| `PluginMetric` | `roundtable/models/metrics.py` | `plugin_metrics` |
|
||||||
| `AnsibleRun` | `fabledscryer/models/ansible.py` | `ansible_runs` |
|
| `AnsibleRun` | `roundtable/models/ansible.py` | `ansible_runs` |
|
||||||
| `User` | `fabledscryer/models/users.py` | `users` |
|
| `User` | `roundtable/models/users.py` | `users` |
|
||||||
| `AppSetting` | `fabledscryer/models/settings.py` | `app_settings` |
|
| `AppSetting` | `roundtable/models/settings.py` | `app_settings` |
|
||||||
| `TraefikMetric` | `plugins/traefik/models.py` | `traefik_metrics` |
|
| `TraefikMetric` | `plugins/traefik/models.py` | `traefik_metrics` |
|
||||||
| SQLAlchemy `Base` | `fabledscryer/models/base.py` | (shared declarative base) |
|
| SQLAlchemy `Base` | `roundtable/models/base.py` | (shared declarative base) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -101,16 +101,16 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| URL pattern | Blueprint | File |
|
| URL pattern | Blueprint | File |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `/` (dashboard) | `dashboard_bp` | `fabledscryer/dashboard/routes.py` |
|
| `/` (dashboard) | `dashboard_bp` | `roundtable/dashboard/routes.py` |
|
||||||
| `/auth/login`, `/auth/logout` | `auth_bp` | `fabledscryer/auth/routes.py` |
|
| `/auth/login`, `/auth/logout` | `auth_bp` | `roundtable/auth/routes.py` |
|
||||||
| `/hosts/` | `hosts_bp` | `fabledscryer/hosts/routes.py` |
|
| `/hosts/` | `hosts_bp` | `roundtable/hosts/routes.py` |
|
||||||
| `/ping/`, `/ping/rows`, `/ping/settings` | `ping_bp` | `fabledscryer/ping/routes.py` |
|
| `/ping/`, `/ping/rows`, `/ping/settings` | `ping_bp` | `roundtable/ping/routes.py` |
|
||||||
| `/dns/`, `/dns/rows` | `dns_bp` | `fabledscryer/dns/routes.py` |
|
| `/dns/`, `/dns/rows` | `dns_bp` | `roundtable/dns/routes.py` |
|
||||||
| `/alerts/` | `alerts_bp` | `fabledscryer/alerts/routes.py` |
|
| `/alerts/` | `alerts_bp` | `roundtable/alerts/routes.py` |
|
||||||
| `/ansible/` | `ansible_bp` | `fabledscryer/ansible/routes.py` |
|
| `/ansible/` | `ansible_bp` | `roundtable/ansible/routes.py` |
|
||||||
| `/settings/` | `settings_bp` | `fabledscryer/settings/routes.py` |
|
| `/settings/` | `settings_bp` | `roundtable/settings/routes.py` |
|
||||||
| `/plugins/traefik/`, `/plugins/traefik/widget` | `traefik_bp` | `plugins/traefik/routes.py` |
|
| `/plugins/traefik/`, `/plugins/traefik/widget` | `traefik_bp` | `plugins/traefik/routes.py` |
|
||||||
| `/health` | (inline) | `fabledscryer/app.py` |
|
| `/health` | (inline) | `roundtable/app.py` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -118,12 +118,12 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| Template | Purpose |
|
| Template | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `fabledscryer/templates/base.html` | Layout, navigation, full CSS design system |
|
| `roundtable/templates/base.html` | Layout, navigation, full CSS design system |
|
||||||
| `fabledscryer/templates/dashboard/index.html` | Dashboard with stat strip and widget grid |
|
| `roundtable/templates/dashboard/index.html` | Dashboard with stat strip and widget grid |
|
||||||
| `fabledscryer/templates/ping/rows.html` | HTMX fragment: ping pill rows (shared by dashboard and /ping/) |
|
| `roundtable/templates/ping/rows.html` | HTMX fragment: ping pill rows (shared by dashboard and /ping/) |
|
||||||
| `fabledscryer/templates/ping/index.html` | Full /ping/ page |
|
| `roundtable/templates/ping/index.html` | Full /ping/ page |
|
||||||
| `fabledscryer/templates/dns/rows.html` | HTMX fragment: DNS status rows |
|
| `roundtable/templates/dns/rows.html` | HTMX fragment: DNS status rows |
|
||||||
| `fabledscryer/templates/dns/index.html` | Full /dns/ page |
|
| `roundtable/templates/dns/index.html` | Full /dns/ page |
|
||||||
| `plugins/traefik/templates/traefik/widget.html` | HTMX fragment: Traefik dashboard widget |
|
| `plugins/traefik/templates/traefik/widget.html` | HTMX fragment: Traefik dashboard widget |
|
||||||
| `plugins/traefik/templates/traefik/index.html` | Full Traefik detail page |
|
| `plugins/traefik/templates/traefik/index.html` | Full Traefik detail page |
|
||||||
|
|
||||||
@@ -133,6 +133,6 @@ Quick reference for where key functions, models, and entry points live in the co
|
|||||||
|
|
||||||
| Location | Covers |
|
| Location | Covers |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `fabledscryer/migrations/versions/` | Core schema (hosts, users, monitors, alerts, app_settings) |
|
| `roundtable/migrations/versions/` | Core schema (hosts, users, monitors, alerts, app_settings) |
|
||||||
| `plugins/traefik/migrations/versions/` | `traefik_metrics` table |
|
| `plugins/traefik/migrations/versions/` | `traefik_metrics` table |
|
||||||
| `alembic.ini` | Alembic config; `version_locations` lists all migration directories |
|
| `alembic.ini` | Alembic config; `version_locations` lists all migration directories |
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# FabledScryer container entrypoint.
|
# Roundtable container entrypoint.
|
||||||
#
|
#
|
||||||
# When NUT_MANAGED=1:
|
# When NUT_MANAGED=1:
|
||||||
# - Generates /etc/nut/*.conf from environment variables
|
# - Generates /etc/nut/*.conf from environment variables
|
||||||
|
|||||||
+1
-1
@@ -9,6 +9,6 @@ from .app import create_app
|
|||||||
@click.option("--port", default=5000, type=int)
|
@click.option("--port", default=5000, type=int)
|
||||||
@click.option("--debug", is_flag=True, default=False)
|
@click.option("--debug", is_flag=True, default=False)
|
||||||
def main(config: str, host: str, port: int, debug: bool) -> None:
|
def main(config: str, host: str, port: int, debug: bool) -> None:
|
||||||
"""Start the Fabled Scryer server."""
|
"""Start the Roundtable server."""
|
||||||
app = create_app(config_path=Path(config))
|
app = create_app(config_path=Path(config))
|
||||||
app.run(host=host, port=port, debug=debug)
|
app.run(host=host, port=port, debug=debug)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ async def dispatch_notifications(
|
|||||||
async def _send_email(cfg: dict, alert: dict) -> dict:
|
async def _send_email(cfg: dict, alert: dict) -> dict:
|
||||||
try:
|
try:
|
||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
msg["Subject"] = f"[Fabled Scryer] {alert['state']} — {alert['rule_name']}"
|
msg["Subject"] = f"[Roundtable] {alert['state']} — {alert['rule_name']}"
|
||||||
msg["From"] = cfg.get("username", "roundtable@localhost")
|
msg["From"] = cfg.get("username", "roundtable@localhost")
|
||||||
msg["To"] = ", ".join(cfg["recipients"])
|
msg["To"] = ", ".join(cfg["recipients"])
|
||||||
msg.set_content(
|
msg.set_content(
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ def _render_report_text(data: dict) -> str:
|
|||||||
now = data["generated_at"]
|
now = data["generated_at"]
|
||||||
lines: list[str] = []
|
lines: list[str] = []
|
||||||
|
|
||||||
lines.append("Fabled Scryer — Weekly Report")
|
lines.append("Roundtable — Weekly Report")
|
||||||
lines.append(f"Generated: {now.strftime('%Y-%m-%d %H:%M UTC')}")
|
lines.append(f"Generated: {now.strftime('%Y-%m-%d %H:%M UTC')}")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ def _render_report_text(data: dict) -> str:
|
|||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
lines.append("─" * 40)
|
lines.append("─" * 40)
|
||||||
lines.append("Fabled Scryer — https://git.fabledsword.com/bvandeusen/FabledScryer")
|
lines.append("Roundtable — https://git.fabledsword.com/bvandeusen/Roundtable")
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ async def send_report(app) -> tuple[bool, str]:
|
|||||||
|
|
||||||
body = _render_report_text(data)
|
body = _render_report_text(data)
|
||||||
date_str = data["generated_at"].strftime("%Y-%m-%d")
|
date_str = data["generated_at"].strftime("%Y-%m-%d")
|
||||||
subject = f"[Fabled Scryer] Weekly Report — {date_str}"
|
subject = f"[Roundtable] Weekly Report — {date_str}"
|
||||||
|
|
||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
msg["Subject"] = subject
|
msg["Subject"] = subject
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ DEFAULTS: dict[str, Any] = {
|
|||||||
"ansible.sources": [],
|
"ansible.sources": [],
|
||||||
"ping.threshold.good_ms": 50,
|
"ping.threshold.good_ms": 50,
|
||||||
"ping.threshold.warn_ms": 200,
|
"ping.threshold.warn_ms": 200,
|
||||||
"plugins.index_url": "https://git.fabledsword.com/bvandeusen/FabledScryer-plugins/raw/branch/main/index.yaml",
|
"plugins.index_url": "https://git.fabledsword.com/bvandeusen/Roundtable-plugins/raw/branch/main/index.yaml",
|
||||||
# OIDC single-sign-on
|
# OIDC single-sign-on
|
||||||
"oidc.enabled": False,
|
"oidc.enabled": False,
|
||||||
"oidc.discovery_url": "",
|
"oidc.discovery_url": "",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Called by entrypoint.sh before the NUT daemons start. Reads config either from
|
Called by entrypoint.sh before the NUT daemons start. Reads config either from
|
||||||
environment variables (legacy NUT_MANAGED=1 path) or from a JSON file written
|
environment variables (legacy NUT_MANAGED=1 path) or from a JSON file written
|
||||||
by the FabledScryer settings page (/data/nut_managed.json).
|
by the Roundtable settings page (/data/nut_managed.json).
|
||||||
|
|
||||||
Environment variables (used when no --from-file is given):
|
Environment variables (used when no --from-file is given):
|
||||||
NUT_DRIVER NUT driver (default: snmp-ups)
|
NUT_DRIVER NUT driver (default: snmp-ups)
|
||||||
|
|||||||
Reference in New Issue
Block a user