feat(prometheus): the widget template library: list, edit with live preview, paste in, upload, export, duplicate (#5692)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m16s
CI / unit (push) Failing after 1m29s
CI / publish (push) Skipped

Prometheus gains a "Widget templates" tab beside Targets. It lists the
built-in templates and an admin's own, any that no longer pass the checker
first. A template opens as its YAML beside a live preview drawn over a
chosen target exactly as the dashboard widget draws it (routes.draw, now
shared by both). An admin writes or pastes a new one, reads a .yaml upload
into the editor to look over, duplicates any template under a free name,
edits and deletes their own; anyone can export the YAML. Built-in
templates are read-only.

Custom templates are rows in prometheus_widget_templates (migration
prometheus_002_templates), kept in backups, keyed "custom:<id>" so a
rename keeps its widgets. Saving needs a template that passes the checker
and a name no other template has. Create, update and delete are audited
and link to the template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 09:22:31 -04:00
co-authored by Claude Opus 5.5
parent 5ca13f9278
commit bd762307e0
18 changed files with 849 additions and 28 deletions
+18
View File
@@ -39,3 +39,21 @@ class PrometheusTarget(Base):
last_kept: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
last_dropped: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
last_capped: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
class PrometheusWidgetTemplate(Base):
"""A widget template an admin wrote or copied in (#5692), beside the
bundled ones in widgets/*.yaml. `text` is the YAML as written; `name` is
its `name:`, kept here so the library lists and orders without reading
every template. Only a template that passes the checker is saved. Its key
in a widget's settings is "custom:<id>", so renaming it keeps its widgets.
"""
__tablename__ = "prometheus_widget_templates"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=time_ordered_id)
name: Mapped[str] = mapped_column(String(80), nullable=False, unique=True)
text: Mapped[str] = mapped_column(Text, nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc))
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc))