feat: add .env based configuration for API, Web, and Auth options
This commit is contained in:
@@ -15,6 +15,13 @@ import typer
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
from decnet.env import (
|
||||
DECNET_API_HOST,
|
||||
DECNET_API_PORT,
|
||||
DECNET_INGEST_LOG_FILE,
|
||||
DECNET_WEB_HOST,
|
||||
DECNET_WEB_PORT,
|
||||
)
|
||||
from decnet.archetypes import Archetype, all_archetypes, get_archetype
|
||||
from decnet.config import (
|
||||
DeckyConfig,
|
||||
@@ -199,22 +206,24 @@ def _build_deckies_from_ini(
|
||||
return deckies
|
||||
|
||||
|
||||
|
||||
@app.command()
|
||||
def api(
|
||||
port: int = typer.Option(8000, "--port", help="Port for the backend API"),
|
||||
log_file: str = typer.Option("/var/log/decnet/decnet.log", "--log-file", help="Path to the DECNET log file to monitor"),
|
||||
port: int = typer.Option(DECNET_API_PORT, "--port", help="Port for the backend API"),
|
||||
host: str = typer.Option(DECNET_API_HOST, "--host", help="Host IP for the backend API"),
|
||||
log_file: str = typer.Option(DECNET_INGEST_LOG_FILE, "--log-file", help="Path to the DECNET log file to monitor"),
|
||||
) -> None:
|
||||
"""Run the DECNET API and Web Dashboard in standalone mode."""
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
console.print(f"[green]Starting DECNET API on port {port}...[/]")
|
||||
console.print(f"[green]Starting DECNET API on {host}:{port}...[/]")
|
||||
_env: dict[str, str] = os.environ.copy()
|
||||
_env["DECNET_INGEST_LOG_FILE"] = str(log_file)
|
||||
try:
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "uvicorn", "decnet.web.api:app", "--host", "0.0.0.0", "--port", str(port)],
|
||||
[sys.executable, "-m", "uvicorn", "decnet.web.api:app", "--host", host, "--port", str(port)],
|
||||
env=_env
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
@@ -555,20 +564,21 @@ def list_archetypes() -> None:
|
||||
|
||||
@app.command(name="web")
|
||||
def serve_web(
|
||||
web_port: int = typer.Option(5173, "--web-port", help="Port to serve the DECNET Web Dashboard"),
|
||||
web_port: int = typer.Option(DECNET_WEB_PORT, "--web-port", help="Port to serve the DECNET Web Dashboard"),
|
||||
host: str = typer.Option(DECNET_WEB_HOST, "--host", help="Host IP to serve the Web Dashboard"),
|
||||
) -> None:
|
||||
"""Serve the DECNET Web Dashboard frontend."""
|
||||
import http.server
|
||||
import socketserver
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# Assuming decnet_web/dist is relative to the project root
|
||||
dist_dir = Path(__file__).parent.parent / "decnet_web" / "dist"
|
||||
|
||||
|
||||
if not dist_dir.exists():
|
||||
console.print(f"[red]Frontend build not found at {dist_dir}. Make sure you run 'npm run build' inside 'decnet_web'.[/]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
class SPAHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
# Try to serve the requested file
|
||||
@@ -577,12 +587,12 @@ def serve_web(
|
||||
# If not found or is a directory, serve index.html (for React Router)
|
||||
self.path = "/index.html"
|
||||
return super().do_GET()
|
||||
|
||||
|
||||
import os
|
||||
os.chdir(dist_dir)
|
||||
|
||||
with socketserver.TCPServer(("", web_port), SPAHTTPRequestHandler) as httpd:
|
||||
console.print(f"[green]Serving DECNET Web Dashboard on http://0.0.0.0:{web_port}[/]")
|
||||
|
||||
with socketserver.TCPServer((host, web_port), SPAHTTPRequestHandler) as httpd:
|
||||
console.print(f"[green]Serving DECNET Web Dashboard on http://{host}:{web_port}[/]")
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
|
||||
22
decnet/env.py
Normal file
22
decnet/env.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Calculate absolute path to the project root
|
||||
_ROOT: Path = Path(__file__).parent.parent.absolute()
|
||||
|
||||
# Load .env.local first, then fallback to .env
|
||||
load_dotenv(_ROOT / ".env.local")
|
||||
load_dotenv(_ROOT / ".env")
|
||||
|
||||
# API Options
|
||||
DECNET_API_HOST: str = os.environ.get("DECNET_API_HOST", "0.0.0.0")
|
||||
DECNET_API_PORT: int = int(os.environ.get("DECNET_API_PORT", "8000"))
|
||||
DECNET_JWT_SECRET: str = os.environ.get("DECNET_JWT_SECRET", "fallback-secret-key-change-me")
|
||||
DECNET_INGEST_LOG_FILE: str | None = os.environ.get("DECNET_INGEST_LOG_FILE", "/var/log/decnet/decnet.log")
|
||||
|
||||
# Web Dashboard Options
|
||||
DECNET_WEB_HOST: str = os.environ.get("DECNET_WEB_HOST", "0.0.0.0")
|
||||
DECNET_WEB_PORT: int = int(os.environ.get("DECNET_WEB_PORT", "8080"))
|
||||
DECNET_ADMIN_USER: str = os.environ.get("DECNET_ADMIN_USER", "admin")
|
||||
DECNET_ADMIN_PASSWORD: str = os.environ.get("DECNET_ADMIN_PASSWORD", "admin")
|
||||
@@ -20,6 +20,7 @@ from decnet.web.auth import (
|
||||
)
|
||||
from decnet.web.sqlite_repository import SQLiteRepository
|
||||
from decnet.web.ingester import log_ingestion_worker
|
||||
from decnet.env import DECNET_ADMIN_USER, DECNET_ADMIN_PASSWORD
|
||||
import asyncio
|
||||
|
||||
repo: SQLiteRepository = SQLiteRepository()
|
||||
@@ -31,13 +32,13 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
global ingestion_task
|
||||
await repo.initialize()
|
||||
# Create default admin if no users exist
|
||||
_admin_user: Optional[dict[str, Any]] = await repo.get_user_by_username("admin")
|
||||
_admin_user: Optional[dict[str, Any]] = await repo.get_user_by_username(DECNET_ADMIN_USER)
|
||||
if not _admin_user:
|
||||
await repo.create_user(
|
||||
{
|
||||
"uuid": str(uuid.uuid4()),
|
||||
"username": "admin",
|
||||
"password_hash": get_password_hash("admin"),
|
||||
"username": DECNET_ADMIN_USER,
|
||||
"password_hash": get_password_hash(DECNET_ADMIN_PASSWORD),
|
||||
"role": "admin",
|
||||
"must_change_password": True
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import os
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional, Any
|
||||
import jwt
|
||||
import bcrypt
|
||||
|
||||
SECRET_KEY: str = os.environ.get("DECNET_SECRET_KEY", "super-secret-key-change-me")
|
||||
from decnet.env import DECNET_JWT_SECRET
|
||||
|
||||
SECRET_KEY: str = DECNET_JWT_SECRET
|
||||
ALGORITHM: str = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 1440
|
||||
|
||||
|
||||
Reference in New Issue
Block a user