# StackScope Worker API Documentation

Complete API documentation for the StackScope Worker endpoints with examples and testing utilities.

## 📋 Endpoints Overview

| Endpoint | Method | Purpose | Content-Type |
|----------|---------|---------|--------------|
| `/health` | GET | Health check | application/json |
| `/webhook/browser` | POST | Browser logs | text/plain (NDJSON) |
| `/webhook/github` | POST | GitHub events | form-urlencoded or json |
| `/webhook/api` | POST | API worker logs | application/json |

## 🏥 Health Check

### GET `/health`

Simple health check endpoint.

**Response:**
```json
{
  "status": "ok"
}
```

**Example:**
```bash
curl https://your-worker.workers.dev/health
```

## 🌐 Browser Logs Endpoint

### POST `/webhook/browser`

Receives log data from StackScope browser SDK.

**Content-Type:** `text/plain`
**Format:** NDJSON (Newline Delimited JSON)

**Expected Headers:**
- `Content-Type: text/plain`
- `Authorization: Bearer {api_key}` (optional)

**Request Body Example:**
```
{"level":"info","msg":"User login","ts":"2024-01-15T10:30:00.000Z","meta":{"userId":123}}
{"level":"error","msg":"API failed","ts":"2024-01-15T10:30:01.000Z","meta":{"endpoint":"/users","status":500}}
```

**Success Response:**
- Status: `200 OK`
- Body: `"OK"`

**Error Responses:**
- `400 Bad Request` - Invalid JSON in log line
- `500 Internal Server Error` - Processing error

**Test Command:**
```bash
curl -X POST https://your-worker.workers.dev/webhook/browser \
  -H "Content-Type: text/plain" \
  -d '{"level":"info","msg":"Test log","ts":"2024-01-01T00:00:00.000Z","meta":{"test":true}}'
```

## 🔗 API Logs Endpoint

### POST `/webhook/api`

Receives log data from Cloudflare Worker API via Wrangler tail streaming.

**Content-Type:** `application/json`
**Format:** Single Wrangler tail log entry JSON object

**Expected Headers:**
- `Content-Type: application/json`
- `X-Source: {source_name}` (optional, default: "api")

**Request Body Example:**
```json
{
  "eventTimestamp": 1705320600000,
  "outcome": "ok",
  "scriptName": "my-api-worker",
  "event": {
    "request": {
      "url": "https://my-api.workers.dev/users",
      "method": "GET",
      "headers": {
        "user-agent": "Mozilla/5.0...",
        "accept": "application/json"
      },
      "cf": {
        "country": "US",
        "colo": "LAX"
      }
    },
    "response": {
      "status": 200
    }
  },
  "logs": [
    {
      "message": "Processing user request",
      "timestamp": 1705320600100,
      "level": "log"
    }
  ],
  "exceptions": [],
  "diagnosticsChannelEvents": []
}
```

**Success Response:**
- Status: `200 OK`
- Body: `"API log processed successfully"`

**Error Responses:**
- `400 Bad Request` - Invalid JSON or wrong Content-Type
- `500 Internal Server Error` - Processing error

**Test Command:**
```bash
curl -X POST https://your-worker.workers.dev/webhook/api \
  -H "Content-Type: application/json" \
  -H "X-Source: test-api" \
  -d '{
    "eventTimestamp": 1705320600000,
    "outcome": "ok", 
    "scriptName": "test-worker",
    "event": {
      "request": {
        "url": "https://test.workers.dev/api",
        "method": "GET"
      },
      "response": {
        "status": 200
      }
    },
    "logs": [{"message": "Test log", "level": "log"}],
    "exceptions": [],
    "diagnosticsChannelEvents": []
  }'
```

**Automated Streaming:**

The easiest way to send API logs is using the generated `stream-api-logs.sh` script:

```bash
# Stream logs from your API worker
./stream-api-logs.sh ../my-api-worker production

# Or use manual streaming
cd ../my-api-worker
wrangler tail --env production --format json | \
while read -r line; do
  echo "$line" | curl -X POST https://your-stackscope-worker.workers.dev/webhook/api \
    -H "Content-Type: application/json" \
    -H "X-Source: api-stream" \
    -d @-
done
```

