Add Dawarich stack
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
COMPOSE_PROJECT_NAME=dawarich
|
||||
|
||||
TZ=Europe/Berlin
|
||||
DAWARICH_HOST=dawarich.kaleschke.info
|
||||
APPLICATION_HOSTS=dawarich.kaleschke.info
|
||||
|
||||
POSTGRES_USER=dawarich
|
||||
POSTGRES_DB=dawarich_production
|
||||
GRAFANA_DB_USER=dawarich_grafana_ro
|
||||
|
||||
METRICS_USERNAME=prometheus
|
||||
BACKGROUND_PROCESSING_CONCURRENCY=5
|
||||
RAILS_MAX_THREADS=10
|
||||
@@ -0,0 +1,152 @@
|
||||
# Dawarich Stack
|
||||
|
||||
Produktionsvorlage fuer Dawarich im KalliLab-Homelab mit GitOps ueber Gitea und Komodo.
|
||||
|
||||
## Gepruefter Stand
|
||||
|
||||
- Dawarich Release: `1.8.1` (GitHub latest am 2026-06-11)
|
||||
- Docker Image: `freikin/dawarich:1.8.1`
|
||||
- Hinweis: `freika/dawarich` existiert auf Docker Hub nicht; das offizielle Image aus dem Upstream-Compose ist `freikin/dawarich`.
|
||||
- Dawarich Tracking-Endpoint fuer OwnTracks: `/api/v1/owntracks/points?api_key=<api-key>`
|
||||
- Dawarich Prometheus ab 1.7.7: `dawarich_app:3000/metrics`; Port `9394` ist intern fuer Sidekiq-Metriken.
|
||||
|
||||
Quellen:
|
||||
|
||||
- https://github.com/Freika/dawarich/releases/tag/1.8.1
|
||||
- https://dawarich.app/docs/getting-started/track-your-location/
|
||||
- https://dawarich.app/docs/self-hosting/monitoring/prometheus/
|
||||
|
||||
## Dateien
|
||||
|
||||
```text
|
||||
apps/dawarich/
|
||||
|-- docker-compose.yml
|
||||
|-- .env.example
|
||||
|-- prometheus-scrape.snippet.yml
|
||||
|-- homeassistant-dawarich.example.yaml
|
||||
|-- grafana/
|
||||
| |-- datasource-dawarich.yml
|
||||
| `-- dashboard-dawarich.json
|
||||
|-- postgres/initdb/20-grafana-readonly.sh
|
||||
`-- secrets/*.txt.example
|
||||
```
|
||||
|
||||
## Setup-Reihenfolge
|
||||
|
||||
1. Stack-Verzeichnis nach Komodo/Gitea uebernehmen: `apps/dawarich`.
|
||||
2. `.env.example` als nicht versionierte Stack-`.env` oder Komodo Stack Environment anlegen.
|
||||
3. Secret-Dateien auf dem Unraid-Host erstellen:
|
||||
|
||||
```bash
|
||||
install -d -m 700 /mnt/user/appdata/secrets
|
||||
openssl rand -base64 48 > /mnt/user/appdata/secrets/dawarich_postgres_password.txt
|
||||
openssl rand -base64 48 | tr -dc 'A-Za-z0-9._~-' | head -c 48 > /mnt/user/appdata/secrets/dawarich_redis_password.txt
|
||||
openssl rand -hex 64 > /mnt/user/appdata/secrets/dawarich_secret_key_base.txt
|
||||
openssl rand -base64 48 > /mnt/user/appdata/secrets/dawarich_metrics_password.txt
|
||||
openssl rand -base64 48 > /mnt/user/appdata/secrets/dawarich_grafana_ro_password.txt
|
||||
chmod 600 /mnt/user/appdata/secrets/dawarich_*.txt
|
||||
```
|
||||
|
||||
4. Bind-Volume-Zielpfade vor dem ersten Deploy anlegen:
|
||||
|
||||
```bash
|
||||
install -d -m 750 \
|
||||
/mnt/user/appdata/dawarich/postgres17 \
|
||||
/mnt/user/appdata/dawarich/redis \
|
||||
/mnt/user/appdata/dawarich/shared \
|
||||
/mnt/user/appdata/dawarich/public \
|
||||
/mnt/user/appdata/dawarich/watched \
|
||||
/mnt/user/appdata/dawarich/storage
|
||||
```
|
||||
|
||||
5. In Komodo als Compose-Stack deployen. `frontend_net` und `backend_net` muessen bereits existieren.
|
||||
6. Ersten Login in Dawarich durchfuehren und den API-Key im Account-Bereich erzeugen.
|
||||
7. Home Assistant `homeassistant-dawarich.example.yaml` in das Smart-Home-Fachrepo uebernehmen und `device_tracker.your_phone` ersetzen.
|
||||
|
||||
## Traefik und Authelia
|
||||
|
||||
Die UI liegt auf `https://dawarich.kaleschke.info` und nutzt `authelia@file,secure-headers@file`.
|
||||
|
||||
Die Tracking-API-Routen fuer OwnTracks, Overland und Traccar sind separat und priorisiert ohne Authelia geroutet, weil diese Clients per Dawarich-API-Key authentifizieren und keine Browser-ForwardAuth-Challenge verarbeiten koennen.
|
||||
|
||||
## Prometheus
|
||||
|
||||
`prometheus-scrape.snippet.yml` ist die dienstnahe Referenz. Produktiv ist der Job bereits in `monitoring/prometheus/prometheus.yml` eingetragen.
|
||||
|
||||
Der Monitoring-Stack ist dafuer bereits vorbereitet:
|
||||
|
||||
- `prometheus` haengt an `backend_net`, damit `dawarich_app` erreichbar ist.
|
||||
- `/mnt/user/appdata/secrets/dawarich_metrics_password.txt` ist als Docker Secret eingebunden.
|
||||
|
||||
Nicht `dawarich_app:9394` scrapen: das ist nach aktueller Dawarich-Doku veraltet. Der Web-Service aggregiert App- und Sidekiq-Metriken unter `dawarich_app:3000/metrics`.
|
||||
|
||||
## Grafana
|
||||
|
||||
Der Read-only-User `dawarich_grafana_ro` wird beim ersten DB-Init durch `postgres/initdb/20-grafana-readonly.sh` angelegt.
|
||||
|
||||
Bei einer bereits initialisierten DB das Script einmal manuell im DB-Container ausfuehren:
|
||||
|
||||
```bash
|
||||
docker exec dawarich_db /docker-entrypoint-initdb.d/20-grafana-readonly.sh
|
||||
```
|
||||
|
||||
Die produktive Provisionierung ist bereits in den vorhandenen Monitoring-Stack integriert:
|
||||
|
||||
- Datasource: `monitoring/grafana/provisioning/datasources/dawarich.yml`
|
||||
- Dashboard: `monitoring/grafana/dashboards/dawarich.json`
|
||||
- Grafana haengt an `backend_net`, damit `dawarich_db:5432` erreichbar ist.
|
||||
- `DAWARICH_GRAFANA_RO_PASSWORD` wird beim Grafana-Start aus `/mnt/user/appdata/secrets/dawarich_grafana_ro_password.txt` exportiert.
|
||||
|
||||
## Home Assistant
|
||||
|
||||
Dawarich akzeptiert OwnTracks-kompatible Location-Punkte per:
|
||||
|
||||
```text
|
||||
https://dawarich.kaleschke.info/api/v1/owntracks/points?api_key=<dawarich-api-key>
|
||||
```
|
||||
|
||||
`homeassistant-dawarich.example.yaml` enthaelt:
|
||||
|
||||
- `rest_command.dawarich_push_owntracks`
|
||||
- Automation fuer `device_tracker`-State-Changes
|
||||
- API-Key aus HA `secrets.yaml` als `dawarich_api_key`
|
||||
|
||||
Alternativ existiert eine HACS-Integration `dawarich-home-assistant`; die YAML-Variante hier bleibt absichtlich transparent und GitOps-lesbar.
|
||||
|
||||
## Backup mit Borg
|
||||
|
||||
Borg-relevante Daten liegen unter:
|
||||
|
||||
```text
|
||||
/mnt/user/appdata/dawarich/postgres17
|
||||
/mnt/user/appdata/dawarich/redis
|
||||
/mnt/user/appdata/dawarich/shared
|
||||
/mnt/user/appdata/dawarich/public
|
||||
/mnt/user/appdata/dawarich/watched
|
||||
/mnt/user/appdata/dawarich/storage
|
||||
/mnt/user/appdata/secrets/dawarich_*.txt
|
||||
```
|
||||
|
||||
Primaerer Restore-Weg fuer die DB sollte ein logischer Dump plus Appdaten sein. Raw-Postgres-Verzeichnisse sind nur fuer gleiches Major/PostGIS-Image und sauberen Shutdown geeignet.
|
||||
|
||||
Empfohlener Dump vor Borg:
|
||||
|
||||
```bash
|
||||
docker exec dawarich_db pg_dump -U dawarich -d dawarich_production -Fc > /mnt/user/backups/borg/dumps/latest/dawarich.dump
|
||||
```
|
||||
|
||||
## Updates
|
||||
|
||||
- Kein `latest` verwenden.
|
||||
- Vor jedem Update Release Notes lesen, besonders bei Dawarich und PostGIS.
|
||||
- Dawarich App und Sidekiq muessen immer dasselbe Image-Tag nutzen.
|
||||
- PostGIS-Major-/Minor-Wechsel getrennt planen und vorher Dump plus Restore-Probe erstellen.
|
||||
- Image-Digests nach Review bewusst aktualisieren.
|
||||
|
||||
## Rollback
|
||||
|
||||
1. Komodo Stack stoppen.
|
||||
2. Vorherigen Git-Commit mit altem Image-Tag/Digest deployen.
|
||||
3. Falls nur App-Code gewechselt wurde: Stack starten und Healthchecks pruefen.
|
||||
4. Falls DB-Migrationen gelaufen sind: DB aus `dawarich.dump` in einen frischen PostGIS-17-Container restoren; kein blindes Zurueckkopieren eines Live-Postgres-Verzeichnisses.
|
||||
5. Dawarich UI, `/api/v1/health`, Prometheus-Scrape und HA-Push testen.
|
||||
@@ -0,0 +1,271 @@
|
||||
name: dawarich
|
||||
|
||||
x-dawarich-image: &dawarich_image freikin/dawarich:1.8.1@sha256:7c70f2169e848ed77ae1cec01dd10ec4a73a70a785d4e4d248db1735c0bc25ed
|
||||
|
||||
services:
|
||||
dawarich_db:
|
||||
image: postgis/postgis:17-3.5-alpine@sha256:fc07e7a034e013d50ada575673b798ca6277e000b8364e39e217f612d94bd9a5
|
||||
container_name: dawarich_db
|
||||
restart: unless-stopped
|
||||
shm_size: 1G
|
||||
environment:
|
||||
TZ: ${TZ}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_PASSWORD_FILE: /run/secrets/dawarich_postgres_password
|
||||
GRAFANA_DB_USER: ${GRAFANA_DB_USER}
|
||||
PGDATA: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- dawarich_db_data:/var/lib/postgresql/data
|
||||
- dawarich_shared:/var/shared
|
||||
- ./postgres/initdb:/docker-entrypoint-initdb.d:ro
|
||||
networks:
|
||||
- backend_net
|
||||
secrets:
|
||||
- dawarich_postgres_password
|
||||
- dawarich_grafana_ro_password
|
||||
expose:
|
||||
- "5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
dawarich_redis:
|
||||
image: redis:7-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99
|
||||
container_name: dawarich_redis
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- /bin/sh
|
||||
- -lc
|
||||
- |
|
||||
exec redis-server \
|
||||
--save 900 1 \
|
||||
--save 300 10 \
|
||||
--appendonly no \
|
||||
--requirepass "$$(cat /run/secrets/dawarich_redis_password)"
|
||||
volumes:
|
||||
- dawarich_redis_data:/data
|
||||
networks:
|
||||
- backend_net
|
||||
secrets:
|
||||
- dawarich_redis_password
|
||||
expose:
|
||||
- "6379"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "redis-cli -a \"$$(cat /run/secrets/dawarich_redis_password)\" --raw incr ping >/dev/null"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
dawarich_app:
|
||||
image: *dawarich_image
|
||||
container_name: dawarich_app
|
||||
restart: unless-stopped
|
||||
stdin_open: true
|
||||
tty: true
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
- -lc
|
||||
command:
|
||||
- |
|
||||
export DATABASE_PASSWORD="$$(cat /run/secrets/dawarich_postgres_password)"
|
||||
export REDIS_URL="redis://:$$(cat /run/secrets/dawarich_redis_password)@dawarich_redis:6379/0"
|
||||
export SECRET_KEY_BASE="$$(cat /run/secrets/dawarich_secret_key_base)"
|
||||
export METRICS_PASSWORD="$$(cat /run/secrets/dawarich_metrics_password)"
|
||||
exec web-entrypoint.sh bin/rails server -p 3000 -b ::
|
||||
environment:
|
||||
TZ: ${TZ}
|
||||
RAILS_ENV: production
|
||||
DATABASE_HOST: dawarich_db
|
||||
DATABASE_PORT: "5432"
|
||||
DATABASE_USERNAME: ${POSTGRES_USER}
|
||||
DATABASE_NAME: ${POSTGRES_DB}
|
||||
APPLICATION_HOSTS: ${APPLICATION_HOSTS}
|
||||
APPLICATION_PROTOCOL: https
|
||||
TIME_ZONE: ${TZ}
|
||||
SELF_HOSTED: "true"
|
||||
STORE_GEODATA: "true"
|
||||
RAILS_LOG_TO_STDOUT: "true"
|
||||
PROMETHEUS_EXPORTER_ENABLED: "true"
|
||||
METRICS_USERNAME: ${METRICS_USERNAME}
|
||||
SIDEKIQ_METRICS_URL: http://dawarich_sidekiq:9394/metrics
|
||||
BACKGROUND_PROCESSING_CONCURRENCY: ${BACKGROUND_PROCESSING_CONCURRENCY}
|
||||
RAILS_MAX_THREADS: ${RAILS_MAX_THREADS}
|
||||
volumes:
|
||||
- dawarich_public:/var/app/public
|
||||
- dawarich_watched:/var/app/tmp/imports/watched
|
||||
- dawarich_storage:/var/app/storage
|
||||
- dawarich_db_data:/dawarich_db_data:ro
|
||||
networks:
|
||||
- frontend_net
|
||||
- backend_net
|
||||
secrets:
|
||||
- dawarich_postgres_password
|
||||
- dawarich_redis_password
|
||||
- dawarich_secret_key_base
|
||||
- dawarich_metrics_password
|
||||
expose:
|
||||
- "3000"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO - http://127.0.0.1:3000/api/v1/health | grep -q '\"status\"[[:space:]]*:[[:space:]]*\"ok\"'"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
depends_on:
|
||||
dawarich_db:
|
||||
condition: service_healthy
|
||||
dawarich_redis:
|
||||
condition: service_healthy
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.docker.network=frontend_net
|
||||
|
||||
# Public API-key endpoints for mobile apps and Home Assistant pushes.
|
||||
- traefik.http.routers.dawarich-api.rule=Host(`${DAWARICH_HOST}`) && (Path(`/api/v1/owntracks/points`) || Path(`/api/v1/overland/batches`) || Path(`/api/v1/traccar/points`))
|
||||
- traefik.http.routers.dawarich-api.entrypoints=websecure
|
||||
- traefik.http.routers.dawarich-api.tls=true
|
||||
- traefik.http.routers.dawarich-api.tls.certresolver=le
|
||||
- traefik.http.routers.dawarich-api.priority=100
|
||||
- traefik.http.routers.dawarich-api.middlewares=secure-headers@file
|
||||
- traefik.http.routers.dawarich-api.service=dawarich
|
||||
|
||||
# UI and all other routes require Authelia ForwardAuth.
|
||||
- traefik.http.routers.dawarich.rule=Host(`${DAWARICH_HOST}`)
|
||||
- traefik.http.routers.dawarich.entrypoints=websecure
|
||||
- traefik.http.routers.dawarich.tls=true
|
||||
- traefik.http.routers.dawarich.tls.certresolver=le
|
||||
- traefik.http.routers.dawarich.priority=10
|
||||
- traefik.http.routers.dawarich.middlewares=authelia@file,secure-headers@file
|
||||
- traefik.http.routers.dawarich.service=dawarich
|
||||
- traefik.http.services.dawarich.loadbalancer.server.port=3000
|
||||
|
||||
dawarich_sidekiq:
|
||||
image: *dawarich_image
|
||||
container_name: dawarich_sidekiq
|
||||
restart: unless-stopped
|
||||
stdin_open: true
|
||||
tty: true
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
- -lc
|
||||
command:
|
||||
- |
|
||||
export DATABASE_PASSWORD="$$(cat /run/secrets/dawarich_postgres_password)"
|
||||
export REDIS_URL="redis://:$$(cat /run/secrets/dawarich_redis_password)@dawarich_redis:6379/0"
|
||||
export SECRET_KEY_BASE="$$(cat /run/secrets/dawarich_secret_key_base)"
|
||||
export METRICS_PASSWORD="$$(cat /run/secrets/dawarich_metrics_password)"
|
||||
exec sidekiq-entrypoint.sh sidekiq
|
||||
environment:
|
||||
TZ: ${TZ}
|
||||
RAILS_ENV: production
|
||||
DATABASE_HOST: dawarich_db
|
||||
DATABASE_PORT: "5432"
|
||||
DATABASE_USERNAME: ${POSTGRES_USER}
|
||||
DATABASE_NAME: ${POSTGRES_DB}
|
||||
APPLICATION_HOSTS: ${APPLICATION_HOSTS}
|
||||
APPLICATION_PROTOCOL: https
|
||||
TIME_ZONE: ${TZ}
|
||||
SELF_HOSTED: "true"
|
||||
STORE_GEODATA: "true"
|
||||
RAILS_LOG_TO_STDOUT: "true"
|
||||
PROMETHEUS_EXPORTER_ENABLED: "true"
|
||||
PROMETHEUS_EXPORTER_PORT: "9394"
|
||||
METRICS_USERNAME: ${METRICS_USERNAME}
|
||||
BACKGROUND_PROCESSING_CONCURRENCY: ${BACKGROUND_PROCESSING_CONCURRENCY}
|
||||
RAILS_MAX_THREADS: ${RAILS_MAX_THREADS}
|
||||
volumes:
|
||||
- dawarich_public:/var/app/public
|
||||
- dawarich_watched:/var/app/tmp/imports/watched
|
||||
- dawarich_storage:/var/app/storage
|
||||
networks:
|
||||
- frontend_net
|
||||
- backend_net
|
||||
secrets:
|
||||
- dawarich_postgres_password
|
||||
- dawarich_redis_password
|
||||
- dawarich_secret_key_base
|
||||
- dawarich_metrics_password
|
||||
expose:
|
||||
- "9394"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pgrep -f sidekiq >/dev/null"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
depends_on:
|
||||
dawarich_db:
|
||||
condition: service_healthy
|
||||
dawarich_redis:
|
||||
condition: service_healthy
|
||||
dawarich_app:
|
||||
condition: service_healthy
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
networks:
|
||||
frontend_net:
|
||||
external: true
|
||||
backend_net:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
dawarich_db_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: /mnt/user/appdata/dawarich/postgres17
|
||||
dawarich_redis_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: /mnt/user/appdata/dawarich/redis
|
||||
dawarich_shared:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: /mnt/user/appdata/dawarich/shared
|
||||
dawarich_public:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: /mnt/user/appdata/dawarich/public
|
||||
dawarich_watched:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: /mnt/user/appdata/dawarich/watched
|
||||
dawarich_storage:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: /mnt/user/appdata/dawarich/storage
|
||||
|
||||
secrets:
|
||||
dawarich_postgres_password:
|
||||
file: /mnt/user/appdata/secrets/dawarich_postgres_password.txt
|
||||
dawarich_redis_password:
|
||||
file: /mnt/user/appdata/secrets/dawarich_redis_password.txt
|
||||
dawarich_secret_key_base:
|
||||
file: /mnt/user/appdata/secrets/dawarich_secret_key_base.txt
|
||||
dawarich_metrics_password:
|
||||
file: /mnt/user/appdata/secrets/dawarich_metrics_password.txt
|
||||
dawarich_grafana_ro_password:
|
||||
file: /mnt/user/appdata/secrets/dawarich_grafana_ro_password.txt
|
||||
@@ -0,0 +1,355 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": false,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "dawarich-postgres"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "none"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 16,
|
||||
"w": 16,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {},
|
||||
"name": "Layer 0",
|
||||
"type": "default"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showMeasure": false,
|
||||
"showScale": true,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"showLegend": true,
|
||||
"style": {
|
||||
"color": {
|
||||
"fixed": "dark-green"
|
||||
},
|
||||
"opacity": 0.55,
|
||||
"rotation": {
|
||||
"fixed": 0,
|
||||
"max": 360,
|
||||
"min": -360,
|
||||
"mode": "mod"
|
||||
},
|
||||
"size": {
|
||||
"fixed": 4,
|
||||
"max": 15,
|
||||
"min": 2
|
||||
},
|
||||
"symbol": {
|
||||
"fixed": "img/icons/marker/circle.svg",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"textConfig": {
|
||||
"fontSize": 12,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"textAlign": "center",
|
||||
"textBaseline": "middle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"latitude": "latitude",
|
||||
"longitude": "longitude",
|
||||
"mode": "coords"
|
||||
},
|
||||
"name": "Location points",
|
||||
"tooltip": true,
|
||||
"type": "markers"
|
||||
}
|
||||
],
|
||||
"tooltip": {
|
||||
"mode": "details"
|
||||
},
|
||||
"view": {
|
||||
"allLayers": true,
|
||||
"id": "fit",
|
||||
"lat": 51,
|
||||
"lon": 10,
|
||||
"zoom": 5
|
||||
}
|
||||
},
|
||||
"pluginVersion": "13.0.2",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "dawarich-postgres"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\n to_timestamp(timestamp) AS \"time\",\n ST_Y(lonlat::geometry) AS latitude,\n ST_X(lonlat::geometry) AS longitude,\n accuracy,\n tracker_id\nFROM points\nWHERE $__unixEpochFilter(timestamp)\n AND lonlat IS NOT NULL\nORDER BY timestamp DESC\nLIMIT 20000;",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Location Points",
|
||||
"type": "geomap"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "dawarich-postgres"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "bars",
|
||||
"fillOpacity": 70,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "km"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"sum"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"hideZeros": false,
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "13.0.2",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "dawarich-postgres"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"format": "time_series",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\n make_date(year, month, 1)::timestamp AS \"time\",\n round((distance::numeric / 1000.0), 2) AS \"km\"\nFROM stats\nWHERE make_date(year, month, 1)::timestamp BETWEEN $__timeFrom() AND $__timeTo()\nORDER BY 1;",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Kilometers per Month",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "dawarich-postgres"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "bars",
|
||||
"fillOpacity": 70,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "short"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 8
|
||||
},
|
||||
"id": 3,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"sum"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"hideZeros": false,
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "13.0.2",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "dawarich-postgres"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"format": "time_series",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\n date_trunc('day', to_timestamp(timestamp)) AS \"time\",\n count(*) AS \"points\"\nFROM points\nWHERE $__unixEpochFilter(timestamp)\nGROUP BY 1\nORDER BY 1;",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Points per Day",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"preload": false,
|
||||
"refresh": "5m",
|
||||
"schemaVersion": 41,
|
||||
"tags": [
|
||||
"dawarich",
|
||||
"location"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-30d",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "Dawarich",
|
||||
"uid": "dawarich",
|
||||
"version": 1,
|
||||
"weekStart": ""
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Dawarich PostgreSQL
|
||||
uid: dawarich-postgres
|
||||
type: postgres
|
||||
access: proxy
|
||||
url: dawarich_db:5432
|
||||
database: dawarich_production
|
||||
user: dawarich_grafana_ro
|
||||
editable: false
|
||||
jsonData:
|
||||
sslmode: disable
|
||||
postgresVersion: 1700
|
||||
timescaledb: false
|
||||
secureJsonData:
|
||||
password: $DAWARICH_GRAFANA_RO_PASSWORD
|
||||
@@ -0,0 +1,47 @@
|
||||
# Add `dawarich_api_key` to Home Assistant `secrets.yaml`.
|
||||
# The endpoint is the current OwnTracks-compatible Dawarich endpoint:
|
||||
# https://<host>/api/v1/owntracks/points?api_key=<api-key>
|
||||
|
||||
rest_command:
|
||||
dawarich_push_owntracks:
|
||||
url: "https://dawarich.kaleschke.info/api/v1/owntracks/points?api_key={{ api_key }}"
|
||||
method: POST
|
||||
content_type: "application/json"
|
||||
payload: >-
|
||||
{
|
||||
"_type": "location",
|
||||
"lat": {{ latitude }},
|
||||
"lon": {{ longitude }},
|
||||
"tst": {{ timestamp }},
|
||||
"acc": {{ accuracy | default(0) }},
|
||||
"alt": {{ altitude | default(0) }},
|
||||
"batt": {{ battery | default(0) }},
|
||||
"tid": "{{ tracker_id[:2] }}"
|
||||
}
|
||||
|
||||
automation:
|
||||
- id: dawarich_push_device_tracker_location
|
||||
alias: Dawarich - push device tracker location
|
||||
mode: queued
|
||||
max: 20
|
||||
trigger:
|
||||
- platform: state
|
||||
entity_id:
|
||||
- device_tracker.your_phone
|
||||
condition:
|
||||
- condition: template
|
||||
value_template: >-
|
||||
{{ trigger.to_state is not none
|
||||
and state_attr(trigger.entity_id, 'latitude') is number
|
||||
and state_attr(trigger.entity_id, 'longitude') is number }}
|
||||
action:
|
||||
- service: rest_command.dawarich_push_owntracks
|
||||
data:
|
||||
api_key: !secret dawarich_api_key
|
||||
tracker_id: "{{ trigger.entity_id.split('.')[1] }}"
|
||||
latitude: "{{ state_attr(trigger.entity_id, 'latitude') }}"
|
||||
longitude: "{{ state_attr(trigger.entity_id, 'longitude') }}"
|
||||
accuracy: "{{ state_attr(trigger.entity_id, 'gps_accuracy') | default(0, true) }}"
|
||||
altitude: "{{ state_attr(trigger.entity_id, 'altitude') | default(0, true) }}"
|
||||
battery: "{{ state_attr(trigger.entity_id, 'battery_level') | default(0, true) }}"
|
||||
timestamp: "{{ as_timestamp(trigger.to_state.last_updated) | int }}"
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
GRAFANA_USER="${GRAFANA_DB_USER:-dawarich_grafana_ro}"
|
||||
GRAFANA_PASSWORD="$(cat /run/secrets/dawarich_grafana_ro_password)"
|
||||
|
||||
sql_ident() {
|
||||
printf '"%s"' "$(printf '%s' "$1" | sed 's/"/""/g')"
|
||||
}
|
||||
|
||||
sql_literal() {
|
||||
printf "'%s'" "$(printf '%s' "$1" | sed "s/'/''/g")"
|
||||
}
|
||||
|
||||
DB_IDENT="$(sql_ident "$POSTGRES_DB")"
|
||||
USER_IDENT="$(sql_ident "$GRAFANA_USER")"
|
||||
USER_LITERAL="$(sql_literal "$GRAFANA_USER")"
|
||||
PASSWORD_LITERAL="$(sql_literal "$GRAFANA_PASSWORD")"
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<EOSQL
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = ${USER_LITERAL}) THEN
|
||||
EXECUTE 'CREATE ROLE ${USER_IDENT} LOGIN PASSWORD ${PASSWORD_LITERAL}';
|
||||
ELSE
|
||||
EXECUTE 'ALTER ROLE ${USER_IDENT} WITH LOGIN PASSWORD ${PASSWORD_LITERAL}';
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
|
||||
GRANT CONNECT ON DATABASE ${DB_IDENT} TO ${USER_IDENT};
|
||||
GRANT USAGE ON SCHEMA public TO ${USER_IDENT};
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ${USER_IDENT};
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO ${USER_IDENT};
|
||||
EOSQL
|
||||
@@ -0,0 +1,18 @@
|
||||
# Dawarich 1.8.1 serves the Prometheus endpoint on the web service at
|
||||
# /metrics. Port 9394 is the internal Sidekiq metrics endpoint consumed by
|
||||
# dawarich_app via SIDEKIQ_METRICS_URL.
|
||||
#
|
||||
# Prerequisites in monitoring/docker-compose.yml:
|
||||
# - attach service `prometheus` to external `backend_net`
|
||||
# - mount Docker secret/file `/mnt/user/appdata/secrets/dawarich_metrics_password.txt`
|
||||
# into the Prometheus container at `/run/secrets/dawarich_metrics_password`
|
||||
|
||||
- job_name: dawarich
|
||||
metrics_path: /metrics
|
||||
scheme: http
|
||||
basic_auth:
|
||||
username: prometheus
|
||||
password_file: /run/secrets/dawarich_metrics_password
|
||||
static_configs:
|
||||
- targets:
|
||||
- dawarich_app:3000
|
||||
@@ -0,0 +1 @@
|
||||
replace-with-a-long-random-grafana-readonly-password
|
||||
@@ -0,0 +1 @@
|
||||
replace-with-a-long-random-metrics-password
|
||||
@@ -0,0 +1 @@
|
||||
replace-with-a-long-random-postgres-password
|
||||
@@ -0,0 +1 @@
|
||||
replace-with-a-long-random-url-safe-redis-password
|
||||
@@ -0,0 +1 @@
|
||||
replace-with-output-of-openssl-rand-hex-64
|
||||
Reference in New Issue
Block a user