@infoxchange/make-it-so-sst-v2 3.0.1

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 (99) hide show
  1. package/.editorconfig +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +377 -0
  4. package/commitlint.config.ts +14 -0
  5. package/dist/cdk-constructs/IxApi.d.ts +12 -0
  6. package/dist/cdk-constructs/IxApi.d.ts.map +1 -0
  7. package/dist/cdk-constructs/IxApi.js +56 -0
  8. package/dist/cdk-constructs/IxBucket.d.ts +9 -0
  9. package/dist/cdk-constructs/IxBucket.d.ts.map +1 -0
  10. package/dist/cdk-constructs/IxBucket.js +22 -0
  11. package/dist/cdk-constructs/IxCertificate.d.ts +16 -0
  12. package/dist/cdk-constructs/IxCertificate.d.ts.map +1 -0
  13. package/dist/cdk-constructs/IxCertificate.js +26 -0
  14. package/dist/cdk-constructs/IxDnsRecord.d.ts +23 -0
  15. package/dist/cdk-constructs/IxDnsRecord.d.ts.map +1 -0
  16. package/dist/cdk-constructs/IxDnsRecord.js +43 -0
  17. package/dist/cdk-constructs/IxElasticache.d.ts +17 -0
  18. package/dist/cdk-constructs/IxElasticache.d.ts.map +1 -0
  19. package/dist/cdk-constructs/IxElasticache.js +70 -0
  20. package/dist/cdk-constructs/IxNextjsSite.d.ts +16 -0
  21. package/dist/cdk-constructs/IxNextjsSite.d.ts.map +1 -0
  22. package/dist/cdk-constructs/IxNextjsSite.js +38 -0
  23. package/dist/cdk-constructs/IxQuicksightWorkspace.d.ts +17 -0
  24. package/dist/cdk-constructs/IxQuicksightWorkspace.d.ts.map +1 -0
  25. package/dist/cdk-constructs/IxQuicksightWorkspace.js +29 -0
  26. package/dist/cdk-constructs/IxSESIdentity.d.ts +12 -0
  27. package/dist/cdk-constructs/IxSESIdentity.d.ts.map +1 -0
  28. package/dist/cdk-constructs/IxSESIdentity.js +45 -0
  29. package/dist/cdk-constructs/IxStaticSite.d.ts +17 -0
  30. package/dist/cdk-constructs/IxStaticSite.d.ts.map +1 -0
  31. package/dist/cdk-constructs/IxStaticSite.js +38 -0
  32. package/dist/cdk-constructs/IxVpcDetails.d.ts +12 -0
  33. package/dist/cdk-constructs/IxVpcDetails.d.ts.map +1 -0
  34. package/dist/cdk-constructs/IxVpcDetails.js +26 -0
  35. package/dist/cdk-constructs/IxWebsiteRedirect.d.ts +35 -0
  36. package/dist/cdk-constructs/IxWebsiteRedirect.d.ts.map +1 -0
  37. package/dist/cdk-constructs/IxWebsiteRedirect.js +72 -0
  38. package/dist/cdk-constructs/SiteOidcAuth/auth-check-handler-body.d.ts +2 -0
  39. package/dist/cdk-constructs/SiteOidcAuth/auth-check-handler-body.d.ts.map +1 -0
  40. package/dist/cdk-constructs/SiteOidcAuth/auth-check-handler-body.js +130 -0
  41. package/dist/cdk-constructs/SiteOidcAuth/auth-route.d.ts +2 -0
  42. package/dist/cdk-constructs/SiteOidcAuth/auth-route.d.ts.map +1 -0
  43. package/dist/cdk-constructs/SiteOidcAuth/auth-route.js +59 -0
  44. package/dist/cdk-constructs/SiteOidcAuth/index.d.ts +197 -0
  45. package/dist/cdk-constructs/SiteOidcAuth/index.d.ts.map +1 -0
  46. package/dist/cdk-constructs/SiteOidcAuth/index.js +188 -0
  47. package/dist/cdk-constructs/index.d.ts +11 -0
  48. package/dist/cdk-constructs/index.d.ts.map +1 -0
  49. package/dist/cdk-constructs/index.js +10 -0
  50. package/dist/deployConfig.d.ts +72 -0
  51. package/dist/deployConfig.d.ts.map +1 -0
  52. package/dist/deployConfig.js +78 -0
  53. package/dist/lib/auth/index.d.ts +2 -0
  54. package/dist/lib/auth/index.d.ts.map +1 -0
  55. package/dist/lib/auth/index.js +1 -0
  56. package/dist/lib/auth/oidc.d.ts +26 -0
  57. package/dist/lib/auth/oidc.d.ts.map +1 -0
  58. package/dist/lib/auth/oidc.js +48 -0
  59. package/dist/lib/proxy/fetch.d.ts +4 -0
  60. package/dist/lib/proxy/fetch.d.ts.map +1 -0
  61. package/dist/lib/proxy/fetch.js +31 -0
  62. package/dist/lib/proxy/index.d.ts +2 -0
  63. package/dist/lib/proxy/index.d.ts.map +1 -0
  64. package/dist/lib/proxy/index.js +1 -0
  65. package/dist/lib/site/support.d.ts +71 -0
  66. package/dist/lib/site/support.d.ts.map +1 -0
  67. package/dist/lib/site/support.js +262 -0
  68. package/dist/lib/utils/hash.d.ts +2 -0
  69. package/dist/lib/utils/hash.d.ts.map +1 -0
  70. package/dist/lib/utils/hash.js +13 -0
  71. package/dist/lib/utils/objects.d.ts +4 -0
  72. package/dist/lib/utils/objects.d.ts.map +1 -0
  73. package/dist/lib/utils/objects.js +7 -0
  74. package/eslint.config.js +11 -0
  75. package/package.json +66 -0
  76. package/src/cdk-constructs/IxApi.ts +81 -0
  77. package/src/cdk-constructs/IxBucket.ts +35 -0
  78. package/src/cdk-constructs/IxCertificate.ts +54 -0
  79. package/src/cdk-constructs/IxDnsRecord.ts +79 -0
  80. package/src/cdk-constructs/IxElasticache.ts +106 -0
  81. package/src/cdk-constructs/IxNextjsSite.ts +72 -0
  82. package/src/cdk-constructs/IxQuicksightWorkspace.ts +54 -0
  83. package/src/cdk-constructs/IxSESIdentity.ts +70 -0
  84. package/src/cdk-constructs/IxStaticSite.ts +69 -0
  85. package/src/cdk-constructs/IxVpcDetails.ts +38 -0
  86. package/src/cdk-constructs/IxWebsiteRedirect.ts +133 -0
  87. package/src/cdk-constructs/SiteOidcAuth/auth-check-handler-body.ts +168 -0
  88. package/src/cdk-constructs/SiteOidcAuth/auth-route.ts +71 -0
  89. package/src/cdk-constructs/SiteOidcAuth/index.ts +299 -0
  90. package/src/cdk-constructs/index.ts +10 -0
  91. package/src/deployConfig.ts +87 -0
  92. package/src/lib/auth/index.ts +1 -0
  93. package/src/lib/auth/oidc.ts +73 -0
  94. package/src/lib/proxy/fetch.ts +41 -0
  95. package/src/lib/proxy/index.ts +1 -0
  96. package/src/lib/site/support.ts +439 -0
  97. package/src/lib/utils/hash.ts +14 -0
  98. package/src/lib/utils/objects.ts +19 -0
  99. package/tsconfig.json +9 -0
