-
-
Notifications
You must be signed in to change notification settings - Fork 150
eat: add Docker support and implement API-driven collaboration request handling #301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
b52b528
52b9261
23564dd
10453f0
2abdfbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| from typing import Annotated | ||
| from fastapi import APIRouter, Depends, HTTPException | ||
| from sqlalchemy.ext.asyncio import AsyncSession | ||
| from sqlalchemy import update | ||
| from ..db.db import get_db | ||
| from ..models.models import Collaboration | ||
|
|
||
| router = APIRouter(prefix="/collaboration", tags=["Collaboration"]) | ||
|
|
||
| @router.put("/update-status/{collab_id}") | ||
| async def update_collab_status( | ||
| collab_id: str, | ||
| status: str, | ||
| db: Annotated[AsyncSession, Depends(get_db)] | ||
| ): | ||
| """ | ||
| Updates the status of a collaboration request. | ||
| Includes validation to ensure the record exists. | ||
| """ | ||
| if status not in ["accepted", "denied"]: | ||
| raise HTTPException(status_code=400, detail="Invalid status") | ||
|
|
||
| query = update(Collaboration).where(Collaboration.id == collab_id).values(status=status) | ||
| result = await db.execute(query) | ||
| await db.commit() | ||
|
|
||
| # Major fix: Check if the row actually existed | ||
| if result.rowcount == 0: | ||
| raise HTTPException(status_code=404, detail=f"Collaboration {collab_id} not found") | ||
|
|
||
| return {"message": f"Collaboration {status} successfully"} | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,23 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Use a stable Python base image | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FROM python:3.10-slim | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+2
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Container runs as root — add a non-root Running the application as Proposed fix — add a non-root user at the end+# Create a non-root user
+RUN useradd --create-home appuser
+USER appuser
+
# Change to the Backend directory to run the application
WORKDIR /app/Backend🧰 Tools🪛 Trivy (0.69.1)[error] 1-1: Image user should not be 'root' Specify at least 1 USER command in Dockerfile with non-root user as argument Rule: DS-0002 (IaC/Dockerfile) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Set the working directory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WORKDIR /app | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Install system-level dependencies for building Python packages | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN apt-get update && apt-get install -y \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| build-essential \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && rm -rf /var/lib/apt/lists/* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+10
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add Without this flag, Proposed fix-RUN apt-get update && apt-get install -y \
+RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*📝 Committable suggestion
Suggested change
🧰 Tools🪛 Trivy (0.69.1)[error] 8-10: 'apt-get' missing '--no-install-recommends' '--no-install-recommends' flag is missed: 'apt-get update && apt-get install -y build-essential && rm -rf /var/lib/apt/lists/*' Rule: DS-0029 (IaC/Dockerfile) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Copy requirements from the Backend folder and install | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COPY Backend/requirements.txt . | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN pip install --no-cache-dir -r requirements.txt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Copy the entire project into the container | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COPY . . | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Set environment variables (standard for AI platforms) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ENV PYTHONUNBUFFERED=1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Change to the Backend directory to run the application | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WORKDIR /app/Backend | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No The Dockerfile sets up the environment but never declares what to run. Without a Add an appropriate entrypoint, e.g.: Proposed fix # Change to the Backend directory to run the application
WORKDIR /app/Backend
+
+# Run the application
+CMD ["python", "main.py"]Adjust 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Trivy (0.69.1)[error] 1-1: Image user should not be 'root' Specify at least 1 USER command in Dockerfile with non-root user as argument Rule: DS-0002 (IaC/Dockerfile) [error] 8-10: 'apt-get' missing '--no-install-recommends' '--no-install-recommends' flag is missed: 'apt-get update && apt-get install -y build-essential && rm -rf /var/lib/apt/lists/*' Rule: DS-0029 (IaC/Dockerfile) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,10 +6,13 @@ import { Badge } from "../ui/badge"; | |
| import { Separator } from "../ui/separator"; | ||
| import { MessageSquare, CheckCircle, XCircle, Lightbulb, TrendingUp, Users, Star, Mail } from "lucide-react"; | ||
|
|
||
| // Mock data for incoming requests | ||
| // 1. Move BASE_URL to module scope to avoid evaluation on every render | ||
| const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000"; | ||
|
|
||
| // Mock data for incoming requests with string IDs for UUID compatibility | ||
| const mockRequests = [ | ||
| { | ||
| id: 1, | ||
| id: "1", | ||
| sender: { | ||
| name: "TechSavvy", | ||
| avatar: "https://randomuser.me/api/portraits/men/32.jpg", | ||
|
|
@@ -51,7 +54,7 @@ const mockRequests = [ | |
| } | ||
| }, | ||
| { | ||
| id: 2, | ||
| id: "2", | ||
| sender: { | ||
| name: "EcoChic", | ||
| avatar: "https://randomuser.me/api/portraits/women/44.jpg", | ||
|
|
@@ -96,18 +99,52 @@ const mockRequests = [ | |
|
|
||
| const CollabRequests: React.FC = () => { | ||
| const [requests, setRequests] = useState(mockRequests); | ||
| // 2. Add loadingId state to track and disable in-flight requests | ||
| const [loadingId, setLoadingId] = useState<string | null>(null); | ||
|
|
||
| const handleAccept = (id: number) => { | ||
| setRequests(prev => prev.filter(req => req.id !== id)); | ||
| // TODO: Integrate with backend to accept request | ||
| const handleAccept = async (id: string) => { | ||
| if (loadingId) return; // Prevent concurrent requests | ||
| setLoadingId(id); | ||
| try { | ||
| const response = await fetch(`${BASE_URL}/collaboration/update-status/${id}?status=accepted`, { | ||
| method: 'PUT', | ||
| }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -type f -name "*.py" | head -20Repository: AOSSIE-Org/InPactAI Length of output: 682 🏁 Script executed: # Look for the backend endpoint implementation
rg -n "update-status|update_status" -t py -B 5 -A 10Repository: AOSSIE-Org/InPactAI Length of output: 1150 🏁 Script executed: # Search for authentication patterns in the codebase
rg -n "Depends\(.*get_current_user\)|HTTPBearer|oauth2_scheme|@.*auth" -t py -B 2 -A 2Repository: AOSSIE-Org/InPactAI Length of output: 316 🏁 Script executed: cat -n Backend/app/routes/collaboration.py | head -50Repository: AOSSIE-Org/InPactAI Length of output: 1385 🏁 Script executed: # Check other routes to see authentication patterns
rg -n "get_current_user|HTTPBearer|oauth2_scheme" Backend/app/routes/ -B 2 -A 2Repository: AOSSIE-Org/InPactAI Length of output: 45 🏁 Script executed: # Check main.py for middleware or global auth setup
cat -n Backend/app/main.pyRepository: AOSSIE-Org/InPactAI Length of output: 2425 🏁 Script executed: # Check full auth.py to understand authentication mechanisms
cat -n Backend/app/routes/auth.pyRepository: AOSSIE-Org/InPactAI Length of output: 251 🏁 Script executed: # Check other routes that modify data (POST, PUT, DELETE) for auth patterns
rg -n "@router\.(post|put|delete)" Backend/app/routes/ -A 5Repository: AOSSIE-Org/InPactAI Length of output: 4408 Backend endpoint currently lacks authentication — critical security vulnerability The The endpoint must implement authentication (e.g., Also applies to: 122-124 🤖 Prompt for AI Agents |
||
| if (response.ok) { | ||
| setRequests(prev => prev.filter(req => req.id !== id)); | ||
| } else { | ||
| const err = await response.json().catch(() => ({})); | ||
| alert(`Failed to accept: ${err.detail || "Server error"}`); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } catch (error) { | ||
| console.error("Failed to accept request:", error); | ||
| } finally { | ||
| setLoadingId(null); | ||
| } | ||
| }; | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| const handleDeny = (id: number) => { | ||
| setRequests(prev => prev.filter(req => req.id !== id)); | ||
| // TODO: Integrate with backend to deny request | ||
| const handleDeny = async (id: string) => { | ||
| if (loadingId) return; // Prevent concurrent requests | ||
| setLoadingId(id); | ||
| try { | ||
| const response = await fetch(`${BASE_URL}/collaboration/update-status/${id}?status=denied`, { | ||
| method: 'PUT', | ||
| }); | ||
| if (response.ok) { | ||
| setRequests(prev => prev.filter(req => req.id !== id)); | ||
| } else { | ||
| // 3. Extract real server error instead of hardcoded string | ||
| const err = await response.json().catch(() => ({})); | ||
| alert(`Failed to deny: ${err.detail || "Server error"}`); | ||
| } | ||
| } catch (error) { | ||
| console.error("Failed to deny request:", error); | ||
| } finally { | ||
| setLoadingId(null); | ||
| } | ||
| }; | ||
|
|
||
| const handleMessage = (id: number) => { | ||
| // 4. Mark unused parameter with underscore to silence linter | ||
| const handleMessage = (_id: string) => { | ||
| // TODO: Open message modal or redirect to chat | ||
| alert("Open chat with sender (not implemented)"); | ||
| }; | ||
|
|
@@ -140,7 +177,6 @@ const CollabRequests: React.FC = () => { | |
| <div className="mb-3"> | ||
| <span className="font-medium text-gray-800">Request:</span> {req.summary} | ||
| </div> | ||
| {/* Initial Collaboration Proposal Section */} | ||
| <div className="bg-purple-50 border border-purple-100 rounded-lg p-3 mb-4"> | ||
| <div className="flex items-center gap-2 mb-1 text-purple-700 font-semibold text-sm"> | ||
| <span role="img" aria-label="proposal">📝</span> Initial Collaboration Proposal | ||
|
|
@@ -154,21 +190,18 @@ const CollabRequests: React.FC = () => { | |
| </ul> | ||
| </div> | ||
| <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4"> | ||
| {/* AI Advantages */} | ||
| <div className="bg-blue-50 border border-blue-100 rounded-lg p-3"> | ||
| <div className="flex items-center gap-2 mb-1 text-blue-700 font-semibold text-sm"><Lightbulb className="h-4 w-4" /> Advantages</div> | ||
| <ul className="list-disc ml-5 text-xs text-blue-900"> | ||
| {req.ai.advantages.map((adv, i) => <li key={i}>{adv}</li>)} | ||
| </ul> | ||
| </div> | ||
| {/* AI Ideas */} | ||
| <div className="bg-green-50 border border-green-100 rounded-lg p-3"> | ||
| <div className="flex items-center gap-2 mb-1 text-green-700 font-semibold text-sm"><Lightbulb className="h-4 w-4" /> Collaboration Ideas</div> | ||
| <ul className="list-disc ml-5 text-xs text-green-900"> | ||
| {req.ai.ideas.map((idea, i) => <li key={i}>{idea}</li>)} | ||
| </ul> | ||
| </div> | ||
| {/* AI Recommendations */} | ||
| <div className="bg-yellow-50 border border-yellow-100 rounded-lg p-3"> | ||
| <div className="flex items-center gap-2 mb-1 text-yellow-700 font-semibold text-sm"><Lightbulb className="h-4 w-4" /> Recommendations</div> | ||
| <ul className="list-disc ml-5 text-xs text-yellow-900"> | ||
|
|
@@ -183,10 +216,23 @@ const CollabRequests: React.FC = () => { | |
| <span>Avg Views: <b>{req.stats.avgViews}</b></span> | ||
| </div> | ||
| <div className="flex gap-3 mt-4"> | ||
| <Button size="sm" className="flex items-center gap-1 bg-green-100 text-green-800 hover:bg-green-200 border border-green-200" onClick={() => handleAccept(req.id)} aria-label="Accept collaboration request"> | ||
| {/* 5. Disable buttons when a request is in progress */} | ||
| <Button | ||
| size="sm" | ||
| className="flex items-center gap-1 bg-green-100 text-green-800 hover:bg-green-200 border border-green-200" | ||
| onClick={() => handleAccept(req.id)} | ||
| disabled={loadingId === req.id} | ||
| aria-label="Accept collaboration request" | ||
| > | ||
| <CheckCircle className="h-4 w-4" /> Accept | ||
| </Button> | ||
| <Button size="sm" className="flex items-center gap-1 bg-red-100 text-red-800 hover:bg-red-200 border border-red-200" onClick={() => handleDeny(req.id)} aria-label="Deny collaboration request"> | ||
| <Button | ||
| size="sm" | ||
| className="flex items-center gap-1 bg-red-100 text-red-800 hover:bg-red-200 border border-red-200" | ||
| onClick={() => handleDeny(req.id)} | ||
| disabled={loadingId === req.id} | ||
| aria-label="Deny collaboration request" | ||
| > | ||
| <XCircle className="h-4 w-4" /> Deny | ||
| </Button> | ||
| <Button size="sm" variant="outline" className="flex items-center gap-1" onClick={() => handleMessage(req.id)} aria-label="Message sender"> | ||
|
|
@@ -204,4 +250,4 @@ const CollabRequests: React.FC = () => { | |
| ); | ||
| }; | ||
|
|
||
| export default CollabRequests; | ||
| export default CollabRequests; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Silent no-op when
collab_iddoesn't exist — returns 200 instead of 404db.execute(update(...))succeeds even when zero rows match. The response always reports success. Checkresult.rowcountand raise a 404 if nothing was updated.🐛 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents