lytx 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/.env.example +37 -0
  2. package/README.md +486 -0
  3. package/alchemy.run.ts +155 -0
  4. package/cli/bootstrap-admin.ts +284 -0
  5. package/cli/deploy-staging.ts +692 -0
  6. package/cli/import-events.ts +628 -0
  7. package/cli/import-sites.ts +518 -0
  8. package/cli/index.ts +609 -0
  9. package/cli/init-db.ts +269 -0
  10. package/cli/migrate-to-durable-objects.ts +564 -0
  11. package/cli/migration-worker.ts +300 -0
  12. package/cli/performance-test.ts +588 -0
  13. package/cli/pg/client.ts +4 -0
  14. package/cli/pg/new-site.ts +153 -0
  15. package/cli/rollback-durable-objects.ts +622 -0
  16. package/cli/seed-data.ts +459 -0
  17. package/cli/setup.js +18 -0
  18. package/cli/setup.ts +463 -0
  19. package/cli/validate-migration.ts +200 -0
  20. package/cli/wrangler-migration.jsonc +28 -0
  21. package/db/adapter.ts +166 -0
  22. package/db/analytics_engine/client.ts +0 -0
  23. package/db/analytics_engine/sites.ts +0 -0
  24. package/db/client.ts +16 -0
  25. package/db/d1/client.ts +8 -0
  26. package/db/d1/drizzle.config.ts +35 -0
  27. package/db/d1/migrations/0000_true_maelstrom.sql +165 -0
  28. package/db/d1/migrations/0001_wonderful_bloodaxe.sql +12 -0
  29. package/db/d1/migrations/0002_late_frightful_four.sql +1 -0
  30. package/db/d1/migrations/0003_cuddly_obadiah_stane.sql +16 -0
  31. package/db/d1/migrations/0004_mute_stardust.sql +1 -0
  32. package/db/d1/migrations/0005_awesome_silvermane.sql +3 -0
  33. package/db/d1/migrations/0006_volatile_shriek.sql +2 -0
  34. package/db/d1/migrations/0007_superb_lila_cheney.sql +1 -0
  35. package/db/d1/migrations/0008_bitter_longshot.sql +17 -0
  36. package/db/d1/migrations/0009_wonderful_madame_masque.sql +28 -0
  37. package/db/d1/migrations/meta/0000_snapshot.json +1112 -0
  38. package/db/d1/migrations/meta/0001_snapshot.json +1187 -0
  39. package/db/d1/migrations/meta/0002_snapshot.json +1194 -0
  40. package/db/d1/migrations/meta/0003_snapshot.json +1296 -0
  41. package/db/d1/migrations/meta/0004_snapshot.json +1303 -0
  42. package/db/d1/migrations/meta/0005_snapshot.json +1325 -0
  43. package/db/d1/migrations/meta/0006_snapshot.json +1339 -0
  44. package/db/d1/migrations/meta/0007_snapshot.json +1347 -0
  45. package/db/d1/migrations/meta/0008_snapshot.json +1464 -0
  46. package/db/d1/migrations/meta/0009_snapshot.json +1648 -0
  47. package/db/d1/migrations/meta/_journal.json +76 -0
  48. package/db/d1/schema.ts +407 -0
  49. package/db/d1/sites.ts +374 -0
  50. package/db/d1/teamAiUsage.ts +101 -0
  51. package/db/d1/teams.ts +127 -0
  52. package/db/durable/drizzle.config.ts +8 -0
  53. package/db/durable/durableObjectClient.ts +480 -0
  54. package/db/durable/events.ts +100 -0
  55. package/db/durable/migrations/0000_fair_bucky.sql +38 -0
  56. package/db/durable/migrations/meta/0000_snapshot.json +278 -0
  57. package/db/durable/migrations/meta/_journal.json +13 -0
  58. package/db/durable/migrations/migrations.js +10 -0
  59. package/db/durable/schema.ts +5 -0
  60. package/db/durable/siteDurableObject.ts +1352 -0
  61. package/db/durable/types.ts +53 -0
  62. package/db/postgres/client.ts +13 -0
  63. package/db/postgres/drizzle.config.ts +12 -0
  64. package/db/postgres/migrations/0000_brainy_sprite.sql +116 -0
  65. package/db/postgres/migrations/meta/0000_snapshot.json +681 -0
  66. package/db/postgres/migrations/meta/_journal.json +13 -0
  67. package/db/postgres/schema.ts +145 -0
  68. package/db/postgres/sites.ts +118 -0
  69. package/db/tranformReports.ts +595 -0
  70. package/db/types.ts +55 -0
  71. package/endpoints/api_worker.tsx +1854 -0
  72. package/endpoints/site_do_worker.ts +11 -0
  73. package/index.d.ts +63 -0
  74. package/index.ts +83 -0
  75. package/lib/auth.ts +279 -0
  76. package/lib/geojson/world_countries.json +45307 -0
  77. package/lib/random_name.ts +41 -0
  78. package/lib/sendMail.ts +252 -0
  79. package/package.json +142 -0
  80. package/public/favicon.ico +0 -0
  81. package/public/images/android-chrome-192x192.png +0 -0
  82. package/public/images/android-chrome-512x512.png +0 -0
  83. package/public/images/apple-touch-icon.png +0 -0
  84. package/public/images/favicon-16x16.png +0 -0
  85. package/public/images/favicon-32x32.png +0 -0
  86. package/public/images/lytx_dark_dashboard.png +0 -0
  87. package/public/images/lytx_light_dashboard.png +0 -0
  88. package/public/images/safari-pinned-tab.svg +4 -0
  89. package/public/logo.png +0 -0
  90. package/public/site.webmanifest +26 -0
  91. package/public/sw.js +107 -0
  92. package/src/Document.tsx +86 -0
  93. package/src/api/ai_api.ts +1156 -0
  94. package/src/api/authMiddleware.ts +45 -0
  95. package/src/api/auth_api.ts +465 -0
  96. package/src/api/event_labels_api.ts +193 -0
  97. package/src/api/events_api.ts +210 -0
  98. package/src/api/queueWorker.ts +303 -0
  99. package/src/api/reports_api.ts +278 -0
  100. package/src/api/seed_api.ts +288 -0
  101. package/src/api/sites_api.ts +904 -0
  102. package/src/api/tag_api.ts +458 -0
  103. package/src/api/tag_api_v2.ts +289 -0
  104. package/src/api/team_api.ts +456 -0
  105. package/src/app/Dashboard.tsx +1339 -0
  106. package/src/app/Events.tsx +974 -0
  107. package/src/app/Explore.tsx +312 -0
  108. package/src/app/Layout.tsx +58 -0
  109. package/src/app/Settings.tsx +1302 -0
  110. package/src/app/components/DashboardCard.tsx +118 -0
  111. package/src/app/components/EditableCell.tsx +123 -0
  112. package/src/app/components/EventForm.tsx +93 -0
  113. package/src/app/components/MarketingFooter.tsx +49 -0
  114. package/src/app/components/MarketingNav.tsx +150 -0
  115. package/src/app/components/Nav.tsx +755 -0
  116. package/src/app/components/NewSiteSetup.tsx +298 -0
  117. package/src/app/components/SQLEditor.tsx +740 -0
  118. package/src/app/components/SiteSelector.tsx +126 -0
  119. package/src/app/components/SiteTag.tsx +42 -0
  120. package/src/app/components/SiteTagInstallCard.tsx +241 -0
  121. package/src/app/components/WorldMapCard.tsx +337 -0
  122. package/src/app/components/charts/ChartComponents.tsx +1481 -0
  123. package/src/app/components/charts/EventFunnel.tsx +45 -0
  124. package/src/app/components/charts/EventSummary.tsx +194 -0
  125. package/src/app/components/charts/SankeyFlows.tsx +72 -0
  126. package/src/app/components/marketing/CheckIcon.tsx +16 -0
  127. package/src/app/components/marketing/MarketingLayout.tsx +23 -0
  128. package/src/app/components/marketing/SectionHeading.tsx +35 -0
  129. package/src/app/components/reports/AskAiWorkspace.tsx +371 -0
  130. package/src/app/components/reports/CreateReportStarter.tsx +74 -0
  131. package/src/app/components/reports/DashboardRouteFiltersContext.tsx +14 -0
  132. package/src/app/components/reports/DashboardToolbar.tsx +154 -0
  133. package/src/app/components/reports/DashboardWorkspaceLayout.tsx +63 -0
  134. package/src/app/components/reports/DashboardWorkspaceShell.tsx +118 -0
  135. package/src/app/components/reports/ReportBuilderWorkspace.tsx +76 -0
  136. package/src/app/components/reports/custom/CustomReportBuilderPage.tsx +1667 -0
  137. package/src/app/components/reports/custom/ReportWidgetChart.tsx +297 -0
  138. package/src/app/components/reports/custom/buildWidgetSql.ts +151 -0
  139. package/src/app/components/reports/custom/chartPalettes.ts +18 -0
  140. package/src/app/components/reports/custom/types.ts +50 -0
  141. package/src/app/components/reports/reportBuilderMenuItems.ts +17 -0
  142. package/src/app/components/reports/useDashboardToolbarControls.tsx +235 -0
  143. package/src/app/components/ui/AlertBanner.tsx +101 -0
  144. package/src/app/components/ui/Button.tsx +55 -0
  145. package/src/app/components/ui/Card.tsx +80 -0
  146. package/src/app/components/ui/Input.tsx +72 -0
  147. package/src/app/components/ui/Link.tsx +23 -0
  148. package/src/app/components/ui/ReportBuilderMenu.tsx +246 -0
  149. package/src/app/components/ui/ThemeToggle.tsx +54 -0
  150. package/src/app/constants.ts +6 -0
  151. package/src/app/headers.ts +33 -0
  152. package/src/app/providers/AuthProvider.tsx +189 -0
  153. package/src/app/providers/ClientProviders.tsx +18 -0
  154. package/src/app/providers/QueryProvider.tsx +23 -0
  155. package/src/app/providers/ThemeProvider.tsx +88 -0
  156. package/src/app/utils/chartThemes.ts +146 -0
  157. package/src/app/utils/keybinds.ts +96 -0
  158. package/src/app/utils/media.tsx +24 -0
  159. package/src/client.tsx +114 -0
  160. package/src/config/createLytxAppConfig.ts +252 -0
  161. package/src/config/resourceNames.ts +88 -0
  162. package/src/db/index.ts +67 -0
  163. package/src/index.css +285 -0
  164. package/src/lib/featureFlags.ts +69 -0
  165. package/src/pages/GetStarted.tsx +290 -0
  166. package/src/pages/Home.tsx +268 -0
  167. package/src/pages/Login.tsx +283 -0
  168. package/src/pages/PrivacyPolicy.tsx +120 -0
  169. package/src/pages/Signup.tsx +267 -0
  170. package/src/pages/TermsOfService.tsx +126 -0
  171. package/src/pages/VerifyEmail.tsx +56 -0
  172. package/src/session/durableObject.ts +7 -0
  173. package/src/session/siteSchema.ts +86 -0
  174. package/src/session/types.ts +36 -0
  175. package/src/templates/README.md +80 -0
  176. package/src/templates/cleanFunctions.js +44 -0
  177. package/src/templates/embedFunctions.js +52 -0
  178. package/src/templates/lytx-shared.ts +662 -0
  179. package/src/templates/lytxpixel-core.ts +144 -0
  180. package/src/templates/lytxpixel.ts +267 -0
  181. package/src/templates/lytxpixelBrowser.js +634 -0
  182. package/src/templates/lytxpixelBrowser.mjs +634 -0
  183. package/src/templates/parseData.js +12 -0
  184. package/src/templates/script.ts +31 -0
  185. package/src/templates/template.tsx +50 -0
  186. package/src/templates/test.js +3 -0
  187. package/src/templates/trackWebEvents.ts +177 -0
  188. package/src/templates/vendors/clickcease.ts +8 -0
  189. package/src/templates/vendors/google.ts +174 -0
  190. package/src/templates/vendors/linkedin.ts +23 -0
  191. package/src/templates/vendors/meta.ts +56 -0
  192. package/src/templates/vendors/quantcast.ts +22 -0
  193. package/src/templates/vendors/simplfi.ts +7 -0
  194. package/src/types/app-context.ts +16 -0
  195. package/src/utilities/dashboardParams.ts +188 -0
  196. package/src/utilities/dashboardQueries.ts +537 -0
  197. package/src/utilities/dashboardTransforms.ts +167 -0
  198. package/src/utilities/dataValidation.ts +414 -0
  199. package/src/utilities/detector.ts +73 -0
  200. package/src/utilities/encrypt.ts +103 -0
  201. package/src/utilities/index.ts +13 -0
  202. package/src/utilities/parser.ts +117 -0
  203. package/src/utilities/performanceMonitoring.ts +570 -0
  204. package/src/utilities/route_interuptors.ts +24 -0
  205. package/src/worker.tsx +675 -0
  206. package/tsconfig.json +78 -0
  207. package/types/env.d.ts +16 -0
  208. package/types/rw.d.ts +7 -0
  209. package/types/shims.d.ts +53 -0
  210. package/types/vite.d.ts +19 -0
  211. package/vite/vite-plugin-pixel-bundle.ts +126 -0
  212. package/vite.config.ts +53 -0
  213. package/worker-configuration.d.ts +8401 -0
