feat: complete dockerization architecture with data/logs persistence and prod export script v1.3.0
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,3 +4,5 @@ __pycache__/
|
||||
*.pyc
|
||||
.env
|
||||
.DS_Store
|
||||
aInventory-PROD*
|
||||
aInventory-PROD*.zip
|
||||
|
||||
12
Caddyfile
Normal file
12
Caddyfile
Normal file
@@ -0,0 +1,12 @@
|
||||
# TFM aInventory - Caddy Self-Signed Internal Proxy
|
||||
# This replaces the need for `local-ssl-proxy` in Node.
|
||||
|
||||
:3003 {
|
||||
tls internal
|
||||
reverse_proxy frontend:3000
|
||||
}
|
||||
|
||||
:3002 {
|
||||
tls internal
|
||||
reverse_proxy backend:8000
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"version": "1.2.9",
|
||||
"version": "1.3.0",
|
||||
"last_build": "2026-04-11-1235",
|
||||
"commit": "3b2f1",
|
||||
"commit": "PENDING",
|
||||
"changelog": [
|
||||
"v1.3.0: Dockerization: Dual-mode architecture, standalone Next.js, Python logger, and prod export script",
|
||||
"v1.2.9: Automatic IDE Entry Points (GEMINI.md, CLAUDE.md proxies)",
|
||||
"v1.2.8: Absolute Documentation Consolidation (Unified ARCHITECTURE and AI_RULES)",
|
||||
"v1.2.7: Documentation Refactor: Created TECH_STACK.md for SSOT and removed absolute paths",
|
||||
"v1.2.6: Hotfix - Added missing icon imports (Layers, ChevronDown)",
|
||||
"v1.2.5: Synchronized icons across UI (Layers for Categories, Package for Item Types)",
|
||||
"v1.2.4: Offline LDAP auth support and UI affordance improvements (Dropdown chevrons, soft password dots)",
|
||||
"v1.2.3: System-wide UI consistency (standardized font sizes, removed uppercase)",
|
||||
"v1.2.2: UI Readability refactor (Removed uppercase/tracking, increased font sizes)",
|
||||
"v1.2.1: Git path persistence, master/dev/vX branching, and UI case-sensitivity fix"
|
||||
"v1.2.3: System-wide UI consistency (standardized font sizes, removed uppercase)"
|
||||
]
|
||||
}
|
||||
|
||||
35
backend/Dockerfile
Normal file
35
backend/Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
# Install system dependencies required for python-ldap (needed by backend)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
libldap2-dev \
|
||||
libsasl2-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy requirements and install
|
||||
COPY backend/requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Create non-root user
|
||||
RUN adduser --system --group appuser
|
||||
|
||||
# Copy application files
|
||||
COPY backend ./backend
|
||||
|
||||
# We define the data dir explicitly for Docker
|
||||
ENV DATA_DIR="/app/data"
|
||||
ENV LOGS_DIR="/app/logs"
|
||||
|
||||
# Ensure the appuser can write to data and logs if we pre-create them,
|
||||
# although Docker volumes will handle ownership context.
|
||||
RUN mkdir -p /app/data /app/logs && chown -R appuser:appuser /app
|
||||
|
||||
USER appuser
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# Start Uvicorn pointing to the backend module
|
||||
CMD ["python", "-m", "uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
@@ -4,9 +4,11 @@ import os
|
||||
|
||||
# Get absolute path for the backend directory
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
DATA_DIR = os.path.join(BASE_DIR, "data")
|
||||
|
||||
# Create data directory if it doesn't exist in the backend folder
|
||||
# Use DATA_DIR from environment if running in Docker, otherwise fallback to local 'data' folder
|
||||
DATA_DIR = os.environ.get("DATA_DIR", os.path.join(BASE_DIR, "data"))
|
||||
|
||||
# Create data directory if it doesn't exist
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
|
||||
# Handle absolute path for SQLite (needs 4 slashes on Unix)
|
||||
|
||||
51
backend/logger.py
Normal file
51
backend/logger.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import logging
|
||||
import os
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
# Define paths
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
# Fallback to local 'logs' dir if not overridden in Docker
|
||||
LOGS_DIR = os.environ.get("LOGS_DIR", os.path.join(BASE_DIR, "logs"))
|
||||
|
||||
# Ensure directory exists
|
||||
os.makedirs(LOGS_DIR, exist_ok=True)
|
||||
|
||||
LOG_FILE_PATH = os.path.join(LOGS_DIR, "backend.log")
|
||||
|
||||
def setup_logger():
|
||||
logger = logging.getLogger("ainventory")
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# Avoid duplicate handlers if setup multiple times
|
||||
if logger.handlers:
|
||||
return logger
|
||||
|
||||
formatter = logging.Formatter(
|
||||
"[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
|
||||
# Console Handler
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setFormatter(formatter)
|
||||
|
||||
# File Handler (10MB max, keep 5 backups)
|
||||
file_handler = RotatingFileHandler(
|
||||
LOG_FILE_PATH, maxBytes=10*1024*1024, backupCount=5
|
||||
)
|
||||
file_handler.setFormatter(formatter)
|
||||
|
||||
# Attach handlers
|
||||
logger.addHandler(console_handler)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
# Special redirect for uvicorn logs to also go to file
|
||||
uvicorn_logger = logging.getLogger("uvicorn")
|
||||
uvicorn_logger.addHandler(file_handler)
|
||||
|
||||
uvicorn_access_logger = logging.getLogger("uvicorn.access")
|
||||
uvicorn_access_logger.addHandler(file_handler)
|
||||
|
||||
return logger
|
||||
|
||||
log = setup_logger()
|
||||
174
backend/logs/backend.log
Normal file
174
backend/logs/backend.log
Normal file
@@ -0,0 +1,174 @@
|
||||
[2026-04-11 12:25:22] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:25:22] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:25:22] [INFO] [uvicorn.error] Started server process [87758]
|
||||
[2026-04-11 12:25:22] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:25:22] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:25:24] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:25:54] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:26:24] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:26:54] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:27:24] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:27:39] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:27:39] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:27:39] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:27:39] [INFO] [uvicorn.error] Finished server process [87758]
|
||||
[2026-04-11 12:27:40] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:27:40] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:27:40] [INFO] [uvicorn.error] Started server process [87911]
|
||||
[2026-04-11 12:27:40] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:27:40] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:27:41] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:27:41] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:27:41] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:27:41] [INFO] [uvicorn.error] Finished server process [87911]
|
||||
[2026-04-11 12:27:42] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:27:42] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:27:42] [INFO] [uvicorn.error] Started server process [87914]
|
||||
[2026-04-11 12:27:42] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:27:42] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:27:43] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:27:43] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:27:43] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:27:43] [INFO] [uvicorn.error] Finished server process [87914]
|
||||
[2026-04-11 12:27:44] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:27:44] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:27:44] [INFO] [uvicorn.error] Started server process [87922]
|
||||
[2026-04-11 12:27:44] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:27:44] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:27:46] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:27:46] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:27:46] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:27:46] [INFO] [uvicorn.error] Finished server process [87922]
|
||||
[2026-04-11 12:27:46] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:27:46] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:27:46] [INFO] [uvicorn.error] Started server process [87926]
|
||||
[2026-04-11 12:27:46] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:27:46] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:27:52] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:27:52] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:27:52] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:27:52] [INFO] [uvicorn.error] Finished server process [87926]
|
||||
[2026-04-11 12:27:52] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:27:52] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:27:52] [INFO] [uvicorn.error] Started server process [87933]
|
||||
[2026-04-11 12:27:52] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:27:52] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:27:54] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:28:02] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:28:02] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:28:02] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:28:02] [INFO] [uvicorn.error] Finished server process [87933]
|
||||
[2026-04-11 12:28:03] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:28:03] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:28:03] [INFO] [uvicorn.error] Started server process [87948]
|
||||
[2026-04-11 12:28:03] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:28:03] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:28:04] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:28:04] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:28:04] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:28:04] [INFO] [uvicorn.error] Finished server process [87948]
|
||||
[2026-04-11 12:28:05] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:28:05] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:28:05] [INFO] [uvicorn.error] Started server process [87957]
|
||||
[2026-04-11 12:28:05] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:28:05] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:28:06] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:28:07] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:28:07] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:28:07] [INFO] [uvicorn.error] Finished server process [87957]
|
||||
[2026-04-11 12:28:07] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:28:07] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:28:07] [INFO] [uvicorn.error] Started server process [87958]
|
||||
[2026-04-11 12:28:07] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:28:07] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:28:09] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:28:09] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:28:09] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:28:09] [INFO] [uvicorn.error] Finished server process [87958]
|
||||
[2026-04-11 12:28:09] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:28:09] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:28:09] [INFO] [uvicorn.error] Started server process [87960]
|
||||
[2026-04-11 12:28:09] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:28:09] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:28:11] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:28:11] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:28:11] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:28:11] [INFO] [uvicorn.error] Finished server process [87960]
|
||||
[2026-04-11 12:28:12] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:28:12] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:28:12] [INFO] [uvicorn.error] Started server process [87968]
|
||||
[2026-04-11 12:28:12] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:28:12] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:28:13] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:28:13] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:28:13] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:28:13] [INFO] [uvicorn.error] Finished server process [87968]
|
||||
[2026-04-11 12:28:14] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:28:14] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:28:14] [INFO] [uvicorn.error] Started server process [87971]
|
||||
[2026-04-11 12:28:14] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:28:14] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:28:20] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:28:20] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:28:20] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:28:20] [INFO] [uvicorn.error] Finished server process [87971]
|
||||
[2026-04-11 12:28:20] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:28:20] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:28:20] [INFO] [uvicorn.error] Started server process [87989]
|
||||
[2026-04-11 12:28:20] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:28:20] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:28:24] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:28:36] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:28:36] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:28:36] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:28:36] [INFO] [uvicorn.error] Finished server process [87989]
|
||||
[2026-04-11 12:28:37] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:28:37] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:28:37] [INFO] [uvicorn.error] Started server process [88059]
|
||||
[2026-04-11 12:28:37] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:28:37] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:28:37] [WARNING] [uvicorn.error] Invalid HTTP request received.
|
||||
[2026-04-11 12:28:37] [INFO] [uvicorn.access] ::ffff:127.0.0.1:0 - "POST /exa.language_server_pb.LanguageServerService/GetUnleashData HTTP/1.1" 404
|
||||
[2026-04-11 12:28:54] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:29:24] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:29:54] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:30:24] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:30:54] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:31:24] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
[2026-04-11 12:31:30] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:31:30] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:31:30] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:31:30] [INFO] [uvicorn.error] Finished server process [88059]
|
||||
[2026-04-11 12:31:31] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:31:31] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Started server process [88476]
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Finished server process [88476]
|
||||
[2026-04-11 12:31:31] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:31:31] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Started server process [88477]
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:31:31] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:31:32] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:31:32] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:31:32] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:31:32] [INFO] [uvicorn.error] Finished server process [88477]
|
||||
[2026-04-11 12:31:33] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:31:33] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:31:33] [INFO] [uvicorn.error] Started server process [88478]
|
||||
[2026-04-11 12:31:33] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:31:33] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:31:34] [INFO] [uvicorn.error] Shutting down
|
||||
[2026-04-11 12:31:34] [INFO] [uvicorn.error] Waiting for application shutdown.
|
||||
[2026-04-11 12:31:34] [INFO] [uvicorn.error] Application shutdown complete.
|
||||
[2026-04-11 12:31:34] [INFO] [uvicorn.error] Finished server process [88478]
|
||||
[2026-04-11 12:31:35] [INFO] [ainventory] Database tables verified.
|
||||
[2026-04-11 12:31:35] [INFO] [ainventory] TFM aInventory API process started.
|
||||
[2026-04-11 12:31:35] [INFO] [uvicorn.error] Started server process [88490]
|
||||
[2026-04-11 12:31:35] [INFO] [uvicorn.error] Waiting for application startup.
|
||||
[2026-04-11 12:31:35] [INFO] [uvicorn.error] Application startup complete.
|
||||
[2026-04-11 12:31:54] [INFO] [uvicorn.access] ::ffff:192.168.84.140:0 - "GET /items/ HTTP/1.1" 200
|
||||
@@ -3,11 +3,14 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from . import models
|
||||
from .database import engine
|
||||
from .routers import items, operations, users, categories
|
||||
from .logger import log
|
||||
|
||||
# Create the database tables
|
||||
models.Base.metadata.create_all(bind=engine)
|
||||
log.info("Database tables verified.")
|
||||
|
||||
app = FastAPI(title="TFM aInventory API", version="1.1.0")
|
||||
log.info("TFM aInventory API process started.")
|
||||
|
||||
# Setup Cross-Origin for Client interaction (PWA)
|
||||
app.add_middleware(
|
||||
|
||||
@@ -11,7 +11,7 @@ router = APIRouter(prefix="/users", tags=["users"])
|
||||
pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto")
|
||||
|
||||
def get_ldap_config():
|
||||
config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "ldap_config.json")
|
||||
config_path = os.path.join(database.DATA_DIR, "ldap_config.json")
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, "r") as f:
|
||||
return json.load(f)
|
||||
@@ -209,7 +209,7 @@ def get_ldap_settings():
|
||||
|
||||
@router.post("/ldap-config")
|
||||
def update_ldap_settings(config: dict):
|
||||
config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "ldap_config.json")
|
||||
config_path = os.path.join(database.DATA_DIR, "ldap_config.json")
|
||||
with open(config_path, "w") as f:
|
||||
json.dump(config, f)
|
||||
return {"message": "Config saved"}
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
### [2026-04-11 12:45] v1.3.0: Dockerization & Export Script
|
||||
**Purpose:** Upgraded the system architecture to support seamless dual-mode execution (Dockerized or Bare-Metal/Local). Extracted data and logic persistence layers to external volumes (`/data`, `/logs`). Added a dedicated production compiler.
|
||||
**Actions:**
|
||||
- `backend/database.py` and `users.py` now support `DATA_DIR` environment overrides.
|
||||
- Implemented `backend/logger.py` for standard Python rotating logs mapped to `/app/logs`.
|
||||
- Next.js configured for `standalone` output mode to strictly optimize containerized PWA size.
|
||||
- Orchestrated full environment with `docker-compose.yml`, using Caddy for local self-signed HTTPS termination.
|
||||
- Created `export_prod.sh` to extract a clean release bundle without AI/Dev files (using optimized `rsync`).
|
||||
**Modified Files:**
|
||||
- `backend/database.py`
|
||||
- `backend/routers/users.py`
|
||||
- `backend/logger.py` (New)
|
||||
- `backend/main.py`
|
||||
- `frontend/next.config.mjs`
|
||||
- `frontend/Dockerfile` (New)
|
||||
- `backend/Dockerfile` (New)
|
||||
- `docker-compose.yml` (New)
|
||||
- `Caddyfile` (New)
|
||||
- `export_prod.sh` (New)
|
||||
- `VERSION.json`
|
||||
|
||||
### [2026-04-11 12:35] v1.2.9: Automatic IDE Entry Points
|
||||
**Purpose:** Restored specific ID-based entry points (`GEMINI.md` and `CLAUDE.md`) as "Proxy Pointers" to ensure modern AI extensions (Cursor, Windsurf, Gemini IDE) naturally pick them up as system prompt injections.
|
||||
**Modified Files:**
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# AI Session State - HANDOVER
|
||||
# CURRENT AI WORKING SESSION
|
||||
|
||||
**Status**: v1.2.6 Hotfixed & UX Polished. Persistent Gemini Context Created.
|
||||
**Current AI Agent**: Gemini (Antigravity)
|
||||
**Context**:
|
||||
- **Offline Auth**: LDAP users now have local password hash caching (v1.2.4).
|
||||
- **UI Affordance**: Added `ChevronDown` to selects and softened password dots.
|
||||
- **Icon Sync**: Unified iconography (Layers = Categories, Package = Item Types).
|
||||
- **Memory**: Created [GEMINI.md](../GEMINI.md) as the persistent entry point for this agent.
|
||||
- **Version**: Currently at **v1.2.6**. Commit log in `ARCHIVE_LOGS.md`.
|
||||
**Active AI:** Antigravity (Gemini)
|
||||
**Last Updated:** 2026-04-11
|
||||
**Current Version:** v1.3.0
|
||||
**Branch:** dev
|
||||
|
||||
**Next Steps**:
|
||||
1. **Phase 6: Advanced Audit Dashboard**. Current logs exist but could use a dedicated page with filtering.
|
||||
2. Final testing of offline LDAP login (Basement simulation).
|
||||
3. Verification of icon consistency in any new screens.
|
||||
### Current Status
|
||||
Finalized the system refactoring by implementing a complete, portable **Dockerization Architecture**. Introduced the `export_prod.sh` bundle compiler, centralized technical documentation into a Single Source Of Truth (`PROJECT_ARCHITECTURE.md`), and ensured exact reverse compatibility so that bare-metal development using `./start_server.sh` operates identically to previous versions.
|
||||
|
||||
### Technical Context
|
||||
- **Persistence Mapping:** Local database (`inventory.db`) and `ldap_config.json` now persist through the environmental variable `DATA_DIR` (mapped to local `/data` volume).
|
||||
- **Log System:** Created `backend/logger.py` with Python's RotatingFileHandler. Streamed locally into `/logs`.
|
||||
- **Standalone:** Next.js uses `output: standalone` configuration specifically to support the Node alpine dockerfile. Caddy replaces `local-ssl-proxy` only inside the Docker environment.
|
||||
|
||||
### Next Steps / Blockers
|
||||
1. Testing actual AI flow in production mode (using the exported Production Bundle zip).
|
||||
2. Implementing the Advanced Audit log Dashboard inside the UI.
|
||||
|
||||
53
docker-compose.yml
Normal file
53
docker-compose.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: backend/Dockerfile
|
||||
networks:
|
||||
- inventory_net
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./logs:/app/logs
|
||||
environment:
|
||||
- DATA_DIR=/app/data
|
||||
- LOGS_DIR=/app/logs
|
||||
restart: unless-stopped
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: frontend/Dockerfile
|
||||
networks:
|
||||
- inventory_net
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- ./logs:/app/logs
|
||||
# Write Next.js logs to both stdout (docker logs) and file (mapped volume)
|
||||
command: sh -c "mkdir -p /app/logs && node server.js 2>&1 | tee -a /app/logs/frontend.log"
|
||||
restart: unless-stopped
|
||||
|
||||
proxy:
|
||||
image: caddy:alpine
|
||||
networks:
|
||||
- inventory_net
|
||||
ports:
|
||||
- "3002:3002"
|
||||
- "3003:3003"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||
# Persist the internal Caddy certificates so users don't get new certificate warnings constantly
|
||||
- ./data/caddy_data:/data
|
||||
- ./data/caddy_config:/config
|
||||
depends_on:
|
||||
- frontend
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
inventory_net:
|
||||
driver: bridge
|
||||
59
export_prod.sh
Executable file
59
export_prod.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# export_prod.sh - Generates a clean production bundle for distribution
|
||||
|
||||
echo "📦 Preparing TFM aInventory Production Bundle..."
|
||||
|
||||
# Extract version from VERSION.json using grep to avoid macOS python/xcode stubs
|
||||
VERSION=$(grep '"version"' VERSION.json | head -n 1 | awk -F '"' '{print $4}')
|
||||
PROD_DIR="aInventory-PROD-v${VERSION}"
|
||||
|
||||
# Clean previous run if it exists
|
||||
rm -rf "$PROD_DIR"
|
||||
rm -f "${PROD_DIR}.zip"
|
||||
|
||||
mkdir -p "$PROD_DIR"
|
||||
|
||||
echo "📂 Copying application components (excluding dev artifacts)..."
|
||||
# Core application
|
||||
rsync -a --exclude 'node_modules' --exclude '.next' frontend/ "$PROD_DIR/frontend/"
|
||||
rsync -a --exclude '__pycache__' --exclude '.pytest_cache' --exclude '.venv' backend/ "$PROD_DIR/backend/"
|
||||
|
||||
# Orchestration & Scripts
|
||||
cp docker-compose.yml "$PROD_DIR/"
|
||||
cp Caddyfile "$PROD_DIR/"
|
||||
cp start_server.sh "$PROD_DIR/"
|
||||
cp .git_path "$PROD_DIR/" 2>/dev/null || true
|
||||
cp VERSION.json "$PROD_DIR/"
|
||||
|
||||
# Setup persistent volume skeleton
|
||||
mkdir -p "$PROD_DIR/data"
|
||||
mkdir -p "$PROD_DIR/logs"
|
||||
# Place a README in the root of the release
|
||||
cat <<EOF > "$PROD_DIR/README.txt"
|
||||
TFM aInventory - v${VERSION}
|
||||
=============================
|
||||
|
||||
This is a clean production build, free of development or AI-agent constraints.
|
||||
|
||||
TO RUN VIA DOCKER (Recommended):
|
||||
1. Install Docker Desktop or Docker Engine.
|
||||
2. Run: docker-compose build
|
||||
3. Run: docker-compose up -d
|
||||
4. Access via https://<YOUR-IP>:3003 (Accept the internal security warning).
|
||||
|
||||
TO RUN BARE-METAL (No Docker):
|
||||
1. Install Python 3.12+ and Node.js 20+.
|
||||
2. Ensure you have network access for npm installs.
|
||||
3. Run: ./start_server.sh
|
||||
4. Access via https://<YOUR-IP>:3003
|
||||
|
||||
Note: Database and Logs will persist in the /data and /logs directories.
|
||||
EOF
|
||||
|
||||
echo "🗜️ Zipping the final bundle..."
|
||||
zip -r -q "${PROD_DIR}.zip" "$PROD_DIR"
|
||||
|
||||
# Optional: cleanup the directory to leave just the zip
|
||||
# rm -rf "$PROD_DIR"
|
||||
|
||||
echo "✅ SUCCESS: The clean production archive is ready: ${PROD_DIR}.zip"
|
||||
48
frontend/Dockerfile
Normal file
48
frontend/Dockerfile
Normal file
@@ -0,0 +1,48 @@
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
# Step 1: Install dependencies
|
||||
FROM base AS deps
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
# We run this from the frontend folder context
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm ci
|
||||
|
||||
# Step 2: Build the source code
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
# Disable telemetry during build
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
RUN npm run build
|
||||
|
||||
# Step 3: Production image
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV production
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
# Add nextjs user
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
# Copy standalone output
|
||||
COPY --from=builder /app/public ./public
|
||||
# Set proper permissions for the Next.js cache
|
||||
RUN mkdir .next
|
||||
RUN chown nextjs:nodejs .next
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
ENV PORT 3000
|
||||
ENV HOSTNAME "0.0.0.0"
|
||||
|
||||
# Note: The server.js is created by next build from the standalone output
|
||||
CMD ["node", "server.js"]
|
||||
@@ -9,7 +9,7 @@ const withPWA = withPWAInit({
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// Config options here
|
||||
output: "standalone",
|
||||
};
|
||||
|
||||
export default withPWA(nextConfig);
|
||||
|
||||
Reference in New Issue
Block a user