another auth checkpoint
webhook issue resolved
This commit is contained in:
parent
cb2355adcd
commit
3718882f5b
70
QUICK_WEBHOOK_SETUP.md
Normal file
70
QUICK_WEBHOOK_SETUP.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Quick Strapi Webhook Setup
|
||||
|
||||
## TL;DR - Quick Configuration
|
||||
|
||||
1. **Access Strapi Admin**: `http://localhost:1337/admin`
|
||||
2. **Go to Settings → Webhooks**
|
||||
3. **Add New Webhook**:
|
||||
- **Name**: `Backend Sync`
|
||||
- **URL**: `http://localhost:3000/api/v1/webhooks/strapi`
|
||||
- **Events**: Select all for "Article" and "Live Blog" content types
|
||||
4. **Save and Test**
|
||||
|
||||
## Already Configured (What We Fixed)
|
||||
|
||||
✅ **Backend webhook endpoints** are now public (no auth required)
|
||||
✅ **Tested webhooks** manually - they work
|
||||
✅ **Articles sync** from Strapi to backend
|
||||
✅ **Frontend TypeScript errors** fixed
|
||||
✅ **Authentication system** working
|
||||
|
||||
## Manual Test Commands
|
||||
|
||||
```bash
|
||||
# Test webhook manually
|
||||
curl -X POST http://localhost:3000/api/v1/webhooks/strapi \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"event": "entry.publish",
|
||||
"model": "article",
|
||||
"entry": {"documentId": "r07qatlpgvx82d7337n3nz1l"}
|
||||
}'
|
||||
|
||||
# Check synced articles
|
||||
curl http://localhost:3000/api/v1/articles?status=published
|
||||
|
||||
# Run comprehensive test
|
||||
./scripts/test-webhooks.sh
|
||||
```
|
||||
|
||||
## Current Status
|
||||
|
||||
- **Strapi Articles**: 2
|
||||
- **Backend Articles**: 2 (synced)
|
||||
- **Webhook Status**: Ready for configuration
|
||||
- **Frontend**: Access at `http://localhost:5173/articles`
|
||||
|
||||
## Immediate Action Required
|
||||
|
||||
1. **Configure webhooks in Strapi admin** (see detailed guide in `STRAPI_WEBHOOKS_SETUP.md`)
|
||||
2. **Test by publishing an article** in Strapi
|
||||
3. **Verify automatic sync** works
|
||||
|
||||
## If Webhooks Don't Work
|
||||
|
||||
Use manual sync as fallback:
|
||||
```bash
|
||||
# Get auth token first
|
||||
TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"testuser","password":"Test123!"}' | jq -r '.access_token')
|
||||
|
||||
# Sync all articles
|
||||
curl -X POST http://localhost:3000/api/v1/webhooks/strapi/sync/all \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
## Verification
|
||||
- Visit `http://localhost:5173/articles` to see synced articles
|
||||
- Check backend logs: `docker logs placebo-backend-dev --tail 20`
|
||||
- Monitor sync status with test script: `./scripts/test-webhooks.sh`
|
||||
191
STRAPI_WEBHOOKS_SETUP.md
Normal file
191
STRAPI_WEBHOOKS_SETUP.md
Normal file
@ -0,0 +1,191 @@
|
||||
# Strapi Webhooks Configuration Guide
|
||||
|
||||
## Overview
|
||||
This guide explains how to configure Strapi webhooks for automatic synchronization with the backend API. When articles or live blogs are published/updated/deleted in Strapi, webhooks will automatically trigger synchronization with the backend.
|
||||
|
||||
## Webhook Endpoints
|
||||
|
||||
The backend provides three webhook endpoints:
|
||||
|
||||
1. **Article-specific webhook**: `POST /api/v1/webhooks/strapi/article`
|
||||
2. **Live Blog-specific webhook**: `POST /api/v1/webhooks/strapi/live-blog`
|
||||
3. **Generic webhook**: `POST /api/v1/webhooks/strapi` (handles both)
|
||||
|
||||
All endpoints accept the following JSON format:
|
||||
```json
|
||||
{
|
||||
"event": "entry.publish", // or "entry.update", "entry.delete", "entry.unpublish"
|
||||
"model": "article", // or "live-blog"
|
||||
"entry": {
|
||||
"documentId": "unique-strapi-id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Steps
|
||||
|
||||
### Step 1: Access Strapi Admin Panel
|
||||
1. Open your browser and go to: `http://localhost:1337/admin`
|
||||
2. Log in with your admin credentials
|
||||
|
||||
### Step 2: Configure Webhooks
|
||||
1. In the left sidebar, click on **Settings** (gear icon)
|
||||
2. Click on **Webhooks** under the "GLOBAL SETTINGS" section
|
||||
3. Click the **"Add new webhook"** button
|
||||
|
||||
### Step 3: Configure Article Webhook
|
||||
**Basic Settings:**
|
||||
- **Name**: `Backend Article Sync`
|
||||
- **URL**: `http://localhost:3000/api/v1/webhooks/strapi/article`
|
||||
- For Docker internal communication: `http://backend:3000/api/v1/webhooks/strapi/article`
|
||||
- **Headers**: Add if needed (usually not required)
|
||||
|
||||
**Trigger Events:**
|
||||
Select the following events for the "Article" content type:
|
||||
- [x] **Create entry** (`entry.create`)
|
||||
- [x] **Update entry** (`entry.update`)
|
||||
- [x] **Delete entry** (`entry.delete`)
|
||||
- [x] **Publish entry** (`entry.publish`)
|
||||
- [x] **Unpublish entry** (`entry.unpublish`)
|
||||
|
||||
**Save the webhook.**
|
||||
|
||||
### Step 4: Configure Live Blog Webhook (Optional)
|
||||
Repeat Step 3 for live blogs:
|
||||
- **Name**: `Backend Live Blog Sync`
|
||||
- **URL**: `http://localhost:3000/api/v1/webhooks/strapi/live-blog`
|
||||
- Select events for the "Live Blog" content type
|
||||
|
||||
### Alternative: Single Generic Webhook
|
||||
You can use a single webhook for all content types:
|
||||
- **Name**: `Backend Generic Sync`
|
||||
- **URL**: `http://localhost:3000/api/v1/webhooks/strapi`
|
||||
- Select events for ALL relevant content types (Article, Live Blog)
|
||||
|
||||
## Testing Webhooks
|
||||
|
||||
### Manual Test
|
||||
You can manually trigger a webhook to test the configuration:
|
||||
|
||||
```bash
|
||||
# Test article webhook
|
||||
curl -X POST http://localhost:3000/api/v1/webhooks/strapi/article \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"event": "entry.publish",
|
||||
"model": "article",
|
||||
"entry": {
|
||||
"documentId": "your-article-id"
|
||||
}
|
||||
}'
|
||||
|
||||
# Test generic webhook
|
||||
curl -X POST http://localhost:3000/api/v1/webhooks/strapi \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"event": "entry.publish",
|
||||
"model": "article",
|
||||
"entry": {
|
||||
"documentId": "your-article-id"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Test from Strapi
|
||||
1. In Strapi admin, edit any article
|
||||
2. Click "Save" or "Publish"
|
||||
3. Check backend logs for webhook receipt:
|
||||
```bash
|
||||
docker logs placebo-backend-dev --tail 20 | grep -i "webhook\|strapi"
|
||||
```
|
||||
|
||||
## Manual Sync (Fallback)
|
||||
If webhooks fail or for initial sync, use manual sync endpoints:
|
||||
|
||||
```bash
|
||||
# Get authentication token first
|
||||
curl -X POST http://localhost:3000/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"your-username","password":"your-password"}'
|
||||
|
||||
# Sync all articles (requires auth)
|
||||
curl -X POST http://localhost:3000/api/v1/webhooks/strapi/sync/all \
|
||||
-H "Authorization: Bearer YOUR_TOKEN_HERE"
|
||||
|
||||
# Sync all live blogs
|
||||
curl -X POST http://localhost:3000/api/v1/webhooks/strapi/sync/live-blogs \
|
||||
-H "Authorization: Bearer YOUR_TOKEN_HERE"
|
||||
|
||||
# Sync everything
|
||||
curl -X POST http://localhost:3000/api/v1/webhooks/strapi/sync/everything \
|
||||
-H "Authorization: Bearer YOUR_TOKEN_HERE"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Webhook not received by backend**
|
||||
- Check backend logs: `docker logs placebo-backend-dev --tail 50`
|
||||
- Verify CORS is configured (should allow `http://localhost:1337`)
|
||||
- Test webhook manually with curl
|
||||
|
||||
2. **401 Unauthorized error**
|
||||
- Webhook endpoints are now public (no auth required)
|
||||
- If getting 401, check that `@Public()` decorator is on webhook methods
|
||||
|
||||
3. **Webhook received but sync fails**
|
||||
- Check Strapi API token in backend `.env.docker`
|
||||
- Verify Strapi is accessible from backend container
|
||||
- Check backend logs for specific error messages
|
||||
|
||||
4. **Article not appearing in frontend**
|
||||
- Verify article is synced to backend: `curl http://localhost:3000/api/v1/articles`
|
||||
- Check frontend browser console for errors
|
||||
- Hard refresh frontend (Ctrl+F5)
|
||||
|
||||
### Log Monitoring
|
||||
```bash
|
||||
# Monitor backend logs for webhooks
|
||||
docker logs -f placebo-backend-dev | grep -i "webhook\|strapi"
|
||||
|
||||
# Monitor Strapi logs
|
||||
docker logs -f placebo-cms-dev
|
||||
|
||||
# Check if articles are in backend
|
||||
curl -s "http://localhost:3000/api/v1/articles?status=published" | jq '.total'
|
||||
```
|
||||
|
||||
## Docker Network Considerations
|
||||
|
||||
### Internal Docker Communication
|
||||
- Within Docker network: Use `http://backend:3000` and `http://cms:1337`
|
||||
- From host machine: Use `http://localhost:3000` and `http://localhost:1337`
|
||||
|
||||
### Webhook URL Examples
|
||||
- **From Strapi container to Backend**: `http://backend:3000/api/v1/webhooks/strapi`
|
||||
- **From Host to Backend**: `http://localhost:3000/api/v1/webhooks/strapi`
|
||||
- **External/Production**: Use your domain: `https://api.yourdomain.com/api/v1/webhooks/strapi`
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Webhook endpoints are public** - ensure your backend is not exposed to the public internet in production
|
||||
2. **Consider adding webhook signature verification** for production
|
||||
3. **Use HTTPS in production** for all webhook calls
|
||||
4. **Monitor webhook logs** for suspicious activity
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- [ ] Webhooks configured in Strapi admin
|
||||
- [ ] Test webhook manually works
|
||||
- [ ] Article publish in Strapi triggers sync
|
||||
- [ ] Synced articles appear in backend API
|
||||
- [ ] Frontend displays synced articles
|
||||
- [ ] All event types tested (publish, update, delete)
|
||||
|
||||
## Support
|
||||
If issues persist:
|
||||
1. Check all service logs
|
||||
2. Verify network connectivity between containers
|
||||
3. Test each component independently
|
||||
4. Review error messages in logs
|
||||
@ -1,5 +1,6 @@
|
||||
import { Controller, Post, Body, Logger } from '@nestjs/common';
|
||||
import { StrapiService } from './strapi.service';
|
||||
import { Public } from './auth/public.decorator';
|
||||
|
||||
interface WebhookBody {
|
||||
event:
|
||||
@ -21,6 +22,7 @@ export class StrapiController {
|
||||
constructor(private readonly strapiService: StrapiService) {}
|
||||
|
||||
@Post('article')
|
||||
@Public()
|
||||
async handleArticleWebhook(@Body() body: WebhookBody) {
|
||||
this.logger.log(`Received article webhook: ${JSON.stringify(body)}`);
|
||||
const { event, model, entry } = body;
|
||||
@ -38,6 +40,7 @@ export class StrapiController {
|
||||
}
|
||||
|
||||
@Post('live-blog')
|
||||
@Public()
|
||||
async handleLiveBlogWebhook(@Body() body: WebhookBody) {
|
||||
this.logger.log(`Received live-blog webhook: ${JSON.stringify(body)}`);
|
||||
const { event, model, entry } = body;
|
||||
@ -55,6 +58,7 @@ export class StrapiController {
|
||||
}
|
||||
|
||||
@Post()
|
||||
@Public()
|
||||
async handleGenericWebhook(@Body() body: unknown) {
|
||||
this.logger.log(`Received generic webhook: ${JSON.stringify(body)}`);
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ export function ArticleTicker() {
|
||||
{articles.map((article, index) => (
|
||||
<Link
|
||||
key={`${article.id}-${index}`}
|
||||
to={`/articles/${article.id}` as any}
|
||||
to={`/articles/${article.id}`}
|
||||
className="text-sm text-muted-foreground hover:text-foreground hover:underline inline-block px-4"
|
||||
>
|
||||
{article.title || 'No title'}
|
||||
@ -34,7 +34,7 @@ export function ArticleTicker() {
|
||||
{articles.map((article, index) => (
|
||||
<Link
|
||||
key={`dup-${article.id}-${index}`}
|
||||
to={`/articles/${article.id}` as any}
|
||||
to={`/articles/${article.id}`}
|
||||
className="text-sm text-muted-foreground hover:text-foreground hover:underline inline-block px-4"
|
||||
>
|
||||
{article.title || 'No title'}
|
||||
|
||||
@ -33,11 +33,11 @@ export function ArticlesComponent() {
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{data?.data.map((article) => (
|
||||
<Link
|
||||
key={article.id}
|
||||
to={`/articles/${article.id}` as any}
|
||||
className="p-6 rounded-xl border bg-card hover:shadow-lg transition-shadow cursor-pointer block"
|
||||
>
|
||||
<Link
|
||||
key={article.id}
|
||||
to={`/articles/${article.id}`}
|
||||
className="p-6 rounded-xl border bg-card hover:shadow-lg transition-shadow cursor-pointer block"
|
||||
>
|
||||
<h2 className="text-xl font-semibold mb-2 line-clamp-2">
|
||||
{article.title}
|
||||
</h2>
|
||||
|
||||
@ -38,7 +38,7 @@ export function LiveBlogsComponent() {
|
||||
{data?.data.map((liveBlog) => (
|
||||
<Link
|
||||
key={liveBlog.id}
|
||||
to={`/live-blogs/${liveBlog.slug}` as any}
|
||||
to={`/live-blogs/${liveBlog.slug}`}
|
||||
className="p-6 rounded-xl border bg-card hover:shadow-lg transition-shadow cursor-pointer block"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useRef, useState, useMemo } from 'react';
|
||||
|
||||
export interface LiveBlogStreamEvent {
|
||||
type: 'connected' | 'update' | 'status-change' | 'pin-update' | 'error';
|
||||
@ -22,7 +22,7 @@ export function useLiveBlogStream(
|
||||
maxReconnectAttempts: 10,
|
||||
};
|
||||
|
||||
const mergedOptions = { ...defaultOptions, ...options };
|
||||
const mergedOptions = useMemo(() => ({ ...defaultOptions, ...options }), [options, defaultOptions]);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [lastEvent, setLastEvent] = useState<LiveBlogStreamEvent | null>(null);
|
||||
const [connectionError, setConnectionError] = useState<string | null>(null);
|
||||
|
||||
113
scripts/test-webhooks.sh
Executable file
113
scripts/test-webhooks.sh
Executable file
@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test script for Strapi webhooks
|
||||
# Usage: ./test-webhooks.sh [article-id]
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${YELLOW}=== Strapi Webhook Test Script ===${NC}\n"
|
||||
|
||||
# Default article ID (first article from Strapi)
|
||||
DEFAULT_ARTICLE_ID="r07qatlpgvx82d7337n3nz1l"
|
||||
ARTICLE_ID=${1:-$DEFAULT_ARTICLE_ID}
|
||||
|
||||
echo "Testing with article ID: $ARTICLE_ID"
|
||||
echo ""
|
||||
|
||||
# Test 1: Check backend is running
|
||||
echo -e "${YELLOW}1. Testing backend connectivity...${NC}"
|
||||
if curl -s -f "http://localhost:3000/api/v1/articles" > /dev/null; then
|
||||
echo -e "${GREEN}✓ Backend is running${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Backend is not accessible${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: Check Strapi is running
|
||||
echo -e "${YELLOW}2. Testing Strapi connectivity...${NC}"
|
||||
if curl -s -f "http://localhost:1337/_health" > /dev/null 2>&1 || curl -s -f "http://localhost:1337/admin" > /dev/null; then
|
||||
echo -e "${GREEN}✓ Strapi is running${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Strapi is not accessible${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Send test webhook
|
||||
echo -e "${YELLOW}3. Sending test webhook...${NC}"
|
||||
WEBHOOK_RESPONSE=$(curl -s -w "%{http_code}" -X POST "http://localhost:3000/api/v1/webhooks/strapi/article" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"event\": \"entry.publish\",
|
||||
\"model\": \"article\",
|
||||
\"entry\": {
|
||||
\"documentId\": \"$ARTICLE_ID\"
|
||||
}
|
||||
}")
|
||||
|
||||
# Extract status code and body
|
||||
HTTP_STATUS=${WEBHOOK_RESPONSE: -3}
|
||||
RESPONSE_BODY=${WEBHOOK_RESPONSE%???}
|
||||
|
||||
if [[ "$HTTP_STATUS" =~ ^2[0-9][0-9]$ ]]; then
|
||||
echo -e "${GREEN}✓ Webhook sent successfully (HTTP $HTTP_STATUS)${NC}"
|
||||
echo "Response: $RESPONSE_BODY"
|
||||
else
|
||||
echo -e "${RED}✗ Webhook failed (HTTP $HTTP_STATUS)${NC}"
|
||||
echo "Response: $RESPONSE_BODY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 4: Check if article is in backend
|
||||
echo -e "${YELLOW}4. Verifying article in backend...${NC}"
|
||||
sleep 2 # Give backend time to process
|
||||
|
||||
ARTICLE_COUNT=$(curl -s "http://localhost:3000/api/v1/articles?status=published" | jq '.total // 0')
|
||||
|
||||
if [ "$ARTICLE_COUNT" -gt 0 ]; then
|
||||
echo -e "${GREEN}✓ Backend has $ARTICLE_COUNT published article(s)${NC}"
|
||||
|
||||
# List articles
|
||||
echo -e "\n${YELLOW}Articles in backend:${NC}"
|
||||
curl -s "http://localhost:3000/api/v1/articles?status=published" | jq -r '.data[] | "• \(.title) (ID: \(.id))"'
|
||||
else
|
||||
echo -e "${RED}✗ No articles found in backend${NC}"
|
||||
fi
|
||||
|
||||
# Test 5: Check Strapi articles
|
||||
echo -e "\n${YELLOW}5. Checking Strapi articles...${NC}"
|
||||
STRAPI_TOKEN="578d628f62df967ff95f95bedb205b5d10bbf792340519c8c467d6473208e16b3918151a97b49fa2285a53df0ec8e340a9ca555b01a654bd22152847840e6a368ee626a6f1338ce2f23790c171013b263ec80fbaf116e2b459d3663b234d08855fd0eb631991ed15bb94f7dbb0b80f190352965c72c7fd327c73629ceff38fbb"
|
||||
STRAPI_ARTICLES=$(curl -s -H "Authorization: Bearer $STRAPI_TOKEN" "http://localhost:1337/api/articles")
|
||||
|
||||
STRAPI_COUNT=$(echo "$STRAPI_ARTICLES" | jq '.data | length // 0')
|
||||
|
||||
if [ "$STRAPI_COUNT" -gt 0 ]; then
|
||||
echo -e "${GREEN}✓ Strapi has $STRAPI_COUNT article(s)${NC}"
|
||||
|
||||
echo -e "\n${YELLOW}Articles in Strapi:${NC}"
|
||||
echo "$STRAPI_ARTICLES" | jq -r '.data[] | "• \(.title) (Strapi ID: \(.documentId))"'
|
||||
else
|
||||
echo -e "${RED}✗ No articles found in Strapi${NC}"
|
||||
fi
|
||||
|
||||
echo -e "\n${YELLOW}=== Summary ===${NC}"
|
||||
echo "Backend articles: $ARTICLE_COUNT"
|
||||
echo "Strapi articles: $STRAPI_COUNT"
|
||||
|
||||
if [ "$ARTICLE_COUNT" -eq "$STRAPI_COUNT" ]; then
|
||||
echo -e "${GREEN}✓ Sync appears to be working correctly!${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Article counts don't match. Manual sync may be needed.${NC}"
|
||||
echo "Run: curl -X POST http://localhost:3000/api/v1/webhooks/strapi/sync/all -H \"Authorization: Bearer YOUR_TOKEN\""
|
||||
fi
|
||||
|
||||
echo -e "\n${YELLOW}Next steps:${NC}"
|
||||
echo "1. Configure webhooks in Strapi admin: http://localhost:1337/admin"
|
||||
echo "2. Test by publishing an article in Strapi"
|
||||
echo "3. Check backend logs: docker logs placebo-backend-dev --tail 20"
|
||||
echo "4. Visit frontend: http://localhost:5173/articles"
|
||||
Loading…
Reference in New Issue
Block a user