meyton-sync

Anleitung zur Installation, Konfiguration und Bedienung

Übersicht

meyton-sync synchronisiert Schießergebnisse aus der lokalen Datenbank der Schießanlage kontinuierlich auf einen externen Webserver. Benutzer und Applikationen können die Ergebnisse dort über eine REST-API abrufen.

Source Schießanlage
SSMDB2 (MySQL)
→ HTTPS →
Target Webserver
FastAPI + MariaDB
Clients REST-API
Admin-UI

Das System besteht aus zwei unabhängigen Komponenten:

  • Source – läuft auf einem Rechner im Netz der Schießanlage, liest Änderungen aus der Datenbank SSMDB2 und sendet sie gebündelt per HTTPS an den Zielserver.
  • Target – läuft auf dem externen Webserver, empfängt die Datenpakete, speichert sie in der Replikationsdatenbank SSMDB2c und stellt sie über eine REST-API sowie eine Admin-Oberfläche bereit.

Datenbankstruktur

TabelleInhalt
ScheibenTreffer-Scheiben (Schütze, Disziplin, Gesamtergebnis, …)
SerienSerien je Scheibe (Stellung, Ringergebnis)
TrefferEinzeltreffer mit X/Y-Koordinaten, Teiler, Qualität
UsersAPI-Benutzer mit Rollen und Passwort-Hashes

Voraussetzungen

Source (Schießanlage)

AnforderungDetails
BetriebssystemopenSUSE 15.2+, SLES, Debian oder Ubuntu
PythonPython 3
Python-PaketePyMySQL, requests, apscheduler
DatenbankLesezugriff auf SSMDB2 (MySQL/MariaDB)
NetzwerkAusgehende HTTPS-Verbindung (Port 443) zum Zielserver

Target (Webserver)

AnforderungDetails
BetriebssystemDebian oder Ubuntu (Pflicht)
PythonPython 3
Python-PaketeFastAPI, Uvicorn, python-multipart, passlib, bcrypt, PyJWT, PyMySQL
WebserverApache 2 mit mod_proxy und mod_proxy_http
DatenbankMariaDB/MySQL lokal (wird automatisch eingerichtet)
NetzwerkHTTPS-Erreichbarkeit; Uvicorn läuft auf 127.0.0.1:8000
Hinweis Die Installations-Skripte für die Target-Komponente funktionieren ausschließlich unter Debian/Ubuntu. Für andere Distributionen ist eine manuelle Installation erforderlich.

Installation

Source installieren

  1. Installationsskript auf dem Quell-Rechner ausführen:
    bash install-source.sh
  2. Das Skript fragt interaktiv nach den Datenbankzugangsdaten, der Ziel-URL sowie den Synchronisierungstoken.
  3. Anschließend wird der Systemd-Dienst meyton-sync-source angelegt und gestartet.
  4. Konfigurationsdatei prüfen:
    /opt/meyton-sync/source/source.conf

source.conf – Konfigurationsparameter

[database]
host     = 192.168.10.200   # IP der SSMDB2-Datenbank
port     = 3306
user     = meyton
password = <Passwort>
database = SSMDB2

[sync]
target_url      = https://www.mydomain.de/meyton/sync
own_token       = <32-Byte-Hex>     # Token dieser Source
target_token    = <32-Byte-Hex>     # Erwarteter Token des Targets
interval        = 60                 # Sekunden zwischen Sync-Läufen (1–600)
stands          = 15                 # Anzahl Schießstände
batch_size      = 100                # Scheiben pro Batch
request_timeout = 30

[logging]
level = INFO
file  = source.log

Target installieren

  1. Installationsskript auf dem Webserver ausführen (Root-Rechte erforderlich):
    sudo bash install-target.sh
  2. Das Skript richtet Datenbank, Benutzer und Apache-Proxy automatisch ein.
  3. Systemd-Dienst meyton-sync-target wird gestartet; die API ist anschließend unter /meyton/ erreichbar.
  4. Konfigurationsdatei prüfen:
    /opt/meyton-sync/target/target.conf

target.conf – Konfigurationsparameter

[database]
host     = localhost
port     = 3306
user     = meyton
password = <Passwort>
database = SSMDB2c

