diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index b4965f9..f647dc4 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -74,7 +74,7 @@ - Bot start: `/home/peter/meshdd-bot/venv/bin/python main.py` - Web port: 8081 (konfigurierbar via `web.port`) - Forgejo remote with token in URL -- Current version: 0.08.15 +- Current version: 0.08.16 - Protobuf objects converted via `google.protobuf.json_format.MessageToDict()` - Auth: bcrypt (12 rounds), aiohttp-session EncryptedCookieStorage, aiosmtplib for emails - SMTP fallback: if no smtp.host configured, verification links logged to console diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b8d340..b8006e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [0.08.17] - 2026-02-20 + +### Changed +- **`.env`-Datei abgeschafft**: Alle Konfigurationswerte (`auth.secret_key`, `smtp.*`) + sind jetzt direkt in `config/config.yaml` enthalten – keine separate `.env`-Datei mehr nötig. +- **`meshbot/auth.py`**: `config.env()` durch `config.get()` ersetzt (liest jetzt aus YAML). +- **`meshbot/config.py`**: `ENV_PATH`, `_load_env()` und `env()` entfernt. +- **`config/env.example`** entfernt. + ## [0.08.16] - 2026-02-20 ### Changed diff --git a/config/config.example.yaml b/config/config.example.yaml new file mode 100644 index 0000000..685635a --- /dev/null +++ b/config/config.example.yaml @@ -0,0 +1,35 @@ +version: "0.08.17" + +bot: + name: "MeshDD-Bot" + command_prefix: "?" + +meshtastic: + host: "192.168.11.4" + port: 4403 + +web: + host: "0.0.0.0" + port: 8081 + online_threshold: 900 + +database: + path: "meshdd.db" + +auth: + session_max_age: 86400 + secret_key: "change-this-secret-key-32bytes!!" + +smtp: + host: "" + port: 465 + user: "" + password: "" + from: "MeshDD-Bot " + app_url: "http://localhost:8081" + +links: + - url: "https://meshtastic.org" + label: "Meshtastic" + - url: "https://meshmap.net" + label: "MeshMap" diff --git a/config/config.yaml b/config/config.yaml index 4bda76d..fc2a1ba 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -1,4 +1,4 @@ -version: "0.08.16" +version: "0.08.17" bot: name: "MeshDD-Bot" @@ -18,6 +18,15 @@ database: auth: session_max_age: 86400 + secret_key: "4pY1h4S/pqpTojjPb7LV6TfaTZC8jas/DV3vixXIP8s=" + +smtp: + host: "ssl0.ovh.net" + port: 465 + user: "info@meshdresden.eu" + password: "meshDresden" + from: "MeshDD-Bot " + app_url: "http://bot.home.pfeiffer-privat.de" links: - url: "https://meshtastic.org" diff --git a/config/env.example b/config/env.example deleted file mode 100644 index 1f5a2ff..0000000 --- a/config/env.example +++ /dev/null @@ -1,7 +0,0 @@ -AUTH_SECRET_KEY=change-this-secret-key-32bytes!! -SMTP_HOST= -SMTP_PORT=465 -SMTP_USER= -SMTP_PASSWORD= -SMTP_FROM=MeshDD-Bot -SMTP_APP_URL=http://localhost:8080 diff --git a/meshbot/auth.py b/meshbot/auth.py index e73939f..e4f0fc8 100644 --- a/meshbot/auth.py +++ b/meshbot/auth.py @@ -27,7 +27,7 @@ def check_password(password: str, hashed: str) -> bool: # ── Session setup ──────────────────────────────────── def setup_session(app: web.Application): - secret_key = config.env("AUTH_SECRET_KEY", "change-this-secret-key-32bytes!!") + secret_key = config.get("auth.secret_key", "change-this-secret-key-32bytes!!") # EncryptedCookieStorage accepts a Fernet object directly key_bytes = secret_key.encode("utf-8")[:32].ljust(32, b"\0") fernet_key = Fernet(base64.urlsafe_b64encode(key_bytes)) @@ -88,7 +88,7 @@ def require_admin_api(request: web.Request): # ── Email sending ──────────────────────────────────── async def _send_email(db, recipient: str, subject: str, html_body: str): - smtp_host = config.env("SMTP_HOST") + smtp_host = config.get("smtp.host", "") if not smtp_host: logger.info("SMTP not configured - email to %s not sent", recipient) await db.log_email(recipient, subject, "console", "SMTP not configured") @@ -100,22 +100,21 @@ async def _send_email(db, recipient: str, subject: str, html_body: str): msg = EmailMessage() msg["Subject"] = subject - msg["From"] = config.env("SMTP_FROM", "MeshDD-Bot ") + msg["From"] = config.get("smtp.from", "MeshDD-Bot ") msg["To"] = recipient msg.set_content("Bitte HTML-fähigen E-Mail-Client verwenden.") msg.add_alternative(html_body, subtype="html") - smtp_port = int(config.env("SMTP_PORT", "465")) smtp_client = aiosmtplib.SMTP( hostname=smtp_host, - port=smtp_port, + port=config.get("smtp.port", 465), use_tls=True, ) async with smtp_client: await smtp_client.login( - config.env("SMTP_USER"), - config.env("SMTP_PASSWORD"), + config.get("smtp.user", ""), + config.get("smtp.password", ""), ) await smtp_client.send_message(msg) @@ -127,7 +126,7 @@ async def _send_email(db, recipient: str, subject: str, html_body: str): async def send_verification_email(db, email: str, token: str): - app_url = config.env("SMTP_APP_URL", "http://localhost:8080") + app_url = config.get("smtp.app_url", "http://localhost:8081") verify_url = f"{app_url}/auth/verify?token={token}" logger.info("Verification link for %s: %s", email, verify_url) subject = "MeshDD-Bot - E-Mail verifizieren" @@ -142,7 +141,7 @@ async def send_verification_email(db, email: str, token: str): async def send_reset_email(db, email: str, token: str): - app_url = config.env("SMTP_APP_URL", "http://localhost:8080") + app_url = config.get("smtp.app_url", "http://localhost:8081") reset_url = f"{app_url}/auth/reset-password?token={token}" logger.info("Reset link for %s: %s", email, reset_url) subject = "MeshDD-Bot - Passwort zuruecksetzen" @@ -157,7 +156,7 @@ async def send_reset_email(db, email: str, token: str): async def send_user_info_email(db, email: str, name: str, password: str = None): - app_url = config.env("SMTP_APP_URL", "http://localhost:8080") + app_url = config.get("smtp.app_url", "http://localhost:8081") login_url = f"{app_url}/login" subject = "MeshDD-Bot - Deine Zugangsdaten" pw_line = f"

Passwort: {password}

" if password else "

Dein Passwort wurde nicht geaendert.

" diff --git a/meshbot/config.py b/meshbot/config.py index 790de40..c176fd9 100644 --- a/meshbot/config.py +++ b/meshbot/config.py @@ -7,26 +7,12 @@ import yaml logger = logging.getLogger(__name__) CONFIG_PATH = os.environ.get("CONFIG_PATH", os.path.join(os.path.dirname(os.path.dirname(__file__)), "config", "config.yaml")) -ENV_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config", ".env") _config = {} _mtime = 0.0 _callbacks = [] -def _load_env(): - if not os.path.exists(ENV_PATH): - return - with open(ENV_PATH, "r") as f: - for line in f: - line = line.strip() - if not line or line.startswith("#") or "=" not in line: - continue - key, _, value = line.partition("=") - os.environ.setdefault(key.strip(), value.strip()) - logger.info("Environment loaded from %s", ENV_PATH) - - def _load(): global _config, _mtime with open(CONFIG_PATH, "r") as f: @@ -75,10 +61,5 @@ def get(key: str, default=None): return val -def env(key: str, default: str = "") -> str: - return os.environ.get(key, default) - - # Load on import -_load_env() _load()