railwatch 0.9.4 → 1.0.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 (452) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +37 -33
  3. data/CHANGELOG.md +50 -0
  4. data/README.md +51 -60
  5. data/config/routes.rb +0 -59
  6. data/docs/ai-and-mcp.md +9 -8
  7. data/docs/configuration.md +35 -66
  8. data/docs/faq.md +17 -25
  9. data/docs/getting-started.md +105 -117
  10. data/docs/replacing-nightwatch.md +8 -7
  11. data/docs/replacing-sentry.md +12 -16
  12. data/docs/security.md +14 -20
  13. data/docs/self-hosting.md +24 -22
  14. data/docs/testing.md +6 -14
  15. data/docs/troubleshooting.md +12 -46
  16. data/lib/generators/railwatch/install/install_generator.rb +12 -240
  17. data/lib/generators/railwatch/install/templates/initializer.rb.tt +10 -40
  18. data/lib/generators/railwatch/install/templates/post-deploy +0 -7
  19. data/lib/railwatch/authentication.rb +6 -5
  20. data/lib/railwatch/configuration.rb +21 -151
  21. data/lib/railwatch/engine.rb +9 -118
  22. data/lib/railwatch/patches.rb +0 -11
  23. data/lib/railwatch/reporter.rb +3 -19
  24. data/lib/railwatch/subscribers/process_info.rb +1 -2
  25. data/lib/railwatch/version.rb +1 -1
  26. data/lib/railwatch.rb +4 -62
  27. data/lib/tasks/railwatch_tasks.rake +3 -325
  28. data/llms.txt +23 -25
  29. metadata +3 -453
  30. data/app/channels/railwatch/environment_channel.rb +0 -62
  31. data/app/controllers/concerns/railwatch/telemetry_identity.rb +0 -25
  32. data/app/controllers/railwatch/alerts_controller.rb +0 -48
  33. data/app/controllers/railwatch/anomaly_rules_controller.rb +0 -31
  34. data/app/controllers/railwatch/attachments_controller.rb +0 -22
  35. data/app/controllers/railwatch/broadcasts_controller.rb +0 -15
  36. data/app/controllers/railwatch/cache_events_controller.rb +0 -30
  37. data/app/controllers/railwatch/commands_controller.rb +0 -16
  38. data/app/controllers/railwatch/comments_controller.rb +0 -11
  39. data/app/controllers/railwatch/dashboard_controller.rb +0 -70
  40. data/app/controllers/railwatch/deploys_controller.rb +0 -34
  41. data/app/controllers/railwatch/deprecations_controller.rb +0 -54
  42. data/app/controllers/railwatch/environment_scoped.rb +0 -81
  43. data/app/controllers/railwatch/exceptions_controller.rb +0 -52
  44. data/app/controllers/railwatch/executions_controller.rb +0 -13
  45. data/app/controllers/railwatch/issues_controller.rb +0 -258
  46. data/app/controllers/railwatch/jobs_controller.rb +0 -63
  47. data/app/controllers/railwatch/llm_calls_controller.rb +0 -124
  48. data/app/controllers/railwatch/logs_controller.rb +0 -40
  49. data/app/controllers/railwatch/mails_controller.rb +0 -19
  50. data/app/controllers/railwatch/notifications_controller.rb +0 -11
  51. data/app/controllers/railwatch/outgoing_requests_controller.rb +0 -19
  52. data/app/controllers/railwatch/overview_controller.rb +0 -38
  53. data/app/controllers/railwatch/people_controller.rb +0 -25
  54. data/app/controllers/railwatch/processes_controller.rb +0 -33
  55. data/app/controllers/railwatch/profiles_controller.rb +0 -65
  56. data/app/controllers/railwatch/queries_controller.rb +0 -59
  57. data/app/controllers/railwatch/releases_controller.rb +0 -75
  58. data/app/controllers/railwatch/requests_controller.rb +0 -37
  59. data/app/controllers/railwatch/saved_views_controller.rb +0 -53
  60. data/app/controllers/railwatch/scheduled_tasks_controller.rb +0 -45
  61. data/app/controllers/railwatch/spans_controller.rb +0 -47
  62. data/app/controllers/railwatch/storage_ops_controller.rb +0 -15
  63. data/app/controllers/railwatch/tenants_controller.rb +0 -44
  64. data/app/controllers/railwatch/thresholds_controller.rb +0 -47
  65. data/app/controllers/railwatch/traces_controller.rb +0 -72
  66. data/app/controllers/railwatch/transactions_controller.rb +0 -21
  67. data/app/controllers/railwatch/view_renders_controller.rb +0 -28
  68. data/app/controllers/railwatch/visits_controller.rb +0 -53
  69. data/app/frontend/components/app-content.tsx +0 -26
  70. data/app/frontend/components/app-logo-icon.tsx +0 -23
  71. data/app/frontend/components/app-logo.tsx +0 -14
  72. data/app/frontend/components/app-shell.tsx +0 -38
  73. data/app/frontend/components/app-sidebar-header.tsx +0 -54
  74. data/app/frontend/components/app-sidebar.tsx +0 -94
  75. data/app/frontend/components/app-wordmark.tsx +0 -29
  76. data/app/frontend/components/breadcrumbs.tsx +0 -46
  77. data/app/frontend/components/icon.tsx +0 -16
  78. data/app/frontend/components/input-error.tsx +0 -18
  79. data/app/frontend/components/nav-footer.tsx +0 -46
  80. data/app/frontend/components/nav-main.tsx +0 -41
  81. data/app/frontend/components/nav-user.tsx +0 -50
  82. data/app/frontend/components/railwatch/chart-hover.tsx +0 -34
  83. data/app/frontend/components/railwatch/chart-panel.tsx +0 -170
  84. data/app/frontend/components/railwatch/code.tsx +0 -28
  85. data/app/frontend/components/railwatch/command-palette.tsx +0 -132
  86. data/app/frontend/components/railwatch/copy-block.tsx +0 -52
  87. data/app/frontend/components/railwatch/copy-id.tsx +0 -49
  88. data/app/frontend/components/railwatch/cursor-load-more.tsx +0 -39
  89. data/app/frontend/components/railwatch/data-table.tsx +0 -195
  90. data/app/frontend/components/railwatch/digit-roll.tsx +0 -312
  91. data/app/frontend/components/railwatch/empty-state.tsx +0 -159
  92. data/app/frontend/components/railwatch/env-nav.tsx +0 -130
  93. data/app/frontend/components/railwatch/env-switcher.tsx +0 -145
  94. data/app/frontend/components/railwatch/filter-bar.tsx +0 -153
  95. data/app/frontend/components/railwatch/flamegraph.tsx +0 -324
  96. data/app/frontend/components/railwatch/frames.tsx +0 -295
  97. data/app/frontend/components/railwatch/json-viewer.tsx +0 -159
  98. data/app/frontend/components/railwatch/live-dot.tsx +0 -78
  99. data/app/frontend/components/railwatch/live-toggle.tsx +0 -76
  100. data/app/frontend/components/railwatch/metric-chart.tsx +0 -112
  101. data/app/frontend/components/railwatch/mobile-page-switcher.tsx +0 -89
  102. data/app/frontend/components/railwatch/onboarding.tsx +0 -91
  103. data/app/frontend/components/railwatch/origin-identity.tsx +0 -84
  104. data/app/frontend/components/railwatch/page-header.tsx +0 -42
  105. data/app/frontend/components/railwatch/percentile-picker.tsx +0 -18
  106. data/app/frontend/components/railwatch/relative-time.tsx +0 -18
  107. data/app/frontend/components/railwatch/saved-views.tsx +0 -232
  108. data/app/frontend/components/railwatch/segmented.tsx +0 -64
  109. data/app/frontend/components/railwatch/series-chart.tsx +0 -400
  110. data/app/frontend/components/railwatch/shortcuts-dialog.tsx +0 -89
  111. data/app/frontend/components/railwatch/signal-toast.tsx +0 -121
  112. data/app/frontend/components/railwatch/sort-header.tsx +0 -37
  113. data/app/frontend/components/railwatch/source-link.tsx +0 -57
  114. data/app/frontend/components/railwatch/sparkline-cell.tsx +0 -14
  115. data/app/frontend/components/railwatch/sql-block.tsx +0 -116
  116. data/app/frontend/components/railwatch/stat.tsx +0 -185
  117. data/app/frontend/components/railwatch/status-badge.tsx +0 -79
  118. data/app/frontend/components/railwatch/step-picker.tsx +0 -57
  119. data/app/frontend/components/railwatch/timeline.tsx +0 -405
  120. data/app/frontend/components/railwatch/utilisation-bar.tsx +0 -44
  121. data/app/frontend/components/railwatch/window-picker.tsx +0 -147
  122. data/app/frontend/components/ui/avatar.tsx +0 -51
  123. data/app/frontend/components/ui/badge.tsx +0 -46
  124. data/app/frontend/components/ui/breadcrumb.tsx +0 -109
  125. data/app/frontend/components/ui/button.tsx +0 -60
  126. data/app/frontend/components/ui/card.tsx +0 -92
  127. data/app/frontend/components/ui/chart.tsx +0 -374
  128. data/app/frontend/components/ui/checkbox.tsx +0 -32
  129. data/app/frontend/components/ui/collapsible.tsx +0 -31
  130. data/app/frontend/components/ui/command.tsx +0 -182
  131. data/app/frontend/components/ui/dialog.tsx +0 -158
  132. data/app/frontend/components/ui/dropdown-menu.tsx +0 -255
  133. data/app/frontend/components/ui/hover-card.tsx +0 -42
  134. data/app/frontend/components/ui/input.tsx +0 -21
  135. data/app/frontend/components/ui/label.tsx +0 -24
  136. data/app/frontend/components/ui/popover.tsx +0 -87
  137. data/app/frontend/components/ui/select.tsx +0 -185
  138. data/app/frontend/components/ui/separator.tsx +0 -28
  139. data/app/frontend/components/ui/sheet.tsx +0 -137
  140. data/app/frontend/components/ui/sidebar.tsx +0 -726
  141. data/app/frontend/components/ui/skeleton.tsx +0 -13
  142. data/app/frontend/components/ui/sonner.tsx +0 -41
  143. data/app/frontend/components/ui/table.tsx +0 -114
  144. data/app/frontend/components/ui/tabs.tsx +0 -91
  145. data/app/frontend/components/ui/textarea.tsx +0 -18
  146. data/app/frontend/components/ui/tooltip.tsx +0 -61
  147. data/app/frontend/components/user-info.tsx +0 -32
  148. data/app/frontend/components/user-menu-content.tsx +0 -94
  149. data/app/frontend/entrypoints/application.css +0 -244
  150. data/app/frontend/entrypoints/inertia.tsx +0 -92
  151. data/app/frontend/fonts/CommitMono-Bold.woff2 +0 -0
  152. data/app/frontend/fonts/CommitMono-Regular.woff2 +0 -0
  153. data/app/frontend/fonts/Roboto-Black.woff2 +0 -0
  154. data/app/frontend/fonts/Roboto-Bold.woff2 +0 -0
  155. data/app/frontend/fonts/Roboto-Medium.woff2 +0 -0
  156. data/app/frontend/fonts/Roboto-Regular.woff2 +0 -0
  157. data/app/frontend/hooks/use-appearance.tsx +0 -67
  158. data/app/frontend/hooks/use-clipboard.ts +0 -32
  159. data/app/frontend/hooks/use-flash.tsx +0 -23
  160. data/app/frontend/hooks/use-hotkeys.ts +0 -63
  161. data/app/frontend/hooks/use-initials.tsx +0 -15
  162. data/app/frontend/hooks/use-live.ts +0 -205
  163. data/app/frontend/hooks/use-mobile-navigation.ts +0 -8
  164. data/app/frontend/hooks/use-mobile.ts +0 -21
  165. data/app/frontend/hooks/use-percentile.ts +0 -24
  166. data/app/frontend/hooks/use-reduced-motion.ts +0 -25
  167. data/app/frontend/hooks/use-row-nav.ts +0 -106
  168. data/app/frontend/hooks/use-step.ts +0 -27
  169. data/app/frontend/hooks/use-ticker.ts +0 -12
  170. data/app/frontend/hooks/use-window.ts +0 -58
  171. data/app/frontend/layouts/app/app-sidebar-layout.tsx +0 -107
  172. data/app/frontend/layouts/app-layout.tsx +0 -21
  173. data/app/frontend/layouts/env-layout.tsx +0 -53
  174. data/app/frontend/layouts/persistent-layout.tsx +0 -18
  175. data/app/frontend/lib/cable.ts +0 -10
  176. data/app/frontend/lib/env-pages.ts +0 -236
  177. data/app/frontend/lib/execution-path.ts +0 -55
  178. data/app/frontend/lib/explain.ts +0 -50
  179. data/app/frontend/lib/filter.ts +0 -25
  180. data/app/frontend/lib/flamegraph.ts +0 -155
  181. data/app/frontend/lib/format.ts +0 -71
  182. data/app/frontend/lib/keyboard.ts +0 -18
  183. data/app/frontend/lib/mount.ts +0 -23
  184. data/app/frontend/lib/source-link.ts +0 -217
  185. data/app/frontend/lib/utils.ts +0 -6
  186. data/app/frontend/pages/alerts/index.tsx +0 -171
  187. data/app/frontend/pages/broadcasts/index.tsx +0 -171
  188. data/app/frontend/pages/cache_events/index.tsx +0 -127
  189. data/app/frontend/pages/commands/index.tsx +0 -143
  190. data/app/frontend/pages/deploys/index.tsx +0 -94
  191. data/app/frontend/pages/deploys/show.tsx +0 -309
  192. data/app/frontend/pages/deprecations/index.tsx +0 -176
  193. data/app/frontend/pages/exceptions/index.tsx +0 -271
  194. data/app/frontend/pages/executions/show.tsx +0 -821
  195. data/app/frontend/pages/issues/index.tsx +0 -399
  196. data/app/frontend/pages/issues/show.tsx +0 -1367
  197. data/app/frontend/pages/jobs/index.tsx +0 -381
  198. data/app/frontend/pages/jobs/klass.tsx +0 -215
  199. data/app/frontend/pages/llm_calls/index.tsx +0 -664
  200. data/app/frontend/pages/logs/index.tsx +0 -303
  201. data/app/frontend/pages/mails/index.tsx +0 -218
  202. data/app/frontend/pages/notifications/index.tsx +0 -161
  203. data/app/frontend/pages/outgoing_requests/index.tsx +0 -309
  204. data/app/frontend/pages/overview/show.tsx +0 -333
  205. data/app/frontend/pages/people/index.tsx +0 -102
  206. data/app/frontend/pages/people/show.tsx +0 -149
  207. data/app/frontend/pages/processes/index.tsx +0 -335
  208. data/app/frontend/pages/profiles/index.tsx +0 -110
  209. data/app/frontend/pages/profiles/show.tsx +0 -105
  210. data/app/frontend/pages/queries/index.tsx +0 -492
  211. data/app/frontend/pages/queries/show.tsx +0 -276
  212. data/app/frontend/pages/releases/index.tsx +0 -190
  213. data/app/frontend/pages/releases/release-health.tsx +0 -68
  214. data/app/frontend/pages/releases/release-path.ts +0 -25
  215. data/app/frontend/pages/releases/show.tsx +0 -262
  216. data/app/frontend/pages/requests/index.tsx +0 -235
  217. data/app/frontend/pages/requests/route.tsx +0 -159
  218. data/app/frontend/pages/scheduled_tasks/index.tsx +0 -220
  219. data/app/frontend/pages/spans/index.tsx +0 -229
  220. data/app/frontend/pages/spans/show.tsx +0 -220
  221. data/app/frontend/pages/storage_ops/index.tsx +0 -150
  222. data/app/frontend/pages/tenants/index.tsx +0 -236
  223. data/app/frontend/pages/tenants/show.tsx +0 -477
  224. data/app/frontend/pages/tenants/tenant-path.ts +0 -26
  225. data/app/frontend/pages/thresholds/index.tsx +0 -414
  226. data/app/frontend/pages/traces/show.tsx +0 -208
  227. data/app/frontend/pages/transactions/index.tsx +0 -220
  228. data/app/frontend/pages/view_renders/index.tsx +0 -241
  229. data/app/frontend/pages/visits/index.tsx +0 -321
  230. data/app/frontend/public/icon.png +0 -0
  231. data/app/frontend/public/icon.svg +0 -5
  232. data/app/frontend/routes/index.d.ts +0 -2136
  233. data/app/frontend/routes/index.js +0 -2112
  234. data/app/frontend/types/globals.d.ts +0 -9
  235. data/app/frontend/types/index.ts +0 -288
  236. data/app/frontend/types/vite-env.d.ts +0 -9
  237. data/app/helpers/railwatch/assets_helper.rb +0 -52
  238. data/app/jobs/railwatch/anomaly_scan_job.rb +0 -11
  239. data/app/jobs/railwatch/application_job.rb +0 -7
  240. data/app/jobs/railwatch/auto_resolve_issues_job.rb +0 -19
  241. data/app/jobs/railwatch/check_scheduled_tasks_job.rb +0 -113
  242. data/app/jobs/railwatch/detect_anomalies_job.rb +0 -171
  243. data/app/jobs/railwatch/detect_performance_issues_job.rb +0 -95
  244. data/app/jobs/railwatch/group_exceptions_job.rb +0 -91
  245. data/app/jobs/railwatch/optimize_telemetry_job.rb +0 -23
  246. data/app/jobs/railwatch/performance_scan_job.rb +0 -11
  247. data/app/jobs/railwatch/prune_telemetry_job.rb +0 -129
  248. data/app/jobs/railwatch/release_health_rollup_job.rb +0 -64
  249. data/app/jobs/railwatch/rollup_catchup_job.rb +0 -21
  250. data/app/jobs/railwatch/rollup_job.rb +0 -132
  251. data/app/jobs/railwatch/scheduled_task_scan_job.rb +0 -11
  252. data/app/models/concerns/railwatch/detection_snapshotting.rb +0 -20
  253. data/app/models/railwatch/alert.rb +0 -229
  254. data/app/models/railwatch/alert_rule.rb +0 -121
  255. data/app/models/railwatch/anomaly_rule.rb +0 -51
  256. data/app/models/railwatch/application.rb +0 -44
  257. data/app/models/railwatch/application_record.rb +0 -33
  258. data/app/models/railwatch/comment.rb +0 -36
  259. data/app/models/railwatch/deploy.rb +0 -67
  260. data/app/models/railwatch/environment.rb +0 -68
  261. data/app/models/railwatch/execution_presenter.rb +0 -185
  262. data/app/models/railwatch/filter_query.rb +0 -184
  263. data/app/models/railwatch/followup_receipt.rb +0 -27
  264. data/app/models/railwatch/ingest/batch.rb +0 -355
  265. data/app/models/railwatch/ingest/mapper.rb +0 -585
  266. data/app/models/railwatch/ingest/payload.rb +0 -96
  267. data/app/models/railwatch/ingest/rollup_absorber.rb +0 -170
  268. data/app/models/railwatch/ingest/writer.rb +0 -137
  269. data/app/models/railwatch/issue.rb +0 -277
  270. data/app/models/railwatch/issue_activity.rb +0 -23
  271. data/app/models/railwatch/issue_detection_presenter.rb +0 -309
  272. data/app/models/railwatch/issue_detection_snapshot.rb +0 -77
  273. data/app/models/railwatch/maintenance_task.rb +0 -53
  274. data/app/models/railwatch/saved_view.rb +0 -63
  275. data/app/models/railwatch/telemetry/aggregations.rb +0 -251
  276. data/app/models/railwatch/telemetry/attachment.rb +0 -31
  277. data/app/models/railwatch/telemetry/bounded_gzip.rb +0 -101
  278. data/app/models/railwatch/telemetry/broadcast.rb +0 -13
  279. data/app/models/railwatch/telemetry/cache_event.rb +0 -16
  280. data/app/models/railwatch/telemetry/child.rb +0 -53
  281. data/app/models/railwatch/telemetry/cursor_page.rb +0 -99
  282. data/app/models/railwatch/telemetry/deprecation.rb +0 -13
  283. data/app/models/railwatch/telemetry/enqueued_job.rb +0 -13
  284. data/app/models/railwatch/telemetry/exception.rb +0 -28
  285. data/app/models/railwatch/telemetry/execution.rb +0 -152
  286. data/app/models/railwatch/telemetry/export_delivery.rb +0 -32
  287. data/app/models/railwatch/telemetry/export_destination.rb +0 -66
  288. data/app/models/railwatch/telemetry/health_sample.rb +0 -72
  289. data/app/models/railwatch/telemetry/ingest_batch.rb +0 -31
  290. data/app/models/railwatch/telemetry/llm_call.rb +0 -37
  291. data/app/models/railwatch/telemetry/log.rb +0 -91
  292. data/app/models/railwatch/telemetry/mail.rb +0 -13
  293. data/app/models/railwatch/telemetry/n_plus_one.rb +0 -92
  294. data/app/models/railwatch/telemetry/notification.rb +0 -13
  295. data/app/models/railwatch/telemetry/outgoing_request.rb +0 -13
  296. data/app/models/railwatch/telemetry/person.rb +0 -50
  297. data/app/models/railwatch/telemetry/process.rb +0 -10
  298. data/app/models/railwatch/telemetry/profile.rb +0 -37
  299. data/app/models/railwatch/telemetry/query.rb +0 -57
  300. data/app/models/railwatch/telemetry/query_shape.rb +0 -45
  301. data/app/models/railwatch/telemetry/release_health.rb +0 -69
  302. data/app/models/railwatch/telemetry/rollup.rb +0 -157
  303. data/app/models/railwatch/telemetry/session.rb +0 -24
  304. data/app/models/railwatch/telemetry/span.rb +0 -19
  305. data/app/models/railwatch/telemetry/storage_op.rb +0 -13
  306. data/app/models/railwatch/telemetry/tenant.rb +0 -240
  307. data/app/models/railwatch/telemetry/transaction.rb +0 -13
  308. data/app/models/railwatch/telemetry/view_render.rb +0 -13
  309. data/app/models/railwatch/telemetry/visit.rb +0 -24
  310. data/app/models/railwatch/telemetry_record.rb +0 -107
  311. data/app/models/railwatch/threshold.rb +0 -66
  312. data/app/models/railwatch/user.rb +0 -47
  313. data/app/models/railwatch/viewer.rb +0 -13
  314. data/app/models/railwatch/window.rb +0 -66
  315. data/app/views/layouts/railwatch/dashboard.html.erb +0 -25
  316. data/db/railwatch_migrate/20260916000000_create_railwatch_tables.rb +0 -151
  317. data/db/railwatch_migrate/20260917000000_create_railwatch_maintenance_tasks.rb +0 -18
  318. data/db/railwatch_migrate/20260917120000_create_railwatch_followup_receipts.rb +0 -20
  319. data/db/railwatch_migrate/20260918120000_widen_host_user_ids.rb +0 -71
  320. data/db/railwatch_telemetry_migrate/20260903000000_enable_incremental_vacuum.rb +0 -62
  321. data/db/railwatch_telemetry_migrate/20260903000001_create_telemetry.rb +0 -481
  322. data/db/railwatch_telemetry_migrate/20260903000002_rename_tenant_to_app_tenant.rb +0 -14
  323. data/db/railwatch_telemetry_migrate/20260903000003_add_statement_count_to_transactions.rb +0 -9
  324. data/db/railwatch_telemetry_migrate/20260903000004_add_role_and_channel.rb +0 -10
  325. data/db/railwatch_telemetry_migrate/20260903000005_add_locals_to_exceptions.rb +0 -9
  326. data/db/railwatch_telemetry_migrate/20260903000006_add_spans_health_vitals_and_fts.rb +0 -82
  327. data/db/railwatch_telemetry_migrate/20260903000007_rename_span_attributes_to_payload.rb +0 -10
  328. data/db/railwatch_telemetry_migrate/20260903000008_add_profiles_and_attachments.rb +0 -60
  329. data/db/railwatch_telemetry_migrate/20260903000009_add_truncated_to_attachments.rb +0 -9
  330. data/db/railwatch_telemetry_migrate/20260903000010_create_sessions_and_release_health.rb +0 -51
  331. data/db/railwatch_telemetry_migrate/20260903000011_add_fingerprint_to_exceptions.rb +0 -11
  332. data/db/railwatch_telemetry_migrate/20260904000012_add_failed_to_broadcasts.rb +0 -10
  333. data/db/railwatch_telemetry_migrate/20260904010000_add_filter_cursor_indexes.rb +0 -37
  334. data/db/railwatch_telemetry_migrate/20260904020000_add_n_plus_ones_execution_id_index.rb +0 -11
  335. data/db/railwatch_telemetry_migrate/20260904120000_create_query_shapes.rb +0 -15
  336. data/db/railwatch_telemetry_migrate/20260906120000_add_backpressure_factor_to_ingest_batches.rb +0 -9
  337. data/db/railwatch_telemetry_migrate/20260907000000_rename_lantern_version_on_processes.rb +0 -10
  338. data/db/railwatch_telemetry_migrate/20260913000000_rename_nightrail_version_on_processes.rb +0 -9
  339. data/db/railwatch_telemetry_migrate/20260914000000_drop_orphan_durable_ingest_tables.rb +0 -18
  340. data/db/railwatch_telemetry_migrate/20260914010000_drop_orphan_durable_ingest_columns.rb +0 -25
  341. data/db/railwatch_telemetry_migrate/20260915000000_create_llm_calls.rb +0 -55
  342. data/db/railwatch_telemetry_migrate/20260915120000_add_detail_to_llm_calls.rb +0 -21
  343. data/db/railwatch_telemetry_migrate/20260915200000_add_explained_index_to_queries.rb +0 -16
  344. data/db/railwatch_telemetry_migrate/20260915210000_add_slowest_index_to_queries.rb +0 -18
  345. data/db/railwatch_telemetry_migrate/20260917010000_add_batch_ledger_to_ingest_batches.rb +0 -16
  346. data/db/railwatch_telemetry_migrate/20260919000100_create_export_queue.rb +0 -146
  347. data/db/railwatch_telemetry_migrate/20260922000000_add_covering_indexes_for_dashboard_aggregates.rb +0 -34
  348. data/db/railwatch_telemetry_migrate/20260923000000_add_people_index.rb +0 -15
  349. data/db/railwatch_telemetry_migrate/20260925000000_add_tenant_summary_index.rb +0 -17
  350. data/db/railwatch_telemetry_migrate/20261008000000_add_in_transaction_to_outgoing_requests.rb +0 -10
  351. data/docs/embedded.md +0 -503
  352. data/lib/puma/plugin/railwatch.rb +0 -215
  353. data/lib/railwatch/dashboard_assets.rb +0 -45
  354. data/lib/railwatch/embedded.rb +0 -50
  355. data/lib/railwatch/export/client.rb +0 -124
  356. data/lib/railwatch/export/lease.rb +0 -56
  357. data/lib/railwatch/export/outbox.rb +0 -361
  358. data/lib/railwatch/export/policy.rb +0 -79
  359. data/lib/railwatch/export/sender.rb +0 -130
  360. data/lib/railwatch/ingest_request_body_limit.rb +0 -10
  361. data/lib/railwatch/maintenance.rb +0 -194
  362. data/lib/railwatch/patches/migration_busy_timeout.rb +0 -73
  363. data/lib/railwatch/transport/local.rb +0 -78
  364. data/lib/railwatch/transport/socket.rb +0 -183
  365. data/lib/railwatch/writer.rb +0 -370
  366. data/public/railwatch/assets/CommitMono-Bold-D6h61ieg.woff2 +0 -0
  367. data/public/railwatch/assets/CommitMono-Regular-zr8w7Obm.woff2 +0 -0
  368. data/public/railwatch/assets/Roboto-Black-auA4GeOK.woff2 +0 -0
  369. data/public/railwatch/assets/Roboto-Bold-CJLnO8j1.woff2 +0 -0
  370. data/public/railwatch/assets/Roboto-Medium-Cm2bwKpj.woff2 +0 -0
  371. data/public/railwatch/assets/Roboto-Regular-Chaq1-PV.woff2 +0 -0
  372. data/public/railwatch/assets/app-layout-by7G3sa1.js +0 -41
  373. data/public/railwatch/assets/application-Cc5v_Vk9.css +0 -1
  374. data/public/railwatch/assets/arrow-up-DCZ7emux.js +0 -1
  375. data/public/railwatch/assets/braces-CykT5_5u.js +0 -1
  376. data/public/railwatch/assets/card-C-PPSA0g.js +0 -1
  377. data/public/railwatch/assets/chart-Cov79YC5.js +0 -39
  378. data/public/railwatch/assets/chart-hover-DdK4E-2h.js +0 -1
  379. data/public/railwatch/assets/chart-panel-BPaYRXJB.js +0 -1
  380. data/public/railwatch/assets/code-BYjC4YZ_.js +0 -1
  381. data/public/railwatch/assets/copy-id-LPfoj9cC.js +0 -1
  382. data/public/railwatch/assets/cursor-load-more-DSdhgpGl.js +0 -1
  383. data/public/railwatch/assets/data-table-CQ_YRWQs.js +0 -1
  384. data/public/railwatch/assets/env-layout-CdMWbUkm.js +0 -1
  385. data/public/railwatch/assets/execution-path-7MYt-Qbn.js +0 -1
  386. data/public/railwatch/assets/filter-bar-CrzpQ_vD.js +0 -1
  387. data/public/railwatch/assets/flamegraph-BIFEdTAg.js +0 -2
  388. data/public/railwatch/assets/format-B9SDkrWj.js +0 -1
  389. data/public/railwatch/assets/frames-C5t-x4f0.js +0 -1
  390. data/public/railwatch/assets/index--NQ7us-L.js +0 -1
  391. data/public/railwatch/assets/index-2kgYe_99.js +0 -1
  392. data/public/railwatch/assets/index-4YEPHxtO.js +0 -1
  393. data/public/railwatch/assets/index-AOCC1GDJ.js +0 -1
  394. data/public/railwatch/assets/index-BEo-u0eO.js +0 -1
  395. data/public/railwatch/assets/index-BJ1V3w2N.js +0 -1
  396. data/public/railwatch/assets/index-BPoHsBKb.js +0 -1
  397. data/public/railwatch/assets/index-BZzyGUM3.js +0 -1
  398. data/public/railwatch/assets/index-BgVq7yKL.js +0 -1
  399. data/public/railwatch/assets/index-BqFGEZvW.js +0 -1
  400. data/public/railwatch/assets/index-ByuHJ1CH.js +0 -1
  401. data/public/railwatch/assets/index-C2j50AL2.js +0 -1
  402. data/public/railwatch/assets/index-CLEdTkvW.js +0 -1
  403. data/public/railwatch/assets/index-CWgq8qBu.js +0 -1
  404. data/public/railwatch/assets/index-CXQ7_oVj.js +0 -2
  405. data/public/railwatch/assets/index-CmyYrjS2.js +0 -1
  406. data/public/railwatch/assets/index-Cn9_HjDo.js +0 -1
  407. data/public/railwatch/assets/index-CsMAm9UK.js +0 -1
  408. data/public/railwatch/assets/index-D0Kk_oGY.js +0 -1
  409. data/public/railwatch/assets/index-DC9ZptJh.js +0 -1
  410. data/public/railwatch/assets/index-DNzg7jmV.js +0 -1
  411. data/public/railwatch/assets/index-DQ8uJO5f.js +0 -1
  412. data/public/railwatch/assets/index-Deq14BxB.js +0 -1
  413. data/public/railwatch/assets/index-IaV3Blzp.js +0 -1
  414. data/public/railwatch/assets/index-TYNWcJQs.js +0 -1
  415. data/public/railwatch/assets/index-WonH2fcZ.js +0 -1
  416. data/public/railwatch/assets/index-WxWUaH_d.js +0 -1
  417. data/public/railwatch/assets/index-mUNrl4ky.js +0 -1
  418. data/public/railwatch/assets/inertia-DfNRARuA.js +0 -97
  419. data/public/railwatch/assets/json-viewer-DMsF5glp.js +0 -1
  420. data/public/railwatch/assets/klass-CmHYk5rz.js +0 -1
  421. data/public/railwatch/assets/origin-identity-1R3bqa3R.js +0 -1
  422. data/public/railwatch/assets/percentile-picker-73z5PAps.js +0 -1
  423. data/public/railwatch/assets/relative-time-Dsc_cCdX.js +0 -1
  424. data/public/railwatch/assets/release-health-BV5OaOWd.js +0 -1
  425. data/public/railwatch/assets/route-CWh58C7K.js +0 -1
  426. data/public/railwatch/assets/segmented-DB4zh-Am.js +0 -1
  427. data/public/railwatch/assets/select-DBKjFlOX.js +0 -1
  428. data/public/railwatch/assets/series-chart-BaiDG795.js +0 -1
  429. data/public/railwatch/assets/show-B4ICCFGh.js +0 -1
  430. data/public/railwatch/assets/show-B5lOU9Gv.js +0 -1
  431. data/public/railwatch/assets/show-BYPB74t4.js +0 -2
  432. data/public/railwatch/assets/show-C7A0m2M8.js +0 -1
  433. data/public/railwatch/assets/show-DT5X1zqE.js +0 -1
  434. data/public/railwatch/assets/show-D_NBdXsn.js +0 -2
  435. data/public/railwatch/assets/show-DgPCnRg8.js +0 -1
  436. data/public/railwatch/assets/show-Yl69HF0x.js +0 -1
  437. data/public/railwatch/assets/show-_QxuUQCm.js +0 -3
  438. data/public/railwatch/assets/show-g__jnN36.js +0 -1
  439. data/public/railwatch/assets/show-l4ztfjl9.js +0 -1
  440. data/public/railwatch/assets/sort-header-BcG8ulEH.js +0 -1
  441. data/public/railwatch/assets/source-link-B2183i2-.js +0 -1
  442. data/public/railwatch/assets/sparkline-cell-B47ipscB.js +0 -1
  443. data/public/railwatch/assets/stat-DT2hhsqS.js +0 -1
  444. data/public/railwatch/assets/status-badge-CmbRKlY7.js +0 -1
  445. data/public/railwatch/assets/tenant-path-C4rNGauE.js +0 -1
  446. data/public/railwatch/assets/use-clipboard-oTNeP4ax.js +0 -1
  447. data/public/railwatch/assets/use-live-CLZZj245.js +0 -1
  448. data/public/railwatch/icon.png +0 -0
  449. data/public/railwatch/icon.svg +0 -5
  450. data/public/railwatch/manifest.json +0 -1245
  451. data/public/railwatch/rails-vite.json +0 -1
  452. /data/{app/frontend/lib → lib/generators/railwatch/install/templates}/railwatch.ts +0 -0
