mcp-wordpress 1.2.2 → 1.3.0
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/README.md +210 -182
- package/dist/cache/CacheInvalidation.d.ts +3 -3
- package/dist/cache/CacheInvalidation.d.ts.map +1 -1
- package/dist/cache/CacheInvalidation.js +119 -119
- package/dist/cache/CacheInvalidation.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +5 -0
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +26 -16
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/HttpCacheWrapper.d.ts +1 -1
- package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
- package/dist/cache/HttpCacheWrapper.js +29 -29
- package/dist/cache/HttpCacheWrapper.js.map +1 -1
- package/dist/cache/__tests__/CacheInvalidation.test.js +96 -94
- package/dist/cache/__tests__/CacheInvalidation.test.js.map +1 -1
- package/dist/cache/__tests__/CacheManager.test.js +113 -113
- package/dist/cache/__tests__/CacheManager.test.js.map +1 -1
- package/dist/cache/__tests__/CachedWordPressClient.test.js +102 -99
- package/dist/cache/__tests__/CachedWordPressClient.test.js.map +1 -1
- package/dist/cache/__tests__/HttpCacheWrapper.test.js +98 -95
- package/dist/cache/__tests__/HttpCacheWrapper.test.js.map +1 -1
- package/dist/cache/index.d.ts +7 -7
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +4 -4
- package/dist/cache/index.js.map +1 -1
- package/dist/client/CachedWordPressClient.d.ts +4 -4
- package/dist/client/CachedWordPressClient.d.ts.map +1 -1
- package/dist/client/CachedWordPressClient.js +55 -51
- package/dist/client/CachedWordPressClient.js.map +1 -1
- package/dist/client/api.d.ts +10 -10
- package/dist/client/api.js +158 -158
- package/dist/client/api.js.map +1 -1
- package/dist/client/auth.d.ts +2 -2
- package/dist/client/auth.js +72 -72
- package/dist/client/managers/AuthenticationManager.d.ts +2 -2
- package/dist/client/managers/AuthenticationManager.js +46 -46
- package/dist/client/managers/BaseManager.d.ts +1 -1
- package/dist/client/managers/BaseManager.js +9 -9
- package/dist/client/managers/RequestManager.d.ts +5 -3
- package/dist/client/managers/RequestManager.d.ts.map +1 -1
- package/dist/client/managers/RequestManager.js +39 -19
- package/dist/client/managers/RequestManager.js.map +1 -1
- package/dist/client/managers/index.d.ts +3 -3
- package/dist/client/managers/index.js +3 -3
- package/dist/config/ConfigurationSchema.d.ts +2 -2
- package/dist/config/ConfigurationSchema.d.ts.map +1 -1
- package/dist/config/ConfigurationSchema.js +40 -40
- package/dist/config/ConfigurationSchema.js.map +1 -1
- package/dist/config/ServerConfiguration.d.ts +2 -2
- package/dist/config/ServerConfiguration.js +35 -35
- package/dist/config/ServerConfiguration.js.map +1 -1
- package/dist/docs/DocumentationGenerator.d.ts.map +1 -1
- package/dist/docs/DocumentationGenerator.js +296 -255
- package/dist/docs/DocumentationGenerator.js.map +1 -1
- package/dist/docs/MarkdownFormatter.d.ts +1 -1
- package/dist/docs/MarkdownFormatter.d.ts.map +1 -1
- package/dist/docs/MarkdownFormatter.js +60 -51
- package/dist/docs/MarkdownFormatter.js.map +1 -1
- package/dist/docs/index.d.ts +3 -3
- package/dist/docs/index.d.ts.map +1 -1
- package/dist/docs/index.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -16
- package/dist/index.js.map +1 -1
- package/dist/mcp-wordpress-1.3.0.tgz +0 -0
- package/dist/performance/MetricsCollector.d.ts +3 -3
- package/dist/performance/MetricsCollector.d.ts.map +1 -1
- package/dist/performance/MetricsCollector.js +33 -27
- package/dist/performance/MetricsCollector.js.map +1 -1
- package/dist/performance/PerformanceAnalytics.d.ts +12 -12
- package/dist/performance/PerformanceAnalytics.d.ts.map +1 -1
- package/dist/performance/PerformanceAnalytics.js +200 -154
- package/dist/performance/PerformanceAnalytics.js.map +1 -1
- package/dist/performance/PerformanceMonitor.d.ts +5 -5
- package/dist/performance/PerformanceMonitor.d.ts.map +1 -1
- package/dist/performance/PerformanceMonitor.js +53 -52
- package/dist/performance/PerformanceMonitor.js.map +1 -1
- package/dist/performance/index.d.ts +6 -6
- package/dist/performance/index.d.ts.map +1 -1
- package/dist/performance/index.js +3 -3
- package/dist/security/InputValidator.d.ts +1 -1
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +111 -88
- package/dist/security/InputValidator.js.map +1 -1
- package/dist/security/SecurityConfig.d.ts +5 -5
- package/dist/security/SecurityConfig.js +92 -92
- package/dist/security/SecurityConfig.js.map +1 -1
- package/dist/server/ConnectionTester.d.ts +1 -1
- package/dist/server/ConnectionTester.d.ts.map +1 -1
- package/dist/server/ConnectionTester.js +4 -4
- package/dist/server/ConnectionTester.js.map +1 -1
- package/dist/server/ToolRegistry.d.ts +2 -2
- package/dist/server/ToolRegistry.d.ts.map +1 -1
- package/dist/server/ToolRegistry.js +35 -32
- package/dist/server/ToolRegistry.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/tools/BaseToolManager.js +5 -5
- package/dist/tools/auth.d.ts +2 -2
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +32 -31
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/cache.d.ts +1 -1
- package/dist/tools/cache.d.ts.map +1 -1
- package/dist/tools/cache.js +71 -71
- package/dist/tools/cache.js.map +1 -1
- package/dist/tools/comments.d.ts +2 -2
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js +79 -79
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/index.d.ts +10 -10
- package/dist/tools/index.js +10 -10
- package/dist/tools/media.d.ts +2 -2
- package/dist/tools/media.js +80 -80
- package/dist/tools/pages.d.ts +2 -2
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js +75 -75
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/performance.d.ts +1 -1
- package/dist/tools/performance.d.ts.map +1 -1
- package/dist/tools/performance.js +311 -287
- package/dist/tools/performance.js.map +1 -1
- package/dist/tools/posts.d.ts +2 -2
- package/dist/tools/posts.d.ts.map +1 -1
- package/dist/tools/posts.js +94 -94
- package/dist/tools/posts.js.map +1 -1
- package/dist/tools/site.d.ts +2 -2
- package/dist/tools/site.d.ts.map +1 -1
- package/dist/tools/site.js +60 -60
- package/dist/tools/site.js.map +1 -1
- package/dist/tools/taxonomies.d.ts +2 -2
- package/dist/tools/taxonomies.js +89 -89
- package/dist/tools/users.d.ts +2 -2
- package/dist/tools/users.js +68 -68
- package/dist/tools/users.js.map +1 -1
- package/dist/types/client.d.ts +13 -13
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/client.js +12 -12
- package/dist/types/client.js.map +1 -1
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -3
- package/dist/types/mcp.d.ts +7 -7
- package/dist/types/wordpress.d.ts +21 -21
- package/dist/types/wordpress.d.ts.map +1 -1
- package/dist/utils/debug.d.ts +2 -2
- package/dist/utils/debug.js +28 -28
- package/dist/utils/error.d.ts.map +1 -1
- package/dist/utils/error.js +13 -13
- package/dist/utils/error.js.map +1 -1
- package/dist/utils/toolWrapper.d.ts.map +1 -1
- package/dist/utils/toolWrapper.js +5 -5
- package/dist/utils/toolWrapper.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +41 -31
- package/dist/utils/validation.js.map +1 -1
- package/docs/CACHING.md +36 -2
- package/docs/DOCKER.md +24 -18
- package/docs/PERFORMANCE_MONITORING.md +49 -1
- package/docs/SECURITY_TESTING.md +30 -1
- package/docs/api/README.md +9 -1
- package/docs/api/summary.json +1 -1
- package/docs/contract-testing.md +24 -3
- package/docs/developer/GITHUB_ACTIONS_SETUP.md +8 -2
- package/docs/developer/MAINTENANCE.md +29 -3
- package/docs/developer/MIGRATION_GUIDE.md +13 -1
- package/docs/developer/NPM_AUTH_SETUP.md +13 -2
- package/docs/developer/REFACTORING.md +31 -1
- package/docs/releases/COMMUNITY_ANNOUNCEMENT_v1.1.2.md +18 -7
- package/docs/releases/RELEASE_NOTES_v1.1.2.md +31 -5
- package/docs/user-guides/DOCKER_SETUP.md +264 -0
- package/docs/user-guides/DTX_SETUP.md +327 -0
- package/docs/user-guides/NPM_SETUP.md +109 -0
- package/docs/user-guides/NPX_SETUP.md +281 -0
- package/docs/wordpress-rest-api-authentication-troubleshooting.md +13 -2
- package/package.json +27 -8
- package/src/cache/CacheInvalidation.ts +140 -132
- package/src/cache/CacheManager.ts +40 -29
- package/src/cache/HttpCacheWrapper.ts +105 -68
- package/src/cache/__tests__/CacheInvalidation.test.ts +123 -118
- package/src/cache/__tests__/CacheManager.test.ts +156 -152
- package/src/cache/__tests__/CachedWordPressClient.test.ts +131 -116
- package/src/cache/__tests__/HttpCacheWrapper.test.ts +118 -115
- package/src/cache/index.ts +13 -13
- package/src/client/CachedWordPressClient.ts +90 -80
- package/src/client/api.ts +205 -205
- package/src/client/auth.ts +80 -80
- package/src/client/managers/AuthenticationManager.ts +61 -61
- package/src/client/managers/BaseManager.ts +11 -11
- package/src/client/managers/RequestManager.ts +79 -47
- package/src/client/managers/index.ts +3 -3
- package/src/config/ConfigurationSchema.ts +44 -44
- package/src/config/ServerConfiguration.ts +39 -39
- package/src/docs/DocumentationGenerator.ts +402 -295
- package/src/docs/MarkdownFormatter.ts +94 -69
- package/src/docs/index.ts +4 -4
- package/src/index.ts +24 -21
- package/src/performance/MetricsCollector.ts +90 -58
- package/src/performance/PerformanceAnalytics.ts +386 -262
- package/src/performance/PerformanceMonitor.ts +152 -118
- package/src/performance/index.ts +9 -9
- package/src/security/InputValidator.ts +148 -91
- package/src/security/SecurityConfig.ts +94 -94
- package/src/server/ConnectionTester.ts +21 -15
- package/src/server/ToolRegistry.ts +64 -51
- package/src/server.ts +2 -2
- package/src/tools/BaseToolManager.ts +6 -6
- package/src/tools/auth.ts +42 -37
- package/src/tools/cache.ts +85 -81
- package/src/tools/comments.ts +93 -91
- package/src/tools/index.ts +10 -10
- package/src/tools/media.ts +89 -89
- package/src/tools/pages.ts +89 -87
- package/src/tools/performance.ts +443 -352
- package/src/tools/posts.ts +109 -107
- package/src/tools/site.ts +86 -77
- package/src/tools/taxonomies.ts +102 -102
- package/src/tools/users.ts +77 -77
- package/src/types/client.ts +157 -60
- package/src/types/index.ts +49 -27
- package/src/types/mcp.ts +15 -15
- package/src/types/wordpress.ts +57 -29
- package/src/utils/debug.ts +37 -37
- package/src/utils/error.ts +47 -25
- package/src/utils/toolWrapper.ts +12 -8
- package/src/utils/validation.ts +116 -65
- package/dist/client/WordPressClient.d.ts +0 -81
- package/dist/client/WordPressClient.d.ts.map +0 -1
- package/dist/client/WordPressClient.js +0 -354
- package/dist/client/WordPressClient.js.map +0 -1
- package/dist/performance/AnomalyDetector.d.ts +0 -63
- package/dist/performance/AnomalyDetector.d.ts.map +0 -1
- package/dist/performance/AnomalyDetector.js +0 -222
- package/dist/performance/AnomalyDetector.js.map +0 -1
- package/dist/performance/BenchmarkAnalyzer.d.ts +0 -67
- package/dist/performance/BenchmarkAnalyzer.d.ts.map +0 -1
- package/dist/performance/BenchmarkAnalyzer.js +0 -301
- package/dist/performance/BenchmarkAnalyzer.js.map +0 -1
- package/dist/performance/TrendAnalyzer.d.ts +0 -69
- package/dist/performance/TrendAnalyzer.d.ts.map +0 -1
- package/dist/performance/TrendAnalyzer.js +0 -203
- package/dist/performance/TrendAnalyzer.js.map +0 -1
- package/dist/tools/BaseToolClass.d.ts +0 -76
- package/dist/tools/BaseToolClass.d.ts.map +0 -1
- package/dist/tools/BaseToolClass.js +0 -104
- package/dist/tools/BaseToolClass.js.map +0 -1
- package/dist/tools/base.d.ts +0 -37
- package/dist/tools/base.d.ts.map +0 -1
- package/dist/tools/base.js +0 -60
- package/dist/tools/base.js.map +0 -1
- package/docs/user-guides/CLAUDE_DESKTOP_SETUP.md +0 -187
package/src/client/api.ts
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Handles all REST API communication with WordPress
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import fetch from
|
|
7
|
-
import FormData from
|
|
8
|
-
import * as fs from
|
|
9
|
-
import * as path from
|
|
6
|
+
import fetch from "node-fetch";
|
|
7
|
+
import FormData from "form-data";
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import * as path from "path";
|
|
10
10
|
import type {
|
|
11
11
|
IWordPressClient,
|
|
12
12
|
WordPressClientConfig,
|
|
@@ -14,13 +14,13 @@ import type {
|
|
|
14
14
|
AuthMethod,
|
|
15
15
|
HTTPMethod,
|
|
16
16
|
RequestOptions,
|
|
17
|
-
ClientStats
|
|
18
|
-
} from
|
|
17
|
+
ClientStats,
|
|
18
|
+
} from "../types/client.js";
|
|
19
19
|
import {
|
|
20
20
|
WordPressAPIError,
|
|
21
21
|
AuthenticationError,
|
|
22
|
-
RateLimitError
|
|
23
|
-
} from
|
|
22
|
+
RateLimitError,
|
|
23
|
+
} from "../types/client.js";
|
|
24
24
|
import type {
|
|
25
25
|
WordPressPost,
|
|
26
26
|
WordPressPage,
|
|
@@ -48,9 +48,9 @@ import type {
|
|
|
48
48
|
CreateTagRequest,
|
|
49
49
|
UpdateTagRequest,
|
|
50
50
|
UploadMediaRequest,
|
|
51
|
-
UpdateMediaRequest
|
|
52
|
-
} from
|
|
53
|
-
import { debug, logError, startTimer } from
|
|
51
|
+
UpdateMediaRequest,
|
|
52
|
+
} from "../types/wordpress.js";
|
|
53
|
+
import { debug, logError, startTimer } from "../utils/debug.js";
|
|
54
54
|
|
|
55
55
|
export class WordPressClient implements IWordPressClient {
|
|
56
56
|
private baseUrl: string;
|
|
@@ -66,18 +66,18 @@ export class WordPressClient implements IWordPressClient {
|
|
|
66
66
|
private _stats: ClientStats;
|
|
67
67
|
|
|
68
68
|
constructor(options: Partial<WordPressClientConfig> = {}) {
|
|
69
|
-
this.baseUrl = options.baseUrl || process.env.WORDPRESS_SITE_URL ||
|
|
70
|
-
this.apiUrl =
|
|
69
|
+
this.baseUrl = options.baseUrl || process.env.WORDPRESS_SITE_URL || "";
|
|
70
|
+
this.apiUrl = "";
|
|
71
71
|
this.timeout =
|
|
72
|
-
options.timeout || parseInt(process.env.WORDPRESS_TIMEOUT ||
|
|
72
|
+
options.timeout || parseInt(process.env.WORDPRESS_TIMEOUT || "30000");
|
|
73
73
|
this.maxRetries =
|
|
74
|
-
options.maxRetries || parseInt(process.env.WORDPRESS_MAX_RETRIES ||
|
|
74
|
+
options.maxRetries || parseInt(process.env.WORDPRESS_MAX_RETRIES || "3");
|
|
75
75
|
|
|
76
76
|
// Authentication configuration
|
|
77
77
|
this.auth = options.auth || this.getAuthFromEnv();
|
|
78
78
|
|
|
79
79
|
// Rate limiting
|
|
80
|
-
this.requestInterval = 60000 / parseInt(process.env.RATE_LIMIT ||
|
|
80
|
+
this.requestInterval = 60000 / parseInt(process.env.RATE_LIMIT || "60");
|
|
81
81
|
|
|
82
82
|
// Initialize stats
|
|
83
83
|
this._stats = {
|
|
@@ -86,7 +86,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
86
86
|
failedRequests: 0,
|
|
87
87
|
averageResponseTime: 0,
|
|
88
88
|
rateLimitHits: 0,
|
|
89
|
-
authFailures: 0
|
|
89
|
+
authFailures: 0,
|
|
90
90
|
};
|
|
91
91
|
|
|
92
92
|
// Validate configuration
|
|
@@ -98,7 +98,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
98
98
|
baseUrl: this.baseUrl,
|
|
99
99
|
auth: this.auth,
|
|
100
100
|
timeout: this.timeout,
|
|
101
|
-
maxRetries: this.maxRetries
|
|
101
|
+
maxRetries: this.maxRetries,
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
|
|
@@ -115,23 +115,23 @@ export class WordPressClient implements IWordPressClient {
|
|
|
115
115
|
|
|
116
116
|
// Use explicit auth method if set
|
|
117
117
|
if (
|
|
118
|
-
authMethod ===
|
|
118
|
+
authMethod === "app-password" &&
|
|
119
119
|
process.env.WORDPRESS_USERNAME &&
|
|
120
120
|
process.env.WORDPRESS_APP_PASSWORD
|
|
121
121
|
) {
|
|
122
122
|
return {
|
|
123
|
-
method:
|
|
123
|
+
method: "app-password",
|
|
124
124
|
username: process.env.WORDPRESS_USERNAME,
|
|
125
|
-
appPassword: process.env.WORDPRESS_APP_PASSWORD
|
|
125
|
+
appPassword: process.env.WORDPRESS_APP_PASSWORD,
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// Try Application Password first (fallback)
|
|
130
130
|
if (process.env.WORDPRESS_USERNAME && process.env.WORDPRESS_APP_PASSWORD) {
|
|
131
131
|
return {
|
|
132
|
-
method:
|
|
132
|
+
method: "app-password",
|
|
133
133
|
username: process.env.WORDPRESS_USERNAME,
|
|
134
|
-
appPassword: process.env.WORDPRESS_APP_PASSWORD
|
|
134
|
+
appPassword: process.env.WORDPRESS_APP_PASSWORD,
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -142,49 +142,49 @@ export class WordPressClient implements IWordPressClient {
|
|
|
142
142
|
process.env.WORDPRESS_PASSWORD
|
|
143
143
|
) {
|
|
144
144
|
return {
|
|
145
|
-
method:
|
|
145
|
+
method: "jwt",
|
|
146
146
|
secret: process.env.WORDPRESS_JWT_SECRET,
|
|
147
147
|
username: process.env.WORDPRESS_USERNAME,
|
|
148
|
-
password: process.env.WORDPRESS_PASSWORD
|
|
148
|
+
password: process.env.WORDPRESS_PASSWORD,
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
// Try API Key
|
|
153
153
|
if (process.env.WORDPRESS_API_KEY) {
|
|
154
154
|
return {
|
|
155
|
-
method:
|
|
156
|
-
apiKey: process.env.WORDPRESS_API_KEY
|
|
155
|
+
method: "api-key",
|
|
156
|
+
apiKey: process.env.WORDPRESS_API_KEY,
|
|
157
157
|
};
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
// Try Cookie
|
|
161
161
|
if (process.env.WORDPRESS_COOKIE_NONCE) {
|
|
162
162
|
return {
|
|
163
|
-
method:
|
|
164
|
-
nonce: process.env.WORDPRESS_COOKIE_NONCE
|
|
163
|
+
method: "cookie",
|
|
164
|
+
nonce: process.env.WORDPRESS_COOKIE_NONCE,
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
// Default to basic authentication
|
|
169
169
|
return {
|
|
170
|
-
method:
|
|
171
|
-
username: process.env.WORDPRESS_USERNAME ||
|
|
170
|
+
method: "basic",
|
|
171
|
+
username: process.env.WORDPRESS_USERNAME || "",
|
|
172
172
|
password:
|
|
173
173
|
process.env.WORDPRESS_PASSWORD ||
|
|
174
174
|
process.env.WORDPRESS_APP_PASSWORD ||
|
|
175
|
-
|
|
175
|
+
"",
|
|
176
176
|
};
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
private validateConfig(): void {
|
|
180
180
|
if (!this.baseUrl) {
|
|
181
181
|
throw new Error(
|
|
182
|
-
|
|
182
|
+
"WordPress configuration is incomplete: baseUrl is required",
|
|
183
183
|
);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
// Ensure URL doesn't end with slash and add API path
|
|
187
|
-
this.baseUrl = this.baseUrl.replace(/\/$/,
|
|
187
|
+
this.baseUrl = this.baseUrl.replace(/\/$/, "");
|
|
188
188
|
this.apiUrl = `${this.baseUrl}/wp-json/wp/v2`;
|
|
189
189
|
|
|
190
190
|
debug.log(`WordPress API Client initialized for: ${this.apiUrl}`);
|
|
@@ -197,7 +197,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
197
197
|
async disconnect(): Promise<void> {
|
|
198
198
|
this.authenticated = false;
|
|
199
199
|
this.jwtToken = null;
|
|
200
|
-
debug.log(
|
|
200
|
+
debug.log("WordPress client disconnected");
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
/**
|
|
@@ -207,38 +207,38 @@ export class WordPressClient implements IWordPressClient {
|
|
|
207
207
|
const method = this.auth.method?.toLowerCase() as AuthMethod;
|
|
208
208
|
|
|
209
209
|
switch (method) {
|
|
210
|
-
case
|
|
210
|
+
case "app-password":
|
|
211
211
|
if (this.auth.username && this.auth.appPassword) {
|
|
212
212
|
const credentials = Buffer.from(
|
|
213
|
-
`${this.auth.username}:${this.auth.appPassword}
|
|
214
|
-
).toString(
|
|
215
|
-
headers[
|
|
213
|
+
`${this.auth.username}:${this.auth.appPassword}`,
|
|
214
|
+
).toString("base64");
|
|
215
|
+
headers["Authorization"] = `Basic ${credentials}`;
|
|
216
216
|
}
|
|
217
217
|
break;
|
|
218
|
-
case
|
|
218
|
+
case "basic":
|
|
219
219
|
if (this.auth.username && this.auth.password) {
|
|
220
220
|
const credentials = Buffer.from(
|
|
221
|
-
`${this.auth.username}:${this.auth.password}
|
|
222
|
-
).toString(
|
|
223
|
-
headers[
|
|
221
|
+
`${this.auth.username}:${this.auth.password}`,
|
|
222
|
+
).toString("base64");
|
|
223
|
+
headers["Authorization"] = `Basic ${credentials}`;
|
|
224
224
|
}
|
|
225
225
|
break;
|
|
226
226
|
|
|
227
|
-
case
|
|
227
|
+
case "jwt":
|
|
228
228
|
if (this.jwtToken) {
|
|
229
|
-
headers[
|
|
229
|
+
headers["Authorization"] = `Bearer ${this.jwtToken}`;
|
|
230
230
|
}
|
|
231
231
|
break;
|
|
232
232
|
|
|
233
|
-
case
|
|
233
|
+
case "api-key":
|
|
234
234
|
if (this.auth.apiKey) {
|
|
235
|
-
headers[
|
|
235
|
+
headers["X-API-Key"] = this.auth.apiKey;
|
|
236
236
|
}
|
|
237
237
|
break;
|
|
238
238
|
|
|
239
|
-
case
|
|
239
|
+
case "cookie":
|
|
240
240
|
if (this.auth.nonce) {
|
|
241
|
-
headers[
|
|
241
|
+
headers["X-WP-Nonce"] = this.auth.nonce;
|
|
242
242
|
}
|
|
243
243
|
break;
|
|
244
244
|
}
|
|
@@ -271,14 +271,14 @@ export class WordPressClient implements IWordPressClient {
|
|
|
271
271
|
|
|
272
272
|
try {
|
|
273
273
|
switch (method) {
|
|
274
|
-
case
|
|
275
|
-
case
|
|
274
|
+
case "app-password":
|
|
275
|
+
case "basic":
|
|
276
276
|
return await this.authenticateWithBasic();
|
|
277
|
-
case
|
|
277
|
+
case "jwt":
|
|
278
278
|
return await this.authenticateWithJWT();
|
|
279
|
-
case
|
|
279
|
+
case "cookie":
|
|
280
280
|
return await this.authenticateWithCookie();
|
|
281
|
-
case
|
|
281
|
+
case "api-key":
|
|
282
282
|
// API key auth doesn't require separate authentication step
|
|
283
283
|
this.authenticated = true;
|
|
284
284
|
return true;
|
|
@@ -298,31 +298,31 @@ export class WordPressClient implements IWordPressClient {
|
|
|
298
298
|
private async authenticateWithBasic(): Promise<boolean> {
|
|
299
299
|
const hasCredentials =
|
|
300
300
|
this.auth.username &&
|
|
301
|
-
(this.auth.method ===
|
|
301
|
+
(this.auth.method === "app-password"
|
|
302
302
|
? this.auth.appPassword
|
|
303
303
|
: this.auth.password);
|
|
304
304
|
|
|
305
305
|
if (!hasCredentials) {
|
|
306
306
|
const methodName =
|
|
307
|
-
this.auth.method ===
|
|
307
|
+
this.auth.method === "app-password" ? "Application Password" : "Basic";
|
|
308
308
|
const passwordField =
|
|
309
|
-
this.auth.method ===
|
|
309
|
+
this.auth.method === "app-password" ? "app password" : "password";
|
|
310
310
|
throw new AuthenticationError(
|
|
311
311
|
`Username and ${passwordField} are required for ${methodName} authentication`,
|
|
312
|
-
this.auth.method
|
|
312
|
+
this.auth.method,
|
|
313
313
|
);
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
try {
|
|
317
317
|
// Test authentication by getting current user
|
|
318
|
-
await this.request<WordPressUser>(
|
|
318
|
+
await this.request<WordPressUser>("GET", "users/me");
|
|
319
319
|
this.authenticated = true;
|
|
320
|
-
debug.log(
|
|
320
|
+
debug.log("Basic/Application Password authentication successful");
|
|
321
321
|
return true;
|
|
322
322
|
} catch (error) {
|
|
323
323
|
throw new AuthenticationError(
|
|
324
324
|
`Basic authentication failed: ${(error as Error).message}`,
|
|
325
|
-
this.auth.method
|
|
325
|
+
this.auth.method,
|
|
326
326
|
);
|
|
327
327
|
}
|
|
328
328
|
}
|
|
@@ -333,8 +333,8 @@ export class WordPressClient implements IWordPressClient {
|
|
|
333
333
|
private async authenticateWithJWT(): Promise<boolean> {
|
|
334
334
|
if (!this.auth.secret || !this.auth.username || !this.auth.password) {
|
|
335
335
|
throw new AuthenticationError(
|
|
336
|
-
|
|
337
|
-
this.auth.method
|
|
336
|
+
"JWT secret, username, and password are required for JWT authentication",
|
|
337
|
+
this.auth.method,
|
|
338
338
|
);
|
|
339
339
|
}
|
|
340
340
|
|
|
@@ -342,15 +342,15 @@ export class WordPressClient implements IWordPressClient {
|
|
|
342
342
|
const response = await fetch(
|
|
343
343
|
`${this.baseUrl}/wp-json/jwt-auth/v1/token`,
|
|
344
344
|
{
|
|
345
|
-
method:
|
|
345
|
+
method: "POST",
|
|
346
346
|
headers: {
|
|
347
|
-
|
|
347
|
+
"Content-Type": "application/json",
|
|
348
348
|
},
|
|
349
349
|
body: JSON.stringify({
|
|
350
350
|
username: this.auth.username,
|
|
351
|
-
password: this.auth.password
|
|
352
|
-
})
|
|
353
|
-
}
|
|
351
|
+
password: this.auth.password,
|
|
352
|
+
}),
|
|
353
|
+
},
|
|
354
354
|
);
|
|
355
355
|
|
|
356
356
|
if (!response.ok) {
|
|
@@ -360,12 +360,12 @@ export class WordPressClient implements IWordPressClient {
|
|
|
360
360
|
const data = (await response.json()) as { token: string };
|
|
361
361
|
this.jwtToken = data.token;
|
|
362
362
|
this.authenticated = true;
|
|
363
|
-
debug.log(
|
|
363
|
+
debug.log("JWT authentication successful");
|
|
364
364
|
return true;
|
|
365
365
|
} catch (error) {
|
|
366
366
|
throw new AuthenticationError(
|
|
367
367
|
`JWT authentication failed: ${(error as Error).message}`,
|
|
368
|
-
this.auth.method
|
|
368
|
+
this.auth.method,
|
|
369
369
|
);
|
|
370
370
|
}
|
|
371
371
|
}
|
|
@@ -376,12 +376,12 @@ export class WordPressClient implements IWordPressClient {
|
|
|
376
376
|
private async authenticateWithCookie(): Promise<boolean> {
|
|
377
377
|
if (!this.auth.nonce) {
|
|
378
378
|
throw new AuthenticationError(
|
|
379
|
-
|
|
380
|
-
this.auth.method
|
|
379
|
+
"Nonce is required for cookie authentication",
|
|
380
|
+
this.auth.method,
|
|
381
381
|
);
|
|
382
382
|
}
|
|
383
383
|
this.authenticated = true;
|
|
384
|
-
debug.log(
|
|
384
|
+
debug.log("Cookie authentication configured");
|
|
385
385
|
return true;
|
|
386
386
|
}
|
|
387
387
|
|
|
@@ -392,21 +392,21 @@ export class WordPressClient implements IWordPressClient {
|
|
|
392
392
|
method: HTTPMethod,
|
|
393
393
|
endpoint: string,
|
|
394
394
|
data: any = null,
|
|
395
|
-
options: RequestOptions = {}
|
|
395
|
+
options: RequestOptions = {},
|
|
396
396
|
): Promise<T> {
|
|
397
397
|
const timer = startTimer();
|
|
398
398
|
this._stats.totalRequests++;
|
|
399
399
|
|
|
400
400
|
// Handle endpoint properly - remove leading slash if present to avoid double slashes
|
|
401
|
-
const cleanEndpoint = endpoint.replace(/^\/+/,
|
|
402
|
-
const url = endpoint.startsWith(
|
|
401
|
+
const cleanEndpoint = endpoint.replace(/^\/+/, "");
|
|
402
|
+
const url = endpoint.startsWith("http")
|
|
403
403
|
? endpoint
|
|
404
404
|
: `${this.apiUrl}/${cleanEndpoint}`;
|
|
405
405
|
|
|
406
406
|
const headers: Record<string, string> = {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
...options.headers
|
|
407
|
+
"Content-Type": "application/json",
|
|
408
|
+
"User-Agent": "MCP-WordPress/1.0.0",
|
|
409
|
+
...options.headers,
|
|
410
410
|
};
|
|
411
411
|
|
|
412
412
|
// Add authentication headers
|
|
@@ -418,25 +418,25 @@ export class WordPressClient implements IWordPressClient {
|
|
|
418
418
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
|
419
419
|
|
|
420
420
|
const fetchOptions: any = {
|
|
421
|
+
...options, // Spread options first
|
|
421
422
|
method,
|
|
422
|
-
headers,
|
|
423
|
+
headers, // Headers come after to ensure auth headers aren't overridden
|
|
423
424
|
signal: controller.signal,
|
|
424
|
-
...options
|
|
425
425
|
};
|
|
426
426
|
|
|
427
427
|
// Add body for POST/PUT/PATCH requests
|
|
428
|
-
if (data && [
|
|
428
|
+
if (data && ["POST", "PUT", "PATCH"].includes(method)) {
|
|
429
429
|
if (
|
|
430
430
|
data instanceof FormData ||
|
|
431
|
-
(data && typeof data.append ===
|
|
431
|
+
(data && typeof data.append === "function")
|
|
432
432
|
) {
|
|
433
433
|
// For FormData, don't set Content-Type (let fetch set it with boundary)
|
|
434
|
-
delete headers[
|
|
434
|
+
delete headers["Content-Type"];
|
|
435
435
|
fetchOptions.body = data;
|
|
436
436
|
} else if (Buffer.isBuffer(data)) {
|
|
437
437
|
// For Buffer data (manual multipart), keep Content-Type from headers
|
|
438
438
|
fetchOptions.body = data;
|
|
439
|
-
} else if (typeof data ===
|
|
439
|
+
} else if (typeof data === "string") {
|
|
440
440
|
fetchOptions.body = data;
|
|
441
441
|
} else {
|
|
442
442
|
fetchOptions.body = JSON.stringify(data);
|
|
@@ -446,11 +446,11 @@ export class WordPressClient implements IWordPressClient {
|
|
|
446
446
|
// Rate limiting
|
|
447
447
|
await this.rateLimit();
|
|
448
448
|
|
|
449
|
-
let lastError: Error = new Error(
|
|
449
|
+
let lastError: Error = new Error("Unknown error");
|
|
450
450
|
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
451
451
|
try {
|
|
452
452
|
debug.log(
|
|
453
|
-
`API Request: ${method} ${url}${attempt > 0 ? ` (attempt ${attempt + 1})` :
|
|
453
|
+
`API Request: ${method} ${url}${attempt > 0 ? ` (attempt ${attempt + 1})` : ""}`,
|
|
454
454
|
);
|
|
455
455
|
|
|
456
456
|
const response = await fetch(url, fetchOptions);
|
|
@@ -479,29 +479,29 @@ export class WordPressClient implements IWordPressClient {
|
|
|
479
479
|
// Handle permission errors specifically for uploads
|
|
480
480
|
if (
|
|
481
481
|
response.status === 403 &&
|
|
482
|
-
endpoint.includes(
|
|
483
|
-
method ===
|
|
482
|
+
endpoint.includes("media") &&
|
|
483
|
+
method === "POST"
|
|
484
484
|
) {
|
|
485
485
|
throw new AuthenticationError(
|
|
486
|
-
|
|
486
|
+
"Media upload blocked: WordPress REST API media uploads appear to be disabled or restricted by a plugin/security policy. " +
|
|
487
487
|
`Error: ${errorMessage}. ` +
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
this.auth.method
|
|
488
|
+
"Common causes: W3 Total Cache, security plugins, or custom REST API restrictions. " +
|
|
489
|
+
"Please check WordPress admin settings or contact your system administrator.",
|
|
490
|
+
this.auth.method,
|
|
491
491
|
);
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
// Handle general upload permission errors
|
|
495
495
|
if (
|
|
496
|
-
errorMessage.includes(
|
|
497
|
-
endpoint.includes(
|
|
496
|
+
errorMessage.includes("Beiträge zu erstellen") &&
|
|
497
|
+
endpoint.includes("media")
|
|
498
498
|
) {
|
|
499
499
|
throw new AuthenticationError(
|
|
500
500
|
`WordPress REST API media upload restriction detected: ${errorMessage}. ` +
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this.auth.method
|
|
501
|
+
"This typically indicates that media uploads via REST API are disabled by WordPress configuration, " +
|
|
502
|
+
"a security plugin (like W3 Total Cache, Borlabs Cookie), or server policy. " +
|
|
503
|
+
"User has sufficient permissions but WordPress/plugins are blocking the upload.",
|
|
504
|
+
this.auth.method,
|
|
505
505
|
);
|
|
506
506
|
}
|
|
507
507
|
|
|
@@ -525,9 +525,9 @@ export class WordPressClient implements IWordPressClient {
|
|
|
525
525
|
return result as T;
|
|
526
526
|
} catch (parseError) {
|
|
527
527
|
// For authentication requests, malformed JSON should be an error
|
|
528
|
-
if (endpoint.includes(
|
|
528
|
+
if (endpoint.includes("users/me") || endpoint.includes("jwt-auth")) {
|
|
529
529
|
throw new WordPressAPIError(
|
|
530
|
-
`Invalid JSON response: ${(parseError as Error).message}
|
|
530
|
+
`Invalid JSON response: ${(parseError as Error).message}`,
|
|
531
531
|
);
|
|
532
532
|
}
|
|
533
533
|
this._stats.successfulRequests++;
|
|
@@ -540,30 +540,30 @@ export class WordPressClient implements IWordPressClient {
|
|
|
540
540
|
lastError = error as Error;
|
|
541
541
|
|
|
542
542
|
// Handle timeout errors
|
|
543
|
-
if ((error as any).name ===
|
|
543
|
+
if ((error as any).name === "AbortError") {
|
|
544
544
|
lastError = new Error(`Request timeout after ${requestTimeout}ms`);
|
|
545
545
|
}
|
|
546
546
|
|
|
547
547
|
// Handle network errors
|
|
548
548
|
if (
|
|
549
|
-
lastError.message.includes(
|
|
550
|
-
lastError.message.includes(
|
|
549
|
+
lastError.message.includes("socket hang up") ||
|
|
550
|
+
lastError.message.includes("ECONNRESET")
|
|
551
551
|
) {
|
|
552
552
|
lastError = new Error(
|
|
553
|
-
`Network connection lost during upload: ${lastError.message}
|
|
553
|
+
`Network connection lost during upload: ${lastError.message}`,
|
|
554
554
|
);
|
|
555
555
|
}
|
|
556
556
|
|
|
557
557
|
debug.log(
|
|
558
|
-
`Request failed (attempt ${attempt + 1}): ${lastError.message}
|
|
558
|
+
`Request failed (attempt ${attempt + 1}): ${lastError.message}`,
|
|
559
559
|
);
|
|
560
560
|
|
|
561
561
|
// Don't retry on authentication errors, timeouts, or critical network errors
|
|
562
562
|
if (
|
|
563
|
-
lastError.message.includes(
|
|
564
|
-
lastError.message.includes(
|
|
565
|
-
lastError.message.includes(
|
|
566
|
-
lastError.message.includes(
|
|
563
|
+
lastError.message.includes("401") ||
|
|
564
|
+
lastError.message.includes("403") ||
|
|
565
|
+
lastError.message.includes("timeout") ||
|
|
566
|
+
lastError.message.includes("Network connection lost")
|
|
567
567
|
) {
|
|
568
568
|
break;
|
|
569
569
|
}
|
|
@@ -577,7 +577,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
577
577
|
this._stats.failedRequests++;
|
|
578
578
|
timer.end();
|
|
579
579
|
throw new WordPressAPIError(
|
|
580
|
-
`Request failed after ${this.maxRetries} attempts: ${lastError.message}
|
|
580
|
+
`Request failed after ${this.maxRetries} attempts: ${lastError.message}`,
|
|
581
581
|
);
|
|
582
582
|
}
|
|
583
583
|
|
|
@@ -591,38 +591,38 @@ export class WordPressClient implements IWordPressClient {
|
|
|
591
591
|
|
|
592
592
|
// HTTP method helpers
|
|
593
593
|
async get<T = any>(endpoint: string, options?: RequestOptions): Promise<T> {
|
|
594
|
-
return this.request<T>(
|
|
594
|
+
return this.request<T>("GET", endpoint, null, options);
|
|
595
595
|
}
|
|
596
596
|
|
|
597
597
|
async post<T = any>(
|
|
598
598
|
endpoint: string,
|
|
599
599
|
data?: any,
|
|
600
|
-
options?: RequestOptions
|
|
600
|
+
options?: RequestOptions,
|
|
601
601
|
): Promise<T> {
|
|
602
|
-
return this.request<T>(
|
|
602
|
+
return this.request<T>("POST", endpoint, data, options);
|
|
603
603
|
}
|
|
604
604
|
|
|
605
605
|
async put<T = any>(
|
|
606
606
|
endpoint: string,
|
|
607
607
|
data?: any,
|
|
608
|
-
options?: RequestOptions
|
|
608
|
+
options?: RequestOptions,
|
|
609
609
|
): Promise<T> {
|
|
610
|
-
return this.request<T>(
|
|
610
|
+
return this.request<T>("PUT", endpoint, data, options);
|
|
611
611
|
}
|
|
612
612
|
|
|
613
613
|
async patch<T = any>(
|
|
614
614
|
endpoint: string,
|
|
615
615
|
data?: any,
|
|
616
|
-
options?: RequestOptions
|
|
616
|
+
options?: RequestOptions,
|
|
617
617
|
): Promise<T> {
|
|
618
|
-
return this.request<T>(
|
|
618
|
+
return this.request<T>("PATCH", endpoint, data, options);
|
|
619
619
|
}
|
|
620
620
|
|
|
621
621
|
async delete<T = any>(
|
|
622
622
|
endpoint: string,
|
|
623
|
-
options?: RequestOptions
|
|
623
|
+
options?: RequestOptions,
|
|
624
624
|
): Promise<T> {
|
|
625
|
-
return this.request<T>(
|
|
625
|
+
return this.request<T>("DELETE", endpoint, null, options);
|
|
626
626
|
}
|
|
627
627
|
|
|
628
628
|
// WordPress API Methods
|
|
@@ -630,20 +630,20 @@ export class WordPressClient implements IWordPressClient {
|
|
|
630
630
|
// Posts
|
|
631
631
|
async getPosts(params?: PostQueryParams): Promise<WordPressPost[]> {
|
|
632
632
|
const queryString = params
|
|
633
|
-
?
|
|
634
|
-
:
|
|
633
|
+
? "?" + new URLSearchParams(params as any).toString()
|
|
634
|
+
: "";
|
|
635
635
|
return this.get<WordPressPost[]>(`posts${queryString}`);
|
|
636
636
|
}
|
|
637
637
|
|
|
638
638
|
async getPost(
|
|
639
639
|
id: number,
|
|
640
|
-
context:
|
|
640
|
+
context: "view" | "embed" | "edit" = "view",
|
|
641
641
|
): Promise<WordPressPost> {
|
|
642
642
|
return this.get<WordPressPost>(`posts/${id}?context=${context}`);
|
|
643
643
|
}
|
|
644
644
|
|
|
645
645
|
async createPost(data: CreatePostRequest): Promise<WordPressPost> {
|
|
646
|
-
return this.post<WordPressPost>(
|
|
646
|
+
return this.post<WordPressPost>("posts", data);
|
|
647
647
|
}
|
|
648
648
|
|
|
649
649
|
async updatePost(data: UpdatePostRequest): Promise<WordPressPost> {
|
|
@@ -653,7 +653,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
653
653
|
|
|
654
654
|
async deletePost(
|
|
655
655
|
id: number,
|
|
656
|
-
force = false
|
|
656
|
+
force = false,
|
|
657
657
|
): Promise<{ deleted: boolean; previous?: WordPressPost }> {
|
|
658
658
|
return this.delete(`posts/${id}?force=${force}`);
|
|
659
659
|
}
|
|
@@ -665,20 +665,20 @@ export class WordPressClient implements IWordPressClient {
|
|
|
665
665
|
// Pages
|
|
666
666
|
async getPages(params?: PostQueryParams): Promise<WordPressPage[]> {
|
|
667
667
|
const queryString = params
|
|
668
|
-
?
|
|
669
|
-
:
|
|
668
|
+
? "?" + new URLSearchParams(params as any).toString()
|
|
669
|
+
: "";
|
|
670
670
|
return this.get<WordPressPage[]>(`pages${queryString}`);
|
|
671
671
|
}
|
|
672
672
|
|
|
673
673
|
async getPage(
|
|
674
674
|
id: number,
|
|
675
|
-
context:
|
|
675
|
+
context: "view" | "embed" | "edit" = "view",
|
|
676
676
|
): Promise<WordPressPage> {
|
|
677
677
|
return this.get<WordPressPage>(`pages/${id}?context=${context}`);
|
|
678
678
|
}
|
|
679
679
|
|
|
680
680
|
async createPage(data: CreatePageRequest): Promise<WordPressPage> {
|
|
681
|
-
return this.post<WordPressPage>(
|
|
681
|
+
return this.post<WordPressPage>("pages", data);
|
|
682
682
|
}
|
|
683
683
|
|
|
684
684
|
async updatePage(data: UpdatePageRequest): Promise<WordPressPage> {
|
|
@@ -688,7 +688,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
688
688
|
|
|
689
689
|
async deletePage(
|
|
690
690
|
id: number,
|
|
691
|
-
force = false
|
|
691
|
+
force = false,
|
|
692
692
|
): Promise<{ deleted: boolean; previous?: WordPressPage }> {
|
|
693
693
|
return this.delete(`pages/${id}?force=${force}`);
|
|
694
694
|
}
|
|
@@ -700,14 +700,14 @@ export class WordPressClient implements IWordPressClient {
|
|
|
700
700
|
// Media
|
|
701
701
|
async getMedia(params?: MediaQueryParams): Promise<WordPressMedia[]> {
|
|
702
702
|
const queryString = params
|
|
703
|
-
?
|
|
704
|
-
:
|
|
703
|
+
? "?" + new URLSearchParams(params as any).toString()
|
|
704
|
+
: "";
|
|
705
705
|
return this.get<WordPressMedia[]>(`media${queryString}`);
|
|
706
706
|
}
|
|
707
707
|
|
|
708
708
|
async getMediaItem(
|
|
709
709
|
id: number,
|
|
710
|
-
context:
|
|
710
|
+
context: "view" | "embed" | "edit" = "view",
|
|
711
711
|
): Promise<WordPressMedia> {
|
|
712
712
|
return this.get<WordPressMedia>(`media/${id}?context=${context}`);
|
|
713
713
|
}
|
|
@@ -725,19 +725,19 @@ export class WordPressClient implements IWordPressClient {
|
|
|
725
725
|
const maxSize = 10 * 1024 * 1024; // 10MB reasonable limit
|
|
726
726
|
if (stats.size > maxSize) {
|
|
727
727
|
throw new Error(
|
|
728
|
-
`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB. Maximum allowed: ${maxSize / 1024 / 1024}MB
|
|
728
|
+
`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB. Maximum allowed: ${maxSize / 1024 / 1024}MB`,
|
|
729
729
|
);
|
|
730
730
|
}
|
|
731
731
|
|
|
732
732
|
debug.log(
|
|
733
|
-
`Uploading file: ${filename} (${(stats.size / 1024).toFixed(2)}KB)
|
|
733
|
+
`Uploading file: ${filename} (${(stats.size / 1024).toFixed(2)}KB)`,
|
|
734
734
|
);
|
|
735
735
|
|
|
736
736
|
return this.uploadFile(
|
|
737
737
|
fileBuffer,
|
|
738
738
|
filename,
|
|
739
739
|
this.getMimeType(data.file_path),
|
|
740
|
-
data
|
|
740
|
+
data,
|
|
741
741
|
);
|
|
742
742
|
}
|
|
743
743
|
|
|
@@ -746,7 +746,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
746
746
|
filename: string,
|
|
747
747
|
mimeType: string,
|
|
748
748
|
meta: Partial<UploadMediaRequest> = {},
|
|
749
|
-
options?: RequestOptions
|
|
749
|
+
options?: RequestOptions,
|
|
750
750
|
): Promise<WordPressMedia> {
|
|
751
751
|
debug.log(`Uploading file: ${filename} (${fileData.length} bytes)`);
|
|
752
752
|
|
|
@@ -755,30 +755,30 @@ export class WordPressClient implements IWordPressClient {
|
|
|
755
755
|
formData.setMaxListeners(20);
|
|
756
756
|
|
|
757
757
|
// Add file with correct options
|
|
758
|
-
formData.append(
|
|
758
|
+
formData.append("file", fileData, {
|
|
759
759
|
filename,
|
|
760
|
-
contentType: mimeType
|
|
760
|
+
contentType: mimeType,
|
|
761
761
|
});
|
|
762
762
|
|
|
763
763
|
// Add metadata
|
|
764
|
-
if (meta.title) formData.append(
|
|
765
|
-
if (meta.alt_text) formData.append(
|
|
766
|
-
if (meta.caption) formData.append(
|
|
767
|
-
if (meta.description) formData.append(
|
|
768
|
-
if (meta.post) formData.append(
|
|
764
|
+
if (meta.title) formData.append("title", meta.title);
|
|
765
|
+
if (meta.alt_text) formData.append("alt_text", meta.alt_text);
|
|
766
|
+
if (meta.caption) formData.append("caption", meta.caption);
|
|
767
|
+
if (meta.description) formData.append("description", meta.description);
|
|
768
|
+
if (meta.post) formData.append("post", meta.post.toString());
|
|
769
769
|
|
|
770
770
|
// Use longer timeout for file uploads
|
|
771
771
|
const uploadTimeout =
|
|
772
772
|
options?.timeout !== undefined ? options.timeout : 600000; // 10 minutes default
|
|
773
773
|
const uploadOptions: RequestOptions = {
|
|
774
774
|
...options,
|
|
775
|
-
timeout: uploadTimeout
|
|
775
|
+
timeout: uploadTimeout,
|
|
776
776
|
};
|
|
777
777
|
|
|
778
778
|
debug.log(`Upload prepared with FormData, timeout: ${uploadTimeout}ms`);
|
|
779
779
|
|
|
780
780
|
// Use the regular post method which handles FormData correctly
|
|
781
|
-
return this.post<WordPressMedia>(
|
|
781
|
+
return this.post<WordPressMedia>("media", formData, uploadOptions);
|
|
782
782
|
}
|
|
783
783
|
|
|
784
784
|
async updateMedia(data: UpdateMediaRequest): Promise<WordPressMedia> {
|
|
@@ -788,7 +788,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
788
788
|
|
|
789
789
|
async deleteMedia(
|
|
790
790
|
id: number,
|
|
791
|
-
force = false
|
|
791
|
+
force = false,
|
|
792
792
|
): Promise<{ deleted: boolean; previous?: WordPressMedia }> {
|
|
793
793
|
return this.delete(`media/${id}?force=${force}`);
|
|
794
794
|
}
|
|
@@ -796,20 +796,20 @@ export class WordPressClient implements IWordPressClient {
|
|
|
796
796
|
// Users
|
|
797
797
|
async getUsers(params?: UserQueryParams): Promise<WordPressUser[]> {
|
|
798
798
|
const queryString = params
|
|
799
|
-
?
|
|
800
|
-
:
|
|
799
|
+
? "?" + new URLSearchParams(params as any).toString()
|
|
800
|
+
: "";
|
|
801
801
|
return this.get<WordPressUser[]>(`users${queryString}`);
|
|
802
802
|
}
|
|
803
803
|
|
|
804
804
|
async getUser(
|
|
805
|
-
id: number |
|
|
806
|
-
context:
|
|
805
|
+
id: number | "me",
|
|
806
|
+
context: "view" | "embed" | "edit" = "view",
|
|
807
807
|
): Promise<WordPressUser> {
|
|
808
808
|
return this.get<WordPressUser>(`users/${id}?context=${context}`);
|
|
809
809
|
}
|
|
810
810
|
|
|
811
811
|
async createUser(data: CreateUserRequest): Promise<WordPressUser> {
|
|
812
|
-
return this.post<WordPressUser>(
|
|
812
|
+
return this.post<WordPressUser>("users", data);
|
|
813
813
|
}
|
|
814
814
|
|
|
815
815
|
async updateUser(data: UpdateUserRequest): Promise<WordPressUser> {
|
|
@@ -819,35 +819,35 @@ export class WordPressClient implements IWordPressClient {
|
|
|
819
819
|
|
|
820
820
|
async deleteUser(
|
|
821
821
|
id: number,
|
|
822
|
-
reassign?: number
|
|
822
|
+
reassign?: number,
|
|
823
823
|
): Promise<{ deleted: boolean; previous?: WordPressUser }> {
|
|
824
824
|
const params = reassign
|
|
825
825
|
? `?reassign=${reassign}&force=true`
|
|
826
|
-
:
|
|
826
|
+
: "?force=true";
|
|
827
827
|
return this.delete(`users/${id}${params}`);
|
|
828
828
|
}
|
|
829
829
|
|
|
830
830
|
async getCurrentUser(): Promise<WordPressUser> {
|
|
831
|
-
return this.getUser(
|
|
831
|
+
return this.getUser("me");
|
|
832
832
|
}
|
|
833
833
|
|
|
834
834
|
// Comments
|
|
835
835
|
async getComments(params?: CommentQueryParams): Promise<WordPressComment[]> {
|
|
836
836
|
const queryString = params
|
|
837
|
-
?
|
|
838
|
-
:
|
|
837
|
+
? "?" + new URLSearchParams(params as any).toString()
|
|
838
|
+
: "";
|
|
839
839
|
return this.get<WordPressComment[]>(`comments${queryString}`);
|
|
840
840
|
}
|
|
841
841
|
|
|
842
842
|
async getComment(
|
|
843
843
|
id: number,
|
|
844
|
-
context:
|
|
844
|
+
context: "view" | "embed" | "edit" = "view",
|
|
845
845
|
): Promise<WordPressComment> {
|
|
846
846
|
return this.get<WordPressComment>(`comments/${id}?context=${context}`);
|
|
847
847
|
}
|
|
848
848
|
|
|
849
849
|
async createComment(data: CreateCommentRequest): Promise<WordPressComment> {
|
|
850
|
-
return this.post<WordPressComment>(
|
|
850
|
+
return this.post<WordPressComment>("comments", data);
|
|
851
851
|
}
|
|
852
852
|
|
|
853
853
|
async updateComment(data: UpdateCommentRequest): Promise<WordPressComment> {
|
|
@@ -857,30 +857,30 @@ export class WordPressClient implements IWordPressClient {
|
|
|
857
857
|
|
|
858
858
|
async deleteComment(
|
|
859
859
|
id: number,
|
|
860
|
-
force = false
|
|
860
|
+
force = false,
|
|
861
861
|
): Promise<{ deleted: boolean; previous?: WordPressComment }> {
|
|
862
862
|
return this.delete(`comments/${id}?force=${force}`);
|
|
863
863
|
}
|
|
864
864
|
|
|
865
865
|
async approveComment(id: number): Promise<WordPressComment> {
|
|
866
|
-
return this.put<WordPressComment>(`comments/${id}`, { status:
|
|
866
|
+
return this.put<WordPressComment>(`comments/${id}`, { status: "approved" });
|
|
867
867
|
}
|
|
868
868
|
|
|
869
869
|
async rejectComment(id: number): Promise<WordPressComment> {
|
|
870
870
|
return this.put<WordPressComment>(`comments/${id}`, {
|
|
871
|
-
status:
|
|
871
|
+
status: "unapproved",
|
|
872
872
|
});
|
|
873
873
|
}
|
|
874
874
|
|
|
875
875
|
async spamComment(id: number): Promise<WordPressComment> {
|
|
876
|
-
return this.put<WordPressComment>(`comments/${id}`, { status:
|
|
876
|
+
return this.put<WordPressComment>(`comments/${id}`, { status: "spam" });
|
|
877
877
|
}
|
|
878
878
|
|
|
879
879
|
// Taxonomies
|
|
880
880
|
async getCategories(params?: any): Promise<WordPressCategory[]> {
|
|
881
881
|
const queryString = params
|
|
882
|
-
?
|
|
883
|
-
:
|
|
882
|
+
? "?" + new URLSearchParams(params).toString()
|
|
883
|
+
: "";
|
|
884
884
|
return this.get<WordPressCategory[]>(`categories${queryString}`);
|
|
885
885
|
}
|
|
886
886
|
|
|
@@ -889,13 +889,13 @@ export class WordPressClient implements IWordPressClient {
|
|
|
889
889
|
}
|
|
890
890
|
|
|
891
891
|
async createCategory(
|
|
892
|
-
data: CreateCategoryRequest
|
|
892
|
+
data: CreateCategoryRequest,
|
|
893
893
|
): Promise<WordPressCategory> {
|
|
894
|
-
return this.post<WordPressCategory>(
|
|
894
|
+
return this.post<WordPressCategory>("categories", data);
|
|
895
895
|
}
|
|
896
896
|
|
|
897
897
|
async updateCategory(
|
|
898
|
-
data: UpdateCategoryRequest
|
|
898
|
+
data: UpdateCategoryRequest,
|
|
899
899
|
): Promise<WordPressCategory> {
|
|
900
900
|
const { id, ...updateData } = data;
|
|
901
901
|
return this.put<WordPressCategory>(`categories/${id}`, updateData);
|
|
@@ -903,15 +903,15 @@ export class WordPressClient implements IWordPressClient {
|
|
|
903
903
|
|
|
904
904
|
async deleteCategory(
|
|
905
905
|
id: number,
|
|
906
|
-
force = false
|
|
906
|
+
force = false,
|
|
907
907
|
): Promise<{ deleted: boolean; previous?: WordPressCategory }> {
|
|
908
908
|
return this.delete(`categories/${id}?force=${force}`);
|
|
909
909
|
}
|
|
910
910
|
|
|
911
911
|
async getTags(params?: any): Promise<WordPressTag[]> {
|
|
912
912
|
const queryString = params
|
|
913
|
-
?
|
|
914
|
-
:
|
|
913
|
+
? "?" + new URLSearchParams(params).toString()
|
|
914
|
+
: "";
|
|
915
915
|
return this.get<WordPressTag[]>(`tags${queryString}`);
|
|
916
916
|
}
|
|
917
917
|
|
|
@@ -920,7 +920,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
920
920
|
}
|
|
921
921
|
|
|
922
922
|
async createTag(data: CreateTagRequest): Promise<WordPressTag> {
|
|
923
|
-
return this.post<WordPressTag>(
|
|
923
|
+
return this.post<WordPressTag>("tags", data);
|
|
924
924
|
}
|
|
925
925
|
|
|
926
926
|
async updateTag(data: UpdateTagRequest): Promise<WordPressTag> {
|
|
@@ -930,51 +930,51 @@ export class WordPressClient implements IWordPressClient {
|
|
|
930
930
|
|
|
931
931
|
async deleteTag(
|
|
932
932
|
id: number,
|
|
933
|
-
force = false
|
|
933
|
+
force = false,
|
|
934
934
|
): Promise<{ deleted: boolean; previous?: WordPressTag }> {
|
|
935
935
|
return this.delete(`tags/${id}?force=${force}`);
|
|
936
936
|
}
|
|
937
937
|
|
|
938
938
|
// Site Management
|
|
939
939
|
async getSiteSettings(): Promise<WordPressSiteSettings> {
|
|
940
|
-
return this.get<WordPressSiteSettings>(
|
|
940
|
+
return this.get<WordPressSiteSettings>("settings");
|
|
941
941
|
}
|
|
942
942
|
|
|
943
943
|
async updateSiteSettings(
|
|
944
|
-
settings: Partial<WordPressSiteSettings
|
|
944
|
+
settings: Partial<WordPressSiteSettings>,
|
|
945
945
|
): Promise<WordPressSiteSettings> {
|
|
946
|
-
return this.post<WordPressSiteSettings>(
|
|
946
|
+
return this.post<WordPressSiteSettings>("settings", settings);
|
|
947
947
|
}
|
|
948
948
|
|
|
949
949
|
async getSiteInfo(): Promise<any> {
|
|
950
|
-
return this.get(
|
|
950
|
+
return this.get("");
|
|
951
951
|
}
|
|
952
952
|
|
|
953
953
|
// Application Passwords
|
|
954
954
|
async getApplicationPasswords(
|
|
955
|
-
userId: number |
|
|
955
|
+
userId: number | "me" = "me",
|
|
956
956
|
): Promise<WordPressApplicationPassword[]> {
|
|
957
957
|
return this.get<WordPressApplicationPassword[]>(
|
|
958
|
-
`users/${userId}/application-passwords
|
|
958
|
+
`users/${userId}/application-passwords`,
|
|
959
959
|
);
|
|
960
960
|
}
|
|
961
961
|
|
|
962
962
|
async createApplicationPassword(
|
|
963
|
-
userId: number |
|
|
963
|
+
userId: number | "me",
|
|
964
964
|
name: string,
|
|
965
|
-
appId?: string
|
|
965
|
+
appId?: string,
|
|
966
966
|
): Promise<WordPressApplicationPassword> {
|
|
967
967
|
const data: any = { name };
|
|
968
968
|
if (appId) data.app_id = appId;
|
|
969
969
|
return this.post<WordPressApplicationPassword>(
|
|
970
970
|
`users/${userId}/application-passwords`,
|
|
971
|
-
data
|
|
971
|
+
data,
|
|
972
972
|
);
|
|
973
973
|
}
|
|
974
974
|
|
|
975
975
|
async deleteApplicationPassword(
|
|
976
|
-
userId: number |
|
|
977
|
-
uuid: string
|
|
976
|
+
userId: number | "me",
|
|
977
|
+
uuid: string,
|
|
978
978
|
): Promise<{ deleted: boolean }> {
|
|
979
979
|
return this.delete(`users/${userId}/application-passwords/${uuid}`);
|
|
980
980
|
}
|
|
@@ -983,11 +983,11 @@ export class WordPressClient implements IWordPressClient {
|
|
|
983
983
|
async search(
|
|
984
984
|
query: string,
|
|
985
985
|
types?: string[],
|
|
986
|
-
subtype?: string
|
|
986
|
+
subtype?: string,
|
|
987
987
|
): Promise<any[]> {
|
|
988
988
|
const params = new URLSearchParams({ search: query });
|
|
989
|
-
if (types) params.append(
|
|
990
|
-
if (subtype) params.append(
|
|
989
|
+
if (types) params.append("type", types.join(","));
|
|
990
|
+
if (subtype) params.append("subtype", subtype);
|
|
991
991
|
|
|
992
992
|
return this.get<any[]>(`search?${params.toString()}`);
|
|
993
993
|
}
|
|
@@ -995,7 +995,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
995
995
|
// Utility Methods
|
|
996
996
|
async ping(): Promise<boolean> {
|
|
997
997
|
try {
|
|
998
|
-
await this.get(
|
|
998
|
+
await this.get("");
|
|
999
999
|
return true;
|
|
1000
1000
|
} catch {
|
|
1001
1001
|
return false;
|
|
@@ -1003,7 +1003,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
1003
1003
|
}
|
|
1004
1004
|
|
|
1005
1005
|
async getServerInfo(): Promise<Record<string, any>> {
|
|
1006
|
-
return this.get(
|
|
1006
|
+
return this.get("");
|
|
1007
1007
|
}
|
|
1008
1008
|
|
|
1009
1009
|
validateEndpoint(endpoint: string): boolean {
|
|
@@ -1011,7 +1011,7 @@ export class WordPressClient implements IWordPressClient {
|
|
|
1011
1011
|
}
|
|
1012
1012
|
|
|
1013
1013
|
buildUrl(endpoint: string, params?: Record<string, any>): string {
|
|
1014
|
-
const url = `${this.apiUrl}/${endpoint.replace(/^\/+/,
|
|
1014
|
+
const url = `${this.apiUrl}/${endpoint.replace(/^\/+/, "")}`;
|
|
1015
1015
|
if (params) {
|
|
1016
1016
|
const searchParams = new URLSearchParams(params);
|
|
1017
1017
|
return `${url}?${searchParams.toString()}`;
|
|
@@ -1022,22 +1022,22 @@ export class WordPressClient implements IWordPressClient {
|
|
|
1022
1022
|
private getMimeType(filePath: string): string {
|
|
1023
1023
|
const ext = path.extname(filePath).toLowerCase();
|
|
1024
1024
|
const mimeTypes: Record<string, string> = {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1025
|
+
".jpg": "image/jpeg",
|
|
1026
|
+
".jpeg": "image/jpeg",
|
|
1027
|
+
".png": "image/png",
|
|
1028
|
+
".gif": "image/gif",
|
|
1029
|
+
".webp": "image/webp",
|
|
1030
|
+
".svg": "image/svg+xml",
|
|
1031
|
+
".pdf": "application/pdf",
|
|
1032
|
+
".doc": "application/msword",
|
|
1033
|
+
".docx":
|
|
1034
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1035
|
+
".txt": "text/plain",
|
|
1036
|
+
".mp4": "video/mp4",
|
|
1037
|
+
".mp3": "audio/mpeg",
|
|
1038
|
+
".wav": "audio/wav",
|
|
1039
1039
|
};
|
|
1040
1040
|
|
|
1041
|
-
return mimeTypes[ext] ||
|
|
1041
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
1042
1042
|
}
|
|
1043
1043
|
}
|