(true)
)
@@ -346,6 +348,7 @@ export const createRecords = (zoneId: string): Record[] => {
path: `dns/${primaryDomain}.ts`,
message: `Update ${primaryDomain}.ts`,
content: Buffer.from(template).toString("base64"),
+ branch: branchName,
sha,
}),
(error) => {
@@ -369,6 +372,7 @@ export const createRecords = (zoneId: string): Record[] => {
path: `dns/${primaryDomain}.ts`,
message: `Create ${primaryDomain}.ts`,
content: Buffer.from(template).toString("base64"),
+ branch: branchName,
}),
(error) => {
logger.error(
diff --git a/src/services/utilServices/SendDNSRecordEmailClient.ts b/src/services/utilServices/SendDNSRecordEmailClient.ts
index 0218b669d..815591988 100644
--- a/src/services/utilServices/SendDNSRecordEmailClient.ts
+++ b/src/services/utilServices/SendDNSRecordEmailClient.ts
@@ -5,7 +5,6 @@ import { DigType } from "@root/types/dig"
export interface DigDNSRecord {
domain: string
- class: string
type: DigType
value: string
}
@@ -21,7 +20,8 @@ export interface DnsRecordsEmailProps {
redirectionDomainSource?: string
redirectionDomainTarget?: string
quadARecords?: DigDNSRecord[]
- addCAARecord?: boolean
+ addLetsEncryptCAA?: boolean
+ addAWSACMCertCAA?: boolean
}
export interface LaunchFailureEmailProps {
@@ -115,7 +115,7 @@ export function getDNSRecordsEmailBody(
Object.keys(groupedDnsRecords).forEach((repoName) => {
groupedDnsRecords[repoName].forEach((dnsRecord) => {
- if (dnsRecord.addCAARecord) {
+ if (dnsRecord.addAWSACMCertCAA || dnsRecord.addLetsEncryptCAA) {
html += `Please add CAA records for the following repo: ${repoName}.`
html += `
@@ -128,7 +128,10 @@ export function getDNSRecordsEmailBody(
Value |
-
+ `
+
+ if (dnsRecord.addAWSACMCertCAA) {
+ html += `
${repoName} |
${dnsRecord.primaryDomainSource} |
@@ -142,7 +145,25 @@ export function getDNSRecordsEmailBody(
issuewild |
amazontrust.com |
-
+ `
+ }
+ if (dnsRecord.addLetsEncryptCAA) {
+ html += `
+
+ ${repoName} |
+ ${dnsRecord.primaryDomainSource} |
+ 0 |
+ issue |
+ letsencrypt.org |
+
+
+ ${dnsRecord.primaryDomainSource} |
+ 0 |
+ issuewild |
+ letsencrypt.org |
+
`
+ }
+ html += `
`
}
})
@@ -174,7 +195,6 @@ export function getDNSRecordsEmailBody(
html += `
${record.domain} |
- ${record.class} |
${record.type} |
${record.value} |
`
diff --git a/src/types/dig.ts b/src/types/dig.ts
index 9cf4f352e..2588febbf 100644
--- a/src/types/dig.ts
+++ b/src/types/dig.ts
@@ -1,26 +1 @@
-export type DigResponse = {
- question: string[][]
- answer?: {
- domain: string
- ttl: string
- class: string
- type: DigType
- value: string
- }[]
- time?: number
- server?: string
- datetime?: string
- size?: number
-}
-
-export type DigType =
- | "A"
- | "AAAA"
- | "CNAME"
- | "MX"
- | "NS"
- | "PTR"
- | "SOA"
- | "SRV"
- | "TXT"
- | "CAA"
+export type DigType = "AAAA" | "CAA"
diff --git a/src/types/node-dig-dns/node-dig-dns.d.ts b/src/types/node-dig-dns/node-dig-dns.d.ts
deleted file mode 100644
index d14bacbf4..000000000
--- a/src/types/node-dig-dns/node-dig-dns.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-declare module "node-dig-dns"
diff --git a/src/types/nodeError.ts b/src/types/nodeError.ts
new file mode 100644
index 000000000..fd9765fbb
--- /dev/null
+++ b/src/types/nodeError.ts
@@ -0,0 +1,11 @@
+/* eslint-disable import/prefer-default-export */
+export function isErrnoException(obj: unknown): obj is NodeJS.ErrnoException {
+ const err = obj as NodeJS.ErrnoException
+ return (
+ err &&
+ (typeof err.errno === "number" || err.errno === undefined) &&
+ (typeof err.code === "string" || err.code === undefined) &&
+ (typeof err.path === "string" || err.path === undefined) &&
+ (typeof err.syscall === "string" || err.syscall === undefined)
+ )
+}
diff --git a/src/types/siteLaunch.ts b/src/types/siteLaunch.ts
index 90dc5a9e0..0965e7e8f 100644
--- a/src/types/siteLaunch.ts
+++ b/src/types/siteLaunch.ts
@@ -40,6 +40,19 @@ export interface SiteLaunchMessage {
statusMetadata?: string
}
+export type SiteLaunchResult = Omit<
+ SiteLaunchMessage,
+ | "status"
+ | "statusMetadata"
+ | "redirectionDomain"
+ | "appId"
+ | "requestorEmail"
+ | "agencyEmail"
+> & {
+ redirectionDomainSource?: string
+ redirectionDomainTarget?: string
+}
+
export function isSiteLaunchMessage(obj: unknown): obj is SiteLaunchMessage {
if (!obj) {
return false
From a453b9f7d6ada354a007352526ee636b3baa3d62 Mon Sep 17 00:00:00 2001
From: Timothee Groleau
Date: Thu, 4 Apr 2024 11:20:10 +0800
Subject: [PATCH 3/6] fix: remove unecessary join and site retrieval (#1268)
---
src/services/identity/NotificationsService.ts | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/src/services/identity/NotificationsService.ts b/src/services/identity/NotificationsService.ts
index fa4261c4c..695e9a810 100644
--- a/src/services/identity/NotificationsService.ts
+++ b/src/services/identity/NotificationsService.ts
@@ -228,6 +228,7 @@ class NotificationsService {
const recentTargetNotification = await this.repository.findOne({
where: {
user_id: siteMember.userId,
+ site_id: siteMember.siteId,
type: notificationType,
created_at: {
[Op.gte]: getNotificationExpiryDate(notificationType),
@@ -235,16 +236,6 @@ class NotificationsService {
link,
source_username: notificationSourceUsername,
},
- include: [
- {
- model: Site,
- as: "site",
- required: true,
- where: {
- id: siteMember.siteId,
- },
- },
- ],
})
if (recentTargetNotification) {
From ccde289f6dfd31542f5f5857fc8431a1bc9ab47b Mon Sep 17 00:00:00 2001
From: Timothee Groleau
Date: Thu, 4 Apr 2024 13:04:54 +0800
Subject: [PATCH 4/6] Improve APM spans (no more ) (#1267)
* refactor: rename wrapper based on original function, only when original.name exists
* feat: utility to name all methods of an object
* feat: ensure all route handlers are named
* feat: drop the bound prefix in span names
* chore: remove unused eslint rule disabling
---------
Co-authored-by: Alexander Lee
---
src/middleware/routeHandler.ts | 19 ++++++++------
src/routes/formsg/formsgSiteCreation.ts | 2 ++
src/routes/formsg/formsgSiteLaunch.ts | 2 ++
src/routes/v2/authenticated/collaborators.ts | 2 ++
src/routes/v2/authenticated/notifications.ts | 2 ++
src/routes/v2/authenticated/review.ts | 2 ++
src/routes/v2/authenticated/sites.ts | 2 ++
src/routes/v2/authenticated/users.ts | 2 ++
src/routes/v2/authenticatedSites/media.ts | 2 ++
.../v2/authenticatedSites/repoManagement.ts | 2 ++
src/routes/v2/sgidAuth.ts | 2 ++
src/utils/apm-utils.ts | 25 +++++++++++++++++++
12 files changed, 57 insertions(+), 7 deletions(-)
create mode 100644 src/utils/apm-utils.ts
diff --git a/src/middleware/routeHandler.ts b/src/middleware/routeHandler.ts
index da27016b9..3fdc60025 100644
--- a/src/middleware/routeHandler.ts
+++ b/src/middleware/routeHandler.ts
@@ -83,11 +83,16 @@ const nameRouteHandlerWrapper = <
Params = Record,
Locals extends Record = Record
>(
- func: RequestHandler,
- name: string
+ wrapper: RequestHandler,
+ original: { name: string }
): RequestHandler => {
- Object.defineProperty(func, "name", { value: name, writable: false })
- return func
+ if (original.name) {
+ Object.defineProperty(wrapper, "name", {
+ value: original.name.replace(/^bound /, ""),
+ writable: false,
+ })
+ }
+ return wrapper
}
// Used when there are no write API calls to the repo on GitHub
@@ -96,7 +101,7 @@ export const attachReadRouteHandlerWrapper: RouteWrapper = (routeHandler) =>
Promise.resolve(routeHandler(req, res, next)).catch((err: Error) => {
next(err)
})
- }, routeHandler.name)
+ }, routeHandler)
// Used when there are write API calls to the repo on GitHub
export const attachWriteRouteHandlerWrapper: RouteWrapper<{
@@ -135,7 +140,7 @@ export const attachWriteRouteHandlerWrapper: RouteWrapper<{
next(err)
}
)
- }, routeHandler.name)
+ }, routeHandler)
export const attachRollbackRouteHandlerWrapper: RouteWrapper<
{
@@ -340,4 +345,4 @@ export const attachRollbackRouteHandlerWrapper: RouteWrapper<
next(err)
}
)
- }, routeHandler.name)
+ }, routeHandler)
diff --git a/src/routes/formsg/formsgSiteCreation.ts b/src/routes/formsg/formsgSiteCreation.ts
index a202a22ef..22252ed5a 100644
--- a/src/routes/formsg/formsgSiteCreation.ts
+++ b/src/routes/formsg/formsgSiteCreation.ts
@@ -11,6 +11,7 @@ import { BadRequestError } from "@errors/BadRequestError"
import { getField } from "@utils/formsg-utils"
import { attachFormSGHandler } from "@root/middleware"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import GitFileSystemService from "@services/db/GitFileSystemService"
import UsersService from "@services/identity/UsersService"
import InfraService from "@services/infra/InfraService"
@@ -45,6 +46,7 @@ export class FormsgSiteCreateRouter {
this.infraService = infraService
this.gitFileSystemService = gitFileSystemService
// We need to bind all methods because we don't invoke them from the class directly
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/routes/formsg/formsgSiteLaunch.ts b/src/routes/formsg/formsgSiteLaunch.ts
index 261479198..ac0ab6af3 100644
--- a/src/routes/formsg/formsgSiteLaunch.ts
+++ b/src/routes/formsg/formsgSiteLaunch.ts
@@ -27,6 +27,7 @@ import {
import TRUSTED_AMPLIFY_CAA_RECORDS from "@root/types/caaAmplify"
import { isErrnoException } from "@root/types/nodeError"
import { SiteLaunchResult } from "@root/types/siteLaunch"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import UsersService from "@services/identity/UsersService"
import InfraService from "@services/infra/InfraService"
@@ -150,6 +151,7 @@ export class FormsgSiteLaunchRouter {
this.usersService = usersService
this.infraService = infraService
// We need to bind all methods because we don't invoke them from the class directly
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/routes/v2/authenticated/collaborators.ts b/src/routes/v2/authenticated/collaborators.ts
index 82ffaa9dd..f6a434e90 100644
--- a/src/routes/v2/authenticated/collaborators.ts
+++ b/src/routes/v2/authenticated/collaborators.ts
@@ -13,6 +13,7 @@ import { attachSiteHandler } from "@root/middleware"
import { RouteCheckerMiddleware } from "@root/middleware/routeChecker"
import { RequestHandler } from "@root/types"
import { UserDto } from "@root/types/dto/review"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import { CreateCollaboratorRequestSchema } from "@root/validators/RequestSchema"
import CollaboratorsService from "@services/identity/CollaboratorsService"
@@ -33,6 +34,7 @@ export class CollaboratorsRouter {
}: CollaboratorsRouterProps) {
this.collaboratorsService = collaboratorsService
this.authorizationMiddleware = authorizationMiddleware
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/routes/v2/authenticated/notifications.ts b/src/routes/v2/authenticated/notifications.ts
index 832d54d4c..0d2eac85c 100644
--- a/src/routes/v2/authenticated/notifications.ts
+++ b/src/routes/v2/authenticated/notifications.ts
@@ -10,6 +10,7 @@ import UserWithSiteSessionData from "@root/classes/UserWithSiteSessionData"
import { attachSiteHandler } from "@root/middleware"
import { AuthorizationMiddleware } from "@root/middleware/authorization"
import { RequestHandler } from "@root/types"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import NotificationsService, {
NotificationResponse,
} from "@services/identity/NotificationsService"
@@ -31,6 +32,7 @@ export class NotificationsRouter {
}: NotificationsRouterProps) {
this.notificationsService = notificationsService
this.authorizationMiddleware = authorizationMiddleware
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/routes/v2/authenticated/review.ts b/src/routes/v2/authenticated/review.ts
index e4e448954..31dd975b4 100644
--- a/src/routes/v2/authenticated/review.ts
+++ b/src/routes/v2/authenticated/review.ts
@@ -31,6 +31,7 @@ import {
BlobDiffDto,
EditedItemDto,
} from "@root/types/dto/review"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import {
CreateCommentSchema,
CreateReviewRequestSchema,
@@ -66,6 +67,7 @@ export class ReviewsRouter {
this.notificationsService = notificationsService
this.githubService = githubService
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/routes/v2/authenticated/sites.ts b/src/routes/v2/authenticated/sites.ts
index 8aeafc5df..2a46eb1e2 100644
--- a/src/routes/v2/authenticated/sites.ts
+++ b/src/routes/v2/authenticated/sites.ts
@@ -21,6 +21,7 @@ import { RepositoryData } from "@root/types/repoInfo"
import { BrokenLinkErrorDto } from "@root/types/siteChecker"
import { SiteInfo, SiteLaunchDto } from "@root/types/siteInfo"
import { StagingBuildStatus } from "@root/types/stagingBuildStatus"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import {
GetPreviewInfoSchema,
LaunchSiteSchema,
@@ -60,6 +61,7 @@ export class SitesRouter {
this.infraService = infraService
this.repoCheckerService = repoCheckerService
// We need to bind all methods because we don't invoke them from the class directly
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/routes/v2/authenticated/users.ts b/src/routes/v2/authenticated/users.ts
index a77974edb..e8d6c956e 100644
--- a/src/routes/v2/authenticated/users.ts
+++ b/src/routes/v2/authenticated/users.ts
@@ -13,6 +13,7 @@ import UserSessionData from "@classes/UserSessionData"
import DatabaseError from "@root/errors/DatabaseError"
import { isError, RequestHandler } from "@root/types"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import {
VerifyEmailOtpSchema,
VerifyMobileNumberOtpSchema,
@@ -29,6 +30,7 @@ export class UsersRouter {
constructor({ usersService }: UsersRouterProps) {
this.usersService = usersService
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/routes/v2/authenticatedSites/media.ts b/src/routes/v2/authenticatedSites/media.ts
index 5f20c61e1..12a0890d2 100644
--- a/src/routes/v2/authenticatedSites/media.ts
+++ b/src/routes/v2/authenticatedSites/media.ts
@@ -17,6 +17,7 @@ import type {
MediaFileOutput,
RequestHandler,
} from "@root/types"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import {
CreateMediaDirectoryRequestSchema,
CreateMediaFileRequestSchema,
@@ -44,6 +45,7 @@ export class MediaRouter {
this.mediaFileService = mediaFileService
this.mediaDirectoryService = mediaDirectoryService
// We need to bind all methods because we don't invoke them from the class directly
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/routes/v2/authenticatedSites/repoManagement.ts b/src/routes/v2/authenticatedSites/repoManagement.ts
index 294fb5a85..c17e82e0b 100644
--- a/src/routes/v2/authenticatedSites/repoManagement.ts
+++ b/src/routes/v2/authenticatedSites/repoManagement.ts
@@ -11,6 +11,7 @@ import UserWithSiteSessionData from "@root/classes/UserWithSiteSessionData"
import GitFileSystemError from "@root/errors/GitFileSystemError"
import { attachSiteHandler } from "@root/middleware"
import type { RequestHandler } from "@root/types"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import { ResetRepoSchema } from "@root/validators/RequestSchema"
import RepoManagementService from "@services/admin/RepoManagementService"
@@ -30,6 +31,7 @@ export class RepoManagementRouter {
}: RepoManagementRouterProps) {
this.repoManagementService = repoManagementService
this.authorizationMiddleware = authorizationMiddleware
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/routes/v2/sgidAuth.ts b/src/routes/v2/sgidAuth.ts
index a1766794b..620121032 100644
--- a/src/routes/v2/sgidAuth.ts
+++ b/src/routes/v2/sgidAuth.ts
@@ -19,6 +19,7 @@ import {
import { RequestHandler } from "@root/types"
import { ResponseErrorBody } from "@root/types/dto/error"
import { isPublicOfficerData, PublicOfficerData } from "@root/types/sgid"
+import { nameAnonymousMethods } from "@root/utils/apm-utils"
import { isSecure } from "@root/utils/auth-utils"
import { EmailSchema } from "@root/validators/RequestSchema"
import UsersService from "@services/identity/UsersService"
@@ -42,6 +43,7 @@ export class SgidAuthRouter {
constructor({ usersService, sgidAuthService }: SgidAuthRouterProps) {
this.usersService = usersService
this.sgidAuthService = sgidAuthService
+ nameAnonymousMethods(this)
autoBind(this)
}
diff --git a/src/utils/apm-utils.ts b/src/utils/apm-utils.ts
new file mode 100644
index 000000000..f26fb8a25
--- /dev/null
+++ b/src/utils/apm-utils.ts
@@ -0,0 +1,25 @@
+/* eslint-disable import/prefer-default-export */
+/**
+ * A function to set the name of anonymous methods on an object, all the way into the prototype chain
+ * This is useful to be reported in APM traces and spans
+ * This is an unconventional thing to do, so we have to disable a couple of eslint rules
+ */
+export const nameAnonymousMethods = (
+ self: SelfType
+): SelfType => {
+ /* eslint-disable no-restricted-syntax */
+ /* eslint-disable no-continue */
+ for (const methodName in self) {
+ if (methodName === "constructor") continue
+
+ const method = self[methodName]
+ if (typeof method !== "function") continue
+ if (method.name) continue
+
+ Object.defineProperty(method, "name", {
+ value: methodName,
+ writable: false,
+ })
+ }
+ return self
+}
From 3043f0fbb9ab8e4228833d78003789e2d0917f92 Mon Sep 17 00:00:00 2001
From: Alexander Lee
Date: Thu, 4 Apr 2024 13:30:24 +0800
Subject: [PATCH 5/6] fix: external links in top level nav (#1272)
---
src/validators/RequestSchema.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/validators/RequestSchema.js b/src/validators/RequestSchema.js
index 2e5476049..9040f2503 100644
--- a/src/validators/RequestSchema.js
+++ b/src/validators/RequestSchema.js
@@ -382,6 +382,7 @@ const UpdateNavigationRequestSchema = Joi.object().keys({
})
),
false_collection: Joi.boolean(),
+ external: Joi.boolean(),
})
.oxor("url", "collection", "resource_room")
.oxor("sublinks", "collection")
From ef627d5a2383033cd903d4bf6ebc7beaa6dd23f7 Mon Sep 17 00:00:00 2001
From: Alexander Lee
Date: Thu, 4 Apr 2024 13:32:34 +0800
Subject: [PATCH 6/6] chore: bump version to v0.76.0
---
CHANGELOG.md | 12 ++++++++++++
package-lock.json | 4 ++--
package.json | 2 +-
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 768b4dcdd..2c2ab26cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,11 +4,23 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
+#### [v0.76.0](https://github.com/isomerpages/isomercms-backend/compare/v0.75.0...v0.76.0)
+
+- fix: external links in top level nav [`#1272`](https://github.com/isomerpages/isomercms-backend/pull/1272)
+- Improve APM spans (no more <anonymous>) [`#1267`](https://github.com/isomerpages/isomercms-backend/pull/1267)
+- fix: remove unecessary join and site retrieval [`#1268`](https://github.com/isomerpages/isomercms-backend/pull/1268)
+- fix(dig): dig not working [`#1246`](https://github.com/isomerpages/isomercms-backend/pull/1246)
+- fix(server): server should die if unable to connect to db [`#1265`](https://github.com/isomerpages/isomercms-backend/pull/1265)
+- backport v0.75.0 [`#1264`](https://github.com/isomerpages/isomercms-backend/pull/1264)
+
#### [v0.75.0](https://github.com/isomerpages/isomercms-backend/compare/v0.74.0...v0.75.0)
+> 2 April 2024
+
- feat: carry the name of the wrapped handler [`#1260`](https://github.com/isomerpages/isomercms-backend/pull/1260)
- fix: only release the lock after the handler is done [`#1259`](https://github.com/isomerpages/isomercms-backend/pull/1259)
- backport v0.74.0 [`#1258`](https://github.com/isomerpages/isomercms-backend/pull/1258)
+- chore: bump version to v0.75.0 [`b138978`](https://github.com/isomerpages/isomercms-backend/commit/b138978086136e15f42d7fc9484118ea835b42cc)
#### [v0.74.0](https://github.com/isomerpages/isomercms-backend/compare/v0.73.0...v0.74.0)
diff --git a/package-lock.json b/package-lock.json
index cd3b5f831..4785e50ae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "isomercms",
- "version": "0.75.0",
+ "version": "0.76.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "isomercms",
- "version": "0.75.0",
+ "version": "0.76.0",
"hasInstallScript": true,
"dependencies": {
"@aws-sdk/client-amplify": "^3.521.0",
diff --git a/package.json b/package.json
index b9a723bbb..8fc5b966c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "isomercms",
- "version": "0.75.0",
+ "version": "0.76.0",
"private": true,
"scripts": {
"build": "tsc -p tsconfig.build.json",