@simpleapps-com/augur-api 0.2.9 → 0.2.10

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 CHANGED
@@ -404,6 +404,218 @@ const auth = await api.joomla.users.verifyPassword({
404
404
  });
405
405
  ```
406
406
 
407
+ ## Power Features (Hidden Gems)
408
+
409
+ **Unlock the full potential** of the Augur API with these advanced features that dramatically improve your development experience:
410
+
411
+ ### Data Methods Pattern ⭐ NEW!
412
+
413
+ Every endpoint provides both complete responses AND streamlined data-only access:
414
+
415
+ ```typescript
416
+ // Standard pattern - full response with metadata
417
+ const response = await api.joomla.users.list({ limit: 10 });
418
+ console.log(`Found ${response.data.length} users`);
419
+ console.log(`Total available: ${response.totalResults}`);
420
+ console.log(`Response status: ${response.status}`);
421
+
422
+ // Data-only pattern - direct access to the data array ⚡
423
+ const users = await api.joomla.users.listData({ limit: 10 });
424
+ console.log(users); // Direct User[] array, no wrapper
425
+
426
+ // Single item data access
427
+ const user = await api.joomla.users.getData('123');
428
+ console.log(user); // Direct User object, no response wrapper
429
+
430
+ // Works across ALL services
431
+ const products = await api.opensearch.itemSearch.searchData({
432
+ q: 'electrical wire',
433
+ searchType: 'query',
434
+ size: 20
435
+ }); // Direct item array
436
+
437
+ const pricing = await api.pricing.getPriceData({
438
+ customerId: 12345,
439
+ itemId: 'WIRE-123',
440
+ quantity: 10
441
+ }); // Direct pricing object
442
+ ```
443
+
444
+ **Benefits:**
445
+ - **50% less code** for data extraction
446
+ - **Cleaner business logic** without response unwrapping
447
+ - **Same caching and validation** as standard methods
448
+ - **Consistent pattern** across all 13 microservices
449
+
450
+ ### Advanced Discovery Filtering
451
+
452
+ Go beyond basic search with sophisticated filtering options:
453
+
454
+ ```typescript
455
+ // Precision search with advanced filtering
456
+ const endpoints = await api.findEndpoint('user management', {
457
+ // Relevance threshold (0.0 to 1.0)
458
+ minScore: 0.7, // Only highly relevant matches
459
+
460
+ // Limit results
461
+ maxResults: 5, // Top 5 matches only
462
+
463
+ // Service-specific filtering
464
+ service: 'joomla', // Only Joomla service endpoints
465
+
466
+ // Business domain filtering
467
+ domain: 'user-management', // Only user management domain
468
+
469
+ // Operation type filtering
470
+ readOnly: true, // Only read operations (GET requests)
471
+ writeOnly: false, // Exclude write operations
472
+
473
+ // Response format preferences
474
+ includeMetadata: true, // Include match reasoning and scores
475
+ sortBy: 'relevance' // Sort by relevance score (default: 'relevance' | 'alphabetical')
476
+ });
477
+
478
+ // Rich result information
479
+ endpoints.forEach(result => {
480
+ console.log(`🔍 ${result.endpoint.fullPath}`);
481
+ console.log(` Score: ${result.score} | Reason: ${result.matchReason}`);
482
+ console.log(` Domain: ${result.endpoint.domain} | Service: ${result.endpoint.service}`);
483
+ console.log(` Method: ${result.endpoint.method} | Read-only: ${result.endpoint.readOnly}`);
484
+ });
485
+
486
+ // Cross-service workflow discovery
487
+ const ecommerceFlow = await api.findEndpoint('complete customer order', {
488
+ domain: ['commerce', 'pricing', 'inventory'], // Multiple domains
489
+ includeWorkflow: true, // Include workflow relationships
490
+ minScore: 0.5
491
+ });
492
+
493
+ // Results include cross-service relationships
494
+ ecommerceFlow.forEach(result => {
495
+ console.log(`${result.endpoint.fullPath} → Related: ${result.relatedEndpoints?.join(', ')}`);
496
+ });
497
+ ```
498
+
499
+ ### Debug Utilities and Troubleshooting
500
+
501
+ Built-in debugging tools help you understand what's happening under the hood:
502
+
503
+ ```typescript
504
+ // Create detailed debug information for any request
505
+ const debugInfo = api.joomla.users.createDebugInfo(
506
+ { limit: 10, offset: 0 }, // Your parameters
507
+ { edgeCache: 2 } // Request config
508
+ );
509
+
510
+ console.log('🔧 Debug Information:');
511
+ console.log('Expected Parameters:', debugInfo.expectedParams);
512
+ console.log('Provided Parameters:', debugInfo.providedParams);
513
+ console.log('Validation Results:', debugInfo.validation);
514
+ console.log('Cache Strategy:', debugInfo.cacheInfo);
515
+ console.log('Generated URL:', debugInfo.finalURL);
516
+
517
+ // Validate parameters before making requests
518
+ const validation = api.pricing.validatePriceParams({
519
+ customerId: 12345,
520
+ itemId: 'INVALID', // This might fail validation
521
+ quantity: -5 // Negative quantity should fail
522
+ });
523
+
524
+ if (!validation.isValid) {
525
+ console.log('❌ Parameter Validation Failed:');
526
+ validation.errors.forEach(error => {
527
+ console.log(` Field: ${error.field} | Issue: ${error.message}`);
528
+ console.log(` Expected: ${error.expected} | Received: ${error.received}`);
529
+ });
530
+ }
531
+
532
+ // Request tracing for performance analysis
533
+ api.enableRequestTracing(true);
534
+
535
+ const users = await api.joomla.users.list({ limit: 10 });
536
+
537
+ // Get trace information
538
+ const trace = api.getLastRequestTrace();
539
+ console.log('🚀 Performance Trace:');
540
+ console.log(` Total Time: ${trace.totalTime}ms`);
541
+ console.log(` Network Time: ${trace.networkTime}ms`);
542
+ console.log(` Validation Time: ${trace.validationTime}ms`);
543
+ console.log(` Cache Status: ${trace.cacheStatus}`);
544
+ console.log(` Request ID: ${trace.requestId}`);
545
+ ```
546
+
547
+ ### Automatic Parameter Extraction
548
+
549
+ The system intelligently maps URL templates to method parameters:
550
+
551
+ ```typescript
552
+ // URL template: /bin-transfer/{binTransferHdrUid}
553
+ // Automatically creates: (id) => { binTransferHdrUid: id }
554
+ const binTransfer = await api.nexus.binTransfers.get(12345);
555
+ // Internally maps: 12345 → { binTransferHdrUid: 12345 }
556
+
557
+ // URL template: /customer/{customerId}/orders/{orderId}
558
+ // Automatically creates: (customerId, orderId) => { customerId, orderId }
559
+ const order = await api.customers.customer.orders.get(123, 456);
560
+ // Internally maps: (123, 456) → { customerId: 123, orderId: 456 }
561
+
562
+ // Complex URL templates with multiple parameters
563
+ // URL template: /pricing/job/{jobPriceHdrUid}/lines/{jobPriceLineUid}
564
+ const jobPriceLine = await api.pricing.jobPriceLines.get(123, 456);
565
+ // Internally maps: (123, 456) → { jobPriceHdrUid: 123, jobPriceLineUid: 456 }
566
+
567
+ // See the mapping for any endpoint
568
+ const mapping = api.nexus.binTransfers.getParameterMapping();
569
+ console.log('URL Template:', mapping.urlTemplate);
570
+ console.log('Parameter Map:', mapping.parameterMap);
571
+ console.log('Required Params:', mapping.requiredParams);
572
+ console.log('Optional Params:', mapping.optionalParams);
573
+
574
+ // Validate parameter mapping before calling
575
+ const isValid = api.nexus.binTransfers.validateParameterMapping(12345);
576
+ if (!isValid.valid) {
577
+ console.log('Parameter mapping issues:', isValid.errors);
578
+ }
579
+ ```
580
+
581
+ ### Smart Request Building
582
+
583
+ Advanced request construction with intelligent defaults:
584
+
585
+ ```typescript
586
+ // Smart parameter inference
587
+ const smartRequest = api.commerce.cartHeaders.buildRequest({
588
+ userId: 123,
589
+ // System automatically infers common parameters:
590
+ // - siteId from API configuration
591
+ // - timestamp for cache busting
592
+ // - request ID for tracing
593
+ });
594
+
595
+ console.log('Generated request:', smartRequest);
596
+ // {
597
+ // userId: 123,
598
+ // siteId: 'your-site-id',
599
+ // _timestamp: 1693934400000,
600
+ // _requestId: 'req_abc123',
601
+ // _cacheKey: 'cart_headers_123_your-site-id'
602
+ // }
603
+
604
+ // Request templating for repeated operations
605
+ const userTemplate = api.joomla.users.createRequestTemplate({
606
+ limit: 50,
607
+ orderBy: 'username|ASC'
608
+ });
609
+
610
+ // Reuse template with variations
611
+ const activeUsers = await userTemplate.execute({ q: 'active' });
612
+ const adminUsers = await userTemplate.execute({ groupId: 1 });
613
+ const recentUsers = await userTemplate.execute({
614
+ createdSince: '2024-01-01',
615
+ limit: 20 // Override template default
616
+ });
617
+ ```
618
+
407
619
  ## Platform Capabilities Matrix
408
620
 
409
621
  | Capability | Impact | Technical Implementation |
@@ -2280,269 +2492,944 @@ managedAPI.updateConfig({
2280
2492
 
2281
2493
  ## Performance Optimization
2282
2494
 
2283
- ### Caching Strategies
2495
+ > 📋 **[Complete Performance Guide](./PERFORMANCE.md)** - Comprehensive strategies for edge caching, batch operations, pagination optimization, and performance monitoring.
2496
+
2497
+ **Quick Performance Tips:**
2284
2498
 
2285
2499
  ```typescript
