Eine Flask-Anwendung SEIRI – Praktikum zum Verwalten von Praktikumsbewerbungen (Fachinformatiker – Daten und Prozessanalyse). Der gesamte Ablauf einer Bewerbung wird unterstützt:
- jede Firma hat einen Status (verwaltbar im „Status“-Bereich),
- ein „Sucht?“-Feld (sucht / sucht nicht / unbekannt),
- eine Website und ein gespeichertes Anschreiben (Text in der Datenbank),
- eine „Mein Profil“-Seite mit deinen persönlichen Daten,
- eine sortierbare Liste (nach Name / Status / letzter Änderung),
- eine Schaltfläche „PDF generieren“ — ein sauberes A4-Anschreiben mit den Firmendaten, deinen Daten und dem aktuellen Datum,
- mehrere Stellenangebot-Links pro Firma (exportierbar),
- eine Praktikumsseiten-Seite — eine separate Liste mit Job-Portalen (eigene Tabelle),
- eine Teilen-Funktion: eine nur-lesbare Firmenliste über einen geheimen Link,
- eine optionale Google-Firmensuche beim Hinzufügen einer neuen Firma.
Die Oberfläche nutzt das CSS-Framework Bulma (dunkles Design). Die Bedienoberfläche ist auf Deutsch.
Im Projektverzeichnis mit einer virtuellen Umgebung:
# virtuelle Umgebung anlegen
python -m venv .venv
# Umgebung aktivieren
# Windows: .venv\Scripts\activate
# Linux/macOS: source .venv/bin/activate
# Abhängigkeiten installieren
pip install -r requirements.txt# (optional) Datenbankschema anlegen / aktualisieren
.venv/bin/python startdb.py
# Entwicklungsserver starten
# Windows: .venv\Scripts\python.exe run.py
# Linux/macOS: .venv/bin/python run.pyBeim Start prüft run.py die Datenbank und aktualisiert das Schema (fehlende Tabellen und Spalten werden angelegt — ohne Daten zu löschen).
Die Anwendung ist dann erreichbar unter: http://127.0.0.1:5001
Hinweis: Der Standard-Port ist 5001, weil auf macOS der Port 5000 vom Systemprozess ControlCenter (AirPlay) belegt ist. Port und Host lassen sich über Umgebungsvariablen ändern:
PORT=8080 HOST=127.0.0.1 .venv/bin/python run.py
models.py— Datenbankmodelle (Status,Company,Profile,Settings,JobLink,JobSite) — siehedocs/db_structure.mdroutes.py— Anwendungsroutengoogle_places.py— Google-Places-Suche für neue Firmen (serverseitiger API-Key)run.py— Application-Factory + Serverstartstartdb.py— Datenbank-Erstellung / automatische Schema-Aktualisierungtemplates/— HTML-Templates (Bulma)static/css/app.css— Design (dunkel, professionell)static/fonts/— Schriftarten für die PDF-Erzeugungdocs/db_structure.md— Dokumentation der Datenbankstruktur
Im Admin-Bereich erstellt der Tab „Teilen“ einen geheimen, nicht erratbaren Link (/share/<token>), der eine nur-lesbare Liste aller Firmen zeigt (Name, Adresse, Stadt, E-Mail, Telefon, Website, Stellenangebote und „Sucht?“) — inklusive Schaltfläche zum CSV-Export. Der Link ist nicht passwortgeschützt — jeder, der den Link hat, sieht die Liste. Erstelle daher einen neuen Link, wenn du den Zugriff widerrufen möchtest.
Auf der geteilten Seite können die Firmen nach Name, Stadt und Stellenangebote sortiert werden. Außerdem gibt es eine Suche nach dem Firmennamen. Stellenangebote werden als kompakte Schaltflächen („Link 1“, „Link 2“ usw.) angezeigt.
Der CSV-Export (immer die gesamte Liste auf einmal) enthält: Name, Adresse, PLZ, Stadt, E-Mail, Telefon, Website, Stellenangebote, Status, Sucht?.
Beim Anlegen einer neuen Firma kann über „Firma suchen“ die Google Places API nach passenden Unternehmen durchsucht werden. Die gefundenen Daten können anschließend über „Übernehmen“ in das Formular übernommen und vor dem Speichern geprüft werden.
Wenn die Suche ohne Stadt Treffer aus mehreren Städten liefert, muss zuerst eine Stadt ausgewählt werden. Danach wird die Suche auf diese Stadt eingeschränkt. Die Suche wird nur nach einem Klick auf „Firma suchen“ ausgeführt — nicht automatisch beim Tippen.
Die Google-Suche kann im Admin-Bereich unter Admin → Teilen → Google-Firmensuche aktivieren ein- oder ausgeschaltet werden. Ist sie ausgeschaltet, wird die Schaltfläche beim Hinzufügen einer Firma ausgeblendet und der Such-Endpunkt blockiert.
Die Suche kann folgende Daten übernehmen, sofern Google sie liefert:
- Firmenname
- vollständige Adresse
- Straße und Hausnummer
- PLZ und Stadt
- Telefonnummer
- Website
E-Mail-Adressen werden von der verwendeten Google-Places-Suche normalerweise nicht geliefert und bleiben daher meist leer. Die Google Places API kann außerdem eine aktivierte Abrechnung im Google-Cloud-Projekt voraussetzen. Beschränke den API-Key in Google Cloud auf die benötigte Places API und verwende ihn ausschließlich serverseitig.
Eine Schritt-für-Schritt-Anleitung für einen eigenen Server (getestet mit Ubuntu 22.04 / Debian 12).
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3 python3-venv python3-pip git nginx# Variante A: per Git
git clone <dein-repo-url> /opt/seiri
# Variante B: per rsync von deinem Rechner
rsync -avz /pfad/zu/seiri/ benutzer@dein-server:/opt/seiri/cd /opt/seiri
python3 -m venv .venv
.venv/bin/pip install -r requirements.txtsudo bash -c 'echo "SECRET_KEY=$(openssl rand -hex 32)" > /etc/seiri.env'
sudo bash -c 'echo "SEIRI_SECURE_COOKIES=1" >> /etc/seiri.env'
sudo bash -c 'echo "BEHIND_PROXY=1" >> /etc/seiri.env'
# Google Places API key — replace the placeholder with your real key
sudo bash -c 'echo "GOOGLE_PLACES_API_KEY=DEIN_GOOGLE_API_KEY" >> /etc/seiri.env'
sudo chmod 600 /etc/seiri.envSEIRI_SECURE_COOKIES=1— Sitzungscookies nur über HTTPS senden (aktivieren, sobald HTTPS läuft, siehe Schritt 7).BEHIND_PROXY=1— nginx als Reverse-Proxy berücksichtigen (echte Client-IP undhttps-Schema für den Teilen-Link).GOOGLE_PLACES_API_KEY— serverseitiger Schlüssel für die Firmensuche auf der Seite „Firma hinzufügen“. Der Schlüssel wird nicht im Quellcode gespeichert.- Ohne gesetzten
SECRET_KEYerzeugt die Anwendung beim ersten Start automatisch einen zufälligen Schlüssel und speichert ihn ininstance/secret_key.
Nach dem Eintragen des Schlüssels die Datei absichern und prüfen, ohne den Schlüssel auszugeben:
sudo chmod 600 /etc/seiri.env
sudo grep -q '^GOOGLE_PLACES_API_KEY=.' /etc/seiri.env && echo 'Google Places API key: SET' || echo 'Google Places API key: MISSING'Das Google-Cloud-Projekt muss die Places API (New) aktiviert haben. Je nach Google-Cloud-Konto ist außerdem ein Billing-Konto erforderlich. API-Keys niemals in Git, Templates oder JavaScript speichern.
Datenbank- und Schlüsseldatei absichern (enthalten persönliche Daten):
sudo chmod 700 /opt/seiri/instance
sudo chmod 600 /opt/seiri/instance/seiri.db /opt/seiri/instance/secret_keyErstelle /etc/systemd/system/seiri.service:
[Unit]
Description=SEIRI – Praktikum
After=network.target
[Service]
User=www-data
WorkingDirectory=/opt/seiri
EnvironmentFile=/etc/seiri.env
ExecStart=/opt/seiri/.venv/bin/waitress-serve --host 127.0.0.1 --port 5001 --call run:create_app
Restart=always
[Install]
WantedBy=multi-user.targetAnschließend aktivieren und starten:
sudo chown -R www-data:www-data /opt/seiri
sudo systemctl daemon-reload
sudo systemctl enable --now seiri
sudo systemctl status seiriNach Änderungen an /etc/seiri.env den Dienst neu starten:
sudo systemctl daemon-reload
sudo systemctl restart seiri
sudo systemctl status seiri --no-pagerErstelle /etc/nginx/sites-available/seiri:
server {
listen 80;
server_name deine-domain.de;
# Teilen-Links nicht loggen (geheimes Token steht in der URL)
location /share/ {
access_log off;
proxy_pass http://127.0.0.1:5001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://127.0.0.1:5001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Aktivieren und neu laden:
sudo ln -s /etc/nginx/sites-available/seiri /etc/nginx/sites-enabled/seiri
sudo nginx -t
sudo systemctl reload nginxsudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d deine-domain.deDanach HSTS aktivieren (im server { … }-Block für Port 443 der nginx-Konfiguration):
add_header Strict-Transport-Security "max-age=31536000" always;Debug-Modus niemals aktivieren: Der Produktivbetrieb läuft über
waitress-serve(siehe Schritt 5).FLASK_DEBUG=1bzw.python run.pyist nur für die lokale Entwicklung gedacht — der Werkzeug-Debugger darf nie öffentlich erreichbar sein.
Die App läuft intern nur auf 127.0.0.1, nach außen werden nur HTTP/HTTPS freigegeben:
sudo ufw allow 'Nginx Full'
sudo ufw enableDanach ist die Anwendung unter https://deine-domain.de erreichbar. Den Teilen-Link findest du im Admin-Bereich unter „Teilen“.