Add configurable API rate limits with subtabs in settings (v0.10.0)

- Make rate limiting configurable via WordPress options
- Add subtabs to API settings: General, Rate Limits, Endpoints
- Add HTTP method badges for endpoint documentation
- Update CHANGELOG with rate limiting configuration details

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 21:50:12 +01:00
parent 481495805b
commit b701d127f8
5 changed files with 420 additions and 140 deletions

View File

@@ -46,21 +46,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Pricing API: - Pricing API:
- `POST /wp-bnb/v1/pricing/calculate` - Full price calculation with services - `POST /wp-bnb/v1/pricing/calculate` - Full price calculation with services
- `GET /wp-bnb/v1/pricing/seasons` - Get configured seasons and pricing modifiers - `GET /wp-bnb/v1/pricing/seasons` - Get configured seasons and pricing modifiers
- API Settings tab in plugin settings: - API Settings tab in plugin settings with three subtabs:
- Enable/disable REST API toggle - General subtab: Enable/disable REST API, rate limiting toggle, API information
- Enable/disable rate limiting toggle - Rate Limits subtab: Configurable time window and endpoint-specific limits
- Endpoint documentation table - Endpoints subtab: Full endpoint documentation with HTTP method badges
- Authentication instructions - Configurable rate limiting:
- Time window setting (10-300 seconds, default 60)
- Per-endpoint-type limits (public, availability, booking, admin)
- Settings stored in WordPress options, defaults maintained in code
### Changed ### Changed
- Plugin.php updated to initialize REST API on `rest_api_init` hook - Plugin.php updated to initialize REST API on `rest_api_init` hook
- Settings page now has seven tabs: General, Pricing, License, Updates, Metrics, API - Settings page now has seven tabs: General, Pricing, License, Updates, Metrics, API
- README.md updated with comprehensive REST API documentation - README.md updated with comprehensive REST API documentation
- RateLimiter class now loads limits from WordPress options with fallback defaults
### Security ### Security
- Rate limiting: public (60/min), availability (30/min), booking (10/min), admin (120/min) - Rate limiting: configurable per endpoint type (defaults: public 60/min, availability 30/min, booking 10/min, admin 120/min)
- Admin endpoints require `edit_posts` capability - Admin endpoints require `edit_posts` capability
- Supports WordPress Application Passwords for external API access - Supports WordPress Application Passwords for external API access
- Client identification by user ID (authenticated) or IP address (anonymous) - Client identification by user ID (authenticated) or IP address (anonymous)

View File

@@ -451,8 +451,10 @@ The plugin provides a comprehensive REST API for integration with external appli
### Enabling the API ### Enabling the API
1. Navigate to **WP BnB → Settings → API** 1. Navigate to **WP BnB → Settings → API**
2. Enable "Enable REST API" 2. In the **General** subtab, enable "Enable REST API"
3. Optionally enable rate limiting for protection against abuse 3. Optionally enable rate limiting for protection against abuse
4. Configure rate limits in the **Rate Limits** subtab
5. View all available endpoints in the **Endpoints** subtab
### Base URL ### Base URL
@@ -517,15 +519,23 @@ curl -u "username:app-password" https://site.com/wp-json/wp-bnb/v1/bookings
### Rate Limiting ### Rate Limiting
When enabled, rate limits are applied per client (by user ID or IP address): When enabled, rate limits are applied per client (by user ID or IP address). Configure limits in **Settings → API → Rate Limits**.
| Type | Limit | Applies To | **Default Limits:**
| ---- | ----- | ---------- |
| Type | Default | Applies To |
| ---- | ------- | ---------- |
| Public | 60/min | Room/building listings | | Public | 60/min | Room/building listings |
| Availability | 30/min | Availability and calendar endpoints | | Availability | 30/min | Availability and calendar endpoints |
| Booking | 10/min | Booking creation | | Booking | 10/min | Booking creation |
| Admin | 120/min | All admin endpoints | | Admin | 120/min | All admin endpoints |
**Configuration Options:**
- **Time Window**: 10-300 seconds (default: 60 seconds)
- **Per-endpoint limits**: Customize for each endpoint type
- **Rate limiting toggle**: Enable/disable without losing settings
Rate limit headers are included in responses: Rate limit headers are included in responses:
- `X-RateLimit-Limit`: Maximum requests allowed - `X-RateLimit-Limit`: Maximum requests allowed