2286
- // Cache strategy based on data volatility
2287
- class CacheStrategy {
2288
- static getDuration(dataType: string, context?: Record<string, any>): number | undefined {
2289
- const strategies = {
2290
- // Static reference data - cache for 8 hours
2291
- reference: 8,
2292
- categories: 8,
2293
- tags: 6,
2294
- distributors: 8,
2295
-
2296
- // Moderate volatility - cache for 2-4 hours
2297
- users: context?.isLargeOrg ? 4 : 2,
2298
- products: 6,
2299
- warehouses: 4,
2300
- recommendations: 4,
2301
-
2302
- // High volatility - cache for 1-2 hours
2303
- pricing: context?.isStandardItem ? 3 : 1,
2304
- inventory: context?.isHighVolume ? 1 : 2,
2305
- cart: 1,
2306
-
2307
- // Real-time data - no caching
2308
- authentication: undefined,
2309
- validation: undefined,
2310
- checkout: undefined,
2311
- };
2312
-
2313
- return strategies[dataType as keyof typeof strategies];
2314
- }
2315
- }
2500
+ // Edge caching with Cloudflare CDN
2501
+ const categories = await api.items.categories.list({ edgeCache: 8 }); // Static data: 8 hours
2502
+ const pricing = await api.pricing.getPrice({ customerId: 123, edgeCache: 3 }); // Dynamic: 3 hours
2503
+ const inventory = await api.vmi.inventory.checkAvailability(123, { edgeCache: 1 }); // Volatile: 1 hour
2316
2504
 
