No description
  • Python 55.9%
  • HTML 36.5%
  • CSS 7.4%
  • Dockerfile 0.2%
Find a file
Robert Vieider a97f2a34e4 Initial commit: retroactive git history for existing ooo-manager app
Local working copy had no git history yet despite already running in
production on the Synology NAS. Establishing it now so ooo-manager can be
registered as a real DevHub project (Git status, deployment history) rather
than a dummy test project.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 10:08:00 +02:00
app Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
.env.example Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
.gitignore Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
docker-compose.yml Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
Dockerfile Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
Logo-PI-V2.png Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
Logo-PI.png Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
Plus-Logo.jpg Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
README.md Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
requirements.txt Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00
run_dev.py Initial commit: retroactive git history for existing ooo-manager app 2026-07-16 10:08:00 +02:00

OOO-Manager Zeitgesteuerte Abwesenheitsnotizen für Exchange Online

Kleines internes Tool für Plus-Immobilien GmbH, um Abwesenheitsnotizen auf gemeinsamen Postfächern (Shared Mailboxes) automatisch nach individuellem Zeitplan pro Postfach ein- und auszuschalten inkl. wiederkehrender Wochenfenster und Einzel-Ausnahmen (Feiertage, Betriebsurlaub, ...) über eine Kalenderansicht.

1. Azure AD App-Registrierung einrichten (einmalig, durch Global Admin)

  1. Azure PortalAzure Active DirectoryApp-RegistrierungenNeue Registrierung
    • Name: OOO-Manager
    • Kontotyp: nur dieses Organisationsverzeichnis (Single Tenant)
  2. API-BerechtigungenBerechtigung hinzufügenMicrosoft GraphAnwendungsberechtigungenMailboxSettings.ReadWrite, GroupMember.Read.All und User.Read.All hinzufügen → anschließend Administratorzustimmung erteilen (Grant admin consent)
    • GroupMember.Read.All erlaubt nur das Lesen der Mitgliedschaft einer Gruppe (wer ist Mitglied) — nicht das Auflösen der Mail-Adresse/des Anzeigenamens der Mitglieder selbst. Dafür wird zusätzlich User.Read.All benötigt. Fehlt User.Read.All, liefert Microsoft Graph die Gruppenmitglieder zwar als Objekte zurück, aber mit leeren mail/displayName-Feldern — das Adress-Dropdown wirkt dann fälschlich leer, obwohl die Gruppe tatsächlich Mitglieder hat. Das Tool erkennt diesen Fall und zeigt einen entsprechenden Warnhinweis im Formular an.
    • Ohne konfigurierte MAILBOX_GROUP_ID fällt das Formular auf die ungefilterte Liste aller mailfähigen Konten des Tenants zurück (ebenfalls User.Read.All), mit deutlichem Warnhinweis im Formular.
  3. Zertifikate & GeheimnisseNeuer geheimer Clientschlüssel → Wert sofort kopieren (wird nur einmal angezeigt)
  4. Von der Übersicht-Seite notieren: Anwendungs-ID (Client-ID) und Verzeichnis-ID (Tenant-ID)

Wichtig: Die hier erzeugten Werte (Client Secret, Anwendungs-ID, Verzeichnis-ID) gehören ausschließlich in die lokale .env-Datei (Schritt 2) niemals in dieses README oder eine andere Datei, die versioniert/hochgeladen wird. .env ist über .gitignore bewusst ausgeschlossen.

Empfohlen: Zugriff auf die relevanten Postfächer einschränken

MailboxSettings.ReadWrite als Anwendungsberechtigung gilt standardmäßig für alle Postfächer im Tenant. Um die App auf die paar Shared Mailboxes zu beschränken, in der Exchange Online PowerShell (Connect-ExchangeOnline):

# Mail-aktivierte Sicherheitsgruppe mit den betroffenen Shared Mailboxes anlegen
New-DistributionGroup -Name "OOO-Manager Postfächer" -Type Security