@@ -0,0 +1,4 @@
1
+ export declare function remapKeys<SourceObject extends object, MapObject extends Record<keyof SourceObject, string>>(object: SourceObject, keyMap: Readonly<MapObject>): {
2
+ [k in keyof SourceObject as k extends keyof MapObject ? MapObject[k] : k]: SourceObject[k];
3
+ };
4
+ //# sourceMappingURL=objects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/objects.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CACvB,YAAY,SAAS,MAAM,EAC3B,SAAS,SAAS,MAAM,CAAC,MAAM,YAAY,EAAE,MAAM,CAAC,EAEpD,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,GAC1B;KACA,CAAC,IAAI,MAAM,YAAY,IAAI,CAAC,SAAS,MAAM,SAAS,GACjD,SAAS,CAAC,CAAC,CAAC,GACZ,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;CACxB,CAQA"}
@@ -0,0 +1,7 @@
1
+ export function remapKeys(object, keyMap) {
2
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => {
3
+ // @ts-expect-error the typing for map() reduces keys to general string
4
+ const newKey = keyMap[key] ?? key;
5
+ return [newKey, value];
6
+ }));
7
+ }
@@ -0,0 +1,11 @@
1
+ import globals from "globals";
2
+ import pluginJs from "@eslint/js";
3
+ import tseslint from "typescript-eslint";
4
+ import eslintConfigPrettier from "eslint-config-prettier";
5
+
6
+ export default [
7
+ { languageOptions: { globals: globals.node } },
8
+ pluginJs.configs.recommended,
9
+ ...tseslint.configs.recommended,
10
+ eslintConfigPrettier,
11
+ ];
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@infoxchange/make-it-so-sst-v2",
3
+ "version": "3.0.1",
4
+ "description": "Makes deploying services to IX infra easy",
5
+ "repository": "github:infoxchange/make-it-so",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "vitest",
13
+ "lint": "eslint . --fix && prettier . --write && tsc --noEmit",
14
+ "lint:check": "eslint . && prettier . --check && tsc --noEmit",
15
+ "prepare": "husky",
16
+ "commit": "lint-staged && commit"
17
+ },
18
+ "author": "Infoxchange Vic Dev Team <vicdevs@infoxchange.org>",
19
+ "license": "MIT",
20
+ "exports": {
21
+ "./cdk-constructs": "./dist/cdk-constructs/index.js",
22
+ "./deployConfig": "./dist/deployConfig.js",
23
+ "./auth": "./dist/lib/auth/index.js",
24
+ "./proxy": "./dist/lib/proxy/index.js"
25
+ },
26
+ "lint-staged": {
27
+ "**/*": [
28
+ "eslint --fix --no-warn-ignored",
29
+ "prettier --write --ignore-unknown"
30
+ ]
31
+ },
32
+ "devDependencies": {
33
+ "@commitlint/cli": "^19.3.0",
34
+ "@commitlint/config-conventional": "^19.2.2",
35
+ "@commitlint/prompt-cli": "^19.3.1",
36
+ "@eslint/js": "^9.3.0",
37
+ "@tsconfig/node21": "^21.0.3",
38
+ "@types/aws-cloudfront-function": "^1.0.6",
39
+ "@types/global-agent": "^3.0.0",
40
+ "@types/jsonwebtoken": "^9.0.10",
41
+ "aws-cdk-lib": "2.142.1",
42
+ "constructs": "^10.3.0",
43
+ "eslint": "^8.57.0",
44
+ "eslint-config-prettier": "^9.1.0",
45
+ "globals": "^15.3.0",
46
+ "husky": "^9.0.11",
47
+ "lint-staged": "^15.2.5",
48
+ "prettier": "3.2.5",
49
+ "semantic-release": "^23.1.1",
50
+ "sst": "2.42.0",
51
+ "typescript": "^5.4.5",
52
+ "typescript-eslint": "^7.11.0",
53
+ "vitest": "^1.6.0"
54
+ },
55
+ "peerDependencies": {
56
+ "aws-cdk-lib": "^2.0.0",
57
+ "constructs": "^10.0.0",
58
+ "sst": "^2.0.0"
59
+ },
60
+ "dependencies": {
61
+ "global-agent": "^3.0.0",
62
+ "jsonwebtoken": "^9.0.2",
63
+ "undici": "^7.16.0",
64
+ "zod": "^3.24.2"
65
+ }
66
+ }
@@ -0,0 +1,81 @@
1
+ import { Api } from "sst/constructs";
2
+ import { IxCertificate } from "./IxCertificate.js";
3
+ import { IxDnsRecord } from "./IxDnsRecord.js";
4
+ import ixDeployConfig from "../deployConfig.js";
5
+ import { convertToBase62Hash } from "../lib/utils/hash.js";
6
+
7
+ type ConstructScope = ConstructorParameters<typeof Api>[0];
8
+ type ConstructId = ConstructorParameters<typeof Api>[1];
9
+ type ConstructProps = Exclude<ConstructorParameters<typeof Api>[2], undefined>;
10
+
11
+ export class IxApi extends Api {
12
+ constructor(
13
+ scope: ConstructScope,
14
+ id: ConstructId,
15
+ props: ConstructProps = {},
16
+ ) {
17
+ if (ixDeployConfig.isIxDeploy) {
18
+ IxApi.setupCustomDomain(scope, id, props);
19
+ }
20
+
21
+ super(scope, id, props);
22
+
23
+ if (ixDeployConfig.isIxDeploy) {
24
+ this.createDnsRecords(scope);
25
+ }
26
+ }
27
+
28
+ // This must be static because we need to call it in the constructor before super
29
+ private static setupCustomDomain(
30
+ scope: ConstructScope,
31
+ id: ConstructId,
32
+ props: ConstructProps,
33
+ ): void {
34
+ // Default to using domains names passed in by the pipeline as the custom domain
35
+ if (ixDeployConfig.isIxDeploy && !("customDomain" in props)) {
36
+ props.customDomain = {
37
+ domainName: ixDeployConfig.siteDomains[0],
38
+ };
39
+ }
40
+
41
+ this.setupCertificate(scope, id, props);
42
+ }
43
+
44
+ // This must be static because we need to call it in the constructor before super
45
+ private static setupCertificate(
46
+ scope: ConstructScope,
47
+ id: ConstructId,
48
+ props: ConstructProps,
49
+ ): void {
50
+ if (!props?.customDomain) return;
51
+
52
+ if (typeof props.customDomain === "string") {
53
+ props.customDomain = { domainName: props.customDomain };
54
+ }
55
+
56
+ const domainName = props.customDomain.domainName;
57
+ if (domainName) {
58
+ const domainCert = new IxCertificate(scope, id + "-IxCertificate", {
59
+ domainName,
60
+ region: "ap-southeast-2", // API Gateway wants southeast-2.
61
+ });
62
+ props.customDomain.isExternalDomain = true;
63
+ props.customDomain.cdk = props.customDomain.cdk ?? {};
64
+ props.customDomain.cdk.certificate = domainCert.acmCertificate;
65
+ }
66
+ }
67
+
68
+ private createDnsRecords(scope: ConstructScope) {
69
+ if (this.cdk.domainName?.name && this.cdk.domainName?.regionalDomainName) {
70
+ const domainNameLogicalId = convertToBase62Hash(this.cdk.domainName.name);
71
+
72
+ // API Gateway has a separate domain for using with a CNAME (regionalDomainName)
73
+ new IxDnsRecord(scope, `DnsRecord-${domainNameLogicalId}`, {
74
+ type: "CNAME",
75
+ name: this.cdk.domainName.name,
76
+ value: this.cdk.domainName?.regionalDomainName,
77
+ ttl: 900,
78
+ });
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,35 @@
1
+ import { Bucket } from "sst/constructs";
2
+ import { BucketEncryption } from "aws-cdk-lib/aws-s3";
3
+ import ixDeployConfig from "../deployConfig.js";
4
+
5
+ type ConstructScope = ConstructorParameters<typeof Bucket>[0];
6
+ type ConstructId = ConstructorParameters<typeof Bucket>[1];
7
+ type ConstructProps = Exclude<
8
+ ConstructorParameters<typeof Bucket>[2],
9
+ undefined
10
+ >;
11
+
12
+ export class IxBucket extends Bucket {
13
+ constructor(
14
+ scope: ConstructScope,
15
+ id: ConstructId,
16
+ props: ConstructProps = {},
17
+ ) {
18
+ const bucketProps: ConstructProps = {
19
+ blockPublicACLs: true,
20
+ ...props,
21
+ cdk: {
22
+ ...props.cdk,
23
+ bucket: {
24
+ enforceSSL: true,
25
+ ...(ixDeployConfig.isIxDeploy
26
+ ? { encryption: BucketEncryption.S3_MANAGED }
27
+ : {}),
28
+ ...props.cdk?.bucket,
29
+ },
30
+ },
31
+ };
32
+
33
+ super(scope, id, bucketProps);
34
+ }
35
+ }
@@ -0,0 +1,54 @@
1
+ import { Construct } from "constructs";
2
+ import { StringParameter } from "aws-cdk-lib/aws-ssm";
3
+ import { Certificate, ICertificate } from "aws-cdk-lib/aws-certificatemanager";
4
+ import { CustomResource } from "aws-cdk-lib";
5
+
6
+ type ConstructScope = ConstructorParameters<typeof Construct>[0];
7
+ type ConstructId = ConstructorParameters<typeof Construct>[1];
8
+
9
+ type Props = {
10
+ domainName: string;
11
+ subjectAlternativeNames?: string[];
12
+ region?: string;
13
+ };
14
+
15
+ export class IxCertificate extends Construct {
16
+ public acmCertificate: ICertificate;
17
+
18
+ constructor(scope: ConstructScope, id: ConstructId, props: Props) {
19
+ super(scope, id);
20
+ this.acmCertificate = this.createCertificate(scope, id, props);
21
+ }
22
+
23
+ private createCertificate(
24
+ scope: ConstructScope,
25
+ id: ConstructId,
26
+ props: Props,
27
+ ): ICertificate {
28
+ const certificateCreationLambdaArn =
29
+ StringParameter.valueForStringParameter(
30
+ scope,
31
+ "/shared-services/acm/lambdaArn-v2",
32
+ );
33
+ const certificateCustomResource = new CustomResource(
34
+ scope,
35
+ "DomainCert-" + id,
36
+ {
37
+ resourceType: "Custom::CertIssuingLambda",
38
+ serviceToken: certificateCreationLambdaArn,
39
+ properties: {
40
+ DomainName: props.domainName,
41
+ ...(props.subjectAlternativeNames && {
42
+ SubjectAlternativeNames: props.subjectAlternativeNames,
43
+ }),
44
+ ...(props.region && { CertificateIssuingRegion: props.region }),
45
+ },
46
+ },
47
+ );
48
+ return Certificate.fromCertificateArn(
49
+ scope,
50
+ id + "-AwsCertificate",
51
+ certificateCustomResource.ref,
52
+ );
53
+ }
54
+ }
@@ -0,0 +1,79 @@
1
+ import { Construct } from "constructs";
2
+ import { StringParameter } from "aws-cdk-lib/aws-ssm";
3
+ import { CustomResource } from "aws-cdk-lib";
4
+ import { remapKeys } from "../lib/utils/objects.js";
5
+
6
+ type ConstructScope = ConstructorParameters<typeof Construct>[0];
7
+ type ConstructId = ConstructorParameters<typeof Construct>[1];
8
+
9
+ type Props = {
10
+ name: string;
11
+ value: string;
12
+ ttl?: number;
13
+ hostedZoneId?: string;
14
+ } & (
15
+ | {
16
+ type: "A" | "CNAME" | "NS" | "SOA" | "TXT";
17
+ }
18
+ | {
19
+ type: "ALIAS";
20
+ aliasZoneId: string;
21
+ }
22
+ | {
23
+ type: "MX";
24
+ priority: number;
25
+ }
26
+ );
27
+
28
+ export class IxDnsRecord extends Construct {
29
+ constructor(scope: ConstructScope, id: ConstructId, props: Props) {
30
+ super(scope, id);
31
+ this.createDnsRecord(scope, id, props);
32
+ }
33
+
34
+ private createDnsRecord(
35
+ scope: ConstructScope,
36
+ id: ConstructId,
37
+ constructProps: Props,
38
+ ): void {
39
+ const dnsRecordUpdaterLambdaArn = StringParameter.valueForStringParameter(
40
+ scope,
41
+ "/shared-services/route53/lambdaArn",
42
+ );
43
+ const keysMap = {
44
+ name: "RecordFQDN",
45
+ value: "RecordValue",
46
+ ttl: "RecordTTL",
47
+ hostedZoneId: "HostedZoneId",
48
+ type: "RecordType",
49
+ aliasZoneId: "AliasZoneId",
50
+ };
51
+ let lambdaProps;
52
+ if (constructProps.type === "TXT") {
53
+ lambdaProps = remapKeys(
54
+ {
55
+ ...constructProps,
56
+ value: `"${constructProps.value}"`,
57
+ },
58
+ keysMap,
59
+ );
60
+ } else if (constructProps.type === "MX") {
61
+ const { priority, ...rest } = constructProps;
62
+ lambdaProps = remapKeys(
63
+ {
64
+ ...rest,
65
+ value: `${priority} ${rest.value}`,
66
+ },
67
+ keysMap,
68
+ );
69
+ } else {
70
+ lambdaProps = remapKeys(constructProps, keysMap);
71
+ }
72
+
73
+ new CustomResource(scope, id + "-CertificateCustomResource", {
74
+ resourceType: "Custom::DNSRecordUpdaterLambda",
75
+ serviceToken: dnsRecordUpdaterLambdaArn,
76
+ properties: lambdaProps,
77
+ });
78
+ }
79
+ }
@@ -0,0 +1,106 @@
1
+ import { Construct } from "constructs";
2
+ import { CfnCacheCluster, CfnSubnetGroup } from "aws-cdk-lib/aws-elasticache";
3
+ import { IVpc, SecurityGroup, Peer, Port } from "aws-cdk-lib/aws-ec2";
4
+ import { StringParameter } from "aws-cdk-lib/aws-ssm";
5
+ import { Stack } from "aws-cdk-lib";
6
+ import { IxVpcDetails } from "./IxVpcDetails.js";
7
+ import deployConfig from "../deployConfig.js";
8
+
9
+ type ConstructScope = ConstructorParameters<typeof Construct>[0];
10
+ type ConstructId = ConstructorParameters<typeof Construct>[1];
11
+ type CacheClusterProps = ConstructorParameters<typeof CfnCacheCluster>[2];
12
+
13
+ type Props = CacheClusterProps & {
14
+ vpc?: IVpc;
15
+ vpcSubnetIds?: string[];
16
+ };
17
+
18
+ export class IxElasticache extends Construct {
19
+ cluster: CfnCacheCluster;
20
+
21
+ connectionString: string;
22
+
23
+ constructor(
24
+ scope: ConstructScope,
25
+ id: ConstructId,
26
+ { vpc, vpcSubnetIds, ...elasticacheProps }: Props,
27
+ ) {
28
+ super(scope, id);
29
+
30
+ // Setup cluster name
31
+ if (!elasticacheProps.clusterName && deployConfig.isIxDeploy) {
32
+ elasticacheProps.clusterName = `${Stack.of(this).stackName}`;
33
+ }
34
+
35
+ if (!elasticacheProps.cacheSubnetGroupName) {
36
+ // Setup VPC
37
+ if (!vpc && deployConfig.isIxDeploy) {
38
+ const vpcDetails = new IxVpcDetails(scope, id + "-IxVpcDetails");
39
+ vpc = vpcDetails.vpc;
40
+ }
41
+
42
+ // Setup VPC subnets
43
+ if (vpc && !vpcSubnetIds) {
44
+ if (deployConfig.isIxDeploy) {
45
+ vpcSubnetIds = IxVpcDetails.getVpcSubnetIds(scope);
46
+ } else {
47
+ vpcSubnetIds = vpc.privateSubnets.map((subnet) => subnet.subnetId);
48
+
49
+ if (!vpcSubnetIds.length) {
50
+ throw Error(`The vpc ${vpc.vpcId} has no private subnets.`);
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ // Setup cluster security group
57
+ if (vpc && vpcSubnetIds) {
58
+ const subnetGroup = new CfnSubnetGroup(scope, "ElasticacheSubnetGroup", {
59
+ subnetIds: vpcSubnetIds,
60
+ description: "Subnet group for redis",
61
+ });
62
+
63
+ elasticacheProps.cacheSubnetGroupName = subnetGroup.ref;
64
+
65
+ const namePrefix = elasticacheProps.clusterName
66
+ ? elasticacheProps.clusterName
67
+ : `${Stack.of(this).stackName}`;
68
+
69
+ const securityGroup = new SecurityGroup(
70
+ scope,
71
+ "ElasticacheSecurityGroup",
72
+ {
73
+ vpc,
74
+ allowAllOutbound: true,
75
+ description: "Security group for Elasticache Cluster",
76
+ securityGroupName: `${namePrefix}-elasticache`,
77
+ },
78
+ );
79
+
80
+ for (const subnetIndex of [1, 2, 3]) {
81
+ const cidr = StringParameter.valueForStringParameter(
82
+ scope,
83
+ `/vpc/subnet/private-${deployConfig.workloadGroup}/${subnetIndex}/cidr`,
84
+ );
85
+ securityGroup.addIngressRule(
86
+ Peer.ipv4(cidr),
87
+ Port.tcp(6379),
88
+ `Allow access to Elasticache cluster from private ${deployConfig.workloadGroup} subnet`,
89
+ );
90
+ }
91
+
92
+ elasticacheProps.vpcSecurityGroupIds = [securityGroup.securityGroupId];
93
+ }
94
+
95
+ // Create Redis Cluster
96
+ this.cluster = new CfnCacheCluster(scope, "RedisCluster", elasticacheProps);
97
+
98
+ if (elasticacheProps.engine === "redis") {
99
+ this.connectionString = `redis://${this.cluster.attrRedisEndpointAddress}:${this.cluster.attrRedisEndpointPort}`;
100
+ } else if (elasticacheProps.engine === "memcached") {
101
+ this.connectionString = `${this.cluster.attrConfigurationEndpointAddress}:${this.cluster.attrConfigurationEndpointPort}`;
102
+ } else {
103
+ throw Error(`Unsupported engine: ${elasticacheProps.engine}`);
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,72 @@
1
+ import { NextjsSite } from "sst/constructs";
2
+ import ixDeployConfig from "../deployConfig.js";
3
+ import {
4
+ type ExtendedNextjsSiteProps,
5
+ getAliasDomain,
6
+ getAlternativeDomains,
7
+ getCustomDomains,
8
+ getPrimaryCustomDomain,
9
+ getPrimaryDomain,
10
+ getPrimaryOrigin,
11
+ setupCertificate,
12
+ setupCustomDomain,
13
+ setupDnsRecords,
14
+ setupDomainAliasRedirect,
15
+ setupVpcDetails,
16
+ setupDefaultEnvVars,
17
+ applyConditionalEnvironmentVariables,
18
+ parentCompatibleSsrProps,
19
+ processAuthProps,
20
+ } from "../lib/site/support.js";
21
+
22
+ type ConstructScope = ConstructorParameters<typeof NextjsSite>[0];
23
+ type ConstructId = ConstructorParameters<typeof NextjsSite>[1];
24
+ type ConstructProps = ExtendedNextjsSiteProps;
25
+
26
+ export class IxNextjsSite extends NextjsSite {
27
+ constructor(
28
+ scope: ConstructScope,
29
+ id: ConstructId,
30
+ props: ConstructProps = {},
31
+ ) {
32
+ if (ixDeployConfig.isIxDeploy) {
33
+ props = setupVpcDetails(scope, id, props);
34
+ props = setupCustomDomain(scope, id, props);
35
+ props = setupCertificate(scope, id, props);
36
+ props = setupDomainAliasRedirect(scope, id, props);
37
+ }
38
+ props = processAuthProps(scope, id, "SsrSite", props);
39
+ props = setupDefaultEnvVars(scope, id, props);
40
+ props = applyConditionalEnvironmentVariables(scope, id, props);
41
+
42
+ super(scope, id, parentCompatibleSsrProps(props));
43
+
44
+ if (ixDeployConfig.isIxDeploy) {
45
+ setupDnsRecords(this, scope, id, props);
46
+ }
47
+ }
48
+
49
+ public get customDomains(): string[] {
50
+ return getCustomDomains(this.props);
51
+ }
52
+
53
+ public get primaryCustomDomain(): string | null {
54
+ return getPrimaryCustomDomain(this.props);
55
+ }
56
+
57
+ public get aliasDomain(): string | null {
58
+ return getAliasDomain(this.props);
59
+ }
60
+
61
+ public get alternativeDomains(): string[] {
62
+ return getAlternativeDomains(this.props);
63
+ }
64
+
65
+ public get primaryDomain(): string | null {
66
+ return getPrimaryDomain(this, this.props);
67
+ }
68
+
69
+ public get primaryOrigin(): string | null {
70
+ return getPrimaryOrigin(this, this.props);
71
+ }
72
+ }
@@ -0,0 +1,54 @@
1
+ import { Construct } from "constructs";
2
+ import { StringParameter } from "aws-cdk-lib/aws-ssm";
3
+ import { CustomResource } from "aws-cdk-lib";
4
+
5
+ type ConstructScope = ConstructorParameters<typeof Construct>[0];
6
+ type ConstructId = ConstructorParameters<typeof Construct>[1];
7
+
8
+ type Props = {
9
+ appName: string;
10
+ dataBuckets: string[];
11
+ };
12
+
13
+ export class IxQuicksightWorkspace extends Construct {
14
+ workspaceBucketName: string;
15
+ athenaWorkgroupName: string;
16
+ serviceRoleArn: string;
17
+ glueDatabaseName: string;
18
+ quickSightDataSourceId: string;
19
+
20
+ constructor(scope: ConstructScope, id: ConstructId, props: Props) {
21
+ super(scope, id);
22
+ const qsWorkspaceSetupLambdaArn = StringParameter.valueForStringParameter(
23
+ scope,
24
+ "/shared-services/quicksight-workspace/lambdaArn",
25
+ );
26
+
27
+ const quicksightWorkspaceLambda = new CustomResource(
28
+ scope,
29
+ id + "-CustomResource",
30
+ {
31
+ resourceType: "Custom::QuicksightWorkspace",
32
+ serviceToken: qsWorkspaceSetupLambdaArn,
33
+ properties: {
34
+ app_name: props.appName,
35
+ data_buckets: props.dataBuckets,
36
+ },
37
+ },
38
+ );
39
+
40
+ this.workspaceBucketName = quicksightWorkspaceLambda.getAttString(
41
+ "WorkspaceBucketName",
42
+ );
43
+ this.athenaWorkgroupName = quicksightWorkspaceLambda.getAttString(
44
+ "AthenaWorkgroupName",
45
+ );
46
+ this.serviceRoleArn =
47
+ quicksightWorkspaceLambda.getAttString("ServiceRoleArn");
48
+ this.glueDatabaseName =
49
+ quicksightWorkspaceLambda.getAttString("GlueDatabaseName");
50
+ this.quickSightDataSourceId = quicksightWorkspaceLambda.getAttString(
51
+ "QuickSightDataSourceId",
52
+ );
53
+ }
54
+ }
@@ -0,0 +1,70 @@
1
+ import { Construct } from "constructs";
2
+ import { IxDnsRecord } from "./IxDnsRecord.js";
3
+ import * as ses from "aws-cdk-lib/aws-ses";
4
+ import * as cdk from "aws-cdk-lib";
5
+
6
+ type ConstructScope = ConstructorParameters<typeof Construct>[0];
7
+ type ConstructId = ConstructorParameters<typeof Construct>[1];
8
+
9
+ type Props = {
10
+ domain: string;
11
+ mailFromSubdomain?: string;
12
+ };
13
+
14
+ export class IxSESIdentity extends Construct {
15
+ constructor(scope: ConstructScope, id: ConstructId, props: Props) {
16
+ const domain = props.domain.includes("@")
17
+ ? props.domain.split("@")[1]
18
+ : props.domain;
19
+ const mailFromDomain = `${props.mailFromSubdomain ?? "mail"}.${domain}`;
20
+
21
+ super(scope, id);
22
+
23
+ const identity = new ses.EmailIdentity(scope, `${id}EmailIdentity`, {
24
+ identity: ses.Identity.domain(domain),
25
+ mailFromDomain,
26
+ });
27
+
28
+ // Based on https://github.com/aws/aws-cdk/blob/e2ef65a26c833ecb4a29c22e070c3c5f01c31995/packages/aws-cdk-lib/aws-ses/lib/email-identity.ts#L247
29
+ for (const i of [1, 2, 3]) {
30
+ new IxDnsRecord(scope, `${id}DkimDnsToken${i}`, {
31
+ type: "CNAME",
32
+ name: identity[
33
+ `dkimDnsTokenName${i}` as
34
+ | "dkimDnsTokenName1"
35
+ | "dkimDnsTokenName2"
36
+ | "dkimDnsTokenName3"
37
+ ],
38
+ value:
39
+ identity[
40
+ `dkimDnsTokenValue${i}` as
41
+ | "dkimDnsTokenValue1"
42
+ | "dkimDnsTokenValue2"
43
+ | "dkimDnsTokenValue3"
44
+ ],
45
+ ttl: 1800,
46
+ });
47
+ }
48
+
49
+ // Based on
50
+ // https://github.com/aws/aws-cdk/blob/e2ef65a26c833ecb4a29c22e070c3c5f01c31995/packages/aws-cdk-lib/aws-ses/lib/email-identity.ts#L512
51
+ new IxDnsRecord(scope, `${id}MailFromMxRecord`, {
52
+ type: "MX",
53
+ name: mailFromDomain,
54
+ value: `feedback-smtp.${cdk.Stack.of(scope).region}.amazonses.com`,
55
+ priority: 10,
56
+ });
57
+ new IxDnsRecord(scope, `${id}MailFromTxtRecord`, {
58
+ type: "TXT",
59
+ name: mailFromDomain,
60
+ value: "v=spf1 include:amazonses.com ~all",
61
+ });
62
+
63
+ // Set up DMARC record
64
+ new IxDnsRecord(scope, `${id}DMARC`, {
65
+ type: "TXT",
66
+ name: `_dmarc.${domain}`,
67
+ value: "v=DMARC1; p=none;",
68
+ });
69
+ }
70
+ }