[sync]
own_token    = <32-Byte-Hex>     # Token dieses Targets
source_token = <32-Byte-Hex>     # Erwarteter Token der Source

[api]
secret_key            = <64-Byte-Hex>   # JWT-Signing-Key
token_expire_minutes  = 60

[logging]
level = INFO
file  = target.log

Apache-Reverse-Proxy

Das Installationsskript fügt in die aktive Apache-Konfiguration ein:

ProxyPass        /meyton/  http://127.0.0.1:8000/
ProxyPassReverse /meyton/  http://127.0.0.1:8000/

Admin-Passwort zurücksetzen

Falls das Admin-Passwort verloren geht, kann es mit folgendem Hilfsprogramm zurückgesetzt werden:

cd /opt/meyton-sync/target
python3 set_admin_password.py
Sicherheit Das Standard-Passwort des Admin-Kontos lautet erhkm-673BX#a. Es muss direkt nach der Installation in der Admin-Oberfläche geändert werden.

Dienste verwalten

# Status prüfen
systemctl status meyton-sync-source
systemctl status meyton-sync-target

# Starten / Stoppen / Neustarten
systemctl start  meyton-sync-target
systemctl stop   meyton-sync-target
systemctl restart meyton-sync-target

# Log-Ausgabe
journalctl -u meyton-sync-target -f

Funktionsweise Sync

Ablauf auf Source-Seite

Der Source-Prozess läuft als systemd-Dienst und führt in regelmäßigen Abständen (Standard: 60 s) einen Sync-Lauf durch:

  1. Zustandsprüfung: Beim ersten Start (keine source.state-Datei) oder wenn der Target 0 Scheiben meldet, wird ein vollständiger Sync durchgeführt.
  2. Inkrementeller Sync: Danach werden nur Datensätze übertragen, deren Zeitstempel nach dem letzten erfolgreichen Sync liegt.
  3. Batching: Scheiben werden in Paketen (Standard: 100) zusammengefasst. Zu jeder Scheibe werden die zugehörigen Serien und Treffer mitgesendet.
  4. Komprimierung: Jedes Paket wird per gzip komprimiert und per HTTP-POST an den Target gesendet.
  5. Zustandsspeicherung: Nach erfolgreichem Versand wird der Zeitstempel in source.state aktualisiert.

Ablauf auf Target-Seite

  1. Eingehende Anfragen werden anhand des Bearer-Tokens authentifiziert.
  2. Die empfangenen Scheiben werden per UPSERT in die Datenbank geschrieben (INSERT … ON DUPLICATE KEY UPDATE).
  3. Zugehörige Serien und Treffer werden gelöscht und neu eingefügt, um Inkonsistenzen zu vermeiden.
  4. Anzahl und Typ (vollständig / inkrementell) werden protokolliert.

Token-Authentifizierung

Source und Target tauschen bei jedem Sync-Aufruf zwei Pre-Shared Tokens aus:

  • own_token (Source) / source_token (Target): Identifiziert die Source.
  • target_token (Source) / own_token (Target): Bestätigt den echten Target-Server.

Beide Token müssen auf beiden Seiten übereinstimmen, sonst wird der Sync abgelehnt.

Kompatibilität älterer Datenbanken

Hinweis Ältere SSMDB2-Versionen (vor 5.4) enthalten die Spalten StandNrText und Serie noch nicht. meyton-sync erkennt dies automatisch und passt die Abfragen entsprechend an.

REST-API

Alle API-Endpunkte sind unter https://<server>/meyton/api/ erreichbar. Eine interaktive Swagger-Dokumentation steht unter /meyton/docs zur Verfügung.

Authentifizierung

Die API verwendet JWT-Bearer-Token. Token werden über den Login-Endpunkt bezogen und sind standardmäßig 60 Minuten gültig.

# Login (gibt JWT-Token zurück)
POST /meyton/api/token
Content-Type: application/x-www-form-urlencoded

username=<login>&password=<passwort>

Den Token anschließend im Authorization-Header mitsenden:

Authorization: Bearer <token>

Scheiben-Endpunkte

