create_db.py/create_admin.py: frisches Setup auf den vollständig migrierten Schema-Stand bringen
Ein wirklich frisches Setup (create_db.py auf einer neuen Installation) erzeugte bislang nur den ALLERERSTEN Schema-Stand aus der Projektgeschichte -- alle seither per _ensure_schema()-Migration nachgerüsteten Spalten und Tabellen fehlten komplett, bis diese Migration beim ersten App-Start erfolgreich durchläuft. Live auf einem frisch installierten Produktivsystem ist genau das nicht passiert: "sqlite3.OperationalError: no such column: deleted_at" beim Dashboard-Aufruf, weil devices/switches/credentials/ users/groups ohne die Papierkorb-Spalte angelegt wurden. Fehlten außerdem komplett: dhcp_subnets, dhcp_manual_reservations, service_accounts, ldap_group_mappings (LDAP/DHCP-Funktionen wären auf einer solchen frischen Installation gar nicht nutzbar gewesen) sowie dhcp_option_defs.is_standard. - create_db.py jetzt deckungsgleich mit dem tatsächlichen, vollständig migrierten Schema (per .schema-Dump von einer produktiv laufenden Instanz verifiziert) -- category/deleted_at auf credentials, deleted_at auf switches, credential_id/ssh_port/deleted_at auf devices, deleted_at auf users/groups, is_standard auf dhcp_option_defs, plus die vier fehlenden Tabellen. - POE_DB_PATH → TESM_DB_PATH in create_db.py UND create_admin.py (weiteres übersehenes Namensrelikt aus der TESM-Umbenennung), Docstring von create_admin.py aktualisiert. - Live verifiziert: komplett frische DB erzeugt, App auf separatem Testport dagegen gestartet, alle wesentlichen Routen (Dashboard, Geräte, Switche, Zugangsdaten, Wartung, Papierkorb, Benutzer, Gruppen, alle Einstellungsseiten, Logs, Konto) liefern 200 ohne einen einzigen Fehler im App-Log -- eine frische Installation hängt jetzt an keiner Stelle mehr von der Laufzeit-Migration ab, um überhaupt benutzbar zu sein.
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Legt einen Admin-Benutzer im PoE Manager an."""
|
||||
"""Legt einen Admin-Benutzer in TESM an."""
|
||||
import os
|
||||
import sqlite3
|
||||
from getpass import getpass
|
||||
from flask_bcrypt import Bcrypt
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
DB_PATH = os.environ.get("POE_DB_PATH", os.path.join(BASE_DIR, "sqlite.db"))
|
||||
DB_PATH = os.environ.get("TESM_DB_PATH", os.path.join(BASE_DIR, "sqlite.db"))
|
||||
bcrypt = Bcrypt()
|
||||
|
||||
|
||||
|
||||
+74
-5
@@ -4,7 +4,7 @@ import os
|
||||
import sqlite3
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
DB_PATH = os.environ.get("POE_DB_PATH", os.path.join(BASE_DIR, "sqlite.db"))
|
||||
DB_PATH = os.environ.get("TESM_DB_PATH", os.path.join(BASE_DIR, "sqlite.db"))
|
||||
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
c = conn.cursor()
|
||||
@@ -16,7 +16,9 @@ CREATE TABLE IF NOT EXISTS credentials (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
password TEXT NOT NULL
|
||||
password TEXT NOT NULL,
|
||||
category TEXT NOT NULL DEFAULT 'switch',
|
||||
deleted_at TEXT
|
||||
);
|
||||
""")
|
||||
|
||||
@@ -30,6 +32,7 @@ CREATE TABLE IF NOT EXISTS switches (
|
||||
credential_id INTEGER,
|
||||
last_modified_by TEXT,
|
||||
last_modified_at TEXT,
|
||||
deleted_at TEXT,
|
||||
FOREIGN KEY (credential_id) REFERENCES credentials(id)
|
||||
);
|
||||
""")
|
||||
@@ -45,6 +48,9 @@ CREATE TABLE IF NOT EXISTS devices (
|
||||
is_active INTEGER DEFAULT 1,
|
||||
last_modified_by TEXT,
|
||||
last_modified_at TEXT,
|
||||
credential_id INTEGER,
|
||||
ssh_port INTEGER,
|
||||
deleted_at TEXT,
|
||||
FOREIGN KEY (switch_hostname) REFERENCES switches(hostname)
|
||||
);
|
||||
""")
|
||||
@@ -61,7 +67,8 @@ CREATE TABLE IF NOT EXISTS users (
|
||||
avatar_filename TEXT,
|
||||
auth_source TEXT NOT NULL DEFAULT 'local',
|
||||
email TEXT,
|
||||
is_locked INTEGER NOT NULL DEFAULT 0
|
||||
is_locked INTEGER NOT NULL DEFAULT 0,
|
||||
deleted_at TEXT
|
||||
);
|
||||
""")
|
||||
|
||||
@@ -73,7 +80,8 @@ CREATE TABLE IF NOT EXISTS groups (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
is_default INTEGER DEFAULT 0,
|
||||
is_system INTEGER DEFAULT 0
|
||||
is_system INTEGER DEFAULT 0,
|
||||
deleted_at TEXT
|
||||
);
|
||||
""")
|
||||
|
||||
@@ -109,13 +117,17 @@ CREATE TABLE IF NOT EXISTS settings (
|
||||
|
||||
# DHCP: eigene Options-Definitionen (analog "Vordefinierte Optionen und
|
||||
# Werte" bei einem Windows-DHCP-Server) — Code/Name/Typ/Beschreibung.
|
||||
# is_standard markiert die vorbefüllten Standard-Optionen (Router/DNS/…) --
|
||||
# die können vom Admin nicht gelöscht werden, nur ihr Wert geleert (die
|
||||
# tatsächliche Vorbefüllung übernimmt app.py: _ensure_schema()).
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS dhcp_option_defs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code INTEGER NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
type TEXT NOT NULL,
|
||||
description TEXT
|
||||
description TEXT,
|
||||
is_standard INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
""")
|
||||
|
||||
@@ -136,6 +148,63 @@ CREATE TABLE IF NOT EXISTS dhcp_option_values (
|
||||
);
|
||||
""")
|
||||
|
||||
# DHCP: konfigurierte Subnetze (Interface + Range + optionales Gateway/DNS)
|
||||
# -- ein Host kann mehrere Subnetze gleichzeitig bedienen, je eines pro
|
||||
# Interface/Netz. sort_order steuert die Anzeige-/Verarbeitungsreihenfolge,
|
||||
# enabled erlaubt ein Subnetz vorübergehend aus der geschriebenen
|
||||
# Konfiguration auszuschließen, ohne es zu löschen.
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS dhcp_subnets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
interface TEXT NOT NULL,
|
||||
range_start TEXT NOT NULL,
|
||||
range_end TEXT NOT NULL,
|
||||
gateway TEXT,
|
||||
dns TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
enabled INTEGER NOT NULL DEFAULT 1
|
||||
);
|
||||
""")
|
||||
|
||||
# DHCP: manuelle Reservierungen für Geräte OHNE eigenen Client-Datensatz
|
||||
# (Geräte mit MAC+IP in der devices-Tabelle bekommen ihre Reservierung
|
||||
# automatisch, siehe _dhcp_reservation_candidates in app.py).
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS dhcp_manual_reservations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
mac TEXT NOT NULL UNIQUE,
|
||||
ip TEXT NOT NULL,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
""")
|
||||
|
||||
# Service-Accounts (aktuell nur das LDAP-Bind-Konto) -- bewusst NICHT in der
|
||||
# users-Tabelle (kein echter, einloggbarer App-Benutzer) und NICHT in der
|
||||
# credentials-Tabelle (die ist für Switch-/Geräte-Zugangsdaten, ein anderer
|
||||
# Anwendungsfall) -- eine eigene, kleine Tabelle. "purpose" ist UNIQUE, ein
|
||||
# neu gespeichertes Konto für denselben Zweck ersetzt das alte.
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS service_accounts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
purpose TEXT UNIQUE NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
password TEXT NOT NULL
|
||||
);
|
||||
""")
|
||||
|
||||
# AD-Gruppe -> App-Rechtegruppe: mehrere AD-Gruppen können jeweils einer
|
||||
# App-Gruppe zugeordnet werden (additiv, siehe _ldap_resolve_app_groups in
|
||||
# app.py) -- passt keine dieser Gruppen, greift die Standardgruppe.
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS ldap_group_mappings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ad_group_dn TEXT UNIQUE NOT NULL,
|
||||
ad_group_name TEXT NOT NULL,
|
||||
app_group_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (app_group_id) REFERENCES groups(id)
|
||||
);
|
||||
""")
|
||||
|
||||
# Änderungslog (Audit-Log) — wer hat wann was geändert. Bewusst OHNE
|
||||
# PoE-Neustarts (die stehen im Live-Log von poe.sh).
|
||||
c.execute("""
|
||||
|
||||
Reference in New Issue
Block a user