@meyicloud/meyi-insight-server 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 (389) hide show
  1. package/dist/constants/incidentCategories.d.ts +18 -0
  2. package/dist/constants/incidentCategories.d.ts.map +1 -0
  3. package/dist/constants/incidentCategories.js +36 -0
  4. package/dist/constants/incidentCategories.js.map +1 -0
  5. package/dist/controllers/alert.controller.d.ts +12 -0
  6. package/dist/controllers/alert.controller.d.ts.map +1 -0
  7. package/dist/controllers/alert.controller.js +89 -0
  8. package/dist/controllers/alert.controller.js.map +1 -0
  9. package/dist/controllers/incident.controller.d.ts +16 -0
  10. package/dist/controllers/incident.controller.d.ts.map +1 -0
  11. package/dist/controllers/incident.controller.js +215 -0
  12. package/dist/controllers/incident.controller.js.map +1 -0
  13. package/dist/controllers/notification-provider.controller.d.ts +10 -0
  14. package/dist/controllers/notification-provider.controller.d.ts.map +1 -0
  15. package/dist/controllers/notification-provider.controller.js +59 -0
  16. package/dist/controllers/notification-provider.controller.js.map +1 -0
  17. package/dist/controllers/settings.controller.d.ts +7 -0
  18. package/dist/controllers/settings.controller.d.ts.map +1 -0
  19. package/dist/controllers/settings.controller.js +27 -0
  20. package/dist/controllers/settings.controller.js.map +1 -0
  21. package/dist/controllers/sop.controller.d.ts +11 -0
  22. package/dist/controllers/sop.controller.d.ts.map +1 -0
  23. package/dist/controllers/sop.controller.js +89 -0
  24. package/dist/controllers/sop.controller.js.map +1 -0
  25. package/dist/controllers/source.controller.d.ts +26 -0
  26. package/dist/controllers/source.controller.d.ts.map +1 -0
  27. package/dist/controllers/source.controller.js +333 -0
  28. package/dist/controllers/source.controller.js.map +1 -0
  29. package/dist/controllers/sourceDatadog.controller.d.ts +17 -0
  30. package/dist/controllers/sourceDatadog.controller.d.ts.map +1 -0
  31. package/dist/controllers/sourceDatadog.controller.js +169 -0
  32. package/dist/controllers/sourceDatadog.controller.js.map +1 -0
  33. package/dist/controllers/tenant.controller.d.ts +7 -0
  34. package/dist/controllers/tenant.controller.d.ts.map +1 -0
  35. package/dist/controllers/tenant.controller.js +33 -0
  36. package/dist/controllers/tenant.controller.js.map +1 -0
  37. package/dist/controllers/webhook.controller.d.ts +6 -0
  38. package/dist/controllers/webhook.controller.d.ts.map +1 -0
  39. package/dist/controllers/webhook.controller.js +83 -0
  40. package/dist/controllers/webhook.controller.js.map +1 -0
  41. package/dist/controllers/workflow.controller.d.ts +18 -0
  42. package/dist/controllers/workflow.controller.d.ts.map +1 -0
  43. package/dist/controllers/workflow.controller.js +118 -0
  44. package/dist/controllers/workflow.controller.js.map +1 -0
  45. package/dist/database/index.d.ts +7 -0
  46. package/dist/database/index.d.ts.map +1 -0
  47. package/dist/database/index.js +12 -0
  48. package/dist/database/index.js.map +1 -0
  49. package/dist/database/schema.d.ts +5358 -0
  50. package/dist/database/schema.d.ts.map +1 -0
  51. package/dist/database/schema.js +516 -0
  52. package/dist/database/schema.js.map +1 -0
  53. package/dist/features/sources/cloudtrail/controller.d.ts +38 -0
  54. package/dist/features/sources/cloudtrail/controller.d.ts.map +1 -0
  55. package/dist/features/sources/cloudtrail/controller.js +421 -0
  56. package/dist/features/sources/cloudtrail/controller.js.map +1 -0
  57. package/dist/features/sources/cloudtrail/routes.d.ts +6 -0
  58. package/dist/features/sources/cloudtrail/routes.d.ts.map +1 -0
  59. package/dist/features/sources/cloudtrail/routes.js +8 -0
  60. package/dist/features/sources/cloudtrail/routes.js.map +1 -0
  61. package/dist/features/sources/cloudtrail/schema.d.ts +2 -0
  62. package/dist/features/sources/cloudtrail/schema.d.ts.map +1 -0
  63. package/dist/features/sources/cloudtrail/schema.js +26 -0
  64. package/dist/features/sources/cloudtrail/schema.js.map +1 -0
  65. package/dist/features/sources/cloudtrail/stackTemplates.d.ts +3 -0
  66. package/dist/features/sources/cloudtrail/stackTemplates.d.ts.map +1 -0
  67. package/dist/features/sources/cloudtrail/stackTemplates.js +548 -0
  68. package/dist/features/sources/cloudtrail/stackTemplates.js.map +1 -0
  69. package/dist/features/sources/cloudtrail/template.d.ts +21 -0
  70. package/dist/features/sources/cloudtrail/template.d.ts.map +1 -0
  71. package/dist/features/sources/cloudtrail/template.js +1230 -0
  72. package/dist/features/sources/cloudtrail/template.js.map +1 -0
  73. package/dist/features/sources/cloudwatch/controller.d.ts +13 -0
  74. package/dist/features/sources/cloudwatch/controller.d.ts.map +1 -0
  75. package/dist/features/sources/cloudwatch/controller.js +514 -0
  76. package/dist/features/sources/cloudwatch/controller.js.map +1 -0
  77. package/dist/features/sources/cloudwatch/eventbridgeTemplate.d.ts +7 -0
  78. package/dist/features/sources/cloudwatch/eventbridgeTemplate.d.ts.map +1 -0
  79. package/dist/features/sources/cloudwatch/eventbridgeTemplate.js +230 -0
  80. package/dist/features/sources/cloudwatch/eventbridgeTemplate.js.map +1 -0
  81. package/dist/features/sources/cloudwatch/service.d.ts +50 -0
  82. package/dist/features/sources/cloudwatch/service.d.ts.map +1 -0
  83. package/dist/features/sources/cloudwatch/service.js +269 -0
  84. package/dist/features/sources/cloudwatch/service.js.map +1 -0
  85. package/dist/features/sources/cloudwatch/template.d.ts +26 -0
  86. package/dist/features/sources/cloudwatch/template.d.ts.map +1 -0
  87. package/dist/features/sources/cloudwatch/template.js +335 -0
  88. package/dist/features/sources/cloudwatch/template.js.map +1 -0
  89. package/dist/features/sources/cloudwatch/types.d.ts +26 -0
  90. package/dist/features/sources/cloudwatch/types.d.ts.map +1 -0
  91. package/dist/features/sources/cloudwatch/types.js +2 -0
  92. package/dist/features/sources/cloudwatch/types.js.map +1 -0
  93. package/dist/features/sources/grafana/controller.d.ts +18 -0
  94. package/dist/features/sources/grafana/controller.d.ts.map +1 -0
  95. package/dist/features/sources/grafana/controller.js +183 -0
  96. package/dist/features/sources/grafana/controller.js.map +1 -0
  97. package/dist/features/sources/grafana/routes.d.ts +6 -0
  98. package/dist/features/sources/grafana/routes.d.ts.map +1 -0
  99. package/dist/features/sources/grafana/routes.js +6 -0
  100. package/dist/features/sources/grafana/routes.js.map +1 -0
  101. package/dist/features/sources/grafana/schema.d.ts +2 -0
  102. package/dist/features/sources/grafana/schema.d.ts.map +1 -0
  103. package/dist/features/sources/grafana/schema.js +24 -0
  104. package/dist/features/sources/grafana/schema.js.map +1 -0
  105. package/dist/features/sources/grafana/service.d.ts +33 -0
  106. package/dist/features/sources/grafana/service.d.ts.map +1 -0
  107. package/dist/features/sources/grafana/service.js +215 -0
  108. package/dist/features/sources/grafana/service.js.map +1 -0
  109. package/dist/features/sources/index.d.ts +29 -0
  110. package/dist/features/sources/index.d.ts.map +1 -0
  111. package/dist/features/sources/index.js +47 -0
  112. package/dist/features/sources/index.js.map +1 -0
  113. package/dist/features/sources/observability/controller.d.ts +6 -0
  114. package/dist/features/sources/observability/controller.d.ts.map +1 -0
  115. package/dist/features/sources/observability/controller.js +16 -0
  116. package/dist/features/sources/observability/controller.js.map +1 -0
  117. package/dist/features/sources/observability/routes.d.ts +4 -0
  118. package/dist/features/sources/observability/routes.d.ts.map +1 -0
  119. package/dist/features/sources/observability/routes.js +8 -0
  120. package/dist/features/sources/observability/routes.js.map +1 -0
  121. package/dist/features/sources/observability/schema.d.ts +3 -0
  122. package/dist/features/sources/observability/schema.d.ts.map +1 -0
  123. package/dist/features/sources/observability/schema.js +44 -0
  124. package/dist/features/sources/observability/schema.js.map +1 -0
  125. package/dist/features/sources/observability/service.d.ts +36 -0
  126. package/dist/features/sources/observability/service.d.ts.map +1 -0
  127. package/dist/features/sources/observability/service.js +127 -0
  128. package/dist/features/sources/observability/service.js.map +1 -0
  129. package/dist/features/sources/prometheus/controller.d.ts +9 -0
  130. package/dist/features/sources/prometheus/controller.d.ts.map +1 -0
  131. package/dist/features/sources/prometheus/controller.js +359 -0
  132. package/dist/features/sources/prometheus/controller.js.map +1 -0
  133. package/dist/features/sources/prometheus/templates.d.ts +18 -0
  134. package/dist/features/sources/prometheus/templates.d.ts.map +1 -0
  135. package/dist/features/sources/prometheus/templates.js +244 -0
  136. package/dist/features/sources/prometheus/templates.js.map +1 -0
  137. package/dist/index.d.ts +7 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +7 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/migrations/0007_pipeline_yaml_definitions.d.ts +3 -0
  142. package/dist/migrations/0007_pipeline_yaml_definitions.d.ts.map +1 -0
  143. package/dist/migrations/0007_pipeline_yaml_definitions.js +19 -0
  144. package/dist/migrations/0007_pipeline_yaml_definitions.js.map +1 -0
  145. package/dist/migrations/0008_tenant_isolation.d.ts +3 -0
  146. package/dist/migrations/0008_tenant_isolation.d.ts.map +1 -0
  147. package/dist/migrations/0008_tenant_isolation.js +42 -0
  148. package/dist/migrations/0008_tenant_isolation.js.map +1 -0
  149. package/dist/migrations/run.d.ts +6 -0
  150. package/dist/migrations/run.d.ts.map +1 -0
  151. package/dist/migrations/run.js +1262 -0
  152. package/dist/migrations/run.js.map +1 -0
  153. package/dist/models/alarm-group.model.d.ts +42 -0
  154. package/dist/models/alarm-group.model.d.ts.map +1 -0
  155. package/dist/models/alarm-group.model.js +98 -0
  156. package/dist/models/alarm-group.model.js.map +1 -0
  157. package/dist/models/alert.model.d.ts +54 -0
  158. package/dist/models/alert.model.d.ts.map +1 -0
  159. package/dist/models/alert.model.js +119 -0
  160. package/dist/models/alert.model.js.map +1 -0
  161. package/dist/models/dedup-config.model.d.ts +53 -0
  162. package/dist/models/dedup-config.model.d.ts.map +1 -0
  163. package/dist/models/dedup-config.model.js +157 -0
  164. package/dist/models/dedup-config.model.js.map +1 -0
  165. package/dist/models/grouping-config.model.d.ts +70 -0
  166. package/dist/models/grouping-config.model.d.ts.map +1 -0
  167. package/dist/models/grouping-config.model.js +174 -0
  168. package/dist/models/grouping-config.model.js.map +1 -0
  169. package/dist/models/incident-comment.model.d.ts +14 -0
  170. package/dist/models/incident-comment.model.d.ts.map +1 -0
  171. package/dist/models/incident-comment.model.js +38 -0
  172. package/dist/models/incident-comment.model.js.map +1 -0
  173. package/dist/models/incident-event.model.d.ts +16 -0
  174. package/dist/models/incident-event.model.d.ts.map +1 -0
  175. package/dist/models/incident-event.model.js +31 -0
  176. package/dist/models/incident-event.model.js.map +1 -0
  177. package/dist/models/incident.model.d.ts +47 -0
  178. package/dist/models/incident.model.d.ts.map +1 -0
  179. package/dist/models/incident.model.js +201 -0
  180. package/dist/models/incident.model.js.map +1 -0
  181. package/dist/models/normalization-config.model.d.ts +44 -0
  182. package/dist/models/normalization-config.model.d.ts.map +1 -0
  183. package/dist/models/normalization-config.model.js +153 -0
  184. package/dist/models/normalization-config.model.js.map +1 -0
  185. package/dist/models/notification-provider.model.d.ts +12 -0
  186. package/dist/models/notification-provider.model.d.ts.map +1 -0
  187. package/dist/models/notification-provider.model.js +48 -0
  188. package/dist/models/notification-provider.model.js.map +1 -0
  189. package/dist/models/pipeline-event.model.d.ts +15 -0
  190. package/dist/models/pipeline-event.model.d.ts.map +1 -0
  191. package/dist/models/pipeline-event.model.js +32 -0
  192. package/dist/models/pipeline-event.model.js.map +1 -0
  193. package/dist/models/settings.model.d.ts +8 -0
  194. package/dist/models/settings.model.d.ts.map +1 -0
  195. package/dist/models/settings.model.js +30 -0
  196. package/dist/models/settings.model.js.map +1 -0
  197. package/dist/models/sop.model.d.ts +58 -0
  198. package/dist/models/sop.model.d.ts.map +1 -0
  199. package/dist/models/sop.model.js +138 -0
  200. package/dist/models/sop.model.js.map +1 -0
  201. package/dist/models/source.model.d.ts +177 -0
  202. package/dist/models/source.model.d.ts.map +1 -0
  203. package/dist/models/source.model.js +794 -0
  204. package/dist/models/source.model.js.map +1 -0
  205. package/dist/models/tenant.model.d.ts +13 -0
  206. package/dist/models/tenant.model.d.ts.map +1 -0
  207. package/dist/models/tenant.model.js +28 -0
  208. package/dist/models/tenant.model.js.map +1 -0
  209. package/dist/models/workflow.model.d.ts +36 -0
  210. package/dist/models/workflow.model.d.ts.map +1 -0
  211. package/dist/models/workflow.model.js +136 -0
  212. package/dist/models/workflow.model.js.map +1 -0
  213. package/dist/pipeline/AlarmPipelineWorker.d.ts +18 -0
  214. package/dist/pipeline/AlarmPipelineWorker.d.ts.map +1 -0
  215. package/dist/pipeline/AlarmPipelineWorker.js +342 -0
  216. package/dist/pipeline/AlarmPipelineWorker.js.map +1 -0
  217. package/dist/pipeline/CanonicalAlarm.d.ts +59 -0
  218. package/dist/pipeline/CanonicalAlarm.d.ts.map +1 -0
  219. package/dist/pipeline/CanonicalAlarm.js +81 -0
  220. package/dist/pipeline/CanonicalAlarm.js.map +1 -0
  221. package/dist/pipeline/DedupService.d.ts +29 -0
  222. package/dist/pipeline/DedupService.d.ts.map +1 -0
  223. package/dist/pipeline/DedupService.js +172 -0
  224. package/dist/pipeline/DedupService.js.map +1 -0
  225. package/dist/pipeline/GroupFlushWorker.d.ts +16 -0
  226. package/dist/pipeline/GroupFlushWorker.d.ts.map +1 -0
  227. package/dist/pipeline/GroupFlushWorker.js +196 -0
  228. package/dist/pipeline/GroupFlushWorker.js.map +1 -0
  229. package/dist/pipeline/GroupingService.d.ts +27 -0
  230. package/dist/pipeline/GroupingService.d.ts.map +1 -0
  231. package/dist/pipeline/GroupingService.js +338 -0
  232. package/dist/pipeline/GroupingService.js.map +1 -0
  233. package/dist/pipeline/NormalizationRuleEngine.d.ts +5 -0
  234. package/dist/pipeline/NormalizationRuleEngine.d.ts.map +1 -0
  235. package/dist/pipeline/NormalizationRuleEngine.js +86 -0
  236. package/dist/pipeline/NormalizationRuleEngine.js.map +1 -0
  237. package/dist/pipeline/NormalizationService.d.ts +29 -0
  238. package/dist/pipeline/NormalizationService.d.ts.map +1 -0
  239. package/dist/pipeline/NormalizationService.js +275 -0
  240. package/dist/pipeline/NormalizationService.js.map +1 -0
  241. package/dist/pipeline/alarmQueue.d.ts +66 -0
  242. package/dist/pipeline/alarmQueue.d.ts.map +1 -0
  243. package/dist/pipeline/alarmQueue.js +254 -0
  244. package/dist/pipeline/alarmQueue.js.map +1 -0
  245. package/dist/pipeline/defaults/ensureDefaults.d.ts +3 -0
  246. package/dist/pipeline/defaults/ensureDefaults.d.ts.map +1 -0
  247. package/dist/pipeline/defaults/ensureDefaults.js +124 -0
  248. package/dist/pipeline/defaults/ensureDefaults.js.map +1 -0
  249. package/dist/pipeline/defaults/pipelineDefaults.d.ts +13 -0
  250. package/dist/pipeline/defaults/pipelineDefaults.d.ts.map +1 -0
  251. package/dist/pipeline/defaults/pipelineDefaults.js +266 -0
  252. package/dist/pipeline/defaults/pipelineDefaults.js.map +1 -0
  253. package/dist/pipeline/yaml/DedupYaml.d.ts +33 -0
  254. package/dist/pipeline/yaml/DedupYaml.d.ts.map +1 -0
  255. package/dist/pipeline/yaml/DedupYaml.js +25 -0
  256. package/dist/pipeline/yaml/DedupYaml.js.map +1 -0
  257. package/dist/pipeline/yaml/GroupingYaml.d.ts +49 -0
  258. package/dist/pipeline/yaml/GroupingYaml.d.ts.map +1 -0
  259. package/dist/pipeline/yaml/GroupingYaml.js +39 -0
  260. package/dist/pipeline/yaml/GroupingYaml.js.map +1 -0
  261. package/dist/pipeline/yaml/NormalizationYaml.d.ts +54 -0
  262. package/dist/pipeline/yaml/NormalizationYaml.d.ts.map +1 -0
  263. package/dist/pipeline/yaml/NormalizationYaml.js +23 -0
  264. package/dist/pipeline/yaml/NormalizationYaml.js.map +1 -0
  265. package/dist/pipeline/yaml/pipelineYaml.d.ts +11 -0
  266. package/dist/pipeline/yaml/pipelineYaml.d.ts.map +1 -0
  267. package/dist/pipeline/yaml/pipelineYaml.js +49 -0
  268. package/dist/pipeline/yaml/pipelineYaml.js.map +1 -0
  269. package/dist/realtime/events.d.ts +11 -0
  270. package/dist/realtime/events.d.ts.map +1 -0
  271. package/dist/realtime/events.js +21 -0
  272. package/dist/realtime/events.js.map +1 -0
  273. package/dist/routes/alert.routes.d.ts +4 -0
  274. package/dist/routes/alert.routes.d.ts.map +1 -0
  275. package/dist/routes/alert.routes.js +15 -0
  276. package/dist/routes/alert.routes.js.map +1 -0
  277. package/dist/routes/dedup-config.routes.d.ts +4 -0
  278. package/dist/routes/dedup-config.routes.d.ts.map +1 -0
  279. package/dist/routes/dedup-config.routes.js +82 -0
  280. package/dist/routes/dedup-config.routes.js.map +1 -0
  281. package/dist/routes/grouping-config.routes.d.ts +4 -0
  282. package/dist/routes/grouping-config.routes.d.ts.map +1 -0
  283. package/dist/routes/grouping-config.routes.js +82 -0
  284. package/dist/routes/grouping-config.routes.js.map +1 -0
  285. package/dist/routes/incident.routes.d.ts +4 -0
  286. package/dist/routes/incident.routes.d.ts.map +1 -0
  287. package/dist/routes/incident.routes.js +21 -0
  288. package/dist/routes/incident.routes.js.map +1 -0
  289. package/dist/routes/index.d.ts +47 -0
  290. package/dist/routes/index.d.ts.map +1 -0
  291. package/dist/routes/index.js +83 -0
  292. package/dist/routes/index.js.map +1 -0
  293. package/dist/routes/normalization-config.routes.d.ts +4 -0
  294. package/dist/routes/normalization-config.routes.d.ts.map +1 -0
  295. package/dist/routes/normalization-config.routes.js +199 -0
  296. package/dist/routes/normalization-config.routes.js.map +1 -0
  297. package/dist/routes/notification-provider.routes.d.ts +4 -0
  298. package/dist/routes/notification-provider.routes.d.ts.map +1 -0
  299. package/dist/routes/notification-provider.routes.js +13 -0
  300. package/dist/routes/notification-provider.routes.js.map +1 -0
  301. package/dist/routes/pipeline.routes.d.ts +8 -0
  302. package/dist/routes/pipeline.routes.d.ts.map +1 -0
  303. package/dist/routes/pipeline.routes.js +243 -0
  304. package/dist/routes/pipeline.routes.js.map +1 -0
  305. package/dist/routes/settings.routes.d.ts +4 -0
  306. package/dist/routes/settings.routes.d.ts.map +1 -0
  307. package/dist/routes/settings.routes.js +10 -0
  308. package/dist/routes/settings.routes.js.map +1 -0
  309. package/dist/routes/sop.routes.d.ts +4 -0
  310. package/dist/routes/sop.routes.d.ts.map +1 -0
  311. package/dist/routes/sop.routes.js +14 -0
  312. package/dist/routes/sop.routes.js.map +1 -0
  313. package/dist/routes/source.routes.d.ts +9 -0
  314. package/dist/routes/source.routes.d.ts.map +1 -0
  315. package/dist/routes/source.routes.js +31 -0
  316. package/dist/routes/source.routes.js.map +1 -0
  317. package/dist/routes/tenant.routes.d.ts +4 -0
  318. package/dist/routes/tenant.routes.d.ts.map +1 -0
  319. package/dist/routes/tenant.routes.js +10 -0
  320. package/dist/routes/tenant.routes.js.map +1 -0
  321. package/dist/routes/workflow.routes.d.ts +4 -0
  322. package/dist/routes/workflow.routes.d.ts.map +1 -0
  323. package/dist/routes/workflow.routes.js +18 -0
  324. package/dist/routes/workflow.routes.js.map +1 -0
  325. package/dist/server.d.ts +22 -0
  326. package/dist/server.d.ts.map +1 -0
  327. package/dist/server.js +76 -0
  328. package/dist/server.js.map +1 -0
  329. package/dist/service.d.ts +159 -0
  330. package/dist/service.d.ts.map +1 -0
  331. package/dist/service.js +156 -0
  332. package/dist/service.js.map +1 -0
  333. package/dist/services/alert.service.d.ts +76 -0
  334. package/dist/services/alert.service.d.ts.map +1 -0
  335. package/dist/services/alert.service.js +688 -0
  336. package/dist/services/alert.service.js.map +1 -0
  337. package/dist/services/connector.service.d.ts +65 -0
  338. package/dist/services/connector.service.d.ts.map +1 -0
  339. package/dist/services/connector.service.js +28 -0
  340. package/dist/services/connector.service.js.map +1 -0
  341. package/dist/services/incident.service.d.ts +108 -0
  342. package/dist/services/incident.service.d.ts.map +1 -0
  343. package/dist/services/incident.service.js +325 -0
  344. package/dist/services/incident.service.js.map +1 -0
  345. package/dist/services/notification-provider.service.d.ts +12 -0
  346. package/dist/services/notification-provider.service.d.ts.map +1 -0
  347. package/dist/services/notification-provider.service.js +25 -0
  348. package/dist/services/notification-provider.service.js.map +1 -0
  349. package/dist/services/settings.service.d.ts +8 -0
  350. package/dist/services/settings.service.d.ts.map +1 -0
  351. package/dist/services/settings.service.js +14 -0
  352. package/dist/services/settings.service.js.map +1 -0
  353. package/dist/services/sop.service.d.ts +60 -0
  354. package/dist/services/sop.service.d.ts.map +1 -0
  355. package/dist/services/sop.service.js +66 -0
  356. package/dist/services/sop.service.js.map +1 -0
  357. package/dist/services/source.service.d.ts +162 -0
  358. package/dist/services/source.service.d.ts.map +1 -0
  359. package/dist/services/source.service.js +124 -0
  360. package/dist/services/source.service.js.map +1 -0
  361. package/dist/services/tenant.service.d.ts +11 -0
  362. package/dist/services/tenant.service.d.ts.map +1 -0
  363. package/dist/services/tenant.service.js +22 -0
  364. package/dist/services/tenant.service.js.map +1 -0
  365. package/dist/services/workflow.service.d.ts +32 -0
  366. package/dist/services/workflow.service.d.ts.map +1 -0
  367. package/dist/services/workflow.service.js +291 -0
  368. package/dist/services/workflow.service.js.map +1 -0
  369. package/dist/standalone.d.ts +2 -0
  370. package/dist/standalone.d.ts.map +1 -0
  371. package/dist/standalone.js +55 -0
  372. package/dist/standalone.js.map +1 -0
  373. package/dist/tenancy.d.ts +5 -0
  374. package/dist/tenancy.d.ts.map +1 -0
  375. package/dist/tenancy.js +35 -0
  376. package/dist/tenancy.js.map +1 -0
  377. package/dist/utils/notificationTemplates.d.ts +98 -0
  378. package/dist/utils/notificationTemplates.d.ts.map +1 -0
  379. package/dist/utils/notificationTemplates.js +227 -0
  380. package/dist/utils/notificationTemplates.js.map +1 -0
  381. package/dist/utils/secretCrypto.d.ts +3 -0
  382. package/dist/utils/secretCrypto.d.ts.map +1 -0
  383. package/dist/utils/secretCrypto.js +40 -0
  384. package/dist/utils/secretCrypto.js.map +1 -0
  385. package/dist/utils/workflowYaml.d.ts +53 -0
  386. package/dist/utils/workflowYaml.d.ts.map +1 -0
  387. package/dist/utils/workflowYaml.js +269 -0
  388. package/dist/utils/workflowYaml.js.map +1 -0
  389. package/package.json +73 -0
