branch:
README.md
4566 bytesRaw
# GitHub Webhook Dashboard
A real-time GitHub repository activity monitor built with Cloudflare Agents. This example demonstrates how to handle webhooks with Agents, verify signatures, store events in SQLite, and stream updates to connected clients.
## Features
- **Webhook Handling** - Receive and process GitHub webhooks
- **Signature Verification** - HMAC-SHA256 verification of webhook payloads
- **Agent-per-Repository** - Each repo gets its own isolated agent instance
- **Real-time Updates** - WebSocket connection streams events as they arrive
- **Event History** - Events stored in SQLite for persistence
- **Beautiful Dashboard** - Dark-themed UI with live event feed
## Quick Start
### 1. Install dependencies
```bash
npm install
```
### 2. Configure webhook secret
Copy `.dev.vars.example` to `.dev.vars` and set your webhook secret:
```bash
cp .dev.vars.example .dev.vars
```
Edit `.dev.vars`:
```
GITHUB_WEBHOOK_SECRET=your-secret-here
```
### 3. Start the development server
```bash
npm start
```
### 4. Expose your local server (for testing)
Since GitHub needs to reach your webhook endpoint, use a tool like ngrok:
```bash
ngrok http 5173
```
Copy the ngrok URL (e.g., `https://abc123.ngrok.io`).
### 5. Configure GitHub Webhook
1. Go to your GitHub repository → **Settings** → **Webhooks**
2. Click **Add webhook**
3. Configure:
- **Payload URL**: `https://your-ngrok-url.ngrok.io/webhooks/github/owner/repo`
- **Content type**: `application/json`
- **Secret**: Same value as `GITHUB_WEBHOOK_SECRET`
- **Events**: Select which events to receive (or "Send me everything")
4. Click **Add webhook**
### 6. Connect to your repo
Open `http://localhost:5173` in your browser, enter your repository name (e.g., `cloudflare/agents`), and click Connect.
## How It Works
### Architecture
```
GitHub → POST /webhooks/github/owner/repo → Worker → RepoAgent (Durable Object)
↓
Browser ← WebSocket ← Agent broadcasts state updates ←─────┘
```
### Key Patterns Demonstrated
1. **Webhook Routing**
```typescript
// Route webhooks to the right agent based on repository
const agentName = sanitizeRepoName(payload.repository.full_name);
const agent = await getAgentByName(env.RepoAgent, agentName);
return agent.fetch(request);
```
2. **Signature Verification**
```typescript
// Verify GitHub's HMAC-SHA256 signature
const key = await crypto.subtle.importKey(
"raw",
secret,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", key, payload);
```
3. **Event Storage in SQLite**
```typescript
this.sql`INSERT INTO events (id, type, title, ...) VALUES (...)`;
```
4. **Real-time State Broadcasting**
- When a webhook arrives, the agent updates its state
- Connected clients receive the update via WebSocket
## Supported Events
| Event Type | Description |
| --------------- | ----------------------------------- |
| `push` | Commits pushed to a branch |
| `pull_request` | PR opened, closed, merged, etc. |
| `issues` | Issue opened, closed, labeled, etc. |
| `issue_comment` | Comment on an issue or PR |
| `star` | Repository starred/unstarred |
| `fork` | Repository forked |
| `release` | Release published |
| `ping` | Webhook configured |
## Deployment
```bash
npm run deploy
```
After deploying:
1. Set the webhook secret in Cloudflare:
```bash
wrangler secret put GITHUB_WEBHOOK_SECRET
```
2. Update your GitHub webhook URL to your deployed worker URL
## Extending This Example
Ideas for enhancements:
- **AI PR Summaries** - Use OpenAI to summarize PR diffs
- **Slack Notifications** - Forward important events to Slack
- **Multi-Repo Dashboard** - Monitor all your repos in one view
- **Custom Alerts** - Schedule reminders for stale PRs
- **Webhook Replay** - Re-send events for testing
## Project Structure
```
examples/github-webhook/
├── src/
│ ├── server.ts # RepoAgent + webhook routing
│ ├── client.tsx # React dashboard
│ ├── github-types.ts # TypeScript types for GitHub payloads
│ └── styles.css # Dashboard styles
├── public/
│ └── normalize.css
├── index.html
├── wrangler.jsonc
└── package.json
```