diff --git a/docs/customservices.md b/docs/customservices.md
index c1f0812..6a52ad1 100644
--- a/docs/customservices.md
+++ b/docs/customservices.md
@@ -536,14 +536,13 @@ See
## Prometheus
-For Prometheus you need to set the type to Prometheus and provide a url.
+For Prometheus you need to set the type to Prometheus and provide a url. This will display the version on desktops.
```yaml
- name: "Prometheus"
- type: Prometheus
+ type: "Prometheus"
logo: "assets/tools/sample.png"
url: "http://192.168.0.151/"
- # subtitle: "Monitor data server"
```
## Proxmox
diff --git a/dummy-data/prometheus/api/v1/alerts b/dummy-data/prometheus/api/v1/alerts
new file mode 100644
index 0000000..5c2835c
--- /dev/null
+++ b/dummy-data/prometheus/api/v1/alerts
@@ -0,0 +1,31 @@
+{
+ "status": "success",
+ "data": {
+ "alerts": [
+ {
+ "labels": {
+ "alertname": "DemoPendingThenFiring",
+ "severity": "warning"
+ },
+ "annotations": {
+ "summary": "Demo Alert"
+ },
+ "state": "pending",
+ "activeAt": "2025-07-04T23:41:29.244509801Z",
+ "value": "1e+00"
+ },
+ {
+ "labels": {
+ "alertname": "DemoAlwaysFiring",
+ "severity": "critical"
+ },
+ "annotations": {
+ "summary": "Critical Test Alert"
+ },
+ "state": "firing",
+ "activeAt": "2025-07-04T23:41:29.244509801Z",
+ "value": "1e+00"
+ }
+ ]
+ }
+}
diff --git a/dummy-data/prometheus/api/v1/rules b/dummy-data/prometheus/api/v1/rules
new file mode 100644
index 0000000..940266d
--- /dev/null
+++ b/dummy-data/prometheus/api/v1/rules
@@ -0,0 +1,127 @@
+{
+ "status": "success",
+ "data": {
+ "groups": [
+ {
+ "name": "demo",
+ "file": "/etc/prometheus/alert.rules.yml",
+ "rules": [
+ {
+ "state": "pending",
+ "name": "DemoPendingThenFiring",
+ "query": "vector(1)",
+ "duration": 120,
+ "keepFiringFor": 0,
+ "labels": {
+ "severity": "warning"
+ },
+ "annotations": {
+ "summary": "Demo Alert"
+ },
+ "alerts": [
+ {
+ "labels": {
+ "alertname": "DemoPendingThenFiring",
+ "severity": "warning"
+ },
+ "annotations": {
+ "summary": "Demo Alert"
+ },
+ "state": "pending",
+ "activeAt": "2025-07-04T23:41:29.244509801Z",
+ "value": "1e+00"
+ }
+ ],
+ "health": "ok",
+ "evaluationTime": 0.00031672,
+ "lastEvaluation": "2025-07-05T01:41:29.244670258+02:00",
+ "type": "alerting"
+ },
+ {
+ "state": "firing",
+ "name": "DemoAlwaysFiring",
+ "query": "vector(1)",
+ "duration": 0,
+ "keepFiringFor": 0,
+ "labels": {
+ "severity": "critical"
+ },
+ "annotations": {
+ "summary": "Critical Test Alert"
+ },
+ "alerts": [
+ {
+ "labels": {
+ "alertname": "DemoAlwaysFiring",
+ "severity": "critical"
+ },
+ "annotations": {
+ "summary": "Critical Test Alert"
+ },
+ "state": "firing",
+ "activeAt": "2025-07-04T23:41:29.244509801Z",
+ "value": "1e+00"
+ }
+ ],
+ "health": "ok",
+ "evaluationTime": 0.000093386,
+ "lastEvaluation": "2025-07-05T01:41:29.244989354+02:00",
+ "type": "alerting"
+ }
+ ],
+ "interval": 60,
+ "limit": 0,
+ "evaluationTime": 0.000432731,
+ "lastEvaluation": "2025-07-05T01:41:29.244651703+02:00"
+ },
+ {
+ "name": "wireguard",
+ "file": "/etc/prometheus/alert.rules.yml",
+ "rules": [
+ {
+ "state": "inactive",
+ "name": "WireGuardClientConnected",
+ "query": "(time() - wireguard_latest_handshake_seconds) \u003c 30 and absent(wireguard_latest_handshake_seconds offset 10m)",
+ "duration": 30,
+ "keepFiringFor": 0,
+ "labels": {
+ "severity": "info"
+ },
+ "annotations": {
+ "description": "🔌 Peer : {{ $labels.name }} ({{ $labels.ipv4Address }}) connected to WireGuard",
+ "summary": "🔌 Login : {{ $labels.name }}"
+ },
+ "alerts": [],
+ "health": "ok",
+ "evaluationTime": 0.00037991,
+ "lastEvaluation": "2025-07-05T01:41:02.847097789+02:00",
+ "type": "alerting"
+ },
+ {
+ "state": "inactive",
+ "name": "WireGuardClientDisconnected",
+ "query": "(time() - wireguard_latest_handshake_seconds) \u003e 300 and (time() - wireguard_latest_handshake_seconds offset 10m) \u003c 600",
+ "duration": 120,
+ "keepFiringFor": 0,
+ "labels": {
+ "severity": "warning"
+ },
+ "annotations": {
+ "description": "📴 No more WireGuard activity for {{ $labels.name }} ({{ $labels.ipv4Address }}) since >5 min",
+ "summary": "📴 Logout : {{ $labels.name }}"
+ },
+ "alerts": [],
+ "health": "ok",
+ "evaluationTime": 0.000122432,
+ "lastEvaluation": "2025-07-05T01:41:02.847481226+02:00",
+ "type": "alerting"
+ }
+ ],
+ "interval": 60,
+ "limit": 0,
+ "evaluationTime": 0.000533061,
+ "lastEvaluation": "2025-07-05T01:41:02.8470724+02:00"
+ }
+ ]
+ }
+}
diff --git a/dummy-data/prometheus/api/v1/status/buildinfo b/dummy-data/prometheus/api/v1/status/buildinfo
new file mode 100644
index 0000000..a391923
--- /dev/null
+++ b/dummy-data/prometheus/api/v1/status/buildinfo
@@ -0,0 +1,11 @@
+{
+ "status": "success",
+ "data": {
+ "version": "2.53.5",
+ "revision": "d344ea7bf4cc9e9e131a0318d10025982e9c4cc1",
+ "branch": "HEAD",
+ "buildUser": "root@31e33add4c49",
+ "buildDate": "20250630-10:18:05",
+ "goVersion": "go1.23.10"
+ }
+}
diff --git a/src/components/services/Prometheus.vue b/src/components/services/Prometheus.vue
index fa019dd..3d0b2e3 100644
--- a/src/components/services/Prometheus.vue
+++ b/src/components/services/Prometheus.vue
@@ -6,7 +6,10 @@
{{ item.subtitle }}
- {{ count }} {{ level }} alerts
+
+ Version {{ version }} -
+ {{ count }} {{ level }} alerts
+
@@ -33,17 +36,26 @@ export default {
item: Object,
},
data: () => ({
- api: {
- status: "",
- count: 0,
- alerts: {
- firing: 0,
- inactive: 0,
- pending: 0,
- },
- },
+ api: null, // /api/v1/alerts
+ // {
+ // status: "",
+ // count: 0,
+ // alerts: {
+ // firing: 0,
+ // inactive: 0,
+ // pending: 0,
+ // },
+ // },
+ rules : null, // /api/v1/rules
+ build : null, // /api/v1/status/buildinfo
}),
computed: {
+ version() {
+ return this.build?.data?.version || null;
+ },
+ showVersionMobile: function () {
+ return this.isSmallScreenMethod();
+ },
count: function () {
return (
this.countFiring() || this.countPending() || this.countInactive() || 0
@@ -62,8 +74,28 @@ export default {
this.fetchStatus();
},
methods: {
+ isSmallScreenMethod: function () {
+ return window.matchMedia("screen and (max-width: 1023px)").matches;
+ },
fetchStatus: async function () {
- this.api = await this.fetch("api/v1/alerts").catch((e) => console.log(e));
+ const promises = [
+ this.fetch("/api/v1/alerts"),
+ this.fetch("/api/v1/rules"),
+ ];
+
+ /* buildinfo only on desktop */
+ if (!this.isSmallScreenMethod()) {
+ promises.push(this.fetch("/api/v1/status/buildinfo"));
+ }
+
+ try {
+ const [alertsResp, rulesResp, buildResp = null] = await Promise.all(promises);
+ this.api = alertsResp;
+ this.rules = rulesResp;
+ this.build = buildResp;
+ } catch (e) {
+ console.error(e)
+ }
},
countFiring: function () {
if (this.api) {
@@ -82,12 +114,11 @@ export default {
return 0;
},
countInactive: function () {
- if (this.api) {
- return this.api.data?.alerts?.filter(
- (alert) => alert.state === AlertsStatus.pending,
- ).length;
- }
- return 0;
+ return (this.rules?.data?.groups
+ ?.flatMap(g => g.rules ?? [])
+ ?.filter((r) => r.state === AlertsStatus.inactive)
+ ?.length || 0
+ );
},
},
};