Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ env/
.gitignore

# Documentation
*.md
docs/
*.md

# Data and logs
data/
Expand All @@ -58,7 +58,11 @@ tests/
pytest.ini
.pytest_cache/

# Node (if any frontend)
# Frontend - Exclude development artifacts but allow source
frontend/node_modules/
frontend/dist/
frontend/.vite/
frontend/coverage/
node_modules/
package-lock.json
yarn.lock
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: Build and Push Docker Images

on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
Expand Down
6 changes: 1 addition & 5 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ class Settings(BaseSettings):
# server settings
host: str = Field(default="0.0.0.0", description="Host", alias="HOST")
port: int = Field(default=8123, description="Port", alias="PORT")

# Monitoring Settings
enable_monitoring: bool = Field(default=True, description="Enable web-based monitoring interface", alias="ENABLE_MONITORING")
monitoring_path: str = Field(default="/ui", description="Base path for monitoring interface", alias="MONITORING_PATH")


# Vector Search Settings (using Neo4j built-in vector index)
vector_index_name: str = Field(default="knowledge_vectors", description="Neo4j vector index name")
vector_dimension: int = Field(default=384, description="Vector embedding dimension")
Expand Down
85 changes: 42 additions & 43 deletions core/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
Responsible for creating and configuring FastAPI application instance
"""

from fastapi import FastAPI
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import JSONResponse
from fastapi.responses import JSONResponse, FileResponse
from loguru import logger
import os

from config import settings
from .exception_handlers import setup_exception_handlers
Expand Down Expand Up @@ -37,47 +38,46 @@ def create_app() -> FastAPI:

# set routes
setup_routes(app)

# static file service
app.mount("/static", StaticFiles(directory="static"), name="static")

# conditionally enable NiceGUI monitoring interface
if settings.enable_monitoring:
try:
from nicegui import ui
from monitoring.task_monitor import setup_monitoring_routes

# setup NiceGUI monitoring routes
setup_monitoring_routes()

# integrate NiceGUI with FastAPI
ui.run_with(app, mount_path=settings.monitoring_path)

logger.info(f"Monitoring interface enabled at {settings.monitoring_path}/monitor")

except ImportError as e:
logger.warning(f"NiceGUI not available, monitoring interface disabled: {e}")
except Exception as e:
logger.error(f"Failed to setup monitoring interface: {e}")

# Check if static directory exists (contains built React frontend)
static_dir = "static"
if os.path.exists(static_dir) and os.path.exists(os.path.join(static_dir, "index.html")):
# Mount static assets (JS, CSS, images, etc.)
app.mount("/assets", StaticFiles(directory=os.path.join(static_dir, "assets")), name="assets")

# SPA fallback - serve index.html for all non-API routes
@app.get("/{full_path:path}")
async def serve_spa(request: Request, full_path: str):
"""Serve React SPA with fallback to index.html for client-side routing"""
# API routes are handled by routers, so we only get here for unmatched routes
# Check if this looks like an API call that wasn't found
if full_path.startswith("api/"):
return JSONResponse(
status_code=404,
content={"detail": "Not Found"}
)

# For all other routes, serve the React SPA
index_path = os.path.join(static_dir, "index.html")
return FileResponse(index_path)

logger.info("React frontend enabled - serving SPA from /static")
logger.info("Task monitoring available at /tasks")
else:
logger.info("Monitoring interface disabled by configuration")

# root path
@app.get("/")
async def root():
"""root path interface"""
response_data = {
"message": "Welcome to Code Graph Knowledge Service",
"version": settings.app_version,
"docs": "/docs" if settings.debug else "Documentation disabled in production",
"health": "/api/v1/health"
}

# conditionally add monitoring URL
if settings.enable_monitoring:
response_data["task_monitor"] = f"{settings.monitoring_path}/monitor"

return response_data
logger.warning("Static directory not found - React frontend not available")
logger.warning("Run 'cd frontend && npm run build' and copy dist/* to static/")

# Fallback root endpoint when frontend is not built
@app.get("/")
async def root():
"""root path interface"""
return {
"message": "Welcome to Code Graph Knowledge Service",
"version": settings.app_version,
"docs": "/docs" if settings.debug else "Documentation disabled in production",
"health": "/api/v1/health",
"note": "React frontend not built - see logs for instructions"
}

# system information interface
@app.get("/info")
Expand All @@ -89,7 +89,6 @@ async def system_info():
"version": settings.app_version,
"python_version": sys.version,
"debug_mode": settings.debug,
"monitoring_enabled": settings.enable_monitoring,
"services": {
"neo4j": {
"uri": settings.neo4j_uri,
Expand Down
86 changes: 59 additions & 27 deletions docker/Dockerfile.full
Original file line number Diff line number Diff line change
@@ -1,56 +1,88 @@
# syntax=docker/dockerfile:1.7
# Full Docker image - All features (LLM + Embedding required)
FROM python:3.13-slim as builder

########### Builder - Build wheels only ###########
FROM python:3.13-slim AS builder

ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1
PYTHONDONTWRITEBYTECODE=1

RUN apt-get update && apt-get install -y \
git \
curl \
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
curl \
ca-certificates \
git \
&& rm -rf /var/lib/apt/lists/*

RUN pip install uv

WORKDIR /app

# Copy source files needed for package installation
COPY pyproject.toml ./
COPY api ./api
COPY core ./core
COPY services ./services
COPY monitoring ./monitoring
COPY mcp_tools ./mcp_tools
COPY start.py start_mcp.py mcp_server.py config.py main.py ./
# Copy requirements file
COPY requirements.txt ./

# Build all dependencies as wheels (using cache mount)
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --upgrade pip && \
pip wheel --no-deps -r requirements.txt -w /wheelhouse && \
pip wheel -r requirements.txt -w /wheelhouse

########### Frontend Builder - Build React app ###########
FROM node:20-slim AS frontend-builder

# Install the package and its dependencies
RUN uv pip install --system .
WORKDIR /frontend

# ============================================
# Final stage
# ============================================
FROM python:3.13-slim
# Copy package files
COPY frontend/package*.json ./

# Install dependencies with cache mount
RUN --mount=type=cache,target=/root/.npm \
npm ci

# Copy frontend source
COPY frontend/ ./

# Build production bundle
RUN npm run build

########### Runtime - Install wheels + code + frontend ###########
FROM python:3.13-slim AS runtime

ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
DEPLOYMENT_MODE=full

RUN apt-get update && apt-get install -y \
git \
# Install runtime dependencies only
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
git \
&& rm -rf /var/lib/apt/lists/*

# Create non-root user
RUN useradd -m -u 1000 appuser && \
mkdir -p /app /data /repos && \
chown -R appuser:appuser /app /data /repos

WORKDIR /app

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 wheels from builder and install
COPY --from=builder /wheelhouse /wheelhouse
COPY requirements.txt ./

RUN --mount=type=cache,target=/root/.cache/pip \
pip install --upgrade pip && \
pip install --no-index --find-links=/wheelhouse -r requirements.txt && \
rm -rf /wheelhouse

# Copy application code (precise, not everything)
COPY --chown=appuser:appuser api ./api
COPY --chown=appuser:appuser core ./core
COPY --chown=appuser:appuser services ./services
COPY --chown=appuser:appuser mcp_tools ./mcp_tools
COPY --chown=appuser:appuser start.py start_mcp.py mcp_server.py config.py main.py ./

COPY --chown=appuser:appuser . .
# Copy built frontend from frontend-builder stage
COPY --from=frontend-builder --chown=appuser:appuser /frontend/dist ./static

USER appuser

Expand Down
87 changes: 59 additions & 28 deletions docker/Dockerfile.minimal
Original file line number Diff line number Diff line change
@@ -1,57 +1,88 @@
# syntax=docker/dockerfile:1.7
# Minimal Docker image - Code Graph only (No LLM required)
FROM python:3.13-slim as builder

########### Builder - Build wheels only ###########
FROM python:3.13-slim AS builder

ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1
PYTHONDONTWRITEBYTECODE=1

# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
curl \
ca-certificates \
git \
&& rm -rf /var/lib/apt/lists/*

RUN pip install uv

WORKDIR /app

# Copy source files needed for package installation
COPY pyproject.toml ./
COPY api ./api
COPY core ./core
COPY services ./services
COPY monitoring ./monitoring
COPY mcp_tools ./mcp_tools
COPY start.py start_mcp.py mcp_server.py config.py main.py ./
# Copy requirements file
COPY requirements.txt ./

# Build all dependencies as wheels (using cache mount)
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --upgrade pip && \
pip wheel --no-deps -r requirements.txt -w /wheelhouse && \
pip wheel -r requirements.txt -w /wheelhouse

########### Frontend Builder - Build React app ###########
FROM node:20-slim AS frontend-builder

# Install the package and its dependencies
RUN uv pip install --system .
WORKDIR /frontend

# ============================================
# Final stage
# ============================================
FROM python:3.13-slim
# Copy package files
COPY frontend/package*.json ./

# Install dependencies with cache mount
RUN --mount=type=cache,target=/root/.npm \
npm ci

# Copy frontend source
COPY frontend/ ./

# Build production bundle
RUN npm run build

########### Runtime - Install wheels + code + frontend ###########
FROM python:3.13-slim AS runtime

ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
DEPLOYMENT_MODE=minimal

RUN apt-get update && apt-get install -y \
git \
# Install runtime dependencies only
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
git \
&& rm -rf /var/lib/apt/lists/*

# Create non-root user
RUN useradd -m -u 1000 appuser && \
mkdir -p /app /data /repos && \
chown -R appuser:appuser /app /data /repos

WORKDIR /app

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 wheels from builder and install
COPY --from=builder /wheelhouse /wheelhouse
COPY requirements.txt ./

RUN --mount=type=cache,target=/root/.cache/pip \
pip install --upgrade pip && \
pip install --no-index --find-links=/wheelhouse -r requirements.txt && \
rm -rf /wheelhouse

# Copy application code (precise, not everything)
COPY --chown=appuser:appuser api ./api
COPY --chown=appuser:appuser core ./core
COPY --chown=appuser:appuser services ./services
COPY --chown=appuser:appuser mcp_tools ./mcp_tools
COPY --chown=appuser:appuser start.py start_mcp.py mcp_server.py config.py main.py ./

COPY --chown=appuser:appuser . .
# Copy built frontend from frontend-builder stage
COPY --from=frontend-builder --chown=appuser:appuser /frontend/dist ./static

USER appuser

Expand Down
Loading
Loading