@@ -0,0 +1,570 @@
1
+ /**
2
+ * Performance Monitoring and Alerting System
3
+ *
4
+ * This module provides real-time performance monitoring for the durable object system,
5
+ * tracking key metrics and triggering alerts when thresholds are exceeded.
6
+ */
7
+
8
+ /**
9
+ * Performance metrics interface
10
+ */
11
+ export interface PerformanceMetrics {
12
+ timestamp: Date;
13
+ siteId?: number;
14
+
15
+ // Response time metrics (milliseconds)
16
+ responseTime: {
17
+ dashboard: number;
18
+ eventIngestion: number;
19
+ api: number;
20
+ };
21
+
22
+ // Throughput metrics (requests/events per second)
23
+ throughput: {
24
+ dashboardRequests: number;
25
+ eventIngestion: number;
26
+ apiRequests: number;
27
+ };
28
+
29
+ // Error metrics (percentage)
30
+ errorRates: {
31
+ dashboard: number;
32
+ eventIngestion: number;
33
+ api: number;
34
+ };
35
+
36
+ // Resource utilization
37
+ resources: {
38
+ memoryUsage: number; // MB
39
+ cpuUsage: number; // percentage
40
+ durableObjectCount: number;
41
+ queueDepth: number;
42
+ };
43
+
44
+ // Business metrics
45
+ business: {
46
+ activeUsers: number;
47
+ eventsPerMinute: number;
48
+ sitesActive: number;
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Performance thresholds for alerting
54
+ */
55
+ export interface PerformanceThresholds {
56
+ responseTime: {
57
+ dashboard: { warning: number; critical: number };
58
+ eventIngestion: { warning: number; critical: number };
59
+ api: { warning: number; critical: number };
60
+ };
61
+
62
+ throughput: {
63
+ dashboardRequests: { warning: number; critical: number };
64
+ eventIngestion: { warning: number; critical: number };
65
+ };
66
+
67
+ errorRates: {
68
+ warning: number; // percentage
69
+ critical: number; // percentage
70
+ };
71
+
72
+ resources: {
73
+ memoryUsage: { warning: number; critical: number }; // MB
74
+ cpuUsage: { warning: number; critical: number }; // percentage
75
+ queueDepth: { warning: number; critical: number };
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Default performance thresholds
81
+ */
82
+ export const DEFAULT_THRESHOLDS: PerformanceThresholds = {
83
+ responseTime: {
84
+ dashboard: { warning: 100, critical: 500 }, // ms
85
+ eventIngestion: { warning: 50, critical: 200 }, // ms
86
+ api: { warning: 200, critical: 1000 } // ms
87
+ },
88
+
89
+ throughput: {
90
+ dashboardRequests: { warning: 100, critical: 50 }, // req/sec
91
+ eventIngestion: { warning: 500, critical: 100 } // events/sec
92
+ },
93
+
94
+ errorRates: {
95
+ warning: 1, // 1%
96
+ critical: 5 // 5%
97
+ },
98
+
99
+ resources: {
100
+ memoryUsage: { warning: 512, critical: 1024 }, // MB
101
+ cpuUsage: { warning: 70, critical: 90 }, // percentage
102
+ queueDepth: { warning: 1000, critical: 5000 }
103
+ }
104
+ };
105
+
106
+ /**
107
+ * Alert severity levels
108
+ */
109
+ export enum AlertSeverity {
110
+ INFO = 'info',
111
+ WARNING = 'warning',
112
+ CRITICAL = 'critical'
113
+ }
114
+
115
+ /**
116
+ * Performance alert interface
117
+ */
118
+ export interface PerformanceAlert {
119
+ id: string;
120
+ timestamp: Date;
121
+ severity: AlertSeverity;
122
+ metric: string;
123
+ value: number;
124
+ threshold: number;
125
+ message: string;
126
+ siteId?: number;
127
+ resolved?: boolean;
128
+ resolvedAt?: Date;
129
+ }
130
+
131
+ /**
132
+ * Performance monitoring class
133
+ */
134
+ export class PerformanceMonitor {
135
+ private metrics: PerformanceMetrics[] = [];
136
+ private alerts: PerformanceAlert[] = [];
137
+ private thresholds: PerformanceThresholds;
138
+ private alertCallbacks: ((alert: PerformanceAlert) => void)[] = [];
139
+
140
+ constructor(thresholds: PerformanceThresholds = DEFAULT_THRESHOLDS) {
141
+ this.thresholds = thresholds;
142
+ }
143
+
144
+ /**
145
+ * Record performance metrics
146
+ */
147
+ recordMetrics(metrics: PerformanceMetrics): void {
148
+ this.metrics.push(metrics);
149
+
150
+ // Keep only last 1000 metrics to prevent memory issues
151
+ if (this.metrics.length > 1000) {
152
+ this.metrics = this.metrics.slice(-1000);
153
+ }
154
+
155
+ // Check for threshold violations
156
+ this.checkThresholds(metrics);
157
+ }
158
+
159
+ /**
160
+ * Check metrics against thresholds and generate alerts
161
+ */
162
+ private checkThresholds(metrics: PerformanceMetrics): void {
163
+ const alerts: PerformanceAlert[] = [];
164
+
165
+ // Response time checks
166
+ if (metrics.responseTime.dashboard > this.thresholds.responseTime.dashboard.critical) {
167
+ alerts.push(this.createAlert(
168
+ AlertSeverity.CRITICAL,
169
+ 'dashboard_response_time',
170
+ metrics.responseTime.dashboard,
171
+ this.thresholds.responseTime.dashboard.critical,
172
+ `Dashboard response time is critically high: ${metrics.responseTime.dashboard}ms`,
173
+ metrics.siteId
174
+ ));
175
+ } else if (metrics.responseTime.dashboard > this.thresholds.responseTime.dashboard.warning) {
176
+ alerts.push(this.createAlert(
177
+ AlertSeverity.WARNING,
178
+ 'dashboard_response_time',
179
+ metrics.responseTime.dashboard,
180
+ this.thresholds.responseTime.dashboard.warning,
181
+ `Dashboard response time is elevated: ${metrics.responseTime.dashboard}ms`,
182
+ metrics.siteId
183
+ ));
184
+ }
185
+
186
+ // Event ingestion response time
187
+ if (metrics.responseTime.eventIngestion > this.thresholds.responseTime.eventIngestion.critical) {
188
+ alerts.push(this.createAlert(
189
+ AlertSeverity.CRITICAL,
190
+ 'event_ingestion_response_time',
191
+ metrics.responseTime.eventIngestion,
192
+ this.thresholds.responseTime.eventIngestion.critical,
193
+ `Event ingestion response time is critically high: ${metrics.responseTime.eventIngestion}ms`,
194
+ metrics.siteId
195
+ ));
196
+ }
197
+
198
+ // Throughput checks
199
+ if (metrics.throughput.eventIngestion < this.thresholds.throughput.eventIngestion.critical) {
200
+ alerts.push(this.createAlert(
201
+ AlertSeverity.CRITICAL,
202
+ 'event_ingestion_throughput',
203
+ metrics.throughput.eventIngestion,
204
+ this.thresholds.throughput.eventIngestion.critical,
205
+ `Event ingestion throughput is critically low: ${metrics.throughput.eventIngestion} events/sec`,
206
+ metrics.siteId
207
+ ));
208
+ }
209
+
210
+ // Error rate checks
211
+ const avgErrorRate = (metrics.errorRates.dashboard + metrics.errorRates.eventIngestion + metrics.errorRates.api) / 3;
212
+ if (avgErrorRate > this.thresholds.errorRates.critical) {
213
+ alerts.push(this.createAlert(
214
+ AlertSeverity.CRITICAL,
215
+ 'error_rate',
216
+ avgErrorRate,
217
+ this.thresholds.errorRates.critical,
218
+ `Average error rate is critically high: ${avgErrorRate.toFixed(2)}%`,
219
+ metrics.siteId
220
+ ));
221
+ } else if (avgErrorRate > this.thresholds.errorRates.warning) {
222
+ alerts.push(this.createAlert(
223
+ AlertSeverity.WARNING,
224
+ 'error_rate',
225
+ avgErrorRate,
226
+ this.thresholds.errorRates.warning,
227
+ `Average error rate is elevated: ${avgErrorRate.toFixed(2)}%`,
228
+ metrics.siteId
229
+ ));
230
+ }
231
+
232
+ // Resource utilization checks
233
+ if (metrics.resources.memoryUsage > this.thresholds.resources.memoryUsage.critical) {
234
+ alerts.push(this.createAlert(
235
+ AlertSeverity.CRITICAL,
236
+ 'memory_usage',
237
+ metrics.resources.memoryUsage,
238
+ this.thresholds.resources.memoryUsage.critical,
239
+ `Memory usage is critically high: ${metrics.resources.memoryUsage}MB`,
240
+ metrics.siteId
241
+ ));
242
+ }
243
+
244
+ if (metrics.resources.queueDepth > this.thresholds.resources.queueDepth.critical) {
245
+ alerts.push(this.createAlert(
246
+ AlertSeverity.CRITICAL,
247
+ 'queue_depth',
248
+ metrics.resources.queueDepth,
249
+ this.thresholds.resources.queueDepth.critical,
250
+ `Queue depth is critically high: ${metrics.resources.queueDepth} items`,
251
+ metrics.siteId
252
+ ));
253
+ }
254
+
255
+ // Process alerts
256
+ for (const alert of alerts) {
257
+ this.processAlert(alert);
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Create a performance alert
263
+ */
264
+ private createAlert(
265
+ severity: AlertSeverity,
266
+ metric: string,
267
+ value: number,
268
+ threshold: number,
269
+ message: string,
270
+ siteId?: number
271
+ ): PerformanceAlert {
272
+ return {
273
+ id: `${metric}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
274
+ timestamp: new Date(),
275
+ severity,
276
+ metric,
277
+ value,
278
+ threshold,
279
+ message,
280
+ siteId,
281
+ resolved: false
282
+ };
283
+ }
284
+
285
+ /**
286
+ * Process and store alert
287
+ */
288
+ private processAlert(alert: PerformanceAlert): void {
289
+ // Check if similar alert already exists and is unresolved
290
+ const existingAlert = this.alerts.find(a =>
291
+ a.metric === alert.metric &&
292
+ a.siteId === alert.siteId &&
293
+ !a.resolved
294
+ );
295
+
296
+ if (existingAlert) {
297
+ // Update existing alert
298
+ existingAlert.value = alert.value;
299
+ existingAlert.timestamp = alert.timestamp;
300
+ existingAlert.message = alert.message;
301
+ } else {
302
+ // Add new alert
303
+ this.alerts.push(alert);
304
+
305
+ // Keep only last 500 alerts
306
+ if (this.alerts.length > 500) {
307
+ this.alerts = this.alerts.slice(-500);
308
+ }
309
+ }
310
+
311
+ // Notify alert callbacks
312
+ this.alertCallbacks.forEach(callback => {
313
+ try {
314
+ callback(alert);
315
+ } catch (error) {
316
+ console.error('Error in alert callback:', error);
317
+ }
318
+ });
319
+ }
320
+
321
+ /**
322
+ * Register alert callback
323
+ */
324
+ onAlert(callback: (alert: PerformanceAlert) => void): void {
325
+ this.alertCallbacks.push(callback);
326
+ }
327
+
328
+ /**
329
+ * Resolve an alert
330
+ */
331
+ resolveAlert(alertId: string): boolean {
332
+ const alert = this.alerts.find(a => a.id === alertId);
333
+ if (alert && !alert.resolved) {
334
+ alert.resolved = true;
335
+ alert.resolvedAt = new Date();
336
+ return true;
337
+ }
338
+ return false;
339
+ }
340
+
341
+ /**
342
+ * Get current metrics summary
343
+ */
344
+ getCurrentMetrics(): PerformanceMetrics | null {
345
+ return this.metrics.length > 0 ? this.metrics[this.metrics.length - 1] : null;
346
+ }
347
+
348
+ /**
349
+ * Get metrics history
350
+ */
351
+ getMetricsHistory(minutes: number = 60): PerformanceMetrics[] {
352
+ const cutoff = new Date(Date.now() - minutes * 60 * 1000);
353
+ return this.metrics.filter(m => m.timestamp >= cutoff);
354
+ }
355
+
356
+ /**
357
+ * Get active alerts
358
+ */
359
+ getActiveAlerts(): PerformanceAlert[] {
360
+ return this.alerts.filter(a => !a.resolved);
361
+ }
362
+
363
+ /**
364
+ * Get alert history
365
+ */
366
+ getAlertHistory(hours: number = 24): PerformanceAlert[] {
367
+ const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
368
+ return this.alerts.filter(a => a.timestamp >= cutoff);
369
+ }
370
+
371
+ /**
372
+ * Generate performance report
373
+ */
374
+ generateReport(hours: number = 24): string {
375
+ const metrics = this.getMetricsHistory(hours * 60);
376
+ const alerts = this.getAlertHistory(hours);
377
+
378
+ if (metrics.length === 0) {
379
+ return 'No performance data available for the specified time period.';
380
+ }
381
+
382
+ const lines: string[] = [];
383
+
384
+ lines.push(`📊 Performance Report (Last ${hours} hours)`);
385
+ lines.push('=' .repeat(50));
386
+ lines.push('');
387
+
388
+ // Calculate averages
389
+ const avgDashboardTime = metrics.reduce((sum, m) => sum + m.responseTime.dashboard, 0) / metrics.length;
390
+ const avgEventIngestionTime = metrics.reduce((sum, m) => sum + m.responseTime.eventIngestion, 0) / metrics.length;
391
+ const avgThroughput = metrics.reduce((sum, m) => sum + m.throughput.eventIngestion, 0) / metrics.length;
392
+ const avgErrorRate = metrics.reduce((sum, m) => sum + (m.errorRates.dashboard + m.errorRates.eventIngestion + m.errorRates.api) / 3, 0) / metrics.length;
393
+
394
+ lines.push('📈 Average Performance Metrics:');
395
+ lines.push(`Dashboard Response Time: ${avgDashboardTime.toFixed(2)}ms`);
396
+ lines.push(`Event Ingestion Time: ${avgEventIngestionTime.toFixed(2)}ms`);
397
+ lines.push(`Event Throughput: ${avgThroughput.toFixed(2)} events/sec`);
398
+ lines.push(`Error Rate: ${avgErrorRate.toFixed(2)}%`);
399
+ lines.push('');
400
+
401
+ // Alert summary
402
+ const criticalAlerts = alerts.filter(a => a.severity === AlertSeverity.CRITICAL);
403
+ const warningAlerts = alerts.filter(a => a.severity === AlertSeverity.WARNING);
404
+ const activeAlerts = alerts.filter(a => !a.resolved);
405
+
406
+ lines.push('🚨 Alert Summary:');
407
+ lines.push(`Critical Alerts: ${criticalAlerts.length}`);
408
+ lines.push(`Warning Alerts: ${warningAlerts.length}`);
409
+ lines.push(`Active Alerts: ${activeAlerts.length}`);
410
+ lines.push('');
411
+
412
+ // Recent alerts
413
+ if (activeAlerts.length > 0) {
414
+ lines.push('🔥 Active Alerts:');
415
+ activeAlerts.slice(0, 5).forEach(alert => {
416
+ const icon = alert.severity === AlertSeverity.CRITICAL ? '🔴' : '🟡';
417
+ lines.push(`${icon} ${alert.message}`);
418
+ });
419
+ lines.push('');
420
+ }
421
+
422
+ // Performance trends
423
+ if (metrics.length >= 2) {
424
+ const recent = metrics.slice(-10);
425
+ const older = metrics.slice(0, 10);
426
+
427
+ const recentAvgDashboard = recent.reduce((sum, m) => sum + m.responseTime.dashboard, 0) / recent.length;
428
+ const olderAvgDashboard = older.reduce((sum, m) => sum + m.responseTime.dashboard, 0) / older.length;
429
+
430
+ const trend = recentAvgDashboard > olderAvgDashboard ? '📈 Increasing' : '📉 Decreasing';
431
+ const change = Math.abs(((recentAvgDashboard - olderAvgDashboard) / olderAvgDashboard) * 100);
432
+
433
+ lines.push('📊 Performance Trends:');
434
+ lines.push(`Dashboard Response Time: ${trend} (${change.toFixed(1)}% change)`);
435
+ lines.push('');
436
+ }
437
+
438
+ // Recommendations
439
+ lines.push('💡 Recommendations:');
440
+ if (avgDashboardTime > this.thresholds.responseTime.dashboard.warning) {
441
+ lines.push('- Consider optimizing dashboard queries or adding caching');
442
+ }
443
+ if (avgThroughput < this.thresholds.throughput.eventIngestion.warning) {
444
+ lines.push('- Review event ingestion pipeline for bottlenecks');
445
+ }
446
+ if (avgErrorRate > this.thresholds.errorRates.warning) {
447
+ lines.push('- Investigate error patterns and implement fixes');
448
+ }
449
+ if (activeAlerts.length > 0) {
450
+ lines.push('- Address active alerts to improve system stability');
451
+ }
452
+ if (lines.length === 1) {
453
+ lines.push('- System performance is within acceptable thresholds');
454
+ }
455
+
456
+ return lines.join('\n');
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Global performance monitor instance
462
+ */
463
+ export const performanceMonitor = new PerformanceMonitor();
464
+
465
+ /**
466
+ * Middleware for automatic performance tracking
467
+ */
468
+ export function createPerformanceMiddleware(monitor: PerformanceMonitor = performanceMonitor) {
469
+ return {
470
+ /**
471
+ * Track dashboard request performance
472
+ */
473
+ trackDashboardRequest: async <T>(operation: () => Promise<T>): Promise<T> => {
474
+ const startTime = performance.now();
475
+ let error = false;
476
+
477
+ try {
478
+ const result = await operation();
479
+ return result;
480
+ } catch (err) {
481
+ error = true;
482
+ throw err;
483
+ } finally {
484
+ const endTime = performance.now();
485
+ const responseTime = endTime - startTime;
486
+
487
+ // Record metrics (simplified - in real implementation, aggregate with other metrics)
488
+ monitor.recordMetrics({
489
+ timestamp: new Date(),
490
+ responseTime: {
491
+ dashboard: responseTime,
492
+ eventIngestion: 0,
493
+ api: 0
494
+ },
495
+ throughput: {
496
+ dashboardRequests: 1,
497
+ eventIngestion: 0,
498
+ apiRequests: 0
499
+ },
500
+ errorRates: {
501
+ dashboard: error ? 100 : 0,
502
+ eventIngestion: 0,
503
+ api: 0
504
+ },
505
+ resources: {
506
+ memoryUsage: 0,
507
+ cpuUsage: 0,
508
+ durableObjectCount: 0,
509
+ queueDepth: 0
510
+ },
511
+ business: {
512
+ activeUsers: 0,
513
+ eventsPerMinute: 0,
514
+ sitesActive: 0
515
+ }
516
+ });
517
+ }
518
+ },
519
+
520
+ /**
521
+ * Track event ingestion performance
522
+ */
523
+ trackEventIngestion: async <T>(eventCount: number, operation: () => Promise<T>): Promise<T> => {
524
+ const startTime = performance.now();
525
+ let error = false;
526
+
527
+ try {
528
+ const result = await operation();
529
+ return result;
530
+ } catch (err) {
531
+ error = true;
532
+ throw err;
533
+ } finally {
534
+ const endTime = performance.now();
535
+ const responseTime = endTime - startTime;
536
+ const throughput = (eventCount / responseTime) * 1000; // events per second
537
+
538
+ monitor.recordMetrics({
539
+ timestamp: new Date(),
540
+ responseTime: {
541
+ dashboard: 0,
542
+ eventIngestion: responseTime,
543
+ api: 0
544
+ },
545
+ throughput: {
546
+ dashboardRequests: 0,
547
+ eventIngestion: throughput,
548
+ apiRequests: 0
549
+ },
550
+ errorRates: {
551
+ dashboard: 0,
552
+ eventIngestion: error ? 100 : 0,
553
+ api: 0
554
+ },
555
+ resources: {
556
+ memoryUsage: 0,
557
+ cpuUsage: 0,
558
+ durableObjectCount: 0,
559
+ queueDepth: 0
560
+ },
561
+ business: {
562
+ activeUsers: 0,
563
+ eventsPerMinute: eventCount,
564
+ sitesActive: 0
565
+ }
566
+ });
567
+ }
568
+ }
569
+ };
570
+ }
@@ -0,0 +1,24 @@
1
+ import type { AppContext } from "@/types/app-context";
2
+ import type { RequestInfo } from "rwsdk/worker";
3
+
4
+ export function checkIfTeamSetupSites({ ctx }: RequestInfo<any, AppContext>) {
5
+ if (!ctx.initial_site_setup) {
6
+ return new Response("User needs to create a site", {
7
+ status: 303,
8
+ headers: { location: "/dashboard/new-site" },
9
+ });
10
+ }
11
+ if (!ctx.sites || ctx.sites.length === 0) {
12
+ return new Response("No sites are assigned to this user", {
13
+ status: 403,
14
+ });
15
+ }
16
+ }
17
+ export function onlyAllowGetPost({ request }: RequestInfo<any, AppContext>) {
18
+ if (!["GET", "POST"].includes(request.method))
19
+ return new Response("Not Found.", { status: 404 });
20
+ }
21
+ export function onlyAllowPost({ request }: RequestInfo<any, AppContext>) {
22
+ if (!["POST"].includes(request.method))
23
+ return new Response("Not Found.", { status: 404 });
24
+ }