@oneuptime/common 12.0.27 → 12.0.29

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 (204) hide show
  1. package/Models/AnalyticsModels/RumSessionChunk.ts +52 -0
  2. package/Models/DatabaseModels/CloudResource.ts +17 -1
  3. package/Models/DatabaseModels/DashboardDomain.ts +43 -0
  4. package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
  5. package/Models/DatabaseModels/RumApplication.ts +35 -2
  6. package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
  7. package/Server/API/DashboardDomainAPI.ts +74 -0
  8. package/Server/API/MicrosoftTeamsAPI.ts +81 -180
  9. package/Server/API/StatusPageDomainAPI.ts +74 -0
  10. package/Server/API/TelemetryAPI.ts +104 -15
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  14. package/Server/Middleware/ProjectAuthorization.ts +6 -1
  15. package/Server/Services/DashboardDomainService.ts +131 -0
  16. package/Server/Services/LogPipelineProcessorService.ts +88 -0
  17. package/Server/Services/MonitorTemplateService.ts +70 -11
  18. package/Server/Services/StatusPageDomainService.ts +163 -3
  19. package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
  20. package/Server/Types/Domain.ts +17 -0
  21. package/Server/Utils/APIKey/AccessPermission.ts +24 -0
  22. package/Server/Utils/Greenlock/Greenlock.ts +156 -58
  23. package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
  24. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
  25. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
  26. package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +12 -5
  27. package/Server/Utils/SessionReplay/SessionReplayIdentity.ts +102 -0
  28. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +11 -2
  29. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
  30. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
  31. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
  32. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
  33. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
  34. package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
  35. package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
  36. package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
  37. package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
  38. package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +81 -0
  39. package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
  40. package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
  41. package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
  42. package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
  43. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
  44. package/Tests/Server/API/SessionReplayAPI.test.ts +232 -0
  45. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
  46. package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
  47. package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
  48. package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
  49. package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
  50. package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
  51. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
  52. package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
  53. package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
  54. package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
  55. package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
  56. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
  57. package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
  58. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
  59. package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
  60. package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
  61. package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
  62. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
  63. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
  64. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
  65. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
  66. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
  67. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
  68. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
  69. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
  70. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
  71. package/Tests/Types/API/HTTPResponse.test.ts +175 -0
  72. package/Tests/Types/API/Route.test.ts +36 -4
  73. package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
  74. package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
  75. package/Tests/Types/DateLocalTimeString.test.ts +259 -0
  76. package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
  77. package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
  78. package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
  79. package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
  80. package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
  81. package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
  82. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
  83. package/Tests/UI/Utils/Navigation.test.ts +82 -0
  84. package/Tests/Utils/Array.test.ts +100 -0
  85. package/Tests/Utils/CertificateReissue.test.ts +225 -0
  86. package/Tests/Utils/Grok/Grok.test.ts +492 -0
  87. package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
  88. package/Types/API/Route.ts +20 -5
  89. package/Types/Date.ts +143 -13
  90. package/Types/Rum/SessionReplay.ts +76 -1
  91. package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
  92. package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
  93. package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
  94. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
  95. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
  96. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
  97. package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
  98. package/UI/Utils/Navigation.ts +42 -0
  99. package/Utils/Array.ts +37 -0
  100. package/Utils/CertificateReissue.ts +128 -0
  101. package/Utils/Grok/Grok.ts +461 -0
  102. package/Utils/Grok/GrokPatterns.ts +118 -0
  103. package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
  104. package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +48 -0
  105. package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -1
  106. package/build/dist/Models/DatabaseModels/CloudResource.js +19 -2
  107. package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
  108. package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
  109. package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
  110. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
  111. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  112. package/build/dist/Models/DatabaseModels/RumApplication.js +37 -3
  113. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  114. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
  115. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  116. package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
  117. package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
  118. package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
  119. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  120. package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
  121. package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
  122. package/build/dist/Server/API/TelemetryAPI.js +67 -14
  123. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  124. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
  125. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
  126. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
  127. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js.map +1 -0
  128. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  129. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  130. package/build/dist/Server/Middleware/ProjectAuthorization.js +6 -1
  131. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  132. package/build/dist/Server/Services/DashboardDomainService.js +104 -0
  133. package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
  134. package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
  135. package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
  136. package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
  137. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  138. package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
  139. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  140. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
  141. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
  142. package/build/dist/Server/Types/Domain.js +16 -0
  143. package/build/dist/Server/Types/Domain.js.map +1 -1
  144. package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
  145. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  146. package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
  147. package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
  148. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
  149. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
  150. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
  151. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  152. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
  153. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  154. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
  155. package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js +59 -0
  156. package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js.map +1 -0
  157. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +9 -2
  158. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  159. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
  160. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  161. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
  162. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  163. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
  164. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
  165. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
  166. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  167. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
  168. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  169. package/build/dist/Types/API/Route.js +16 -5
  170. package/build/dist/Types/API/Route.js.map +1 -1
  171. package/build/dist/Types/Date.js +115 -13
  172. package/build/dist/Types/Date.js.map +1 -1
  173. package/build/dist/Types/Rum/SessionReplay.js +38 -0
  174. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  175. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
  176. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
  177. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
  178. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  179. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
  180. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
  181. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
  182. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  183. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
  184. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  185. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
  186. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  187. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
  188. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
  189. package/build/dist/UI/Utils/Navigation.js +30 -0
  190. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  191. package/build/dist/Utils/Array.js +25 -0
  192. package/build/dist/Utils/Array.js.map +1 -1
  193. package/build/dist/Utils/CertificateReissue.js +97 -0
  194. package/build/dist/Utils/CertificateReissue.js.map +1 -0
  195. package/build/dist/Utils/Grok/Grok.js +324 -0
  196. package/build/dist/Utils/Grok/Grok.js.map +1 -0
  197. package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
  198. package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
  199. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
  200. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
  201. package/package.json +1 -1
  202. package/UI/Utils/JsonWebToken.ts +0 -14
  203. package/build/dist/UI/Utils/JsonWebToken.js +0 -10
  204. package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