@@ -0,0 +1,1262 @@
1
+ import { sql } from "drizzle-orm";
2
+ import yaml from "js-yaml";
3
+ /**
4
+ * Run all DDL migrations for the Insight plugin.
5
+ * Safe to run on every start - all statements use IF NOT EXISTS / IF EXISTS guards.
6
+ */
7
+ export async function runMigrations(db) {
8
+ const defaultTenantId = (process.env.INSIGHT_DEFAULT_TENANT_ID || "default").trim() || "default";
9
+ const defaultTenantSql = defaultTenantId.replace(/'/g, "''");
10
+ const defaultTokenTtlDaysRaw = Number.parseInt(process.env.INSIGHT_WEBHOOK_TOKEN_DEFAULT_DAYS || "30", 10);
11
+ const defaultTokenTtlDays = Number.isFinite(defaultTokenTtlDaysRaw) && defaultTokenTtlDaysRaw > 0
12
+ ? defaultTokenTtlDaysRaw
13
+ : 30;
14
+ // Rename tables and columns if they exist from the old 'connector' naming convention
15
+ await db.execute(sql.raw(`
16
+ DO $$
17
+ BEGIN
18
+ -- Rename main table
19
+ IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_connectors') THEN
20
+ IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_sources') THEN
21
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_connectors" RENAME TO "insight_sources"';
22
+ END IF;
23
+ END IF;
24
+
25
+ -- Rename CloudWatch setups
26
+ IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_cloudwatch_connector_setups') THEN
27
+ IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_cloudwatch_source_setups') THEN
28
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_cloudwatch_connector_setups" RENAME TO "insight_cloudwatch_source_setups"';
29
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_cloudwatch_source_setups" RENAME COLUMN "connector_id" TO "source_id"';
30
+ END IF;
31
+ END IF;
32
+
33
+ -- Rename CloudTrail setups
34
+ IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_cloudtrail_connector_setups') THEN
35
+ IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_cloudtrail_source_setups') THEN
36
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_cloudtrail_connector_setups" RENAME TO "insight_cloudtrail_source_setups"';
37
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_cloudtrail_source_setups" RENAME COLUMN "connector_id" TO "source_id"';
38
+ END IF;
39
+ END IF;
40
+
41
+ -- Rename Grafana setups
42
+ IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_grafana_connector_setups') THEN
43
+ IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_grafana_source_setups') THEN
44
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_grafana_connector_setups" RENAME TO "insight_grafana_source_setups"';
45
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_grafana_source_setups" RENAME COLUMN "connector_id" TO "source_id"';
46
+ END IF;
47
+ END IF;
48
+
49
+ -- Rename Prometheus setups
50
+ IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_prometheus_connector_setups') THEN
51
+ IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_prometheus_source_setups') THEN
52
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_prometheus_connector_setups" RENAME TO "insight_prometheus_source_setups"';
53
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_prometheus_source_setups" RENAME COLUMN "connector_id" TO "source_id"';
54
+ END IF;
55
+ END IF;
56
+
57
+ -- Rename Connector Health
58
+ IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_connector_health') THEN
59
+ IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'meyiconnect' AND table_name = 'insight_source_health') THEN
60
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_connector_health" RENAME TO "insight_source_health"';
61
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_source_health" RENAME COLUMN "connector_id" TO "source_id"';
62
+ END IF;
63
+ END IF;
64
+
65
+ -- Rename Webhook Failure Logs
66
+ IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'meyiconnect' AND table_name = 'insight_webhook_failure_logs' AND column_name = 'connector_id') THEN
67
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_webhook_failure_logs" RENAME COLUMN "connector_id" TO "source_id"';
68
+ END IF;
69
+
70
+ -- Rename columns in alerts
71
+ IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'connector_id') THEN
72
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_alerts" RENAME COLUMN "connector_id" TO "source_id"';
73
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_alerts" RENAME COLUMN "connector_name" TO "source_name"';
74
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_alerts" RENAME COLUMN "connector_type" TO "source_type"';
75
+ END IF;
76
+
77
+ -- Rename columns in configs
78
+ IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'meyiconnect' AND table_name = 'insight_normalization_configs' AND column_name = 'connector_id') THEN
79
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_normalization_configs" RENAME COLUMN "connector_id" TO "source_id"';
80
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_normalization_configs" RENAME COLUMN "connector_type" TO "source_type"';
81
+ END IF;
82
+
83
+ IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'meyiconnect' AND table_name = 'insight_dedup_configs' AND column_name = 'connector_id') THEN
84
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_dedup_configs" RENAME COLUMN "connector_id" TO "source_id"';
85
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_dedup_configs" RENAME COLUMN "connector_type" TO "source_type"';
86
+ END IF;
87
+
88
+ IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'meyiconnect' AND table_name = 'insight_grouping_configs' AND column_name = 'connector_id') THEN
89
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_grouping_configs" RENAME COLUMN "connector_id" TO "source_id"';
90
+ EXECUTE 'ALTER TABLE "meyiconnect"."insight_grouping_configs" RENAME COLUMN "connector_type" TO "source_type"';
91
+ END IF;
92
+
93
+ END $$;
94
+ `));
95
+ await db.execute(sql.raw(`
96
+ DO $$
97
+ DECLARE
98
+ v_relkind "char";
99
+ BEGIN
100
+ SELECT c.relkind INTO v_relkind
101
+ FROM pg_class c
102
+ JOIN pg_namespace n ON n.oid = c.relnamespace
103
+ WHERE n.nspname = 'meyiconnect' AND c.relname = 'insight_tenants'
104
+ LIMIT 1;
105
+
106
+ IF v_relkind IS NULL THEN
107
+ CREATE TABLE "meyiconnect"."insight_tenants" (
108
+ "id" TEXT PRIMARY KEY,
109
+ "name" TEXT NOT NULL,
110
+ "is_default" BOOLEAN NOT NULL DEFAULT false,
111
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
112
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
113
+ );
114
+ INSERT INTO "meyiconnect"."insight_tenants" ("id", "name", "is_default")
115
+ VALUES ('${defaultTenantSql}', 'Default tenant', true)
116
+ ON CONFLICT ("id") DO UPDATE SET "is_default" = true, "updated_at" = NOW();
117
+ ELSIF v_relkind = 'r' THEN
118
+ INSERT INTO "meyiconnect"."insight_tenants" ("id", "name", "is_default")
119
+ VALUES ('${defaultTenantSql}', 'Default tenant', true)
120
+ ON CONFLICT ("id") DO UPDATE SET "is_default" = true, "updated_at" = NOW();
121
+ END IF;
122
+ END $$;
123
+ `));
124
+ // insight_sources
125
+ await db.execute(sql.raw(`
126
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_sources" (
127
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
128
+ "name" TEXT NOT NULL,
129
+ "type" TEXT NOT NULL,
130
+ "webhook_token" TEXT NOT NULL,
131
+ "webhook_auth_token" TEXT,
132
+ "webhook_token_never_expires" BOOLEAN NOT NULL DEFAULT false,
133
+ "webhook_token_expires_at" TIMESTAMPTZ,
134
+ "is_active" BOOLEAN NOT NULL DEFAULT true,
135
+ "description" TEXT,
136
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
137
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
138
+ )
139
+ `));
140
+ await db.execute(sql.raw(`
141
+ DO $$
142
+ BEGIN
143
+ IF EXISTS (
144
+ SELECT 1 FROM information_schema.columns
145
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_sources' AND column_name = 'tenant_id' AND data_type = 'uuid'
146
+ ) THEN
147
+ ALTER TABLE "meyiconnect"."insight_sources" ALTER COLUMN "tenant_id" TYPE TEXT USING "tenant_id"::TEXT;
148
+ ELSIF NOT EXISTS (
149
+ SELECT 1 FROM information_schema.columns
150
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_sources' AND column_name = 'tenant_id'
151
+ ) THEN
152
+ ALTER TABLE "meyiconnect"."insight_sources" ADD COLUMN "tenant_id" TEXT;
153
+ END IF;
154
+ END $$;
155
+ `));
156
+ await db.execute(sql.raw(`UPDATE "meyiconnect"."insight_sources" SET "tenant_id" = '${defaultTenantSql}' WHERE "tenant_id" IS NULL OR "tenant_id"::text = ''`));
157
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_sources_tenant_id_idx ON "meyiconnect"."insight_sources" ("tenant_id")`));
158
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sources" ADD COLUMN IF NOT EXISTS "webhook_token_never_expires" BOOLEAN NOT NULL DEFAULT false`));
159
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sources" ADD COLUMN IF NOT EXISTS "webhook_token_expires_at" TIMESTAMPTZ`));
160
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sources" ADD COLUMN IF NOT EXISTS "is_active" BOOLEAN NOT NULL DEFAULT true`));
161
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sources" ADD COLUMN IF NOT EXISTS "description" TEXT`));
162
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sources" ALTER COLUMN "webhook_token_expires_at" SET DEFAULT (NOW() + INTERVAL '${defaultTokenTtlDays} days')`));
163
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sources" DROP COLUMN IF EXISTS "config"`));
164
+ await db.execute(sql.raw(`
165
+ UPDATE "meyiconnect"."insight_sources"
166
+ SET "webhook_token" = gen_random_uuid()::TEXT
167
+ WHERE "webhook_token" IS NULL
168
+ `));
169
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sources" ALTER COLUMN "webhook_token" SET NOT NULL`)).catch(() => { });
170
+ await db.execute(sql.raw(`
171
+ UPDATE "meyiconnect"."insight_sources"
172
+ SET "webhook_token_expires_at" = NOW() + INTERVAL '${defaultTokenTtlDays} days'
173
+ WHERE COALESCE("webhook_token_never_expires", FALSE) = FALSE
174
+ AND "webhook_token_expires_at" IS NULL
175
+ `));
176
+ await db.execute(sql.raw(`
177
+ CREATE UNIQUE INDEX IF NOT EXISTS insight_sources_webhook_token_unique
178
+ ON "meyiconnect"."insight_sources" ("webhook_token")
179
+ `));
180
+ // CloudWatch source setup lives in a dedicated one-to-one table.
181
+ await db.execute(sql.raw(`
182
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_cloudwatch_source_setups" (
183
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
184
+ "source_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE CASCADE,
185
+ "aws_access_key_id" TEXT,
186
+ "aws_secret_access_key" TEXT,
187
+ "aws_session_token" TEXT,
188
+ "aws_region" TEXT,
189
+ "aws_stack_name" TEXT,
190
+ "aws_lambda_name" TEXT,
191
+ "aws_alarm_name_prefix" TEXT,
192
+ "aws_selected_alarm_names" JSONB DEFAULT '[]',
193
+ "aws_selected_metric_keys" JSONB DEFAULT '[]',
194
+ "aws_deployment_status" TEXT NOT NULL DEFAULT 'not_started',
195
+ "aws_deployment_status_reason" TEXT,
196
+ "aws_stack_id" TEXT,
197
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
198
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
199
+ )
200
+ `));
201
+ await db.execute(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS insight_cloudwatch_source_setups_source_unique ON "meyiconnect"."insight_cloudwatch_source_setups" ("source_id")`));
202
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_cloudwatch_source_setups_status_idx ON "meyiconnect"."insight_cloudwatch_source_setups" ("aws_deployment_status")`));
203
+ // CloudTrail source setup — dedicated one-to-one side-table (same pattern as cloudwatch).
204
+ await db.execute(sql.raw(`
205
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_cloudtrail_source_setups" (
206
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
207
+ "source_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE CASCADE,
208
+ "aws_region" TEXT,
209
+ "aws_stack_name" TEXT,
210
+ "aws_create_new_trail" BOOLEAN NOT NULL DEFAULT true,
211
+ "aws_deployment_status" TEXT NOT NULL DEFAULT 'not_started',
212
+ "aws_deployment_status_reason" TEXT,
213
+ "aws_stack_id" TEXT,
214
+ "aws_deployment_mode" TEXT DEFAULT 'Organizations',
215
+ "aws_target_ous" TEXT,
216
+ "aws_target_accounts" TEXT,
217
+ "aws_target_regions" TEXT,
218
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
219
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
220
+ )
221
+ `));
222
+ await db.execute(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS insight_cloudtrail_source_setups_source_unique ON "meyiconnect"."insight_cloudtrail_source_setups" ("source_id")`));
223
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_cloudtrail_source_setups_status_idx ON "meyiconnect"."insight_cloudtrail_source_setups" ("aws_deployment_status")`));
224
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_cloudtrail_source_setups" ADD COLUMN IF NOT EXISTS "aws_create_new_trail" BOOLEAN NOT NULL DEFAULT true`));
225
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_cloudtrail_source_setups" ADD COLUMN IF NOT EXISTS "aws_deployment_mode" TEXT DEFAULT 'Organizations'`));
226
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_cloudtrail_source_setups" ADD COLUMN IF NOT EXISTS "aws_target_ous" TEXT`));
227
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_cloudtrail_source_setups" ADD COLUMN IF NOT EXISTS "aws_target_accounts" TEXT`));
228
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_cloudtrail_source_setups" ADD COLUMN IF NOT EXISTS "aws_target_regions" TEXT`));
229
+ await db.execute(sql.raw(`
230
+ DO $$
231
+ BEGIN
232
+ IF EXISTS (
233
+ SELECT 1
234
+ FROM information_schema.columns
235
+ WHERE table_schema = 'meyiconnect'
236
+ AND table_name = 'insight_sources'
237
+ AND column_name = 'aws_region'
238
+ ) THEN
239
+ INSERT INTO "meyiconnect"."insight_cloudwatch_source_setups" (
240
+ "source_id",
241
+ "aws_access_key_id",
242
+ "aws_secret_access_key",
243
+ "aws_session_token",
244
+ "aws_region",
245
+ "aws_stack_name",
246
+ "aws_lambda_name",
247
+ "aws_alarm_name_prefix",
248
+ "aws_selected_alarm_names",
249
+ "aws_selected_metric_keys",
250
+ "aws_deployment_status",
251
+ "aws_deployment_status_reason",
252
+ "aws_stack_id",
253
+ "created_at",
254
+ "updated_at"
255
+ )
256
+ SELECT
257
+ "id",
258
+ "aws_access_key_id",
259
+ "aws_secret_access_key",
260
+ "aws_session_token",
261
+ "aws_region",
262
+ "aws_stack_name",
263
+ "aws_lambda_name",
264
+ "aws_alarm_name_prefix",
265
+ COALESCE("aws_selected_alarm_names", '[]'::jsonb),
266
+ COALESCE("aws_selected_metric_keys", '[]'::jsonb),
267
+ COALESCE("aws_deployment_status", 'not_started'),
268
+ "aws_deployment_status_reason",
269
+ "aws_stack_id",
270
+ NOW(),
271
+ NOW()
272
+ FROM "meyiconnect"."insight_sources"
273
+ WHERE "type" = 'cloudwatch'
274
+ AND (
275
+ "aws_access_key_id" IS NOT NULL OR
276
+ "aws_secret_access_key" IS NOT NULL OR
277
+ "aws_session_token" IS NOT NULL OR
278
+ "aws_region" IS NOT NULL OR
279
+ "aws_stack_name" IS NOT NULL OR
280
+ "aws_lambda_name" IS NOT NULL OR
281
+ "aws_alarm_name_prefix" IS NOT NULL OR
282
+ "aws_deployment_status" IS NOT NULL OR
283
+ "aws_stack_id" IS NOT NULL
284
+ )
285
+ ON CONFLICT ("source_id") DO UPDATE SET
286
+ "aws_access_key_id" = EXCLUDED."aws_access_key_id",
287
+ "aws_secret_access_key" = EXCLUDED."aws_secret_access_key",
288
+ "aws_session_token" = EXCLUDED."aws_session_token",
289
+ "aws_region" = EXCLUDED."aws_region",
290
+ "aws_stack_name" = EXCLUDED."aws_stack_name",
291
+ "aws_lambda_name" = EXCLUDED."aws_lambda_name",
292
+ "aws_alarm_name_prefix" = EXCLUDED."aws_alarm_name_prefix",
293
+ "aws_selected_alarm_names" = EXCLUDED."aws_selected_alarm_names",
294
+ "aws_selected_metric_keys" = EXCLUDED."aws_selected_metric_keys",
295
+ "aws_deployment_status" = EXCLUDED."aws_deployment_status",
296
+ "aws_deployment_status_reason" = EXCLUDED."aws_deployment_status_reason",
297
+ "aws_stack_id" = EXCLUDED."aws_stack_id",
298
+ "updated_at" = NOW();
299
+ END IF;
300
+ END $$;
301
+ `));
302
+ await db.execute(sql.raw(`
303
+ ALTER TABLE "meyiconnect"."insight_sources"
304
+ DROP COLUMN IF EXISTS "aws_access_key_id",
305
+ DROP COLUMN IF EXISTS "aws_secret_access_key",
306
+ DROP COLUMN IF EXISTS "aws_session_token",
307
+ DROP COLUMN IF EXISTS "aws_region",
308
+ DROP COLUMN IF EXISTS "aws_stack_name",
309
+ DROP COLUMN IF EXISTS "aws_lambda_name",
310
+ DROP COLUMN IF EXISTS "aws_alarm_name_prefix",
311
+ DROP COLUMN IF EXISTS "aws_selected_alarm_names",
312
+ DROP COLUMN IF EXISTS "aws_selected_metric_keys",
313
+ DROP COLUMN IF EXISTS "aws_deployment_status",
314
+ DROP COLUMN IF EXISTS "aws_deployment_status_reason",
315
+ DROP COLUMN IF EXISTS "aws_stack_id"
316
+ `));
317
+ // Grafana source setup lives in a dedicated one-to-one table.
318
+ await db.execute(sql.raw(`
319
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_grafana_source_setups" (
320
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
321
+ "source_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE CASCADE,
322
+ "grafana_base_url" TEXT,
323
+ "grafana_contact_point_name" TEXT,
324
+ "grafana_account_token_encrypted" TEXT,
325
+ "grafana_auto_route" BOOLEAN NOT NULL DEFAULT false,
326
+ "grafana_matcher_label" TEXT,
327
+ "grafana_matcher_value" TEXT,
328
+ "grafana_policy_updated" BOOLEAN NOT NULL DEFAULT false,
329
+ "grafana_setup_status" TEXT NOT NULL DEFAULT 'not_started',
330
+ "grafana_setup_status_reason" TEXT,
331
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
332
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
333
+ )
334
+ `));
335
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_grafana_source_setups" ADD COLUMN IF NOT EXISTS "grafana_account_token_encrypted" TEXT`));
336
+ await db.execute(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS insight_grafana_source_setups_source_unique ON "meyiconnect"."insight_grafana_source_setups" ("source_id")`));
337
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_grafana_source_setups_status_idx ON "meyiconnect"."insight_grafana_source_setups" ("grafana_setup_status")`));
338
+ // Prometheus source setup lives in a dedicated one-to-one table.
339
+ await db.execute(sql.raw(`
340
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_prometheus_source_setups" (
341
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
342
+ "source_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE CASCADE,
343
+ "prometheus_environment" TEXT,
344
+ "prometheus_receiver_name" TEXT,
345
+ "prometheus_base_url" TEXT,
346
+ "alertmanager_base_url" TEXT,
347
+ "prometheus_setup_status" TEXT NOT NULL DEFAULT 'not_started',
348
+ "prometheus_setup_status_reason" TEXT,
349
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
350
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
351
+ )
352
+ `));
353
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_prometheus_source_setups" ADD COLUMN IF NOT EXISTS "prometheus_base_url" TEXT`));
354
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_prometheus_source_setups" ADD COLUMN IF NOT EXISTS "alertmanager_base_url" TEXT`));
355
+ await db.execute(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS insight_prometheus_source_setups_source_unique ON "meyiconnect"."insight_prometheus_source_setups" ("source_id")`));
356
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_prometheus_source_setups_status_idx ON "meyiconnect"."insight_prometheus_source_setups" ("prometheus_setup_status")`));
357
+ // Source observability: one health row per source + failed webhook log.
358
+ await db.execute(sql.raw(`
359
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_source_health" (
360
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
361
+ "source_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE CASCADE,
362
+ "delivery_status" TEXT NOT NULL DEFAULT 'unknown',
363
+ "last_webhook_received_at" TIMESTAMPTZ,
364
+ "last_successful_webhook_at" TIMESTAMPTZ,
365
+ "last_failed_webhook_at" TIMESTAMPTZ,
366
+ "last_error_code" TEXT,
367
+ "last_error_message" TEXT,
368
+ "last_alert_id" UUID,
369
+ "success_count" INTEGER NOT NULL DEFAULT 0,
370
+ "failure_count" INTEGER NOT NULL DEFAULT 0,
371
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
372
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
373
+ )
374
+ `));
375
+ await db.execute(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS insight_source_health_source_unique ON "meyiconnect"."insight_source_health" ("source_id")`));
376
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_source_health_status_idx ON "meyiconnect"."insight_source_health" ("delivery_status")`));
377
+ await db.execute(sql.raw(`
378
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_webhook_failure_logs" (
379
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
380
+ "source_id" UUID REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE SET NULL,
381
+ "token_hash" TEXT,
382
+ "token_preview" TEXT,
383
+ "error_code" TEXT NOT NULL,
384
+ "error_message" TEXT NOT NULL,
385
+ "request_ip" TEXT,
386
+ "user_agent" TEXT,
387
+ "method" TEXT,
388
+ "path" TEXT,
389
+ "payload" JSONB,
390
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
391
+ )
392
+ `));
393
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_webhook_failure_logs_source_idx ON "meyiconnect"."insight_webhook_failure_logs" ("source_id")`));
394
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_webhook_failure_logs_error_idx ON "meyiconnect"."insight_webhook_failure_logs" ("error_code")`));
395
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_webhook_failure_logs_created_idx ON "meyiconnect"."insight_webhook_failure_logs" ("created_at")`));
396
+ // insight_projects
397
+ await db.execute(sql.raw(`
398
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_projects" (
399
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
400
+ "name" TEXT NOT NULL,
401
+ "tag" TEXT NOT NULL,
402
+ "description" TEXT,
403
+ "dependencies" JSONB DEFAULT '[]',
404
+ "aws_access_key_id" TEXT,
405
+ "aws_secret_access_key" TEXT,
406
+ "aws_region" TEXT,
407
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
408
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
409
+ )
410
+ `));
411
+ await db.execute(sql.raw(`
412
+ DO $$
413
+ BEGIN
414
+ IF EXISTS (
415
+ SELECT 1 FROM information_schema.columns
416
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_projects' AND column_name = 'tenant_id' AND data_type = 'uuid'
417
+ ) THEN
418
+ ALTER TABLE "meyiconnect"."insight_projects" ALTER COLUMN "tenant_id" TYPE TEXT USING "tenant_id"::TEXT;
419
+ ELSIF NOT EXISTS (
420
+ SELECT 1 FROM information_schema.columns
421
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_projects' AND column_name = 'tenant_id'
422
+ ) THEN
423
+ ALTER TABLE "meyiconnect"."insight_projects" ADD COLUMN "tenant_id" TEXT;
424
+ END IF;
425
+ END $$;
426
+ `));
427
+ await db.execute(sql.raw(`
428
+ ALTER TABLE "meyiconnect"."insight_projects"
429
+ ADD COLUMN IF NOT EXISTS "aws_access_key_id" TEXT,
430
+ ADD COLUMN IF NOT EXISTS "aws_secret_access_key" TEXT,
431
+ ADD COLUMN IF NOT EXISTS "aws_region" TEXT
432
+ `));
433
+ await db.execute(sql.raw(`UPDATE "meyiconnect"."insight_projects" SET "tenant_id" = '${defaultTenantSql}' WHERE "tenant_id" IS NULL OR "tenant_id"::text = ''`));
434
+ await db.execute(sql.raw(`DROP INDEX IF EXISTS "meyiconnect"."insight_projects_tag_unique"`)).catch(() => { });
435
+ await db.execute(sql.raw(`DROP INDEX IF EXISTS insight_projects_tag_unique`)).catch(() => { });
436
+ await db.execute(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS insight_projects_tenant_tag_unique ON "meyiconnect"."insight_projects" ("tenant_id", "tag")`));
437
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_projects_tag_idx ON "meyiconnect"."insight_projects" ("tag")`));
438
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_projects_tenant_id_idx ON "meyiconnect"."insight_projects" ("tenant_id")`));
439
+ // insight_incidents
440
+ await db.execute(sql.raw(`
441
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_incidents" (
442
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
443
+ "alias_id" SERIAL,
444
+ "monitor_id" TEXT,
445
+ "monitor_name" TEXT,
446
+ "monitor_type" TEXT,
447
+ "monitor_url" TEXT,
448
+ "name" TEXT NOT NULL DEFAULT '',
449
+ "source" TEXT NOT NULL DEFAULT '',
450
+ "resource_name" TEXT NOT NULL DEFAULT '',
451
+ "resource_type" TEXT NOT NULL DEFAULT '',
452
+ "down_message" TEXT,
453
+ "project_tag" TEXT,
454
+ "down_time" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
455
+ "incident_down_time" TEXT NOT NULL DEFAULT '',
456
+ "resolved_at" TIMESTAMPTZ,
457
+ "analysis_status" TEXT NOT NULL DEFAULT 'pending',
458
+ "analysis_percent" INTEGER NOT NULL DEFAULT 0,
459
+ "analysis_triggered_at" TIMESTAMPTZ,
460
+ "analysis_completed_at" TIMESTAMPTZ,
461
+ "analysis_result" JSONB,
462
+ "remediation_steps" JSONB DEFAULT '[]',
463
+ "confidence_score" REAL,
464
+ "ai_model_used" TEXT,
465
+ "source_alert_id" UUID,
466
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
467
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
468
+ )
469
+ `));
470
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_incidents_monitor_id_idx ON "meyiconnect"."insight_incidents" ("monitor_id")`));
471
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_incidents_status_idx ON "meyiconnect"."insight_incidents" ("analysis_status")`));
472
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "alias_id" SERIAL`));
473
+ await db.execute(sql.raw(`
474
+ DO $$
475
+ BEGIN
476
+ IF EXISTS (
477
+ SELECT 1 FROM information_schema.columns
478
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_incidents' AND column_name = 'tenant_id' AND data_type = 'uuid'
479
+ ) THEN
480
+ ALTER TABLE "meyiconnect"."insight_incidents" ALTER COLUMN "tenant_id" TYPE TEXT USING "tenant_id"::TEXT;
481
+ ELSIF NOT EXISTS (
482
+ SELECT 1 FROM information_schema.columns
483
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_incidents' AND column_name = 'tenant_id'
484
+ ) THEN
485
+ ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN "tenant_id" TEXT;
486
+ END IF;
487
+ END $$;
488
+ `));
489
+ await db.execute(sql.raw(`UPDATE "meyiconnect"."insight_incidents" SET "tenant_id" = '${defaultTenantSql}' WHERE "tenant_id" IS NULL OR "tenant_id"::text = ''`));
490
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_incidents_tenant_id_idx ON "meyiconnect"."insight_incidents" ("tenant_id")`));
491
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "incident_down_time" TEXT NOT NULL DEFAULT ''`));
492
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "name" TEXT`));
493
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "source" TEXT`));
494
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "resource_name" TEXT`));
495
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "resource_type" TEXT`));
496
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "down_time" TIMESTAMPTZ`));
497
+ await db.execute(sql.raw(`
498
+ DO $$
499
+ BEGIN
500
+ UPDATE "meyiconnect"."insight_incidents" SET "name" = COALESCE("monitor_name", 'Unknown') WHERE "name" IS NULL;
501
+ UPDATE "meyiconnect"."insight_incidents" SET "source" = 'Unknown' WHERE "source" IS NULL;
502
+ UPDATE "meyiconnect"."insight_incidents" SET "resource_name" = COALESCE("monitor_name", 'Unknown') WHERE "resource_name" IS NULL;
503
+ UPDATE "meyiconnect"."insight_incidents" SET "resource_type" = COALESCE("monitor_type", 'Unknown') WHERE "resource_type" IS NULL;
504
+ UPDATE "meyiconnect"."insight_incidents" SET "down_time" = "created_at" WHERE "down_time" IS NULL;
505
+
506
+ ALTER TABLE "meyiconnect"."insight_incidents" ALTER COLUMN "name" SET NOT NULL;
507
+ ALTER TABLE "meyiconnect"."insight_incidents" ALTER COLUMN "source" SET NOT NULL;
508
+ ALTER TABLE "meyiconnect"."insight_incidents" ALTER COLUMN "resource_name" SET NOT NULL;
509
+ ALTER TABLE "meyiconnect"."insight_incidents" ALTER COLUMN "resource_type" SET NOT NULL;
510
+ ALTER TABLE "meyiconnect"."insight_incidents" ALTER COLUMN "down_time" SET NOT NULL;
511
+
512
+ -- Remove NOT NULL from old columns if they exist
513
+ IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'meyiconnect' AND table_name = 'insight_incidents' AND column_name = 'monitor_id') THEN
514
+ ALTER TABLE "meyiconnect"."insight_incidents" ALTER COLUMN "monitor_id" DROP NOT NULL;
515
+ ALTER TABLE "meyiconnect"."insight_incidents" ALTER COLUMN "monitor_name" DROP NOT NULL;
516
+ ALTER TABLE "meyiconnect"."insight_incidents" ALTER COLUMN "monitor_type" DROP NOT NULL;
517
+ END IF;
518
+ END $$;
519
+ `));
520
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "status" TEXT NOT NULL DEFAULT 'open'`));
521
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "analysis_percent" INTEGER NOT NULL DEFAULT 0`));
522
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "remediation_steps" JSONB DEFAULT '[]'`));
523
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "confidence_score" REAL`));
524
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "ai_model_used" TEXT`));
525
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "source_alert_id" UUID`));
526
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "assignee_ids" JSONB DEFAULT '[]'::jsonb`));
527
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" DROP COLUMN IF EXISTS "processing_status"`));
528
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" DROP COLUMN IF EXISTS "progress_percent"`));
529
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "account_id" TEXT`));
530
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "account_name" TEXT`));
531
+ await db.execute(sql.raw(`
532
+ UPDATE "meyiconnect"."insight_incidents" i
533
+ SET "account_name" = c."account_name"
534
+ FROM "meyiconnect"."connectors" c
535
+ WHERE i."account_name" IS NULL
536
+ AND i."account_id" IS NOT NULL
537
+ AND i."account_id" = c."account_id";
538
+ `));
539
+ await db.execute(sql.raw(`
540
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."incident_comments" (
541
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
542
+ "incident_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_incidents"("id") ON DELETE CASCADE,
543
+ "author_id" TEXT,
544
+ "author_name" TEXT,
545
+ "message" TEXT NOT NULL,
546
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
547
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
548
+ )
549
+ `));
550
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS incident_comments_incident_id_idx ON "meyiconnect"."incident_comments" ("incident_id")`));
551
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS incident_comments_created_at_idx ON "meyiconnect"."incident_comments" ("created_at")`));
552
+ // insight_alerts
553
+ await db.execute(sql.raw(`
554
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_alerts" (
555
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
556
+ "tenant_id" TEXT,
557
+ "source_id" UUID,
558
+ "source_name" TEXT,
559
+ "source_type" TEXT,
560
+ "resource_id" TEXT NOT NULL,
561
+ "resource_name" TEXT NOT NULL,
562
+ "resource_type" TEXT NOT NULL,
563
+ "project_tag" TEXT,
564
+ "severity" TEXT NOT NULL DEFAULT 'high',
565
+ "alert_name" TEXT NOT NULL,
566
+ "description" TEXT,
567
+ "additional_configuration" JSONB,
568
+ "status" TEXT NOT NULL DEFAULT 'open',
569
+ "converted_incident_id" UUID,
570
+ "incident_id" UUID,
571
+ "alias_id" TEXT,
572
+ "assignee_id" UUID,
573
+ "down_time" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
574
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
575
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
576
+ "dup_count" INT NOT NULL DEFAULT 0,
577
+ "last_dup_at" TIMESTAMPTZ,
578
+ "fingerprint" TEXT,
579
+ "dedup_key" TEXT,
580
+ "pipeline_status" TEXT DEFAULT 'queued',
581
+ "group_id" UUID,
582
+ "raw_payload" JSONB
583
+ )
584
+ `));
585
+ await db.execute(sql.raw(`
586
+ DO $$
587
+ BEGIN
588
+ IF NOT EXISTS (
589
+ SELECT 1 FROM information_schema.columns
590
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'alert_name'
591
+ ) THEN
592
+ ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN "alert_name" TEXT;
593
+ END IF;
594
+
595
+ IF EXISTS (
596
+ SELECT 1 FROM information_schema.columns
597
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'title'
598
+ ) THEN
599
+ ALTER TABLE "meyiconnect"."insight_alerts" ALTER COLUMN "title" DROP NOT NULL;
600
+ ALTER TABLE "meyiconnect"."insight_alerts" ALTER COLUMN "title" SET DEFAULT 'Alert';
601
+ UPDATE "meyiconnect"."insight_alerts"
602
+ SET "alert_name" = COALESCE("alert_name", "title", 'Alert')
603
+ WHERE "alert_name" IS NULL;
604
+ ELSE
605
+ UPDATE "meyiconnect"."insight_alerts"
606
+ SET "alert_name" = COALESCE("alert_name", 'Alert')
607
+ WHERE "alert_name" IS NULL;
608
+ END IF;
609
+
610
+ ALTER TABLE "meyiconnect"."insight_alerts" ALTER COLUMN "alert_name" SET NOT NULL;
611
+ END $$;
612
+ `));
613
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "project_tag" TEXT`));
614
+ await db.execute(sql.raw(`
615
+ DO $$
616
+ BEGIN
617
+ IF EXISTS (
618
+ SELECT 1 FROM information_schema.columns
619
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'tenant_id' AND data_type = 'uuid'
620
+ ) THEN
621
+ ALTER TABLE "meyiconnect"."insight_alerts" ALTER COLUMN "tenant_id" TYPE TEXT USING "tenant_id"::TEXT;
622
+ ELSIF NOT EXISTS (
623
+ SELECT 1 FROM information_schema.columns
624
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'tenant_id'
625
+ ) THEN
626
+ ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN "tenant_id" TEXT;
627
+ END IF;
628
+ END $$;
629
+ `));
630
+ await db.execute(sql.raw(`UPDATE "meyiconnect"."insight_alerts" SET "tenant_id" = '${defaultTenantSql}' WHERE "tenant_id" IS NULL OR "tenant_id"::text = ''`));
631
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_alerts_tenant_id_idx ON "meyiconnect"."insight_alerts" ("tenant_id")`));
632
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "alias_id" TEXT`));
633
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "additional_configuration" JSONB`));
634
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "incident_id" UUID`));
635
+ await db.execute(sql.raw(`
636
+ DO $$
637
+ BEGIN
638
+ IF NOT EXISTS (
639
+ SELECT 1 FROM information_schema.columns
640
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'down_time'
641
+ ) THEN
642
+ ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN "down_time" TIMESTAMPTZ;
643
+ END IF;
644
+
645
+ IF EXISTS (
646
+ SELECT 1 FROM information_schema.columns
647
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'alert_time'
648
+ ) THEN
649
+ UPDATE "meyiconnect"."insight_alerts"
650
+ SET "down_time" = COALESCE("down_time", "alert_time", NOW())
651
+ WHERE "down_time" IS NULL;
652
+ ELSE
653
+ UPDATE "meyiconnect"."insight_alerts"
654
+ SET "down_time" = COALESCE("down_time", NOW())
655
+ WHERE "down_time" IS NULL;
656
+ END IF;
657
+
658
+ ALTER TABLE "meyiconnect"."insight_alerts" ALTER COLUMN "down_time" SET NOT NULL;
659
+ ALTER TABLE "meyiconnect"."insight_alerts" ALTER COLUMN "down_time" SET DEFAULT NOW();
660
+ END $$;
661
+ `));
662
+ await db.execute(sql.raw(`
663
+ DO $$
664
+ BEGIN
665
+ IF EXISTS (
666
+ SELECT 1 FROM information_schema.columns
667
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'promoted_incident_id'
668
+ ) THEN
669
+ IF NOT EXISTS (
670
+ SELECT 1 FROM information_schema.columns
671
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'converted_incident_id'
672
+ ) THEN
673
+ ALTER TABLE "meyiconnect"."insight_alerts" RENAME COLUMN "promoted_incident_id" TO "converted_incident_id";
674
+ ELSE
675
+ ALTER TABLE "meyiconnect"."insight_alerts" DROP COLUMN "promoted_incident_id";
676
+ END IF;
677
+ END IF;
678
+ END $$;
679
+ `));
680
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "converted_incident_id" UUID`));
681
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "assignee_id" UUID`));
682
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "dup_count" INT NOT NULL DEFAULT 0`));
683
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "last_dup_at" TIMESTAMPTZ`));
684
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "account_id" TEXT`));
685
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "account_name" TEXT`));
686
+ await db.execute(sql.raw(`
687
+ UPDATE "meyiconnect"."insight_alerts" a
688
+ SET "account_name" = c."account_name"
689
+ FROM "meyiconnect"."connectors" c
690
+ WHERE a."account_name" IS NULL
691
+ AND a."account_id" IS NOT NULL
692
+ AND a."account_id" = c."account_id";
693
+ `));
694
+ await db.execute(sql.raw(`
695
+ UPDATE "meyiconnect"."insight_alerts"
696
+ SET "account_id" = COALESCE(
697
+ "additional_configuration"->>'accountId',
698
+ "raw_payload"->>'account',
699
+ "raw_payload"->>'accountId',
700
+ "raw_payload"->>'AWSAccountId',
701
+ "raw_payload"->'rawEvent'->>'account',
702
+ "raw_payload"->'detail'->>'account',
703
+ "raw_payload"->'detail'->>'recipientAccountId',
704
+ substring("resource_id" FROM 'arn:aws[a-z0-9-]*:cloudwatch:[^:]+:([0-9]{12}):alarm:')
705
+ )
706
+ WHERE "account_id" IS NULL
707
+ AND (
708
+ "additional_configuration"->>'accountId' IS NOT NULL
709
+ OR "raw_payload"->>'account' IS NOT NULL
710
+ OR "raw_payload"->>'accountId' IS NOT NULL
711
+ OR "raw_payload"->>'AWSAccountId' IS NOT NULL
712
+ OR "raw_payload"->'rawEvent'->>'account' IS NOT NULL
713
+ OR "raw_payload"->'detail'->>'account' IS NOT NULL
714
+ OR "raw_payload"->'detail'->>'recipientAccountId' IS NOT NULL
715
+ OR "resource_id" ~ '^arn:aws[a-z0-9-]*:cloudwatch:[^:]+:[0-9]{12}:alarm:'
716
+ )
717
+ `));
718
+ await db.execute(sql.raw(`
719
+ UPDATE "meyiconnect"."insight_alerts"
720
+ SET "additional_configuration" = jsonb_set(
721
+ COALESCE("additional_configuration", '{}'::jsonb),
722
+ '{tags}',
723
+ COALESCE("raw_payload"->'tags', "raw_payload"->'Tags', "raw_payload"->'detail'->'tags')
724
+ )
725
+ WHERE ("additional_configuration" IS NULL OR "additional_configuration"->'tags' IS NULL)
726
+ AND (
727
+ "raw_payload"->'tags' IS NOT NULL
728
+ OR "raw_payload"->'Tags' IS NOT NULL
729
+ OR "raw_payload"->'detail'->'tags' IS NOT NULL
730
+ )
731
+ `));
732
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_alerts_status_idx ON "meyiconnect"."insight_alerts" ("status")`));
733
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_alerts_source_idx ON "meyiconnect"."insight_alerts" ("source_id")`));
734
+ // insight_settings
735
+ await db.execute(sql.raw(`
736
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_settings" (
737
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
738
+
739
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
740
+ )
741
+ `));
742
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_settings" ADD COLUMN IF NOT EXISTS "aws_bedrock_access_key_id" TEXT`));
743
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_settings" ADD COLUMN IF NOT EXISTS "aws_bedrock_secret_access_key" TEXT`));
744
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_settings" ADD COLUMN IF NOT EXISTS "aws_bedrock_region" TEXT`));
745
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_settings" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
746
+ await db.execute(sql.raw(`UPDATE "meyiconnect"."insight_settings" SET "tenant_id" = '${defaultTenantSql}' WHERE "tenant_id" IS NULL OR "tenant_id" = ''`));
747
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_settings_tenant_id_idx ON "meyiconnect"."insight_settings" ("tenant_id")`));
748
+ // notification_providers
749
+ await db.execute(sql.raw(`
750
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."notification_providers" (
751
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
752
+ "tenant_id" TEXT,
753
+ "provider_name" VARCHAR(50) NOT NULL,
754
+ "display_name" VARCHAR(100) NOT NULL,
755
+ "is_enabled" BOOLEAN NOT NULL DEFAULT false,
756
+ "config" JSONB,
757
+ "description" TEXT,
758
+ "icon" TEXT,
759
+ "created_at" TIMESTAMP DEFAULT NOW(),
760
+ "updated_at" TIMESTAMP DEFAULT NOW()
761
+ )
762
+ `));
763
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."notification_providers" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
764
+ await db.execute(sql.raw(`UPDATE "meyiconnect"."notification_providers" SET "tenant_id" = '${defaultTenantSql}' WHERE "tenant_id" IS NULL OR "tenant_id" = ''`));
765
+ await db.execute(sql.raw(`DROP INDEX IF EXISTS "meyiconnect"."notification_providers_provider_name_unique"`)).catch(() => { });
766
+ await db.execute(sql.raw(`DROP INDEX IF EXISTS notification_providers_provider_name_unique`)).catch(() => { });
767
+ await db.execute(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS notification_providers_tenant_name_unique ON "meyiconnect"."notification_providers" ("tenant_id", "provider_name")`));
768
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS notification_providers_tenant_id_idx ON "meyiconnect"."notification_providers" ("tenant_id")`));
769
+ // incident_events — timestamped audit/timeline log (status changes, assigns, analysis, comments)
770
+ await db.execute(sql.raw(`
771
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."incident_events" (
772
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
773
+ "incident_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_incidents"("id") ON DELETE CASCADE,
774
+ "type" TEXT NOT NULL,
775
+ "actor" TEXT,
776
+ "message" TEXT,
777
+ "before_value" TEXT,
778
+ "after_value" TEXT,
779
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
780
+ )
781
+ `));
782
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS incident_events_incident_id_idx ON "meyiconnect"."incident_events" ("incident_id")`));
783
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS incident_events_created_at_idx ON "meyiconnect"."incident_events" ("created_at")`));
784
+ // incident_logs
785
+ await db.execute(sql.raw(`
786
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."incident_logs" (
787
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
788
+ "incident_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_incidents"("id") ON DELETE CASCADE,
789
+ "ec2_details" JSONB,
790
+ "ec2_status_checks" JSONB,
791
+ "cloudwatch_metrics" JSONB,
792
+ "raw_logs" JSONB,
793
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
794
+ )
795
+ `));
796
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS incident_logs_incident_id_idx ON "meyiconnect"."incident_logs" ("incident_id")`));
797
+ // insight_sops
798
+ await db.execute(sql.raw(`
799
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_sops" (
800
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
801
+ "sop_id" TEXT NOT NULL,
802
+ "title" TEXT NOT NULL,
803
+ "alert_type" TEXT,
804
+ "service" TEXT,
805
+ "severity" TEXT,
806
+ "status" TEXT NOT NULL DEFAULT 'draft',
807
+ "description" TEXT,
808
+ "steps" TEXT,
809
+ "error_response" TEXT,
810
+ "created_by" TEXT DEFAULT 'Manual',
811
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
812
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
813
+ )
814
+ `));
815
+ await db.execute(sql.raw(`DROP INDEX IF EXISTS "meyiconnect"."insight_sops_sop_id_unique"`)).catch(() => { });
816
+ await db.execute(sql.raw(`DROP INDEX IF EXISTS insight_sops_sop_id_unique`)).catch(() => { });
817
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_sops_status_idx ON "meyiconnect"."insight_sops" ("status")`));
818
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_sops_severity_idx ON "meyiconnect"."insight_sops" ("severity")`));
819
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "prompt" TEXT`));
820
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "error_response" TEXT`));
821
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "source_id" UUID`));
822
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "project_id" UUID`));
823
+ await db.execute(sql.raw(`
824
+ DO $$
825
+ BEGIN
826
+ IF EXISTS (
827
+ SELECT 1 FROM information_schema.columns
828
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_sops' AND column_name = 'incident_id'
829
+ ) THEN
830
+ IF NOT EXISTS (
831
+ SELECT 1 FROM information_schema.columns
832
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_sops' AND column_name = 'alert_id'
833
+ ) THEN
834
+ -- Drop old FK before renaming to ensure it points to the correct table
835
+ ALTER TABLE "meyiconnect"."insight_sops" DROP CONSTRAINT IF EXISTS insight_sops_incident_id_fkey;
836
+ ALTER TABLE "meyiconnect"."insight_sops" RENAME COLUMN "incident_id" TO "alert_id";
837
+ ELSE
838
+ ALTER TABLE "meyiconnect"."insight_sops" DROP COLUMN "incident_id";
839
+ END IF;
840
+ END IF;
841
+ END $$;
842
+ `));
843
+ // Drop constraint anyway in case we renamed it but left the constraint intact previously
844
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" DROP CONSTRAINT IF EXISTS insight_sops_incident_id_fkey;`));
845
+ // Add column if it doesn't exist
846
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "alert_id" UUID`));
847
+ // Explicitly add the new foreign key constraint if it doesn't exist
848
+ await db.execute(sql.raw(`
849
+ DO $$
850
+ BEGIN
851
+ IF NOT EXISTS (
852
+ SELECT 1 FROM information_schema.table_constraints
853
+ WHERE constraint_schema = 'meyiconnect' AND constraint_name = 'insight_sops_alert_id_fkey'
854
+ ) THEN
855
+ -- Clear invalid alert_ids (which are actually old incident_ids) before adding the constraint
856
+ UPDATE "meyiconnect"."insight_sops"
857
+ SET alert_id = NULL
858
+ WHERE alert_id IS NOT NULL AND alert_id NOT IN (SELECT id FROM "meyiconnect"."insight_alerts");
859
+
860
+ ALTER TABLE "meyiconnect"."insight_sops" ADD CONSTRAINT insight_sops_alert_id_fkey FOREIGN KEY ("alert_id") REFERENCES "meyiconnect"."insight_alerts"("id") ON DELETE SET NULL;
861
+ END IF;
862
+ END $$;
863
+ `));
864
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "alert_name" TEXT`));
865
+ await db.execute(sql.raw(`DROP INDEX IF EXISTS "meyiconnect"."insight_sops_alert_name_unique"`)).catch(() => { });
866
+ await db.execute(sql.raw(`DROP INDEX IF EXISTS insight_sops_alert_name_unique`)).catch(() => { });
867
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "source" TEXT`));
868
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "project" TEXT`));
869
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "ai_model_used" TEXT`));
870
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_sops" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
871
+ await db.execute(sql.raw(`UPDATE "meyiconnect"."insight_sops" SET "tenant_id" = '${defaultTenantSql}' WHERE "tenant_id" IS NULL OR "tenant_id" = ''`));
872
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_sops_tenant_id_idx ON "meyiconnect"."insight_sops" ("tenant_id")`));
873
+ await db.execute(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS insight_sops_tenant_sop_id_unique ON "meyiconnect"."insight_sops" ("tenant_id", "sop_id")`));
874
+ // incident_evidence
875
+ await db.execute(sql.raw(`
876
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."incident_evidence" (
877
+ "incident_id" UUID PRIMARY KEY REFERENCES "meyiconnect"."insight_incidents"("id") ON DELETE CASCADE,
878
+ "evidence_text" JSONB NOT NULL DEFAULT '[]'::jsonb,
879
+ "investigation_findings" JSONB NOT NULL DEFAULT '[]'::jsonb,
880
+ "rca_signals" JSONB NOT NULL DEFAULT '{}'::jsonb,
881
+ "tool_calls_made" JSONB NOT NULL DEFAULT '[]'::jsonb,
882
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
883
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
884
+ )
885
+ `));
886
+ // insight_workflows
887
+ await db.execute(sql.raw(`
888
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_workflows" (
889
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
890
+ "name" TEXT NOT NULL,
891
+ "description" TEXT,
892
+ "is_active" BOOLEAN NOT NULL DEFAULT true,
893
+ "priority" INTEGER NOT NULL DEFAULT 0,
894
+ "conditions" JSONB NOT NULL DEFAULT '[]'::jsonb,
895
+ "actions" JSONB NOT NULL DEFAULT '[]'::jsonb,
896
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
897
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
898
+ )
899
+ `));
900
+ await db.execute(sql.raw(`
901
+ ALTER TABLE "meyiconnect"."insight_workflows"
902
+ ADD COLUMN IF NOT EXISTS "yaml_definition" TEXT NOT NULL DEFAULT ''
903
+ `));
904
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_workflows" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
905
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_workflows" ADD COLUMN IF NOT EXISTS "source_id" UUID REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE CASCADE`));
906
+ await db.execute(sql.raw(`UPDATE "meyiconnect"."insight_workflows" SET "tenant_id" = '${defaultTenantSql}' WHERE "tenant_id" IS NULL OR "tenant_id" = ''`));
907
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_workflows_tenant_id_idx ON "meyiconnect"."insight_workflows" ("tenant_id")`));
908
+ await db.execute(sql.raw(`
909
+ ALTER TABLE "meyiconnect"."insight_workflows"
910
+ DROP COLUMN IF EXISTS "conditions"
911
+ `));
912
+ await db.execute(sql.raw(`
913
+ ALTER TABLE "meyiconnect"."insight_workflows"
914
+ DROP COLUMN IF EXISTS "actions"
915
+ `));
916
+ await db.execute(sql.raw(`
917
+ ALTER TABLE "meyiconnect"."insight_workflows"
918
+ DROP COLUMN IF EXISTS "description"
919
+ `));
920
+ // insight_workflow_executions
921
+ await db.execute(sql.raw(`
922
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_workflow_executions" (
923
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
924
+ "workflow_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_workflows"("id") ON DELETE CASCADE,
925
+ "alert_id" UUID REFERENCES "meyiconnect"."insight_alerts"("id") ON DELETE SET NULL,
926
+ "status" TEXT NOT NULL DEFAULT 'pending',
927
+ "actions_executed" JSONB NOT NULL DEFAULT '[]'::jsonb,
928
+ "error_message" TEXT,
929
+ "triggered_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
930
+ "completed_at" TIMESTAMPTZ
931
+ )
932
+ `));
933
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_workflow_executions_workflow_id_idx ON "meyiconnect"."insight_workflow_executions" ("workflow_id")`));
934
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_workflow_executions_alert_id_idx ON "meyiconnect"."insight_workflow_executions" ("alert_id")`));
935
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_workflow_executions_status_idx ON "meyiconnect"."insight_workflow_executions" ("status")`));
936
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_workflow_executions" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
937
+ await db.execute(sql.raw(`
938
+ UPDATE "meyiconnect"."insight_workflow_executions" e
939
+ SET "tenant_id" = COALESCE(w."tenant_id", '${defaultTenantSql}')
940
+ FROM "meyiconnect"."insight_workflows" w
941
+ WHERE e."workflow_id" = w."id"
942
+ AND (e."tenant_id" IS NULL OR e."tenant_id" = '')
943
+ `));
944
+ await db.execute(sql.raw(`UPDATE "meyiconnect"."insight_workflow_executions" SET "tenant_id" = '${defaultTenantSql}' WHERE "tenant_id" IS NULL OR "tenant_id" = ''`));
945
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_workflow_executions_tenant_id_idx ON "meyiconnect"."insight_workflow_executions" ("tenant_id")`));
946
+ // ─── insight_alerts pipeline columns (Phase 1) ───────────────────────────────
947
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "fingerprint" TEXT`));
948
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "dedup_key" TEXT`));
949
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "pipeline_status" TEXT DEFAULT 'queued'`));
950
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "group_id" UUID`));
951
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alerts" ADD COLUMN IF NOT EXISTS "raw_payload" JSONB`));
952
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_alerts_pipeline_status_idx ON "meyiconnect"."insight_alerts" ("pipeline_status")`));
953
+ // ─── insight_provider_configs ────────────────────────────────────────────────
954
+ await db.execute(sql.raw(`
955
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_provider_configs" (
956
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
957
+ "tenant_id" TEXT,
958
+ "llm_provider_name" TEXT NOT NULL,
959
+ "llm_model_name" TEXT,
960
+ "llm_model_id" TEXT,
961
+ "llm_api_key" TEXT,
962
+ "is_active" BOOLEAN NOT NULL DEFAULT true,
963
+ "auth_type" TEXT,
964
+ "aws_region" TEXT,
965
+ "aws_access_key_id" TEXT,
966
+ "aws_secret_access_key" TEXT,
967
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
968
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
969
+ )
970
+ `));
971
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_provider_configs_tenant_id_idx ON "meyiconnect"."insight_provider_configs" ("tenant_id")`));
972
+ // ─── insight_normalization_configs ───────────────────────────────────────────
973
+ await db.execute(sql.raw(`
974
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_normalization_configs" (
975
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
976
+ "tenant_id" TEXT,
977
+ "name" TEXT NOT NULL,
978
+ "priority" INTEGER NOT NULL DEFAULT 0,
979
+ "is_active" BOOLEAN NOT NULL DEFAULT true,
980
+ "source_id" UUID REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE CASCADE,
981
+ "source_type" TEXT,
982
+ "field_mapping" TEXT NOT NULL DEFAULT '',
983
+ "yaml_definition" TEXT NOT NULL DEFAULT '',
984
+ "is_system" BOOLEAN NOT NULL DEFAULT false,
985
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
986
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
987
+ )
988
+ `));
989
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_normalization_configs" ADD COLUMN IF NOT EXISTS "is_system" BOOLEAN NOT NULL DEFAULT false`));
990
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_normalization_configs_priority_idx ON "meyiconnect"."insight_normalization_configs" ("priority")`));
991
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_normalization_configs_source_type_idx ON "meyiconnect"."insight_normalization_configs" ("source_type")`));
992
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_normalization_configs" ADD COLUMN IF NOT EXISTS "yaml_definition" TEXT NOT NULL DEFAULT ''`));
993
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_normalization_configs" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
994
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_normalization_configs_tenant_id_idx ON "meyiconnect"."insight_normalization_configs" ("tenant_id")`));
995
+ // ─── insight_dedup_configs ───────────────────────────────────────────────────
996
+ await db.execute(sql.raw(`
997
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_dedup_configs" (
998
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
999
+ "tenant_id" TEXT,
1000
+ "name" TEXT NOT NULL,
1001
+ "priority" INTEGER NOT NULL DEFAULT 0,
1002
+ "is_active" BOOLEAN NOT NULL DEFAULT true,
1003
+ "source_id" UUID REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE CASCADE,
1004
+ "source_type" TEXT,
1005
+ "window_seconds" INTEGER NOT NULL DEFAULT 300,
1006
+ "key_fields" JSONB NOT NULL DEFAULT '[]'::jsonb,
1007
+ "flap_threshold" INTEGER NOT NULL DEFAULT 4,
1008
+ "flap_window_seconds" INTEGER NOT NULL DEFAULT 600,
1009
+ "on_flap" TEXT NOT NULL DEFAULT 'none',
1010
+ "yaml_definition" TEXT NOT NULL DEFAULT '',
1011
+ "is_system" BOOLEAN NOT NULL DEFAULT false,
1012
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1013
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
1014
+ )
1015
+ `));
1016
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_dedup_configs" ADD COLUMN IF NOT EXISTS "is_system" BOOLEAN NOT NULL DEFAULT false`));
1017
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_dedup_configs_priority_idx ON "meyiconnect"."insight_dedup_configs" ("priority")`));
1018
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_dedup_configs_source_type_idx ON "meyiconnect"."insight_dedup_configs" ("source_type")`));
1019
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_dedup_configs" ADD COLUMN IF NOT EXISTS "yaml_definition" TEXT NOT NULL DEFAULT ''`));
1020
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_dedup_configs" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
1021
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_dedup_configs_tenant_id_idx ON "meyiconnect"."insight_dedup_configs" ("tenant_id")`));
1022
+ // ─── insight_grouping_configs ────────────────────────────────────────────────
1023
+ await db.execute(sql.raw(`
1024
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_grouping_configs" (
1025
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
1026
+ "tenant_id" TEXT,
1027
+ "name" TEXT NOT NULL,
1028
+ "priority" INTEGER NOT NULL DEFAULT 0,
1029
+ "is_active" BOOLEAN NOT NULL DEFAULT true,
1030
+ "source_id" UUID REFERENCES "meyiconnect"."insight_sources"("id") ON DELETE CASCADE,
1031
+ "source_type" TEXT,
1032
+ "group_by" JSONB NOT NULL DEFAULT '[]'::jsonb,
1033
+ "timeout_seconds" INTEGER NOT NULL DEFAULT 600,
1034
+ "max_alarms" INTEGER NOT NULL DEFAULT 50,
1035
+ "incident_title_template" TEXT DEFAULT '{{count}} alerts on {{resourceName}}',
1036
+ "use_ai_generate" BOOLEAN NOT NULL DEFAULT false,
1037
+ "yaml_definition" TEXT NOT NULL DEFAULT '',
1038
+ "is_system" BOOLEAN NOT NULL DEFAULT false,
1039
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1040
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
1041
+ )
1042
+ `));
1043
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_grouping_configs" ADD COLUMN IF NOT EXISTS "is_system" BOOLEAN NOT NULL DEFAULT false`));
1044
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_grouping_configs_priority_idx ON "meyiconnect"."insight_grouping_configs" ("priority")`));
1045
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_grouping_configs_source_type_idx ON "meyiconnect"."insight_grouping_configs" ("source_type")`));
1046
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_grouping_configs" ADD COLUMN IF NOT EXISTS "yaml_definition" TEXT NOT NULL DEFAULT ''`));
1047
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_grouping_configs" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
1048
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_grouping_configs_tenant_id_idx ON "meyiconnect"."insight_grouping_configs" ("tenant_id")`));
1049
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_grouping_configs" ADD COLUMN IF NOT EXISTS "use_ai_generate" BOOLEAN NOT NULL DEFAULT false`));
1050
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_grouping_configs" ADD COLUMN IF NOT EXISTS "topology_tag_mapping" TEXT`));
1051
+ // ─── insight_alarm_groups ────────────────────────────────────────────────────
1052
+ await db.execute(sql.raw(`
1053
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_alarm_groups" (
1054
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
1055
+ "tenant_id" TEXT,
1056
+ "group_key" TEXT NOT NULL,
1057
+ "config_id" UUID REFERENCES "meyiconnect"."insight_grouping_configs"("id") ON DELETE SET NULL,
1058
+ "status" TEXT NOT NULL DEFAULT 'buffering',
1059
+ "first_alarm_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1060
+ "last_alarm_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1061
+ "incident_id" UUID,
1062
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1063
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
1064
+ )
1065
+ `));
1066
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_alarm_groups_flush_idx ON "meyiconnect"."insight_alarm_groups" ("status", "last_alarm_at")`));
1067
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_alarm_groups_group_key_idx ON "meyiconnect"."insight_alarm_groups" ("group_key")`));
1068
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_alarm_groups" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
1069
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_alarm_groups_tenant_id_idx ON "meyiconnect"."insight_alarm_groups" ("tenant_id")`));
1070
+ // ─── insight_alarm_group_members (P1 junction table) ─────────────────────────
1071
+ await db.execute(sql.raw(`
1072
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_alarm_group_members" (
1073
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
1074
+ "group_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_alarm_groups"("id") ON DELETE CASCADE,
1075
+ "alarm_id" UUID NOT NULL REFERENCES "meyiconnect"."insight_alerts"("id") ON DELETE CASCADE,
1076
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
1077
+ )
1078
+ `));
1079
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_alarm_group_members_group_id_idx ON "meyiconnect"."insight_alarm_group_members" ("group_id")`));
1080
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_alarm_group_members_alarm_id_idx ON "meyiconnect"."insight_alarm_group_members" ("alarm_id")`));
1081
+ await db.execute(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS insight_alarm_group_members_unique_idx ON "meyiconnect"."insight_alarm_group_members" ("group_id", "alarm_id")`));
1082
+ // ─── insight_pipeline_events ─────────────────────────────────────────────────
1083
+ await db.execute(sql.raw(`
1084
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_pipeline_events" (
1085
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
1086
+ "tenant_id" TEXT,
1087
+ "alarm_id" UUID REFERENCES "meyiconnect"."insight_alerts"("id") ON DELETE CASCADE,
1088
+ "stage" TEXT NOT NULL,
1089
+ "status" TEXT NOT NULL,
1090
+ "matched_config_id" UUID,
1091
+ "meta" JSONB,
1092
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
1093
+ )
1094
+ `));
1095
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_pipeline_events_alarm_id_idx ON "meyiconnect"."insight_pipeline_events" ("alarm_id")`));
1096
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_pipeline_events_stage_idx ON "meyiconnect"."insight_pipeline_events" ("stage")`));
1097
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_pipeline_events_created_at_idx ON "meyiconnect"."insight_pipeline_events" ("created_at")`));
1098
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_pipeline_events" ADD COLUMN IF NOT EXISTS "tenant_id" TEXT`));
1099
+ await db.execute(sql.raw(`CREATE INDEX IF NOT EXISTS insight_pipeline_events_tenant_id_idx ON "meyiconnect"."insight_pipeline_events" ("tenant_id")`));
1100
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_incidents" ADD COLUMN IF NOT EXISTS "description" TEXT`));
1101
+ await db.execute(sql.raw(`ALTER TABLE "meyiconnect"."insight_grouping_configs" ADD COLUMN IF NOT EXISTS "user_prompt" TEXT`));
1102
+ // ─── Seed Pipeline Defaults ──────────────────────────────────────────────────
1103
+ try {
1104
+ const { PipelineDefaults } = await import("../pipeline/defaults/pipelineDefaults.js");
1105
+ for (const [sourceType, configs] of Object.entries(PipelineDefaults)) {
1106
+ if (configs.normalization) {
1107
+ const parsed = yaml.load(configs.normalization.yamlDefinition);
1108
+ const name = parsed.name || `${sourceType} Default Normalization`;
1109
+ await db.execute(sql.raw(`
1110
+ UPDATE "meyiconnect"."insight_normalization_configs"
1111
+ SET "field_mapping" = $token$${configs.normalization.fieldMapping}$token$
1112
+ WHERE "source_type" = '${sourceType}' AND "is_system" = true
1113
+ `));
1114
+ await db.execute(sql.raw(`
1115
+ INSERT INTO "meyiconnect"."insight_normalization_configs"
1116
+ ("name", "priority", "is_active", "source_type", "field_mapping", "yaml_definition", "is_system")
1117
+ SELECT '${name}', 10, true, '${sourceType}', $token$${configs.normalization.fieldMapping}$token$, $token$${configs.normalization.yamlDefinition}$token$, true
1118
+ WHERE NOT EXISTS (
1119
+ SELECT 1 FROM "meyiconnect"."insight_normalization_configs"
1120
+ WHERE "source_type" = '${sourceType}' AND "is_system" = true
1121
+ )
1122
+ `));
1123
+ }
1124
+ if (configs.dedup) {
1125
+ const parsed = yaml.load(configs.dedup.yamlDefinition);
1126
+ const name = parsed.name || `${sourceType} Default Deduplication`;
1127
+ await db.execute(sql.raw(`
1128
+ INSERT INTO "meyiconnect"."insight_dedup_configs"
1129
+ ("name", "priority", "is_active", "source_type", "yaml_definition", "is_system")
1130
+ SELECT '${name}', 10, true, '${sourceType}', $token$${configs.dedup.yamlDefinition}$token$, true
1131
+ WHERE NOT EXISTS (
1132
+ SELECT 1 FROM "meyiconnect"."insight_dedup_configs"
1133
+ WHERE "source_type" = '${sourceType}' AND "is_system" = true
1134
+ )
1135
+ `));
1136
+ }
1137
+ if (configs.grouping) {
1138
+ const parsed = yaml.load(configs.grouping.yamlDefinition);
1139
+ const name = parsed.name || `${sourceType} Default Grouping`;
1140
+ await db.execute(sql.raw(`
1141
+ INSERT INTO "meyiconnect"."insight_grouping_configs"
1142
+ ("name", "priority", "is_active", "source_type", "yaml_definition", "is_system")
1143
+ SELECT '${name}', 10, true, '${sourceType}', $token$${configs.grouping.yamlDefinition}$token$, true
1144
+ WHERE NOT EXISTS (
1145
+ SELECT 1 FROM "meyiconnect"."insight_grouping_configs"
1146
+ WHERE "source_type" = '${sourceType}' AND "is_system" = true
1147
+ )
1148
+ `));
1149
+ }
1150
+ }
1151
+ }
1152
+ catch (err) {
1153
+ console.error("Failed to seed pipeline defaults:", err.message);
1154
+ }
1155
+ // ─── insight_connectors ────────────────────────────────────────────────────
1156
+ // Denormalized connector table written by marketplace-service during
1157
+ // onboarding. insight-server reads from this table for the Connectors page.
1158
+ await db.execute(sql.raw(`
1159
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_connectors" (
1160
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
1161
+ "tenant_id" TEXT NOT NULL,
1162
+ "account_id" TEXT NOT NULL,
1163
+ "account_name" TEXT NOT NULL,
1164
+ "email" TEXT,
1165
+ "access_status" TEXT NOT NULL DEFAULT 'Active',
1166
+ "connection_status" TEXT NOT NULL DEFAULT 'PENDING',
1167
+ "connection_id" TEXT NOT NULL,
1168
+ "management_account_id" TEXT,
1169
+ "external_id" TEXT NOT NULL,
1170
+ "role_name" TEXT NOT NULL,
1171
+ "role_arn" TEXT NOT NULL,
1172
+ "selected" BOOLEAN NOT NULL DEFAULT false,
1173
+ "joined_method" TEXT,
1174
+ "connected_at" TIMESTAMPTZ,
1175
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1176
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
1177
+ )
1178
+ `));
1179
+ await db.execute(sql.raw(`
1180
+ CREATE UNIQUE INDEX IF NOT EXISTS insight_connectors_tenant_account_unique
1181
+ ON "meyiconnect"."insight_connectors" ("tenant_id", "account_id")
1182
+ `));
1183
+ await db.execute(sql.raw(`
1184
+ CREATE INDEX IF NOT EXISTS insight_connectors_tenant_idx
1185
+ ON "meyiconnect"."insight_connectors" ("tenant_id")
1186
+ `));
1187
+ await db.execute(sql.raw(`
1188
+ CREATE INDEX IF NOT EXISTS insight_connectors_connection_idx
1189
+ ON "meyiconnect"."insight_connectors" ("connection_id")
1190
+ `));
1191
+ await db.execute(sql.raw(`
1192
+ CREATE INDEX IF NOT EXISTS insight_connectors_access_status_idx
1193
+ ON "meyiconnect"."insight_connectors" ("access_status")
1194
+ `));
1195
+ // ─── insight_job_queue ────────────────────────────────────────────────────
1196
+ await db.execute(sql.raw(`
1197
+ CREATE TABLE IF NOT EXISTS "meyiconnect"."insight_job_queue" (
1198
+ "id" SERIAL PRIMARY KEY,
1199
+ "job_type" TEXT NOT NULL,
1200
+ "payload" JSONB NOT NULL,
1201
+ "status" TEXT NOT NULL DEFAULT 'pending',
1202
+ "attempts" INTEGER NOT NULL DEFAULT 0,
1203
+ "max_attempts" INTEGER NOT NULL DEFAULT 3,
1204
+ "available_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1205
+ "leased_by" TEXT,
1206
+ "leased_at" TIMESTAMPTZ,
1207
+ "last_error" TEXT,
1208
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1209
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
1210
+ )
1211
+ `));
1212
+ await db.execute(sql.raw(`
1213
+ CREATE INDEX IF NOT EXISTS insight_job_queue_fetch_idx
1214
+ ON "meyiconnect"."insight_job_queue" ("job_type", "status", "available_at")
1215
+ `));
1216
+ // ─── generic tags migration & project_tag removal ─────────────────────────
1217
+ await db.execute(sql.raw(`
1218
+ ALTER TABLE "meyiconnect"."insight_alerts"
1219
+ ADD COLUMN IF NOT EXISTS "tags" JSONB NOT NULL DEFAULT '{}'::jsonb;
1220
+ `));
1221
+ await db.execute(sql.raw(`
1222
+ ALTER TABLE "meyiconnect"."insight_incidents"
1223
+ ADD COLUMN IF NOT EXISTS "tags" JSONB NOT NULL DEFAULT '{}'::jsonb;
1224
+ `));
1225
+ await db.execute(sql.raw(`
1226
+ DO $$
1227
+ BEGIN
1228
+ IF EXISTS (
1229
+ SELECT 1 FROM information_schema.columns
1230
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_alerts' AND column_name = 'project_tag'
1231
+ ) THEN
1232
+ UPDATE "meyiconnect"."insight_alerts"
1233
+ SET "tags" = COALESCE("tags", '{}'::jsonb)
1234
+ || CASE WHEN "additional_configuration"->'tags' IS NOT NULL THEN ("additional_configuration"->'tags') ELSE '{}'::jsonb END
1235
+ || CASE WHEN "project_tag" IS NOT NULL AND "project_tag" != '' THEN jsonb_build_object('project', "project_tag") ELSE '{}'::jsonb END;
1236
+
1237
+ DROP INDEX IF EXISTS "meyiconnect"."insight_alerts_project_tag_idx";
1238
+ ALTER TABLE "meyiconnect"."insight_alerts" DROP COLUMN "project_tag";
1239
+ END IF;
1240
+
1241
+ IF EXISTS (
1242
+ SELECT 1 FROM information_schema.columns
1243
+ WHERE table_schema = 'meyiconnect' AND table_name = 'insight_incidents' AND column_name = 'project_tag'
1244
+ ) THEN
1245
+ UPDATE "meyiconnect"."insight_incidents"
1246
+ SET "tags" = COALESCE("tags", '{}'::jsonb)
1247
+ || CASE WHEN "project_tag" IS NOT NULL AND "project_tag" != '' THEN jsonb_build_object('project', "project_tag") ELSE '{}'::jsonb END;
1248
+
1249
+ ALTER TABLE "meyiconnect"."insight_incidents" DROP COLUMN "project_tag";
1250
+ END IF;
1251
+ END $$;
1252
+ `));
1253
+ await db.execute(sql.raw(`
1254
+ CREATE INDEX IF NOT EXISTS insight_alerts_tags_idx
1255
+ ON "meyiconnect"."insight_alerts" USING GIN ("tags");
1256
+ `));
1257
+ await db.execute(sql.raw(`
1258
+ CREATE INDEX IF NOT EXISTS insight_incidents_tags_idx
1259
+ ON "meyiconnect"."insight_incidents" USING GIN ("tags");
1260
+ `));
1261
+ }
1262
+ //# sourceMappingURL=run.js.map