@@ -1,303 +0,0 @@
1
- import { Link, router, usePage } from "@inertiajs/react"
2
- import { FileText } from "lucide-react"
3
- import { useState } from "react"
4
-
5
- import { CursorLoadMore } from "@/components/railwatch/cursor-load-more"
6
- import { DataTable } from "@/components/railwatch/data-table"
7
- import { EmptyState } from "@/components/railwatch/empty-state"
8
- import { FilterBar } from "@/components/railwatch/filter-bar"
9
- import { PageHeader } from "@/components/railwatch/page-header"
10
- import { SavedViewsMenu } from "@/components/railwatch/saved-views"
11
- import { Segmented, SegmentedItem } from "@/components/railwatch/segmented"
12
- import { LevelBadge } from "@/components/railwatch/status-badge"
13
- import EnvLayout from "@/layouts/env-layout"
14
- import { parseFilter, serializeFilter } from "@/lib/filter"
15
- import { count, when } from "@/lib/format"
16
- import { cn } from "@/lib/utils"
17
- import * as R from "@/routes"
18
- import type { CursorMeta, SharedProps } from "@/types"
19
-
20
- interface LogRow {
21
- id: number
22
- level: string
23
- message: string
24
- tags: string[]
25
- occurred_at: string
26
- execution_id: string | null
27
- execution_source: string | null
28
- execution_preview: string | null
29
- source: string | null
30
- tenant: string | null
31
- user_ref: string | null
32
- context: string | null
33
- }
34
- interface Props {
35
- logs: LogRow[]
36
- counts: Record<string, number>
37
- // id -> the matching part of the message, each hit wrapped in MARK. Only
38
- // present for rows the full-text search actually matched.
39
- highlights: Record<number, string>
40
- pagination: CursorMeta
41
- q: string
42
- }
43
-
44
- // Telemetry::Log::SNIPPET_MARK: one character opens and closes every hit, so
45
- // the odd-numbered pieces of the split are the ones to mark.
46
- const MARK = "\u001f"
47
-
48
- // Long lines (structured events, stack dumps) are clamped to a few rows so
49
- // the table stays scannable, especially on phones where the message column
50
- // is narrow. Tapping the message toggles the full text. Short lines have
51
- // nothing to expand, so they are plain.
52
- const CLAMP_AT = 160
53
-
54
- function LogMessage({
55
- message,
56
- highlight,
57
- tags,
58
- }: {
59
- message: string
60
- highlight?: string
61
- tags: string[]
62
- }) {
63
- const [open, setOpen] = useState(false)
64
- const long = message.length > CLAMP_AT || message.includes("\n")
65
- const body = (
66
- <span
67
- className={cn(
68
- "font-mono text-xs break-all whitespace-pre-wrap",
69
- long && !open && "line-clamp-3",
70
- )}
71
- >
72
- {highlight ? <Snippet text={highlight} /> : message}
73
- {tags.length > 0 && (
74
- <span className="text-muted-foreground"> [{tags.join(", ")}]</span>
75
- )}
76
- </span>
77
- )
78
- if (!long) return body
79
- return (
80
- <button
81
- type="button"
82
- onClick={() => setOpen((o) => !o)}
83
- aria-expanded={open}
84
- className="block w-full cursor-pointer text-left"
85
- title={open ? "Collapse" : "Expand"}
86
- >
87
- {body}
88
- </button>
89
- )
90
- }
91
-
92
- function Snippet({ text }: { text: string }) {
93
- return (
94
- <>
95
- {text.split(MARK).map((part, i) =>
96
- i % 2 === 1 ? (
97
- <mark key={i} className="fts-mark">
98
- {part}
99
- </mark>
100
- ) : (
101
- <span key={i}>{part}</span>
102
- ),
103
- )}
104
- </>
105
- )
106
- }
107
-
108
- export default function Logs(p: Props) {
109
- const { environment, window, range } = usePage<SharedProps>().props
110
- const a = environment!.application_id
111
- const e = environment!.id
112
- const timeParams =
113
- window === "custom"
114
- ? { from: range?.from, to: range?.to }
115
- : { window: window }
116
- const cursorTimeParams = range
117
- ? { from: range.from, to: range.to }
118
- : timeParams
119
- const visit = (q: string) =>
120
- router.visit(
121
- R.applicationEnvironmentLogsPath(a, e, {
122
- ...timeParams,
123
- q: q || undefined,
124
- }),
125
- {
126
- only: ["logs", "pagination", "highlights", "counts", "q"],
127
- preserveState: true,
128
- preserveScroll: true,
129
- reset: ["logs", "highlights"],
130
- },
131
- )
132
- const logsPath = (cursor?: string) =>
133
- R.applicationEnvironmentLogsPath(a, e, {
134
- ...cursorTimeParams,
135
- q: p.q || undefined,
136
- cursor,
137
- limit: p.pagination.limit,
138
- })
139
- const currentLevel = parseFilter(p.q).fields.level
140
- const setLevel = (level: string | undefined) => {
141
- const parsed = parseFilter(p.q)
142
- const fields = { ...parsed.fields }
143
- if (level) fields.level = level
144
- else delete fields.level
145
- visit(serializeFilter({ text: parsed.text, fields }))
146
- }
147
- return (
148
- <EnvLayout title="Logs">
149
- <PageHeader
150
- title="Logs"
151
- description="Rails.logger lines and Rails.event structured events, linked to the request or job that wrote them."
152
- actions={<SavedViewsMenu page="logs" />}
153
- />
154
- <div className="-mx-3 [scrollbar-width:none] overflow-x-auto px-3 md:mx-0 md:px-0 [&::-webkit-scrollbar]:hidden">
155
- <Segmented className="w-max">
156
- <SegmentedItem
157
- active={!currentLevel}
158
- onClick={() => setLevel(undefined)}
159
- >
160
- All
161
- </SegmentedItem>
162
- {["debug", "info", "warn", "error", "fatal", "event"].map((l) => (
163
- <SegmentedItem
164
- key={l}
165
- active={currentLevel === l}
166
- onClick={() => setLevel(l)}
167
- className="gap-1.5"
168
- >
169
- {l}
170
- <span className="opacity-60">{count(p.counts[l] ?? 0)}</span>
171
- </SegmentedItem>
172
- ))}
173
- </Segmented>
174
- </div>
175
- <FilterBar
176
- value={p.q}
177
- fields={[
178
- { key: "after", label: "After" },
179
- { key: "before", label: "Before" },
180
- { key: "user", label: "User" },
181
- { key: "deploy", label: "Deploy" },
182
- {
183
- key: "level",
184
- label: "Level",
185
- options: [
186
- "debug",
187
- "info",
188
- "warn",
189
- "error",
190
- "fatal",
191
- "event",
192
- "unknown",
193
- ],
194
- },
195
- {
196
- key: "kind",
197
- label: "Execution kind",
198
- options: ["request", "job", "scheduled_task", "command"],
199
- },
200
- { key: "source", label: "Source" },
201
- { key: "tenant", label: "Tenant" },
202
- ]}
203
- onChange={visit}
204
- placeholder="level:error kind:job source:app/jobs/sync_job.rb:12"
205
- />
206
- <div className="text-muted-foreground flex flex-wrap items-baseline gap-x-3 gap-y-1 text-xs">
207
- <span className="font-mono tabular-nums">
208
- {count(p.logs.length)} {p.logs.length === 1 ? "line" : "lines"}
209
- </span>
210
- <span>
211
- Full-text search: quotes for phrases, -word to exclude, prefixes match
212
- </span>
213
- </div>
214
- <DataTable
215
- rows={p.logs}
216
- rowKey={(l) => l.id}
217
- empty={
218
- <EmptyState
219
- icon={FileText}
220
- title="No log lines match"
221
- signal="caution"
222
- description="Log lines are captured from your app's Rails logger."
223
- />
224
- }
225
- columns={[
226
- {
227
- key: "when",
228
- header: "When",
229
- className: "w-40",
230
- cell: (l) => (
231
- <span className="text-xs tabular-nums">
232
- {when(l.occurred_at)}
233
- </span>
234
- ),
235
- },
236
- {
237
- key: "lvl",
238
- header: "Level",
239
- cell: (l) => <LevelBadge level={l.level} />,
240
- },
241
- {
242
- key: "msg",
243
- header: "Message",
244
- grow: true,
245
- className: "whitespace-normal",
246
- cell: (l) => (
247
- <LogMessage
248
- message={l.message}
249
- highlight={p.highlights[l.id]}
250
- tags={l.tags}
251
- />
252
- ),
253
- },
254
- {
255
- key: "in",
256
- hideOnMobile: true,
257
- header: "In",
258
- cell: (l) =>
259
- l.execution_id ? (
260
- <Link
261
- className="font-mono text-xs hover:underline"
262
- href={R.applicationEnvironmentRequestPath(
263
- a,
264
- e,
265
- l.execution_id,
266
- )}
267
- >
268
- {l.execution_preview}
269
- </Link>
270
- ) : null,
271
- },
272
- {
273
- key: "t",
274
- hideOnMobile: true,
275
- header: "Tenant",
276
- cell: (l) =>
277
- l.tenant ? (
278
- <button
279
- className="font-mono text-xs hover:underline"
280
- onClick={() => {
281
- const parsed = parseFilter(p.q)
282
- visit(
283
- serializeFilter({
284
- text: parsed.text,
285
- fields: { ...parsed.fields, tenant: l.tenant! },
286
- }),
287
- )
288
- }}
289
- >
290
- {l.tenant}
291
- </button>
292
- ) : null,
293
- },
294
- ]}
295
- />
296
- <CursorLoadMore
297
- meta={p.pagination}
298
- href={logsPath}
299
- only={["logs", "pagination", "highlights"]}
300
- />
301
- </EnvLayout>
302
- )
303
- }
@@ -1,218 +0,0 @@
1
- import { Link, router, usePage } from "@inertiajs/react"
2
- import { Mail } from "lucide-react"
3
-
4
- import { VolumePanel } from "@/components/railwatch/chart-panel"
5
- import { DataTable } from "@/components/railwatch/data-table"
6
- import { EmptyState } from "@/components/railwatch/empty-state"
7
- import { FilterBar } from "@/components/railwatch/filter-bar"
8
- import { PageHeader } from "@/components/railwatch/page-header"
9
- import { PercentilePicker } from "@/components/railwatch/percentile-picker"
10
- import { SparklineCell } from "@/components/railwatch/sparkline-cell"
11
- import { Badge } from "@/components/ui/badge"
12
- import { usePercentile } from "@/hooks/use-percentile"
13
- import EnvLayout from "@/layouts/env-layout"
14
- import { count, ms, when } from "@/lib/format"
15
- import * as R from "@/routes"
16
- import type { GroupRow, SeriesPoint, SharedProps } from "@/types"
17
-
18
- interface MailRow {
19
- id: number
20
- mailer: string
21
- subject: string
22
- to: number
23
- cc: number
24
- bcc: number
25
- attachments: number
26
- delivery_method: string | null
27
- duration: number
28
- failed: boolean
29
- occurred_at: string
30
- execution_id: string | null
31
- execution_preview: string | null
32
- }
33
- interface Props {
34
- mailers: GroupRow[]
35
- series: SeriesPoint[]
36
- recent: MailRow[]
37
- q: string
38
- }
39
-
40
- export default function Mails(p: Props) {
41
- const { environment, window } = usePage<SharedProps>().props
42
- const { percentile } = usePercentile()
43
- const a = environment!.application_id
44
- const e = environment!.id
45
- const mailHref = (m: MailRow) =>
46
- m.execution_id
47
- ? R.applicationEnvironmentRequestPath(a, e, m.execution_id)
48
- : null
49
- return (
50
- <EnvLayout title="Mail">
51
- <PageHeader
52
- title="Mail"
53
- description="Action Mailer deliveries: recipients, delivery method, render and send time."
54
- actions={<PercentilePicker />}
55
- />
56
- <VolumePanel
57
- legend="outcome"
58
- label="Deliveries"
59
- seriesLabel="Deliveries"
60
- data={p.series}
61
- />
62
- <DataTable
63
- rows={p.mailers}
64
- rowKey={(m) => m.group_hash}
65
- empty={
66
- <EmptyState
67
- icon={Mail}
68
- title="No mail sent in this window"
69
- description="ActionMailer deliveries appear here as your app sends them."
70
- />
71
- }
72
- columns={[
73
- {
74
- key: "m",
75
- header: "Mailer",
76
- cell: (m) => <span className="font-mono text-xs">{m.name}</span>,
77
- },
78
- {
79
- key: "trend",
80
- hideOnMobile: true,
81
- header: "Trend",
82
- cell: (m) => <SparklineCell data={m.sparkline} />,
83
- },
84
- {
85
- key: "n",
86
- header: "Sent",
87
- align: "right",
88
- cell: (m) => count(m.count),
89
- },
90
- {
91
- key: "f",
92
- hideOnMobile: true,
93
- header: "Failed",
94
- align: "right",
95
- cell: (m) => m.errors,
96
- },
97
- {
98
- key: "avg",
99
- hideOnMobile: true,
100
- header: "Avg",
101
- align: "right",
102
- cell: (m) => ms(m.avg),
103
- },
104
- {
105
- key: percentile,
106
- header: percentile,
107
- align: "right",
108
- cell: (m) => (
109
- <span className="font-semibold">{ms(m[percentile])}</span>
110
- ),
111
- },
112
- ]}
113
- />
114
- <h2 className="text-sm font-semibold">Recent</h2>
115
- <FilterBar
116
- value={p.q}
117
- fields={[
118
- { key: "mailer", label: "Mailer" },
119
- { key: "kind", label: "Kind" },
120
- ]}
121
- onChange={(q) =>
122
- router.visit(
123
- R.applicationEnvironmentMailsPath(a, e, {
124
- window,
125
- q: q || undefined,
126
- }),
127
- { preserveState: true },
128
- )
129
- }
130
- placeholder="mailer:WidgetMailer kind:smtp"
131
- />
132
- <DataTable
133
- rows={p.recent}
134
- rowKey={(m) => m.id}
135
- empty={
136
- <EmptyState
137
- icon={Mail}
138
- title="No mail sent in this window"
139
- description="ActionMailer deliveries appear here as your app sends them."
140
- />
141
- }
142
- keyboardNav={{
143
- onOpen: (m, opts) => {
144
- const href = mailHref(m)
145
- if (!href) return
146
- if (opts?.newTab) globalThis.window.open(href, "_blank")
147
- else router.visit(href)
148
- },
149
- }}
150
- columns={[
151
- {
152
- key: "when",
153
- header: "When",
154
- cell: (m) => <span className="text-xs">{when(m.occurred_at)}</span>,
155
- },
156
- {
157
- key: "m",
158
- header: "Mailer",
159
- cell: (m) => <span className="font-mono text-xs">{m.mailer}</span>,
160
- },
161
- {
162
- key: "s",
163
- header: "Subject",
164
- cell: (m) => (
165
- <span className="line-clamp-1 text-xs">{m.subject}</span>
166
- ),
167
- },
168
- {
169
- key: "to",
170
- hideOnMobile: true,
171
- header: "To/CC/BCC",
172
- align: "right",
173
- cell: (m) => `${m.to}/${m.cc}/${m.bcc}`,
174
- },
175
- {
176
- key: "att",
177
- hideOnMobile: true,
178
- header: "Att.",
179
- align: "right",
180
- cell: (m) => m.attachments,
181
- },
182
- {
183
- key: "dm",
184
- hideOnMobile: true,
185
- header: "Via",
186
- cell: (m) => m.delivery_method,
187
- },
188
- {
189
- key: "st",
190
- header: "",
191
- cell: (m) =>
192
- m.failed ? <Badge variant="destructive">failed</Badge> : null,
193
- },
194
- {
195
- key: "d",
196
- hideOnMobile: true,
197
- header: "Duration",
198
- align: "right",
199
- cell: (m) => ms(m.duration),
200
- },
201
- {
202
- key: "in",
203
- hideOnMobile: true,
204
- header: "In",
205
- cell: (m) => {
206
- const href = mailHref(m)
207
- return href ? (
208
- <Link className="font-mono text-xs hover:underline" href={href}>
209
- {m.execution_preview}
210
- </Link>
211
- ) : null
212
- },
213
- },
214
- ]}
215
- />
216
- </EnvLayout>
217
- )
218
- }
@@ -1,161 +0,0 @@
1
- import { Link, router, usePage } from "@inertiajs/react"
2
- import { BellRing } from "lucide-react"
3
-
4
- import { DataTable } from "@/components/railwatch/data-table"
5
- import { EmptyState } from "@/components/railwatch/empty-state"
6
- import { PageHeader } from "@/components/railwatch/page-header"
7
- import EnvLayout from "@/layouts/env-layout"
8
- import { count, ms, when } from "@/lib/format"
9
- import * as R from "@/routes"
10
- import type { SharedProps } from "@/types"
11
-
12
- interface Group {
13
- group_hash: string
14
- name: string
15
- count: number
16
- errors: number
17
- avg: number
18
- p95: number
19
- }
20
- interface Recent {
21
- id: number
22
- notifier: string | null
23
- delivery_method: string | null
24
- duration: number
25
- failed: boolean
26
- occurred_at: string
27
- execution_id: string | null
28
- execution_preview: string | null
29
- }
30
- interface Props {
31
- groups: Group[]
32
- recent: Recent[]
33
- }
34
-
35
- export default function Notifications(p: Props) {
36
- const { environment } = usePage<SharedProps>().props
37
- const a = environment!.application_id
38
- const e = environment!.id
39
- const recentHref = (r: Recent) =>
40
- r.execution_id
41
- ? R.applicationEnvironmentRequestPath(a, e, r.execution_id)
42
- : null
43
- return (
44
- <EnvLayout title="Notifications">
45
- <PageHeader
46
- title="Notifications"
47
- description="ActionMailer/Noticed deliveries, grouped by notifier and delivery method."
48
- />
49
- <DataTable
50
- rows={p.groups}
51
- rowKey={(g) => g.group_hash}
52
- empty={
53
- <EmptyState
54
- icon={BellRing}
55
- title="No notifications in this window"
56
- description="Notifications delivered by your app appear here as they're sent."
57
- />
58
- }
59
- columns={[
60
- {
61
- key: "n",
62
- header: "Notifier",
63
- cell: (g) => <span className="font-mono text-xs">{g.name}</span>,
64
- },
65
- {
66
- key: "c",
67
- hideOnMobile: true,
68
- header: "Count",
69
- align: "right",
70
- cell: (g) => count(g.count),
71
- },
72
- {
73
- key: "f",
74
- hideOnMobile: true,
75
- header: "Failed",
76
- align: "right",
77
- cell: (g) => count(g.errors),
78
- },
79
- {
80
- key: "avg",
81
- hideOnMobile: true,
82
- header: "Avg",
83
- align: "right",
84
- cell: (g) => ms(g.avg, 3),
85
- },
86
- {
87
- key: "p95",
88
- header: "p95",
89
- align: "right",
90
- cell: (g) => ms(g.p95, 3),
91
- },
92
- ]}
93
- />
94
- <h2 className="text-sm font-semibold">Recent</h2>
95
- <DataTable
96
- rows={p.recent}
97
- rowKey={(r) => r.id}
98
- empty={
99
- <EmptyState
100
- icon={BellRing}
101
- title="No notifications in this window"
102
- description="Notifications delivered by your app appear here as they're sent."
103
- />
104
- }
105
- keyboardNav={{
106
- onOpen: (r, opts) => {
107
- const href = recentHref(r)
108
- if (!href) return
109
- if (opts?.newTab) window.open(href, "_blank")
110
- else router.visit(href)
111
- },
112
- }}
113
- columns={[
114
- {
115
- key: "when",
116
- header: "When",
117
- cell: (r) => <span className="text-xs">{when(r.occurred_at)}</span>,
118
- },
119
- {
120
- key: "n",
121
- header: "Notifier",
122
- cell: (r) => (
123
- <span className="font-mono text-xs">{r.notifier}</span>
124
- ),
125
- },
126
- {
127
- key: "dm",
128
- hideOnMobile: true,
129
- header: "Delivery method",
130
- cell: (r) => r.delivery_method,
131
- },
132
- {
133
- key: "f",
134
- hideOnMobile: true,
135
- header: "Failed",
136
- cell: (r) => (r.failed ? "yes" : ""),
137
- },
138
- {
139
- key: "d",
140
- header: "Duration",
141
- align: "right",
142
- cell: (r) => ms(r.duration, 3),
143
- },
144
- {
145
- key: "in",
146
- hideOnMobile: true,
147
- header: "In",
148
- cell: (r) => {
149
- const href = recentHref(r)
150
- return href ? (
151
- <Link className="font-mono text-xs hover:underline" href={href}>
152
- {r.execution_preview}
153
- </Link>
154
- ) : null
155
- },
156
- },
157
- ]}
158
- />
159
- </EnvLayout>
160
- )
161
- }