Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ pnpm-debug.log*

# macOS-specific files
.DS_Store

# Local Netlify folder
.netlify
91 changes: 91 additions & 0 deletions docs/README_FEEDBACK_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Feedback Widget - GitHub Issues Integration

The feedback widget is **fully integrated with GitHub Issues** via a Netlify Function. When users submit feedback, it automatically creates an issue in your repository.

## 🚀 Quick Setup

### Step 1: Create a GitHub Personal Access Token

1. Go to: https://github.com/settings/tokens/new
2. Give it a name: "Interledger Docs Feedback"
3. Select expiration (recommend: 90 days or No expiration)
4. Select scopes: **`public_repo`** (for public repositories)
5. Click "Generate token"
6. **Copy the token** (you won't see it again!)

### Step 2: Add Token to Netlify Environment Variables

#### For Local Development

Create a `.env` file in the `/docs` directory:

```env
GITHUB_TOKEN=ghp_your_actual_token_here
```

⚠️ **Important:** Make sure `.env` is in your `.gitignore` (it should be by default)

#### For Production (Netlify)

1. Go to your site in the Netlify dashboard
2. Navigate to **Site settings > Environment variables**
3. Click **Add a variable**
4. Set:
- **Key:** `GITHUB_TOKEN`
- **Value:** Your GitHub personal access token
- **Scopes:** All scopes (or specific deploy contexts if needed)
5. Click **Create variable**
6. Trigger a new deploy for the changes to take effect

## 🏗️ Architecture

The feedback system uses a **Netlify Function** (serverless) to securely handle GitHub API authentication:

```
User clicks feedback → FeedbackWidget.astro → /.netlify/functions/feedback → GitHub API
```

**Files:**
- `/docs/netlify/functions/feedback.ts` - Netlify Function that creates GitHub issues
- `/docs/src/components/FeedbackWidget.astro` - Frontend widget component
- `/docs/src/components/Footer.astro` - Conditionally displays the widget
- `/docs/netlify.toml` - Netlify configuration

## 🧪 Testing Locally

To test the feedback widget with Netlify Functions locally:

```bash
# Install Netlify CLI if you haven't already
npm install -g netlify-cli

# Run the dev server with Netlify Functions
cd docs
netlify dev
```

This will start the Astro dev server and make the Netlify Functions available at `/.netlify/functions/feedback`.

## 📝 How It Works

1. User clicks "Yes" or "No" on a documentation page
2. Widget shows a textarea for optional feedback
3. On submit, JavaScript sends POST request to `/.netlify/functions/feedback`
4. Netlify Function authenticates with GitHub using `GITHUB_TOKEN` env var
5. Function creates an issue in `interledger/open-payments-docs-feedback` repository
6. Issue is automatically labeled with `feedback`, `docs`, and sentiment label
7. Widget shows success message

## 🔒 Security

- ✅ GitHub token is stored securely in Netlify environment variables
- ✅ Token is never exposed to the client/browser
- ✅ Function runs server-side only
- ✅ CORS is handled automatically by Netlify

## 🎯 Fallback Behavior

If the GitHub token is not configured or the API fails, the widget will:
1. Open a pre-filled GitHub issue creation page in a new tab
2. User can manually submit the feedback
3. This ensures feedback is never lost
6 changes: 5 additions & 1 deletion docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import starlightLinksValidator from 'starlight-links-validator'
import starlightFullViewMode from 'starlight-fullview-mode'
import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex'
import netlify from '@astrojs/netlify';

// https://astro.build/config
export default defineConfig({
output: 'static',
adapter: netlify(),
site: 'https://openpayments.dev',
markdown: {
remarkPlugins: [remarkMath],
Expand All @@ -31,7 +34,8 @@ export default defineConfig({
],
components: {
Header: './src/components/Header.astro',
PageSidebar: './src/components/PageSidebar.astro'
PageSidebar: './src/components/PageSidebar.astro',
Footer: "./src/components/Footer.astro",
},
customCss: [
'./node_modules/@interledger/docs-design-system/src/styles/teal-theme.css',
Expand Down
11 changes: 11 additions & 0 deletions docs/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[build]
command = "pnpm build"
publish = "dist"

[functions]
directory = "netlify/functions"
node_bundler = "esbuild"

# Environment variables (set these in Netlify UI)
# GITHUB_TOKEN = "your-github-personal-access-token"

101 changes: 101 additions & 0 deletions docs/netlify/functions/feedback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import type { Handler, HandlerEvent, HandlerContext } from '@netlify/functions';

const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
// Only allow POST requests
if (event.httpMethod !== 'POST') {
return {
statusCode: 405,
body: JSON.stringify({ error: 'Method not allowed' })
};
}

try {
const data = JSON.parse(event.body || '{}');
const { type, page, message } = data;

// Get GitHub token from environment variable
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const GITHUB_REPO = 'interledger/open-payments-docs-feedback';

if (!GITHUB_TOKEN) {
console.error('GITHUB_TOKEN not configured');
return {
statusCode: 500,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
success: false,
error: 'GitHub token not configured'
})
};
}

// Create issue title and body
const emoji = type === 'yes' ? '👍' : '👎';
const sentiment = type === 'yes' ? 'Positive' : 'Negative';
const issueTitle = `[Feedback] ${emoji} ${page}`;

const issueBody = `**Page:** ${page}
**Feedback Type:** ${sentiment} ${emoji}
**User Message:**

${message || '_No additional feedback provided_'}

---
_Submitted via feedback widget on ${new Date().toISOString()}_`;

// Create GitHub issue
const response = await fetch(
`https://api.github.com/repos/${GITHUB_REPO}/issues`,
{
method: 'POST',
headers: {
'Authorization': `token ${GITHUB_TOKEN}`,
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/json',
'User-Agent': 'Interledger-Docs-Feedback-Widget'
},
body: JSON.stringify({
title: issueTitle,
body: issueBody,
labels: [
'feedback',
'docs',
type === 'yes' ? 'feedback-positive' : 'feedback-negative'
]
})
}
);

if (!response.ok) {
const errorData = await response.text();
console.error('GitHub API error:', errorData);
throw new Error(`GitHub API returned ${response.status}`);
}

const issue = await response.json();

return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
success: true,
issueUrl: issue.html_url,
issueNumber: issue.number
})
};

} catch (error) {
console.error('Error creating GitHub issue:', error);
return {
statusCode: 500,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
})
};
}
};

export { handler };

4 changes: 3 additions & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/netlify": "^6.6.0",
"@astrojs/starlight": "^0.34.7",
"@interledger/docs-design-system": "^0.10.0",
"astro": "5.11.1",
Expand All @@ -21,7 +22,8 @@
"shiki": "3.8.0",
"starlight-fullview-mode": "^0.2.3",
"starlight-links-validator": "^0.17.0",
"starlight-openapi": "^0.19.1"
"starlight-openapi": "^0.19.1",
"@netlify/functions": "^5.1.0"
},
"devDependencies": {
"@prettier/plugin-php": "^0.24.0"
Expand Down
Loading