@@ -16,11 +16,64 @@ import ServerException from "../../../Types/Exception/ServerException";
16
16
  import Text from "../../../Types/Text";
17
17
  import AcmeCertificate from "../../../Models/DatabaseModels/AcmeCertificate";
18
18
  import AcmeChallenge from "../../../Models/DatabaseModels/AcmeChallenge";
19
+ import ArrayUtil from "../../../Utils/Array";
19
20
  import acme from "acme-client";
20
21
  import { Challenge } from "acme-client/types/rfc8555";
21
22
  import CaptureSpan from "../Telemetry/CaptureSpan";
22
23
 
23
24
  export default class GreenlockUtil {
25
+ /*
26
+ * How early a certificate is renewed, as a range rather than a single value.
27
+ *
28
+ * Let's Encrypt issues for 90 days, so every domain ordered on the same day
29
+ * expires on the same day. Renewing them all at one fixed lead time keeps
30
+ * that batch together for every cycle that follows, and a single bad day -
31
+ * an upstream outage, a rate limit, a run that does not finish - then expires
32
+ * the whole batch at once instead of one domain.
33
+ *
34
+ * Spreading the lead time across a range pulls the batch apart: domains that
35
+ * expire on the same day come due days apart, and having been renewed days
36
+ * apart they expire days apart next cycle too. The offset is derived from the
37
+ * domain name so it is stable - a domain must not drift in and out of
38
+ * eligibility between two runs, which would leave it renewed by neither.
39
+ */
40
+ public static readonly RENEW_LEAD_TIME_MAX_IN_DAYS: number = 40;
41
+ public static readonly RENEW_LEAD_TIME_MIN_IN_DAYS: number = 25;
42
+
43
+ /*
44
+ * A run renews at most this many domains, at most this many at a time.
45
+ *
46
+ * Both bound how much of a backlog is dispatched at once. Let's Encrypt
47
+ * allows 300 new orders per account per three hours, and the reactive
48
+ * provisioning sweep spends from the same allowance, so a run that tried to
49
+ * clear a large backlog in one pass would spend the account's budget and get
50
+ * the renewals behind it refused. At the schedule this job runs on the cap
51
+ * still clears far more per day than one sequential daily pass ever did,
52
+ * while leaving room under the limit.
53
+ */
54
+ public static readonly RENEW_MAX_PER_RUN: number = 10;
55
+ public static readonly RENEW_CONCURRENCY: number = 5;
56
+
57
+ /*
58
+ * Stable per-domain lead time, somewhere in the range above. Same domain,
59
+ * same answer, on every run and every replica.
60
+ */
61
+ public static getRenewalLeadTimeInDays(domain: string): number {
62
+ const spanInDays: number =
63
+ GreenlockUtil.RENEW_LEAD_TIME_MAX_IN_DAYS -
64
+ GreenlockUtil.RENEW_LEAD_TIME_MIN_IN_DAYS;
65
+
66
+ let hash: number = 0;
67
+
68
+ for (let i: number = 0; i < domain.length; i++) {
69
+ hash = (hash * 31 + domain.charCodeAt(i)) % 1000003;
70
+ }
71
+
72
+ return (
73
+ GreenlockUtil.RENEW_LEAD_TIME_MIN_IN_DAYS + (hash % (spanInDays + 1))
74
+ );
75
+ }
76
+
24
77
  @CaptureSpan()
25
78
  public static async renewAllCertsWhichAreExpiringSoon(data: {
26
79
  validateCname: (domain: string) => Promise<boolean>;
@@ -29,15 +82,17 @@ export default class GreenlockUtil {
29
82
  try {
30
83
  logger.debug("Renewing all certificates");
31
84
 
32
- // get all certificates which are expiring soon
33
-
85
+ /*
86
+ * Read the widest window any domain's lead time can make it due in, then
87
+ * keep only the domains whose own lead time has actually been reached.
88
+ */
34
89
  const certificates: AcmeCertificate[] =
35
90
  await AcmeCertificateService.findBy({
36
91
  query: {
37
92
  expiresAt: QueryHelper.lessThanEqualTo(
38
93
  OneUptimeDate.addRemoveDays(
39
94
  OneUptimeDate.getCurrentDate(),
40
- 40, // 40 days before expiry
95
+ GreenlockUtil.RENEW_LEAD_TIME_MAX_IN_DAYS,
41
96
  ),
42
97
  ),
43
98
  },
@@ -45,6 +100,7 @@ export default class GreenlockUtil {
45
100
  skip: 0,
46
101
  select: {
47
102
  domain: true,
103
+ expiresAt: true,
48
104
  },
49
105
  sort: {
50
106
  expiresAt: SortOrder.Ascending,
@@ -54,76 +110,118 @@ export default class GreenlockUtil {
54
110
  },
55
111
  });
56
112
 
113
+ const now: Date = OneUptimeDate.getCurrentDate();
114
+
115
+ const dueCertificates: AcmeCertificate[] = certificates.filter(
116
+ (certificate: AcmeCertificate) => {
117
+ if (!certificate.domain || !certificate.expiresAt) {
118
+ return false;
119
+ }
120
+
121
+ const renewAt: Date = OneUptimeDate.addRemoveDays(
122
+ certificate.expiresAt,
123
+ -GreenlockUtil.getRenewalLeadTimeInDays(certificate.domain),
124
+ );
125
+
126
+ return !OneUptimeDate.isAfter(renewAt, now);
127
+ },
128
+ );
129
+
130
+ /*
131
+ * Still sorted by expiry, so a run that cannot take the whole backlog
132
+ * spends itself on the domains closest to expiring and leaves the rest -
133
+ * which by construction still have weeks of lead time - to the next run.
134
+ */
135
+ const batch: AcmeCertificate[] = dueCertificates.slice(
136
+ 0,
137
+ GreenlockUtil.RENEW_MAX_PER_RUN,
138
+ );
139
+
57
140
  logger.debug(
58
- `Found ${certificates.length} certificates which are expiring soon`,
59
- { certificateCount: certificates.length },
141
+ `Found ${dueCertificates.length} certificates due for renewal, renewing ${batch.length} in this run`,
142
+ {
143
+ dueCount: dueCertificates.length,
144
+ batchCount: batch.length,
145
+ },
146
+ );
147
+
148
+ await ArrayUtil.forEachWithConcurrency(
149
+ batch,
150
+ GreenlockUtil.RENEW_CONCURRENCY,
151
+ async (certificate: AcmeCertificate): Promise<void> => {
152
+ await GreenlockUtil.renewCertForDomain({
153
+ domain: certificate.domain as string,
154
+ validateCname: data.validateCname,
155
+ notifyDomainRemoved: data.notifyDomainRemoved,
156
+ });
157
+ },
60
158
  );
159
+ } catch (e) {
160
+ logger.error("Error renewing all certificates");
161
+ logger.error(e);
61
162
 
62
- // order certificate for each domain
163
+ throw e;
164
+ }
165
+ }
63
166
 
64
- for (const certificate of certificates) {
65
- if (!certificate.domain) {
66
- continue;
67
- }
167
+ /*
168
+ * Renew one domain. Never throws: one domain that cannot be renewed - a CNAME
169
+ * that no longer points here, a challenge the CA would not accept - must not
170
+ * take down the rest of the run with it.
171
+ */
172
+ private static async renewCertForDomain(data: {
173
+ domain: string;
174
+ validateCname: (domain: string) => Promise<boolean>;
175
+ notifyDomainRemoved: (domain: string) => Promise<void>;
176
+ }): Promise<void> {
177
+ const { domain } = data;
178
+
179
+ const certLogAttributes: LogAttributes = {
180
+ domain: domain,
181
+ };
68
182
 
69
- const certLogAttributes: LogAttributes = {
70
- domain: certificate.domain,
71
- };
183
+ logger.debug(
184
+ `Renewing certificate for domain: ${domain}`,
185
+ certLogAttributes,
186
+ );
72
187
 
188
+ try {
189
+ //validate cname
190
+ const isValidCname: boolean = await data.validateCname(domain);
191
+
192
+ if (!isValidCname) {
73
193
  logger.debug(
74
- `Renewing certificate for domain: ${certificate.domain}`,
194
+ `CNAME is not valid for domain: ${domain}`,
75
195
  certLogAttributes,
76
196
  );
77
197
 
78
- try {
79
- //validate cname
80
- const isValidCname: boolean = await data.validateCname(
81
- certificate.domain,
82
- );
83
-
84
- if (!isValidCname) {
85
- logger.debug(
86
- `CNAME is not valid for domain: ${certificate.domain}`,
87
- certLogAttributes,
88
- );
89
-
90
- // if cname is not valid then remove the domain
91
- await GreenlockUtil.removeDomain(certificate.domain);
92
- await data.notifyDomainRemoved(certificate.domain);
198
+ // if cname is not valid then remove the domain
199
+ await GreenlockUtil.removeDomain(domain);
200
+ await data.notifyDomainRemoved(domain);
93
201
 
94
- logger.error(
95
- `Cname is not valid for domain: ${certificate.domain}`,
96
- certLogAttributes,
97
- );
98
- } else {
99
- logger.debug(
100
- `CNAME is valid for domain: ${certificate.domain}`,
101
- certLogAttributes,
102
- );
202
+ logger.error(
203
+ `Cname is not valid for domain: ${domain}`,
204
+ certLogAttributes,
205
+ );
206
+ } else {
207
+ logger.debug(`CNAME is valid for domain: ${domain}`, certLogAttributes);
103
208
 
104
- await GreenlockUtil.orderCert({
105
- domain: certificate.domain,
106
- validateCname: data.validateCname,
107
- });
209
+ await GreenlockUtil.orderCert({
210
+ domain: domain,
211
+ validateCname: data.validateCname,
212
+ });
108
213
 
109
- logger.debug(
110
- `Certificate renewed for domain: ${certificate.domain}`,
111
- certLogAttributes,
112
- );
113
- }
114
- } catch (e) {
115
- logger.error(
116
- `Error renewing certificate for domain: ${certificate.domain}`,
117
- certLogAttributes,
118
- );
119
- logger.error(e, certLogAttributes);
120
- }
214
+ logger.debug(
215
+ `Certificate renewed for domain: ${domain}`,
216
+ certLogAttributes,
217
+ );
121
218
  }
122
219
  } catch (e) {
123
- logger.error("Error renewing all certificates");
124
- logger.error(e);
125
-
126
- throw e;
220
+ logger.error(
221
+ `Error renewing certificate for domain: ${domain}`,
222
+ certLogAttributes,
223
+ );
224
+ logger.error(e, certLogAttributes);
127
225
  }
128
226
  }
129
227
 
@@ -0,0 +1,112 @@
1
+ import BadDataException from "../../Types/Exception/BadDataException";
2
+ import { JSONObject } from "../../Types/JSON";
3
+ import LogPipelineProcessorType, {
4
+ GrokParserConfig,
5
+ } from "../../Types/Log/LogPipelineProcessorType";
6
+ import { compileGrokPattern } from "../../Utils/Grok/Grok";
7
+
8
+ /*
9
+ * Save-time validation for log pipeline processors.
10
+ *
11
+ * A processor that cannot do anything is worse than a rejected one: it
12
+ * sits in the pipeline list looking configured while every log flows
13
+ * past it untouched, and nothing surfaces the fact. That is exactly how
14
+ * GrokParser behaved before it was implemented
15
+ * (OneUptime/oneuptime#2515).
16
+ *
17
+ * The grok pattern is the one field where a typo is both easy and
18
+ * invisible - `%{IPV4:client ip}` or a stray bracket compiles to
19
+ * nothing. Compiling it here is cheap, happens once per human edit, and
20
+ * puts the error in front of the only person who can fix it. The ingest
21
+ * path stays defensive anyway: rows saved before this validation
22
+ * existed still fail closed to "leave the log alone".
23
+ */
24
+
25
+ /*
26
+ * Prefixes become the leading part of an attribute key, so hold them to
27
+ * what an attribute key can sensibly be.
28
+ */
29
+ const TARGET_PREFIX_REGEX: RegExp = /^[A-Za-z_][A-Za-z0-9_.@:-]*$/;
30
+
31
+ export interface LogPipelineProcessorCandidate {
32
+ processorType: string | undefined | null;
33
+ configuration: JSONObject | string | undefined | null;
34
+ }
35
+
36
+ /*
37
+ * `configuration` is a jsonb column, but the dashboard's JSON form field
38
+ * has historically persisted it as a JSON string literal - so both
39
+ * shapes reach here (see LogPipelineService.normalizeProcessorConfig).
40
+ */
41
+ function readConfiguration(
42
+ configuration: JSONObject | string | undefined | null,
43
+ ): JSONObject | null {
44
+ if (configuration && typeof configuration === "object") {
45
+ return configuration as JSONObject;
46
+ }
47
+
48
+ if (typeof configuration === "string") {
49
+ try {
50
+ const parsed: unknown = JSON.parse(configuration);
51
+
52
+ if (parsed && typeof parsed === "object") {
53
+ return parsed as JSONObject;
54
+ }
55
+ } catch {
56
+ throw new BadDataException("Processor configuration is not valid JSON.");
57
+ }
58
+ }
59
+
60
+ return null;
61
+ }
62
+
63
+ export function validateLogPipelineProcessor(
64
+ candidate: LogPipelineProcessorCandidate,
65
+ ): void {
66
+ if (candidate.processorType !== LogPipelineProcessorType.GrokParser) {
67
+ return;
68
+ }
69
+
70
+ const configuration: JSONObject | null = readConfiguration(
71
+ candidate.configuration,
72
+ );
73
+
74
+ if (!configuration) {
75
+ throw new BadDataException(
76
+ "A Grok Parser processor needs a configuration with a grok pattern.",
77
+ );
78
+ }
79
+
80
+ const config: GrokParserConfig = configuration as unknown as GrokParserConfig;
81
+ const pattern: string =
82
+ typeof config.pattern === "string" ? config.pattern.trim() : "";
83
+
84
+ if (!pattern) {
85
+ throw new BadDataException(
86
+ "A Grok Parser processor needs a grok pattern, for example: %{IPV4:client_ip} %{WORD:verb}",
87
+ );
88
+ }
89
+
90
+ // Throws BadDataException with a message written for the person editing.
91
+ compileGrokPattern(pattern);
92
+
93
+ if (config.targetPrefix !== undefined && config.targetPrefix !== null) {
94
+ if (typeof config.targetPrefix !== "string") {
95
+ throw new BadDataException("Target prefix must be text.");
96
+ }
97
+
98
+ const targetPrefix: string = config.targetPrefix.trim();
99
+
100
+ if (targetPrefix && !TARGET_PREFIX_REGEX.test(targetPrefix)) {
101
+ throw new BadDataException(
102
+ `"${config.targetPrefix}" is not a valid target prefix. Use letters, digits, and . _ - : @ (starting with a letter or underscore).`,
103
+ );
104
+ }
105
+ }
106
+
107
+ if (config.source !== undefined && config.source !== null) {
108
+ if (typeof config.source !== "string") {
109
+ throw new BadDataException("Source field must be text.");
110
+ }
111
+ }
112
+ }