## 🐙 GitHub Webhook Endpoint  

### POST `/webhook/github`

Receives GitHub webhook events.

**Content-Type:** `application/x-www-form-urlencoded` OR `application/json`

**Required Headers:**
- `X-GitHub-Event: {event_type}` - GitHub event type (push, pull_request, etc.)
- `X-GitHub-Delivery: {unique_id}` - Unique delivery identifier
- `X-Hub-Signature-256: {signature}` - HMAC signature (if secret configured)

### Form-Encoded Format (GitHub Default)

**Content-Type:** `application/x-www-form-urlencoded`

**Request Body:**
```
payload={"pusher":{"name":"username"},"repository":{"full_name":"owner/repo"},"action":"opened"}
```

**Example:**
```bash
curl -X POST https://your-worker.workers.dev/webhook/github \
  -H "X-GitHub-Event: push" \
  -H "X-GitHub-Delivery: 12345-67890-abcdef" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d 'payload={"pusher":{"name":"testuser"},"repository":{"full_name":"test/repo"},"ref":"refs/heads/main"}'
```

### JSON Format (Alternative)

**Content-Type:** `application/json`

**Request Body:**
```json
{
  "pusher": {
    "name": "username",
    "login": "username"
  },
  "repository": {
    "full_name": "owner/repo",
    "name": "repo"
  },
  "action": "opened"
}
```

**Example:**
```bash
curl -X POST https://your-worker.workers.dev/webhook/github \
  -H "X-GitHub-Event: pull_request" \
  -H "X-GitHub-Delivery: 12345-67890-abcdef" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "opened",
    "pusher": {"name": "testuser"},
    "repository": {"full_name": "test/repo"}
  }'
```

### Error Responses

**400 Bad Request - Missing Headers:**
```json
{
  "error": "Missing X-GitHub-Event header"
}
```

```json
{
  "error": "Missing X-GitHub-Delivery header"  
}
```

**400 Bad Request - Invalid Payload:**
```json
{
  "error": "Missing payload field in form data"
}
```

**401 Unauthorized - Invalid Signature:**
```json
{
  "error": "Invalid signature"
}
```

**500 Internal Server Error:**
```json
{
  "error": "Webhook processing failed"
}
```

## 🔐 Webhook Signature Validation

If `GITHUB_WEBHOOK_SECRET` is configured, the worker validates webhook signatures.

### Signature Format
- Header: `X-Hub-Signature-256`
- Format: `sha256={hex_signature}`
- Algorithm: HMAC SHA256 of request body

### Example Signature Calculation (Node.js)
```javascript
const crypto = require('crypto');

function calculateSignature(payload, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload, 'utf8');
  return 'sha256=' + hmac.digest('hex');
}

// For form-encoded payload
const body = 'payload=' + encodeURIComponent(JSON.stringify(payloadObj));
const signature = calculateSignature(body, process.env.GITHUB_WEBHOOK_SECRET);
```

### Example with Signature
```bash
# Calculate signature first
SECRET="your-webhook-secret"
PAYLOAD='payload={"pusher":{"name":"test"}}'
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -binary | xxd -p -c 256)

# Send request
curl -X POST https://your-worker.workers.dev/webhook/github \
  -H "X-GitHub-Event: push" \
  -H "X-GitHub-Delivery: test-delivery" \
  -H "X-Hub-Signature-256: sha256=$SIGNATURE" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "$PAYLOAD"
```

## 🧪 Testing Utilities

### Test Script for All Endpoints

Create `test-worker.js`:

