-
-
Notifications
You must be signed in to change notification settings - Fork 141
feat: frontend revamps and connection establishment; enabling org owners to setup DevR AI to their respected platforms #141
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
Merged
chandansgowda
merged 10 commits into
AOSSIE-Org:main
from
smokeyScraper:frontend_revamp
Oct 23, 2025
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
c593d22
feat: add organization integrations database schema
smokeyScraper 3a13e03
feat: add integration API request/response models
smokeyScraper bcac3ea
feat: add integration service for org management
smokeyScraper ee34c94
feat: add integration REST API endpoints
smokeyScraper 83adbc5
feat: add CORS middleware for frontend connection
smokeyScraper 211602a
feat: add backend API client
smokeyScraper ed4cee6
feat: add integration management UI
smokeyScraper 6baa3b1
chore: env and package updates
smokeyScraper cedaad3
refactor-fixes: coderrabbit fixes
smokeyScraper cea55a8
Merge branch 'main' into frontend_revamp
smokeyScraper File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| from fastapi import APIRouter, HTTPException, Depends, status | ||
| from uuid import UUID | ||
| from app.models.integration import ( | ||
| IntegrationCreateRequest, | ||
| IntegrationUpdateRequest, | ||
| IntegrationResponse, | ||
| IntegrationListResponse, | ||
| IntegrationStatusResponse | ||
| ) | ||
| from app.services.integration_service import integration_service, NotFoundError | ||
| from app.core.dependencies import get_current_user | ||
|
|
||
| router = APIRouter() | ||
|
|
||
|
|
||
| @router.post("/", response_model=IntegrationResponse, status_code=status.HTTP_201_CREATED) | ||
| async def create_integration( | ||
| request: IntegrationCreateRequest, | ||
| user_id: UUID = Depends(get_current_user) | ||
| ): | ||
| """Create a new organization integration.""" | ||
| try: | ||
| return await integration_service.create_integration(user_id, request) | ||
| except ValueError as e: | ||
| raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) | ||
| except Exception as e: | ||
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) from e | ||
|
|
||
|
|
||
| @router.get("/", response_model=IntegrationListResponse) | ||
| async def list_integrations(user_id: UUID = Depends(get_current_user)): | ||
| """List all integrations for the current user.""" | ||
| try: | ||
| integrations = await integration_service.get_integrations(user_id) | ||
| return IntegrationListResponse(integrations=integrations, total=len(integrations)) | ||
| except Exception as e: | ||
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) from e | ||
|
|
||
|
|
||
| @router.get("/status/{platform}", response_model=IntegrationStatusResponse) | ||
| async def get_integration_status( | ||
| platform: str, | ||
| user_id: UUID = Depends(get_current_user) | ||
| ): | ||
| """Get the status of a specific platform integration.""" | ||
| try: | ||
| return await integration_service.get_integration_status(user_id, platform) | ||
| except Exception as e: | ||
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) from e | ||
|
|
||
| @router.get("/{integration_id}", response_model=IntegrationResponse) | ||
| async def get_integration( | ||
| integration_id: UUID, | ||
| user_id: UUID = Depends(get_current_user) | ||
| ): | ||
| """Get a specific integration.""" | ||
| try: | ||
| integration = await integration_service.get_integration(user_id, integration_id) | ||
|
|
||
| if not integration: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_404_NOT_FOUND, | ||
| detail="Integration not found" | ||
| ) | ||
|
|
||
| return integration | ||
| except Exception as e: | ||
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) from e | ||
|
|
||
| @router.put("/{integration_id}", response_model=IntegrationResponse) | ||
| async def update_integration( | ||
| integration_id: UUID, | ||
| request: IntegrationUpdateRequest, | ||
| user_id: UUID = Depends(get_current_user) | ||
| ): | ||
| """Update an existing integration.""" | ||
| try: | ||
| return await integration_service.update_integration(user_id, integration_id, request) | ||
| except NotFoundError as e: | ||
| raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) from e | ||
| except HTTPException: | ||
| raise | ||
| except Exception as e: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | ||
| detail=f"Failed to update integration: {str(e)}" | ||
| ) from e | ||
|
|
||
| @router.delete("/{integration_id}", status_code=status.HTTP_204_NO_CONTENT) | ||
| async def delete_integration( | ||
| integration_id: UUID, | ||
| user_id: UUID = Depends(get_current_user) | ||
| ): | ||
| """Delete an integration.""" | ||
| try: | ||
| await integration_service.delete_integration(user_id, integration_id) | ||
| except NotFoundError as e: | ||
| raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) from e | ||
| except HTTPException: | ||
| raise | ||
| except Exception as e: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | ||
| detail=f"Failed to delete integration: {str(e)}" | ||
| ) from e |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,69 @@ | ||
| from fastapi import Request | ||
| from fastapi import Header, HTTPException, status, Request | ||
| from uuid import UUID | ||
| from app.database.supabase.client import get_supabase_client | ||
| import logging | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| if TYPE_CHECKING: | ||
| from main import DevRAIApplication | ||
|
|
||
| async def get_app_instance(request: Request) -> "DevRAIApplication": | ||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def get_app_instance(request: Request) -> "DevRAIApplication": | ||
| """Get the application instance from FastAPI app state.""" | ||
| return request.app.state.app_instance | ||
|
|
||
|
|
||
| async def get_current_user(authorization: str = Header(None)) -> UUID: | ||
| """ | ||
| Dependency to get the application instance from FastAPI's state. | ||
| This avoids circular imports by using dependency injection. | ||
| Get the current authenticated user from the Supabase JWT token. | ||
|
|
||
| Args: | ||
| authorization: The Authorization header containing the Bearer token | ||
|
|
||
| Returns: | ||
| UUID: The user's ID | ||
|
|
||
| Raises: | ||
| HTTPException: If authentication fails | ||
| """ | ||
| return request.app.state.app_instance | ||
| if not authorization: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_401_UNAUTHORIZED, | ||
| detail="Missing authorization header", | ||
| headers={"WWW-Authenticate": "Bearer"}, | ||
| ) | ||
|
|
||
| if not authorization.startswith("Bearer "): | ||
| raise HTTPException( | ||
| status_code=status.HTTP_401_UNAUTHORIZED, | ||
| detail="Invalid authorization header format. Expected 'Bearer <token>'", | ||
| headers={"WWW-Authenticate": "Bearer"}, | ||
| ) | ||
|
|
||
| token = authorization.replace("Bearer ", "") | ||
|
|
||
| try: | ||
| supabase = get_supabase_client() | ||
| # Verify the token and get user | ||
| user_response = supabase.auth.get_user(token) | ||
|
|
||
| if not user_response or not user_response.user: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_401_UNAUTHORIZED, | ||
| detail="Invalid or expired token", | ||
| headers={"WWW-Authenticate": "Bearer"}, | ||
| ) | ||
|
|
||
| return UUID(user_response.user.id) | ||
|
|
||
| except HTTPException: | ||
| raise | ||
| except Exception as e: | ||
| logger.exception("Authentication error") | ||
| raise HTTPException( | ||
| status_code=status.HTTP_401_UNAUTHORIZED, | ||
| detail="Authentication failed", | ||
| headers={"WWW-Authenticate": "Bearer"}, | ||
| ) from e | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| from pydantic import BaseModel, Field | ||
| from typing import Optional, Literal | ||
| from datetime import datetime | ||
| from uuid import UUID | ||
|
|
||
|
|
||
| # Request Models | ||
| class IntegrationCreateRequest(BaseModel): | ||
| """Request model for registering an organization.""" | ||
| platform: Literal["github", "discord", "slack", "discourse"] | ||
| organization_name: str | ||
| organization_link: Optional[str] = None # GitHub org URL, Discord server ID, etc. | ||
| config: Optional[dict] = None # Platform-specific data (discord_guild_id, etc.) | ||
|
|
||
|
|
||
| class IntegrationUpdateRequest(BaseModel): | ||
| """Request model for updating an integration.""" | ||
| organization_name: Optional[str] = None | ||
| organization_link: Optional[str] = None | ||
| is_active: Optional[bool] = None | ||
| config: Optional[dict] = None | ||
|
|
||
|
|
||
| # Response Models | ||
| class IntegrationResponse(BaseModel): | ||
| """Response model for integration data.""" | ||
| id: UUID | ||
| user_id: UUID | ||
| platform: str | ||
| organization_name: str | ||
| is_active: bool | ||
| created_at: datetime | ||
| updated_at: datetime | ||
| config: Optional[dict] = None | ||
| # Note: We never return the actual token in responses | ||
|
|
||
|
|
||
| class IntegrationListResponse(BaseModel): | ||
| """Response model for listing integrations.""" | ||
| integrations: list[IntegrationResponse] | ||
| total: int | ||
|
|
||
|
|
||
| class IntegrationStatusResponse(BaseModel): | ||
| """Response model for checking integration status.""" | ||
| platform: str | ||
| is_connected: bool | ||
| organization_name: Optional[str] = None | ||
| last_updated: Optional[datetime] = None |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.