mcp-wordpress 2.5.1 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/dist/src/cache/CacheInvalidation.d.ts +0 -120
- package/dist/src/cache/CacheInvalidation.d.ts.map +0 -1
- package/dist/src/cache/CacheInvalidation.js +0 -355
- package/dist/src/cache/CacheInvalidation.js.map +0 -1
- package/dist/src/cache/CacheManager.d.ts +0 -149
- package/dist/src/cache/CacheManager.d.ts.map +0 -1
- package/dist/src/cache/CacheManager.js +0 -326
- package/dist/src/cache/CacheManager.js.map +0 -1
- package/dist/src/cache/HttpCacheWrapper.d.ts +0 -122
- package/dist/src/cache/HttpCacheWrapper.d.ts.map +0 -1
- package/dist/src/cache/HttpCacheWrapper.js +0 -283
- package/dist/src/cache/HttpCacheWrapper.js.map +0 -1
- package/dist/src/cache/index.d.ts +0 -12
- package/dist/src/cache/index.d.ts.map +0 -1
- package/dist/src/cache/index.js +0 -9
- package/dist/src/cache/index.js.map +0 -1
- package/dist/src/client/CachedWordPressClient.d.ts +0 -174
- package/dist/src/client/CachedWordPressClient.d.ts.map +0 -1
- package/dist/src/client/CachedWordPressClient.js +0 -345
- package/dist/src/client/CachedWordPressClient.js.map +0 -1
- package/dist/src/client/MockWordPressClient.d.ts +0 -56
- package/dist/src/client/MockWordPressClient.d.ts.map +0 -1
- package/dist/src/client/MockWordPressClient.js +0 -371
- package/dist/src/client/MockWordPressClient.js.map +0 -1
- package/dist/src/client/api.d.ts +0 -235
- package/dist/src/client/api.d.ts.map +0 -1
- package/dist/src/client/api.js +0 -896
- package/dist/src/client/api.js.map +0 -1
- package/dist/src/client/auth.d.ts +0 -121
- package/dist/src/client/auth.d.ts.map +0 -1
- package/dist/src/client/auth.js +0 -429
- package/dist/src/client/auth.js.map +0 -1
- package/dist/src/client/managers/AuthenticationManager.d.ts +0 -92
- package/dist/src/client/managers/AuthenticationManager.d.ts.map +0 -1
- package/dist/src/client/managers/AuthenticationManager.js +0 -369
- package/dist/src/client/managers/AuthenticationManager.js.map +0 -1
- package/dist/src/client/managers/BaseManager.d.ts +0 -22
- package/dist/src/client/managers/BaseManager.d.ts.map +0 -1
- package/dist/src/client/managers/BaseManager.js +0 -53
- package/dist/src/client/managers/BaseManager.js.map +0 -1
- package/dist/src/client/managers/RequestManager.d.ts +0 -47
- package/dist/src/client/managers/RequestManager.d.ts.map +0 -1
- package/dist/src/client/managers/RequestManager.js +0 -193
- package/dist/src/client/managers/RequestManager.js.map +0 -1
- package/dist/src/client/managers/index.d.ts +0 -8
- package/dist/src/client/managers/index.d.ts.map +0 -1
- package/dist/src/client/managers/index.js +0 -8
- package/dist/src/client/managers/index.js.map +0 -1
- package/dist/src/config/Config.d.ts +0 -155
- package/dist/src/config/Config.d.ts.map +0 -1
- package/dist/src/config/Config.js +0 -215
- package/dist/src/config/Config.js.map +0 -1
- package/dist/src/config/ConfigurationSchema.d.ts +0 -281
- package/dist/src/config/ConfigurationSchema.d.ts.map +0 -1
- package/dist/src/config/ConfigurationSchema.js +0 -205
- package/dist/src/config/ConfigurationSchema.js.map +0 -1
- package/dist/src/config/ServerConfiguration.d.ts +0 -47
- package/dist/src/config/ServerConfiguration.d.ts.map +0 -1
- package/dist/src/config/ServerConfiguration.js +0 -255
- package/dist/src/config/ServerConfiguration.js.map +0 -1
- package/dist/src/docs/DocumentationGenerator.d.ts +0 -185
- package/dist/src/docs/DocumentationGenerator.d.ts.map +0 -1
- package/dist/src/docs/DocumentationGenerator.js +0 -777
- package/dist/src/docs/DocumentationGenerator.js.map +0 -1
- package/dist/src/docs/MarkdownFormatter.d.ts +0 -84
- package/dist/src/docs/MarkdownFormatter.d.ts.map +0 -1
- package/dist/src/docs/MarkdownFormatter.js +0 -458
- package/dist/src/docs/MarkdownFormatter.js.map +0 -1
- package/dist/src/docs/index.d.ts +0 -8
- package/dist/src/docs/index.d.ts.map +0 -1
- package/dist/src/docs/index.js +0 -7
- package/dist/src/docs/index.js.map +0 -1
- package/dist/src/dxt-entry.d.ts +0 -6
- package/dist/src/dxt-entry.d.ts.map +0 -1
- package/dist/src/dxt-entry.js +0 -39
- package/dist/src/dxt-entry.js.map +0 -1
- package/dist/src/index.d.ts +0 -18
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -143
- package/dist/src/index.js.map +0 -1
- package/dist/src/performance/MetricsCollector.d.ts +0 -145
- package/dist/src/performance/MetricsCollector.d.ts.map +0 -1
- package/dist/src/performance/MetricsCollector.js +0 -368
- package/dist/src/performance/MetricsCollector.js.map +0 -1
- package/dist/src/performance/PerformanceAnalytics.d.ts +0 -168
- package/dist/src/performance/PerformanceAnalytics.d.ts.map +0 -1
- package/dist/src/performance/PerformanceAnalytics.js +0 -570
- package/dist/src/performance/PerformanceAnalytics.js.map +0 -1
- package/dist/src/performance/PerformanceMonitor.d.ts +0 -203
- package/dist/src/performance/PerformanceMonitor.d.ts.map +0 -1
- package/dist/src/performance/PerformanceMonitor.js +0 -478
- package/dist/src/performance/PerformanceMonitor.js.map +0 -1
- package/dist/src/performance/index.d.ts +0 -11
- package/dist/src/performance/index.d.ts.map +0 -1
- package/dist/src/performance/index.js +0 -8
- package/dist/src/performance/index.js.map +0 -1
- package/dist/src/security/AISecurityScanner.d.ts +0 -176
- package/dist/src/security/AISecurityScanner.d.ts.map +0 -1
- package/dist/src/security/AISecurityScanner.js +0 -655
- package/dist/src/security/AISecurityScanner.js.map +0 -1
- package/dist/src/security/AutomatedRemediation.d.ts +0 -146
- package/dist/src/security/AutomatedRemediation.d.ts.map +0 -1
- package/dist/src/security/AutomatedRemediation.js +0 -566
- package/dist/src/security/AutomatedRemediation.js.map +0 -1
- package/dist/src/security/InputValidator.d.ts +0 -219
- package/dist/src/security/InputValidator.d.ts.map +0 -1
- package/dist/src/security/InputValidator.js +0 -295
- package/dist/src/security/InputValidator.js.map +0 -1
- package/dist/src/security/SecurityCIPipeline.d.ts +0 -213
- package/dist/src/security/SecurityCIPipeline.d.ts.map +0 -1
- package/dist/src/security/SecurityCIPipeline.js +0 -693
- package/dist/src/security/SecurityCIPipeline.js.map +0 -1
- package/dist/src/security/SecurityConfig.d.ts +0 -129
- package/dist/src/security/SecurityConfig.d.ts.map +0 -1
- package/dist/src/security/SecurityConfig.js +0 -266
- package/dist/src/security/SecurityConfig.js.map +0 -1
- package/dist/src/security/SecurityConfigManager.d.ts +0 -294
- package/dist/src/security/SecurityConfigManager.d.ts.map +0 -1
- package/dist/src/security/SecurityConfigManager.js +0 -558
- package/dist/src/security/SecurityConfigManager.js.map +0 -1
- package/dist/src/security/SecurityMonitoring.d.ts +0 -245
- package/dist/src/security/SecurityMonitoring.d.ts.map +0 -1
- package/dist/src/security/SecurityMonitoring.js +0 -598
- package/dist/src/security/SecurityMonitoring.js.map +0 -1
- package/dist/src/security/SecurityReviewer.d.ts +0 -168
- package/dist/src/security/SecurityReviewer.d.ts.map +0 -1
- package/dist/src/security/SecurityReviewer.js +0 -686
- package/dist/src/security/SecurityReviewer.js.map +0 -1
- package/dist/src/security/index.d.ts +0 -183
- package/dist/src/security/index.d.ts.map +0 -1
- package/dist/src/security/index.js +0 -218
- package/dist/src/security/index.js.map +0 -1
- package/dist/src/server/ConnectionTester.d.ts +0 -32
- package/dist/src/server/ConnectionTester.d.ts.map +0 -1
- package/dist/src/server/ConnectionTester.js +0 -135
- package/dist/src/server/ConnectionTester.js.map +0 -1
- package/dist/src/server/ToolRegistry.d.ts +0 -50
- package/dist/src/server/ToolRegistry.d.ts.map +0 -1
- package/dist/src/server/ToolRegistry.js +0 -219
- package/dist/src/server/ToolRegistry.js.map +0 -1
- package/dist/src/server.d.ts +0 -7
- package/dist/src/server.d.ts.map +0 -1
- package/dist/src/server.js +0 -7
- package/dist/src/server.js.map +0 -1
- package/dist/src/tools/BaseToolManager.d.ts +0 -62
- package/dist/src/tools/BaseToolManager.d.ts.map +0 -1
- package/dist/src/tools/BaseToolManager.js +0 -195
- package/dist/src/tools/BaseToolManager.js.map +0 -1
- package/dist/src/tools/auth.d.ts +0 -50
- package/dist/src/tools/auth.d.ts.map +0 -1
- package/dist/src/tools/auth.js +0 -133
- package/dist/src/tools/auth.js.map +0 -1
- package/dist/src/tools/cache.d.ts +0 -260
- package/dist/src/tools/cache.d.ts.map +0 -1
- package/dist/src/tools/cache.js +0 -232
- package/dist/src/tools/cache.js.map +0 -1
- package/dist/src/tools/comments.d.ts +0 -33
- package/dist/src/tools/comments.d.ts.map +0 -1
- package/dist/src/tools/comments.js +0 -235
- package/dist/src/tools/comments.js.map +0 -1
- package/dist/src/tools/index.d.ts +0 -11
- package/dist/src/tools/index.d.ts.map +0 -1
- package/dist/src/tools/index.js +0 -11
- package/dist/src/tools/index.js.map +0 -1
- package/dist/src/tools/media.d.ts +0 -70
- package/dist/src/tools/media.d.ts.map +0 -1
- package/dist/src/tools/media.js +0 -248
- package/dist/src/tools/media.js.map +0 -1
- package/dist/src/tools/pages.d.ts +0 -32
- package/dist/src/tools/pages.d.ts.map +0 -1
- package/dist/src/tools/pages.js +0 -215
- package/dist/src/tools/pages.js.map +0 -1
- package/dist/src/tools/performance.d.ts +0 -73
- package/dist/src/tools/performance.d.ts.map +0 -1
- package/dist/src/tools/performance.js +0 -922
- package/dist/src/tools/performance.js.map +0 -1
- package/dist/src/tools/posts/PostHandlers.d.ts +0 -46
- package/dist/src/tools/posts/PostHandlers.d.ts.map +0 -1
- package/dist/src/tools/posts/PostHandlers.js +0 -400
- package/dist/src/tools/posts/PostHandlers.js.map +0 -1
- package/dist/src/tools/posts/PostToolDefinitions.d.ts +0 -37
- package/dist/src/tools/posts/PostToolDefinitions.d.ts.map +0 -1
- package/dist/src/tools/posts/PostToolDefinitions.js +0 -236
- package/dist/src/tools/posts/PostToolDefinitions.js.map +0 -1
- package/dist/src/tools/posts/index.d.ts +0 -138
- package/dist/src/tools/posts/index.d.ts.map +0 -1
- package/dist/src/tools/posts/index.js +0 -163
- package/dist/src/tools/posts/index.js.map +0 -1
- package/dist/src/tools/posts.d.ts +0 -15
- package/dist/src/tools/posts.d.ts.map +0 -1
- package/dist/src/tools/posts.js +0 -16
- package/dist/src/tools/posts.js.map +0 -1
- package/dist/src/tools/site.d.ts +0 -32
- package/dist/src/tools/site.d.ts.map +0 -1
- package/dist/src/tools/site.js +0 -234
- package/dist/src/tools/site.js.map +0 -1
- package/dist/src/tools/taxonomies.d.ts +0 -36
- package/dist/src/tools/taxonomies.d.ts.map +0 -1
- package/dist/src/tools/taxonomies.js +0 -286
- package/dist/src/tools/taxonomies.js.map +0 -1
- package/dist/src/tools/users.d.ts +0 -33
- package/dist/src/tools/users.d.ts.map +0 -1
- package/dist/src/tools/users.js +0 -308
- package/dist/src/tools/users.js.map +0 -1
- package/dist/src/types/client.d.ts +0 -223
- package/dist/src/types/client.d.ts.map +0 -1
- package/dist/src/types/client.js +0 -97
- package/dist/src/types/client.js.map +0 -1
- package/dist/src/types/enhanced.d.ts +0 -237
- package/dist/src/types/enhanced.d.ts.map +0 -1
- package/dist/src/types/enhanced.js +0 -49
- package/dist/src/types/enhanced.js.map +0 -1
- package/dist/src/types/index.d.ts +0 -160
- package/dist/src/types/index.d.ts.map +0 -1
- package/dist/src/types/index.js +0 -14
- package/dist/src/types/index.js.map +0 -1
- package/dist/src/types/mcp.d.ts +0 -178
- package/dist/src/types/mcp.d.ts.map +0 -1
- package/dist/src/types/mcp.js +0 -7
- package/dist/src/types/mcp.js.map +0 -1
- package/dist/src/types/requests.d.ts +0 -322
- package/dist/src/types/requests.d.ts.map +0 -1
- package/dist/src/types/requests.js +0 -8
- package/dist/src/types/requests.js.map +0 -1
- package/dist/src/types/tools.d.ts +0 -506
- package/dist/src/types/tools.d.ts.map +0 -1
- package/dist/src/types/tools.js +0 -8
- package/dist/src/types/tools.js.map +0 -1
- package/dist/src/types/wordpress.d.ts +0 -471
- package/dist/src/types/wordpress.d.ts.map +0 -1
- package/dist/src/types/wordpress.js +0 -14
- package/dist/src/types/wordpress.js.map +0 -1
- package/dist/src/utils/debug.d.ts +0 -71
- package/dist/src/utils/debug.d.ts.map +0 -1
- package/dist/src/utils/debug.js +0 -235
- package/dist/src/utils/debug.js.map +0 -1
- package/dist/src/utils/enhancedError.d.ts +0 -61
- package/dist/src/utils/enhancedError.d.ts.map +0 -1
- package/dist/src/utils/enhancedError.js +0 -221
- package/dist/src/utils/enhancedError.js.map +0 -1
- package/dist/src/utils/error.d.ts +0 -17
- package/dist/src/utils/error.d.ts.map +0 -1
- package/dist/src/utils/error.js +0 -108
- package/dist/src/utils/error.js.map +0 -1
- package/dist/src/utils/logger.d.ts +0 -106
- package/dist/src/utils/logger.d.ts.map +0 -1
- package/dist/src/utils/logger.js +0 -280
- package/dist/src/utils/logger.js.map +0 -1
- package/dist/src/utils/streaming.d.ts +0 -104
- package/dist/src/utils/streaming.d.ts.map +0 -1
- package/dist/src/utils/streaming.js +0 -331
- package/dist/src/utils/streaming.js.map +0 -1
- package/dist/src/utils/toolWrapper.d.ts +0 -42
- package/dist/src/utils/toolWrapper.d.ts.map +0 -1
- package/dist/src/utils/toolWrapper.js +0 -101
- package/dist/src/utils/toolWrapper.js.map +0 -1
- package/dist/src/utils/validation/core.d.ts +0 -21
- package/dist/src/utils/validation/core.d.ts.map +0 -1
- package/dist/src/utils/validation/core.js +0 -71
- package/dist/src/utils/validation/core.js.map +0 -1
- package/dist/src/utils/validation/index.d.ts +0 -25
- package/dist/src/utils/validation/index.d.ts.map +0 -1
- package/dist/src/utils/validation/index.js +0 -29
- package/dist/src/utils/validation/index.js.map +0 -1
- package/dist/src/utils/validation/network.d.ts +0 -19
- package/dist/src/utils/validation/network.d.ts.map +0 -1
- package/dist/src/utils/validation/network.js +0 -93
- package/dist/src/utils/validation/network.js.map +0 -1
- package/dist/src/utils/validation/rateLimit.d.ts +0 -21
- package/dist/src/utils/validation/rateLimit.d.ts.map +0 -1
- package/dist/src/utils/validation/rateLimit.js +0 -43
- package/dist/src/utils/validation/rateLimit.js.map +0 -1
- package/dist/src/utils/validation/security.d.ts +0 -29
- package/dist/src/utils/validation/security.d.ts.map +0 -1
- package/dist/src/utils/validation/security.js +0 -327
- package/dist/src/utils/validation/security.js.map +0 -1
- package/dist/src/utils/validation/wordpress.d.ts +0 -31
- package/dist/src/utils/validation/wordpress.d.ts.map +0 -1
- package/dist/src/utils/validation/wordpress.js +0 -146
- package/dist/src/utils/validation/wordpress.js.map +0 -1
- package/dist/src/utils/validation.d.ts +0 -15
- package/dist/src/utils/validation.d.ts.map +0 -1
- package/dist/src/utils/validation.js +0 -27
- package/dist/src/utils/validation.js.map +0 -1
- package/dist/tests/vitest.setup.d.ts +0 -6
- package/dist/tests/vitest.setup.d.ts.map +0 -1
- package/dist/tests/vitest.setup.js +0 -39
- package/dist/tests/vitest.setup.js.map +0 -1
package/dist/src/client/api.js
DELETED
|
@@ -1,896 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WordPress API Client
|
|
3
|
-
* Handles all REST API communication with WordPress
|
|
4
|
-
*/
|
|
5
|
-
// Use native fetch in Node.js 18+
|
|
6
|
-
import FormData from "form-data";
|
|
7
|
-
import * as fs from "fs";
|
|
8
|
-
import * as path from "path";
|
|
9
|
-
import { WordPressAPIError, AuthenticationError, RateLimitError } from "../types/client.js";
|
|
10
|
-
import { config } from "../config/Config.js";
|
|
11
|
-
import { debug, logError, startTimer } from "../utils/debug.js";
|
|
12
|
-
/**
|
|
13
|
-
* WordPress REST API Client
|
|
14
|
-
*
|
|
15
|
-
* A comprehensive client for interacting with the WordPress REST API v2.
|
|
16
|
-
* Provides full CRUD operations for posts, pages, media, users, comments,
|
|
17
|
-
* categories, tags, and site settings with robust error handling and performance optimization.
|
|
18
|
-
*
|
|
19
|
-
* Features:
|
|
20
|
-
* - Multiple authentication methods (App Passwords, JWT, Basic Auth, API Key)
|
|
21
|
-
* - Automatic retry logic with exponential backoff
|
|
22
|
-
* - Request rate limiting and queue management
|
|
23
|
-
* - Comprehensive error handling with detailed messages
|
|
24
|
-
* - Performance monitoring and request statistics
|
|
25
|
-
* - Caching support for improved performance
|
|
26
|
-
* - Multi-site configuration support
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```typescript
|
|
30
|
-
* // Initialize with app password authentication
|
|
31
|
-
* const client = new WordPressClient({
|
|
32
|
-
* baseUrl: 'https://mysite.com',
|
|
33
|
-
* auth: {
|
|
34
|
-
* method: 'app-password',
|
|
35
|
-
* username: 'admin',
|
|
36
|
-
* password: 'xxxx xxxx xxxx xxxx xxxx xxxx'
|
|
37
|
-
* }
|
|
38
|
-
* });
|
|
39
|
-
*
|
|
40
|
-
* // Create a new post
|
|
41
|
-
* const post = await client.createPost({
|
|
42
|
-
* title: 'My New Post',
|
|
43
|
-
* content: '<p>This is the content</p>',
|
|
44
|
-
* status: 'publish'
|
|
45
|
-
* });
|
|
46
|
-
*
|
|
47
|
-
* // List posts with filtering
|
|
48
|
-
* const posts = await client.getPosts({
|
|
49
|
-
* search: 'WordPress',
|
|
50
|
-
* status: 'publish',
|
|
51
|
-
* per_page: 10
|
|
52
|
-
* });
|
|
53
|
-
* ```
|
|
54
|
-
*
|
|
55
|
-
* @since 1.0.0
|
|
56
|
-
* @author MCP WordPress Team
|
|
57
|
-
* @implements {IWordPressClient}
|
|
58
|
-
*/
|
|
59
|
-
export class WordPressClient {
|
|
60
|
-
baseUrl;
|
|
61
|
-
apiUrl;
|
|
62
|
-
timeout;
|
|
63
|
-
maxRetries;
|
|
64
|
-
auth;
|
|
65
|
-
requestQueue = [];
|
|
66
|
-
lastRequestTime = 0;
|
|
67
|
-
requestInterval;
|
|
68
|
-
authenticated = false;
|
|
69
|
-
jwtToken = null;
|
|
70
|
-
_stats;
|
|
71
|
-
/**
|
|
72
|
-
* Creates a new WordPress API client instance.
|
|
73
|
-
*
|
|
74
|
-
* Initializes the client with configuration options for connecting to a WordPress site.
|
|
75
|
-
* Supports multiple authentication methods and automatic environment variable detection.
|
|
76
|
-
*
|
|
77
|
-
* @param {Partial<WordPressClientConfig>} [options={}] - Configuration options for the client
|
|
78
|
-
* @param {string} [options.baseUrl] - WordPress site URL (falls back to WORDPRESS_SITE_URL env var)
|
|
79
|
-
* @param {number} [options.timeout=30000] - Request timeout in milliseconds
|
|
80
|
-
* @param {number} [options.maxRetries=3] - Maximum number of retry attempts for failed requests
|
|
81
|
-
* @param {AuthConfig} [options.auth] - Authentication configuration (auto-detected from env if not provided)
|
|
82
|
-
* @param {boolean} [options.enableCache=true] - Whether to enable response caching
|
|
83
|
-
* @param {number} [options.cacheMaxAge=300000] - Cache max age in milliseconds (5 minutes default)
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* ```typescript
|
|
87
|
-
* // Basic configuration with app password
|
|
88
|
-
* const client = new WordPressClient({
|
|
89
|
-
* baseUrl: 'https://mysite.com',
|
|
90
|
-
* auth: {
|
|
91
|
-
* method: 'app-password',
|
|
92
|
-
* username: 'admin',
|
|
93
|
-
* password: 'xxxx xxxx xxxx xxxx xxxx xxxx'
|
|
94
|
-
* }
|
|
95
|
-
* });
|
|
96
|
-
*
|
|
97
|
-
* // Configuration with environment variables
|
|
98
|
-
* // Set WORDPRESS_SITE_URL, WORDPRESS_USERNAME, WORDPRESS_APP_PASSWORD
|
|
99
|
-
* const client = new WordPressClient(); // Auto-detects from env
|
|
100
|
-
*
|
|
101
|
-
* // Custom timeout and retry settings
|
|
102
|
-
* const client = new WordPressClient({
|
|
103
|
-
* baseUrl: 'https://mysite.com',
|
|
104
|
-
* timeout: 60000, // 60 seconds
|
|
105
|
-
* maxRetries: 5, // 5 retry attempts
|
|
106
|
-
* auth: { method: 'app-password', username: 'user', password: 'pass' }
|
|
107
|
-
* });
|
|
108
|
-
* ```
|
|
109
|
-
*
|
|
110
|
-
* @throws {Error} When required configuration is missing or invalid
|
|
111
|
-
*
|
|
112
|
-
* @since 1.0.0
|
|
113
|
-
*/
|
|
114
|
-
constructor(options = {}) {
|
|
115
|
-
const cfg = config();
|
|
116
|
-
const baseUrl = options.baseUrl || cfg.wordpress.siteUrl || "";
|
|
117
|
-
// Validate and sanitize base URL
|
|
118
|
-
this.baseUrl = this.validateAndSanitizeUrl(baseUrl);
|
|
119
|
-
this.apiUrl = "";
|
|
120
|
-
this.timeout = options.timeout || cfg.wordpress.timeout;
|
|
121
|
-
this.maxRetries = options.maxRetries || cfg.wordpress.maxRetries;
|
|
122
|
-
// Authentication configuration
|
|
123
|
-
this.auth = options.auth || this.getAuthFromEnv();
|
|
124
|
-
// Rate limiting
|
|
125
|
-
this.requestInterval = 60000 / cfg.security.rateLimit;
|
|
126
|
-
// Initialize stats
|
|
127
|
-
this._stats = {
|
|
128
|
-
totalRequests: 0,
|
|
129
|
-
successfulRequests: 0,
|
|
130
|
-
failedRequests: 0,
|
|
131
|
-
averageResponseTime: 0,
|
|
132
|
-
rateLimitHits: 0,
|
|
133
|
-
authFailures: 0,
|
|
134
|
-
};
|
|
135
|
-
// Validate configuration
|
|
136
|
-
this.validateConfig();
|
|
137
|
-
}
|
|
138
|
-
get config() {
|
|
139
|
-
return {
|
|
140
|
-
baseUrl: this.baseUrl,
|
|
141
|
-
auth: this.auth,
|
|
142
|
-
timeout: this.timeout,
|
|
143
|
-
maxRetries: this.maxRetries,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
get isAuthenticated() {
|
|
147
|
-
return this.authenticated;
|
|
148
|
-
}
|
|
149
|
-
get stats() {
|
|
150
|
-
return { ...this._stats };
|
|
151
|
-
}
|
|
152
|
-
getSiteUrl() {
|
|
153
|
-
return this.baseUrl;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Validate and sanitize URL for security
|
|
157
|
-
*/
|
|
158
|
-
validateAndSanitizeUrl(url) {
|
|
159
|
-
if (!url) {
|
|
160
|
-
throw new Error("WordPress site URL is required");
|
|
161
|
-
}
|
|
162
|
-
try {
|
|
163
|
-
const parsed = new URL(url);
|
|
164
|
-
// Only allow HTTP/HTTPS protocols
|
|
165
|
-
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
166
|
-
throw new Error("Only HTTP and HTTPS protocols are allowed");
|
|
167
|
-
}
|
|
168
|
-
// Prevent localhost/private IP access in production
|
|
169
|
-
if (config().app.isProduction) {
|
|
170
|
-
const hostname = parsed.hostname.toLowerCase();
|
|
171
|
-
if (hostname === "localhost" ||
|
|
172
|
-
hostname === "127.0.0.1" ||
|
|
173
|
-
hostname === "::1" ||
|
|
174
|
-
hostname.match(/^10\./) ||
|
|
175
|
-
hostname.match(/^172\.(1[6-9]|2[0-9]|3[01])\./) ||
|
|
176
|
-
hostname.match(/^192\.168\./)) {
|
|
177
|
-
throw new Error("Private/localhost URLs not allowed in production");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
// Return clean URL without query parameters or fragments
|
|
181
|
-
return `${parsed.protocol}//${parsed.host}${parsed.pathname}`.replace(/\/$/, "");
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
if (error instanceof TypeError) {
|
|
185
|
-
throw new Error("Invalid WordPress site URL format");
|
|
186
|
-
}
|
|
187
|
-
throw error;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
getAuthFromEnv() {
|
|
191
|
-
const cfg = config();
|
|
192
|
-
const wp = cfg.wordpress;
|
|
193
|
-
const authMethod = wp.authMethod;
|
|
194
|
-
// Use explicit auth method if set
|
|
195
|
-
if (authMethod === "app-password" && wp.username && wp.appPassword) {
|
|
196
|
-
return {
|
|
197
|
-
method: "app-password",
|
|
198
|
-
username: wp.username,
|
|
199
|
-
appPassword: wp.appPassword,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
// Try Application Password first (fallback)
|
|
203
|
-
if (wp.username && wp.appPassword) {
|
|
204
|
-
return {
|
|
205
|
-
method: "app-password",
|
|
206
|
-
username: wp.username,
|
|
207
|
-
appPassword: wp.appPassword,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
// Try JWT
|
|
211
|
-
if (wp.jwtSecret && wp.username && wp.password) {
|
|
212
|
-
return {
|
|
213
|
-
method: "jwt",
|
|
214
|
-
secret: wp.jwtSecret,
|
|
215
|
-
username: wp.username,
|
|
216
|
-
password: wp.password,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
// Try API Key
|
|
220
|
-
if (wp.apiKey) {
|
|
221
|
-
return {
|
|
222
|
-
method: "api-key",
|
|
223
|
-
apiKey: wp.apiKey,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
// Try Cookie
|
|
227
|
-
if (wp.cookieNonce) {
|
|
228
|
-
return {
|
|
229
|
-
method: "cookie",
|
|
230
|
-
nonce: wp.cookieNonce,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
// Default to basic authentication
|
|
234
|
-
return {
|
|
235
|
-
method: "basic",
|
|
236
|
-
username: wp.username || "",
|
|
237
|
-
password: wp.password || wp.appPassword || "",
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
validateConfig() {
|
|
241
|
-
if (!this.baseUrl) {
|
|
242
|
-
throw new Error("WordPress configuration is incomplete: baseUrl is required");
|
|
243
|
-
}
|
|
244
|
-
// Ensure URL doesn't end with slash and add API path
|
|
245
|
-
this.baseUrl = this.baseUrl.replace(/\/$/, "");
|
|
246
|
-
this.apiUrl = `${this.baseUrl}/wp-json/wp/v2`;
|
|
247
|
-
debug.log(`WordPress API Client initialized for: ${this.apiUrl}`);
|
|
248
|
-
}
|
|
249
|
-
async initialize() {
|
|
250
|
-
await this.authenticate();
|
|
251
|
-
}
|
|
252
|
-
async disconnect() {
|
|
253
|
-
this.authenticated = false;
|
|
254
|
-
this.jwtToken = null;
|
|
255
|
-
debug.log("WordPress client disconnected");
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Add authentication headers to request
|
|
259
|
-
*/
|
|
260
|
-
addAuthHeaders(headers) {
|
|
261
|
-
const method = this.auth.method?.toLowerCase();
|
|
262
|
-
switch (method) {
|
|
263
|
-
case "app-password":
|
|
264
|
-
if (this.auth.username && this.auth.appPassword) {
|
|
265
|
-
const credentials = Buffer.from(`${this.auth.username}:${this.auth.appPassword}`).toString("base64");
|
|
266
|
-
headers["Authorization"] = `Basic ${credentials}`;
|
|
267
|
-
}
|
|
268
|
-
break;
|
|
269
|
-
case "basic":
|
|
270
|
-
if (this.auth.username && this.auth.password) {
|
|
271
|
-
const credentials = Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64");
|
|
272
|
-
headers["Authorization"] = `Basic ${credentials}`;
|
|
273
|
-
}
|
|
274
|
-
break;
|
|
275
|
-
case "jwt":
|
|
276
|
-
if (this.jwtToken) {
|
|
277
|
-
headers["Authorization"] = `Bearer ${this.jwtToken}`;
|
|
278
|
-
}
|
|
279
|
-
break;
|
|
280
|
-
case "api-key":
|
|
281
|
-
if (this.auth.apiKey) {
|
|
282
|
-
headers["X-API-Key"] = this.auth.apiKey;
|
|
283
|
-
}
|
|
284
|
-
break;
|
|
285
|
-
case "cookie":
|
|
286
|
-
if (this.auth.nonce) {
|
|
287
|
-
headers["X-WP-Nonce"] = this.auth.nonce;
|
|
288
|
-
}
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Rate limiting implementation
|
|
294
|
-
*/
|
|
295
|
-
async rateLimit() {
|
|
296
|
-
const now = Date.now();
|
|
297
|
-
const timeSinceLastRequest = now - this.lastRequestTime;
|
|
298
|
-
if (timeSinceLastRequest < this.requestInterval) {
|
|
299
|
-
const delay = this.requestInterval - timeSinceLastRequest;
|
|
300
|
-
await this.delay(delay);
|
|
301
|
-
}
|
|
302
|
-
this.lastRequestTime = Date.now();
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Delay utility
|
|
306
|
-
*/
|
|
307
|
-
delay(ms) {
|
|
308
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
309
|
-
}
|
|
310
|
-
async authenticate() {
|
|
311
|
-
const method = this.auth.method?.toLowerCase();
|
|
312
|
-
try {
|
|
313
|
-
switch (method) {
|
|
314
|
-
case "app-password":
|
|
315
|
-
case "basic":
|
|
316
|
-
return await this.authenticateWithBasic();
|
|
317
|
-
case "jwt":
|
|
318
|
-
return await this.authenticateWithJWT();
|
|
319
|
-
case "cookie":
|
|
320
|
-
return await this.authenticateWithCookie();
|
|
321
|
-
case "api-key":
|
|
322
|
-
// API key auth doesn't require separate authentication step
|
|
323
|
-
this.authenticated = true;
|
|
324
|
-
return true;
|
|
325
|
-
default:
|
|
326
|
-
throw new Error(`Unsupported authentication method: ${method}`);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
catch (error) {
|
|
330
|
-
this._stats.authFailures++;
|
|
331
|
-
logError(error, { method });
|
|
332
|
-
throw error;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Authenticate using Basic/Application Password
|
|
337
|
-
*/
|
|
338
|
-
async authenticateWithBasic() {
|
|
339
|
-
const hasCredentials = this.auth.username && (this.auth.method === "app-password" ? this.auth.appPassword : this.auth.password);
|
|
340
|
-
if (!hasCredentials) {
|
|
341
|
-
const methodName = this.auth.method === "app-password" ? "Application Password" : "Basic";
|
|
342
|
-
const passwordField = this.auth.method === "app-password" ? "app password" : "password";
|
|
343
|
-
throw new AuthenticationError(`Username and ${passwordField} are required for ${methodName} authentication`, this.auth.method);
|
|
344
|
-
}
|
|
345
|
-
try {
|
|
346
|
-
// Test authentication by getting current user
|
|
347
|
-
await this.request("GET", "users/me");
|
|
348
|
-
this.authenticated = true;
|
|
349
|
-
debug.log("Basic/Application Password authentication successful");
|
|
350
|
-
return true;
|
|
351
|
-
}
|
|
352
|
-
catch (error) {
|
|
353
|
-
throw new AuthenticationError(`Basic authentication failed: ${error.message}`, this.auth.method);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Authenticate using JWT
|
|
358
|
-
*/
|
|
359
|
-
async authenticateWithJWT() {
|
|
360
|
-
if (!this.auth.secret || !this.auth.username || !this.auth.password) {
|
|
361
|
-
throw new AuthenticationError("JWT secret, username, and password are required for JWT authentication", this.auth.method);
|
|
362
|
-
}
|
|
363
|
-
try {
|
|
364
|
-
const response = await fetch(`${this.baseUrl}/wp-json/jwt-auth/v1/token`, {
|
|
365
|
-
method: "POST",
|
|
366
|
-
headers: {
|
|
367
|
-
"Content-Type": "application/json",
|
|
368
|
-
},
|
|
369
|
-
body: JSON.stringify({
|
|
370
|
-
username: this.auth.username,
|
|
371
|
-
password: this.auth.password,
|
|
372
|
-
}),
|
|
373
|
-
});
|
|
374
|
-
if (!response.ok) {
|
|
375
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
376
|
-
}
|
|
377
|
-
const data = (await response.json());
|
|
378
|
-
this.jwtToken = data.token;
|
|
379
|
-
this.authenticated = true;
|
|
380
|
-
debug.log("JWT authentication successful");
|
|
381
|
-
return true;
|
|
382
|
-
}
|
|
383
|
-
catch (error) {
|
|
384
|
-
throw new AuthenticationError(`JWT authentication failed: ${error.message}`, this.auth.method);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* Authenticate using Cookie
|
|
389
|
-
*/
|
|
390
|
-
async authenticateWithCookie() {
|
|
391
|
-
if (!this.auth.nonce) {
|
|
392
|
-
throw new AuthenticationError("Nonce is required for cookie authentication", this.auth.method);
|
|
393
|
-
}
|
|
394
|
-
this.authenticated = true;
|
|
395
|
-
debug.log("Cookie authentication configured");
|
|
396
|
-
return true;
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Make authenticated request to WordPress REST API
|
|
400
|
-
*/
|
|
401
|
-
async request(method, endpoint, data = null, options = {}) {
|
|
402
|
-
const timer = startTimer();
|
|
403
|
-
this._stats.totalRequests++;
|
|
404
|
-
// Handle endpoint properly - remove leading slash if present to avoid double slashes
|
|
405
|
-
const cleanEndpoint = endpoint.replace(/^\/+/, "");
|
|
406
|
-
const url = endpoint.startsWith("http") ? endpoint : `${this.apiUrl}/${cleanEndpoint}`;
|
|
407
|
-
const headers = {
|
|
408
|
-
"Content-Type": "application/json",
|
|
409
|
-
"User-Agent": "MCP-WordPress/1.0.0",
|
|
410
|
-
...options.headers,
|
|
411
|
-
};
|
|
412
|
-
// Add authentication headers
|
|
413
|
-
this.addAuthHeaders(headers);
|
|
414
|
-
// Set up timeout using AbortController - use options timeout if provided
|
|
415
|
-
const controller = new AbortController();
|
|
416
|
-
const requestTimeout = options.timeout || this.timeout;
|
|
417
|
-
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
|
418
|
-
const fetchOptions = {
|
|
419
|
-
...options, // Spread options first
|
|
420
|
-
method,
|
|
421
|
-
headers, // Headers come after to ensure auth headers aren't overridden
|
|
422
|
-
signal: controller.signal,
|
|
423
|
-
};
|
|
424
|
-
// Add body for POST/PUT/PATCH requests
|
|
425
|
-
if (data && ["POST", "PUT", "PATCH"].includes(method)) {
|
|
426
|
-
if (data instanceof FormData ||
|
|
427
|
-
(typeof data === "object" && data && "append" in data && typeof data.append === "function")) {
|
|
428
|
-
// For FormData, check if it has getHeaders method (form-data package)
|
|
429
|
-
if (typeof data.getHeaders === "function") {
|
|
430
|
-
// Use headers from form-data package
|
|
431
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
432
|
-
const formHeaders = data.getHeaders();
|
|
433
|
-
Object.assign(headers, formHeaders);
|
|
434
|
-
}
|
|
435
|
-
else {
|
|
436
|
-
// For native FormData, don't set Content-Type (let fetch set it with boundary)
|
|
437
|
-
delete headers["Content-Type"];
|
|
438
|
-
}
|
|
439
|
-
fetchOptions.body = data;
|
|
440
|
-
}
|
|
441
|
-
else if (Buffer.isBuffer(data)) {
|
|
442
|
-
// For Buffer data (manual multipart), keep Content-Type from headers
|
|
443
|
-
fetchOptions.body = data;
|
|
444
|
-
}
|
|
445
|
-
else if (typeof data === "string") {
|
|
446
|
-
fetchOptions.body = data;
|
|
447
|
-
}
|
|
448
|
-
else {
|
|
449
|
-
fetchOptions.body = JSON.stringify(data);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
// Rate limiting
|
|
453
|
-
await this.rateLimit();
|
|
454
|
-
let lastError = new Error("Unknown error");
|
|
455
|
-
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
456
|
-
try {
|
|
457
|
-
debug.log(`API Request: ${method} ${url}${attempt > 0 ? ` (attempt ${attempt + 1})` : ""}`);
|
|
458
|
-
const response = await fetch(url, fetchOptions);
|
|
459
|
-
clearTimeout(timeoutId);
|
|
460
|
-
// Handle different response types
|
|
461
|
-
if (!response.ok) {
|
|
462
|
-
const errorText = await response.text();
|
|
463
|
-
let errorMessage;
|
|
464
|
-
try {
|
|
465
|
-
const errorData = JSON.parse(errorText);
|
|
466
|
-
errorMessage = errorData.message || errorData.error || `HTTP ${response.status}`;
|
|
467
|
-
}
|
|
468
|
-
catch {
|
|
469
|
-
errorMessage = errorText || `HTTP ${response.status}: ${response.statusText}`;
|
|
470
|
-
}
|
|
471
|
-
// Handle rate limiting
|
|
472
|
-
if (response.status === 429) {
|
|
473
|
-
this._stats.rateLimitHits++;
|
|
474
|
-
throw new RateLimitError(errorMessage, Date.now() + 60000);
|
|
475
|
-
}
|
|
476
|
-
// Handle permission errors specifically for uploads
|
|
477
|
-
if (response.status === 403 && endpoint.includes("media") && method === "POST") {
|
|
478
|
-
throw new AuthenticationError("Media upload blocked: WordPress REST API media uploads appear to be disabled or restricted by a plugin/security policy. " +
|
|
479
|
-
`Error: ${errorMessage}. ` +
|
|
480
|
-
"Common causes: W3 Total Cache, security plugins, or custom REST API restrictions. " +
|
|
481
|
-
"Please check WordPress admin settings or contact your system administrator.", this.auth.method);
|
|
482
|
-
}
|
|
483
|
-
// Handle general upload permission errors
|
|
484
|
-
if (errorMessage.includes("Beiträge zu erstellen") && endpoint.includes("media")) {
|
|
485
|
-
throw new AuthenticationError(`WordPress REST API media upload restriction detected: ${errorMessage}. ` +
|
|
486
|
-
"This typically indicates that media uploads via REST API are disabled by WordPress configuration, " +
|
|
487
|
-
"a security plugin (like W3 Total Cache, Borlabs Cookie), or server policy. " +
|
|
488
|
-
"User has sufficient permissions but WordPress/plugins are blocking the upload.", this.auth.method);
|
|
489
|
-
}
|
|
490
|
-
// Fallback for 404 errors - try index.php approach for REST API
|
|
491
|
-
if (response.status === 404 && attempt === 0 && url.includes("/wp-json/wp/v2")) {
|
|
492
|
-
debug.log(`404 on pretty permalinks, trying index.php approach`);
|
|
493
|
-
// Parse the URL to handle query parameters correctly
|
|
494
|
-
const urlObj = new URL(url);
|
|
495
|
-
const endpoint = urlObj.pathname.replace("/wp-json/wp/v2", "");
|
|
496
|
-
const queryParams = urlObj.searchParams.toString();
|
|
497
|
-
let fallbackUrl = `${urlObj.origin}/index.php?rest_route=/wp/v2${endpoint}`;
|
|
498
|
-
if (queryParams) {
|
|
499
|
-
fallbackUrl += `&${queryParams}`;
|
|
500
|
-
}
|
|
501
|
-
try {
|
|
502
|
-
// Create a new timeout for the fallback request
|
|
503
|
-
const fallbackController = new AbortController();
|
|
504
|
-
const fallbackTimeoutId = setTimeout(() => {
|
|
505
|
-
fallbackController.abort();
|
|
506
|
-
}, requestTimeout);
|
|
507
|
-
const fallbackOptions = { ...fetchOptions, signal: fallbackController.signal };
|
|
508
|
-
const fallbackResponse = await fetch(fallbackUrl, fallbackOptions);
|
|
509
|
-
clearTimeout(fallbackTimeoutId);
|
|
510
|
-
if (fallbackResponse.ok) {
|
|
511
|
-
const responseText = await fallbackResponse.text();
|
|
512
|
-
if (!responseText) {
|
|
513
|
-
this._stats.successfulRequests++;
|
|
514
|
-
const duration = timer.end();
|
|
515
|
-
this.updateAverageResponseTime(duration);
|
|
516
|
-
return null;
|
|
517
|
-
}
|
|
518
|
-
const result = JSON.parse(responseText);
|
|
519
|
-
this._stats.successfulRequests++;
|
|
520
|
-
const duration = timer.end();
|
|
521
|
-
this.updateAverageResponseTime(duration);
|
|
522
|
-
return result;
|
|
523
|
-
}
|
|
524
|
-
else {
|
|
525
|
-
// If fallback also fails, continue with original error
|
|
526
|
-
debug.log(`Fallback also failed with status ${fallbackResponse.status}`);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
catch (fallbackError) {
|
|
530
|
-
debug.log(`Fallback request failed: ${fallbackError.message}`);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
throw new WordPressAPIError(errorMessage, response.status);
|
|
534
|
-
}
|
|
535
|
-
// Parse response
|
|
536
|
-
const responseText = await response.text();
|
|
537
|
-
if (!responseText) {
|
|
538
|
-
this._stats.successfulRequests++;
|
|
539
|
-
const duration = timer.end();
|
|
540
|
-
this.updateAverageResponseTime(duration);
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
try {
|
|
544
|
-
const result = JSON.parse(responseText);
|
|
545
|
-
this._stats.successfulRequests++;
|
|
546
|
-
const duration = timer.end();
|
|
547
|
-
this.updateAverageResponseTime(duration);
|
|
548
|
-
return result;
|
|
549
|
-
}
|
|
550
|
-
catch (parseError) {
|
|
551
|
-
// For authentication requests, malformed JSON should be an error
|
|
552
|
-
if (endpoint.includes("users/me") || endpoint.includes("jwt-auth")) {
|
|
553
|
-
throw new WordPressAPIError(`Invalid JSON response: ${parseError.message}`);
|
|
554
|
-
}
|
|
555
|
-
this._stats.successfulRequests++;
|
|
556
|
-
const duration = timer.end();
|
|
557
|
-
this.updateAverageResponseTime(duration);
|
|
558
|
-
return responseText;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
catch (error) {
|
|
562
|
-
clearTimeout(timeoutId);
|
|
563
|
-
lastError = error;
|
|
564
|
-
// Handle timeout errors
|
|
565
|
-
if (error.name === "AbortError") {
|
|
566
|
-
lastError = new Error(`Request timeout after ${requestTimeout}ms`);
|
|
567
|
-
}
|
|
568
|
-
// Handle network errors
|
|
569
|
-
if (lastError.message.includes("socket hang up") || lastError.message.includes("ECONNRESET")) {
|
|
570
|
-
lastError = new Error(`Network connection lost during upload: ${lastError.message}`);
|
|
571
|
-
}
|
|
572
|
-
debug.log(`Request failed (attempt ${attempt + 1}): ${lastError.message}`);
|
|
573
|
-
// Don't retry on authentication errors, timeouts, or critical network errors
|
|
574
|
-
if (lastError.message.includes("401") ||
|
|
575
|
-
lastError.message.includes("403") ||
|
|
576
|
-
lastError.message.includes("timeout") ||
|
|
577
|
-
lastError.message.includes("Network connection lost")) {
|
|
578
|
-
break;
|
|
579
|
-
}
|
|
580
|
-
if (attempt < this.maxRetries - 1) {
|
|
581
|
-
await this.delay(1000 * (attempt + 1)); // Exponential backoff
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
this._stats.failedRequests++;
|
|
586
|
-
timer.end();
|
|
587
|
-
throw new WordPressAPIError(`Request failed after ${this.maxRetries} attempts: ${lastError.message}`);
|
|
588
|
-
}
|
|
589
|
-
updateAverageResponseTime(duration) {
|
|
590
|
-
const totalSuccessful = this._stats.successfulRequests;
|
|
591
|
-
this._stats.averageResponseTime =
|
|
592
|
-
(this._stats.averageResponseTime * (totalSuccessful - 1) + duration) / totalSuccessful;
|
|
593
|
-
this._stats.lastRequestTime = Date.now();
|
|
594
|
-
}
|
|
595
|
-
// HTTP method helpers
|
|
596
|
-
async get(endpoint, options) {
|
|
597
|
-
return this.request("GET", endpoint, null, options);
|
|
598
|
-
}
|
|
599
|
-
async post(endpoint, data, options) {
|
|
600
|
-
return this.request("POST", endpoint, data, options);
|
|
601
|
-
}
|
|
602
|
-
async put(endpoint, data, options) {
|
|
603
|
-
return this.request("PUT", endpoint, data, options);
|
|
604
|
-
}
|
|
605
|
-
async patch(endpoint, data, options) {
|
|
606
|
-
return this.request("PATCH", endpoint, data, options);
|
|
607
|
-
}
|
|
608
|
-
async delete(endpoint, options) {
|
|
609
|
-
return this.request("DELETE", endpoint, null, options);
|
|
610
|
-
}
|
|
611
|
-
// WordPress API Methods
|
|
612
|
-
// Posts
|
|
613
|
-
async getPosts(params) {
|
|
614
|
-
const queryString = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
615
|
-
return this.get(`posts${queryString}`);
|
|
616
|
-
}
|
|
617
|
-
async getPost(id, context = "view") {
|
|
618
|
-
return this.get(`posts/${id}?context=${context}`);
|
|
619
|
-
}
|
|
620
|
-
async createPost(data) {
|
|
621
|
-
return this.post("posts", data);
|
|
622
|
-
}
|
|
623
|
-
async updatePost(data) {
|
|
624
|
-
const { id, ...updateData } = data;
|
|
625
|
-
return this.put(`posts/${id}`, updateData);
|
|
626
|
-
}
|
|
627
|
-
async deletePost(id, force = false) {
|
|
628
|
-
return this.delete(`posts/${id}?force=${force}`);
|
|
629
|
-
}
|
|
630
|
-
async getPostRevisions(id) {
|
|
631
|
-
return this.get(`posts/${id}/revisions`);
|
|
632
|
-
}
|
|
633
|
-
// Pages
|
|
634
|
-
async getPages(params) {
|
|
635
|
-
const normalizedParams = params
|
|
636
|
-
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
|
|
637
|
-
: undefined;
|
|
638
|
-
const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
|
|
639
|
-
return this.get(`pages${queryString}`);
|
|
640
|
-
}
|
|
641
|
-
async getPage(id, context = "view") {
|
|
642
|
-
return this.get(`pages/${id}?context=${context}`);
|
|
643
|
-
}
|
|
644
|
-
async createPage(data) {
|
|
645
|
-
return this.post("pages", data);
|
|
646
|
-
}
|
|
647
|
-
async updatePage(data) {
|
|
648
|
-
const { id, ...updateData } = data;
|
|
649
|
-
return this.put(`pages/${id}`, updateData);
|
|
650
|
-
}
|
|
651
|
-
async deletePage(id, force = false) {
|
|
652
|
-
return this.delete(`pages/${id}?force=${force}`);
|
|
653
|
-
}
|
|
654
|
-
async getPageRevisions(id) {
|
|
655
|
-
return this.get(`pages/${id}/revisions`);
|
|
656
|
-
}
|
|
657
|
-
// Media
|
|
658
|
-
async getMedia(params) {
|
|
659
|
-
const normalizedParams = params
|
|
660
|
-
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
|
|
661
|
-
: undefined;
|
|
662
|
-
const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
|
|
663
|
-
return this.get(`media${queryString}`);
|
|
664
|
-
}
|
|
665
|
-
async getMediaItem(id, context = "view") {
|
|
666
|
-
return this.get(`media/${id}?context=${context}`);
|
|
667
|
-
}
|
|
668
|
-
async uploadMedia(data) {
|
|
669
|
-
if (!fs.existsSync(data.file_path)) {
|
|
670
|
-
throw new Error(`File not found: ${data.file_path}`);
|
|
671
|
-
}
|
|
672
|
-
const stats = fs.statSync(data.file_path);
|
|
673
|
-
const filename = data.title || path.basename(data.file_path);
|
|
674
|
-
const fileBuffer = fs.readFileSync(data.file_path);
|
|
675
|
-
// Check if file is too large (WordPress default is 2MB for most installs)
|
|
676
|
-
const maxSize = 10 * 1024 * 1024; // 10MB reasonable limit
|
|
677
|
-
if (stats.size > maxSize) {
|
|
678
|
-
throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB. Maximum allowed: ${maxSize / 1024 / 1024}MB`);
|
|
679
|
-
}
|
|
680
|
-
debug.log(`Uploading file: ${filename} (${(stats.size / 1024).toFixed(2)}KB)`);
|
|
681
|
-
return this.uploadFile(fileBuffer, filename, this.getMimeType(data.file_path), data);
|
|
682
|
-
}
|
|
683
|
-
async uploadFile(fileData, filename, mimeType, meta = {}, options) {
|
|
684
|
-
debug.log(`Uploading file: ${filename} (${fileData.length} bytes)`);
|
|
685
|
-
// Use FormData but with correct configuration for node-fetch
|
|
686
|
-
const formData = new FormData();
|
|
687
|
-
formData.setMaxListeners(20);
|
|
688
|
-
// Add file with correct options
|
|
689
|
-
formData.append("file", fileData, {
|
|
690
|
-
filename,
|
|
691
|
-
contentType: mimeType,
|
|
692
|
-
});
|
|
693
|
-
// Add metadata
|
|
694
|
-
if (meta.title)
|
|
695
|
-
formData.append("title", meta.title);
|
|
696
|
-
if (meta.alt_text)
|
|
697
|
-
formData.append("alt_text", meta.alt_text);
|
|
698
|
-
if (meta.caption)
|
|
699
|
-
formData.append("caption", meta.caption);
|
|
700
|
-
if (meta.description)
|
|
701
|
-
formData.append("description", meta.description);
|
|
702
|
-
if (meta.post)
|
|
703
|
-
formData.append("post", meta.post.toString());
|
|
704
|
-
// Use longer timeout for file uploads
|
|
705
|
-
const uploadTimeout = options?.timeout !== undefined ? options.timeout : 600000; // 10 minutes default
|
|
706
|
-
const uploadOptions = {
|
|
707
|
-
...options,
|
|
708
|
-
timeout: uploadTimeout,
|
|
709
|
-
};
|
|
710
|
-
debug.log(`Upload prepared with FormData, timeout: ${uploadTimeout}ms`);
|
|
711
|
-
// Use the regular post method which handles FormData correctly
|
|
712
|
-
return this.post("media", formData, uploadOptions);
|
|
713
|
-
}
|
|
714
|
-
async updateMedia(data) {
|
|
715
|
-
const { id, ...updateData } = data;
|
|
716
|
-
return this.put(`media/${id}`, updateData);
|
|
717
|
-
}
|
|
718
|
-
async deleteMedia(id, force = false) {
|
|
719
|
-
return this.delete(`media/${id}?force=${force}`);
|
|
720
|
-
}
|
|
721
|
-
// Users
|
|
722
|
-
async getUsers(params) {
|
|
723
|
-
const normalizedParams = params
|
|
724
|
-
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
|
|
725
|
-
: undefined;
|
|
726
|
-
const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
|
|
727
|
-
return this.get(`users${queryString}`);
|
|
728
|
-
}
|
|
729
|
-
async getUser(id, context = "view") {
|
|
730
|
-
return this.get(`users/${id}?context=${context}`);
|
|
731
|
-
}
|
|
732
|
-
async createUser(data) {
|
|
733
|
-
return this.post("users", data);
|
|
734
|
-
}
|
|
735
|
-
async updateUser(data) {
|
|
736
|
-
const { id, ...updateData } = data;
|
|
737
|
-
return this.put(`users/${id}`, updateData);
|
|
738
|
-
}
|
|
739
|
-
async deleteUser(id, reassign) {
|
|
740
|
-
const params = reassign ? `?reassign=${reassign}&force=true` : "?force=true";
|
|
741
|
-
return this.delete(`users/${id}${params}`);
|
|
742
|
-
}
|
|
743
|
-
async getCurrentUser() {
|
|
744
|
-
return this.getUser("me");
|
|
745
|
-
}
|
|
746
|
-
// Comments
|
|
747
|
-
async getComments(params) {
|
|
748
|
-
const normalizedParams = params
|
|
749
|
-
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
|
|
750
|
-
: undefined;
|
|
751
|
-
const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
|
|
752
|
-
return this.get(`comments${queryString}`);
|
|
753
|
-
}
|
|
754
|
-
async getComment(id, context = "view") {
|
|
755
|
-
return this.get(`comments/${id}?context=${context}`);
|
|
756
|
-
}
|
|
757
|
-
async createComment(data) {
|
|
758
|
-
return this.post("comments", data);
|
|
759
|
-
}
|
|
760
|
-
async updateComment(data) {
|
|
761
|
-
const { id, ...updateData } = data;
|
|
762
|
-
return this.put(`comments/${id}`, updateData);
|
|
763
|
-
}
|
|
764
|
-
async deleteComment(id, force = false) {
|
|
765
|
-
return this.delete(`comments/${id}?force=${force}`);
|
|
766
|
-
}
|
|
767
|
-
async approveComment(id) {
|
|
768
|
-
return this.put(`comments/${id}`, { status: "approved" });
|
|
769
|
-
}
|
|
770
|
-
async rejectComment(id) {
|
|
771
|
-
return this.put(`comments/${id}`, {
|
|
772
|
-
status: "unapproved",
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
|
-
async spamComment(id) {
|
|
776
|
-
return this.put(`comments/${id}`, { status: "spam" });
|
|
777
|
-
}
|
|
778
|
-
// Taxonomies
|
|
779
|
-
async getCategories(params) {
|
|
780
|
-
const normalizedParams = params
|
|
781
|
-
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
|
|
782
|
-
: undefined;
|
|
783
|
-
const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
|
|
784
|
-
return this.get(`categories${queryString}`);
|
|
785
|
-
}
|
|
786
|
-
async getCategory(id) {
|
|
787
|
-
return this.get(`categories/${id}`);
|
|
788
|
-
}
|
|
789
|
-
async createCategory(data) {
|
|
790
|
-
return this.post("categories", data);
|
|
791
|
-
}
|
|
792
|
-
async updateCategory(data) {
|
|
793
|
-
const { id, ...updateData } = data;
|
|
794
|
-
return this.put(`categories/${id}`, updateData);
|
|
795
|
-
}
|
|
796
|
-
async deleteCategory(id, force = false) {
|
|
797
|
-
return this.delete(`categories/${id}?force=${force}`);
|
|
798
|
-
}
|
|
799
|
-
async getTags(params) {
|
|
800
|
-
const normalizedParams = params
|
|
801
|
-
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]))
|
|
802
|
-
: undefined;
|
|
803
|
-
const queryString = normalizedParams ? "?" + new URLSearchParams(normalizedParams).toString() : "";
|
|
804
|
-
return this.get(`tags${queryString}`);
|
|
805
|
-
}
|
|
806
|
-
async getTag(id) {
|
|
807
|
-
return this.get(`tags/${id}`);
|
|
808
|
-
}
|
|
809
|
-
async createTag(data) {
|
|
810
|
-
return this.post("tags", data);
|
|
811
|
-
}
|
|
812
|
-
async updateTag(data) {
|
|
813
|
-
const { id, ...updateData } = data;
|
|
814
|
-
return this.put(`tags/${id}`, updateData);
|
|
815
|
-
}
|
|
816
|
-
async deleteTag(id, force = false) {
|
|
817
|
-
return this.delete(`tags/${id}?force=${force}`);
|
|
818
|
-
}
|
|
819
|
-
// Site Management
|
|
820
|
-
async getSiteSettings() {
|
|
821
|
-
return this.get("settings");
|
|
822
|
-
}
|
|
823
|
-
async updateSiteSettings(settings) {
|
|
824
|
-
return this.post("settings", settings);
|
|
825
|
-
}
|
|
826
|
-
async getSiteInfo() {
|
|
827
|
-
return this.get("");
|
|
828
|
-
}
|
|
829
|
-
// Application Passwords
|
|
830
|
-
async getApplicationPasswords(userId = "me") {
|
|
831
|
-
return this.get(`users/${userId}/application-passwords`);
|
|
832
|
-
}
|
|
833
|
-
async createApplicationPassword(userId, name, appId) {
|
|
834
|
-
const data = { name };
|
|
835
|
-
if (appId)
|
|
836
|
-
data.app_id = appId;
|
|
837
|
-
return this.post(`users/${userId}/application-passwords`, data);
|
|
838
|
-
}
|
|
839
|
-
async deleteApplicationPassword(userId, uuid) {
|
|
840
|
-
return this.delete(`users/${userId}/application-passwords/${uuid}`);
|
|
841
|
-
}
|
|
842
|
-
// Search
|
|
843
|
-
async search(query, types, subtype) {
|
|
844
|
-
const params = new URLSearchParams({ search: query });
|
|
845
|
-
if (types)
|
|
846
|
-
params.append("type", types.join(","));
|
|
847
|
-
if (subtype)
|
|
848
|
-
params.append("subtype", subtype);
|
|
849
|
-
return this.get(`search?${params.toString()}`);
|
|
850
|
-
}
|
|
851
|
-
// Utility Methods
|
|
852
|
-
async ping() {
|
|
853
|
-
try {
|
|
854
|
-
await this.get("");
|
|
855
|
-
return true;
|
|
856
|
-
}
|
|
857
|
-
catch {
|
|
858
|
-
return false;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
async getServerInfo() {
|
|
862
|
-
return this.get("");
|
|
863
|
-
}
|
|
864
|
-
validateEndpoint(endpoint) {
|
|
865
|
-
return /^[a-zA-Z0-9\/\-_]+$/.test(endpoint);
|
|
866
|
-
}
|
|
867
|
-
buildUrl(endpoint, params) {
|
|
868
|
-
const url = `${this.apiUrl}/${endpoint.replace(/^\/+/, "")}`;
|
|
869
|
-
if (params) {
|
|
870
|
-
const normalizedParams = Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]));
|
|
871
|
-
const searchParams = new URLSearchParams(normalizedParams);
|
|
872
|
-
return `${url}?${searchParams.toString()}`;
|
|
873
|
-
}
|
|
874
|
-
return url;
|
|
875
|
-
}
|
|
876
|
-
getMimeType(filePath) {
|
|
877
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
878
|
-
const mimeTypes = {
|
|
879
|
-
".jpg": "image/jpeg",
|
|
880
|
-
".jpeg": "image/jpeg",
|
|
881
|
-
".png": "image/png",
|
|
882
|
-
".gif": "image/gif",
|
|
883
|
-
".webp": "image/webp",
|
|
884
|
-
".svg": "image/svg+xml",
|
|
885
|
-
".pdf": "application/pdf",
|
|
886
|
-
".doc": "application/msword",
|
|
887
|
-
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
888
|
-
".txt": "text/plain",
|
|
889
|
-
".mp4": "video/mp4",
|
|
890
|
-
".mp3": "audio/mpeg",
|
|
891
|
-
".wav": "audio/wav",
|
|
892
|
-
};
|
|
893
|
-
return mimeTypes[ext] || "application/octet-stream";
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
//# sourceMappingURL=api.js.map
|