Skip to content

Commit a2ce982

Browse files
authored
Merge pull request #10 from SecrinLabs/feature/github-app-for-secrin
Implement GitHub App integration with webhook handling and signature verification
2 parents 771df3f + 8174cc9 commit a2ce982

23 files changed

Lines changed: 581 additions & 16 deletions

File tree

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ TRACING_ENDPOINT=
105105
API_KEY=
106106
ALLOWED_HOSTS=["*"]
107107

108+
# =============================================================================
109+
# App Store Configuration
110+
# =============================================================================
111+
112+
GITHUB_WEBHOOK_SECRET=
113+
108114
# =============================================================================
109115
# Feature Flags (Environment-level overrides)
110116
# Use these to override default feature flag behavior

apps/api/main.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from fastapi import FastAPI, Request
22
from fastapi.responses import JSONResponse
3+
from fastapi.middleware.cors import CORSMiddleware
34
from packages.config.settings import Settings
45
from apps.api.utils import APIResponse, APIException
56
from apps.api.routes import api_router
@@ -12,6 +13,15 @@
1213
version="0.1.0"
1314
)
1415

16+
# Configure CORS
17+
app.add_middleware(
18+
CORSMiddleware,
19+
allow_origins=settings.API_CORS_ORIGINS,
20+
allow_credentials=True,
21+
allow_methods=["*"],
22+
allow_headers=["*"],
23+
)
24+
1525

1626
# Global exception handler
1727
@app.exception_handler(APIException)

apps/api/routes/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from fastapi import APIRouter
22
from apps.api.routes.v1 import v1_router
33

4-
api_router = APIRouter()
4+
api_router = APIRouter(prefix="/api")
55

66
# Include versioned routes
77
api_router.include_router(v1_router)

apps/api/routes/v1/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from fastapi import APIRouter
2-
from apps.api.routes.v1 import ask, health, connect
2+
from apps.api.routes.v1 import ask, health, connect, integrations
33

44
v1_router = APIRouter(prefix="/v1")
55

66
# Include all v1 route modules
77
v1_router.include_router(health.router)
88
v1_router.include_router(connect.router)
9-
v1_router.include_router(ask.router)
9+
v1_router.include_router(ask.router)
10+
v1_router.include_router(integrations.router)

