@pixelated-tech/components 3.4.1 → 3.4.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.
@@ -0,0 +1,142 @@
1
+ /**
2
+ * CloudWatch Health Check Integration Services
3
+ * Server-side utilities for Route53 health check data retrieval via CloudWatch
4
+ */
5
+ "use server";
6
+ import { CloudWatchClient, GetMetricDataCommand } from '@aws-sdk/client-cloudwatch';
7
+ import { RouteCache } from './site-health-cache';
8
+ // Cache for health check data (15 minutes)
9
+ const healthCheckCache = new RouteCache(15 * 60 * 1000);
10
+ /**
11
+ * Get health check data for a site using CloudWatch metrics
12
+ */
13
+ export async function getCloudwatchHealthCheckData(config, siteName, startDate, endDate) {
14
+ try {
15
+ // Check cache first
16
+ const cacheKey = `cloudwatch-${siteName}-${config.healthCheckId}-${startDate || 'default'}-${endDate || 'default'}`;
17
+ const cached = healthCheckCache.get(cacheKey);
18
+ if (cached) {
19
+ return { success: true, data: cached };
20
+ }
21
+ // Use CloudWatch to get historical health check data
22
+ const cloudWatchClient = new CloudWatchClient({
23
+ region: config.region || 'us-east-1'
24
+ });
25
+ // Set up date range
26
+ const endTime = endDate ? new Date(endDate) : new Date();
27
+ const startTime = startDate ? new Date(startDate) : new Date(endTime.getTime() - (30 * 24 * 60 * 60 * 1000)); // 30 days ago
28
+ // Add 1 day to end time to include the full end date
29
+ const endTimePlusOne = new Date(endTime);
30
+ endTimePlusOne.setDate(endTimePlusOne.getDate() + 1);
31
+ console.log(`CloudWatch: Fetching data for health check ${config.healthCheckId} from ${startTime.toISOString()} to ${endTimePlusOne.toISOString()}`);
32
+ const metricDataQuery = {
33
+ MetricDataQueries: [
34
+ {
35
+ Id: 'healthCheckStatus',
36
+ MetricStat: {
37
+ Metric: {
38
+ Namespace: 'AWS/Route53',
39
+ MetricName: 'HealthCheckStatus',
40
+ Dimensions: [
41
+ {
42
+ Name: 'HealthCheckId',
43
+ Value: config.healthCheckId
44
+ }
45
+ ]
46
+ },
47
+ Period: 3600, // 1 hour intervals
48
+ Stat: 'Average'
49
+ },
50
+ ReturnData: true
51
+ }
52
+ ],
53
+ StartTime: startTime,
54
+ EndTime: endTimePlusOne
55
+ };
56
+ const command = new GetMetricDataCommand(metricDataQuery);
57
+ const response = await cloudWatchClient.send(command);
58
+ console.log(`CloudWatch: Received ${response.MetricDataResults?.[0]?.Timestamps?.length || 0} data points`);
59
+ if (!response.MetricDataResults || response.MetricDataResults.length === 0) {
60
+ console.log(`CloudWatch: No metric data found for health check ${config.healthCheckId}`);
61
+ return {
62
+ success: false,
63
+ error: 'No health check metric data found'
64
+ };
65
+ }
66
+ const metricResult = response.MetricDataResults[0];
67
+ if (!metricResult.Timestamps || !metricResult.Values) {
68
+ console.log(`CloudWatch: No timestamps or values in metric data`);
69
+ return {
70
+ success: false,
71
+ error: 'No health check metric data available'
72
+ };
73
+ }
74
+ // Group data by date
75
+ const dateGroups = {};
76
+ metricResult.Timestamps.forEach((timestamp, index) => {
77
+ const date = timestamp.toISOString().split('T')[0]; // YYYY-MM-DD
78
+ const value = metricResult.Values[index];
79
+ if (!dateGroups[date]) {
80
+ dateGroups[date] = { success: 0, failure: 0 };
81
+ }
82
+ // CloudWatch returns 1 for healthy, 0 for unhealthy
83
+ if (value >= 0.5) { // Consider >= 0.5 as success
84
+ dateGroups[date].success++;
85
+ }
86
+ else {
87
+ dateGroups[date].failure++;
88
+ }
89
+ });
90
+ // Convert to data points
91
+ const data = Object.entries(dateGroups)
92
+ .map(([date, counts]) => {
93
+ const total = counts.success + counts.failure;
94
+ const successRate = total > 0 ? (counts.success / total) * 100 : 0;
95
+ return {
96
+ date,
97
+ successCount: counts.success,
98
+ failureCount: counts.failure,
99
+ totalChecks: total,
100
+ successRate: Math.round(successRate * 100) / 100 // Round to 2 decimal places
101
+ };
102
+ })
103
+ .sort((a, b) => a.date.localeCompare(b.date));
104
+ console.log(`CloudWatch: Processed ${data.length} date groups with data`);
105
+ // Fill in the date range with data points for each day
106
+ let filledData = [];
107
+ if (startDate && endDate) {
108
+ const start = new Date(startDate);
109
+ const end = new Date(endDate);
110
+ const dataMap = new Map(data.map(d => [d.date, d]));
111
+ for (let date = new Date(start); date <= end; date.setDate(date.getDate() + 1)) {
112
+ const dateStr = date.toISOString().split('T')[0];
113
+ const existingData = dataMap.get(dateStr);
114
+ if (existingData) {
115
+ filledData.push(existingData);
116
+ }
117
+ else {
118
+ filledData.push({
119
+ date: dateStr,
120
+ successCount: 0,
121
+ failureCount: 0,
122
+ totalChecks: 0,
123
+ successRate: 0
124
+ });
125
+ }
126
+ }
127
+ }
128
+ else {
129
+ filledData = data;
130
+ }
131
+ // Cache the result
132
+ healthCheckCache.set(cacheKey, filledData);
133
+ return { success: true, data: filledData };
134
+ }
135
+ catch (error) {
136
+ console.error('CloudWatch error:', error);
137
+ return {
138
+ success: false,
139
+ error: error instanceof Error ? error.message : 'Failed to fetch health check data from CloudWatch'
140
+ };
141
+ }
142
+ }
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ComposedChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
4
+ import { SiteHealthTemplate } from './site-health-template';
5
+ export function SiteHealthCloudwatch({ siteName, startDate, endDate }) {
6
+ const fetchCloudwatchData = async (site) => {
7
+ const params = new URLSearchParams({ siteName: site });
8
+ if (startDate)
9
+ params.append('startDate', startDate);
10
+ if (endDate)
11
+ params.append('endDate', endDate);
12
+ const response = await fetch(`/api/site-health/cloudwatch?${params.toString()}`);
13
+ if (!response.ok) {
14
+ throw new Error(`Failed to fetch CloudWatch data: ${response.status}`);
15
+ }
16
+ const result = await response.json();
17
+ if (!result.success) {
18
+ if (result.error?.includes('Health Check ID not configured')) {
19
+ throw new Error('Route53 Health Check ID not configured for this site');
20
+ }
21
+ else {
22
+ throw new Error(result.error || 'Failed to load CloudWatch health check data');
23
+ }
24
+ }
25
+ return result.data;
26
+ };
27
+ return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "CloudWatch Uptime", columnSpan: 2, fetchData: fetchCloudwatchData, children: (data) => {
28
+ if (!data || data.length === 0) {
29
+ return (_jsx("div", { className: "flex items-center justify-center h-64", children: _jsx("div", { className: "text-gray-500", children: "No uptime data available. Route53 health checks may not be configured to send metrics to CloudWatch." }) }));
30
+ }
31
+ // Check if all data points have zero checks (no actual data)
32
+ const hasActualData = data.some(point => point.totalChecks > 0);
33
+ if (!hasActualData) {
34
+ return (_jsx("div", { className: "flex items-center justify-center h-64", children: _jsxs("div", { className: "text-gray-500", children: ["Health check exists but has no metric data in CloudWatch for the selected period.", _jsx("br", {}), "Route53 health checks must be configured to send metrics to CloudWatch for historical data."] }) }));
35
+ }
36
+ return (_jsx("div", { children: _jsx("div", { style: { width: '100%', height: '400px', border: '1px solid #ddd' }, children: _jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(ComposedChart, { data: data, margin: { top: 40, right: 30, left: 20, bottom: 5 }, children: [_jsx("text", { x: "50%", y: 20, textAnchor: "middle", fontSize: "16", fontWeight: "bold", fill: "#374151", children: "CloudWatch Health Check Availability Over Time" }), _jsx(CartesianGrid, { strokeDasharray: "3 3" }), _jsx(XAxis, { dataKey: "date", tick: { fontSize: 12 }, angle: -45, textAnchor: "end", height: 60 }), _jsx(YAxis, { tick: { fontSize: 12 }, label: { value: 'Check Count', angle: -90, position: 'insideLeft' } }), _jsx(Tooltip, { formatter: (value, name) => [
37
+ value?.toLocaleString() || '0',
38
+ name || 'Unknown'
39
+ ], labelFormatter: (label) => `Date: ${label}` }), _jsx(Legend, { wrapperStyle: {
40
+ fontSize: '12px',
41
+ paddingTop: '10px'
42
+ } }), _jsx(Bar, { dataKey: "successCount", stackId: "checks", fill: "#10b981", name: "Successful Checks", radius: [2, 2, 0, 0] }), _jsx(Bar, { dataKey: "failureCount", stackId: "checks", fill: "#ef4444", name: "Failed Checks", radius: [2, 2, 0, 0] })] }, `cloudwatch-chart-${data.length}`) }) }) }));
43
+ } }));
44
+ }
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
3
  import React, { useState, useEffect } from 'react';
