@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.
- package/.editorconfig +16 -0
- package/LICENSE +21 -0
- package/README.md +377 -0
- package/commitlint.config.ts +14 -0
- package/dist/cdk-constructs/IxApi.d.ts +12 -0
- package/dist/cdk-constructs/IxApi.d.ts.map +1 -0
- package/dist/cdk-constructs/IxApi.js +56 -0
- package/dist/cdk-constructs/IxBucket.d.ts +9 -0
- package/dist/cdk-constructs/IxBucket.d.ts.map +1 -0
- package/dist/cdk-constructs/IxBucket.js +22 -0
- package/dist/cdk-constructs/IxCertificate.d.ts +16 -0
- package/dist/cdk-constructs/IxCertificate.d.ts.map +1 -0
- package/dist/cdk-constructs/IxCertificate.js +26 -0
- package/dist/cdk-constructs/IxDnsRecord.d.ts +23 -0
- package/dist/cdk-constructs/IxDnsRecord.d.ts.map +1 -0
- package/dist/cdk-constructs/IxDnsRecord.js +43 -0
- package/dist/cdk-constructs/IxElasticache.d.ts +17 -0
- package/dist/cdk-constructs/IxElasticache.d.ts.map +1 -0
- package/dist/cdk-constructs/IxElasticache.js +70 -0
- package/dist/cdk-constructs/IxNextjsSite.d.ts +16 -0
- package/dist/cdk-constructs/IxNextjsSite.d.ts.map +1 -0
- package/dist/cdk-constructs/IxNextjsSite.js +38 -0
- package/dist/cdk-constructs/IxQuicksightWorkspace.d.ts +17 -0
- package/dist/cdk-constructs/IxQuicksightWorkspace.d.ts.map +1 -0
- package/dist/cdk-constructs/IxQuicksightWorkspace.js +29 -0
- package/dist/cdk-constructs/IxSESIdentity.d.ts +12 -0
- package/dist/cdk-constructs/IxSESIdentity.d.ts.map +1 -0
- package/dist/cdk-constructs/IxSESIdentity.js +45 -0
- package/dist/cdk-constructs/IxStaticSite.d.ts +17 -0
- package/dist/cdk-constructs/IxStaticSite.d.ts.map +1 -0
- package/dist/cdk-constructs/IxStaticSite.js +38 -0
- package/dist/cdk-constructs/IxVpcDetails.d.ts +12 -0
- package/dist/cdk-constructs/IxVpcDetails.d.ts.map +1 -0
- package/dist/cdk-constructs/IxVpcDetails.js +26 -0
- package/dist/cdk-constructs/IxWebsiteRedirect.d.ts +35 -0
- package/dist/cdk-constructs/IxWebsiteRedirect.d.ts.map +1 -0
- package/dist/cdk-constructs/IxWebsiteRedirect.js +72 -0
- package/dist/cdk-constructs/SiteOidcAuth/auth-check-handler-body.d.ts +2 -0
- package/dist/cdk-constructs/SiteOidcAuth/auth-check-handler-body.d.ts.map +1 -0
- package/dist/cdk-constructs/SiteOidcAuth/auth-check-handler-body.js +130 -0
- package/dist/cdk-constructs/SiteOidcAuth/auth-route.d.ts +2 -0
- package/dist/cdk-constructs/SiteOidcAuth/auth-route.d.ts.map +1 -0
- package/dist/cdk-constructs/SiteOidcAuth/auth-route.js +59 -0
- package/dist/cdk-constructs/SiteOidcAuth/index.d.ts +197 -0
- package/dist/cdk-constructs/SiteOidcAuth/index.d.ts.map +1 -0
- package/dist/cdk-constructs/SiteOidcAuth/index.js +188 -0
- package/dist/cdk-constructs/index.d.ts +11 -0
- package/dist/cdk-constructs/index.d.ts.map +1 -0
- package/dist/cdk-constructs/index.js +10 -0
- package/dist/deployConfig.d.ts +72 -0
- package/dist/deployConfig.d.ts.map +1 -0
- package/dist/deployConfig.js +78 -0
- package/dist/lib/auth/index.d.ts +2 -0
- package/dist/lib/auth/index.d.ts.map +1 -0
- package/dist/lib/auth/index.js +1 -0
- package/dist/lib/auth/oidc.d.ts +26 -0
- package/dist/lib/auth/oidc.d.ts.map +1 -0
- package/dist/lib/auth/oidc.js +48 -0
- package/dist/lib/proxy/fetch.d.ts +4 -0
- package/dist/lib/proxy/fetch.d.ts.map +1 -0
- package/dist/lib/proxy/fetch.js +31 -0
- package/dist/lib/proxy/index.d.ts +2 -0
- package/dist/lib/proxy/index.d.ts.map +1 -0
- package/dist/lib/proxy/index.js +1 -0
- package/dist/lib/site/support.d.ts +71 -0
- package/dist/lib/site/support.d.ts.map +1 -0
- package/dist/lib/site/support.js +262 -0
- package/dist/lib/utils/hash.d.ts +2 -0
- package/dist/lib/utils/hash.d.ts.map +1 -0
- package/dist/lib/utils/hash.js +13 -0
- package/dist/lib/utils/objects.d.ts +4 -0
- package/dist/lib/utils/objects.d.ts.map +1 -0
- package/dist/lib/utils/objects.js +7 -0
- package/eslint.config.js +11 -0
- package/package.json +66 -0
- package/src/cdk-constructs/IxApi.ts +81 -0
- package/src/cdk-constructs/IxBucket.ts +35 -0
- package/src/cdk-constructs/IxCertificate.ts +54 -0
- package/src/cdk-constructs/IxDnsRecord.ts +79 -0
- package/src/cdk-constructs/IxElasticache.ts +106 -0
- package/src/cdk-constructs/IxNextjsSite.ts +72 -0
- package/src/cdk-constructs/IxQuicksightWorkspace.ts +54 -0
- package/src/cdk-constructs/IxSESIdentity.ts +70 -0
- package/src/cdk-constructs/IxStaticSite.ts +69 -0
- package/src/cdk-constructs/IxVpcDetails.ts +38 -0
- package/src/cdk-constructs/IxWebsiteRedirect.ts +133 -0
- package/src/cdk-constructs/SiteOidcAuth/auth-check-handler-body.ts +168 -0
- package/src/cdk-constructs/SiteOidcAuth/auth-route.ts +71 -0
- package/src/cdk-constructs/SiteOidcAuth/index.ts +299 -0
- package/src/cdk-constructs/index.ts +10 -0
- package/src/deployConfig.ts +87 -0
- package/src/lib/auth/index.ts +1 -0
- package/src/lib/auth/oidc.ts +73 -0
- package/src/lib/proxy/fetch.ts +41 -0
- package/src/lib/proxy/index.ts +1 -0
- package/src/lib/site/support.ts +439 -0
- package/src/lib/utils/hash.ts +14 -0
- package/src/lib/utils/objects.ts +19 -0
- 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"}
|
package/eslint.config.js
ADDED
|
@@ -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
|
+
}
|