View File

@@ -491,6 +491,37 @@
height: 16px; height: 16px;
} }
/* API Method Badges */
.wp-bnb-method {
display: inline-block;
padding: 2px 8px;
border-radius: 3px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
.wp-bnb-method.get {
background: #e7f5e7;
color: #1e7e1e;
}
.wp-bnb-method.post {
background: #e7f0f5;
color: #1e5f7e;
}
.wp-bnb-method.patch {
background: #f5f0e7;
color: #7e5f1e;
}
.wp-bnb-method.delete {
background: #f5e7e7;
color: #7e1e1e;
}
/* Form Tables */ /* Form Tables */
.form-table th { .form-table th {
width: 200px; width: 200px;

View File

@@ -22,23 +22,61 @@ final class RateLimiter {
private const TRANSIENT_PREFIX = 'wp_bnb_rate_'; private const TRANSIENT_PREFIX = 'wp_bnb_rate_';
/** /**
* Rate limits per minute by endpoint type. * Default rate limits per minute by endpoint type.
* *
* @var array<string, int> * @var array<string, int>
*/ */
private array $limits = array( private const DEFAULT_LIMITS = array(
'public' => 60, // Public read endpoints. 'public' => 60, // Public read endpoints.
'availability' => 30, // Availability checks. 'availability' => 30, // Availability checks.
'booking' => 10, // Booking creation. 'booking' => 10, // Booking creation.
'admin' => 120, // Admin endpoints. 'admin' => 120, // Admin endpoints.
); );
/**
* Rate limits per minute by endpoint type.
*
* @var array<string, int>
*/
private array $limits;
/** /**
* Time window in seconds. * Time window in seconds.
* *
* @var int * @var int
*/ */
private int $window = 60; private int $window;
/**
* Constructor.
*/
public function __construct() {
$this->load_limits_from_options();
}
/**
* Load rate limits from WordPress options.
*
* @return void
*/
private function load_limits_from_options(): void {
$this->limits = array(
'public' => (int) get_option( 'wp_bnb_rate_limit_public', self::DEFAULT_LIMITS['public'] ),
'availability' => (int) get_option( 'wp_bnb_rate_limit_availability', self::DEFAULT_LIMITS['availability'] ),
'booking' => (int) get_option( 'wp_bnb_rate_limit_booking', self::DEFAULT_LIMITS['booking'] ),
'admin' => (int) get_option( 'wp_bnb_rate_limit_admin', self::DEFAULT_LIMITS['admin'] ),
);
$this->window = (int) get_option( 'wp_bnb_rate_limit_window', 60 );
}
/**
* Get default rate limits.
*
* @return array<string, int>
*/
public static function get_default_limits(): array {
return self::DEFAULT_LIMITS;
}
/** /**
* Check if request is within rate limit. * Check if request is within rate limit.

View File

@@ -1460,13 +1460,48 @@ final class Plugin {
* @return void * @return void
*/ */
private function render_api_settings(): void { private function render_api_settings(): void {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Subtab switching only.
$active_subtab = isset( $_GET['subtab'] ) ? sanitize_key( $_GET['subtab'] ) : 'general';
$api_enabled = get_option( 'wp_bnb_api_enabled', 'yes' ); $api_enabled = get_option( 'wp_bnb_api_enabled', 'yes' );
$rate_limiting = get_option( 'wp_bnb_api_rate_limiting', 'yes' ); $rate_limiting = get_option( 'wp_bnb_api_rate_limiting', 'yes' );
$api_base_url = rest_url( RestApi::NAMESPACE ); $api_base_url = rest_url( RestApi::NAMESPACE );
// Rate limit values.
$defaults = \Magdev\WpBnb\Api\RateLimiter::get_default_limits();
$limit_public = get_option( 'wp_bnb_rate_limit_public', $defaults['public'] );
$limit_avail = get_option( 'wp_bnb_rate_limit_availability', $defaults['availability'] );
$limit_booking = get_option( 'wp_bnb_rate_limit_booking', $defaults['booking'] );
$limit_admin = get_option( 'wp_bnb_rate_limit_admin', $defaults['admin'] );
$limit_window = get_option( 'wp_bnb_rate_limit_window', 60 );
$base_url = admin_url( 'admin.php?page=wp-bnb-settings&tab=api' );
?> ?>
<!-- API Subtabs -->
<div class="wp-bnb-subtabs">
<a href="<?php echo esc_url( $base_url . '&subtab=general' ); ?>"
class="wp-bnb-subtab <?php echo 'general' === $active_subtab ? 'active' : ''; ?>">
<span class="dashicons dashicons-admin-generic"></span>
<?php esc_html_e( 'General', 'wp-bnb' ); ?>
</a>
<a href="<?php echo esc_url( $base_url . '&subtab=rate-limits' ); ?>"
class="wp-bnb-subtab <?php echo 'rate-limits' === $active_subtab ? 'active' : ''; ?>">
<span class="dashicons dashicons-dashboard"></span>
<?php esc_html_e( 'Rate Limits', 'wp-bnb' ); ?>
</a>
<a href="<?php echo esc_url( $base_url . '&subtab=endpoints' ); ?>"
class="wp-bnb-subtab <?php echo 'endpoints' === $active_subtab ? 'active' : ''; ?>">
<span class="dashicons dashicons-rest-api"></span>
<?php esc_html_e( 'Endpoints', 'wp-bnb' ); ?>
</a>
</div>
<form method="post" action=""> <form method="post" action="">
<?php wp_nonce_field( 'wp_bnb_save_settings', 'wp_bnb_settings_nonce' ); ?> <?php wp_nonce_field( 'wp_bnb_save_settings', 'wp_bnb_settings_nonce' ); ?>
<?php if ( 'general' === $active_subtab ) : ?>
<!-- General Subtab -->
<h2><?php esc_html_e( 'REST API Settings', 'wp-bnb' ); ?></h2> <h2><?php esc_html_e( 'REST API Settings', 'wp-bnb' ); ?></h2>
<table class="form-table"> <table class="form-table">
@@ -1491,6 +1526,9 @@ final class Plugin {
</label> </label>
<p class="description"> <p class="description">
<?php esc_html_e( 'Limits API requests to prevent abuse. Recommended for production sites.', 'wp-bnb' ); ?> <?php esc_html_e( 'Limits API requests to prevent abuse. Recommended for production sites.', 'wp-bnb' ); ?>
<?php if ( 'yes' === $rate_limiting ) : ?>
<a href="<?php echo esc_url( $base_url . '&subtab=rate-limits' ); ?>"><?php esc_html_e( 'Configure limits', 'wp-bnb' ); ?> &rarr;</a>
<?php endif; ?>
</p> </p>
</td> </td>
</tr> </tr>
@@ -1525,64 +1563,102 @@ final class Plugin {
</tr> </tr>
</table> </table>
<h2 style="margin-top: 30px;"><?php esc_html_e( 'Available Endpoints', 'wp-bnb' ); ?></h2> <?php submit_button( __( 'Save Settings', 'wp-bnb' ) ); ?>
<h3><?php esc_html_e( 'Public Endpoints', 'wp-bnb' ); ?></h3> <?php elseif ( 'rate-limits' === $active_subtab ) : ?>
<table class="widefat" style="margin-bottom: 20px;"> <!-- Rate Limits Subtab -->
<thead> <h2><?php esc_html_e( 'Rate Limit Configuration', 'wp-bnb' ); ?></h2>
<?php if ( 'yes' !== $rate_limiting ) : ?>
<div class="notice notice-warning inline" style="margin: 15px 0;">
<p>
<?php esc_html_e( 'Rate limiting is currently disabled.', 'wp-bnb' ); ?>
<a href="<?php echo esc_url( $base_url . '&subtab=general' ); ?>"><?php esc_html_e( 'Enable it in General settings', 'wp-bnb' ); ?></a>
</p>
</div>
<?php endif; ?>
<p class="description"><?php esc_html_e( 'Configure the number of requests allowed per time window for each endpoint type.', 'wp-bnb' ); ?></p>
<table class="form-table">
<tr> <tr>
<th><?php esc_html_e( 'Method', 'wp-bnb' ); ?></th> <th scope="row"><?php esc_html_e( 'Time Window', 'wp-bnb' ); ?></th>
<th><?php esc_html_e( 'Endpoint', 'wp-bnb' ); ?></th> <td>
<th><?php esc_html_e( 'Description', 'wp-bnb' ); ?></th> <input type="number" name="wp_bnb_rate_limit_window" value="<?php echo esc_attr( $limit_window ); ?>" min="10" max="300" step="10" class="small-text">
<?php esc_html_e( 'seconds', 'wp-bnb' ); ?>
<p class="description">
<?php esc_html_e( 'The time window for rate limit counting. Default: 60 seconds.', 'wp-bnb' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Public Endpoints', 'wp-bnb' ); ?></th>
<td>
<input type="number" name="wp_bnb_rate_limit_public" value="<?php echo esc_attr( $limit_public ); ?>" min="1" max="1000" class="small-text">
<?php esc_html_e( 'requests per window', 'wp-bnb' ); ?>
<p class="description">
<?php
printf(
/* translators: %d: default limit */
esc_html__( 'Limit for public read endpoints (rooms, buildings, services). Default: %d', 'wp-bnb' ),
$defaults['public']
);
?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Availability Endpoints', 'wp-bnb' ); ?></th>
<td>
<input type="number" name="wp_bnb_rate_limit_availability" value="<?php echo esc_attr( $limit_avail ); ?>" min="1" max="1000" class="small-text">
<?php esc_html_e( 'requests per window', 'wp-bnb' ); ?>
<p class="description">
<?php
printf(
/* translators: %d: default limit */
esc_html__( 'Limit for availability checks and calendar requests. Default: %d', 'wp-bnb' ),
$defaults['availability']
);
?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Booking Endpoints', 'wp-bnb' ); ?></th>
<td>
<input type="number" name="wp_bnb_rate_limit_booking" value="<?php echo esc_attr( $limit_booking ); ?>" min="1" max="100" class="small-text">
<?php esc_html_e( 'requests per window', 'wp-bnb' ); ?>
<p class="description">
<?php
printf(
/* translators: %d: default limit */
esc_html__( 'Limit for booking creation. Keep low to prevent abuse. Default: %d', 'wp-bnb' ),
$defaults['booking']
);
?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Admin Endpoints', 'wp-bnb' ); ?></th>
<td>
<input type="number" name="wp_bnb_rate_limit_admin" value="<?php echo esc_attr( $limit_admin ); ?>" min="1" max="1000" class="small-text">
<?php esc_html_e( 'requests per window', 'wp-bnb' ); ?>
<p class="description">
<?php
printf(
/* translators: %d: default limit */
esc_html__( 'Limit for authenticated admin endpoints. Default: %d', 'wp-bnb' ),
$defaults['admin']
);
?>
</p>
</td>
</tr> </tr>
</thead>
<tbody>
<tr><td>GET</td><td><code>/buildings</code></td><td><?php esc_html_e( 'List all buildings', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/buildings/{id}</code></td><td><?php esc_html_e( 'Get building details', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/buildings/{id}/rooms</code></td><td><?php esc_html_e( 'List rooms in building', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/rooms</code></td><td><?php esc_html_e( 'List/search rooms', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/rooms/{id}</code></td><td><?php esc_html_e( 'Get room details', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/rooms/{id}/availability</code></td><td><?php esc_html_e( 'Check room availability', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/rooms/{id}/calendar</code></td><td><?php esc_html_e( 'Get room calendar', 'wp-bnb' ); ?></td></tr>
<tr><td>POST</td><td><code>/availability/search</code></td><td><?php esc_html_e( 'Search available rooms', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/services</code></td><td><?php esc_html_e( 'List services', 'wp-bnb' ); ?></td></tr>
<tr><td>POST</td><td><code>/pricing/calculate</code></td><td><?php esc_html_e( 'Calculate booking price', 'wp-bnb' ); ?></td></tr>
<tr><td>POST</td><td><code>/bookings</code></td><td><?php esc_html_e( 'Create booking (pending status)', 'wp-bnb' ); ?></td></tr>
</tbody>
</table> </table>
<h3><?php esc_html_e( 'Admin Endpoints (Requires Authentication)', 'wp-bnb' ); ?></h3> <h2 style="margin-top: 30px;"><?php esc_html_e( 'Current Rate Limits Summary', 'wp-bnb' ); ?></h2>
<table class="widefat" style="margin-bottom: 20px;"> <table class="widefat striped" style="max-width: 600px;">
<thead>
<tr>
<th><?php esc_html_e( 'Method', 'wp-bnb' ); ?></th>
<th><?php esc_html_e( 'Endpoint', 'wp-bnb' ); ?></th>
<th><?php esc_html_e( 'Description', 'wp-bnb' ); ?></th>
</tr>
</thead>
<tbody>
<tr><td>GET</td><td><code>/bookings</code></td><td><?php esc_html_e( 'List all bookings', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/bookings/{id}</code></td><td><?php esc_html_e( 'Get booking details', 'wp-bnb' ); ?></td></tr>
<tr><td>PATCH</td><td><code>/bookings/{id}</code></td><td><?php esc_html_e( 'Update booking', 'wp-bnb' ); ?></td></tr>
<tr><td>DELETE</td><td><code>/bookings/{id}</code></td><td><?php esc_html_e( 'Cancel booking', 'wp-bnb' ); ?></td></tr>
<tr><td>POST</td><td><code>/bookings/{id}/confirm</code></td><td><?php esc_html_e( 'Confirm booking', 'wp-bnb' ); ?></td></tr>
<tr><td>POST</td><td><code>/bookings/{id}/check-in</code></td><td><?php esc_html_e( 'Check in guest', 'wp-bnb' ); ?></td></tr>
<tr><td>POST</td><td><code>/bookings/{id}/check-out</code></td><td><?php esc_html_e( 'Check out guest', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/guests</code></td><td><?php esc_html_e( 'List guests', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/guests/{id}</code></td><td><?php esc_html_e( 'Get guest details', 'wp-bnb' ); ?></td></tr>
<tr><td>GET</td><td><code>/guests/search</code></td><td><?php esc_html_e( 'Search guests', 'wp-bnb' ); ?></td></tr>
</tbody>
</table>
<h2 style="margin-top: 30px;"><?php esc_html_e( 'Authentication', 'wp-bnb' ); ?></h2>
<p><?php esc_html_e( 'Admin endpoints require authentication. Use one of the following methods:', 'wp-bnb' ); ?></p>
<ul style="list-style: disc; margin-left: 20px;">
<li><strong><?php esc_html_e( 'Application Passwords:', 'wp-bnb' ); ?></strong> <?php esc_html_e( 'Create one in Users > Your Profile. Use Basic Auth with username and app password.', 'wp-bnb' ); ?></li>
<li><strong><?php esc_html_e( 'Cookie + Nonce:', 'wp-bnb' ); ?></strong> <?php esc_html_e( 'For same-domain requests. Pass nonce in X-WP-Nonce header.', 'wp-bnb' ); ?></li>
</ul>
<h2 style="margin-top: 30px;"><?php esc_html_e( 'Rate Limits', 'wp-bnb' ); ?></h2>
<table class="widefat" style="margin-bottom: 20px;">
<thead> <thead>
<tr> <tr>
<th><?php esc_html_e( 'Type', 'wp-bnb' ); ?></th> <th><?php esc_html_e( 'Type', 'wp-bnb' ); ?></th>
@@ -1591,16 +1667,112 @@ final class Plugin {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr><td><?php esc_html_e( 'Public', 'wp-bnb' ); ?></td><td>60/min</td><td><?php esc_html_e( 'GET rooms, buildings, services', 'wp-bnb' ); ?></td></tr> <tr>
<tr><td><?php esc_html_e( 'Availability', 'wp-bnb' ); ?></td><td>30/min</td><td><?php esc_html_e( 'Availability checks, calendar', 'wp-bnb' ); ?></td></tr> <td><?php esc_html_e( 'Public', 'wp-bnb' ); ?></td>
<tr><td><?php esc_html_e( 'Booking', 'wp-bnb' ); ?></td><td>10/min</td><td><?php esc_html_e( 'Booking creation', 'wp-bnb' ); ?></td></tr> <td><strong><?php echo esc_html( $limit_public . '/' . $limit_window . 's' ); ?></strong></td>
<tr><td><?php esc_html_e( 'Admin', 'wp-bnb' ); ?></td><td>120/min</td><td><?php esc_html_e( 'All admin endpoints', 'wp-bnb' ); ?></td></tr> <td><?php esc_html_e( 'GET rooms, buildings, services', 'wp-bnb' ); ?></td>
</tr>
<tr>
<td><?php esc_html_e( 'Availability', 'wp-bnb' ); ?></td>
<td><strong><?php echo esc_html( $limit_avail . '/' . $limit_window . 's' ); ?></strong></td>
<td><?php esc_html_e( 'Availability checks, calendar', 'wp-bnb' ); ?></td>
</tr>
<tr>
<td><?php esc_html_e( 'Booking', 'wp-bnb' ); ?></td>
<td><strong><?php echo esc_html( $limit_booking . '/' . $limit_window . 's' ); ?></strong></td>
<td><?php esc_html_e( 'Booking creation', 'wp-bnb' ); ?></td>
</tr>
<tr>
<td><?php esc_html_e( 'Admin', 'wp-bnb' ); ?></td>
<td><strong><?php echo esc_html( $limit_admin . '/' . $limit_window . 's' ); ?></strong></td>
<td><?php esc_html_e( 'All admin endpoints', 'wp-bnb' ); ?></td>
</tr>
</tbody> </tbody>
</table> </table>
<p class="submit"> <?php submit_button( __( 'Save Rate Limits', 'wp-bnb' ) ); ?>
<?php submit_button( __( 'Save API Settings', 'wp-bnb' ), 'primary', 'submit', false ); ?>
</p> <?php else : ?>
<!-- Endpoints Subtab -->
<h2><?php esc_html_e( 'Available Endpoints', 'wp-bnb' ); ?></h2>
<h3><?php esc_html_e( 'Public Endpoints', 'wp-bnb' ); ?></h3>
<p class="description"><?php esc_html_e( 'These endpoints are accessible without authentication.', 'wp-bnb' ); ?></p>
<table class="widefat striped" style="margin-bottom: 20px;">
<thead>
<tr>
<th style="width: 80px;"><?php esc_html_e( 'Method', 'wp-bnb' ); ?></th>
<th style="width: 250px;"><?php esc_html_e( 'Endpoint', 'wp-bnb' ); ?></th>
<th><?php esc_html_e( 'Description', 'wp-bnb' ); ?></th>
</tr>
</thead>
<tbody>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/buildings</code></td><td><?php esc_html_e( 'List all buildings', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/buildings/{id}</code></td><td><?php esc_html_e( 'Get building details', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/buildings/{id}/rooms</code></td><td><?php esc_html_e( 'List rooms in building', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/rooms</code></td><td><?php esc_html_e( 'List/search rooms', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/rooms/{id}</code></td><td><?php esc_html_e( 'Get room details', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/rooms/{id}/availability</code></td><td><?php esc_html_e( 'Check room availability', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/rooms/{id}/calendar</code></td><td><?php esc_html_e( 'Get room calendar', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method post">POST</span></td><td><code>/availability/search</code></td><td><?php esc_html_e( 'Search available rooms', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/services</code></td><td><?php esc_html_e( 'List services', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method post">POST</span></td><td><code>/pricing/calculate</code></td><td><?php esc_html_e( 'Calculate booking price', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method post">POST</span></td><td><code>/bookings</code></td><td><?php esc_html_e( 'Create booking (pending status)', 'wp-bnb' ); ?></td></tr>
</tbody>
</table>
<h3><?php esc_html_e( 'Admin Endpoints', 'wp-bnb' ); ?></h3>
<p class="description"><?php esc_html_e( 'These endpoints require authentication (Application Password or Cookie + Nonce).', 'wp-bnb' ); ?></p>
<table class="widefat striped" style="margin-bottom: 20px;">
<thead>
<tr>
<th style="width: 80px;"><?php esc_html_e( 'Method', 'wp-bnb' ); ?></th>
<th style="width: 250px;"><?php esc_html_e( 'Endpoint', 'wp-bnb' ); ?></th>
<th><?php esc_html_e( 'Description', 'wp-bnb' ); ?></th>
</tr>
</thead>
<tbody>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/bookings</code></td><td><?php esc_html_e( 'List all bookings', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/bookings/{id}</code></td><td><?php esc_html_e( 'Get booking details', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method patch">PATCH</span></td><td><code>/bookings/{id}</code></td><td><?php esc_html_e( 'Update booking', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method delete">DELETE</span></td><td><code>/bookings/{id}</code></td><td><?php esc_html_e( 'Cancel booking', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method post">POST</span></td><td><code>/bookings/{id}/confirm</code></td><td><?php esc_html_e( 'Confirm booking', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method post">POST</span></td><td><code>/bookings/{id}/check-in</code></td><td><?php esc_html_e( 'Check in guest', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method post">POST</span></td><td><code>/bookings/{id}/check-out</code></td><td><?php esc_html_e( 'Check out guest', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/guests</code></td><td><?php esc_html_e( 'List guests', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/guests/{id}</code></td><td><?php esc_html_e( 'Get guest details', 'wp-bnb' ); ?></td></tr>
<tr><td><span class="wp-bnb-method get">GET</span></td><td><code>/guests/search</code></td><td><?php esc_html_e( 'Search guests', 'wp-bnb' ); ?></td></tr>
</tbody>
</table>
<h2 style="margin-top: 30px;"><?php esc_html_e( 'Authentication', 'wp-bnb' ); ?></h2>
<p><?php esc_html_e( 'Admin endpoints require authentication. Use one of the following methods:', 'wp-bnb' ); ?></p>
<table class="form-table">
<tr>
<th scope="row">
<span class="dashicons dashicons-admin-network" style="color: #2271b1;"></span>
<?php esc_html_e( 'Application Passwords', 'wp-bnb' ); ?>
</th>
<td>
<p><?php esc_html_e( 'Create an Application Password in Users > Your Profile.', 'wp-bnb' ); ?></p>
<p><code>Authorization: Basic base64(username:app-password)</code></p>
<p class="description"><?php esc_html_e( 'Recommended for external applications and integrations.', 'wp-bnb' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<span class="dashicons dashicons-lock" style="color: #2271b1;"></span>
<?php esc_html_e( 'Cookie + Nonce', 'wp-bnb' ); ?>
</th>
<td>
<p><?php esc_html_e( 'For same-domain JavaScript requests when user is logged in.', 'wp-bnb' ); ?></p>
<p><code>X-WP-Nonce: <?php echo esc_html( wp_create_nonce( 'wp_rest' ) ); ?></code></p>
<p class="description"><?php esc_html_e( 'Best for frontend JavaScript that interacts with the API.', 'wp-bnb' ); ?></p>
</td>
</tr>
</table>
<?php endif; ?>
</form> </form>
<?php <?php
} }
@@ -1836,6 +2008,31 @@ final class Plugin {
update_option( 'wp_bnb_api_enabled', $api_enabled ); update_option( 'wp_bnb_api_enabled', $api_enabled );
update_option( 'wp_bnb_api_rate_limiting', $rate_limiting ); update_option( 'wp_bnb_api_rate_limiting', $rate_limiting );
// Save rate limit configuration.
$defaults = \Magdev\WpBnb\Api\RateLimiter::get_default_limits();
$limit_window = isset( $_POST['wp_bnb_rate_limit_window'] )
? max( 10, min( 300, absint( $_POST['wp_bnb_rate_limit_window'] ) ) )
: 60;
$limit_public = isset( $_POST['wp_bnb_rate_limit_public'] )
? max( 1, min( 1000, absint( $_POST['wp_bnb_rate_limit_public'] ) ) )
: $defaults['public'];
$limit_avail = isset( $_POST['wp_bnb_rate_limit_availability'] )
? max( 1, min( 1000, absint( $_POST['wp_bnb_rate_limit_availability'] ) ) )
: $defaults['availability'];
$limit_booking = isset( $_POST['wp_bnb_rate_limit_booking'] )
? max( 1, min( 100, absint( $_POST['wp_bnb_rate_limit_booking'] ) ) )
: $defaults['booking'];
$limit_admin = isset( $_POST['wp_bnb_rate_limit_admin'] )
? max( 1, min( 1000, absint( $_POST['wp_bnb_rate_limit_admin'] ) ) )
: $defaults['admin'];
update_option( 'wp_bnb_rate_limit_window', $limit_window );
update_option( 'wp_bnb_rate_limit_public', $limit_public );
update_option( 'wp_bnb_rate_limit_availability', $limit_avail );
update_option( 'wp_bnb_rate_limit_booking', $limit_booking );
update_option( 'wp_bnb_rate_limit_admin', $limit_admin );
add_settings_error( 'wp_bnb_settings', 'settings_saved', __( 'API settings saved.', 'wp-bnb' ), 'success' ); add_settings_error( 'wp_bnb_settings', 'settings_saved', __( 'API settings saved.', 'wp-bnb' ), 'success' );
settings_errors( 'wp_bnb_settings' ); settings_errors( 'wp_bnb_settings' );
} }