# App auf diese Gruppe beschränken (AppId = Client-ID aus Schritt 4)
New-ApplicationAccessPolicy -AppId "<CLIENT_ID>" `
  -PolicyScopeGroupId "OOO-Manager Postfächer" `
  -AccessRight RestrictAccess `
  -Description "OOO-Manager darf nur diese Postfächer verwalten"

Dieselbe Gruppe wird für MAILBOX_GROUP_ID in .env verwendet, damit Adress-Auswahl im Formular und tatsächliche Schreibrechte durchgängig auf denselben Postfach-Kreis beschränkt sind. Die Object ID der Gruppe ermitteln mit:

Get-DistributionGroup "OOO-Manager Postfächer" | Select-Object ExternalDirectoryObjectId

(oder im Entra-Admin-Center unter Gruppen → Gruppe öffnen → "Objekt-ID")

2. Konfiguration

cp .env.example .env

.env befüllen:

  • TENANT_ID, CLIENT_ID, CLIENT_SECRET aus Schritt 1

  • ADMIN_PASSWORD_HASH: erzeugen mit

    python -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('DEIN_PASSWORT', method='pbkdf2:sha256'))"
    

    (method='pbkdf2:sha256' explizit angeben der werkzeug-Default scrypt fehlt auf manchen Systemen, z.B. macOS mit LibreSSL-basiertem Python, im hashlib-Modul)

    Dieser Hash wird nur beim allerersten Start verwendet, um automatisch ein Admin-Konto namens admin anzulegen (echte Benutzerkonten, siehe unten). Danach spielt der Wert in .env keine Rolle mehr — Passwörter werden ausschließlich über die Benutzerverwaltung im Tool verändert.

  • SECRET_KEY: erzeugen mit

    python -c "import secrets; print(secrets.token_hex(32))"
    

3. Lokal starten (ohne Docker)

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export DATABASE_PATH=./data/ooo.sqlite
python -m app.main

App läuft auf http://localhost:5000

4. Auf der Synology DS1522+ betreiben (Container Manager)

  1. Projektordner (dieses Verzeichnis) auf die NAS kopieren, z.B. nach /volume1/docker/ooo-manager/
  2. In DSM Container ManagerProjektErstellen → vorhandenen Ordner /volume1/docker/ooo-manager als Projekt mit der docker-compose.yml auswählen
  3. .env mit den echten Werten aus Schritt 1/2 befüllen (liegt auf der NAS, nicht im Repo)
  4. Projekt starten die SQLite-Datenbank liegt persistent unter ./data/ooo.sqlite (Volume-Mount), übersteht also Container-Neustarts und -Updates
  5. Zugriff im Firmennetz über http://<NAS-IP>:8080
  6. Optional für Fernzugriff: DSM SystemsteuerungAnmeldeportalReverse-Proxy mit Let's-Encrypt-Zertifikat vor den Container schalten (HTTPS)

Funktionsweise

  • Pro Postfach werden in einer Kalenderansicht fixe Abwesenheiten (wiederkehrende Wochenmuster, z.B. jeden Abend 17:0008:00 oder Sa+So ganztägig, mit eigenem Namen) und individuelle Abwesenheiten gepflegt. Fixe Abwesenheiten können beim Anlegen für mehrere Wochentage gleichzeitig angelegt werden — diese Wochentage bilden zusammen ein Set (verknüpft über eine gemeinsame Gruppen-ID) und werden beim Bearbeiten (Klick auf einen beliebigen Wochentag des Sets) immer als Ganzes angezeigt: Bezeichnung/Zeiten gelten für alle, Wochentage können jederzeit hinzugefügt oder entfernt werden, "Ganze Serie löschen" entfernt das komplette Set auf einmal. Individuelle Abwesenheiten können ebenfalls als Serie über einen Datumsbereich + ausgewählte Wochentage angelegt werden (z.B. "jeden Mo+Mi vom 1.31.8."), lassen sich danach einzeln bearbeiten/löschen, als Serie ab einem Termin weiterbearbeiten ("diesen und zukünftige") oder ganz entfernen — bereits individuell angepasste Termine bleiben davon immer unberührt. Ganztägige Termine werden nicht in einer separaten Zeile über dem Kalender dargestellt, sondern als 00:0024:00-Block direkt in der Zeitleiste (kompaktere, einheitlichere Ansicht). Beide Terminarten können über Mitternacht hinausgehen und werden dann als zwei unabhängig verschiebbare Kalendersegmente dargestellt.
  • Individuelle Abwesenheiten sind entweder add (Abwesenheitsnotiz AN, z.B. Feiertag) oder suppress (Abwesenheitsnotiz AUS, überstimmt auch eine greifende fixe Abwesenheit) — für Letzteres gibt es einen eigenen, vereinfachten Schnellzugriff-Button ohne Serienoption.
  • Kalender unterstützt Drag & Drop zum Anlegen (Fläche ziehen) und Verschieben/Verlängern (auch von der Startseite), Rechtsklick-Kontextmenü (Bearbeiten/Duplizieren/Löschen) sowie eine Listendarstellung als Alternative zur Kalenderansicht.
  • Interne und externe Antwort lassen sich unabhängig voneinander aktivieren/deaktivieren. Deaktivierte externe Antworten werden über das native Graph-Feld externalAudience: "none" zuverlässig unterdrückt; für interne Antworten gibt es kein Graph-Äquivalent, weshalb lediglich ein leerer Text übermittelt wird (siehe Hinweis im Formular). Ist eine Antwort aktiviert, aber ohne Inhalt, wird das Speichern mit einer Fehlermeldung verhindert.
  • Beim Anlegen/Ändern eines Postfachs wird die Adresse per Microsoft Graph auf Existenz geprüft; ein eindeutiges "nicht gefunden" blockiert das Speichern, andere Fehler (Netzwerk, Rechte) lösen nur eine Warnung aus.
  • Die Antworttexte werden über einen Rich-Text-Editor (Quill) mit Formatierung erfasst und als HTML in internalReplyMessage/externalReplyMessage an Graph übergeben.
  • Ein Hintergrund-Job (alle SCHEDULER_INTERVAL_MINUTES, Standard 10 Minuten) berechnet je Postfach den Soll-Zustand und setzt ihn nur bei einer Änderung per Microsoft-Graph- API-Call (PATCH /users/{mailbox}/mailboxSettings).
  • „Nur bekannte Kontakte“ vs. „alle externen Absender“ wird über das native Graph-Feld automaticRepliesSetting abgebildet. Individuelle Nachrichten pro einzelnem externen Partner/Lieferanten sind bewusst nicht Teil dieses Tools (dafür wären Exchange-Mail-Flow-Regeln oder ein eigener Mail-Verarbeitungsdienst nötig).
  • Das Logbuch (Navigationsleiste) zeigt Konfigurationsänderungen (inkl. „Von“-Spalte, wer die Änderung gemacht hat), Benutzer-Anmeldungen und den Autoresponder-Verlauf (wann/warum ein Autoresponder aktiviert/deaktiviert wurde, inkl. Fehlern, nicht mehr auffindbaren Postfächern und globalen Verbindungsproblemen) gemeinsam an, filterbar nach Typ und Postfach.
  • Schutz vor manuellen OWA-Änderungen: Bevor der Hintergrund-Job eine geplante Zustandsänderung tatsächlich per Graph setzt, prüft er den aktuellen Live-Zustand in Microsoft 365. Weicht dieser vom zuletzt selbst gesetzten Zustand ab (= jemand hat die Abwesenheitsnotiz manuell in OWA geändert), wird das Postfach nicht überschrieben, sondern die automatische Verwaltung pausiert — im Dashboard sichtbar als Badge „Manuell im OWA aktiviert/deaktiviert“. Admins und zuständige Operatoren können die automatische Verwaltung über den Button „Automatik übernehmen“ gezielt wieder aktivieren — dabei wird der aktuelle Kalender-Zeitplan inkl. Antworttexte sofort per Graph auf die Mailbox gesetzt (nicht erst beim nächsten periodischen Durchlauf), die manuelle Einstellung wird also direkt überschrieben.
  • Rollen: Es gibt echte Benutzerkonten mit zwei Rollen, verwaltet unter „Benutzer“ (nur für Admins sichtbar):
    • Admin: voller Zugriff, inkl. Postfächer anlegen/archivieren, Benutzerverwaltung — sieht/verwaltet immer alle Postfächer, unabhängig von Zuweisungen
    • Operator: sieht und bearbeitet nur die ihm zugewiesenen Postfächer (Kalender, Antworttexte) — nicht zugewiesene Postfächer sind für Operatoren komplett unsichtbar (weder im Dashboard noch per direktem Link/API erreichbar). Archivierte, ihm zugewiesene Postfächer sieht der Operator, kann sie aber nur ansehen (nicht bearbeiten). Operatoren dürfen weder Postfächer anlegen/archivieren/löschen noch Benutzer verwalten.
    • Zuweisung: Auf der Bearbeiten-Seite eines Postfachs wählt ein Admin unter „Zuständige Operatoren“ aus, welche Operator-Konten dieses Postfach sehen/bearbeiten dürfen (Mehrfachauswahl möglich, z.B. bei Vertretung). Ein neu angelegtes Postfach hat zunächst keine zugewiesenen Operatoren — nur Admins sehen es, bis jemand zugewiesen wird.
    • Jeder Benutzer kann sein eigenes Passwort über das Benutzermenü (Klick auf den Benutzernamen oben rechts) ändern
  • Archivieren statt Löschen: Postfächer werden nicht direkt aus der Oberfläche gelöscht, sondern zunächst archiviert (Dashboard → „Archiv anzeigen“) und können von Admins jederzeit reaktiviert werden. Aus dem Archiv heraus können Admins ein Postfach zusätzlich endgültig löschen (inkl. aller fixen/individuellen Abwesenheiten und Nachrichten) — dieser Schritt ist nur für bereits archivierte Postfächer möglich und kann nicht rückgängig gemacht werden; Logbuch-Einträge zum gelöschten Postfach bleiben als Historie erhalten.
  • Sofortige Aktualisierung: Der periodische Hintergrund-Job (siehe unten) läuft normalerweise alle SCHEDULER_INTERVAL_MINUTES. Über den Button „Jetzt aktualisieren“ (Dashboard, nur Admins) lässt sich diese Prüfung sofort auslösen, statt auf den nächsten Intervall-Lauf zu warten — praktisch direkt nach dem Bearbeiten von Abwesenheiten.
  • Mehrere Profile pro Adresse: Für dieselbe E-Mail-Adresse können mehrere Postfach-Profile mit unterschiedlichen Zeitplänen angelegt werden (z.B. „Bürozeiten“ und „Betriebsurlaub“). Es ist bewusst immer nur ein Profil pro Adresse gleichzeitig aktiv — wird eines aktiviert, deaktiviert das Tool automatisch alle anderen Profile derselben Adresse, um widersprüchliche Graph-Aufrufe zu vermeiden.
  • Postfach duplizieren: Kopiert Antworttexte, Einstellungen sowie alle fixen/individuellen Abwesenheiten eines bestehenden Postfachs auf eine andere Adresse aus der Dropdown-Liste („Duplizieren“ auf der Bearbeiten-Seite). Die Kopie wird bewusst immer inaktiv angelegt.
  • E-Mail-Adresse nachträglich ändern: Beim Bearbeiten eines bestehenden Postfachs kann die Adresse nur gegen eine andere aus der (gruppen-eingeschränkten) Dropdown-Liste getauscht werden, nicht mehr frei eingegeben werden — verhindert versehentliche Tippfehler und hält die Zugriffsbeschränkung auf die Sicherheitsgruppe konsistent durch.