Add vue-router, login/setup flow and backend logging
Refactor frontend to route-based navigation with vue-router, add system setup and login pages with API integration. Add structured logging, access-log middleware and startup lifecycle to FastAPI backend.
This commit is contained in:
@@ -44,7 +44,13 @@ class Settings(BaseSettings):
|
||||
|
||||
redis_url: str | None = Field(default=None, alias="REDIS_URL")
|
||||
cors_origins: list[str] = Field(default_factory=list, alias="CORS_ORIGINS")
|
||||
vite_api_base_url: str = Field(default="http://127.0.0.1:8000/api/v1", alias="VITE_API_BASE_URL")
|
||||
vite_api_base_url: str = Field(
|
||||
default="http://127.0.0.1:8000/api/v1", alias="VITE_API_BASE_URL"
|
||||
)
|
||||
|
||||
log_level: str = Field(default="INFO", alias="LOG_LEVEL")
|
||||
log_dir: str = Field(default="logs", alias="LOG_DIR")
|
||||
log_file_enabled: bool = Field(default=True, alias="LOG_FILE_ENABLED")
|
||||
|
||||
@property
|
||||
def resolved_database_url(self) -> str:
|
||||
|
||||
72
server/src/app/core/logging.py
Normal file
72
server/src/app/core/logging.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
|
||||
LOG_FORMAT = "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s"
|
||||
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
_LEVEL_COLORS: dict[int, str] = {
|
||||
logging.DEBUG: "\033[36m",
|
||||
logging.INFO: "\033[32m",
|
||||
logging.WARNING: "\033[33m",
|
||||
logging.ERROR: "\033[31m",
|
||||
logging.CRITICAL: "\033[1;31m",
|
||||
}
|
||||
_RESET = "\033[0m"
|
||||
|
||||
|
||||
class _ColorFormatter(logging.Formatter):
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
color = _LEVEL_COLORS.get(record.levelno, "")
|
||||
record.levelname = f"{color}{record.levelname:<8}{_RESET}"
|
||||
return super().format(record)
|
||||
|
||||
|
||||
def _build_console_handler(level: int) -> logging.StreamHandler:
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setLevel(level)
|
||||
handler.setFormatter(_ColorFormatter(LOG_FORMAT, datefmt=DATE_FORMAT))
|
||||
return handler
|
||||
|
||||
|
||||
def _build_file_handler(log_dir: Path, level: int) -> RotatingFileHandler:
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
handler = RotatingFileHandler(
|
||||
log_dir / "app.log",
|
||||
maxBytes=10 * 1024 * 1024,
|
||||
backupCount=10,
|
||||
encoding="utf-8",
|
||||
)
|
||||
handler.setLevel(level)
|
||||
handler.setFormatter(logging.Formatter(LOG_FORMAT, datefmt=DATE_FORMAT))
|
||||
return handler
|
||||
|
||||
|
||||
def setup_logging(
|
||||
*,
|
||||
level: str = "INFO",
|
||||
log_dir: str = "logs",
|
||||
enable_file: bool = True,
|
||||
) -> None:
|
||||
numeric_level = getattr(logging, level.upper(), logging.INFO)
|
||||
root = logging.getLogger()
|
||||
root.setLevel(numeric_level)
|
||||
root.handlers.clear()
|
||||
|
||||
root.addHandler(_build_console_handler(numeric_level))
|
||||
|
||||
if enable_file:
|
||||
from app.core.config import SERVER_DIR
|
||||
|
||||
file_path = SERVER_DIR / log_dir
|
||||
root.addHandler(_build_file_handler(file_path, numeric_level))
|
||||
|
||||
logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
|
||||
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
|
||||
|
||||
|
||||
def get_logger(name: str) -> logging.Logger:
|
||||
return logging.getLogger(name)
|
||||
Reference in New Issue
Block a user