```javascript
#!/usr/bin/env node

const WORKER_URL = process.env.WORKER_URL || 'https://your-worker.workers.dev';

async function testHealth() {
  console.log('🏥 Testing health endpoint...');
  const response = await fetch(`${WORKER_URL}/health`);
  const data = await response.json();
  console.log('Response:', data);
  console.log('Status:', response.status === 200 ? '✅ PASS' : '❌ FAIL');
}

async function testBrowserLogs() {
  console.log('\n🌐 Testing browser logs endpoint...');
  const logs = [
    '{"level":"info","msg":"Test log 1","ts":"2024-01-01T00:00:00.000Z"}',
    '{"level":"error","msg":"Test error","ts":"2024-01-01T00:00:01.000Z"}'
  ].join('\n');

  const response = await fetch(`${WORKER_URL}/webhook/browser`, {
    method: 'POST',
    headers: { 'Content-Type': 'text/plain' },
    body: logs
  });
  
  const text = await response.text();
  console.log('Response:', text);
  console.log('Status:', response.status === 200 ? '✅ PASS' : '❌ FAIL');
}

async function testGitHubWebhook() {
  console.log('\n🐙 Testing GitHub webhook (form-encoded)...');
  
  const payload = {
    pusher: { name: 'testuser' },
    repository: { full_name: 'test/repo' },
    ref: 'refs/heads/main'
  };

  const body = 'payload=' + encodeURIComponent(JSON.stringify(payload));
  
  const response = await fetch(`${WORKER_URL}/webhook/github`, {
    method: 'POST',
    headers: {
      'X-GitHub-Event': 'push',
      'X-GitHub-Delivery': 'test-delivery-' + Date.now(),
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: body
  });
  
  const text = await response.text();
  console.log('Response:', text);
  console.log('Status:', response.status === 200 ? '✅ PASS' : '❌ FAIL');
}

async function testGitHubWebhookJSON() {
  console.log('\n🐙 Testing GitHub webhook (JSON)...');
  
  const payload = {
    pusher: { name: 'testuser' },
    repository: { full_name: 'test/repo' },
    action: 'opened'
  };
  
  const response = await fetch(`${WORKER_URL}/webhook/github`, {
    method: 'POST',
    headers: {
      'X-GitHub-Event': 'pull_request',
      'X-GitHub-Delivery': 'test-delivery-json-' + Date.now(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
  
  const text = await response.text();
  console.log('Response:', text);
  console.log('Status:', response.status === 200 ? '✅ PASS' : '❌ FAIL');
}

async function testMissingHeaders() {
  console.log('\n❌ Testing missing headers (should fail)...');
  
  const response = await fetch(`${WORKER_URL}/webhook/github`, {
    method: 'POST',
    body: 'payload={}'
  });
  
  const text = await response.text();
  console.log('Response:', text);
  console.log('Status:', response.status === 400 ? '✅ PASS (correctly failed)' : '❌ FAIL');
}

async function runTests() {
  console.log(`🚀 Testing StackScope Worker at: ${WORKER_URL}\n`);
  
  try {
    await testHealth();
    await testBrowserLogs();
    await testGitHubWebhook();
    await testGitHubWebhookJSON();
    await testMissingHeaders();
    
    console.log('\n✅ All tests completed!');
  } catch (error) {
    console.error('❌ Test failed:', error);
  }
}

runTests();
```

### Usage
```bash
# Set your worker URL
export WORKER_URL="https://your-worker.workers.dev"

# Run tests
node test-worker.js
```

## 🛠️ Development Tools

### Live Log Monitoring
```bash
# Stream all worker logs
wrangler tail

# Filter by log level
wrangler tail --format json | jq 'select(.level == "error")'

# Filter by source
wrangler tail --format json | jq 'select(.source == "github")'
```

### Environment Setup
```bash
# Set webhook secret
wrangler secret put GITHUB_WEBHOOK_SECRET

# Set CORS origins (optional)
wrangler secret put CORS_ORIGINS
# Example: https://myapp.com,https://localhost:3000

# List current secrets
wrangler secret list
```

### Debugging Failed Requests
```bash
# Check recent deployments
wrangler deployments list

# View worker code
wrangler download

# Test locally
wrangler dev --local
```

## ❓ Common Issues

### CORS Errors
- Add your domain to CORS_ORIGINS
- Check preflight OPTIONS handling
- Verify response headers include CORS

### 404 Not Found
- Verify endpoint path is correct
- Check method (GET vs POST)
- Ensure worker is deployed

### 400 Bad Request
- Verify required headers are present
- Check payload format matches endpoint expectations
- Validate JSON syntax

### 500 Internal Server Error
- Check worker logs with `wrangler tail`
- Verify environment variables are set
- Test with minimal payload first