apps/api/routes/v1/connect.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from fastapi import APIRouter
1+
from fastapi import APIRouter, BackgroundTasks
22
from apps.api.utils import APIResponse
33
from apps.api.routes.v1.schemas.connect import (
44
GitHubRepoConnect
@@ -10,7 +10,7 @@
1010

1111

1212
@router.post("/github")
13-
async def connect_github_repo(repo_data: GitHubRepoConnect):
13+
async def connect_github_repo(repo_data: GitHubRepoConnect, background_tasks: BackgroundTasks):
1414
repo_path = repo_data.repo_url.replace("https://github.com/", "").replace("http://github.com/", "").strip("/")
1515
parts = repo_path.split("/")
1616

@@ -21,9 +21,7 @@ async def connect_github_repo(repo_data: GitHubRepoConnect):
2121
owner = "unknown"
2222
repo = "unknown"
2323

24-
summary = process_repository(
25-
repo_data.repo_url,
26-
)
24+
background_tasks.add_task(process_repository, repo_data.repo_url)
2725

2826
return APIResponse.success(
2927
data={

apps/api/routes/v1/integrations.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from fastapi import APIRouter, Request, Header, BackgroundTasks
2+
from packages.config.settings import Settings
3+
from packages.app_store.github.utils import verify_github_signature, SignatureVerificationError
4+
from packages.app_store.github.webhook import handle_github_event, run_ingestion
5+
from apps.api.utils.response import APIResponse
6+
from apps.api.utils.exceptions import BadRequestException, UnauthorizedException
7+
import logging
8+
9+
router = APIRouter(prefix="/integrations", tags=["Integrations"])
10+
settings = Settings()
11+
logger = logging.getLogger(__name__)
12+
13+
@router.post("/github/webhook")
14+
async def github_webhook(
15+
request: Request,
16+
background_tasks: BackgroundTasks,
17+
x_github_event: str = Header(None)
18+
):
19+
"""
20+
Handle GitHub Webhooks.
21+
"""
22+
if not x_github_event:
23+
raise BadRequestException(message="Missing X-GitHub-Event header")
24+
25+
# Always verify signature in production
26+
try:
27+
await verify_github_signature(request, settings.GITHUB_WEBHOOK_SECRET)
28+
except SignatureVerificationError as e:
29+
logger.warning(f"GitHub signature verification failed: {str(e)}")
30+
raise UnauthorizedException(message=str(e))
31+
32+
payload = await request.json()
33+
34+
result = handle_github_event(x_github_event, payload)
35+
36+
if result.get("status") == "triggered" and result.get("task") == "ingest":
37+
repo_url = result.get("repo_url")
38+
branch = result.get("branch")
39+
if repo_url:
40+
background_tasks.add_task(run_ingestion, repo_url, branch)
41+
return APIResponse.success(
42+
message=f"Ingestion triggered for {repo_url} on {branch}",
43+
data={"status": "accepted", "repo_url": repo_url, "branch": branch}
44+
)
45+
46+
return APIResponse.success(
47+
message="Webhook processed",
48+
data=result
49+
)

apps/web/.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Neo4j environment variables for graph visualization
2+
NEXT_PUBLIC_NEO4J_URI=neo4j://127.0.0.1:7687
3+
NEXT_PUBLIC_NEO4J_USER=neo4j
4+
NEXT_PUBLIC_NEO4J_PASS=10514912
5+
NEXT_PUBLIC_NEO4J_DB=demo

apps/web/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ yarn-error.log*
3131
.pnpm-debug.log*
3232

3333
# env files (can opt-in for committing if needed)
34-
.env*
34+
.env
3535

3636
# vercel
3737
.vercel

apps/web/app/integrations/page.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"use client";
2+
3+
import {
4+
Card,
5+
CardContent,
6+
CardDescription,
7+
CardFooter,
8+
CardHeader,
9+
CardTitle,
10+
} from "@/components/ui/card";
11+
import { Button } from "@/components/ui/button";
12+
import { GitHubConnectCard } from "@/components/integrations/github/github-connect-card";
13+
14+
export default function IntegrationsPage() {
15+
return (
16+
<div className="container mx-auto py-10 px-4">
17+
<div className="mb-8">
18+
<h1 className="text-3xl font-bold tracking-tight">App Integrations</h1>
19+
<p className="text-muted-foreground mt-2">
20+
Connect your favorite tools to Secrin to enhance your knowledge graph.
21+
</p>
22+
</div>
23+
24+
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
25+
{/* GitHub Integration */}
26+
<GitHubConnectCard />
27+
28+
{/* Placeholder for future integrations */}
29+
<Card className="flex flex-col opacity-60">
30+
<CardHeader>
31+
<CardTitle>Jira</CardTitle>
32+
<CardDescription>Coming Soon</CardDescription>
33+
</CardHeader>
34+
<CardContent>
35+
<p className="text-sm text-muted-foreground">
36+
Connect Jira to link issues with code changes.
37+
</p>
38+
</CardContent>
39+
<CardFooter>
40+
<Button disabled variant="outline" className="w-full">
41+
Coming Soon
42+
</Button>
43+
</CardFooter>
44+
</Card>
45+
46+
<Card className="flex flex-col opacity-60">
47+
<CardHeader>
48+
<CardTitle>Slack</CardTitle>
49+
<CardDescription>Coming Soon</CardDescription>
50+
</CardHeader>
51+
<CardContent>
52+
<p className="text-sm text-muted-foreground">
53+
Get notifications and query the knowledge graph from Slack.
54+
</p>
55+
</CardContent>
56+
<CardFooter>
57+
<Button disabled variant="outline" className="w-full">
58+
Coming Soon
59+
</Button>
60+
</CardFooter>
61+
</Card>
62+
</div>
63+
</div>
64+
);
65+
}

apps/web/app/page.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
import Neo4jGraph from "@/components/Neo4jGraph";
2-
31
export default function Home() {
4-
return (
5-
<div>
6-
<Neo4jGraph />
7-
</div>
8-
);
2+
return <div>{/* Dashboard coming soon... */}</div>;
93
}

0 commit comments

Comments
 (0)