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.
Files changed (289) hide show
  1. package/package.json +1 -1
  2. package/dist/src/cache/CacheInvalidation.d.ts +0 -120
  3. package/dist/src/cache/CacheInvalidation.d.ts.map +0 -1
  4. package/dist/src/cache/CacheInvalidation.js +0 -355
  5. package/dist/src/cache/CacheInvalidation.js.map +0 -1
  6. package/dist/src/cache/CacheManager.d.ts +0 -149
  7. package/dist/src/cache/CacheManager.d.ts.map +0 -1
  8. package/dist/src/cache/CacheManager.js +0 -326
  9. package/dist/src/cache/CacheManager.js.map +0 -1
  10. package/dist/src/cache/HttpCacheWrapper.d.ts +0 -122
  11. package/dist/src/cache/HttpCacheWrapper.d.ts.map +0 -1
  12. package/dist/src/cache/HttpCacheWrapper.js +0 -283
  13. package/dist/src/cache/HttpCacheWrapper.js.map +0 -1
  14. package/dist/src/cache/index.d.ts +0 -12
  15. package/dist/src/cache/index.d.ts.map +0 -1
  16. package/dist/src/cache/index.js +0 -9
  17. package/dist/src/cache/index.js.map +0 -1
  18. package/dist/src/client/CachedWordPressClient.d.ts +0 -174
  19. package/dist/src/client/CachedWordPressClient.d.ts.map +0 -1
  20. package/dist/src/client/CachedWordPressClient.js +0 -345
  21. package/dist/src/client/CachedWordPressClient.js.map +0 -1
  22. package/dist/src/client/MockWordPressClient.d.ts +0 -56
  23. package/dist/src/client/MockWordPressClient.d.ts.map +0 -1
  24. package/dist/src/client/MockWordPressClient.js +0 -371
  25. package/dist/src/client/MockWordPressClient.js.map +0 -1
  26. package/dist/src/client/api.d.ts +0 -235
  27. package/dist/src/client/api.d.ts.map +0 -1
  28. package/dist/src/client/api.js +0 -896
  29. package/dist/src/client/api.js.map +0 -1
  30. package/dist/src/client/auth.d.ts +0 -121
  31. package/dist/src/client/auth.d.ts.map +0 -1
  32. package/dist/src/client/auth.js +0 -429
  33. package/dist/src/client/auth.js.map +0 -1
  34. package/dist/src/client/managers/AuthenticationManager.d.ts +0 -92
  35. package/dist/src/client/managers/AuthenticationManager.d.ts.map +0 -1
  36. package/dist/src/client/managers/AuthenticationManager.js +0 -369
  37. package/dist/src/client/managers/AuthenticationManager.js.map +0 -1
  38. package/dist/src/client/managers/BaseManager.d.ts +0 -22
  39. package/dist/src/client/managers/BaseManager.d.ts.map +0 -1
  40. package/dist/src/client/managers/BaseManager.js +0 -53
  41. package/dist/src/client/managers/BaseManager.js.map +0 -1
  42. package/dist/src/client/managers/RequestManager.d.ts +0 -47
  43. package/dist/src/client/managers/RequestManager.d.ts.map +0 -1
  44. package/dist/src/client/managers/RequestManager.js +0 -193
  45. package/dist/src/client/managers/RequestManager.js.map +0 -1
  46. package/dist/src/client/managers/index.d.ts +0 -8
  47. package/dist/src/client/managers/index.d.ts.map +0 -1
  48. package/dist/src/client/managers/index.js +0 -8
  49. package/dist/src/client/managers/index.js.map +0 -1
  50. package/dist/src/config/Config.d.ts +0 -155
  51. package/dist/src/config/Config.d.ts.map +0 -1
  52. package/dist/src/config/Config.js +0 -215
  53. package/dist/src/config/Config.js.map +0 -1
  54. package/dist/src/config/ConfigurationSchema.d.ts +0 -281
  55. package/dist/src/config/ConfigurationSchema.d.ts.map +0 -1
  56. package/dist/src/config/ConfigurationSchema.js +0 -205
  57. package/dist/src/config/ConfigurationSchema.js.map +0 -1
  58. package/dist/src/config/ServerConfiguration.d.ts +0 -47
  59. package/dist/src/config/ServerConfiguration.d.ts.map +0 -1
  60. package/dist/src/config/ServerConfiguration.js +0 -255
  61. package/dist/src/config/ServerConfiguration.js.map +0 -1
  62. package/dist/src/docs/DocumentationGenerator.d.ts +0 -185
  63. package/dist/src/docs/DocumentationGenerator.d.ts.map +0 -1
  64. package/dist/src/docs/DocumentationGenerator.js +0 -777
  65. package/dist/src/docs/DocumentationGenerator.js.map +0 -1
  66. package/dist/src/docs/MarkdownFormatter.d.ts +0 -84
  67. package/dist/src/docs/MarkdownFormatter.d.ts.map +0 -1
  68. package/dist/src/docs/MarkdownFormatter.js +0 -458
  69. package/dist/src/docs/MarkdownFormatter.js.map +0 -1
  70. package/dist/src/docs/index.d.ts +0 -8
  71. package/dist/src/docs/index.d.ts.map +0 -1
  72. package/dist/src/docs/index.js +0 -7
  73. package/dist/src/docs/index.js.map +0 -1
  74. package/dist/src/dxt-entry.d.ts +0 -6
  75. package/dist/src/dxt-entry.d.ts.map +0 -1
  76. package/dist/src/dxt-entry.js +0 -39
  77. package/dist/src/dxt-entry.js.map +0 -1
  78. package/dist/src/index.d.ts +0 -18
  79. package/dist/src/index.d.ts.map +0 -1
  80. package/dist/src/index.js +0 -143
  81. package/dist/src/index.js.map +0 -1
  82. package/dist/src/performance/MetricsCollector.d.ts +0 -145
  83. package/dist/src/performance/MetricsCollector.d.ts.map +0 -1
  84. package/dist/src/performance/MetricsCollector.js +0 -368
  85. package/dist/src/performance/MetricsCollector.js.map +0 -1
  86. package/dist/src/performance/PerformanceAnalytics.d.ts +0 -168
  87. package/dist/src/performance/PerformanceAnalytics.d.ts.map +0 -1
  88. package/dist/src/performance/PerformanceAnalytics.js +0 -570
  89. package/dist/src/performance/PerformanceAnalytics.js.map +0 -1
  90. package/dist/src/performance/PerformanceMonitor.d.ts +0 -203
  91. package/dist/src/performance/PerformanceMonitor.d.ts.map +0 -1
  92. package/dist/src/performance/PerformanceMonitor.js +0 -478
  93. package/dist/src/performance/PerformanceMonitor.js.map +0 -1
  94. package/dist/src/performance/index.d.ts +0 -11
  95. package/dist/src/performance/index.d.ts.map +0 -1
  96. package/dist/src/performance/index.js +0 -8
  97. package/dist/src/performance/index.js.map +0 -1
  98. package/dist/src/security/AISecurityScanner.d.ts +0 -176
  99. package/dist/src/security/AISecurityScanner.d.ts.map +0 -1
  100. package/dist/src/security/AISecurityScanner.js +0 -655
  101. package/dist/src/security/AISecurityScanner.js.map +0 -1
  102. package/dist/src/security/AutomatedRemediation.d.ts +0 -146
  103. package/dist/src/security/AutomatedRemediation.d.ts.map +0 -1
  104. package/dist/src/security/AutomatedRemediation.js +0 -566
  105. package/dist/src/security/AutomatedRemediation.js.map +0 -1
  106. package/dist/src/security/InputValidator.d.ts +0 -219
  107. package/dist/src/security/InputValidator.d.ts.map +0 -1
  108. package/dist/src/security/InputValidator.js +0 -295
  109. package/dist/src/security/InputValidator.js.map +0 -1
  110. package/dist/src/security/SecurityCIPipeline.d.ts +0 -213
  111. package/dist/src/security/SecurityCIPipeline.d.ts.map +0 -1
  112. package/dist/src/security/SecurityCIPipeline.js +0 -693
  113. package/dist/src/security/SecurityCIPipeline.js.map +0 -1
  114. package/dist/src/security/SecurityConfig.d.ts +0 -129
  115. package/dist/src/security/SecurityConfig.d.ts.map +0 -1
  116. package/dist/src/security/SecurityConfig.js +0 -266
  117. package/dist/src/security/SecurityConfig.js.map +0 -1
  118. package/dist/src/security/SecurityConfigManager.d.ts +0 -294
  119. package/dist/src/security/SecurityConfigManager.d.ts.map +0 -1
  120. package/dist/src/security/SecurityConfigManager.js +0 -558
  121. package/dist/src/security/SecurityConfigManager.js.map +0 -1
  122. package/dist/src/security/SecurityMonitoring.d.ts +0 -245
  123. package/dist/src/security/SecurityMonitoring.d.ts.map +0 -1
  124. package/dist/src/security/SecurityMonitoring.js +0 -598
  125. package/dist/src/security/SecurityMonitoring.js.map +0 -1
  126. package/dist/src/security/SecurityReviewer.d.ts +0 -168
  127. package/dist/src/security/SecurityReviewer.d.ts.map +0 -1
  128. package/dist/src/security/SecurityReviewer.js +0 -686
  129. package/dist/src/security/SecurityReviewer.js.map +0 -1
  130. package/dist/src/security/index.d.ts +0 -183
  131. package/dist/src/security/index.d.ts.map +0 -1
  132. package/dist/src/security/index.js +0 -218
  133. package/dist/src/security/index.js.map +0 -1
  134. package/dist/src/server/ConnectionTester.d.ts +0 -32
  135. package/dist/src/server/ConnectionTester.d.ts.map +0 -1
  136. package/dist/src/server/ConnectionTester.js +0 -135
  137. package/dist/src/server/ConnectionTester.js.map +0 -1
  138. package/dist/src/server/ToolRegistry.d.ts +0 -50
  139. package/dist/src/server/ToolRegistry.d.ts.map +0 -1
  140. package/dist/src/server/ToolRegistry.js +0 -219
  141. package/dist/src/server/ToolRegistry.js.map +0 -1
  142. package/dist/src/server.d.ts +0 -7
  143. package/dist/src/server.d.ts.map +0 -1
  144. package/dist/src/server.js +0 -7
  145. package/dist/src/server.js.map +0 -1
  146. package/dist/src/tools/BaseToolManager.d.ts +0 -62
  147. package/dist/src/tools/BaseToolManager.d.ts.map +0 -1
  148. package/dist/src/tools/BaseToolManager.js +0 -195
  149. package/dist/src/tools/BaseToolManager.js.map +0 -1
  150. package/dist/src/tools/auth.d.ts +0 -50
  151. package/dist/src/tools/auth.d.ts.map +0 -1
  152. package/dist/src/tools/auth.js +0 -133
  153. package/dist/src/tools/auth.js.map +0 -1
  154. package/dist/src/tools/cache.d.ts +0 -260
  155. package/dist/src/tools/cache.d.ts.map +0 -1
  156. package/dist/src/tools/cache.js +0 -232
  157. package/dist/src/tools/cache.js.map +0 -1
  158. package/dist/src/tools/comments.d.ts +0 -33
  159. package/dist/src/tools/comments.d.ts.map +0 -1
  160. package/dist/src/tools/comments.js +0 -235
  161. package/dist/src/tools/comments.js.map +0 -1
  162. package/dist/src/tools/index.d.ts +0 -11
  163. package/dist/src/tools/index.d.ts.map +0 -1
  164. package/dist/src/tools/index.js +0 -11
  165. package/dist/src/tools/index.js.map +0 -1
  166. package/dist/src/tools/media.d.ts +0 -70
  167. package/dist/src/tools/media.d.ts.map +0 -1
  168. package/dist/src/tools/media.js +0 -248
  169. package/dist/src/tools/media.js.map +0 -1
  170. package/dist/src/tools/pages.d.ts +0 -32
  171. package/dist/src/tools/pages.d.ts.map +0 -1
  172. package/dist/src/tools/pages.js +0 -215
  173. package/dist/src/tools/pages.js.map +0 -1
  174. package/dist/src/tools/performance.d.ts +0 -73
  175. package/dist/src/tools/performance.d.ts.map +0 -1
  176. package/dist/src/tools/performance.js +0 -922
  177. package/dist/src/tools/performance.js.map +0 -1
  178. package/dist/src/tools/posts/PostHandlers.d.ts +0 -46
  179. package/dist/src/tools/posts/PostHandlers.d.ts.map +0 -1
  180. package/dist/src/tools/posts/PostHandlers.js +0 -400
  181. package/dist/src/tools/posts/PostHandlers.js.map +0 -1
  182. package/dist/src/tools/posts/PostToolDefinitions.d.ts +0 -37
  183. package/dist/src/tools/posts/PostToolDefinitions.d.ts.map +0 -1
  184. package/dist/src/tools/posts/PostToolDefinitions.js +0 -236
  185. package/dist/src/tools/posts/PostToolDefinitions.js.map +0 -1
  186. package/dist/src/tools/posts/index.d.ts +0 -138
  187. package/dist/src/tools/posts/index.d.ts.map +0 -1
  188. package/dist/src/tools/posts/index.js +0 -163
  189. package/dist/src/tools/posts/index.js.map +0 -1
  190. package/dist/src/tools/posts.d.ts +0 -15
  191. package/dist/src/tools/posts.d.ts.map +0 -1
  192. package/dist/src/tools/posts.js +0 -16
  193. package/dist/src/tools/posts.js.map +0 -1
  194. package/dist/src/tools/site.d.ts +0 -32
  195. package/dist/src/tools/site.d.ts.map +0 -1
  196. package/dist/src/tools/site.js +0 -234
  197. package/dist/src/tools/site.js.map +0 -1
  198. package/dist/src/tools/taxonomies.d.ts +0 -36
  199. package/dist/src/tools/taxonomies.d.ts.map +0 -1
  200. package/dist/src/tools/taxonomies.js +0 -286
  201. package/dist/src/tools/taxonomies.js.map +0 -1
  202. package/dist/src/tools/users.d.ts +0 -33
  203. package/dist/src/tools/users.d.ts.map +0 -1
  204. package/dist/src/tools/users.js +0 -308
  205. package/dist/src/tools/users.js.map +0 -1
  206. package/dist/src/types/client.d.ts +0 -223
  207. package/dist/src/types/client.d.ts.map +0 -1
  208. package/dist/src/types/client.js +0 -97
  209. package/dist/src/types/client.js.map +0 -1
  210. package/dist/src/types/enhanced.d.ts +0 -237
  211. package/dist/src/types/enhanced.d.ts.map +0 -1
  212. package/dist/src/types/enhanced.js +0 -49
  213. package/dist/src/types/enhanced.js.map +0 -1
  214. package/dist/src/types/index.d.ts +0 -160
  215. package/dist/src/types/index.d.ts.map +0 -1
  216. package/dist/src/types/index.js +0 -14
  217. package/dist/src/types/index.js.map +0 -1
  218. package/dist/src/types/mcp.d.ts +0 -178
  219. package/dist/src/types/mcp.d.ts.map +0 -1
  220. package/dist/src/types/mcp.js +0 -7
  221. package/dist/src/types/mcp.js.map +0 -1
  222. package/dist/src/types/requests.d.ts +0 -322
  223. package/dist/src/types/requests.d.ts.map +0 -1
  224. package/dist/src/types/requests.js +0 -8
  225. package/dist/src/types/requests.js.map +0 -1
  226. package/dist/src/types/tools.d.ts +0 -506
  227. package/dist/src/types/tools.d.ts.map +0 -1
  228. package/dist/src/types/tools.js +0 -8
  229. package/dist/src/types/tools.js.map +0 -1
  230. package/dist/src/types/wordpress.d.ts +0 -471
  231. package/dist/src/types/wordpress.d.ts.map +0 -1
  232. package/dist/src/types/wordpress.js +0 -14
  233. package/dist/src/types/wordpress.js.map +0 -1
  234. package/dist/src/utils/debug.d.ts +0 -71
  235. package/dist/src/utils/debug.d.ts.map +0 -1
  236. package/dist/src/utils/debug.js +0 -235
  237. package/dist/src/utils/debug.js.map +0 -1
  238. package/dist/src/utils/enhancedError.d.ts +0 -61
  239. package/dist/src/utils/enhancedError.d.ts.map +0 -1
  240. package/dist/src/utils/enhancedError.js +0 -221
  241. package/dist/src/utils/enhancedError.js.map +0 -1
  242. package/dist/src/utils/error.d.ts +0 -17
  243. package/dist/src/utils/error.d.ts.map +0 -1
  244. package/dist/src/utils/error.js +0 -108
  245. package/dist/src/utils/error.js.map +0 -1
  246. package/dist/src/utils/logger.d.ts +0 -106
  247. package/dist/src/utils/logger.d.ts.map +0 -1
  248. package/dist/src/utils/logger.js +0 -280
  249. package/dist/src/utils/logger.js.map +0 -1
  250. package/dist/src/utils/streaming.d.ts +0 -104
  251. package/dist/src/utils/streaming.d.ts.map +0 -1
  252. package/dist/src/utils/streaming.js +0 -331
  253. package/dist/src/utils/streaming.js.map +0 -1
  254. package/dist/src/utils/toolWrapper.d.ts +0 -42
  255. package/dist/src/utils/toolWrapper.d.ts.map +0 -1
  256. package/dist/src/utils/toolWrapper.js +0 -101
  257. package/dist/src/utils/toolWrapper.js.map +0 -1
  258. package/dist/src/utils/validation/core.d.ts +0 -21
  259. package/dist/src/utils/validation/core.d.ts.map +0 -1
  260. package/dist/src/utils/validation/core.js +0 -71
  261. package/dist/src/utils/validation/core.js.map +0 -1
  262. package/dist/src/utils/validation/index.d.ts +0 -25
  263. package/dist/src/utils/validation/index.d.ts.map +0 -1
  264. package/dist/src/utils/validation/index.js +0 -29
  265. package/dist/src/utils/validation/index.js.map +0 -1
  266. package/dist/src/utils/validation/network.d.ts +0 -19
  267. package/dist/src/utils/validation/network.d.ts.map +0 -1
  268. package/dist/src/utils/validation/network.js +0 -93
  269. package/dist/src/utils/validation/network.js.map +0 -1
  270. package/dist/src/utils/validation/rateLimit.d.ts +0 -21
  271. package/dist/src/utils/validation/rateLimit.d.ts.map +0 -1
  272. package/dist/src/utils/validation/rateLimit.js +0 -43
  273. package/dist/src/utils/validation/rateLimit.js.map +0 -1
  274. package/dist/src/utils/validation/security.d.ts +0 -29
  275. package/dist/src/utils/validation/security.d.ts.map +0 -1
  276. package/dist/src/utils/validation/security.js +0 -327
  277. package/dist/src/utils/validation/security.js.map +0 -1
  278. package/dist/src/utils/validation/wordpress.d.ts +0 -31
  279. package/dist/src/utils/validation/wordpress.d.ts.map +0 -1
  280. package/dist/src/utils/validation/wordpress.js +0 -146
  281. package/dist/src/utils/validation/wordpress.js.map +0 -1
  282. package/dist/src/utils/validation.d.ts +0 -15
  283. package/dist/src/utils/validation.d.ts.map +0 -1
  284. package/dist/src/utils/validation.js +0 -27
  285. package/dist/src/utils/validation.js.map +0 -1
  286. package/dist/tests/vitest.setup.d.ts +0 -6
  287. package/dist/tests/vitest.setup.d.ts.map +0 -1
  288. package/dist/tests/vitest.setup.js +0 -39
  289. package/dist/tests/vitest.setup.js.map +0 -1
@@ -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