MethodePfadBeschreibungMindestrolle
GET /api/scheiben/today Alle Scheiben des heutigen Tages user
GET /api/scheiben/last-per-stand Letzte Scheibe je Stand user
GET /api/scheiben/sportpass/{id}?limit=20 Scheiben eines Schützen nach Sportpass-ID restricted / singe *
GET /api/scheiben/shooter?nachname=X&vorname=Y&limit=20 Scheiben nach Name des Schützen restricted / singe *
GET /api/scheibe/{scheiben_id} Vollständige Scheibe mit Serien und Einzeltreffern restricted / singe *

* singe-Benutzer erhalten nur Ergebnisse, die zu ihrem eigenen Profil gehören – alle anderen Anfragen werden mit 403 abgelehnt. Weitere Details siehe Singe-Rolle.

Benutzerverwaltung (nur Admin)

MethodePfadBeschreibung
GET /api/users Alle Benutzer auflisten (inkl. Nachname, Vorname, SportpassID)
POST /api/users Neuen Benutzer anlegen (Rollen: user, restricted oder singe; bei singe sind Nachname und Vorname Pflicht)
DELETE /api/users/{login} Benutzer löschen (Admin-Konto kann nicht gelöscht werden)
PUT /api/users/{login}/password Passwort eines Benutzers ändern
PUT /api/users/{login}/role Rolle eines Benutzers ändern; bei Wechsel auf singe müssen Nachname und Vorname mitübergeben werden
PUT /api/admin/password Admin-Passwort ändern

Datenbankoperationen (nur Admin)

MethodePfadBeschreibung
GET /api/db/disziplinen Alle Disziplinen auflisten
GET /api/db/starterlisten Alle Starterlisten auflisten
DELETE /api/db/clearErgebnisse Alle Ergebnisse löschen
DELETE /api/db/clearErgebnisseDisziplin?disziplin=X Ergebnisse einer Disziplin löschen
DELETE /api/db/clearErgebnisseStarterliste?starterliste=X Ergebnisse einer Starterliste löschen

Sync-Endpunkte (intern)

Diese Endpunkte werden ausschließlich von der Source-Komponente verwendet und sind nicht in der Swagger-Dokumentation sichtbar.

MethodePfadBeschreibung
GET /sync/status Liefert Token und aktuelle Scheiben-Anzahl zurück
POST /sync Empfängt komprimiertes Batch-Paket mit Scheiben-Daten

Passwort-Anforderungen

  • Mindestens 10 Zeichen
  • Muss sowohl Buchstaben als auch Ziffern enthalten

Rollen & Berechtigungen

Jeder Benutzer hat genau eine der vier Rollen. Die Rolle wird beim Login in den JWT-Token eingebettet und bei jeder Anfrage geprüft.

RolleBeschreibungZugang
admin Systemadministrator Vollzugriff auf alle Endpunkte inkl. Benutzerverwaltung und Datenbank-Operationen
user Normaler Benutzer Lesezugriff auf alle Scheiben-Endpunkte (today, last-per-stand, Suche nach Name/SportpassID, Einzelscheibe)
restricted Eingeschränkter Benutzer Nur Schützen-bezogene Suchen (sportpass, shooter, Einzelscheibe) – kein Zugriff auf Tages- oder Standübersichten; keine Filterung nach Eigentümer
singe Einzelschütze (persönlich) Wie restricted, aber alle Suchergebnisse werden auf das eigene Profil (Nachname, Vorname, SportpassID) gefiltert. Fremde Ergebnisse werden mit 403 abgelehnt.

Berechtigungsmatrix

Endpunkt admin user restricted singe
scheiben/today
scheiben/last-per-stand
scheiben/sportpass/{id} nur eigene
scheiben/shooter nur eigene
scheibe/{id} nur eigene
Benutzerverwaltung
Datenbankoperationen
Statistik (stats)

Singe-Rolle

Die Rolle singe ist für Einzelschützen gedacht, die ausschließlich ihre eigenen Ergebnisse abrufen dürfen. Sie hat dieselben Zugriffsrechte wie restricted, mit dem Unterschied, dass alle zurückgegebenen Ergebnisse serverseitig auf das Profil des eingeloggten Benutzers gefiltert werden.

