diff --git a/QUICK_WEBHOOK_SETUP.md b/QUICK_WEBHOOK_SETUP.md new file mode 100644 index 0000000..48fb361 --- /dev/null +++ b/QUICK_WEBHOOK_SETUP.md @@ -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` \ No newline at end of file diff --git a/STRAPI_WEBHOOKS_SETUP.md b/STRAPI_WEBHOOKS_SETUP.md new file mode 100644 index 0000000..ecf7616 --- /dev/null +++ b/STRAPI_WEBHOOKS_SETUP.md @@ -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 \ No newline at end of file diff --git a/backend/src/modules/strapi.controller.ts b/backend/src/modules/strapi.controller.ts index cf6a02b..a0a71fc 100644 --- a/backend/src/modules/strapi.controller.ts +++ b/backend/src/modules/strapi.controller.ts @@ -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)}`); diff --git a/frontend/src/components/ArticleTicker.tsx b/frontend/src/components/ArticleTicker.tsx index 7c1657c..ea0a390 100644 --- a/frontend/src/components/ArticleTicker.tsx +++ b/frontend/src/components/ArticleTicker.tsx @@ -24,7 +24,7 @@ export function ArticleTicker() { {articles.map((article, index) => ( {article.title || 'No title'} @@ -34,7 +34,7 @@ export function ArticleTicker() { {articles.map((article, index) => ( {article.title || 'No title'} diff --git a/frontend/src/components/routes/ArticlesComponent.tsx b/frontend/src/components/routes/ArticlesComponent.tsx index 0ddbca3..e5155dd 100644 --- a/frontend/src/components/routes/ArticlesComponent.tsx +++ b/frontend/src/components/routes/ArticlesComponent.tsx @@ -33,11 +33,11 @@ export function ArticlesComponent() {