2317
- // Usage
2318
- const duration = CacheStrategy.getDuration('pricing', { isStandardItem: true });
2319
- const pricing = await api.pricing.getPrice({
2320
- customerId: 12345,
2321
- itemId: 'STANDARD-ITEM',
2322
- quantity: 10,
2323
- edgeCache: duration // Will be 3 hours
2324
- });
2325
- ```
2326
-
2327
- ### Request Batching
2328
-
2329
- ```typescript
2330
- // Batch related requests for better performance
2331
- async function loadDashboardData() {
2332
- // Execute all requests in parallel
2333
- const [users, products, inventory, pricing] = await Promise.allSettled([
2334
- api.joomla.users.list({ limit: 10, edgeCache: 2 }),
2335
- api.opensearch.itemSearch.search({
2336
- q: 'featured',
2337
- searchType: 'query',
2338
- size: 5,
2339
- edgeCache: 4
2340
- }),
2341
- api.vmi.warehouses.list({
2342
- customerId: 12345,
2343
- limit: 5,
2344
- edgeCache: 4
2345
- }),
2346
- api.pricing.jobPriceHeaders.list({
2347
- limit: 5,
2348
- edgeCache: 6
2349
- })
2350
- ]);
2351
-
2352
- // Handle results
2353
- return {
2354
- users: users.status === 'fulfilled' ? users.value : null,
2355
- products: products.status === 'fulfilled' ? products.value : null,
2356
- inventory: inventory.status === 'fulfilled' ? inventory.value : null,
2357
- pricing: pricing.status === 'fulfilled' ? pricing.value : null,
2358
- };
2505
+ // 🚀 Batch operations for multiple requests
2506
+ const [users, groups, content] = await Promise.allSettled([
2507
+ api.joomla.users.list({ limit: 50, edgeCache: 2 }),
2508
+ api.joomla.userGroups.list({ edgeCache: 8 }),
2509
+ api.joomla.content.list({ categoryIdList: '1,2,3', edgeCache: 6 })
2510
+ ]);
2511
+
2512
+ // 📄 Smart pagination with prefetching
2513
+ class SmartPagination {
2514
+ async loadPage(fetcher, page, pageSize = 50) {
2515
+ // Automatic prefetching and caching
2516
+ return this.loadWithPrefetch(fetcher, page, pageSize);
2517
+ }
2359
2518
  }
2360
2519
  ```
2361
2520
 
2362
- ### Connection Pooling
2521
+ **Key Performance Features:**
2522
+ - **Edge Cache Decision Tree** - Choose optimal cache duration by data volatility
2523
+ - **Service-Specific Batching** - Parallel operations tailored to each microservice
2524
+ - **Advanced Pagination** - Prefetching, infinite scroll, and streaming patterns
2525
+ - **Performance Analytics** - Monitor cache hit rates, response times, and optimization opportunities
2526
+
2527
+ For complete implementation details, see [Performance Optimization Guide](./PERFORMANCE.md).
2528
+
2529
+ ## Enterprise Integration Patterns
2530
+
2531
+ **Scale your enterprise applications** with proven patterns for multi-tenant architectures, cross-site authentication, and production deployment.
2532
+
2533
+ ### Cross-Site Authentication Architecture
2534
+
2535
+ > 📋 **[Complete Authentication Guide](./AUTHENTICATION.md)** - Detailed implementation patterns, security considerations, and troubleshooting.
2536
+
2537
+ **Enterprise Authentication Flow:**
2363
2538
 
2364
2539
  ```typescript
2365
- // Configure HTTP client for optimal performance
2366
- const api = new AugurAPI({
2367
- siteId: 'your-site-id',
2368
- bearerToken: 'your-token',
2369
-
2370
- // Optimize for high-throughput scenarios
2371
- timeout: 15000, // Shorter timeout for better user experience
2372
- retries: 2, // Fewer retries for faster failure detection
2373
-
2374
- onRequest: (config) => {
2375
- // Add keep-alive headers for connection reuse
2376
- config.headers['Connection'] = 'keep-alive';
2377
- config.headers['Keep-Alive'] = 'timeout=5, max=1000';
2378
- return config;
2540
+ /**
2541
+ * 🏢 ENTERPRISE MULTI-TENANT AUTHENTICATION
2542
+ *
2543
+ * Supports authenticating users across multiple tenant sites
2544
+ * from a centralized application or admin dashboard.
2545
+ */
2546
+
2547
+ import { createCrossSiteAuthenticator, authenticateUserForSite } from '@simpleapps-com/augur-api';
2548
+
2549
+ // 🎯 Option 1: Single Authentication (Recommended for most cases)
2550
+ const authResult = await authenticateUserForSite({
2551
+ targetSiteId: 'tenant_site_alpha',
2552
+ username: 'user@tenant.com',
2553
+ password: 'user_password',
2554
+ augurInfoToken: process.env.AUGUR_ADMIN_TOKEN! // Admin token with augur_info privileges
2555
+ });
2556
+
2557
+ if (authResult.success) {
2558
+ // Ready-to-use API client for the tenant
2559
+ const tenantAPI = authResult.targetSiteAPI!;
2560
+ const userData = await tenantAPI.joomla.users.get(authResult.userId!);
2561
+ console.log(`✅ Authenticated: ${authResult.username} on ${targetSiteId}`);
2562
+ }
2563
+
2564
+ // 🎯 Option 2: Reusable Authenticator (For multiple sites)
2565
+ const crossSiteAuth = createCrossSiteAuthenticator(process.env.AUGUR_ADMIN_TOKEN!);
2566
+
2567
+ const tenantResults = await Promise.allSettled([
2568
+ crossSiteAuth('tenant_alpha', 'user@alpha.com', 'pass1'),
2569
+ crossSiteAuth('tenant_beta', 'user@beta.com', 'pass2'),
2570
+ crossSiteAuth('tenant_gamma', 'user@gamma.com', 'pass3')
2571
+ ]);
2572
+
2573
+ // Process results for each tenant
2574
+ tenantResults.forEach((result, index) => {
2575
+ const tenantId = ['tenant_alpha', 'tenant_beta', 'tenant_gamma'][index];
2576
+ if (result.status === 'fulfilled' && result.value.success) {
2577
+ console.log(`✅ ${tenantId}: User authenticated`);
2379
2578
  }
2380
2579
  });
2381
2580
  ```
2382
2581
 
2383
- ### Memory Management
2582
+ **Authentication Flow Diagram:**
2583
+ ```
2584
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
2585
+ │ Your App │───▶│ augur_info │───▶│ Target Site │───▶│ User Token │
2586
+ │ │ │ (Admin) │ │ (Tenant) │ │ (Scoped) │
2587
+ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
2588
+ │ │ │ │
2589
+ Admin JWT Verify User Check Password Return Scoped
2590
+ Token Credentials Against Site JWT for Site
2591
+ ```
2592
+
2593
+ ### Multi-Tenant Setup Patterns
2594
+
2595
+ **Configure your application** for enterprise multi-tenancy with these proven patterns:
2384
2596
 
2385
2597
  ```typescript
2386
- // Efficient memory usage patterns
2387
- class EfficientAugurUsage {
2388
- private api: AugurAPI;
2389
- private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();
2598
+ /**
2599
+ * 🏗️ MULTI-TENANT ARCHITECTURE PATTERNS
2600
+ *
2601
+ * Choose the pattern that best fits your enterprise requirements:
2602
+ * - Shared Infrastructure, Isolated Data
2603
+ * - Complete Tenant Isolation
2604
+ * - Hybrid Multi-Tenant Model
2605
+ */
2390
2606
 
2391
- constructor(config: AugurAPIConfig) {
2392
- this.api = new AugurAPI(config);
2607
+ // 🏢 Pattern 1: Tenant Context Manager (Recommended)
2608
+ class TenantContextManager {
2609
+ private tenantAPIs = new Map<string, AugurAPI>();
2610
+ private currentTenant: string | null = null;
2611
+
2612
+ async initializeTenant(
2613
+ tenantId: string,
2614
+ userCredentials: { username: string; password: string },
2615
+ adminToken: string
2616
+ ): Promise<boolean> {
2617
+ try {
2618
+ const authResult = await authenticateUserForSite({
2619
+ targetSiteId: tenantId,
2620
+ username: userCredentials.username,
2621
+ password: userCredentials.password,
2622
+ augurInfoToken: adminToken
2623
+ });
2624
+
2625
+ if (authResult.success) {
2626
+ // Store authenticated API client for tenant
2627
+ this.tenantAPIs.set(tenantId, authResult.targetSiteAPI!);
2628
+ this.currentTenant = tenantId;
2629
+
2630
+ console.log(`✅ Tenant ${tenantId} initialized successfully`);
2631
+ return true;
2632
+ }
2633
+
2634
+ console.error(`❌ Failed to initialize tenant ${tenantId}: ${authResult.error}`);
2635
+ return false;
2636
+ } catch (error) {
2637
+ console.error(`❌ Tenant initialization error:`, error);
2638
+ return false;
2639
+ }
2640
+ }
2641
+
2642
+ // Switch between tenants seamlessly
2643
+ switchTenant(tenantId: string): AugurAPI | null {
2644
+ if (this.tenantAPIs.has(tenantId)) {
2645
+ this.currentTenant = tenantId;
2646
+ return this.tenantAPIs.get(tenantId)!;
2647
+ }
2393
2648
 
2394
- // Clean up expired cache entries every 5 minutes
2395
- setInterval(() => this.cleanupCache(), 5 * 60 * 1000);
2649
+ console.warn(`⚠️ Tenant ${tenantId} not initialized`);
2650
+ return null;
2396
2651
  }
2397
2652
 
2398
- async getCachedData<T>(
2399
- key: string,
2400
- fetcher: () => Promise<T>,
2401
- ttlSeconds = 300
2402
- ): Promise<T> {
2403
- const cached = this.cache.get(key);
2404
- const now = Date.now();
2653
+ // Get current tenant's API client
2654
+ getCurrentAPI(): AugurAPI | null {
2655
+ return this.currentTenant ? this.tenantAPIs.get(this.currentTenant) || null : null;
2656
+ }
2405
2657
 
2406
- if (cached && (now - cached.timestamp) < (cached.ttl * 1000)) {
2407
- return cached.data;
2658
+ // Bulk operations across all tenants
2659
+ async executeAcrossAllTenants<T>(
2660
+ operation: (api: AugurAPI, tenantId: string) => Promise<T>
2661
+ ): Promise<Array<{ tenantId: string; result: T; success: boolean; error?: string }>> {
2662
+ const results: Array<{ tenantId: string; result: T; success: boolean; error?: string }> = [];
2663
+
2664
+ for (const [tenantId, api] of this.tenantAPIs.entries()) {
2665
+ try {
2666
+ const result = await operation(api, tenantId);
2667
+ results.push({ tenantId, result, success: true });
2668
+ } catch (error) {
2669
+ results.push({
2670
+ tenantId,
2671
+ result: null as any,
2672
+ success: false,
2673
+ error: error instanceof Error ? error.message : 'Unknown error'
2674
+ });
2675
+ }
2408
2676
  }
2677
+
2678
+ return results;
2679
+ }
2680
+ }
2409
2681
 
2410
- const data = await fetcher();
2411
- this.cache.set(key, {
2412
- data,
2413
- timestamp: now,
2414
- ttl: ttlSeconds
2682
+ // 🏢 Pattern 2: Tenant Factory (For dynamic tenant creation)
2683
+ class TenantAPIFactory {
2684
+ constructor(private adminToken: string) {}
2685
+
2686
+ async createTenantAPI(
2687
+ tenantId: string,
2688
+ userCredentials: { username: string; password: string }
2689
+ ): Promise<AugurAPI | null> {
2690
+ const authResult = await authenticateUserForSite({
2691
+ targetSiteId: tenantId,
2692
+ username: userCredentials.username,
2693
+ password: userCredentials.password,
2694
+ augurInfoToken: this.adminToken
2415
2695
  });
2416
2696
 
2417
- return data;
2697
+ return authResult.success ? authResult.targetSiteAPI! : null;
2418
2698
  }
2419
2699
 
2420
- private cleanupCache(): void {
2421
- const now = Date.now();
2422
- for (const [key, entry] of this.cache.entries()) {
2423
- if ((now - entry.timestamp) >= (entry.ttl * 1000)) {
2424
- this.cache.delete(key);
2700
+ // Create multiple tenant APIs in parallel
2701
+ async createMultipleTenantAPIs(
2702
+ tenantConfigs: Array<{
2703
+ tenantId: string;
2704
+ credentials: { username: string; password: string };
2705
+ }>
2706
+ ): Promise<Map<string, AugurAPI>> {
2707
+ const results = await Promise.allSettled(
2708
+ tenantConfigs.map(async ({ tenantId, credentials }) => ({
2709
+ tenantId,
2710
+ api: await this.createTenantAPI(tenantId, credentials)
2711
+ }))
2712
+ );
2713
+
2714
+ const tenantAPIs = new Map<string, AugurAPI>();
2715
+
2716
+ results.forEach((result, index) => {
2717
+ if (result.status === 'fulfilled' && result.value.api) {
2718
+ tenantAPIs.set(result.value.tenantId, result.value.api);
2719
+ } else {
2720
+ const tenantId = tenantConfigs[index].tenantId;
2721
+ console.error(`❌ Failed to create API for tenant: ${tenantId}`);
2425
2722
  }
2426
- }
2723
+ });
2724
+
2725
+ return tenantAPIs;
2427
2726
  }
2727
+ }
2428
2728
 
2429
- async getUsers(useCache = true): Promise<any> {
2430
- if (!useCache) {
2431
- return this.api.joomla.users.list({ limit: 50 });
2432
- }
2729
+ // 🏢 Pattern 3: Multi-Tenant Dashboard Implementation
2730
+ class MultiTenantDashboard {
2731
+ private tenantManager = new TenantContextManager();
2732
+ private tenantFactory: TenantAPIFactory;
2433
2733
 
2434
- return this.getCachedData(
2435
- 'users-list',
2436
- () => this.api.joomla.users.list({ limit: 50, edgeCache: 2 }),
2437
- 600 // 10 minutes local cache
2734
+ constructor(adminToken: string) {
2735
+ this.tenantFactory = new TenantAPIFactory(adminToken);
2736
+ }
2737
+
2738
+ async initializeDashboard(
2739
+ userCredentials: { username: string; password: string },
2740
+ tenantIds: string[]
2741
+ ): Promise<void> {
2742
+ console.log(`🚀 Initializing dashboard for ${tenantIds.length} tenants...`);
2743
+
2744
+ // Initialize all tenants in parallel
2745
+ const initResults = await Promise.allSettled(
2746
+ tenantIds.map(tenantId =>
2747
+ this.tenantManager.initializeTenant(
2748
+ tenantId,
2749
+ userCredentials,
2750
+ process.env.AUGUR_ADMIN_TOKEN!
2751
+ )
2752
+ )
2438
2753
  );
2754
+
2755
+ const successCount = initResults.filter(r =>
2756
+ r.status === 'fulfilled' && r.value === true
2757
+ ).length;
2758
+
2759
+ console.log(`✅ Dashboard initialized: ${successCount}/${tenantIds.length} tenants ready`);
2439
2760
  }
2440
2761
 
2441
- destroy(): void {
2442
- this.cache.clear();
2762
+ // Get aggregated data across all tenants
2763
+ async getAggregatedUserData(): Promise<Record<string, any[]>> {
2764
+ const results = await this.tenantManager.executeAcrossAllTenants(
2765
+ async (api, tenantId) => {
2766
+ const users = await api.joomla.users.listData({ limit: 100 });
2767
+ return { tenantId, userCount: users.length, users };
2768
+ }
2769
+ );
2770
+
2771
+ const aggregatedData: Record<string, any[]> = {};
2772
+ results.forEach(({ tenantId, result, success }) => {
2773
+ if (success) {
2774
+ aggregatedData[tenantId] = result.users;
2775
+ }
2776
+ });
2777
+
2778
+ return aggregatedData;
2779
+ }
2780
+
2781
+ // Bulk operations example
2782
+ async bulkUpdateAcrossTenants(updates: Record<string, any>): Promise<void> {
2783
+ const results = await this.tenantManager.executeAcrossAllTenants(
2784
+ async (api, tenantId) => {
2785
+ // Example: Update site settings across all tenants
2786
+ return await api.agrsite.settings.update(updates);
2787
+ }
2788
+ );
2789
+
2790
+ const successCount = results.filter(r => r.success).length;
2791
+ console.log(`✅ Bulk update completed: ${successCount}/${results.length} tenants updated`);
2443
2792
  }
2444
2793
  }
2794
+
2795
+ // 🎮 USAGE EXAMPLES
2796
+
2797
+ // Initialize multi-tenant dashboard
2798
+ const dashboard = new MultiTenantDashboard(process.env.AUGUR_ADMIN_TOKEN!);
2799
+
2800
+ await dashboard.initializeDashboard(
2801
+ { username: 'admin@company.com', password: 'admin_password' },
2802
+ ['tenant_alpha', 'tenant_beta', 'tenant_gamma']
2803
+ );
2804
+
2805
+ // Get aggregated data from all tenants
2806
+ const userData = await dashboard.getAggregatedUserData();
2807
+ console.log('User data across all tenants:', userData);
2808
+
2809
+ // Execute bulk operations
2810
+ await dashboard.bulkUpdateAcrossTenants({
2811
+ maintenanceMode: false,
2812
+ themeColor: '#2563eb'
2813
+ });
2445
2814
  ```
2446
2815
 
2447
- ### Performance Monitoring
2816
+ ### Security Configuration Best Practices
2817
+
2818
+ **Secure your enterprise deployment** with comprehensive security measures:
2448
2819
 
2449
2820
  ```typescript
2450
- // Monitor API performance
2451
- class PerformanceMonitor {
2452
- private metrics = {
2453
- requests: 0,
2454
- errors: 0,
2455
- totalTime: 0,
2456
- cacheHits: 0,
2457
- cacheMisses: 0
2821
+ /**
2822
+ * 🔒 ENTERPRISE SECURITY CONFIGURATION
2823
+ *
2824
+ * Implement defense-in-depth security practices for production
2825
+ * environments with comprehensive monitoring and access control.
2826
+ */
2827
+
2828
+ // 🛡️ Secure Configuration Manager
2829
+ class SecureConfigManager {
2830
+ private static readonly SECURITY_CONFIG = {
2831
+ // Token rotation intervals (in milliseconds)
2832
+ TOKEN_ROTATION_INTERVAL: 4 * 60 * 60 * 1000, // 4 hours
2833
+ ADMIN_TOKEN_ROTATION_INTERVAL: 2 * 60 * 60 * 1000, // 2 hours
2834
+
2835
+ // Rate limiting
2836
+ MAX_REQUESTS_PER_MINUTE: 100,
2837
+ MAX_CROSS_SITE_AUTH_PER_HOUR: 50,
2838
+
2839
+ // Session management
2840
+ SESSION_TIMEOUT: 30 * 60 * 1000, // 30 minutes
2841
+ MAX_CONCURRENT_SESSIONS: 5,
2842
+
2843
+ // Network security
2844
+ ALLOWED_ORIGINS: process.env.ALLOWED_ORIGINS?.split(',') || [],
2845
+ REQUIRE_HTTPS: process.env.NODE_ENV === 'production',
2458
2846
  };
2459
2847
 
2460
- createMonitoredAPI(config: AugurAPIConfig): AugurAPI {
2848
+ // 🔑 Secure token management
2849
+ static createSecureAPIClient(config: {
2850
+ siteId: string;
2851
+ tokenProvider: () => Promise<string>;
2852
+ onTokenRefresh?: (newToken: string) => void;
2853
+ securityLogger?: (event: string, details: any) => void;
2854
+ }): AugurAPI {
2855
+
2856
+ let currentToken: string | null = null;
2857
+ let lastTokenRefresh = 0;
2858
+
2461
2859
  return new AugurAPI({
2462
- ...config,
2860
+ siteId: config.siteId,
2463
2861
 
2464
- onRequest: (config) => {
2465
- // Track request start time
2466
- (config as any).startTime = Date.now();
2467
- this.metrics.requests++;
2468
- return config;
2862
+ // Dynamic token provider with automatic rotation
2863
+ get bearerToken() {
2864
+ return currentToken;
2469
2865
  },
2470
-
2471
- onResponse: (response) => {
2472
- // Calculate request duration
2473
- const startTime = (response.config as any)?.startTime;
2474
- if (startTime) {
2475
- const duration = Date.now() - startTime;
2476
- this.metrics.totalTime += duration;
2477
- }
2478
2866
 
2479
- // Track cache performance
2480
- const cacheStatus = response.headers?.['cf-cache-status'];
2481
- if (cacheStatus === 'HIT') {
2482
- this.metrics.cacheHits++;
2483
- } else if (cacheStatus === 'MISS') {
2484
- this.metrics.cacheMisses++;
2867
+ // Request interceptor for security
2868
+ onRequest: async (requestConfig) => {
2869
+ const now = Date.now();
2870
+
2871
+ // Auto-refresh token if needed
2872
+ if (!currentToken || (now - lastTokenRefresh) > SecureConfigManager.SECURITY_CONFIG.TOKEN_ROTATION_INTERVAL) {
2873
+ try {
2874
+ currentToken = await config.tokenProvider();
2875
+ lastTokenRefresh = now;
2876
+ config.onTokenRefresh?.(currentToken);
2877
+
2878
+ config.securityLogger?.('token_refreshed', {
2879
+ siteId: config.siteId,
2880
+ timestamp: new Date().toISOString()
2881
+ });
2882
+ } catch (error) {
2883
+ config.securityLogger?.('token_refresh_failed', {
2884
+ siteId: config.siteId,
2885
+ error: error instanceof Error ? error.message : 'Unknown error'
2886
+ });
2887
+ throw new Error('Token refresh failed');
2888
+ }
2485
2889
  }
2486
2890
 
2891
+ // Add security headers
2892
+ requestConfig.headers = {
2893
+ ...requestConfig.headers,
2894
+ 'X-Request-ID': crypto.randomUUID(),
2895
+ 'X-Timestamp': new Date().toISOString(),
2896
+ 'User-Agent': `AugurAPI-Client/1.0 (Enterprise; ${process.env.NODE_ENV || 'development'})`
2897
+ };
2898
+
2899
+ // Log security events
2900
+ config.securityLogger?.('api_request', {
2901
+ siteId: config.siteId,
2902
+ url: requestConfig.url,
2903
+ method: requestConfig.method,
2904
+ requestId: requestConfig.headers['X-Request-ID']
2905
+ });
2906
+
2907
+ return requestConfig;
2908
+ },
2909
+
2910
+ // Response interceptor for security monitoring
2911
+ onResponse: (response) => {
2912
+ config.securityLogger?.('api_response', {
2913
+ siteId: config.siteId,
2914
+ status: response.status,
2915
+ requestId: response.config.headers?.['X-Request-ID'],
2916
+ responseTime: Date.now() - (response.config as any)?.startTime
2917
+ });
2918
+
2487
2919
  return response;
2488
2920
  },
2489
-
2921
+
2922
+ // Error interceptor for security logging
2490
2923
  onError: (error) => {
2491
- this.metrics.errors++;
2492
- console.error('API Error metrics:', this.getMetrics());
2924
+ config.securityLogger?.('api_error', {
2925
+ siteId: config.siteId,
2926
+ error: error.message,
2927
+ status: error.response?.status,
2928
+ requestId: error.config?.headers?.['X-Request-ID']
2929
+ });
2930
+
2931
+ // Don't expose sensitive errors in production
2932
+ if (process.env.NODE_ENV === 'production') {
2933
+ throw new Error('API request failed');
2934
+ }
2935
+
2493
2936
  throw error;
2494
2937
  }
2495
2938
  });
2496
2939
  }
2497
2940
 
2498
- getMetrics() {
2499
- const avgResponseTime = this.metrics.requests > 0
2500
- ? this.metrics.totalTime / this.metrics.requests
2501
- : 0;
2502
-
2503
- const errorRate = this.metrics.requests > 0
2504
- ? (this.metrics.errors / this.metrics.requests) * 100
2505
- : 0;
2506
-
2507
- const cacheHitRate = (this.metrics.cacheHits + this.metrics.cacheMisses) > 0
2508
- ? (this.metrics.cacheHits / (this.metrics.cacheHits + this.metrics.cacheMisses)) * 100
2509
- : 0;
2941
+ // 🔐 Cross-site authentication with security controls
2942
+ static createSecureCrossSiteAuthenticator(config: {
2943
+ adminTokenProvider: () => Promise<string>;
2944
+ auditLogger: (event: string, details: any) => void;
2945
+ rateLimiter?: (key: string) => Promise<boolean>;
2946
+ }) {
2947
+ return async (
2948
+ targetSiteId: string,
2949
+ username: string,
2950
+ password: string
2951
+ ): Promise<CrossSiteAuthResult> => {
2952
+ const startTime = Date.now();
2953
+ const requestId = crypto.randomUUID();
2510
2954
 
2511
- return {
2512
- totalRequests: this.metrics.requests,
2513
- totalErrors: this.metrics.errors,
2514
- errorRate: errorRate.toFixed(2) + '%',
2515
- avgResponseTime: avgResponseTime.toFixed(2) + 'ms',
2516
- cacheHitRate: cacheHitRate.toFixed(2) + '%',
2517
- totalCacheHits: this.metrics.cacheHits,
2518
- totalCacheMisses: this.metrics.cacheMisses
2955
+ try {
2956
+ // Rate limiting check
2957
+ if (config.rateLimiter) {
2958
+ const rateLimitKey = `cross_site_auth:${username}:${targetSiteId}`;
2959
+ const allowed = await config.rateLimiter(rateLimitKey);
2960
+ if (!allowed) {
2961
+ config.auditLogger('cross_site_auth_rate_limited', {
2962
+ username,
2963
+ targetSiteId,
2964
+ requestId,
2965
+ timestamp: new Date().toISOString()
2966
+ });
2967
+ return { success: false, error: 'Rate limit exceeded' };
2968
+ }
2969
+ }
2970
+
2971
+ // Get fresh admin token
2972
+ const adminToken = await config.adminTokenProvider();
2973
+
2974
+ // Perform authentication with audit logging
2975
+ config.auditLogger('cross_site_auth_attempt', {
2976
+ username,
2977
+ targetSiteId,
2978
+ requestId,
2979
+ timestamp: new Date().toISOString()
2980
+ });
2981
+
2982
+ const result = await authenticateUserForSite({
2983
+ targetSiteId,
2984
+ username,
2985
+ password,
2986
+ augurInfoToken: adminToken
2987
+ });
2988
+
2989
+ // Log result
2990
+ config.auditLogger('cross_site_auth_result', {
2991
+ username,
2992
+ targetSiteId,
2993
+ requestId,
2994
+ success: result.success,
2995
+ error: result.error,
2996
+ duration: Date.now() - startTime,
2997
+ timestamp: new Date().toISOString()
2998
+ });
2999
+
3000
+ return result;
3001
+
3002
+ } catch (error) {
3003
+ config.auditLogger('cross_site_auth_error', {
3004
+ username,
3005
+ targetSiteId,
3006
+ requestId,
3007
+ error: error instanceof Error ? error.message : 'Unknown error',
3008
+ duration: Date.now() - startTime,
3009
+ timestamp: new Date().toISOString()
3010
+ });
3011
+
3012
+ return {
3013
+ success: false,
3014
+ error: 'Authentication service unavailable'
3015
+ };
3016
+ }
2519
3017
  };
2520
3018
  }
3019
+ }
2521
3020
 
2522
- reset(): void {
2523
- this.metrics = {
2524
- requests: 0,
2525
- errors: 0,
2526
- totalTime: 0,
2527
- cacheHits: 0,
2528
- cacheMisses: 0
3021
+ // 📊 Security monitoring and alerting
3022
+ class SecurityMonitor {
3023
+ private suspiciousActivity = new Map<string, number>();
3024
+ private readonly MAX_FAILED_ATTEMPTS = 5;
3025
+ private readonly MONITORING_WINDOW = 60 * 60 * 1000; // 1 hour
3026
+
3027
+ logSecurityEvent(event: string, details: any): void {
3028
+ const timestamp = new Date().toISOString();
3029
+ const logEntry = {
3030
+ timestamp,
3031
+ event,
3032
+ ...details,
3033
+ severity: this.getEventSeverity(event)
2529
3034
  };
3035
+
3036
+ // Log to security system (console for example)
3037
+ console.log('🔒 SECURITY EVENT:', JSON.stringify(logEntry));
3038
+
3039
+ // Check for suspicious patterns
3040
+ this.detectSuspiciousActivity(event, details);
3041
+
3042
+ // Alert on high-severity events
3043
+ if (logEntry.severity === 'HIGH') {
3044
+ this.triggerSecurityAlert(logEntry);
3045
+ }
3046
+ }
3047
+
3048
+ private getEventSeverity(event: string): 'LOW' | 'MEDIUM' | 'HIGH' {
3049
+ const highSeverityEvents = [
3050
+ 'cross_site_auth_rate_limited',
3051
+ 'token_refresh_failed',
3052
+ 'multiple_failed_attempts'
3053
+ ];
3054
+
3055
+ const mediumSeverityEvents = [
3056
+ 'cross_site_auth_result',
3057
+ 'api_error'
3058
+ ];
3059
+
3060
+ if (highSeverityEvents.includes(event)) return 'HIGH';
3061
+ if (mediumSeverityEvents.includes(event)) return 'MEDIUM';
3062
+ return 'LOW';
3063
+ }
3064
+
3065
+ private detectSuspiciousActivity(event: string, details: any): void {
3066
+ if (event === 'cross_site_auth_result' && !details.success) {
3067
+ const key = `failed_auth:${details.username}:${details.targetSiteId}`;
3068
+ const count = (this.suspiciousActivity.get(key) || 0) + 1;
3069
+ this.suspiciousActivity.set(key, count);
3070
+
3071
+ if (count >= this.MAX_FAILED_ATTEMPTS) {
3072
+ this.logSecurityEvent('multiple_failed_attempts', {
3073
+ username: details.username,
3074
+ targetSiteId: details.targetSiteId,
3075
+ attemptCount: count,
3076
+ windowStart: new Date(Date.now() - this.MONITORING_WINDOW).toISOString()
3077
+ });
3078
+ }
3079
+ }
3080
+ }
3081
+
3082
+ private triggerSecurityAlert(logEntry: any): void {
3083
+ // Implement your alerting system here
3084
+ console.warn('🚨 SECURITY ALERT:', logEntry);
3085
+
3086
+ // Example integrations:
3087
+ // - Send to SIEM system
3088
+ // - Slack/Teams notification
3089
+ // - Email security team
3090
+ // - Trigger automated response
2530
3091
  }
2531
3092
  }
2532
3093
 
2533
- // Usage
2534
- const monitor = new PerformanceMonitor();
2535
- const api = monitor.createMonitoredAPI({
2536
- siteId: 'your-site-id',
2537
- bearerToken: 'your-token'
3094
+ // 🎮 USAGE EXAMPLE
3095
+ const securityMonitor = new SecurityMonitor();
3096
+
3097
+ // Create secure API client with monitoring
3098
+ const secureAPI = SecureConfigManager.createSecureAPIClient({
3099
+ siteId: 'production-site',
3100
+ tokenProvider: async () => {
3101
+ // Your secure token provider implementation
3102
+ return await getTokenFromSecureVault();
3103
+ },
3104
+ onTokenRefresh: (newToken) => {
3105
+ // Store token securely
3106
+ storeTokenInSecureVault(newToken);
3107
+ },
3108
+ securityLogger: (event, details) => {
3109
+ securityMonitor.logSecurityEvent(event, details);
3110
+ }
2538
3111
  });
2539
3112
 
2540
- // After some API calls, check performance
2541
- setTimeout(() => {
2542
- console.log('Performance metrics:', monitor.getMetrics());
2543
- }, 60000);
3113
+ // Create secure cross-site authenticator
3114
+ const secureCrossSiteAuth = SecureConfigManager.createSecureCrossSiteAuthenticator({
3115
+ adminTokenProvider: async () => await getAdminTokenFromVault(),
3116
+ auditLogger: (event, details) => securityMonitor.logSecurityEvent(event, details),
3117
+ rateLimiter: async (key) => {
3118
+ // Implement rate limiting logic
3119
+ return await checkRateLimit(key);
3120
+ }
3121
+ });
2544
3122
  ```
2545
3123
 
3124
+ ### Production Deployment Guidelines
3125
+
3126
+ **Deploy with confidence** using enterprise-grade deployment practices:
3127
+
3128
+ ```typescript
3129
+ /**
3130
+ * 🚀 PRODUCTION DEPLOYMENT CHECKLIST
3131
+ *
3132
+ * Complete guide for deploying Augur API clients in enterprise
3133
+ * production environments with monitoring and reliability.
3134
+ */
3135
+
3136
+ // 🏗️ Production Configuration Template
3137
+ class ProductionConfig {
3138
+ static readonly ENVIRONMENT_CONFIG = {
3139
+ development: {
3140
+ logLevel: 'debug',
3141
+ requestTimeout: 30000,
3142
+ retryAttempts: 2,
3143
+ enableDetailedErrors: true,
3144
+ cacheEnabled: false
3145
+ },
3146
+ staging: {
3147
+ logLevel: 'info',
3148
+ requestTimeout: 15000,
3149
+ retryAttempts: 3,
3150
+ enableDetailedErrors: true,
3151
+ cacheEnabled: true
3152
+ },
3153
+ production: {
3154
+ logLevel: 'warn',
3155
+ requestTimeout: 10000,
3156
+ retryAttempts: 5,
3157
+ enableDetailedErrors: false,
3158
+ cacheEnabled: true
3159
+ }
3160
+ };
3161
+
3162
+ static createProductionAPI(environment: 'development' | 'staging' | 'production'): AugurAPI {
3163
+ const config = ProductionConfig.ENVIRONMENT_CONFIG[environment];
3164
+
3165
+ return new AugurAPI({
3166
+ siteId: process.env.AUGUR_SITE_ID!,
3167
+ bearerToken: process.env.AUGUR_JWT_TOKEN!,
3168
+
3169
+ // Production timeouts and retries
3170
+ timeout: config.requestTimeout,
3171
+ retries: config.retryAttempts,
3172
+
3173
+ // Environment-specific request handling
3174
+ onRequest: (requestConfig) => {
3175
+ // Add request ID for tracing
3176
+ requestConfig.headers = {
3177
+ ...requestConfig.headers,
3178
+ 'X-Environment': environment,
3179
+ 'X-Request-ID': crypto.randomUUID(),
3180
+ 'X-Deployment-Version': process.env.DEPLOYMENT_VERSION || 'unknown'
3181
+ };
3182
+
3183
+ if (config.logLevel === 'debug') {
3184
+ console.log(`🔍 API Request: ${requestConfig.method?.toUpperCase()} ${requestConfig.url}`);
3185
+ }
3186
+
3187
+ return requestConfig;
3188
+ },
3189
+
3190
+ onResponse: (response) => {
3191
+ if (config.logLevel === 'debug') {
3192
+ console.log(`✅ API Response: ${response.status} for ${response.config.url}`);
3193
+ }
3194
+ return response;
3195
+ },
3196
+
3197
+ onError: (error) => {
3198
+ // Production error handling
3199
+ const sanitizedError = config.enableDetailedErrors
3200
+ ? error
3201
+ : new Error('API request failed - check logs for details');
3202
+
3203
+ // Log error details for debugging (always log internally)
3204
+ console.error('❌ API Error:', {
3205
+ url: error.config?.url,
3206
+ method: error.config?.method,
3207
+ status: error.response?.status,
3208
+ message: error.message,
3209
+ requestId: error.config?.headers?.['X-Request-ID'],
3210
+ environment
3211
+ });
3212
+
3213
+ throw sanitizedError;
3214
+ }
3215
+ });
3216
+ }
3217
+ }
3218
+
3219
+ // 📊 Health Check & Monitoring System
3220
+ class ProductionHealthMonitor {
3221
+ private healthCheckInterval: NodeJS.Timer | null = null;
3222
+ private lastHealthCheck: Record<string, any> = {};
3223
+
3224
+ async initializeHealthChecks(apis: Map<string, AugurAPI>): Promise<void> {
3225
+ console.log('🏥 Initializing health monitoring...');
3226
+
3227
+ // Initial health check
3228
+ await this.performHealthChecks(apis);
3229
+
3230
+ // Periodic health checks (every 5 minutes)
3231
+ this.healthCheckInterval = setInterval(async () => {
3232
+ await this.performHealthChecks(apis);
3233
+ }, 5 * 60 * 1000);
3234
+
3235
+ console.log('✅ Health monitoring initialized');
3236
+ }
3237
+
3238
+ private async performHealthChecks(apis: Map<string, AugurAPI>): Promise<void> {
3239
+ const timestamp = new Date().toISOString();
3240
+ const healthResults: Record<string, any> = {};
3241
+
3242
+ for (const [siteId, api] of apis.entries()) {
3243
+ try {
3244
+ const startTime = Date.now();
3245
+
3246
+ // Test core services
3247
+ const [joomlaHealth, pricingHealth, vmiHealth] = await Promise.allSettled([
3248
+ api.joomla.getHealthCheck(),
3249
+ api.pricing.getHealthCheck(),
3250
+ api.vmi.health.ping()
3251
+ ]);
3252
+
3253
+ const responseTime = Date.now() - startTime;
3254
+
3255
+ healthResults[siteId] = {
3256
+ status: 'healthy',
3257
+ responseTime,
3258
+ services: {
3259
+ joomla: joomlaHealth.status === 'fulfilled' ? 'healthy' : 'unhealthy',
3260
+ pricing: pricingHealth.status === 'fulfilled' ? 'healthy' : 'unhealthy',
3261
+ vmi: vmiHealth.status === 'fulfilled' ? 'healthy' : 'unhealthy'
3262
+ },
3263
+ timestamp
3264
+ };
3265
+
3266
+ console.log(`✅ Health check passed for ${siteId} (${responseTime}ms)`);
3267
+
3268
+ } catch (error) {
3269
+ healthResults[siteId] = {
3270
+ status: 'unhealthy',
3271
+ error: error instanceof Error ? error.message : 'Unknown error',
3272
+ timestamp
3273
+ };
3274
+
3275
+ console.error(`❌ Health check failed for ${siteId}:`, error);
3276
+
3277
+ // Trigger alert for unhealthy sites
3278
+ await this.alertUnhealthySite(siteId, error);
3279
+ }
3280
+ }
3281
+
3282
+ this.lastHealthCheck = healthResults;
3283
+ }
3284
+
3285
+ private async alertUnhealthySite(siteId: string, error: any): Promise<void> {
3286
+ // Implement your alerting system
3287
+ console.warn(`🚨 HEALTH ALERT: Site ${siteId} is unhealthy`, error);
3288
+
3289
+ // Example integrations:
3290
+ // - PagerDuty incident
3291
+ // - Slack notification
3292
+ // - Email alert
3293
+ // - Automated failover
3294
+ }
3295
+
3296
+ getHealthStatus(): Record<string, any> {
3297
+ return this.lastHealthCheck;
3298
+ }
3299
+
3300
+ destroy(): void {
3301
+ if (this.healthCheckInterval) {
3302
+ clearInterval(this.healthCheckInterval);
3303
+ this.healthCheckInterval = null;
3304
+ }
3305
+ }
3306
+ }
3307
+
3308
+ // 🔄 Graceful Shutdown Manager
3309
+ class GracefulShutdownManager {
3310
+ private isShuttingDown = false;
3311
+ private activeRequests = new Set<Promise<any>>();
3312
+
3313
+ initialize(): void {
3314
+ // Handle shutdown signals
3315
+ process.on('SIGTERM', () => this.handleShutdown('SIGTERM'));
3316
+ process.on('SIGINT', () => this.handleShutdown('SIGINT'));
3317
+ process.on('SIGHUP', () => this.handleShutdown('SIGHUP'));
3318
+
3319
+ console.log('🛡️ Graceful shutdown handler initialized');
3320
+ }
3321
+
3322
+ private async handleShutdown(signal: string): Promise<void> {
3323
+ if (this.isShuttingDown) return;
3324
+
3325
+ console.log(`🛑 Received ${signal}, starting graceful shutdown...`);
3326
+ this.isShuttingDown = true;
3327
+
3328
+ try {
3329
+ // Wait for active requests to complete (with timeout)
3330
+ const shutdownTimeout = 30000; // 30 seconds
3331
+ const shutdownPromise = Promise.all(this.activeRequests);
3332
+
3333
+ await Promise.race([
3334
+ shutdownPromise,
3335
+ new Promise(resolve => setTimeout(resolve, shutdownTimeout))
3336
+ ]);
3337
+
3338
+ console.log('✅ Graceful shutdown completed');
3339
+ process.exit(0);
3340
+
3341
+ } catch (error) {
3342
+ console.error('❌ Error during shutdown:', error);
3343
+ process.exit(1);
3344
+ }
3345
+ }
3346
+
3347
+ trackRequest<T>(request: Promise<T>): Promise<T> {
3348
+ if (this.isShuttingDown) {
3349
+ throw new Error('Service is shutting down');
3350
+ }
3351
+
3352
+ this.activeRequests.add(request);
3353
+
3354
+ return request.finally(() => {
3355
+ this.activeRequests.delete(request);
3356
+ });
3357
+ }
3358
+
3359
+ isShutdown(): boolean {
3360
+ return this.isShuttingDown;
3361
+ }
3362
+ }
3363
+
3364
+ // 🎮 PRODUCTION DEPLOYMENT EXAMPLE
3365
+ async function deployProductionApplication() {
3366
+ console.log('🚀 Starting production application...');
3367
+
3368
+ // 1. Initialize graceful shutdown
3369
+ const shutdownManager = new GracefulShutdownManager();
3370
+ shutdownManager.initialize();
3371
+
3372
+ // 2. Create production API clients
3373
+ const environment = process.env.NODE_ENV as 'development' | 'staging' | 'production' || 'production';
3374
+ const apis = new Map<string, AugurAPI>();
3375
+
3376
+ // Multiple tenant sites
3377
+ const tenantSites = process.env.TENANT_SITES?.split(',') || ['main-site'];
3378
+
3379
+ for (const siteId of tenantSites) {
3380
+ const api = ProductionConfig.createProductionAPI(environment);
3381
+ apis.set(siteId, api);
3382
+ }
3383
+
3384
+ // 3. Initialize health monitoring
3385
+ const healthMonitor = new ProductionHealthMonitor();
3386
+ await healthMonitor.initializeHealthChecks(apis);
3387
+
3388
+ // 4. Start application logic
3389
+ console.log(`✅ Production application started with ${apis.size} tenant site(s)`);
3390
+
3391
+ // 5. Cleanup on shutdown
3392
+ process.on('exit', () => {
3393
+ healthMonitor.destroy();
3394
+ console.log('🧹 Cleanup completed');
3395
+ });
3396
+
3397
+ return { apis, healthMonitor, shutdownManager };
3398
+ }
3399
+
3400
+ // Environment Variables Checklist for Production:
3401
+ /*
3402
+ REQUIRED ENVIRONMENT VARIABLES:
3403
+ - AUGUR_SITE_ID: Primary site identifier
3404
+ - AUGUR_JWT_TOKEN: Authentication token
3405
+ - AUGUR_ADMIN_TOKEN: Admin token for cross-site operations
3406
+ - NODE_ENV: production|staging|development
3407
+ - TENANT_SITES: Comma-separated list of tenant site IDs
3408
+ - DEPLOYMENT_VERSION: Current deployment version
3409
+ - ALLOWED_ORIGINS: Comma-separated list of allowed origins
3410
+
3411
+ OPTIONAL ENVIRONMENT VARIABLES:
3412
+ - LOG_LEVEL: error|warn|info|debug
3413
+ - REQUEST_TIMEOUT: Request timeout in milliseconds
3414
+ - MAX_RETRIES: Maximum retry attempts
3415
+ - HEALTH_CHECK_INTERVAL: Health check interval in minutes
3416
+ */
3417
+ ```
3418
+
3419
+ **Production Deployment Checklist:**
3420
+
3421
+ - ✅ **Environment Variables**: All required variables set and verified
3422
+ - ✅ **Security Configuration**: Tokens secured, HTTPS enforced, rate limiting enabled
3423
+ - ✅ **Health Monitoring**: Health checks configured with alerting
3424
+ - ✅ **Error Handling**: Production error handling with proper logging
3425
+ - ✅ **Graceful Shutdown**: Proper cleanup on application termination
3426
+ - ✅ **Multi-Tenant Support**: Tenant isolation and cross-site authentication
3427
+ - ✅ **Performance Optimization**: Edge caching and batch operations configured
3428
+ - ✅ **Monitoring & Alerting**: Security events, API performance, and health status tracking
3429
+
3430
+ For detailed authentication patterns, see [Authentication Guide](./AUTHENTICATION.md).
3431
+ For performance optimization strategies, see [Performance Guide](./PERFORMANCE.md).
3432
+
2546
3433
  ## Development
2547
3434
 
2548
3435
  ### Building from Source