Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
51 changes: 51 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Docker Build and Test

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Backend
run: |
cd Backend
docker build -t inpactai-backend:test .

- name: Build Frontend
run: |
cd Frontend
docker build -t inpactai-frontend:test .

- name: Start services
run: |
docker compose up -d
sleep 30
Comment on lines +30 to +33

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI workflow will fail because it tries to start services without environment files. The docker compose up command on line 32 requires Backend/.env and Frontend/.env files, but these are not created in the workflow. Add steps to create dummy .env files from .env.example before starting services.

Copilot uses AI. Check for mistakes.

- name: Check backend health
run: |
curl -f http://localhost:8000/ || exit 1

- name: Check frontend health
run: |
curl -f http://localhost:5173/ || exit 1
Comment on lines +35 to +41

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The health checks will fail because the backend requires valid Supabase credentials to start, which won't be available in the CI environment. Consider adding a skip-database or test mode for CI environments, or mock the Supabase connection for health checks.

Copilot uses AI. Check for mistakes.

- name: Show logs on failure
if: failure()
run: |
docker compose logs

- name: Cleanup
if: always()
run: |
docker compose down -v
21 changes: 21 additions & 0 deletions Backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
__pycache__
*.pyc
*.pyo
*.pyd
.Python
*.so
.env
.venv
env/
venv/
ENV/
.git
.gitignore
.pytest_cache
.coverage
htmlcov/
dist/
build/
*.egg-info/
.DS_Store
*.log
12 changes: 12 additions & 0 deletions Backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
user=postgres
password=your_postgres_password
host=your_postgres_host
port=5432
dbname=postgres
GROQ_API_KEY=your_groq_api_key
SUPABASE_URL=your_supabase_url
SUPABASE_KEY=your_supabase_key
GEMINI_API_KEY=your_gemini_api_key
YOUTUBE_API_KEY=your_youtube_api_key
REDIS_HOST=redis
REDIS_PORT=6379
18 changes: 18 additions & 0 deletions Backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM python:3.10-slim

WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpq-dev \
curl \
&& rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
33 changes: 33 additions & 0 deletions Backend/Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM python:3.10-slim AS builder

WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

FROM python:3.10-slim

WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The production Dockerfile doesn't install curl, but the health check in docker-compose.yml requires it. This will cause the health check to fail in production deployments. Add curl to the runtime dependencies or use a different health check method.

Suggested change
libpq5 \
libpq5 \
curl \