Profilfelder

Jeder singe-Benutzer hat in der Datenbank drei zusätzliche Felder:

FeldTypBeschreibung
NachnameText (max. 50 Zeichen)Nachname des Schützen, wie er in den Scheiben-Daten steht (Pflicht)
VornameText (max. 50 Zeichen)Vorname des Schützen (Pflicht)
SportpassIDGanzzahl (bigint)Sportpass-Nummer des Schützen (optional, 0 = nicht gesetzt)
Hinweis Diese Felder existieren in der Users-Tabelle für alle Benutzerrollen; bei admin, user und restricted bleiben sie leer und haben keine Auswirkung auf die API-Filterung.

Filterlogik

Die Profilfelder werden beim Login in den JWT-Token eingebettet und bei jeder Anfrage serverseitig ausgewertet:

GET /api/scheiben/sportpass/{sportpass_id}

Der angefragte Wert sportpass_id muss exakt mit der im Profil hinterlegten SportpassID übereinstimmen. Jede andere ID wird mit 403 Keine Berechtigung für diese SportpassID abgewiesen.

GET /api/scheiben/shooter?nachname=…&vorname=…

Nachname und Vorname aus der Anfrage müssen (Groß-/Kleinschreibung wird ignoriert) mit den Profilfeldern übereinstimmen. Abweichende Namen werden mit 403 Keine Berechtigung für diesen Schützen abgewiesen.

GET /api/scheibe/{scheiben_id}

Der Server prüft, ob die Scheibe dem Benutzer gehört. Die Prüfung erfolgt in dieser Reihenfolge:

  1. Wenn SportpassID > 0: Scheibe muss dieselbe SportpassID haben.
  2. Sonst: Nachname und Vorname der Scheibe müssen mit dem Profil übereinstimmen.

Gehört die Scheibe nicht zum Benutzer, wird 403 Keine Berechtigung für diese Scheibe zurückgegeben.

Benutzer anlegen

Beim Anlegen über POST /api/users müssen für die Rolle singe mindestens nachname und vorname angegeben werden. Die sportpass_id ist optional (Standard: 0).

POST /meyton/api/users
Authorization: Bearer <admin-token>
Content-Type: application/json

{
  "login":        "mueller.hans",
  "password":     "Passwort123!",
  "role":         "singe",
  "nachname":     "Mueller",
  "vorname":      "Hans",
  "sportpass_id": 1234567
}

Rolle nachträglich ändern

Beim Wechsel auf singe über PUT /api/users/{login}/role müssen Nachname und Vorname ebenfalls mitübergeben werden:

PUT /meyton/api/users/mueller.hans/role
Authorization: Bearer <admin-token>
Content-Type: application/json

{
  "role":         "singe",
  "nachname":     "Mueller",
  "vorname":      "Hans",
  "sportpass_id": 1234567
}
Achtung Beim Rollenwechsel werden die Felder Nachname, Vorname und SportpassID immer überschrieben – auch wenn die neue Rolle nicht singe ist. Bei einem Wechsel zurück auf user oder restricted können die Felder leer gelassen werden; sie haben dann keinen Einfluss auf das Verhalten der API.

Statistik-API

Der Endpunkt GET /api/stats liefert eine Übersicht über den aktuellen Datenbankinhalt sowie den Synchronisierungsstatus. Er ist ausschließlich für die Rolle admin zugänglich.

GET /meyton/api/stats
Authorization: Bearer <admin-token>

Antwortfelder

FeldTypBeschreibung
scheiben Ganzzahl Gesamtanzahl der gespeicherten Scheiben
serien Ganzzahl Gesamtanzahl der Serien-Einträge
treffer Ganzzahl Gesamtanzahl der Einzeltreffer
scheiben_pro_disziplin Liste Anzahl Scheiben je Disziplin (disziplin + count), absteigend sortiert
letzter_verbindungsaufbau ISO-8601-Zeitstempel / null Zeitpunkt des letzten GET /sync/status-Aufrufs durch die Source-Komponente
letzte_synchronisierung ISO-8601-Zeitstempel / null Zeitpunkt des letzten erfolgreich empfangenen Sync-Pakets
letzte_scheibe ISO-8601-Zeitstempel / null Zeitstempel der jüngsten Scheibe in der Datenbank

