refactor(config): .env in config.yaml integriert
- auth.secret_key und smtp.* direkt in config/config.yaml aufgenommen - config/env.example entfernt, config/config.example.yaml als Vorlage hinzugefügt - meshbot/auth.py: config.env() → config.get() für alle Auth/SMTP-Werte - meshbot/config.py: ENV_PATH, _load_env(), env() entfernt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4b9dd45f05
commit
261f0dac13
|
|
@ -74,7 +74,7 @@
|
||||||
- Bot start: `/home/peter/meshdd-bot/venv/bin/python main.py`
|
- Bot start: `/home/peter/meshdd-bot/venv/bin/python main.py`
|
||||||
- Web port: 8081 (konfigurierbar via `web.port`)
|
- Web port: 8081 (konfigurierbar via `web.port`)
|
||||||
- Forgejo remote with token in URL
|
- 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()`
|
- Protobuf objects converted via `google.protobuf.json_format.MessageToDict()`
|
||||||
- Auth: bcrypt (12 rounds), aiohttp-session EncryptedCookieStorage, aiosmtplib for emails
|
- Auth: bcrypt (12 rounds), aiohttp-session EncryptedCookieStorage, aiosmtplib for emails
|
||||||
- SMTP fallback: if no smtp.host configured, verification links logged to console
|
- SMTP fallback: if no smtp.host configured, verification links logged to console
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
# Changelog
|
# 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
|
## [0.08.16] - 2026-02-20
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
||||||
35
config/config.example.yaml
Normal file
35
config/config.example.yaml
Normal file
|
|
@ -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 <noreply@example.com>"
|
||||||
|
app_url: "http://localhost:8081"
|
||||||
|
|
||||||
|
links:
|
||||||
|
- url: "https://meshtastic.org"
|
||||||
|
label: "Meshtastic"
|
||||||
|
- url: "https://meshmap.net"
|
||||||
|
label: "MeshMap"
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
version: "0.08.16"
|
version: "0.08.17"
|
||||||
|
|
||||||
bot:
|
bot:
|
||||||
name: "MeshDD-Bot"
|
name: "MeshDD-Bot"
|
||||||
|
|
@ -18,6 +18,15 @@ database:
|
||||||
|
|
||||||
auth:
|
auth:
|
||||||
session_max_age: 86400
|
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 <info@meshdresden.eu>"
|
||||||
|
app_url: "http://bot.home.pfeiffer-privat.de"
|
||||||
|
|
||||||
links:
|
links:
|
||||||
- url: "https://meshtastic.org"
|
- url: "https://meshtastic.org"
|
||||||
|
|
|
||||||
|
|
@ -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 <noreply@example.com>
|
|
||||||
SMTP_APP_URL=http://localhost:8080
|
|
||||||
|
|
@ -27,7 +27,7 @@ def check_password(password: str, hashed: str) -> bool:
|
||||||
# ── Session setup ────────────────────────────────────
|
# ── Session setup ────────────────────────────────────
|
||||||
|
|
||||||
def setup_session(app: web.Application):
|
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
|
# EncryptedCookieStorage accepts a Fernet object directly
|
||||||
key_bytes = secret_key.encode("utf-8")[:32].ljust(32, b"\0")
|
key_bytes = secret_key.encode("utf-8")[:32].ljust(32, b"\0")
|
||||||
fernet_key = Fernet(base64.urlsafe_b64encode(key_bytes))
|
fernet_key = Fernet(base64.urlsafe_b64encode(key_bytes))
|
||||||
|
|
@ -88,7 +88,7 @@ def require_admin_api(request: web.Request):
|
||||||
# ── Email sending ────────────────────────────────────
|
# ── Email sending ────────────────────────────────────
|
||||||
|
|
||||||
async def _send_email(db, recipient: str, subject: str, html_body: str):
|
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:
|
if not smtp_host:
|
||||||
logger.info("SMTP not configured - email to %s not sent", recipient)
|
logger.info("SMTP not configured - email to %s not sent", recipient)
|
||||||
await db.log_email(recipient, subject, "console", "SMTP not configured")
|
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 = EmailMessage()
|
||||||
msg["Subject"] = subject
|
msg["Subject"] = subject
|
||||||
msg["From"] = config.env("SMTP_FROM", "MeshDD-Bot <noreply@example.com>")
|
msg["From"] = config.get("smtp.from", "MeshDD-Bot <noreply@example.com>")
|
||||||
msg["To"] = recipient
|
msg["To"] = recipient
|
||||||
|
|
||||||
msg.set_content("Bitte HTML-fähigen E-Mail-Client verwenden.")
|
msg.set_content("Bitte HTML-fähigen E-Mail-Client verwenden.")
|
||||||
msg.add_alternative(html_body, subtype="html")
|
msg.add_alternative(html_body, subtype="html")
|
||||||
smtp_port = int(config.env("SMTP_PORT", "465"))
|
|
||||||
smtp_client = aiosmtplib.SMTP(
|
smtp_client = aiosmtplib.SMTP(
|
||||||
hostname=smtp_host,
|
hostname=smtp_host,
|
||||||
port=smtp_port,
|
port=config.get("smtp.port", 465),
|
||||||
use_tls=True,
|
use_tls=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
async with smtp_client:
|
async with smtp_client:
|
||||||
await smtp_client.login(
|
await smtp_client.login(
|
||||||
config.env("SMTP_USER"),
|
config.get("smtp.user", ""),
|
||||||
config.env("SMTP_PASSWORD"),
|
config.get("smtp.password", ""),
|
||||||
)
|
)
|
||||||
await smtp_client.send_message(msg)
|
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):
|
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}"
|
verify_url = f"{app_url}/auth/verify?token={token}"
|
||||||
logger.info("Verification link for %s: %s", email, verify_url)
|
logger.info("Verification link for %s: %s", email, verify_url)
|
||||||
subject = "MeshDD-Bot - E-Mail verifizieren"
|
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):
|
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}"
|
reset_url = f"{app_url}/auth/reset-password?token={token}"
|
||||||
logger.info("Reset link for %s: %s", email, reset_url)
|
logger.info("Reset link for %s: %s", email, reset_url)
|
||||||
subject = "MeshDD-Bot - Passwort zuruecksetzen"
|
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):
|
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"
|
login_url = f"{app_url}/login"
|
||||||
subject = "MeshDD-Bot - Deine Zugangsdaten"
|
subject = "MeshDD-Bot - Deine Zugangsdaten"
|
||||||
pw_line = f"<p><strong>Passwort:</strong> {password}</p>" if password else "<p>Dein Passwort wurde nicht geaendert.</p>"
|
pw_line = f"<p><strong>Passwort:</strong> {password}</p>" if password else "<p>Dein Passwort wurde nicht geaendert.</p>"
|
||||||
|
|
|
||||||
|
|
@ -7,26 +7,12 @@ import yaml
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONFIG_PATH = os.environ.get("CONFIG_PATH", os.path.join(os.path.dirname(os.path.dirname(__file__)), "config", "config.yaml"))
|
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 = {}
|
_config = {}
|
||||||
_mtime = 0.0
|
_mtime = 0.0
|
||||||
_callbacks = []
|
_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():
|
def _load():
|
||||||
global _config, _mtime
|
global _config, _mtime
|
||||||
with open(CONFIG_PATH, "r") as f:
|
with open(CONFIG_PATH, "r") as f:
|
||||||
|
|
@ -75,10 +61,5 @@ def get(key: str, default=None):
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
def env(key: str, default: str = "") -> str:
|
|
||||||
return os.environ.get(key, default)
|
|
||||||
|
|
||||||
|
|
||||||
# Load on import
|
# Load on import
|
||||||
_load_env()
|
|
||||||
_load()
|
_load()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue