feat: Enhance Docker setup and application status reporting with build date and container info
This commit is contained in:
+14
-17
@@ -1,41 +1,38 @@
|
|||||||
# Stage 1: Build dependencies
|
# Use multi-stage build for a smaller final image
|
||||||
FROM python:3.13 AS builder
|
FROM python:3.13 AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy requirements first for better layer caching
|
||||||
COPY requirements.txt /app/
|
COPY requirements.txt /app/
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
# Stage 2: Final image
|
# Second stage for the actual runtime
|
||||||
FROM python:3.13.2-slim
|
FROM python:3.13.2-slim
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy installed dependencies
|
# Copy installed packages from builder stage
|
||||||
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
|
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
|
||||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||||
|
|
||||||
# Copy application files correctly
|
# Copy application code
|
||||||
COPY ./app /app/app
|
COPY ./app /app/app
|
||||||
COPY ./frontend /app/frontend
|
|
||||||
COPY ./VERSION /app/VERSION
|
COPY ./VERSION /app/VERSION
|
||||||
COPY ./LICENSE /app/LICENSE
|
COPY ./frontend /app/frontend
|
||||||
|
|
||||||
# Copy build script and generate build date
|
# Create runtime_info directory
|
||||||
COPY ./docker/build-scripts/save-build-date.sh /tmp/
|
RUN mkdir -p /app/runtime_info
|
||||||
RUN mkdir -p /app/docker/build-scripts/ && \
|
|
||||||
cp /tmp/save-build-date.sh /app/docker/build-scripts/ && \
|
|
||||||
chmod +x /tmp/save-build-date.sh && \
|
|
||||||
/tmp/save-build-date.sh
|
|
||||||
|
|
||||||
# Set build date as environment variable
|
# Create necessary directories
|
||||||
#ENV BUILD_DATE=$(cat /app/BUILD_DATE)
|
RUN mkdir -p /workdir
|
||||||
|
|
||||||
# Set Python path explicitly
|
# Set environment variables
|
||||||
ENV PYTHONPATH=/app
|
ENV PYTHONPATH=/app
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
# Expose API port
|
# Expose the port the app runs on
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
|
# Default command
|
||||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
|||||||
+16
-4
@@ -1,18 +1,19 @@
|
|||||||
"""
|
"""
|
||||||
General routes for the application homepage and basic pages.
|
General routes for the application homepage and basic pages.
|
||||||
"""
|
"""
|
||||||
from fastapi import Request, HTTPException
|
from fastapi import Request, HTTPException, Depends
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.views.base import APIRouter, templates, require_login
|
from app.views.base import APIRouter, templates, require_login, get_db
|
||||||
from app.utils.config_validator import get_provider_status, validate_storage_configs
|
from app.utils.config_validator import get_provider_status, validate_storage_configs
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/", include_in_schema=False)
|
@router.get("/", include_in_schema=False)
|
||||||
async def serve_index(request: Request):
|
async def serve_index(request: Request, db: Session = Depends(get_db)):
|
||||||
"""Serve the index/home page."""
|
"""Serve the index/home page."""
|
||||||
# Get provider information from config validator
|
# Get provider information from config validator
|
||||||
providers = get_provider_status()
|
providers = get_provider_status()
|
||||||
@@ -27,9 +28,20 @@ async def serve_index(request: Request):
|
|||||||
's3', 'ftp', 'webdav',
|
's3', 'ftp', 'webdav',
|
||||||
'google_drive', 'onedrive'])
|
'google_drive', 'onedrive'])
|
||||||
|
|
||||||
|
# Query the actual file count from the database
|
||||||
|
processed_files = 0
|
||||||
|
try:
|
||||||
|
# Import the model here to avoid circular imports
|
||||||
|
from app.models import FileRecord
|
||||||
|
processed_files = db.query(FileRecord).count()
|
||||||
|
except Exception as e:
|
||||||
|
# Log error but continue (don't break the page if DB query fails)
|
||||||
|
from app.views.base import logger
|
||||||
|
logger.error(f"Error counting files: {str(e)}")
|
||||||
|
|
||||||
# Create stats object to pass to the template
|
# Create stats object to pass to the template
|
||||||
stats = {
|
stats = {
|
||||||
"processed_files": 0, # Placeholder - would need actual DB query
|
"processed_files": processed_files,
|
||||||
"active_integrations": configured_providers,
|
"active_integrations": configured_providers,
|
||||||
"storage_targets": configured_storage_targets
|
"storage_targets": configured_storage_targets
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-1
@@ -3,6 +3,8 @@ Status and configuration views for the application.
|
|||||||
"""
|
"""
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from app.views.base import APIRouter, templates, require_login, settings
|
from app.views.base import APIRouter, templates, require_login, settings
|
||||||
|
|
||||||
@@ -19,14 +21,78 @@ async def status_dashboard(request: Request):
|
|||||||
# Get provider status
|
# Get provider status
|
||||||
providers = get_provider_status()
|
providers = get_provider_status()
|
||||||
|
|
||||||
|
# Get build date from settings
|
||||||
|
build_date = getattr(settings, 'build_date', 'Unknown')
|
||||||
|
|
||||||
|
# Try to get container information
|
||||||
|
container_info = {}
|
||||||
|
try:
|
||||||
|
# Check for Docker environment
|
||||||
|
if os.path.exists('/.dockerenv'):
|
||||||
|
# We're inside a Docker container
|
||||||
|
container_info['is_docker'] = True
|
||||||
|
|
||||||
|
# Try to get container ID
|
||||||
|
try:
|
||||||
|
with open('/proc/self/cgroup', 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
if 'docker' in line:
|
||||||
|
container_id = line.split('/')[-1].strip()
|
||||||
|
container_info['id'] = container_id[:12] # Short ID format
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
container_info['id'] = 'Unknown'
|
||||||
|
|
||||||
|
# Try to get Git commit SHA from runtime info
|
||||||
|
try:
|
||||||
|
# First check runtime info directory
|
||||||
|
if os.path.exists('/app/runtime_info/GIT_SHA'):
|
||||||
|
with open('/app/runtime_info/GIT_SHA', 'r') as f:
|
||||||
|
git_sha = f.read().strip()
|
||||||
|
# Then try environment variable
|
||||||
|
else:
|
||||||
|
git_sha = os.environ.get('GIT_COMMIT_SHA', '')
|
||||||
|
|
||||||
|
# If still not found, try the original file location
|
||||||
|
if not git_sha and os.path.exists('/.git-commit-sha'):
|
||||||
|
with open('/.git-commit-sha', 'r') as f:
|
||||||
|
git_sha = f.read().strip()
|
||||||
|
|
||||||
|
container_info['git_sha'] = git_sha[:7] if git_sha and git_sha != 'unknown' else 'Unknown'
|
||||||
|
except Exception:
|
||||||
|
container_info['git_sha'] = 'Unknown'
|
||||||
|
|
||||||
|
# Try to get runtime information
|
||||||
|
try:
|
||||||
|
if os.path.exists('/app/runtime_info/RUNTIME_INFO'):
|
||||||
|
with open('/app/runtime_info/RUNTIME_INFO', 'r') as f:
|
||||||
|
container_info['runtime_info'] = f.read().strip()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
container_info['is_docker'] = False
|
||||||
|
|
||||||
|
# If not in Docker, try to get Git info directly
|
||||||
|
try:
|
||||||
|
git_sha = subprocess.check_output(['git', 'rev-parse', 'HEAD'],
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
text=True).strip()[:7]
|
||||||
|
container_info['git_sha'] = git_sha
|
||||||
|
except (subprocess.SubprocessError, FileNotFoundError):
|
||||||
|
container_info['git_sha'] = 'Unknown'
|
||||||
|
except Exception:
|
||||||
|
container_info = {'is_docker': False, 'id': 'Unknown', 'git_sha': 'Unknown'}
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"status_dashboard.html",
|
"status_dashboard.html",
|
||||||
{
|
{
|
||||||
"request": request,
|
"request": request,
|
||||||
"providers": providers,
|
"providers": providers,
|
||||||
"app_version": settings.version,
|
"app_version": settings.version,
|
||||||
|
"build_date": build_date,
|
||||||
"debug_enabled": getattr(settings, 'debug', False),
|
"debug_enabled": getattr(settings, 'debug', False),
|
||||||
"last_check": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
"last_check": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"container_info": container_info
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -10,7 +10,7 @@ services:
|
|||||||
working_dir: /workdir
|
working_dir: /workdir
|
||||||
|
|
||||||
# We'll run uvicorn from the container's /app code
|
# We'll run uvicorn from the container's /app code
|
||||||
command: ["sh", "-c", "cd /app && uvicorn app.main:app --host 0.0.0.0 --port 8000 --proxy-headers"]
|
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"]
|
||||||
|
|
||||||
# Environment variables
|
# Environment variables
|
||||||
environment:
|
environment:
|
||||||
@@ -50,7 +50,7 @@ services:
|
|||||||
- redis
|
- redis
|
||||||
- gotenberg
|
- gotenberg
|
||||||
|
|
||||||
# Mount the shared directory (and optionally your code if you want dev mode)
|
# Mount the shared directory
|
||||||
volumes:
|
volumes:
|
||||||
- /var/docparse/workdir:/workdir
|
- /var/docparse/workdir:/workdir
|
||||||
|
|
||||||
@@ -59,7 +59,6 @@ services:
|
|||||||
container_name: gotenberg
|
container_name: gotenberg
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:alpine
|
image: redis:alpine
|
||||||
container_name: document_redis
|
container_name: document_redis
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Get current date in Month DD, YYYY format (e.g., May 15, 2024)
|
|
||||||
BUILD_DATE=$(date +"%B %d, %Y")
|
|
||||||
|
|
||||||
# Save it to the BUILD_DATE file
|
|
||||||
echo $BUILD_DATE > /app/BUILD_DATE
|
|
||||||
|
|
||||||
# Also set it as an environment variable
|
|
||||||
echo "Setting BUILD_DATE=$BUILD_DATE"
|
|
||||||
export BUILD_DATE
|
|
||||||
@@ -14,10 +14,20 @@
|
|||||||
</p>
|
</p>
|
||||||
<div class="bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 my-4" role="alert">
|
<div class="bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 my-4" role="alert">
|
||||||
<p><strong>App Version:</strong> {{ app_version }}</p>
|
<p><strong>App Version:</strong> {{ app_version }}</p>
|
||||||
|
<p><strong>Build Date:</strong> {{ build_date }}</p>
|
||||||
<p><strong>Debug Mode:</strong> {{ "Enabled" if debug_enabled else "Disabled" }}</p>
|
<p><strong>Debug Mode:</strong> {{ "Enabled" if debug_enabled else "Disabled" }}</p>
|
||||||
{% if last_check %}
|
{% if last_check %}
|
||||||
<p><strong>Last Check:</strong> {{ last_check }}</p>
|
<p><strong>Last Check:</strong> {{ last_check }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if container_info.is_docker %}
|
||||||
|
<p><strong>Container ID:</strong> {{ container_info.id }}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if container_info.git_sha and container_info.git_sha != 'Unknown' %}
|
||||||
|
<p><strong>Git Commit:</strong> {{ container_info.git_sha }}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if container_info.runtime_info %}
|
||||||
|
<p><strong>Container Started:</strong> {{ container_info.runtime_info }}</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user