Beispielantwort

{
  "scheiben": 4821,
  "serien":   28926,
  "treffer":  144630,
  "scheiben_pro_disziplin": [
    { "disziplin": "LG", "count": 2410 },
    { "disziplin": "LP", "count": 1837 },
    { "disziplin": "KK", "count":  574 }
  ],
  "letzter_verbindungsaufbau": "2026-05-10T08:15:02+00:00",
  "letzte_synchronisierung":   "2026-05-10T08:15:05+00:00",
  "letzte_scheibe":            "2026-05-10T08:14:51"
}
Sync-Überwachung Die Felder letzter_verbindungsaufbau und letzte_synchronisierung erlauben eine einfache Überwachung des Sync-Prozesses. Liegt letzter_verbindungsaufbau mehr als zwei Sync-Intervalle (Standard: 2 × 60 s) in der Vergangenheit, ist die Verbindung zur Schießanlage unterbrochen. letzte_synchronisierung kann älter sein, wenn in der Zwischenzeit keine neuen Scheiben erfasst wurden.

Admin-Oberfläche

Die Admin-Oberfläche ist unter https://<server>/meyton/admin erreichbar und steht ausschließlich Benutzern mit der Rolle admin zur Verfügung.

Login

Nach dem Aufrufen der Seite erscheint ein Login-Formular. Nach erfolgreicher Anmeldung wird ein JWT-Token im Browser gespeichert und alle weiteren Aktionen laufen im Hintergrund über die REST-API.

Tab: Benutzerverwaltung

  • Alle vorhandenen Benutzer mit Rolle, Nachname, Vorname und SportpassID anzeigen
  • Neuen Benutzer anlegen (Rollen: user, restricted oder singe)
  • Bei Rolle singe: Nachname und Vorname sind Pflichtfelder; SportpassID ist optional
  • Passwort eines bestehenden Benutzers ändern
  • Rolle eines Benutzers ändern (außer Admin); beim Wechsel auf singe müssen Nachname und Vorname angegeben werden
  • Benutzer löschen (außer Admin)
Hinweis Das Admin-Konto selbst kann nicht gelöscht werden und seine Rolle kann nicht geändert werden. Das Admin-Passwort kann über den Endpunkt PUT /api/admin/password oder über das Hilfsskript set_admin_password.py zurückgesetzt werden.

Tab: Statistiken

  • Gesamtanzahl von Scheiben, Serien und Einzeltreffern
  • Aufschlüsselung nach Disziplin mit prozentualem Balkendiagramm
  • Zeitpunkt des letzten Verbindungsaufbaus durch die Source-Komponente
  • Zeitpunkt der letzten erfolgreichen Synchronisierung
  • Zeitstempel der jüngsten Scheibe in der Datenbank
  • Schaltfläche zum manuellen Aktualisieren der Anzeige

Tab: Datenbank

  • Alle Ergebnisse löschen: Löscht sämtliche Scheiben, Serien und Treffer (Sicherheitsabfrage erforderlich)
  • Nach Disziplin löschen: Ergebnisse einer bestimmten Disziplin entfernen (Auswahl über Dropdown)
  • Nach Starterliste löschen: Ergebnisse einer Starterliste entfernen (Auswahl über Dropdown)
Achtung Löschvorgänge können nicht rückgängig gemacht werden. Gelöschte Daten werden beim nächsten Sync-Lauf nicht automatisch wiederhergestellt, da der Source-Prozess nur geänderte Datensätze überträgt. Um alle Daten neu einzuspielen, muss ein vollständiger Sync erzwungen werden (z. B. durch Löschen der Datei source.state auf dem Source-Rechner).

Vollständigen Sync erzwingen

Wenn nach einem Löschvorgang alle Daten erneut eingespielt werden sollen:

# Auf dem Source-Rechner:
sudo systemctl stop meyton-sync-source
sudo rm /opt/meyton-sync/source/source.state
sudo systemctl start meyton-sync-source

Beim nächsten Start erkennt der Source-Dienst das Fehlen der Statusdatei und überträgt alle Datensätze vollständig.