mcp-wordpress 2.6.4 → 2.7.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 +1 -1
- package/dist/cache/CacheInvalidation.d.ts +25 -6
- package/dist/cache/CacheInvalidation.d.ts.map +1 -1
- package/dist/cache/CacheInvalidation.js +168 -16
- package/dist/cache/CacheInvalidation.js.map +1 -1
- package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
- package/dist/cache/HttpCacheWrapper.js +3 -4
- package/dist/cache/HttpCacheWrapper.js.map +1 -1
- package/dist/cache/SEOCacheManager.d.ts +150 -0
- package/dist/cache/SEOCacheManager.d.ts.map +1 -0
- package/dist/cache/SEOCacheManager.js +275 -0
- package/dist/cache/SEOCacheManager.js.map +1 -0
- package/dist/client/SEOWordPressClient.d.ts +164 -0
- package/dist/client/SEOWordPressClient.d.ts.map +1 -0
- package/dist/client/SEOWordPressClient.js +674 -0
- package/dist/client/SEOWordPressClient.js.map +1 -0
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +50 -20
- package/dist/client/api.js.map +1 -1
- package/dist/client/auth.js +19 -19
- package/dist/client/auth.js.map +1 -1
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +14 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/managers/AuthManager.d.ts +39 -0
- package/dist/client/managers/AuthManager.d.ts.map +1 -0
- package/dist/client/managers/AuthManager.js +142 -0
- package/dist/client/managers/AuthManager.js.map +1 -0
- package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
- package/dist/client/managers/AuthenticationManager.js +10 -9
- package/dist/client/managers/AuthenticationManager.js.map +1 -1
- package/dist/client/managers/BaseManager.d.ts.map +1 -1
- package/dist/client/managers/BaseManager.js +12 -0
- package/dist/client/managers/BaseManager.js.map +1 -1
- package/dist/client/managers/ComposedAuthenticationManager.d.ts +94 -0
- package/dist/client/managers/ComposedAuthenticationManager.d.ts.map +1 -0
- package/dist/client/managers/ComposedAuthenticationManager.js +340 -0
- package/dist/client/managers/ComposedAuthenticationManager.js.map +1 -0
- package/dist/client/managers/ComposedManagerFactory.d.ts +104 -0
- package/dist/client/managers/ComposedManagerFactory.d.ts.map +1 -0
- package/dist/client/managers/ComposedManagerFactory.js +180 -0
- package/dist/client/managers/ComposedManagerFactory.js.map +1 -0
- package/dist/client/managers/ComposedRequestManager.d.ts +82 -0
- package/dist/client/managers/ComposedRequestManager.d.ts.map +1 -0
- package/dist/client/managers/ComposedRequestManager.js +260 -0
- package/dist/client/managers/ComposedRequestManager.js.map +1 -0
- package/dist/client/managers/JWTAuthImplementation.d.ts +86 -0
- package/dist/client/managers/JWTAuthImplementation.d.ts.map +1 -0
- package/dist/client/managers/JWTAuthImplementation.js +240 -0
- package/dist/client/managers/JWTAuthImplementation.js.map +1 -0
- package/dist/client/managers/ManagersIndex.d.ts +6 -0
- package/dist/client/managers/ManagersIndex.d.ts.map +1 -0
- package/dist/client/managers/ManagersIndex.js +6 -0
- package/dist/client/managers/ManagersIndex.js.map +1 -0
- package/dist/client/managers/RequestManager.d.ts.map +1 -1
- package/dist/client/managers/RequestManager.js +5 -3
- package/dist/client/managers/RequestManager.js.map +1 -1
- package/dist/client/managers/composed/MigrationAdapter.d.ts +80 -0
- package/dist/client/managers/composed/MigrationAdapter.d.ts.map +1 -0
- package/dist/client/managers/composed/MigrationAdapter.js +214 -0
- package/dist/client/managers/composed/MigrationAdapter.js.map +1 -0
- package/dist/client/managers/composed/index.d.ts +23 -0
- package/dist/client/managers/composed/index.d.ts.map +1 -0
- package/dist/client/managers/composed/index.js +26 -0
- package/dist/client/managers/composed/index.js.map +1 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts +27 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.js +41 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.js.map +1 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts +31 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.js +73 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.js.map +1 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts +47 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.js +141 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.js.map +1 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.d.ts +147 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.d.ts.map +1 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.js +6 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.js.map +1 -0
- package/dist/config/Config.d.ts +30 -0
- package/dist/config/Config.d.ts.map +1 -1
- package/dist/config/Config.js +30 -0
- package/dist/config/Config.js.map +1 -1
- package/dist/config/ConfigurationSchema.d.ts +75 -198
- package/dist/config/ConfigurationSchema.d.ts.map +1 -1
- package/dist/config/ConfigurationSchema.js +17 -17
- package/dist/config/ConfigurationSchema.js.map +1 -1
- package/dist/config/ServerConfiguration.d.ts +2 -2
- package/dist/config/ServerConfiguration.d.ts.map +1 -1
- package/dist/config/ServerConfiguration.js +15 -13
- package/dist/config/ServerConfiguration.js.map +1 -1
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +11 -0
- package/dist/config/index.js.map +1 -0
- package/dist/docs/DocumentationGenerator.js +2 -2
- package/dist/docs/DocumentationGenerator.js.map +1 -1
- package/dist/dxt-entry.js +3 -3
- package/dist/dxt-entry.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -37
- package/dist/index.js.map +1 -1
- package/dist/performance/MetricsCollector.d.ts.map +1 -1
- package/dist/performance/MetricsCollector.js +5 -4
- package/dist/performance/MetricsCollector.js.map +1 -1
- package/dist/security/AISecurityScanner.js +7 -7
- package/dist/security/AISecurityScanner.js.map +1 -1
- package/dist/security/AutomatedRemediation.d.ts.map +1 -1
- package/dist/security/AutomatedRemediation.js +11 -11
- package/dist/security/AutomatedRemediation.js.map +1 -1
- package/dist/security/InputValidator.d.ts +50 -126
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +9 -9
- package/dist/security/InputValidator.js.map +1 -1
- package/dist/security/SecurityCIPipeline.d.ts +47 -5
- package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
- package/dist/security/SecurityCIPipeline.js +390 -49
- package/dist/security/SecurityCIPipeline.js.map +1 -1
- package/dist/security/SecurityConfigManager.js +10 -10
- package/dist/security/SecurityConfigManager.js.map +1 -1
- package/dist/security/SecurityMonitoring.js +4 -4
- package/dist/security/SecurityMonitoring.js.map +1 -1
- package/dist/security/SecurityReviewer.d.ts.map +1 -1
- package/dist/security/SecurityReviewer.js +13 -6
- package/dist/security/SecurityReviewer.js.map +1 -1
- package/dist/security/index.js +3 -3
- package/dist/security/index.js.map +1 -1
- package/dist/server/ConnectionTester.js +5 -5
- 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 +7 -6
- package/dist/server/ToolRegistry.js.map +1 -1
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/tools/BaseToolManager.d.ts.map +1 -1
- package/dist/tools/BaseToolManager.js +11 -11
- package/dist/tools/BaseToolManager.js.map +1 -1
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +7 -7
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js +14 -14
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/media.d.ts.map +1 -1
- package/dist/tools/media.js +14 -11
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js +12 -12
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/performance.d.ts.map +1 -1
- package/dist/tools/performance.js +13 -11
- package/dist/tools/performance.js.map +1 -1
- package/dist/tools/posts/PostHandlers.d.ts.map +1 -1
- package/dist/tools/posts/PostHandlers.js +16 -16
- package/dist/tools/posts/PostHandlers.js.map +1 -1
- package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -1
- package/dist/tools/posts/index.d.ts.map +1 -1
- package/dist/tools/seo/BulkOperations.d.ts +113 -0
- package/dist/tools/seo/BulkOperations.d.ts.map +1 -0
- package/dist/tools/seo/BulkOperations.js +398 -0
- package/dist/tools/seo/BulkOperations.js.map +1 -0
- package/dist/tools/seo/SEOHandlers.d.ts +55 -0
- package/dist/tools/seo/SEOHandlers.d.ts.map +1 -0
- package/dist/tools/seo/SEOHandlers.js +255 -0
- package/dist/tools/seo/SEOHandlers.js.map +1 -0
- package/dist/tools/seo/SEOToolDefinitions.d.ts +59 -0
- package/dist/tools/seo/SEOToolDefinitions.d.ts.map +1 -0
- package/dist/tools/seo/SEOToolDefinitions.js +385 -0
- package/dist/tools/seo/SEOToolDefinitions.js.map +1 -0
- package/dist/tools/seo/SEOTools.d.ts +203 -0
- package/dist/tools/seo/SEOTools.d.ts.map +1 -0
- package/dist/tools/seo/SEOTools.js +708 -0
- package/dist/tools/seo/SEOTools.js.map +1 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts +94 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts.map +1 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.js +402 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.js.map +1 -0
- package/dist/tools/seo/auditors/SiteAuditor.d.ts +121 -0
- package/dist/tools/seo/auditors/SiteAuditor.d.ts.map +1 -0
- package/dist/tools/seo/auditors/SiteAuditor.js +600 -0
- package/dist/tools/seo/auditors/SiteAuditor.js.map +1 -0
- package/dist/tools/seo/generators/MetaGenerator.d.ts +128 -0
- package/dist/tools/seo/generators/MetaGenerator.d.ts.map +1 -0
- package/dist/tools/seo/generators/MetaGenerator.js +547 -0
- package/dist/tools/seo/generators/MetaGenerator.js.map +1 -0
- package/dist/tools/seo/generators/SchemaGenerator.d.ts +204 -0
- package/dist/tools/seo/generators/SchemaGenerator.d.ts.map +1 -0
- package/dist/tools/seo/generators/SchemaGenerator.js +670 -0
- package/dist/tools/seo/generators/SchemaGenerator.js.map +1 -0
- package/dist/tools/seo/index.d.ts +17 -0
- package/dist/tools/seo/index.d.ts.map +1 -0
- package/dist/tools/seo/index.js +18 -0
- package/dist/tools/seo/index.js.map +1 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts +186 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts.map +1 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.js +683 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.js.map +1 -0
- package/dist/tools/site.d.ts.map +1 -1
- package/dist/tools/site.js +12 -12
- package/dist/tools/site.js.map +1 -1
- package/dist/tools/taxonomies.d.ts.map +1 -1
- package/dist/tools/taxonomies.js +20 -20
- package/dist/tools/taxonomies.js.map +1 -1
- package/dist/tools/users.d.ts.map +1 -1
- package/dist/tools/users.js +12 -12
- package/dist/tools/users.js.map +1 -1
- package/dist/types/client.d.ts +8 -6
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/client.js.map +1 -1
- package/dist/types/seo.d.ts +473 -0
- package/dist/types/seo.d.ts.map +1 -0
- package/dist/types/seo.js +94 -0
- package/dist/types/seo.js.map +1 -0
- package/dist/utils/enhancedError.js +1 -1
- package/dist/utils/enhancedError.js.map +1 -1
- package/dist/utils/error.d.ts.map +1 -1
- package/dist/utils/error.js +0 -1
- package/dist/utils/error.js.map +1 -1
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.js +3 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/toolWrapper.d.ts +2 -2
- package/dist/utils/toolWrapper.js +8 -8
- package/dist/utils/toolWrapper.js.map +1 -1
- package/dist/utils/validation/core.d.ts.map +1 -1
- package/dist/utils/validation/core.js.map +1 -1
- package/dist/utils/validation/index.d.ts.map +1 -1
- package/dist/utils/validation/index.js.map +1 -1
- package/dist/utils/validation/network.js +3 -3
- package/dist/utils/validation/network.js.map +1 -1
- package/dist/utils/validation/rateLimit.js.map +1 -1
- package/dist/utils/validation/security.js.map +1 -1
- package/dist/utils/validation/wordpress.js.map +1 -1
- package/dist/utils/version.d.ts +144 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +318 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +21 -55
- package/src/cache/CacheInvalidation.ts +183 -20
- package/src/cache/HttpCacheWrapper.ts +8 -5
- package/src/cache/SEOCacheManager.ts +330 -0
- package/src/cache/__tests__/CacheInvalidation.test.ts +6 -11
- package/src/cache/__tests__/CachedWordPressClient.test.ts +37 -62
- package/src/client/SEOWordPressClient.ts +876 -0
- package/src/client/api.ts +50 -21
- package/src/client/auth.ts +19 -19
- package/src/client/index.ts +16 -0
- package/src/client/managers/AuthManager.ts +175 -0
- package/src/client/managers/AuthenticationManager.ts +16 -14
- package/src/client/managers/BaseManager.ts +24 -5
- package/src/client/managers/ComposedAuthenticationManager.ts +409 -0
- package/src/client/managers/ComposedManagerFactory.ts +231 -0
- package/src/client/managers/ComposedRequestManager.ts +336 -0
- package/src/client/managers/JWTAuthImplementation.ts +326 -0
- package/src/client/managers/ManagersIndex.ts +6 -0
- package/src/client/managers/RequestManager.ts +9 -7
- package/src/client/managers/composed/MigrationAdapter.ts +263 -0
- package/src/client/managers/composed/index.ts +47 -0
- package/src/client/managers/implementations/ConfigurationProviderImpl.ts +52 -0
- package/src/client/managers/implementations/ErrorHandlerImpl.ts +102 -0
- package/src/client/managers/implementations/ParameterValidatorImpl.ts +221 -0
- package/src/client/managers/interfaces/ManagerInterfaces.ts +171 -0
- package/src/config/Config.ts +63 -0
- package/src/config/ConfigurationSchema.ts +17 -17
- package/src/config/ServerConfiguration.ts +18 -16
- package/src/config/index.ts +13 -0
- package/src/docs/DocumentationGenerator.ts +2 -2
- package/src/dxt-entry.ts +3 -3
- package/src/index.ts +43 -43
- package/src/performance/MetricsCollector.ts +15 -11
- package/src/security/AISecurityScanner.ts +7 -7
- package/src/security/AutomatedRemediation.ts +13 -11
- package/src/security/InputValidator.ts +10 -9
- package/src/security/SecurityCIPipeline.ts +494 -56
- package/src/security/SecurityConfigManager.ts +10 -10
- package/src/security/SecurityMonitoring.ts +5 -5
- package/src/security/SecurityReviewer.ts +13 -6
- package/src/security/index.ts +3 -3
- package/src/server/ConnectionTester.ts +5 -5
- package/src/server/ToolRegistry.ts +9 -8
- package/src/server/index.ts +10 -0
- package/src/tools/BaseToolManager.ts +55 -83
- package/src/tools/auth.ts +21 -12
- package/src/tools/comments.ts +23 -19
- package/src/tools/index.ts +1 -0
- package/src/tools/media.ts +23 -20
- package/src/tools/pages.ts +20 -13
- package/src/tools/performance.ts +101 -32
- package/src/tools/posts/PostHandlers.ts +23 -23
- package/src/tools/posts/PostToolDefinitions.ts +1 -1
- package/src/tools/posts/index.ts +2 -2
- package/src/tools/seo/BulkOperations.ts +557 -0
- package/src/tools/seo/SEOHandlers.ts +296 -0
- package/src/tools/seo/SEOToolDefinitions.ts +402 -0
- package/src/tools/seo/SEOTools.ts +871 -0
- package/src/tools/seo/analyzers/ContentAnalyzer.ts +493 -0
- package/src/tools/seo/auditors/SiteAuditor.ts +787 -0
- package/src/tools/seo/generators/MetaGenerator.ts +694 -0
- package/src/tools/seo/generators/SchemaGenerator.ts +955 -0
- package/src/tools/seo/index.ts +47 -0
- package/src/tools/seo/optimizers/InternalLinkingSuggester.ts +934 -0
- package/src/tools/site.ts +27 -26
- package/src/tools/taxonomies.ts +29 -25
- package/src/tools/users.ts +20 -13
- package/src/types/client.ts +8 -6
- package/src/types/seo.ts +546 -0
- package/src/utils/enhancedError.ts +1 -1
- package/src/utils/error.ts +1 -2
- package/src/utils/index.ts +23 -0
- package/src/utils/logger.ts +3 -3
- package/src/utils/toolWrapper.ts +10 -10
- package/src/utils/validation/core.ts +2 -2
- package/src/utils/validation/index.ts +2 -2
- package/src/utils/validation/network.ts +5 -5
- package/src/utils/validation/rateLimit.ts +1 -1
- package/src/utils/validation/security.ts +1 -1
- package/src/utils/validation/wordpress.ts +1 -1
- package/src/utils/version.ts +402 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composed Request Manager
|
|
3
|
+
* Uses composition instead of inheritance for better testability and flexibility
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { HTTPMethod, RequestOptions, ClientStats, WordPressClientConfig } from "@/types/client.js";
|
|
7
|
+
import { WordPressAPIError, RateLimitError } from "@/types/client.js";
|
|
8
|
+
import { config } from "@/config/Config.js";
|
|
9
|
+
import { startTimer } from "@/utils/debug.js";
|
|
10
|
+
import { getUserAgent } from "@/utils/version.js";
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
ConfigurationProvider,
|
|
14
|
+
ErrorHandler,
|
|
15
|
+
ParameterValidator,
|
|
16
|
+
AuthenticationProvider,
|
|
17
|
+
RequestHandler,
|
|
18
|
+
} from "./interfaces/ManagerInterfaces.js";
|
|
19
|
+
|
|
20
|
+
import { ConfigurationProviderImpl } from "./implementations/ConfigurationProviderImpl.js";
|
|
21
|
+
import { ErrorHandlerImpl } from "./implementations/ErrorHandlerImpl.js";
|
|
22
|
+
import { ParameterValidatorImpl } from "./implementations/ParameterValidatorImpl.js";
|
|
23
|
+
|
|
24
|
+
interface ComposedRequestManagerDependencies {
|
|
25
|
+
configProvider: ConfigurationProvider;
|
|
26
|
+
errorHandler: ErrorHandler;
|
|
27
|
+
validator: ParameterValidator;
|
|
28
|
+
authProvider: AuthenticationProvider;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class ComposedRequestManager implements RequestHandler {
|
|
32
|
+
private stats: ClientStats;
|
|
33
|
+
private lastRequestTime: number = 0;
|
|
34
|
+
private requestInterval: number;
|
|
35
|
+
private initialized: boolean = false;
|
|
36
|
+
|
|
37
|
+
constructor(private dependencies: ComposedRequestManagerDependencies) {
|
|
38
|
+
this.requestInterval = 60000 / config().security.rateLimit;
|
|
39
|
+
this.stats = {
|
|
40
|
+
totalRequests: 0,
|
|
41
|
+
successfulRequests: 0,
|
|
42
|
+
failedRequests: 0,
|
|
43
|
+
averageResponseTime: 0,
|
|
44
|
+
rateLimitHits: 0,
|
|
45
|
+
authFailures: 0,
|
|
46
|
+
errors: 0,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Factory method to create ComposedRequestManager with default implementations
|
|
52
|
+
*/
|
|
53
|
+
static create(clientConfig: WordPressClientConfig, authProvider: AuthenticationProvider): ComposedRequestManager {
|
|
54
|
+
const configProvider = new ConfigurationProviderImpl(clientConfig);
|
|
55
|
+
const errorHandler = new ErrorHandlerImpl(configProvider);
|
|
56
|
+
const validator = new ParameterValidatorImpl();
|
|
57
|
+
|
|
58
|
+
return new ComposedRequestManager({
|
|
59
|
+
configProvider,
|
|
60
|
+
errorHandler,
|
|
61
|
+
validator,
|
|
62
|
+
authProvider,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Initialize the manager
|
|
68
|
+
*/
|
|
69
|
+
async initialize(): Promise<void> {
|
|
70
|
+
if (this.initialized) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
// Validate configuration
|
|
76
|
+
if (!this.dependencies.configProvider.config.baseUrl) {
|
|
77
|
+
throw new Error("Base URL is required");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Ensure authentication is ready
|
|
81
|
+
await this.dependencies.authProvider.authenticate();
|
|
82
|
+
|
|
83
|
+
this.initialized = true;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
this.dependencies.errorHandler.handleError(error, "initialize request manager");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Make HTTP request with retry logic and rate limiting
|
|
91
|
+
*/
|
|
92
|
+
async request<T>(method: HTTPMethod, endpoint: string, data?: unknown, options: RequestOptions = {}): Promise<T> {
|
|
93
|
+
this.ensureInitialized();
|
|
94
|
+
|
|
95
|
+
const timer = startTimer();
|
|
96
|
+
this.stats.totalRequests++;
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
// Validate inputs
|
|
100
|
+
this.dependencies.validator.validateString(method, "method", { required: true });
|
|
101
|
+
this.dependencies.validator.validateString(endpoint, "endpoint", { required: true });
|
|
102
|
+
|
|
103
|
+
await this.enforceRateLimit();
|
|
104
|
+
|
|
105
|
+
const response = await this.makeRequestWithRetry(method, endpoint, data, options);
|
|
106
|
+
|
|
107
|
+
this.stats.successfulRequests++;
|
|
108
|
+
this.updateAverageResponseTime(timer.end());
|
|
109
|
+
|
|
110
|
+
this.dependencies.errorHandler.logSuccess(`${method} ${endpoint}`, { responseTime: timer.end() });
|
|
111
|
+
|
|
112
|
+
return response as T;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
this.stats.failedRequests++;
|
|
115
|
+
this.stats.errors++;
|
|
116
|
+
|
|
117
|
+
if (error instanceof RateLimitError) {
|
|
118
|
+
this.stats.rateLimitHits++;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.dependencies.errorHandler.handleError(error, `${method} ${endpoint}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get request statistics
|
|
127
|
+
*/
|
|
128
|
+
getStats(): ClientStats {
|
|
129
|
+
return { ...this.stats };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Reset statistics
|
|
134
|
+
*/
|
|
135
|
+
resetStats(): void {
|
|
136
|
+
this.stats = {
|
|
137
|
+
totalRequests: 0,
|
|
138
|
+
successfulRequests: 0,
|
|
139
|
+
failedRequests: 0,
|
|
140
|
+
averageResponseTime: 0,
|
|
141
|
+
rateLimitHits: 0,
|
|
142
|
+
authFailures: 0,
|
|
143
|
+
errors: 0,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Enforce rate limiting
|
|
149
|
+
*/
|
|
150
|
+
private async enforceRateLimit(): Promise<void> {
|
|
151
|
+
const now = Date.now();
|
|
152
|
+
const timeSinceLastRequest = now - this.lastRequestTime;
|
|
153
|
+
|
|
154
|
+
if (timeSinceLastRequest < this.requestInterval) {
|
|
155
|
+
const delay = this.requestInterval - timeSinceLastRequest;
|
|
156
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.lastRequestTime = Date.now();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Make request with retry logic
|
|
164
|
+
*/
|
|
165
|
+
private async makeRequestWithRetry<T>(
|
|
166
|
+
method: HTTPMethod,
|
|
167
|
+
endpoint: string,
|
|
168
|
+
data?: unknown,
|
|
169
|
+
options: RequestOptions = {},
|
|
170
|
+
attempt: number = 1,
|
|
171
|
+
): Promise<T> {
|
|
172
|
+
const maxRetries = 3; // Use default retries for composed manager
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
return await this.makeRequest<T>(method, endpoint, data, options);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (attempt <= maxRetries && this.shouldRetry(error)) {
|
|
178
|
+
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
|
|
179
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
180
|
+
return this.makeRequestWithRetry<T>(method, endpoint, data, options, attempt + 1);
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Make actual HTTP request
|
|
188
|
+
*/
|
|
189
|
+
private async makeRequest<T>(
|
|
190
|
+
method: HTTPMethod,
|
|
191
|
+
endpoint: string,
|
|
192
|
+
data?: unknown,
|
|
193
|
+
options: RequestOptions = {},
|
|
194
|
+
): Promise<T> {
|
|
195
|
+
const url = this.buildUrl(endpoint);
|
|
196
|
+
const requestOptions = await this.buildRequestOptions(method, data, options);
|
|
197
|
+
|
|
198
|
+
const response = await fetch(url, requestOptions);
|
|
199
|
+
|
|
200
|
+
if (!response.ok) {
|
|
201
|
+
await this.handleHttpError(response);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const contentType = response.headers.get("content-type");
|
|
205
|
+
|
|
206
|
+
if (contentType && contentType.includes("application/json")) {
|
|
207
|
+
return (await response.json()) as T;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return (await response.text()) as unknown as T;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Build full URL for request
|
|
215
|
+
*/
|
|
216
|
+
private buildUrl(endpoint: string): string {
|
|
217
|
+
const baseUrl = this.dependencies.configProvider.config.baseUrl;
|
|
218
|
+
const cleanBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
219
|
+
const cleanEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
220
|
+
|
|
221
|
+
return `${cleanBase}/wp-json${cleanEndpoint}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Build request options with authentication
|
|
226
|
+
*/
|
|
227
|
+
private async buildRequestOptions(
|
|
228
|
+
method: HTTPMethod,
|
|
229
|
+
data?: unknown,
|
|
230
|
+
options: RequestOptions = {},
|
|
231
|
+
): Promise<RequestInit> {
|
|
232
|
+
const authHeaders = this.dependencies.authProvider.getAuthHeaders();
|
|
233
|
+
const timeout = this.dependencies.configProvider.config.timeout || 30000;
|
|
234
|
+
|
|
235
|
+
const headers = {
|
|
236
|
+
"Content-Type": "application/json",
|
|
237
|
+
Accept: "application/json",
|
|
238
|
+
"User-Agent": getUserAgent(),
|
|
239
|
+
...authHeaders,
|
|
240
|
+
...options.headers,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const requestOptions: RequestInit = {
|
|
244
|
+
method,
|
|
245
|
+
headers,
|
|
246
|
+
signal: typeof AbortSignal !== "undefined" && AbortSignal.timeout ? AbortSignal.timeout(timeout) : null,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
if (data && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
250
|
+
requestOptions.body = JSON.stringify(data);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return requestOptions;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Handle HTTP error responses
|
|
258
|
+
*/
|
|
259
|
+
private async handleHttpError(response: Response): Promise<never> {
|
|
260
|
+
const statusCode = response.status;
|
|
261
|
+
|
|
262
|
+
if (statusCode === 401) {
|
|
263
|
+
this.stats.authFailures++;
|
|
264
|
+
|
|
265
|
+
// Try to refresh authentication
|
|
266
|
+
try {
|
|
267
|
+
await this.dependencies.authProvider.handleAuthFailure(new Error(`HTTP ${statusCode}`));
|
|
268
|
+
// If refresh succeeds, the caller should retry
|
|
269
|
+
throw new WordPressAPIError("Authentication refreshed, retry needed", 401, "auth_refreshed");
|
|
270
|
+
} catch (_authError) {
|
|
271
|
+
throw new WordPressAPIError("Authentication failed", 401, "auth_failed");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (statusCode === 429) {
|
|
276
|
+
const retryAfter = response.headers.get("retry-after");
|
|
277
|
+
const delay = retryAfter ? parseInt(retryAfter, 10) : 60;
|
|
278
|
+
throw new RateLimitError("Rate limit exceeded", delay);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let errorMessage = `HTTP ${statusCode}: ${response.statusText}`;
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const errorBody = (await response.json()) as { message?: string };
|
|
285
|
+
if (errorBody.message) {
|
|
286
|
+
errorMessage = errorBody.message;
|
|
287
|
+
}
|
|
288
|
+
} catch {
|
|
289
|
+
// Ignore JSON parsing errors for non-JSON error responses
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
throw new WordPressAPIError(errorMessage, statusCode, "http_error");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Determine if error is retryable
|
|
297
|
+
*/
|
|
298
|
+
private shouldRetry(error: unknown): boolean {
|
|
299
|
+
if (error instanceof WordPressAPIError) {
|
|
300
|
+
const nonRetryableCodes = [400, 401, 403, 404];
|
|
301
|
+
return !nonRetryableCodes.includes(error.statusCode || 500);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (error instanceof Error) {
|
|
305
|
+
const retryableErrors = ["ECONNRESET", "ETIMEDOUT", "ENOTFOUND", "EAI_AGAIN"];
|
|
306
|
+
return retryableErrors.some((code) => error.message.includes(code));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Update average response time with new measurement
|
|
314
|
+
*/
|
|
315
|
+
private updateAverageResponseTime(newTime: number): void {
|
|
316
|
+
const total = this.stats.averageResponseTime * (this.stats.successfulRequests - 1) + newTime;
|
|
317
|
+
this.stats.averageResponseTime = Math.round(total / this.stats.successfulRequests);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Ensure manager is initialized before operations
|
|
322
|
+
*/
|
|
323
|
+
private ensureInitialized(): void {
|
|
324
|
+
if (!this.initialized) {
|
|
325
|
+
throw new Error("ComposedRequestManager not initialized. Call initialize() first.");
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Cleanup resources
|
|
331
|
+
*/
|
|
332
|
+
dispose(): void {
|
|
333
|
+
// Cleanup any resources, timers, etc.
|
|
334
|
+
this.initialized = false;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT Authentication Implementation
|
|
3
|
+
* Complete JWT authentication with token refresh and RequestManager integration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AuthConfig, RequestOptions } from "@/types/client.js";
|
|
7
|
+
import { AuthenticationError } from "@/types/client.js";
|
|
8
|
+
import { AUTH_METHODS } from "@/types/wordpress.js";
|
|
9
|
+
import { LoggerFactory } from "@/utils/logger.js";
|
|
10
|
+
import type { RequestManager } from "./RequestManager.js";
|
|
11
|
+
|
|
12
|
+
export interface JWTTokenResponse {
|
|
13
|
+
token: string;
|
|
14
|
+
user_email: string;
|
|
15
|
+
user_nicename: string;
|
|
16
|
+
user_display_name: string;
|
|
17
|
+
expires_in?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface JWTValidateResponse {
|
|
21
|
+
code: string;
|
|
22
|
+
data: {
|
|
23
|
+
status: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface JWTRefreshResponse {
|
|
28
|
+
token: string;
|
|
29
|
+
expires_in?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class JWTAuthImplementation {
|
|
33
|
+
private logger = LoggerFactory.api();
|
|
34
|
+
private jwtToken: string | null = null;
|
|
35
|
+
private tokenExpiry: number | null = null;
|
|
36
|
+
private refreshTokenValue: string | null = null;
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
private requestManager: RequestManager,
|
|
40
|
+
private authConfig: AuthConfig
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Complete JWT authentication implementation
|
|
45
|
+
*/
|
|
46
|
+
async authenticateJWT(): Promise<void> {
|
|
47
|
+
if (!this.authConfig.username || !this.authConfig.password) {
|
|
48
|
+
throw new AuthenticationError("JWT authentication requires username and password", AUTH_METHODS.JWT);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
this.logger.info("Starting JWT authentication", {
|
|
53
|
+
username: this.authConfig.username,
|
|
54
|
+
endpoint: "jwt-auth/v1/token"
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const response = await this.requestManager.request<JWTTokenResponse>(
|
|
58
|
+
"POST",
|
|
59
|
+
"jwt-auth/v1/token",
|
|
60
|
+
{
|
|
61
|
+
username: this.authConfig.username,
|
|
62
|
+
password: this.authConfig.password,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
skipAuth: true, // Don't use auth headers for the auth request itself
|
|
66
|
+
timeout: 10000,
|
|
67
|
+
} as RequestOptions & { skipAuth: boolean }
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (!response.token) {
|
|
71
|
+
throw new AuthenticationError("JWT token not received in response", AUTH_METHODS.JWT);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Store the JWT token
|
|
75
|
+
this.jwtToken = response.token;
|
|
76
|
+
|
|
77
|
+
// Calculate token expiry (default to 24 hours if not provided)
|
|
78
|
+
const expiresInSeconds = response.expires_in || (24 * 60 * 60);
|
|
79
|
+
this.tokenExpiry = Date.now() + (expiresInSeconds * 1000);
|
|
80
|
+
|
|
81
|
+
this.logger.info("JWT authentication successful", {
|
|
82
|
+
user: response.user_nicename,
|
|
83
|
+
expiresAt: new Date(this.tokenExpiry).toISOString(),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Test the token by validating it
|
|
87
|
+
await this.validateToken();
|
|
88
|
+
|
|
89
|
+
} catch (error) {
|
|
90
|
+
this.jwtToken = null;
|
|
91
|
+
this.tokenExpiry = null;
|
|
92
|
+
this.logger.error("JWT authentication failed", {
|
|
93
|
+
error: error instanceof Error ? error.message : String(error),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (error instanceof Error) {
|
|
97
|
+
throw new AuthenticationError(`JWT authentication failed: ${error.message}`, AUTH_METHODS.JWT);
|
|
98
|
+
}
|
|
99
|
+
throw new AuthenticationError("JWT authentication failed", AUTH_METHODS.JWT);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Validate JWT token
|
|
105
|
+
*/
|
|
106
|
+
async validateToken(): Promise<boolean> {
|
|
107
|
+
if (!this.jwtToken) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const response = await this.requestManager.request<JWTValidateResponse>(
|
|
113
|
+
"POST",
|
|
114
|
+
"jwt-auth/v1/token/validate",
|
|
115
|
+
{},
|
|
116
|
+
{
|
|
117
|
+
headers: {
|
|
118
|
+
Authorization: `Bearer ${this.jwtToken}`,
|
|
119
|
+
},
|
|
120
|
+
timeout: 5000,
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const isValid = response.data.status === 200;
|
|
125
|
+
|
|
126
|
+
if (!isValid) {
|
|
127
|
+
this.logger.warn("JWT token validation failed", { response });
|
|
128
|
+
this.jwtToken = null;
|
|
129
|
+
this.tokenExpiry = null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return isValid;
|
|
133
|
+
|
|
134
|
+
} catch (error) {
|
|
135
|
+
this.logger.warn("JWT token validation error", {
|
|
136
|
+
error: error instanceof Error ? error.message : String(error),
|
|
137
|
+
});
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Refresh JWT token
|
|
144
|
+
*/
|
|
145
|
+
async refreshToken(): Promise<void> {
|
|
146
|
+
if (!this.jwtToken) {
|
|
147
|
+
throw new AuthenticationError("No JWT token to refresh", AUTH_METHODS.JWT);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
this.logger.debug("Refreshing JWT token");
|
|
152
|
+
|
|
153
|
+
// Some JWT implementations support refresh endpoints
|
|
154
|
+
// First try the refresh endpoint if available
|
|
155
|
+
try {
|
|
156
|
+
const refreshResponse = await this.requestManager.request<JWTRefreshResponse>(
|
|
157
|
+
"POST",
|
|
158
|
+
"jwt-auth/v1/token/refresh",
|
|
159
|
+
{},
|
|
160
|
+
{
|
|
161
|
+
headers: {
|
|
162
|
+
Authorization: `Bearer ${this.jwtToken}`,
|
|
163
|
+
},
|
|
164
|
+
timeout: 5000,
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if (refreshResponse.token) {
|
|
169
|
+
this.jwtToken = refreshResponse.token;
|
|
170
|
+
const expiresInSeconds = refreshResponse.expires_in || (24 * 60 * 60);
|
|
171
|
+
this.tokenExpiry = Date.now() + (expiresInSeconds * 1000);
|
|
172
|
+
|
|
173
|
+
this.logger.info("JWT token refreshed successfully", {
|
|
174
|
+
expiresAt: new Date(this.tokenExpiry).toISOString(),
|
|
175
|
+
});
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
} catch (_refreshError) {
|
|
179
|
+
this.logger.debug("JWT refresh endpoint not available, falling back to re-authentication");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Fallback: Re-authenticate with original credentials
|
|
183
|
+
if (!this.authConfig.username || !this.authConfig.password) {
|
|
184
|
+
throw new AuthenticationError("Cannot refresh JWT token: original credentials not available", AUTH_METHODS.JWT);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this.logger.debug("Re-authenticating to refresh JWT token");
|
|
188
|
+
await this.authenticateJWT();
|
|
189
|
+
|
|
190
|
+
} catch (error) {
|
|
191
|
+
this.jwtToken = null;
|
|
192
|
+
this.tokenExpiry = null;
|
|
193
|
+
this.logger.error("JWT token refresh failed", {
|
|
194
|
+
error: error instanceof Error ? error.message : String(error),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
throw new AuthenticationError(
|
|
198
|
+
`JWT token refresh failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
199
|
+
AUTH_METHODS.JWT
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Check if JWT token is expired
|
|
206
|
+
*/
|
|
207
|
+
isTokenExpired(): boolean {
|
|
208
|
+
if (!this.tokenExpiry) {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Consider token expired if it expires within 5 minutes (300000ms)
|
|
213
|
+
const buffer = 5 * 60 * 1000;
|
|
214
|
+
return Date.now() >= (this.tokenExpiry - buffer);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get current JWT token
|
|
219
|
+
*/
|
|
220
|
+
getToken(): string | null {
|
|
221
|
+
return this.jwtToken;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get token expiry timestamp
|
|
226
|
+
*/
|
|
227
|
+
getTokenExpiry(): number | null {
|
|
228
|
+
return this.tokenExpiry;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Clear JWT token and expiry
|
|
233
|
+
*/
|
|
234
|
+
clearToken(): void {
|
|
235
|
+
this.jwtToken = null;
|
|
236
|
+
this.tokenExpiry = null;
|
|
237
|
+
this.refreshTokenValue = null;
|
|
238
|
+
this.logger.debug("JWT token cleared");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get JWT auth headers
|
|
243
|
+
*/
|
|
244
|
+
getAuthHeaders(): Record<string, string> {
|
|
245
|
+
if (!this.jwtToken) {
|
|
246
|
+
return {};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
Authorization: `Bearer ${this.jwtToken}`,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Check if we have a valid JWT token
|
|
256
|
+
*/
|
|
257
|
+
hasValidToken(): boolean {
|
|
258
|
+
return this.jwtToken !== null && !this.isTokenExpired();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Automatic token refresh if needed
|
|
263
|
+
*/
|
|
264
|
+
async ensureValidToken(): Promise<void> {
|
|
265
|
+
if (!this.jwtToken) {
|
|
266
|
+
// No token, need to authenticate
|
|
267
|
+
await this.authenticateJWT();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (this.isTokenExpired()) {
|
|
272
|
+
// Token expired, need to refresh
|
|
273
|
+
await this.refreshToken();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Token is still valid
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Logout and invalidate token
|
|
282
|
+
*/
|
|
283
|
+
async logout(): Promise<void> {
|
|
284
|
+
if (!this.jwtToken) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
// Try to invalidate the token on the server
|
|
290
|
+
await this.requestManager.request(
|
|
291
|
+
"POST",
|
|
292
|
+
"jwt-auth/v1/token/invalidate",
|
|
293
|
+
{},
|
|
294
|
+
{
|
|
295
|
+
headers: this.getAuthHeaders(),
|
|
296
|
+
timeout: 5000,
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
this.logger.info("JWT token invalidated on server");
|
|
301
|
+
} catch (error) {
|
|
302
|
+
this.logger.warn("Failed to invalidate JWT token on server", {
|
|
303
|
+
error: error instanceof Error ? error.message : String(error),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
this.clearToken();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Get token information
|
|
312
|
+
*/
|
|
313
|
+
getTokenInfo(): {
|
|
314
|
+
hasToken: boolean;
|
|
315
|
+
isExpired: boolean;
|
|
316
|
+
expiresAt: string | null;
|
|
317
|
+
expiresIn: number | null;
|
|
318
|
+
} {
|
|
319
|
+
return {
|
|
320
|
+
hasToken: this.jwtToken !== null,
|
|
321
|
+
isExpired: this.isTokenExpired(),
|
|
322
|
+
expiresAt: this.tokenExpiry ? new Date(this.tokenExpiry).toISOString() : null,
|
|
323
|
+
expiresIn: this.tokenExpiry ? Math.max(0, this.tokenExpiry - Date.now()) : null,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// Use native fetch in Node.js 18+
|
|
7
|
-
import type { HTTPMethod, RequestOptions, ClientStats, WordPressClientConfig } from "
|
|
8
|
-
import { WordPressAPIError, RateLimitError } from "
|
|
9
|
-
import { config } from "
|
|
7
|
+
import type { HTTPMethod, RequestOptions, ClientStats, WordPressClientConfig } from "@/types/client.js";
|
|
8
|
+
import { WordPressAPIError, RateLimitError } from "@/types/client.js";
|
|
9
|
+
import { config } from "@/config/Config.js";
|
|
10
10
|
import { BaseManager } from "./BaseManager.js";
|
|
11
11
|
import { AuthenticationManager } from "./AuthenticationManager.js";
|
|
12
|
-
import { debug, startTimer } from "
|
|
12
|
+
import { debug, startTimer } from "@/utils/debug.js";
|
|
13
|
+
import { getUserAgent } from "@/utils/version.js";
|
|
13
14
|
|
|
14
15
|
export class RequestManager extends BaseManager {
|
|
15
16
|
private stats: ClientStats;
|
|
@@ -29,6 +30,7 @@ export class RequestManager extends BaseManager {
|
|
|
29
30
|
averageResponseTime: 0,
|
|
30
31
|
rateLimitHits: 0,
|
|
31
32
|
authFailures: 0,
|
|
33
|
+
errors: 0,
|
|
32
34
|
};
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -47,9 +49,9 @@ export class RequestManager extends BaseManager {
|
|
|
47
49
|
this.updateAverageResponseTime(timer.end());
|
|
48
50
|
|
|
49
51
|
return response as T;
|
|
50
|
-
} catch (
|
|
52
|
+
} catch (_error) {
|
|
51
53
|
this.stats.failedRequests++;
|
|
52
|
-
this.handleError(
|
|
54
|
+
this.handleError(_error, `${method} ${endpoint}`);
|
|
53
55
|
} finally {
|
|
54
56
|
this.stats.totalRequests++;
|
|
55
57
|
}
|
|
@@ -118,7 +120,7 @@ export class RequestManager extends BaseManager {
|
|
|
118
120
|
method,
|
|
119
121
|
headers: {
|
|
120
122
|
"Content-Type": "application/json",
|
|
121
|
-
"User-Agent":
|
|
123
|
+
"User-Agent": getUserAgent(),
|
|
122
124
|
...authHeaders, // Add auth headers before custom headers
|
|
123
125
|
...options.headers,
|
|
124
126
|
},
|