-
Notifications
You must be signed in to change notification settings - Fork 214
Create WP-Revalidate Next.js Plugin! #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8e98309
f07c1b7
d7635bc
9bab777
a254069
865f1e4
422a153
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,6 @@ | ||
WORDPRESS_URL="https://wordpress.com" | ||
WORDPRESS_HOSTNAME="wordpress.com" | ||
|
||
# If using the revalidate plugin | ||
# You can generate by running `openssl rand -base64 32` in the terminal | ||
WORDPRESS_WEBHOOK_SECRET="your-secret-key-here" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,3 +34,5 @@ yarn-error.log* | |
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts | ||
|
||
CLAUDE.md |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { revalidateTag } from "next/cache"; | ||
import { NextRequest, NextResponse } from "next/server"; | ||
|
||
export async function POST(request: NextRequest) { | ||
try { | ||
const requestBody = await request.json(); | ||
const secret = request.headers.get("x-webhook-secret"); | ||
|
||
// Validate webhook secret | ||
if (secret !== process.env.WORDPRESS_WEBHOOK_SECRET) { | ||
console.error("Invalid webhook secret"); | ||
return NextResponse.json( | ||
{ message: "Invalid webhook secret" }, | ||
{ status: 401 } | ||
); | ||
} | ||
|
||
// Extract content type and ID from the webhook payload | ||
const { contentType, contentId } = requestBody; | ||
|
||
if (!contentType) { | ||
return NextResponse.json( | ||
{ message: "Missing content type" }, | ||
{ status: 400 } | ||
); | ||
} | ||
|
||
// Determine which tags to revalidate | ||
const tagsToRevalidate = ["wordpress"]; | ||
|
||
// Add content type specific tag | ||
if (contentType === "post") { | ||
tagsToRevalidate.push("posts"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`post-${contentId}`); | ||
} | ||
} else if (contentType === "page") { | ||
tagsToRevalidate.push("pages"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`page-${contentId}`); | ||
} | ||
} else if (contentType === "category") { | ||
tagsToRevalidate.push("categories"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`category-${contentId}`); | ||
} | ||
} else if (contentType === "tag") { | ||
tagsToRevalidate.push("tags"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`tag-${contentId}`); | ||
} | ||
} else if (contentType === "author" || contentType === "user") { | ||
tagsToRevalidate.push("authors"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`author-${contentId}`); | ||
} | ||
} else if (contentType === "media") { | ||
tagsToRevalidate.push("media"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`media-${contentId}`); | ||
} | ||
} | ||
|
||
// Revalidate all determined tags | ||
for (const tag of tagsToRevalidate) { | ||
console.log(`Revalidating tag: ${tag}`); | ||
revalidateTag(tag); | ||
} | ||
|
||
return NextResponse.json({ | ||
revalidated: true, | ||
message: `Revalidated tags: ${tagsToRevalidate.join(", ")}`, | ||
}); | ||
} catch (error) { | ||
console.error("Revalidation error:", error); | ||
return NextResponse.json( | ||
{ message: "Error revalidating content" }, | ||
{ status: 500 } | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -10,40 +10,41 @@ | |||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||
"dependencies": { | ||||||||||||||||||||||||||||||||||||||||||||||
"@hookform/resolvers": "^3.10.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-dialog": "^1.1.7", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-dropdown-menu": "^2.1.7", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-label": "^2.1.3", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-navigation-menu": "^1.2.6", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-scroll-area": "^1.2.4", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-select": "^2.1.7", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-separator": "^1.1.3", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-dialog": "^1.1.11", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-dropdown-menu": "^2.1.12", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-label": "^2.1.4", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-navigation-menu": "^1.2.10", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-scroll-area": "^1.2.6", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-select": "^2.2.2", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-separator": "^1.1.4", | ||||||||||||||||||||||||||||||||||||||||||||||
"@radix-ui/react-slot": "^1.2.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"@vercel/analytics": "^1.5.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"class-variance-authority": "^0.7.1", | ||||||||||||||||||||||||||||||||||||||||||||||
"clsx": "^2.1.1", | ||||||||||||||||||||||||||||||||||||||||||||||
"lucide-react": "^0.469.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"next": "^15.3.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"next": "^15.3.1", | ||||||||||||||||||||||||||||||||||||||||||||||
"next-themes": "^0.4.6", | ||||||||||||||||||||||||||||||||||||||||||||||
"query-string": "^9.1.1", | ||||||||||||||||||||||||||||||||||||||||||||||
"query-string": "^9.1.2", | ||||||||||||||||||||||||||||||||||||||||||||||
"react": "19.0.0-rc.1", | ||||||||||||||||||||||||||||||||||||||||||||||
"react-dom": "19.0.0-rc.1", | ||||||||||||||||||||||||||||||||||||||||||||||
"react-hook-form": "^7.55.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"react-hook-form": "^7.56.2", | ||||||||||||||||||||||||||||||||||||||||||||||
"react-wrap-balancer": "^1.1.1", | ||||||||||||||||||||||||||||||||||||||||||||||
"tailwind-merge": "^2.6.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"tailwindcss-animate": "^1.0.7", | ||||||||||||||||||||||||||||||||||||||||||||||
"use-debounce": "^10.0.4", | ||||||||||||||||||||||||||||||||||||||||||||||
"zod": "^3.24.2" | ||||||||||||||||||||||||||||||||||||||||||||||
"zod": "^3.24.4" | ||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||
"devDependencies": { | ||||||||||||||||||||||||||||||||||||||||||||||
"@tailwindcss/typography": "^0.5.16", | ||||||||||||||||||||||||||||||||||||||||||||||
"@types/node": "^20.17.30", | ||||||||||||||||||||||||||||||||||||||||||||||
"@types/node": "^20.17.32", | ||||||||||||||||||||||||||||||||||||||||||||||
"@types/react": "^18.3.20", | ||||||||||||||||||||||||||||||||||||||||||||||
"@types/react-dom": "^18.3.6", | ||||||||||||||||||||||||||||||||||||||||||||||
"@types/react-dom": "^18.3.7", | ||||||||||||||||||||||||||||||||||||||||||||||
"autoprefixer": "^10.4.21", | ||||||||||||||||||||||||||||||||||||||||||||||
"eslint": "^9.24.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"eslint-config-next": "^15.3.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"eslint": "^9.26.0", | ||||||||||||||||||||||||||||||||||||||||||||||
"eslint-config-next": "^15.3.1", | ||||||||||||||||||||||||||||||||||||||||||||||
"postcss": "^8.5.3", | ||||||||||||||||||||||||||||||||||||||||||||||
"tailwindcss": "^3.4.17", | ||||||||||||||||||||||||||||||||||||||||||||||
"typescript": "^5.8.3" | ||||||||||||||||||||||||||||||||||||||||||||||
"typescript": "^5.8.3" | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+39
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove duplicated There's a duplicate @@ devDependencies
- "typescript": "^5.8.3" 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (1.9.4)[error] 48-48: expected Remove "typescript" (parse) [error] 47-47: The key typescript was already declared. This where a duplicated key was declared again. If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored. (lint/suspicious/noDuplicateObjectKeys) |
||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Next.js WordPress Revalidation Plugin | ||
|
||
This plugin enables automatic revalidation of your Next.js site when content is changed in WordPress. | ||
|
||
## Installation | ||
|
||
1. Upload the `next-revalidate.zip` file through the WordPress admin plugin installer, or | ||
2. Extract the `next-revalidate` folder to your `/wp-content/plugins/` directory | ||
3. Activate the plugin through the WordPress admin interface | ||
4. Go to Settings > Next.js Revalidation to configure your settings | ||
|
||
## Configuration | ||
|
||
### 1. WordPress Plugin Settings | ||
|
||
After installing and activating the plugin: | ||
|
||
1. Go to Settings > Next.js Revalidation in your WordPress admin | ||
2. Enter your Next.js site URL (without trailing slash) | ||
3. Create a secure webhook secret (a random string), you can use `openssl rand -base64 32` to generate one | ||
4. Save your settings | ||
|
||
### 2. Next.js Environment Variables | ||
|
||
Add the webhook secret to your Next.js environment variables: | ||
|
||
```bash | ||
# .env.local | ||
WORDPRESS_WEBHOOK_SECRET="your-secret-key-here" | ||
``` | ||
|
||
## How It Works | ||
|
||
1. When content in WordPress is created, updated, or deleted, the plugin sends a webhook to your Next.js API route | ||
2. The webhook contains information about the content type (post, page, category, etc.) and ID | ||
3. The Next.js API validates the request using the secret and revalidates the appropriate cache tags | ||
4. Your Next.js site will fetch new content for the affected pages | ||
|
||
## Features | ||
|
||
- Automatic revalidation for posts, pages, categories, tags, and media | ||
- Manual revalidation option through the admin interface | ||
- Secure webhook communication with your Next.js site | ||
- Optional admin notifications for revalidation events | ||
|
||
## Troubleshooting | ||
|
||
If revalidation isn't working: | ||
|
||
1. Check that your Next.js URL is correct in the plugin settings | ||
2. Verify the webhook secret matches in both WordPress and Next.js | ||
3. Check your server logs for any errors in the API route | ||
4. Enable notifications in the plugin settings to see revalidation status |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
=== Next.js Revalidation === | ||
Contributors: 9d8 | ||
Tags: next.js, headless, revalidation, cache | ||
Requires at least: 5.0 | ||
Tested up to: 6.4 | ||
Stable tag: 1.0.1 | ||
Requires PHP: 7.2 | ||
License: GPLv2 or later | ||
License URI: https://www.gnu.org/licenses/gpl-2.0.html | ||
|
||
Automatically revalidate your Next.js site when WordPress content changes. | ||
|
||
== Description == | ||
|
||
Next.js Revalidation is a WordPress plugin designed to work with the `next-wp` Next.js starter template. It triggers revalidation of your Next.js site's cache whenever content is added, updated, or deleted in WordPress. | ||
|
||
The plugin sends webhooks to your Next.js site's revalidation API endpoint, ensuring your headless frontend always displays the most up-to-date content. | ||
|
||
**Key Features:** | ||
|
||
* Automatic revalidation when posts, pages, categories, tags, authors, or media are modified | ||
* Settings page to configure your Next.js site URL and webhook secret | ||
* Manual revalidation option for full site refresh | ||
* Support for custom post types and taxonomies | ||
* Optional admin notifications for revalidation events | ||
|
||
== Installation == | ||
|
||
1. Upload the `next-revalidate` folder to the `/wp-content/plugins/` directory | ||
2. Activate the plugin through the 'Plugins' menu in WordPress | ||
3. Go to Settings > Next.js Revalidation to configure your Next.js site URL and webhook secret | ||
|
||
== Configuration == | ||
|
||
1. Visit Settings > Next.js Revalidation in your WordPress admin | ||
2. Enter your Next.js site URL without a trailing slash (e.g., https://your-site.com) | ||
3. Enter the webhook secret which should match the WORDPRESS_WEBHOOK_SECRET in your Next.js environment | ||
4. Optionally enable admin notifications for revalidation events | ||
5. Click "Save Settings" | ||
|
||
== Frequently Asked Questions == | ||
|
||
= What is the webhook secret for? = | ||
|
||
The webhook secret provides security for your revalidation API endpoint. It ensures that only your WordPress site can trigger revalidations. | ||
|
||
= How do I set up my Next.js site for revalidation? = | ||
|
||
Your Next.js site needs an API endpoint at `/api/revalidate` that can process the webhook payloads from this plugin. | ||
See the README in your Next.js project for more details. | ||
|
||
= Does this work with custom post types? = | ||
|
||
Yes, the plugin automatically detects and handles revalidation for custom post types and taxonomies. | ||
|
||
== Changelog == | ||
|
||
= 1.0.1 = | ||
* Fix: Register AJAX actions for manual revalidation | ||
* Fix: Normalize Next.js site URL in settings (remove trailing slash) | ||
= 1.0.0 = | ||
* Initial release | ||
|
||
== Upgrade Notice == | ||
|
||
= 1.0.0 = | ||
Initial release of the Next.js Revalidation plugin. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
<?php | ||
// Silence is golden. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add support for custom post types
The current implementation doesn't explicitly handle custom post types which are mentioned in the plugin documentation. Consider adding support for them.
📝 Committable suggestion