Skip to content
Merged
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to Waygate will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.0] - 2026-05-25

### Added
- REST API endpoints under `waygate/v1`: `GET /patterns` (requires `edit_posts`) lists all registered patterns with optional `?category=` filter; `POST /pages` (requires `publish_pages`) creates a draft page from an ordered pattern slug list and returns `page_id`, `edit_url`, and `view_url`
- Per-user rate limiting on `POST /wp-json/waygate/v1/pages`: maximum 10 requests per 60-second window per user; returns `429` when exceeded; limit is filterable via `waygate_rate_limit`
- `POST /pages` always creates pages as `draft`; the `status` parameter has been removed to prevent accidental publishing via the API
- Failed page creation via the REST API returns a `422` status code with the underlying error message
- PHPUnit tests for both REST endpoints and the rate limiter (19 new tests, 91 total assertions)
- REST bootstrap stubs (`WP_REST_Request`, `WP_REST_Response`, `WP_REST_Server`, `rest_ensure_response`, `register_rest_route`, transient helpers) added to the unit test bootstrap

## [0.6.2] - 2026-05-24

### Documentation
Expand Down
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Waygate lets you assemble WordPress pages from block patterns — manually or via a natural-language AI prompt powered by the WordPress AI Client (WordPress 7.0+). Works with any block theme; [Elayne](https://github.com/imagewize/elayne) is the primary supported theme.

> **Beta** — v0.6.0. Use on staging/development sites; not yet recommended for production.
> **Beta** — v0.7.0. Use on staging/development sites; not yet recommended for production.

---

Expand All @@ -17,6 +17,7 @@ Waygate lets you assemble WordPress pages from block patterns — manually or vi
- **Prompt templates** — Six built-in page templates (Homepage, About, Services, Contact, Landing Page, Portfolio) pre-fill the AI prompt; extend via the `waygate_prompt_templates` filter
- **Feature detection** — AI form is hidden automatically when no provider supports text generation
- **Abilities API** — Exposes `elayne/list-patterns` and `elayne/create-page` abilities for WP 7.0+
- **REST API** — `GET /wp-json/waygate/v1/patterns` and `POST /wp-json/waygate/v1/pages` for headless and external tool integration
- **Multi-provider** — Works with Mistral, Claude, OpenAI, or Gemini via WP AI Client
- **Any block theme** — Default prefix is `elayne/`; extend via the `waygate_pattern_prefixes` filter

Expand Down Expand Up @@ -103,6 +104,37 @@ When WordPress 7.0's Abilities API is available, Waygate registers two abilities

---

## REST API

Waygate exposes two REST endpoints under `/wp-json/waygate/v1/`:

| Method | Endpoint | Permission | Description |
|---|---|---|---|
| `GET` | `/patterns` | `edit_posts` | List all registered patterns; optional `?category=hero` filter |
| `POST` | `/pages` | `publish_pages` | Create a **draft** page from pattern slugs (max 10 req/min per user) |

**Example — list patterns filtered by category:**

```bash
curl -u admin:password https://example.com/wp-json/waygate/v1/patterns?category=hero
```

**Example — create a page:**

```bash
curl -u admin:password -X POST https://example.com/wp-json/waygate/v1/pages \
-H "Content-Type: application/json" \
-d '{"title":"My Page","patterns":["elayne/hero","elayne/features","elayne/cta"],"status":"draft"}'
```

Response:

```json
{ "page_id": 42, "edit_url": "https://example.com/wp-admin/post.php?post=42&action=edit", "view_url": "https://example.com/?page_id=42" }
```

---

## Development

```bash
Expand Down
21 changes: 15 additions & 6 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Waygate Roadmap & Improvement Ideas

> **Document Version**: 1.1.0
> **Last Updated**: 2026-05-24
> **Last Updated**: 2026-05-25
> **Status**: Draft

This document outlines potential improvements and feature additions for Waygate, based on WordPress 7.0 AI Client and Abilities API capabilities.
Expand Down Expand Up @@ -143,6 +143,14 @@ Add a dropdown to filter patterns by category in the catalog table.

### Phase 2: Medium Effort ← **Next Up**

#### 8. Add Prompt Templates ✅
**File**: `includes/class-ai-integration.php`
**File**: `includes/class-admin.php`

Built-in prompt templates with `waygate_prompt_templates` filter, and a "Quick template" dropdown in the admin UI that populates the description field (with confirmation if the field already has content).

**Status**: Complete as of 2026-05-25.

#### 5. Add Image Generation for Pattern Previews
**New file**: `includes/class-image-generator.php`

Expand Down Expand Up @@ -177,8 +185,8 @@ class ImageGenerator {

**Benefit**: Visual pattern selection, better UX.

#### 6. Add REST API Endpoints for Remote Access
**New file**: `includes/class-rest-api.php`
#### 6. Add REST API Endpoints for Remote Access
**File**: `includes/class-rest-api.php`

```php
class RestApi {
Expand Down Expand Up @@ -447,9 +455,9 @@ public static function track_pattern_usage( string $pattern_slug ): void {
### Recommended Order

1. ~~Feature detection, ability annotations, generic prefixes — *Phase 1*~~ ✅ Done
2. Prompt templates — *Phase 2* (1–2 days) ← **Start here**
3. REST API endpoints — *Phase 2* (2–3 days)
4. Client-side abilities for editor integration — *Phase 2* (2–3 days)
2. ~~Prompt templates — *Phase 2* (1–2 days)~~ ✅ Done
3. ~~REST API endpoints — *Phase 2* (2–3 days)~~ ✅ Done
4. Client-side abilities for editor integration — *Phase 2* (2–3 days) ← **Start here**
5. Image generation for previews — *Phase 2* (2–3 days)
6. Batch page creation — *Phase 2* (1–2 days)
7. Cost tracking, pattern popularity — *Phase 3* (2–3 days)
Expand Down Expand Up @@ -577,3 +585,4 @@ const abilities = getAbilities();
|------|--------|--------|
| 2026-05-22 | Initial draft | Created roadmap document |
| 2026-05-24 | Jasper Frumau | Marked Phase 1 complete; Phase 2 is next |
| 2026-05-25 | Jasper Frumau | Marked Prompt Templates (#8) complete; REST API is next |
152 changes: 152 additions & 0 deletions includes/class-rest-api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php
/**
* REST API endpoints for remote pattern listing and page creation.
*
* @package Imagewize\Waygate
*/

namespace Imagewize\Waygate;

defined( 'ABSPATH' ) || exit;

/**
* Registers /waygate/v1/patterns and /waygate/v1/pages REST endpoints.
*/
class Rest_API {

const NAMESPACE = 'waygate/v1';
const RATE_LIMIT = 10; // Max page-creation requests per user per 60 seconds.

/**
* Registers the rest_api_init hook.
*/
public static function init(): void {
add_action( 'rest_api_init', array( self::class, 'register_routes' ) );
}

/**
* Registers all Waygate REST routes.
*/
public static function register_routes(): void {
register_rest_route(
self::NAMESPACE,
'/patterns',
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array( self::class, 'get_patterns' ),
'permission_callback' => fn() => current_user_can( 'edit_posts' ),
'args' => array(
'category' => array(
'type' => 'string',
'description' => 'Filter by category slug, e.g. "hero", "features", "cta".',
'required' => false,
'sanitize_callback' => 'sanitize_key',
),
),
)
);

register_rest_route(
self::NAMESPACE,
'/pages',
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array( self::class, 'create_page' ),
'permission_callback' => function () {
if ( ! current_user_can( 'publish_pages' ) ) {
return false;
}
if ( self::is_rate_limited() ) {
return new \WP_Error(
'rate_limited',
'Too many requests. Try again in a minute.',
array( 'status' => 429 )
);
}
return true;
},
'args' => array(
'title' => array(
'type' => 'string',
'description' => 'Page title.',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
'patterns' => array(
'type' => 'array',
'description' => 'Ordered list of pattern slugs.',
'required' => true,
'items' => array( 'type' => 'string' ),
),
),
)
);
}

/**
* GET /waygate/v1/patterns
*
* @param \WP_REST_Request $request Incoming REST request.
* @return \WP_REST_Response
*/
public static function get_patterns( \WP_REST_Request $request ): \WP_REST_Response {
$patterns = Pattern_Lab::get_patterns();

$category = $request->get_param( 'category' );
if ( ! empty( $category ) ) {
$cat_slug = 'elayne/' . $category;
$patterns = array_values(
array_filter( $patterns, fn( $p ) => in_array( $cat_slug, $p['categories'], true ) )
);
}

return rest_ensure_response( $patterns );
}

/**
* Returns true if the current user has exceeded the page-creation rate limit.
* Uses a per-user transient counter with a 60-second sliding window.
*/
public static function is_rate_limited(): bool {
$key = 'waygate_rl_' . get_current_user_id();
$count = (int) get_transient( $key );
$limit = apply_filters( 'waygate_rate_limit', self::RATE_LIMIT );

if ( $count >= $limit ) {
return true;
}

set_transient( $key, $count + 1, 60 );
return false;
}

/**
* POST /waygate/v1/pages
*
* @param \WP_REST_Request $request Incoming REST request.
* @return \WP_REST_Response|\WP_Error
*/
public static function create_page( \WP_REST_Request $request ) {
$result = Pattern_Lab::create_page(
$request->get_param( 'title' ),
(array) $request->get_param( 'patterns' ),
'draft'
);

if ( is_wp_error( $result ) ) {
return new \WP_Error(
$result->get_error_code(),
$result->get_error_message(),
array( 'status' => 422 )
);
}

return rest_ensure_response(
array(
'page_id' => $result,
'edit_url' => get_edit_post_link( $result, 'raw' ),
'view_url' => get_permalink( $result ),
)
);
}
}
Loading
Loading