Copilot uses AI. Check for mistakes.
&& rm -rf /var/lib/apt/lists/* \
&& groupadd -r appuser && useradd -r -g appuser appuser

COPY --from=builder /root/.local /root/.local
COPY . .

RUN chown -R appuser:appuser /app

USER appuser

ENV PATH=/root/.local/bin:$PATH
Comment on lines +22 to +29

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Package installation path incompatible with non-root user.

The builder stage installs packages to /root/.local (line 22), but the runtime switches to appuser (line 27) who cannot access /root/. The PATH also references /root/.local/bin which will be inaccessible to appuser, causing the application to fail at startup.

Apply this diff to install packages to a shared location:

 FROM python:3.10-slim AS builder
 
 WORKDIR /app
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
     gcc \
     libpq-dev \
     && rm -rf /var/lib/apt/lists/*
 
 COPY requirements.txt .
-RUN pip install --no-cache-dir --user -r requirements.txt
+RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
 
 FROM python:3.10-slim
 
 WORKDIR /app
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
     libpq5 \
     && rm -rf /var/lib/apt/lists/* \
     && groupadd -r appuser && useradd -r -g appuser appuser
 
-COPY --from=builder /root/.local /root/.local
+COPY --from=builder /install /usr/local
 COPY . .
 
 RUN chown -R appuser:appuser /app
 
 USER appuser
 
-ENV PATH=/root/.local/bin:$PATH
-
 EXPOSE 8000
 
 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In Backend/Dockerfile.prod around lines 22 to 29, the runtime copies packages
from /root/.local and then switches to appuser, making /root/.local/bin
inaccessible; change to a shared install location and update PATH: copy or
install build artifacts into a non-root location such as /opt/.local (e.g. COPY
--from=builder /root/.local /opt/.local or update the builder to install
directly to /opt/.local), set ENV PATH=/opt/.local/bin:$PATH, and run chown -R
appuser:appuser /opt/.local so the appuser can access the binaries before
switching USER to appuser.

Comment on lines +22 to +29

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PATH environment variable points to /root/.local/bin but the application runs as the 'appuser' user (non-root). This means the installed packages in /root/.local won't be accessible. The COPY command should use --chown flag and copy to a location accessible by appuser, or the PATH should be updated accordingly.

Copilot uses AI. Check for mistakes.

EXPOSE 8000

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
6 changes: 5 additions & 1 deletion Backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ async def lifespan(app: FastAPI):
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"],
allow_origins=[
"http://localhost:5173",
"http://frontend:5173",
"http://127.0.0.1:5173"
],
Comment on lines +78 to +85

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing proper CORS origin for production. While localhost origins are covered, the production frontend URL is not included in the allow_origins list. When deploying to production with nginx on port 80, requests will be blocked by CORS policy.

Copilot uses AI. Check for mistakes.
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
Expand Down
24 changes: 18 additions & 6 deletions Backend/app/routes/post.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,37 @@
import uuid
from datetime import datetime, timezone

# Load environment variables
load_dotenv()
url: str = os.getenv("SUPABASE_URL")
key: str = os.getenv("SUPABASE_KEY")
supabase: Client = create_client(url, key)

url: str = os.getenv("SUPABASE_URL", "")
key: str = os.getenv("SUPABASE_KEY", "")

if not url or not key or "your-" in url:
print("⚠️ Supabase credentials not configured. Some features will be limited.")

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning message uses emoji that might not render correctly on Windows terminals. Consider using plain text markers like [WARNING] instead of the ⚠ symbol for better cross-platform compatibility.

Suggested change
print("⚠️ Supabase credentials not configured. Some features will be limited.")
print("[WARNING] Supabase credentials not configured. Some features will be limited.")

Copilot uses AI. Check for mistakes.

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Print statement may execute during import.

Copilot uses AI. Check for mistakes.
supabase = None
else:
try:
supabase: Client = create_client(url, key)
except Exception as e:
print(f"❌ Supabase connection failed: {e}")

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Print statement may execute during import.

Copilot uses AI. Check for mistakes.
supabase = None
Comment on lines +23 to +34

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The supabase variable type annotation is lost when set to None. The variable is initially typed as 'Client' but then conditionally set to None, which will cause type checking issues. Consider using 'Optional[Client]' type annotation or handle this more explicitly.

Copilot uses AI. Check for mistakes.

# Define Router
router = APIRouter()

# Helper Functions
def generate_uuid():
return str(uuid.uuid4())

def current_timestamp():
return datetime.now(timezone.utc).isoformat()

# ========== USER ROUTES ==========
def check_supabase():
if not supabase:
raise HTTPException(status_code=503, detail="Database service unavailable. Please configure Supabase credentials.")

@router.post("/users/")
async def create_user(user: UserCreate):
check_supabase()
Comment on lines +45 to +51

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Incomplete Supabase availability check across endpoints.

While create_user correctly calls check_supabase() at Line 51, all other endpoints (lines 69, 94, 119, 143, 167, 188, 210) directly access the supabase client without checking if it's None. This will cause AttributeError exceptions when Supabase credentials are missing or invalid.

Apply this pattern to all endpoints that use the Supabase client:

 @router.get("/users/")
 async def get_users():
+    check_supabase()
     result = supabase.table("users").select("*").execute()
     return result

 @router.post("/audience-insights/")
 async def create_audience_insights(insights: AudienceInsightsCreate):
+    check_supabase()
     insight_id = generate_uuid()
     ...

Alternatively, create a dependency that can be injected into all endpoints:

from fastapi import Depends

def get_supabase() -> Client:
    if not supabase:
        raise HTTPException(status_code=503, detail="Database service unavailable. Please configure Supabase credentials.")
    return supabase

@router.get("/users/")
async def get_users(db: Client = Depends(get_supabase)):
    result = db.table("users").select("*").execute()
    return result
🤖 Prompt for AI Agents
In Backend/app/routes/post.py around lines 45 to 51 (and for all endpoints that
use the supabase client at lines 69, 94, 119, 143, 167, 188, 210), the Supabase
client is used without verifying availability which can raise AttributeError
when credentials are missing; either call the existing check_supabase() at the
start of each endpoint that uses supabase or implement and inject a FastAPI
dependency (e.g., get_supabase using Depends) that performs the same None check
and raises HTTPException(503) if unavailable, then replace direct uses of the
global supabase in those endpoints with the validated client returned by the
dependency.

user_id = generate_uuid()
t = current_timestamp()

Expand Down
175 changes: 175 additions & 0 deletions DOCKER-ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Docker Architecture Diagram

```
┌─────────────────────────────────────────────────────────────────────┐
│ Docker Host Machine │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Docker Network: inpactai-network │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌────────┐│ │
│ │ │ Frontend │ │ Backend │ │ Redis ││ │
│ │ │ Container │ │ Container │ │ Container │
│ │ │ │ │ │ │ ││ │
│ │ │ Node 18-alpine │ │ Python 3.10-slim │ │ Redis 7││ │
│ │ │ Vite Dev Server │◄───┤ FastAPI + uvicorn │ Alpine ││ │
│ │ │ Port: 5173 │ │ Port: 8000 │◄───┤ Port: ││ │
│ │ │ │ │ │ │ 6379 ││ │
│ │ └──────────────────┘ └──────────────────┘ └────────┘│ │
│ │ │ │ │ │ │
│ │ │ Volume Mount │ Volume Mount │ │ │
│ │ │ (Hot Reload) │ (Hot Reload) │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌──────────────┐ ┌─────────────┐ ┌──────────┐│ │
│ │ │ ./Frontend │ │ ./Backend │ │redis_data││ │
│ │ │ /app │ │ /app │ │ Volume ││ │
│ │ └──────────────┘ └─────────────┘ └──────────┘│ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ Port Mappings: │
│ ┌─────────────┬──────────────┬────────────────────────────────┐ │
│ │ Host:5173 │ ──────────► │ frontend:5173 (React + Vite) │ │
│ │ Host:8000 │ ──────────► │ backend:8000 (FastAPI) │ │
│ │ Host:6379 │ ──────────► │ redis:6379 (Cache) │ │
│ └─────────────┴──────────────┴────────────────────────────────┘ │
│ │
│ Environment Files: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Backend/.env → Backend Container │ │
│ │ Frontend/.env → Frontend Container │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘

User Browser
http://localhost:5173 ──► Frontend Container ──► React UI
│ API Calls
http://backend:8000 ──► Backend Container ──► FastAPI
│ Cache/PubSub
redis:6379 ──► Redis Container


Communication Flow:
──────────────────────

1. User accesses http://localhost:5173
└─► Docker routes to Frontend Container

2. Frontend makes API call to /api/*
└─► Vite proxy forwards to http://backend:8000
└─► Docker network resolves 'backend' to Backend Container

3. Backend connects to Redis
└─► Uses REDIS_HOST=redis environment variable
└─► Docker network resolves 'redis' to Redis Container

4. Backend connects to Supabase
└─► Uses credentials from Backend/.env
└─► External connection via internet


Service Dependencies:
─────────────────────

redis (no dependencies)
└─► backend (depends on redis)
└─► frontend (depends on backend)


Health Checks:
──────────────

Redis: redis-cli ping
Backend: curl http://localhost:8000/
Frontend: No health check (depends on backend health)


Volume Mounts:
──────────────

Development:
./Backend:/app (Hot reload for Python)
./Frontend:/app (Hot reload for Vite)
/app/__pycache__ (Excluded)
/app/node_modules (Excluded)

Production:
redis_data:/data (Persistent Redis storage only)


Build Process:
──────────────

Development:
1. Copy package files
2. Install dependencies
3. Copy source code
4. Start dev server with hot reload

Production:
Stage 1: Build
1. Copy package files
2. Install dependencies
3. Copy source code
4. Build optimized bundle

Stage 2: Serve
1. Copy built artifacts
2. Use minimal runtime (nginx for frontend)
3. Serve optimized files


Network Isolation:
──────────────────

Internal Network (inpactai-network):
- frontend ←→ backend (HTTP)
- backend ←→ redis (TCP)

External Access:
- Host machine → All containers (via port mapping)
- Backend → Supabase (via internet)
- Backend → External APIs (via internet)


Security Model:
───────────────

Development:
- Root user in containers (for hot reload)
- Source code mounted as volumes
- Debug logging enabled

Production:
- Non-root user in containers
- No volume mounts (except data)
- Production logging
- Resource limits enforced
- Optimized images
```
Comment on lines +3 to +157

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Specify language for ASCII diagram code block.

The fenced code block starting at line 3 contains ASCII art but lacks a language identifier. While the diagram displays correctly, the markdown linter (MD040) recommends specifying a language.

Apply this fix:

-```
+```ascii
 ┌─────────────────────────────────────────────────────────────────────┐
 │                         Docker Host Machine                          │
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

3-3: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In DOCKER-ARCHITECTURE.md around lines 3 to 157 the large fenced code block
containing the ASCII diagram is missing a language identifier, which triggers
MD040; update the opening fence to include a language (use "ascii") so the block
starts with ```ascii and leave the rest of the block unchanged to satisfy the
linter.


## Quick Command Reference

```bash
Start: docker compose up --build
Stop: docker compose down
Logs: docker compose logs -f
Rebuild: docker compose up --build
Clean: docker compose down -v
```

## Service URLs

| Service | Internal | External |
|---------|----------|----------|
| Frontend | frontend:5173 | http://localhost:5173 |
| Backend | backend:8000 | http://localhost:8000 |
| Redis | redis:6379 | localhost:6379 |
Comment on lines +173 to +175

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Format URLs properly in markdown table.

Lines 173 and 174 contain bare URLs. While they render visually, markdown linters (MD034) recommend proper URL formatting.

Apply this fix:

- | Frontend | frontend:5173 | http://localhost:5173 |
- | Backend | backend:8000 | http://localhost:8000 |
+ | Frontend | frontend:5173 | [localhost:5173](http://localhost:5173) |
+ | Backend | backend:8000 | [localhost:8000](http://localhost:8000) |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| Frontend | frontend:5173 | http://localhost:5173 |
| Backend | backend:8000 | http://localhost:8000 |
| Redis | redis:6379 | localhost:6379 |
| Frontend | frontend:5173 | [localhost:5173](http://localhost:5173) |
| Backend | backend:8000 | [localhost:8000](http://localhost:8000) |
| Redis | redis:6379 | localhost:6379 |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

173-173: Bare URL used

(MD034, no-bare-urls)


174-174: Bare URL used

(MD034, no-bare-urls)

🤖 Prompt for AI Agents
In DOCKER-ARCHITECTURE.md around lines 173 to 175, the table contains bare URLs
which trigger MD034; replace the raw URL text in each table cell with proper
markdown links (for example use [http://localhost:5173](http://localhost:5173),
[http://localhost:8000](http://localhost:8000) and
[localhost:6379](http://localhost:6379) or angle-bracket form <http://...>) so
the table cells contain valid markdown link syntax.

Loading