3
4
  export function LinkedIn() {
4
5
  const [data, setData] = useState(null);
package/dist/index.js CHANGED
@@ -88,6 +88,7 @@ export * from './components/admin/site-health/site-health-github';
88
88
  export * from './components/admin/site-health/site-health-google-analytics';
89
89
  export * from './components/admin/site-health/site-health-google-search-console';
90
90
  export * from './components/admin/site-health/site-health-on-site-seo';
91
+ export * from './components/admin/site-health/site-health-cloudwatch';
91
92
  export * from './components/admin/site-health/seo-constants';
92
93
  export * from './components/admin/site-health/site-health-overview';
93
94
  export * from './components/admin/site-health/site-health-performance';
@@ -14,6 +14,7 @@ export * from './components/admin/site-health/site-health-google-analytics.integ
14
14
  export * from './components/admin/site-health/site-health-google-search-console.integration';
15
15
  export * from './components/admin/site-health/site-health-indicators';
16
16
  export * from './components/admin/site-health/site-health-on-site-seo.integration';
17
+ export * from './components/admin/site-health/site-health-cloudwatch.integration';
17
18
  export * from './components/admin/site-health/seo-constants';
18
19
  export * from './components/admin/site-health/site-health-security.integration';
19
20
  export * from './components/admin/site-health/site-health-performance';
@@ -0,0 +1,8 @@
1
+ interface SiteHealthCloudwatchProps {
2
+ siteName: string;
3
+ startDate?: string;
4
+ endDate?: string;
5
+ }
6
+ export declare function SiteHealthCloudwatch({ siteName, startDate, endDate }: SiteHealthCloudwatchProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=site-health-cloudwatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-health-cloudwatch.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-cloudwatch.tsx"],"names":[],"mappings":"AAcA,UAAU,yBAAyB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,yBAAyB,2CAiH/F"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * CloudWatch Health Check Integration Services
3
+ * Server-side utilities for Route53 health check data retrieval via CloudWatch
4
+ */
5
+ export interface CloudwatchHealthCheckConfig {
6
+ healthCheckId: string;
7
+ region?: string;
8
+ }
9
+ export interface HealthCheckDataPoint {
10
+ date: string;
11
+ successCount: number;
12
+ failureCount: number;
13
+ totalChecks: number;
14
+ successRate: number;
15
+ }
16
+ export interface CloudwatchHealthCheckResponse {
17
+ success: boolean;
18
+ data?: HealthCheckDataPoint[];
19
+ error?: string;
20
+ }
21
+ /**
22
+ * Get health check data for a site using CloudWatch metrics
23
+ */
24
+ export declare function getCloudwatchHealthCheckData(config: CloudwatchHealthCheckConfig, siteName: string, startDate?: string, endDate?: string): Promise<CloudwatchHealthCheckResponse>;
25
+ //# sourceMappingURL=site-health-cloudwatch.integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-health-cloudwatch.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-cloudwatch.integration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,6BAA6B;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAKD;;GAEG;AACH,wBAAsB,4BAA4B,CACjD,MAAM,EAAE,2BAA2B,EACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,6BAA6B,CAAC,CAiJxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"pixelated.linkedin1.d.ts","sourceRoot":"","sources":["../../../../src/components/cms/pixelated.linkedin1.js"],"names":[],"mappings":"AAEA,oEAiGC"}
1
+ {"version":3,"file":"pixelated.linkedin1.d.ts","sourceRoot":"","sources":["../../../../src/components/cms/pixelated.linkedin1.js"],"names":[],"mappings":"AAGA,oEAiGC"}
@@ -87,6 +87,7 @@ export * from "./components/admin/site-health/site-health-github";
87
87
  export * from "./components/admin/site-health/site-health-google-analytics";
88
88
  export * from "./components/admin/site-health/site-health-google-search-console";
89
89
  export * from "./components/admin/site-health/site-health-on-site-seo";
90
+ export * from "./components/admin/site-health/site-health-cloudwatch";
90
91
  export * from "./components/admin/site-health/seo-constants";
91
92
  export * from "./components/admin/site-health/site-health-overview";
92
93
  export * from "./components/admin/site-health/site-health-performance";
@@ -10,6 +10,7 @@ export * from "./components/admin/site-health/site-health-google-analytics.integ
10
10
  export * from "./components/admin/site-health/site-health-google-search-console.integration";
11
11
  export * from "./components/admin/site-health/site-health-indicators";
12
12
  export * from "./components/admin/site-health/site-health-on-site-seo.integration";
13
+ export * from "./components/admin/site-health/site-health-cloudwatch.integration";
13
14
  export * from "./components/admin/site-health/seo-constants";
14
15
  export * from "./components/admin/site-health/site-health-security.integration";
15
16
  export * from "./components/admin/site-health/site-health-performance";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixelated-tech/components",
3
- "version": "3.4.1",
3
+ "version": "3.4.2",
4
4
  "private": false,
5
5
  "author": {
6
6
  "name": "Pixelated Technologies",
@@ -79,6 +79,7 @@
79
79
  "build-webpack-rsync": "rsync -a --include='*' --include='*/' src/css dist/css"
80
80
  },
81
81
  "dependencies": {
82
+ "@aws-sdk/client-cloudwatch": "^3.958.0",
82
83
  "@aws-sdk/client-route-53": "^3.958.0",
83
84
  "date-fns": "^4.1.0",
84
85
  "globals": "^17.0.0",