Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -20,6 +20,7 @@ logging.basicConfig(level=logging.INFO)
|
|
| 20 |
logger = logging.getLogger(__name__)
|
| 21 |
|
| 22 |
from github_api import GitHubAppAuth, GitHubInsights, TokenManager
|
|
|
|
| 23 |
|
| 24 |
# Load environment variables from .env file
|
| 25 |
load_dotenv()
|
|
@@ -43,8 +44,8 @@ app.add_middleware(
|
|
| 43 |
|
| 44 |
# Load environment variables
|
| 45 |
APP_ID = os.getenv("GITHUB_APP_ID")
|
| 46 |
-
PRIVATE_KEY_PATH = os.getenv("GITHUB_PRIVATE_KEY_PATH")
|
| 47 |
-
PRIVATE_KEY_CONTENT =
|
| 48 |
SUPABASE_URL = os.getenv("SUPABASE_URL")
|
| 49 |
SUPABASE_SECRET_KEY = os.getenv("SUPABASE_SECRET_KEY")
|
| 50 |
DEFAULT_FIREBASE_ID = os.getenv("DEFAULT_FIREBASE_ID", "JDfZoVuGJhdLBEvR7rVCQKBG9r02")
|
|
@@ -83,6 +84,9 @@ else:
|
|
| 83 |
supabase = None
|
| 84 |
print("⚠️ WARNING: Supabase credentials not configured")
|
| 85 |
|
|
|
|
|
|
|
|
|
|
| 86 |
# Simple JSON storage for installations
|
| 87 |
INSTALLATIONS_FILE = "installations.json"
|
| 88 |
|
|
@@ -523,6 +527,64 @@ def register_installation(data: InstallationCallback):
|
|
| 523 |
}
|
| 524 |
|
| 525 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 526 |
@app.get("/installations")
|
| 527 |
def list_installations():
|
| 528 |
"""
|
|
@@ -991,9 +1053,5 @@ def test_sync(data: GitHubSyncRequest):
|
|
| 991 |
}
|
| 992 |
|
| 993 |
|
| 994 |
-
# ============================================================================
|
| 995 |
-
# RUN SERVER
|
| 996 |
-
# ============================================================================
|
| 997 |
-
|
| 998 |
|
| 999 |
|
|
|
|
| 20 |
logger = logging.getLogger(__name__)
|
| 21 |
|
| 22 |
from github_api import GitHubAppAuth, GitHubInsights, TokenManager
|
| 23 |
+
from webhooks import GitHubWebhookHandler
|
| 24 |
|
| 25 |
# Load environment variables from .env file
|
| 26 |
load_dotenv()
|
|
|
|
| 44 |
|
| 45 |
# Load environment variables
|
| 46 |
APP_ID = os.getenv("GITHUB_APP_ID")
|
| 47 |
+
PRIVATE_KEY_PATH = os.getenv("GITHUB_PRIVATE_KEY_PATH", "private-key.pem")
|
| 48 |
+
PRIVATE_KEY_CONTENT = os.getenv("GITHUB_PRIVATE_KEY") # Direct private key content
|
| 49 |
SUPABASE_URL = os.getenv("SUPABASE_URL")
|
| 50 |
SUPABASE_SECRET_KEY = os.getenv("SUPABASE_SECRET_KEY")
|
| 51 |
DEFAULT_FIREBASE_ID = os.getenv("DEFAULT_FIREBASE_ID", "JDfZoVuGJhdLBEvR7rVCQKBG9r02")
|
|
|
|
| 84 |
supabase = None
|
| 85 |
print("⚠️ WARNING: Supabase credentials not configured")
|
| 86 |
|
| 87 |
+
# Initialize webhook handler
|
| 88 |
+
webhook_handler = GitHubWebhookHandler(supabase, GITHUB_WEBHOOK_SECRET) if supabase else None
|
| 89 |
+
|
| 90 |
# Simple JSON storage for installations
|
| 91 |
INSTALLATIONS_FILE = "installations.json"
|
| 92 |
|
|
|
|
| 527 |
}
|
| 528 |
|
| 529 |
|
| 530 |
+
@app.post("/github/webhooks")
|
| 531 |
+
async def handle_github_webhook(request: Request):
|
| 532 |
+
"""
|
| 533 |
+
Handle GitHub webhook events in real-time
|
| 534 |
+
|
| 535 |
+
GitHub sends POST requests here when repository events occur:
|
| 536 |
+
- push: New commits pushed
|
| 537 |
+
- pull_request: PR created/updated/merged
|
| 538 |
+
- issues: Issue created/updated/closed
|
| 539 |
+
- repository: Repository created/deleted/renamed
|
| 540 |
+
|
| 541 |
+
Verify signature header: X-Hub-Signature-256
|
| 542 |
+
Event type header: X-GitHub-Event
|
| 543 |
+
"""
|
| 544 |
+
if not webhook_handler:
|
| 545 |
+
return {
|
| 546 |
+
"success": False,
|
| 547 |
+
"message": "Webhook handler not configured (Supabase not available)"
|
| 548 |
+
}
|
| 549 |
+
|
| 550 |
+
try:
|
| 551 |
+
# Get headers
|
| 552 |
+
signature = request.headers.get("X-Hub-Signature-256")
|
| 553 |
+
event_type = request.headers.get("X-GitHub-Event")
|
| 554 |
+
|
| 555 |
+
if not signature or not event_type:
|
| 556 |
+
return {
|
| 557 |
+
"success": False,
|
| 558 |
+
"message": "Missing required headers (X-Hub-Signature-256, X-GitHub-Event)"
|
| 559 |
+
}
|
| 560 |
+
|
| 561 |
+
# Get raw body for signature verification
|
| 562 |
+
body = await request.body()
|
| 563 |
+
|
| 564 |
+
# Verify signature
|
| 565 |
+
if not webhook_handler.verify_signature(body, signature):
|
| 566 |
+
logger.warning(f"Invalid webhook signature for {event_type}")
|
| 567 |
+
return {
|
| 568 |
+
"success": False,
|
| 569 |
+
"message": "Invalid signature"
|
| 570 |
+
}
|
| 571 |
+
|
| 572 |
+
# Parse payload
|
| 573 |
+
payload = await request.json()
|
| 574 |
+
|
| 575 |
+
# Route to handler
|
| 576 |
+
result = webhook_handler.handle_webhook(event_type, payload)
|
| 577 |
+
|
| 578 |
+
return result
|
| 579 |
+
|
| 580 |
+
except Exception as e:
|
| 581 |
+
logger.error(f"Failed to process webhook: {str(e)}", exc_info=True)
|
| 582 |
+
return {
|
| 583 |
+
"success": False,
|
| 584 |
+
"message": f"Failed to process webhook: {str(e)}"
|
| 585 |
+
}
|
| 586 |
+
|
| 587 |
+
|
| 588 |
@app.get("/installations")
|
| 589 |
def list_installations():
|
| 590 |
"""
|
|
|
|
| 1053 |
}
|
| 1054 |
|
| 1055 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1056 |
|
| 1057 |
|