@aws/nx-plugin 0.1.6 → 0.2.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/LICENSE-THIRD-PARTY +1554 -3241
- package/generators.json +1 -13
- package/package.json +14 -14
- package/src/cloudscape-website/app/README.md +84 -48
- package/src/cloudscape-website/app/__snapshots__/generator.spec.ts.snap +168 -233
- package/src/cloudscape-website/app/files/app/README.md.template +44 -0
- package/src/cloudscape-website/app/files/app/src/layouts/App/index.tsx.template +40 -43
- package/src/cloudscape-website/app/files/app/src/layouts/App/navitems.ts.template +3 -3
- package/src/cloudscape-website/app/files/app/src/layouts/Routes/index.tsx.template +4 -6
- package/src/cloudscape-website/app/files/app/src/main.tsx.template +7 -10
- package/src/cloudscape-website/app/files/app/src/pages/Home/index.tsx.template +0 -2
- package/src/cloudscape-website/app/files/common/constructs/src/app/static-websites/__websiteNameKebabCase__.ts.template +13 -0
- package/src/cloudscape-website/app/files/common/constructs/src/{__websiteNameKebabCase__ → core}/static-website.ts.template +79 -144
- package/src/cloudscape-website/app/generator.js +90 -74
- package/src/cloudscape-website/app/generator.js.map +1 -1
- package/src/cloudscape-website/app/schema.d.ts +3 -5
- package/src/cloudscape-website/app/schema.json +1 -24
- package/src/cloudscape-website/cognito-auth/README.md +53 -32
- package/src/cloudscape-website/cognito-auth/__snapshots__/generator.spec.ts.snap +162 -124
- package/src/cloudscape-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +53 -39
- package/src/cloudscape-website/cognito-auth/files/common/constructs/src/core/user-identity.ts.template +168 -0
- package/src/cloudscape-website/cognito-auth/generator.js +130 -47
- package/src/cloudscape-website/cognito-auth/generator.js.map +1 -1
- package/src/cloudscape-website/cognito-auth/schema.d.ts +1 -0
- package/src/cloudscape-website/cognito-auth/schema.json +7 -1
- package/src/cloudscape-website/runtime-config/__snapshots__/generator.spec.ts.snap +20 -15
- package/src/cloudscape-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +7 -10
- package/src/cloudscape-website/runtime-config/files/app/hooks/useRuntimeConfig.tsx.template +13 -0
- package/src/cloudscape-website/runtime-config/generator.js +4 -2
- package/src/cloudscape-website/runtime-config/generator.js.map +1 -1
- package/src/infra/app/README.md +71 -46
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +184 -305
- package/src/infra/app/files/app/README.md.template +76 -0
- package/src/infra/app/files/app/src/main.ts.template +18 -0
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/aws-prototyping.guard +1282 -0
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/cfn-nag.guard +6839 -0
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/hipaa-security.guard +2807 -0
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/nist-csf.guard +2585 -0
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/pci-dss-3-2-1.guard +2236 -0
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-reliability-pillar.guard +885 -0
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-security-pillar.guard +2205 -0
- package/src/infra/app/files/common/constructs/src/core/cfn-guard.ts.template +63 -0
- package/src/infra/app/generator.js +36 -7
- package/src/infra/app/generator.js.map +1 -1
- package/src/infra/app/schema.d.ts +10 -1
- package/src/infra/app/schema.json +16 -8
- package/src/trpc/backend/README.md +102 -80
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +42 -19
- package/src/trpc/backend/files/backend/README.md.template +33 -0
- package/src/trpc/backend/files/common/constructs/src/app/trpc-apis/__apiNameKebabCase__.ts.template +18 -0
- package/src/trpc/backend/files/common/constructs/src/{__apiNameKebabCase__/index.ts.template → core/trpc-api.ts.template} +12 -16
- package/src/trpc/backend/files/schema/README.md.template +33 -0
- package/src/trpc/backend/generator.js +30 -44
- package/src/trpc/backend/generator.js.map +1 -1
- package/src/trpc/backend/schema.d.ts +3 -1
- package/src/trpc/backend/schema.json +8 -13
- package/src/trpc/react/README.md +46 -66
- package/src/trpc/react/__snapshots__/generator.spec.ts.snap +104 -65
- package/src/trpc/react/files/src/components/TrpcClients/IsolatedTrpcProvider.tsx.template +75 -0
- package/src/trpc/react/files/src/components/TrpcClients/TrpcApis.tsx.template +1 -0
- package/src/trpc/react/files/src/components/TrpcClients/TrpcClientProviders.tsx.template +10 -0
- package/src/trpc/react/files/src/components/TrpcClients/index.tsx.template +5 -0
- package/src/trpc/react/files/src/hooks/useSigV4.tsx.template +38 -0
- package/src/trpc/react/files/src/hooks/use__apiNameClassName__.tsx.template +3 -0
- package/src/trpc/react/generator.js +124 -25
- package/src/trpc/react/generator.js.map +1 -1
- package/src/trpc/react/schema.json +2 -2
- package/src/ts/lib/__snapshots__/generator.spec.ts.snap +47 -93
- package/src/ts/lib/eslint.d.ts +1 -2
- package/src/ts/lib/eslint.js +62 -21
- package/src/ts/lib/eslint.js.map +1 -1
- package/src/ts/lib/files/README.md.template +33 -0
- package/src/ts/lib/generator.js +44 -5
- package/src/ts/lib/generator.js.map +1 -1
- package/src/ts/lib/schema.d.ts +1 -4
- package/src/ts/lib/schema.json +2 -21
- package/src/ts/lib/ts-project-utils.js +3 -18
- package/src/ts/lib/ts-project-utils.js.map +1 -1
- package/src/ts/lib/vitest.js +12 -0
- package/src/ts/lib/vitest.js.map +1 -1
- package/src/utils/ast.d.ts +13 -0
- package/src/utils/ast.js +102 -0
- package/src/utils/ast.js.map +1 -0
- package/src/utils/files/common/constructs/src/app/index.ts.template +0 -0
- package/src/utils/files/common/constructs/src/{runtime-config → core}/runtime-config.ts.template +3 -5
- package/src/utils/files/common/constructs/src/index.ts.template +2 -1
- package/src/utils/files/common/readme/README.md.template +33 -0
- package/src/utils/files/common/types/src/runtime-config.ts.template +2 -13
- package/src/utils/format.d.ts +1 -1
- package/src/utils/format.js +2 -2
- package/src/utils/format.js.map +1 -1
- package/src/utils/names.d.ts +2 -0
- package/src/utils/names.js +27 -0
- package/src/utils/names.js.map +1 -0
- package/src/utils/npm-scope.js.map +1 -1
- package/src/utils/paths.js.map +1 -1
- package/src/utils/shared-constructs.js +37 -4
- package/src/utils/shared-constructs.js.map +1 -1
- package/src/utils/test.d.ts +2 -0
- package/src/utils/test.js +19 -0
- package/src/utils/test.js.map +1 -0
- package/src/utils/versions.d.ts +15 -9
- package/src/utils/versions.js +14 -8
- package/src/utils/versions.js.map +1 -1
- package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/cloudfront-web-acl.ts.template +0 -317
- package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/index.ts.template +0 -4
- package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/webacl_event_handler/index.ts.template +0 -301
- package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/index.ts.template +0 -4
- package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/user-identity.ts.template +0 -66
- package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/userpool-with-mfa.ts.template +0 -70
- package/src/gitlab/generator.d.ts +0 -8
- package/src/gitlab/generator.js +0 -16
- package/src/gitlab/generator.js.map +0 -1
- package/src/gitlab/schema.d.ts +0 -9
- package/src/gitlab/schema.json +0 -52
- package/src/infra/app/files/src/main.ts.template +0 -37
- package/src/trpc/react/files/src/components/TRPCClientProvider/index.tsx.template +0 -34
- package/src/trpc/react/files/src/hooks/useTrpc.tsx.template +0 -5
- package/src/ts/cjs-to-esm/generator.d.ts +0 -12
- package/src/ts/cjs-to-esm/generator.js +0 -189
- package/src/ts/cjs-to-esm/generator.js.map +0 -1
- package/src/ts/cjs-to-esm/schema.d.ts +0 -9
- package/src/ts/cjs-to-esm/schema.json +0 -28
- /package/src/infra/app/files/{cdk.json → app/cdk.json} +0 -0
- /package/src/infra/app/files/{src → app/src}/stacks/application-stack.ts.template +0 -0
- /package/src/utils/files/common/constructs/src/{runtime-config → core}/index.ts.template +0 -0
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
|
|
2
|
-
SPDX-License-Identifier: Apache-2.0 */
|
|
3
|
-
import { useCognitoAuthContext } from '@aws-northstar/ui';
|
|
4
|
-
import getBreadcrumbs from '@aws-northstar/ui/components/AppLayout/utils/getBreadcrumbs';
|
|
5
|
-
import NavHeader from '@aws-northstar/ui/components/AppLayout/components/NavHeader';
|
|
6
1
|
import * as React from 'react';
|
|
7
2
|
import { createContext, useCallback, useEffect, useState } from 'react';
|
|
8
3
|
import { NavItems } from './navitems';
|
|
@@ -13,11 +8,42 @@ import {
|
|
|
13
8
|
BreadcrumbGroup,
|
|
14
9
|
BreadcrumbGroupProps,
|
|
15
10
|
SideNavigation,
|
|
11
|
+
TopNavigation,
|
|
16
12
|
} from '@cloudscape-design/components';
|
|
17
13
|
import AppLayout, {
|
|
18
14
|
AppLayoutProps,
|
|
19
15
|
} from '@cloudscape-design/components/app-layout';
|
|
20
|
-
import { useLocation, useNavigate } from 'react-router-dom';
|
|
16
|
+
import { matchPath, useLocation, useNavigate } from 'react-router-dom';
|
|
17
|
+
|
|
18
|
+
const getBreadcrumbs = (
|
|
19
|
+
pathName: string,
|
|
20
|
+
search: string,
|
|
21
|
+
defaultBreadcrumb: string,
|
|
22
|
+
availableRoutes?: string[]
|
|
23
|
+
) => {
|
|
24
|
+
const segments = [
|
|
25
|
+
defaultBreadcrumb,
|
|
26
|
+
...pathName.split('/').filter((segment) => segment !== ''),
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
return segments.map((segment, i) => {
|
|
30
|
+
const href =
|
|
31
|
+
i === 0
|
|
32
|
+
? '/'
|
|
33
|
+
: `/${segments
|
|
34
|
+
.slice(1, i + 1)
|
|
35
|
+
.join('/')
|
|
36
|
+
.replace('//', '/')}`;
|
|
37
|
+
|
|
38
|
+
const matched =
|
|
39
|
+
!availableRoutes || availableRoutes.find((r) => matchPath(r, href));
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
href: matched ? `${href}${search}` : '#',
|
|
43
|
+
text: segment,
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
};
|
|
21
47
|
|
|
22
48
|
/**
|
|
23
49
|
* Context for updating/retrieving the AppLayout.
|
|
@@ -32,10 +58,6 @@ export const AppLayoutContext = createContext({
|
|
|
32
58
|
* Defines the App layout and contains logic for routing.
|
|
33
59
|
*/
|
|
34
60
|
const App: React.FC = () => {
|
|
35
|
-
const [username, setUsername] = useState<string | undefined>();
|
|
36
|
-
const [email, setEmail] = useState<string | undefined>();
|
|
37
|
-
const { getAuthenticatedUser } = useCognitoAuthContext();
|
|
38
|
-
|
|
39
61
|
const navigate = useNavigate();
|
|
40
62
|
const [activeHref, setActiveHref] = useState('/');
|
|
41
63
|
const [activeBreadcrumbs, setActiveBreadcrumbs] = useState<
|
|
@@ -44,17 +66,6 @@ const App: React.FC = () => {
|
|
|
44
66
|
const [appLayoutProps, setAppLayoutProps] = useState<AppLayoutProps>({});
|
|
45
67
|
const location = useLocation();
|
|
46
68
|
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
const authUser = getAuthenticatedUser();
|
|
49
|
-
setUsername(authUser?.getUsername());
|
|
50
|
-
|
|
51
|
-
authUser?.getSession(() => {
|
|
52
|
-
authUser.getUserAttributes((_, attributes) => {
|
|
53
|
-
setEmail(attributes?.find((a) => a.Name === 'email')?.Value);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
}, [getAuthenticatedUser, setUsername, setEmail]);
|
|
57
|
-
|
|
58
69
|
const setAppLayoutPropsSafe = useCallback(
|
|
59
70
|
(props: AppLayoutProps) => {
|
|
60
71
|
JSON.stringify(appLayoutProps) !== JSON.stringify(props) &&
|
|
@@ -75,9 +86,6 @@ const App: React.FC = () => {
|
|
|
75
86
|
e.preventDefault();
|
|
76
87
|
setAppLayoutPropsSafe({
|
|
77
88
|
contentType: undefined,
|
|
78
|
-
splitPanelOpen: false,
|
|
79
|
-
splitPanelSize: undefined,
|
|
80
|
-
splitPanelPreferences: undefined,
|
|
81
89
|
});
|
|
82
90
|
navigate(e.detail.href);
|
|
83
91
|
}
|
|
@@ -89,23 +97,14 @@ const App: React.FC = () => {
|
|
|
89
97
|
<AppLayoutContext.Provider
|
|
90
98
|
value={{ appLayoutProps, setAppLayoutProps: setAppLayoutPropsSafe }}
|
|
91
99
|
>
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
: undefined
|
|
102
|
-
}
|
|
103
|
-
onSignout={() =>
|
|
104
|
-
new Promise(() => {
|
|
105
|
-
getAuthenticatedUser()?.signOut();
|
|
106
|
-
window.location.href = '/';
|
|
107
|
-
})
|
|
108
|
-
}
|
|
100
|
+
<TopNavigation
|
|
101
|
+
identity={{
|
|
102
|
+
href: '/',
|
|
103
|
+
title: Config.applicationName,
|
|
104
|
+
logo: {
|
|
105
|
+
src: Config.logo,
|
|
106
|
+
},
|
|
107
|
+
}}
|
|
109
108
|
/>
|
|
110
109
|
<AppLayout
|
|
111
110
|
breadcrumbs={
|
|
@@ -121,8 +120,6 @@ const App: React.FC = () => {
|
|
|
121
120
|
/>
|
|
122
121
|
}
|
|
123
122
|
content={<Routes />}
|
|
124
|
-
splitPanelOpen={false}
|
|
125
|
-
splitPanelPreferences={{ position: 'bottom' }}
|
|
126
123
|
{...appLayoutProps}
|
|
127
124
|
/>
|
|
128
125
|
</AppLayoutContext.Provider>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { SideNavigationProps } from
|
|
1
|
+
import { SideNavigationProps } from '@cloudscape-design/components';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Define your Navigation Items here
|
|
5
5
|
*/
|
|
6
6
|
export const NavItems: SideNavigationProps.Item[] = [
|
|
7
|
-
{ text:
|
|
8
|
-
];
|
|
7
|
+
{ text: 'Home', type: 'link', href: '/' },
|
|
8
|
+
];
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import { Route, Routes as ReactRoutes } from "react-router-dom";
|
|
5
|
-
import Home from "../../pages/Home";
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Route, Routes as ReactRoutes } from 'react-router-dom';
|
|
3
|
+
import Home from '../../pages/Home';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* Defines the Routes.
|
|
@@ -15,4 +13,4 @@ const Routes: React.FC = () => {
|
|
|
15
13
|
);
|
|
16
14
|
};
|
|
17
15
|
|
|
18
|
-
export default Routes;
|
|
16
|
+
export default Routes;
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
|
|
2
|
-
SPDX-License-Identifier: Apache-2.0 */
|
|
3
|
-
import { NorthStarThemeProvider } from '@aws-northstar/ui';
|
|
4
1
|
import React from 'react';
|
|
5
2
|
import { createRoot } from 'react-dom/client';
|
|
6
3
|
import { BrowserRouter } from 'react-router-dom';
|
|
@@ -8,16 +5,16 @@ import { I18nProvider } from '@cloudscape-design/components/i18n';
|
|
|
8
5
|
import messages from '@cloudscape-design/components/i18n/messages/all.en';
|
|
9
6
|
import App from './layouts/App';
|
|
10
7
|
|
|
8
|
+
import '@cloudscape-design/global-styles/index.css';
|
|
9
|
+
|
|
11
10
|
const root = document.getElementById('root');
|
|
12
11
|
root &&
|
|
13
12
|
createRoot(root).render(
|
|
14
13
|
<React.StrictMode>
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
</I18nProvider>
|
|
21
|
-
</NorthStarThemeProvider>
|
|
14
|
+
<I18nProvider locale="en" messages={[messages]}>
|
|
15
|
+
<BrowserRouter>
|
|
16
|
+
<App />
|
|
17
|
+
</BrowserRouter>
|
|
18
|
+
</I18nProvider>
|
|
22
19
|
</React.StrictMode>
|
|
23
20
|
);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as url from 'url';
|
|
2
|
+
import { Construct } from 'constructs';
|
|
3
|
+
import { StaticWebsite } from '../../core/index.js';
|
|
4
|
+
|
|
5
|
+
export class <%= websiteNameClassName %> extends StaticWebsite {
|
|
6
|
+
constructor(scope: Construct, id: string) {
|
|
7
|
+
super(scope, id, {
|
|
8
|
+
websiteFilePath: url.fileURLToPath(
|
|
9
|
+
new URL('../../../../../../<%= websiteContentPath %>', import.meta.url)
|
|
10
|
+
),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
SPDX-License-Identifier: Apache-2.0 */
|
|
3
|
-
import * as url from 'url';
|
|
4
|
-
import { PDKNag } from '@aws/pdk/pdk-nag';
|
|
5
|
-
import { CfnJson, CfnOutput, Lazy, RemovalPolicy, Stack, Token } from 'aws-cdk-lib';
|
|
1
|
+
import { CfnJson, CfnOutput, RemovalPolicy, Stack, Token } from 'aws-cdk-lib';
|
|
6
2
|
import { Distribution, ViewerProtocolPolicy } from 'aws-cdk-lib/aws-cloudfront';
|
|
7
3
|
import { S3BucketOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
|
|
8
4
|
import {
|
|
@@ -13,13 +9,16 @@ import {
|
|
|
13
9
|
ObjectOwnership,
|
|
14
10
|
} from 'aws-cdk-lib/aws-s3';
|
|
15
11
|
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';
|
|
16
|
-
import { NagSuppressions } from 'cdk-nag';
|
|
17
12
|
import { Construct } from 'constructs';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
|
|
13
|
+
import { RuntimeConfig } from './runtime-config.js';
|
|
14
|
+
import { Key } from 'aws-cdk-lib/aws-kms';
|
|
15
|
+
import { CfnWebACL } from 'aws-cdk-lib/aws-wafv2';
|
|
21
16
|
const DEFAULT_RUNTIME_CONFIG_FILENAME = 'runtime-config.json';
|
|
22
17
|
|
|
18
|
+
export interface StaticWebsiteProps {
|
|
19
|
+
readonly websiteFilePath: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
23
22
|
/**
|
|
24
23
|
* Deploys a Static Website using by default a private S3 bucket as an origin and Cloudfront as the entrypoint.
|
|
25
24
|
*
|
|
@@ -33,61 +32,68 @@ export class StaticWebsite extends Construct {
|
|
|
33
32
|
public readonly cloudFrontDistribution: Distribution;
|
|
34
33
|
public readonly bucketDeployment: BucketDeployment;
|
|
35
34
|
|
|
36
|
-
constructor(
|
|
35
|
+
constructor(
|
|
36
|
+
scope: Construct,
|
|
37
|
+
id: string,
|
|
38
|
+
{ websiteFilePath }: StaticWebsiteProps
|
|
39
|
+
) {
|
|
37
40
|
super(scope, id);
|
|
38
|
-
|
|
39
41
|
this.node.setContext(
|
|
40
42
|
'@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy',
|
|
41
43
|
true
|
|
42
44
|
);
|
|
43
45
|
|
|
46
|
+
const websiteKey = new Key(this, 'WebsiteKey', {
|
|
47
|
+
enableKeyRotation: true,
|
|
48
|
+
});
|
|
49
|
+
|
|
44
50
|
const accessLogsBucket = new Bucket(this, 'AccessLogsBucket', {
|
|
45
51
|
versioned: false,
|
|
46
52
|
enforceSSL: true,
|
|
47
53
|
autoDeleteObjects: true,
|
|
48
54
|
removalPolicy: RemovalPolicy.DESTROY,
|
|
49
|
-
encryption: BucketEncryption.
|
|
55
|
+
encryption: BucketEncryption.KMS,
|
|
56
|
+
encryptionKey: websiteKey,
|
|
50
57
|
objectOwnership: ObjectOwnership.OBJECT_WRITER,
|
|
51
58
|
publicReadAccess: false,
|
|
52
59
|
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
|
|
53
60
|
});
|
|
54
|
-
|
|
55
61
|
// S3 Bucket to hold website files
|
|
56
62
|
this.websiteBucket = new Bucket(this, 'WebsiteBucket', {
|
|
57
63
|
versioned: true,
|
|
58
64
|
enforceSSL: true,
|
|
59
65
|
autoDeleteObjects: true,
|
|
60
66
|
removalPolicy: RemovalPolicy.DESTROY,
|
|
61
|
-
encryption: BucketEncryption.
|
|
67
|
+
encryption: BucketEncryption.KMS,
|
|
68
|
+
encryptionKey: websiteKey,
|
|
62
69
|
objectOwnership: ObjectOwnership.BUCKET_OWNER_ENFORCED,
|
|
63
70
|
publicReadAccess: false,
|
|
64
71
|
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
|
|
65
72
|
serverAccessLogsPrefix: 'website-access-logs',
|
|
66
73
|
serverAccessLogsBucket: accessLogsBucket,
|
|
67
74
|
});
|
|
68
|
-
|
|
69
75
|
// Web ACL
|
|
70
|
-
const
|
|
76
|
+
const wafStack = new CloudfrontWebAcl(this, 'waf');
|
|
71
77
|
|
|
72
78
|
// Cloudfront Distribution
|
|
73
79
|
const logBucket = new Bucket(this, 'DistributionLogBucket', {
|
|
74
80
|
enforceSSL: true,
|
|
75
81
|
autoDeleteObjects: true,
|
|
76
82
|
removalPolicy: RemovalPolicy.DESTROY,
|
|
77
|
-
encryption: BucketEncryption.
|
|
83
|
+
encryption: BucketEncryption.KMS,
|
|
84
|
+
encryptionKey: websiteKey,
|
|
78
85
|
objectOwnership: ObjectOwnership.BUCKET_OWNER_PREFERRED,
|
|
79
86
|
publicReadAccess: false,
|
|
80
87
|
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
|
|
81
88
|
serverAccessLogsPrefix: 'distribution-access-logs',
|
|
82
89
|
serverAccessLogsBucket: accessLogsBucket,
|
|
83
90
|
});
|
|
84
|
-
|
|
85
91
|
const defaultRootObject = 'index.html';
|
|
86
92
|
this.cloudFrontDistribution = new Distribution(
|
|
87
93
|
this,
|
|
88
94
|
'CloudfrontDistribution',
|
|
89
95
|
{
|
|
90
|
-
webAclId:
|
|
96
|
+
webAclId: wafStack.wafArn,
|
|
91
97
|
enableLogging: true,
|
|
92
98
|
logBucket: logBucket,
|
|
93
99
|
defaultBehavior: {
|
|
@@ -101,48 +107,42 @@ export class StaticWebsite extends Construct {
|
|
|
101
107
|
responseHttpStatus: 200,
|
|
102
108
|
responsePagePath: `/${defaultRootObject}`,
|
|
103
109
|
},
|
|
110
|
+
{
|
|
111
|
+
httpStatus: 403, // We need to redirect reloads from paths (e.g. /foo/bar) to index.html for single page apps
|
|
112
|
+
responseHttpStatus: 200,
|
|
113
|
+
responsePagePath: `/${defaultRootObject}`,
|
|
114
|
+
},
|
|
104
115
|
],
|
|
105
116
|
}
|
|
106
117
|
);
|
|
107
|
-
|
|
108
118
|
// Deploy Website
|
|
109
|
-
const runtimeConfig = RuntimeConfig.ensure(this).config;
|
|
110
119
|
this.bucketDeployment = new BucketDeployment(this, 'WebsiteDeployment', {
|
|
111
120
|
sources: [
|
|
112
|
-
Source.asset(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
121
|
+
Source.asset(websiteFilePath),
|
|
122
|
+
Source.jsonData(
|
|
123
|
+
DEFAULT_RUNTIME_CONFIG_FILENAME,
|
|
124
|
+
this.resolveTokens(RuntimeConfig.ensure(this).config)
|
|
116
125
|
),
|
|
117
|
-
...(Object.keys(runtimeConfig).length > 0
|
|
118
|
-
? [Source.jsonData(DEFAULT_RUNTIME_CONFIG_FILENAME, this.lazilyRender(runtimeConfig))]
|
|
119
|
-
: []),
|
|
120
126
|
],
|
|
121
127
|
destinationBucket: this.websiteBucket,
|
|
122
128
|
// Files in the distribution's edge caches will be invalidated after files are uploaded to the destination bucket.
|
|
123
129
|
distribution: this.cloudFrontDistribution,
|
|
124
130
|
});
|
|
125
|
-
|
|
126
131
|
new CfnOutput(this, 'DistributionDomainName', {
|
|
127
132
|
value: this.cloudFrontDistribution.domainName,
|
|
128
133
|
});
|
|
129
|
-
|
|
130
|
-
this.suppressCDKNagViolations();
|
|
131
134
|
}
|
|
132
|
-
|
|
133
|
-
private lazilyRender = (payload: any) => Lazy.any({
|
|
134
|
-
produce: () => this.resolveTokens(payload),
|
|
135
|
-
});
|
|
136
|
-
|
|
137
135
|
private resolveTokens = (payload: any) => {
|
|
138
136
|
const _payload: Record<string, any> = {};
|
|
139
|
-
|
|
140
137
|
Object.entries(payload).forEach(([key, value]) => {
|
|
141
|
-
if (
|
|
138
|
+
if (
|
|
139
|
+
Token.isUnresolved(value) ||
|
|
140
|
+
(typeof value === 'string' && value.endsWith('}}'))
|
|
141
|
+
) {
|
|
142
142
|
_payload[key] = new CfnJson(this, `ResolveToken-${key}`, {
|
|
143
|
-
|
|
143
|
+
value,
|
|
144
144
|
}).value;
|
|
145
|
-
} else if (typeof value ===
|
|
145
|
+
} else if (typeof value === 'object') {
|
|
146
146
|
_payload[key] = this.resolveTokens(value);
|
|
147
147
|
} else if (Array.isArray(value)) {
|
|
148
148
|
_payload[key] = value.map((v) => this.resolveTokens(v));
|
|
@@ -150,114 +150,49 @@ export class StaticWebsite extends Construct {
|
|
|
150
150
|
_payload[key] = value;
|
|
151
151
|
}
|
|
152
152
|
});
|
|
153
|
-
|
|
154
153
|
return _payload;
|
|
155
|
-
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
export class CloudfrontWebAcl extends Stack {
|
|
158
|
+
public readonly wafArn;
|
|
159
|
+
constructor(scope: Construct, id: string) {
|
|
160
|
+
super(scope, id, {
|
|
161
|
+
env: {
|
|
162
|
+
region: 'us-east-1',
|
|
163
|
+
account: Stack.of(scope).account,
|
|
164
|
+
},
|
|
165
|
+
crossRegionReferences: true,
|
|
166
|
+
});
|
|
159
167
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
168
|
+
this.wafArn = new CfnWebACL(this, 'WebAcl', {
|
|
169
|
+
defaultAction: { allow: {} },
|
|
170
|
+
scope: 'CLOUDFRONT',
|
|
171
|
+
visibilityConfig: {
|
|
172
|
+
cloudWatchMetricsEnabled: true,
|
|
173
|
+
metricName: id,
|
|
174
|
+
sampledRequestsEnabled: true,
|
|
175
|
+
},
|
|
176
|
+
rules: [
|
|
163
177
|
{
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
178
|
+
name: 'CRSRule',
|
|
179
|
+
priority: 0,
|
|
180
|
+
statement: {
|
|
181
|
+
managedRuleGroupStatement: {
|
|
182
|
+
name: 'AWSManagedRulesCommonRuleSet',
|
|
183
|
+
vendorName: 'AWS',
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
visibilityConfig: {
|
|
187
|
+
cloudWatchMetricsEnabled: true,
|
|
188
|
+
metricName: 'MetricForWebACLCDK-CRS',
|
|
189
|
+
sampledRequestsEnabled: true,
|
|
190
|
+
},
|
|
191
|
+
overrideAction: {
|
|
192
|
+
none: {},
|
|
193
|
+
},
|
|
167
194
|
},
|
|
168
195
|
],
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
[
|
|
173
|
-
'AwsSolutions-CFR4',
|
|
174
|
-
'AwsPrototyping-CloudFrontDistributionHttpsViewerNoOutdatedSSL',
|
|
175
|
-
].forEach((RuleId) => {
|
|
176
|
-
NagSuppressions.addResourceSuppressions(this.cloudFrontDistribution, [
|
|
177
|
-
{
|
|
178
|
-
id: RuleId,
|
|
179
|
-
reason:
|
|
180
|
-
'Certificate is not mandatory therefore the Cloudfront certificate will be used.',
|
|
181
|
-
},
|
|
182
|
-
]);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
['AwsSolutions-L1', 'AwsPrototyping-LambdaLatestVersion'].forEach(
|
|
186
|
-
(RuleId) => {
|
|
187
|
-
NagSuppressions.addResourceSuppressions(
|
|
188
|
-
this,
|
|
189
|
-
[
|
|
190
|
-
{
|
|
191
|
-
id: RuleId,
|
|
192
|
-
reason:
|
|
193
|
-
'Latest runtime cannot be configured. CDK will need to upgrade the BucketDeployment construct accordingly.',
|
|
194
|
-
},
|
|
195
|
-
],
|
|
196
|
-
true
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
['AwsSolutions-IAM5', 'AwsPrototyping-IAMNoWildcardPermissions'].forEach(
|
|
202
|
-
(RuleId) => {
|
|
203
|
-
NagSuppressions.addResourceSuppressions(
|
|
204
|
-
this,
|
|
205
|
-
[
|
|
206
|
-
{
|
|
207
|
-
id: RuleId,
|
|
208
|
-
reason:
|
|
209
|
-
'All Policies have been scoped to a Bucket. Given Buckets can contain arbitrary content, wildcard resources with bucket scope are required.',
|
|
210
|
-
appliesTo: [
|
|
211
|
-
{
|
|
212
|
-
regex: '/^Action::s3:.*$/g',
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
regex: `/^Resource::.*$/g`,
|
|
216
|
-
},
|
|
217
|
-
],
|
|
218
|
-
},
|
|
219
|
-
],
|
|
220
|
-
true
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
['AwsSolutions-IAM4', 'AwsPrototyping-IAMNoManagedPolicies'].forEach(
|
|
226
|
-
(RuleId) => {
|
|
227
|
-
NagSuppressions.addResourceSuppressions(
|
|
228
|
-
this,
|
|
229
|
-
[
|
|
230
|
-
{
|
|
231
|
-
id: RuleId,
|
|
232
|
-
reason:
|
|
233
|
-
'Buckets can contain arbitrary content, therefore wildcard resources under a bucket are required.',
|
|
234
|
-
appliesTo: [
|
|
235
|
-
{
|
|
236
|
-
regex: `/^Policy::arn:${PDKNag.getStackPartitionRegex(
|
|
237
|
-
stack
|
|
238
|
-
)}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole$/g`,
|
|
239
|
-
},
|
|
240
|
-
],
|
|
241
|
-
},
|
|
242
|
-
],
|
|
243
|
-
true
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
['AwsSolutions-S1', 'AwsPrototyping-S3BucketLoggingEnabled'].forEach(
|
|
249
|
-
(RuleId) => {
|
|
250
|
-
NagSuppressions.addResourceSuppressions(
|
|
251
|
-
this,
|
|
252
|
-
[
|
|
253
|
-
{
|
|
254
|
-
id: RuleId,
|
|
255
|
-
reason: 'Access Log buckets should not have s3 bucket logging',
|
|
256
|
-
},
|
|
257
|
-
],
|
|
258
|
-
true
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
);
|
|
262
|
-
};
|
|
196
|
+
}).attrArn;
|
|
197
|
+
}
|
|
263
198
|
}
|