From f6651bacfcbd283f0706744fac37071b742ae74e Mon Sep 17 00:00:00 2001 From: Floke Date: Wed, 31 Dec 2025 12:58:54 +0000 Subject: [PATCH] chore(gtm): Optimize Docker container with multi-stage build and flat structure --- docker-compose.yml | 2 +- gtm-architect/Dockerfile | 65 +++++++++++++++++++++++++++++----------- gtm-architect/server.cjs | 24 ++++++++++++--- 3 files changed, 69 insertions(+), 22 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f12000c8..376ef1ea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -98,7 +98,7 @@ services: - ./gtm_architect_orchestrator.py:/app/gtm_architect_orchestrator.py - ./market_db_manager.py:/app/market_db_manager.py # Sideloading: Server Logic - - ./gtm-architect/server.cjs:/app/gtm-architect/server.cjs + - ./gtm-architect/server.cjs:/app/server.cjs # Database Persistence - ./gtm_projects.db:/app/gtm_projects.db # Keys diff --git a/gtm-architect/Dockerfile b/gtm-architect/Dockerfile index e99dcac0..d4b809ac 100644 --- a/gtm-architect/Dockerfile +++ b/gtm-architect/Dockerfile @@ -1,28 +1,59 @@ -# Base image for Python -FROM python:3.10-slim +# Stage 1: Build the React frontend +FROM node:20-slim AS frontend-builder -# Set the working directory WORKDIR /app -# Copy shared modules first -COPY helpers.py /app/ -COPY config.py /app/ +# Copy package.json and install dependencies +# We are inside the build context root, so path is gtm-architect/package.json +COPY gtm-architect/package.json ./ +RUN npm install -# Copy application-specific files -COPY gtm_architect_orchestrator.py /app/ -COPY gtm-architect /app/gtm-architect +# Copy the source code +COPY gtm-architect/ . + +# Build the application +RUN npm run build + +# --- + +# Stage 2: Final application image +FROM python:3.11-slim + +WORKDIR /app + +# Install Node.js (minimal runtime) +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl ca-certificates && \ + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \ + apt-get install -y --no-install-recommends nodejs && \ + rm -rf /var/lib/apt/lists/* # Install Python dependencies -RUN pip install --no-cache-dir -r /app/gtm-architect/requirements.txt +# We use the requirements from the subdirectory +COPY gtm-architect/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt -# Install Node.js and npm -RUN apt-get update && apt-get install -y nodejs npm && npm cache clean --force +# Copy the Node.js server script and package.json for runtime deps +COPY gtm-architect/server.cjs . +COPY gtm-architect/package.json . -# Install Node.js dependencies for the frontend bridge -RUN npm install --prefix /app/gtm-architect +# Install only production Node.js dependencies +RUN npm install --omit=dev -# Expose the port the app runs on +# Copy the built frontend from the builder stage +COPY --from=frontend-builder /app/dist ./dist + +# Copy the Python Orchestrator and shared modules +COPY gtm_architect_orchestrator.py . +COPY helpers.py . +COPY config.py . +COPY market_db_manager.py . + +# Copy API Key if available (handled by docker-compose usually, but good for standalone) +# COPY gemini_api_key.txt . + +# Expose port EXPOSE 3005 -# Command to run the application -CMD ["node", "/app/gtm-architect/server.cjs"] +# Start the server +CMD ["node", "server.cjs"] \ No newline at end of file diff --git a/gtm-architect/server.cjs b/gtm-architect/server.cjs index 5c885196..b599f479 100644 --- a/gtm-architect/server.cjs +++ b/gtm-architect/server.cjs @@ -1,15 +1,31 @@ const express = require('express'); const { spawn } = require('child_process'); +const fs = require('fs'); +const path = require('path'); const app = express(); const port = 3005; // Port for the GTM Architect service app.use(express.json({ limit: '50mb' })); -// Middleware to serve static files from the React app -app.use(express.static('.')); +// Determine Environment and Paths +const distPath = path.join(__dirname, 'dist'); +const isProduction = fs.existsSync(distPath); +const staticDir = isProduction ? distPath : __dirname; + +console.log(`[Init] Serving static files from: ${staticDir}`); +app.use(express.static(staticDir)); + +// Determine Python Script Path +// In Docker (optimized), script is in same dir. Locally, it might be one level up. +let pythonScriptPath = path.join(__dirname, 'gtm_architect_orchestrator.py'); +if (!fs.existsSync(pythonScriptPath)) { + pythonScriptPath = path.join(__dirname, '../gtm_architect_orchestrator.py'); +} +console.log(`[Init] Using Python script at: ${pythonScriptPath}`); + function callPythonScript(mode, data, res) { - const pythonProcess = spawn('python3', ['../gtm_architect_orchestrator.py', '--mode', mode]); + const pythonProcess = spawn('python3', [pythonScriptPath, '--mode', mode]); let pythonData = ''; let errorData = ''; @@ -53,7 +69,7 @@ app.post('/api/gtm', (req, res) => { // Serve the main index.html for any other requests to support client-side routing app.get('*', (req, res) => { - res.sendFile(__dirname + '/index.html'); + res.sendFile(path.join(staticDir, 'index.html')); });