Skip to content

Commit caeaed3

Browse files
authored
Merge pull request #40 from rehanvdm/feature/release-v1.1.1
feat: release v1.1.1
2 parents 8b29026 + f02edd6 commit caeaed3

File tree

10 files changed

+263
-35
lines changed

10 files changed

+263
-35
lines changed

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ export class App extends cdk.Stack {
6868
/* Optional, if not specified uses default CloudFront and Cognito domains */
6969
domain: {
7070
name: 'demo.serverless-website-analytics.com',
71-
certificate: wildCardCertUsEast1,
71+
/* The certificate must be in us-east-1 */
72+
usEast1Certificate: wildCardCertUsEast1,
7273
/* Optional, if not specified then no DNS records will be created. You will have to create the DNS records yourself. */
7374
hostedZone: route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
7475
hostedZoneId: 'Z00387321EPPVXNC20CIS',
@@ -117,6 +118,19 @@ For a full list of options see the [API.md](https://github.com/rehanvdm/serverle
117118

118119
You can see an example implementation of the demo site [here](https://github.com/rehanvdm/serverless-website-analytics-test)
119120

121+
#### Certificate Requirements
122+
When specifying a domain, the certificate must be in `us-east-1` but your stack can be in ANY region. This is because
123+
CloudFront requires the certificate to be in `us-east-1`.
124+
125+
You have one of two choices:
126+
- Create the certificate in `us-east-1` manually (Click Ops) and import it from the Cert ARN as in the [demo example](https://github.com/rehanvdm/serverless-website-analytics-test/blob/main/lib/app.ts#L16).
127+
- Create a `us-east-1` stack that your main stack (that contains this construct) depends. This main stack can be in any region.
128+
Create the Certificate in the `us-east-1` stack and export the cert ARN. Then import the cert ARN in your main stack.
129+
Ensure that you have the [crossRegionReferences](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stack.html#crossregionreferences) flag set
130+
on both stacks so that the CDK can export and import the Cert ARN via SSM. This is necessary because CloudFormation
131+
can not export and import values across regions. Alternatively you can DIY it, here is a blog from [AWS](https://aws.amazon.com/blogs/infrastructure-and-automation/read-parameters-across-aws-regions-with-aws-cloudformation-custom-resources/)
132+
and a quick example from [SO](https://stackoverflow.com/questions/59774627/cloudformation-cross-region-reference).
133+
120134
### Client side setup
121135

122136
There are **two ways to use the client**:

docs/API.md

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ export class App extends cdk.Stack {
6868
/* Optional, if not specified uses default CloudFront and Cognito domains */
6969
domain: {
7070
name: 'demo.serverless-website-analytics.com',
71-
certificate: wildCardCertUsEast1,
71+
/* The certificate must be in us-east-1 */
72+
usEast1Certificate: wildCardCertUsEast1,
7273
/* Optional, if not specified then no DNS records will be created. You will have to create the DNS records yourself. */
7374
hostedZone: route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
7475
hostedZoneId: 'Z00387321EPPVXNC20CIS',
@@ -117,6 +118,19 @@ For a full list of options see the [API.md](https://github.com/rehanvdm/serverle
117118

118119
You can see an example implementation of the demo site [here](https://github.com/rehanvdm/serverless-website-analytics-test)
119120

121+
#### Certificate Requirements
122+
When specifying a domain, the certificate must be in `us-east-1` but your stack can be in ANY region. This is because
123+
CloudFront requires the certificate to be in `us-east-1`.
124+
125+
You have one of two choices:
126+
- Create the certificate in `us-east-1` manually (Click Ops) and import it from the Cert ARN as in the [demo example](https://github.com/rehanvdm/serverless-website-analytics-test/blob/main/lib/app.ts#L16).
127+
- Create a `us-east-1` stack that your main stack (that contains this construct) depends. This main stack can be in any region.
128+
Create the Certificate in the `us-east-1` stack and export the cert ARN. Then import the cert ARN in your main stack.
129+
Ensure that you have the [crossRegionReferences](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stack.html#crossregionreferences) flag set
130+
on both stacks so that the CDK can export and import the Cert ARN via SSM. This is necessary because CloudFormation
131+
can not export and import values across regions. Alternatively you can DIY it, here is a blog from [AWS](https://aws.amazon.com/blogs/infrastructure-and-automation/read-parameters-across-aws-regions-with-aws-cloudformation-custom-resources/)
132+
and a quick example from [SO](https://stackoverflow.com/questions/59774627/cloudformation-cross-region-reference).
133+
120134
### Client side setup
121135

122136
There are **two ways to use the client**:
@@ -553,37 +567,40 @@ const domain: Domain = { ... }
553567

554568
| **Name** | **Type** | **Description** |
555569
| --- | --- | --- |
556-
| <code><a href="#serverless-website-analytics.Domain.property.certificate">certificate</a></code> | <code>aws-cdk-lib.aws_certificatemanager.ICertificate</code> | The certificate to use for the domain. |
557570
| <code><a href="#serverless-website-analytics.Domain.property.name">name</a></code> | <code>string</code> | Name of the domain to use for the site, example: `serverless-website-analytics.com`. |
571+
| <code><a href="#serverless-website-analytics.Domain.property.certificate">certificate</a></code> | <code>aws-cdk-lib.aws_certificatemanager.ICertificate</code> | The certificate to use for the domain. |
558572
| <code><a href="#serverless-website-analytics.Domain.property.hostedZone">hostedZone</a></code> | <code>aws-cdk-lib.aws_route53.IHostedZone</code> | Optional, if not specified then no DNS records will be created. |
559573
| <code><a href="#serverless-website-analytics.Domain.property.trackOwnDomain">trackOwnDomain</a></code> | <code>boolean</code> | Optional, if specified, it adds tracking to the dashboard. |
574+
| <code><a href="#serverless-website-analytics.Domain.property.usEast1Certificate">usEast1Certificate</a></code> | <code>aws-cdk-lib.aws_certificatemanager.ICertificate</code> | The certificate to use for the domain. |
560575

561576
---
562577

563-
##### `certificate`<sup>Required</sup> <a name="certificate" id="serverless-website-analytics.Domain.property.certificate"></a>
578+
##### `name`<sup>Required</sup> <a name="name" id="serverless-website-analytics.Domain.property.name"></a>
564579

565580
```typescript
566-
public readonly certificate: ICertificate;
581+
public readonly name: string;
567582
```
568583

569-
- *Type:* aws-cdk-lib.aws_certificatemanager.ICertificate
570-
571-
The certificate to use for the domain.
584+
- *Type:* string
572585

573-
This certificate must be in the `us-east-1` region. It must be for the
574-
domain specified in `domain.name` and {auth.cognito.loginSubDomain}.{domain.name}`
586+
Name of the domain to use for the site, example: `serverless-website-analytics.com`.
575587

576588
---
577589

578-
##### `name`<sup>Required</sup> <a name="name" id="serverless-website-analytics.Domain.property.name"></a>
590+
##### ~~`certificate`~~<sup>Optional</sup> <a name="certificate" id="serverless-website-analytics.Domain.property.certificate"></a>
591+
592+
- *Deprecated:* Use `usEast1Certificate` instead.
579593

580594
```typescript
581-
public readonly name: string;
595+
public readonly certificate: ICertificate;
582596
```
583597

584-
- *Type:* string
598+
- *Type:* aws-cdk-lib.aws_certificatemanager.ICertificate
585599

586-
Name of the domain to use for the site, example: `serverless-website-analytics.com`.
600+
The certificate to use for the domain.
601+
602+
This certificate must be in the `us-east-1` region. It must be for the
603+
domain specified in `domain.name` and {auth.cognito.loginSubDomain}.{domain.name}`
587604

588605
---
589606

@@ -619,6 +636,23 @@ the serverless-website-analytics dashboard page.
619636

620637
---
621638

639+
##### `usEast1Certificate`<sup>Optional</sup> <a name="usEast1Certificate" id="serverless-website-analytics.Domain.property.usEast1Certificate"></a>
640+
641+
```typescript
642+
public readonly usEast1Certificate: ICertificate;
643+
```
644+
645+
- *Type:* aws-cdk-lib.aws_certificatemanager.ICertificate
646+
647+
The certificate to use for the domain.
648+
649+
This certificate must be in the `us-east-1` region. It must be for the
650+
domain specified in `domain.name` and {auth.cognito.loginSubDomain}.{domain.name}`
651+
652+
Required when specifying Domain.
653+
654+
---
655+
622656
### Observability <a name="Observability" id="serverless-website-analytics.Observability"></a>
623657

624658
#### Initializer <a name="Initializer" id="serverless-website-analytics.Observability.Initializer"></a>

src/frontend.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function frontend(
7272
? {}
7373
: {
7474
domainNames: [props.domain.name],
75-
certificate: props.domain.certificate,
75+
certificate: props.domain.usEast1Certificate,
7676
};
7777

7878
const defaultBucketOrigin = new origins.HttpOrigin(frontendBucket.bucketWebsiteDomainName, {
@@ -207,7 +207,7 @@ export function frontend(
207207
const cognitoDomain = authProps.userPool!.addDomain('custom-domain', {
208208
customDomain: {
209209
domainName: `${props.auth.cognito.loginSubDomain}.${props.domain.name}`,
210-
certificate: props.domain.certificate,
210+
certificate: props.domain.usEast1Certificate!,
211211
},
212212
});
213213
new cdk.CfnOutput(scope, name('CognitoHostedUrl'), {

src/index.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Arn, ArnFormat } from 'aws-cdk-lib';
21
import * as cert from 'aws-cdk-lib/aws-certificatemanager';
32
import * as route53 from 'aws-cdk-lib/aws-route53';
43
import * as sns from 'aws-cdk-lib/aws-sns';
@@ -82,8 +81,17 @@ export interface Domain {
8281
/**
8382
* The certificate to use for the domain. This certificate must be in the `us-east-1` region. It must be for the
8483
* domain specified in `domain.name` and {auth.cognito.loginSubDomain}.{domain.name}`
84+
* @deprecated Use `usEast1Certificate` instead.
8585
*/
86-
readonly certificate: cert.ICertificate;
86+
readonly certificate?: cert.ICertificate;
87+
88+
/**
89+
* The certificate to use for the domain. This certificate must be in the `us-east-1` region. It must be for the
90+
* domain specified in `domain.name` and {auth.cognito.loginSubDomain}.{domain.name}`
91+
*
92+
* Required when specifying Domain.
93+
*/
94+
readonly usEast1Certificate?: cert.ICertificate;
8795

8896
/**
8997
* Optional, if not specified then no DNS records will be created. You will have to create the DNS records yourself.
@@ -222,12 +230,7 @@ export class Swa extends Construct {
222230
return id + '-' + resourceId;
223231
}
224232

225-
if (props.domain?.certificate) {
226-
const certRegion = Arn.split(props.domain.certificate.certificateArn, ArnFormat.COLON_RESOURCE_NAME).region;
227-
if (certRegion !== 'us-east-1') {
228-
throw new Error(`Certificate must be in us-east-1, not in ${certRegion}, this is a requirement for CloudFront`);
229-
}
230-
}
233+
/* Other pre-CloudFormation checks here */
231234

232235
if (props.firehoseBufferInterval && (props.firehoseBufferInterval < 60 || props.firehoseBufferInterval > 900)) {
233236
throw new Error('`firehoseBufferInterval` must be between 60 and 900 seconds');
@@ -238,6 +241,23 @@ export class Swa extends Construct {
238241
props.allowedOrigins.push(`https://${props.domain.name}`);
239242
}
240243

244+
/* Remapping `domain.certificate` to `domain.usEast1Certificate` */
245+
if (props.domain) {
246+
if (!props.domain?.usEast1Certificate && !props.domain?.certificate)
247+
throw new Error('`domain.usEast1Certificate` must be specified');
248+
249+
if (props.domain?.certificate) {
250+
props = {
251+
...props,
252+
domain: {
253+
...props.domain,
254+
usEast1Certificate: props.domain.certificate,
255+
certificate: undefined,
256+
},
257+
};
258+
}
259+
}
260+
241261
const authProps = auth(scope, name, props);
242262
const backendAnalyticsProps = backendAnalytics(scope, name, props);
243263
const backendProps = backend(scope, name, props, authProps, backendAnalyticsProps);
3.27 MB
Binary file not shown.

src/src/frontend/src/stores/system.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,21 @@ export const getSystemStore = defineStore('counter', () => {
88

99
const frontendEnvironmentQueried = ref(false);
1010
const frontendEnvironment: Ref<FrontendEnvironment> = ref({});
11-
const cognitoLoginUrlWithRedirect = computed(
12-
() =>
11+
const cognitoLoginUrlWithRedirect = computed(() => {
12+
const urlSplitByQuery = window.location.href.split('?');
13+
14+
let ret =
1315
frontendEnvironment.value.cognitoLoginUrl +
1416
'&redirect_uri=' +
15-
encodeURIComponent(window.location.href) +
16-
'login_callback'
17-
);
17+
encodeURIComponent(urlSplitByQuery[0]) +
18+
'login_callback';
19+
20+
if (urlSplitByQuery.length > 1) {
21+
// Have to double encode because Cognito decodes when sending this back and we need it preserved
22+
ret += '&state=' + encodeURIComponent(encodeURIComponent(urlSplitByQuery[1]));
23+
}
24+
25+
return ret;
26+
});
1827
return { apiJwtToken, frontendEnvironmentQueried, frontendEnvironment, cognitoLoginUrlWithRedirect };
1928
});

src/src/frontend/src/views/login_callback.vue

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,22 @@ import router from '@frontend/src/router';
1010
1111
onMounted(() => {
1212
const idToken = new URLSearchParams(window.location.hash).get('#id_token');
13+
let state = new URLSearchParams(window.location.href).get('state');
14+
let queryObject: Record<string, string> | undefined = undefined;
15+
if(state)
16+
{
17+
state = decodeURIComponent(state);
18+
queryObject = state.split("&").reduce((acc: Record<string, string>, pair) => {
19+
const [key, value] = pair.split("=");
20+
acc[key] = value;
21+
return acc;
22+
}, {});
23+
}
1324
if(idToken)
1425
{
1526
const systemStore = getSystemStore();
1627
systemStore.$patch({apiJwtToken: idToken});
17-
router.push({name: 'page_stats'});
28+
router.push({name: 'page_stats', query: queryObject});
1829
}
1930
2031
});

src/src/frontend/src/views/page_stats/index.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,9 @@ const showClearFilters = computed(() => {
234234
filter.value.utm_content;
235235
});
236236
237-
watch([sites, dateFilter, filter], () => {
237+
watch([selectedSitesConfirmed, dateFilter, filter], () => {
238238
router.push({ query: {
239-
sites: encodeURIComponent(selectedSites.value.join(',')),
239+
sites: encodeURIComponent(selectedSitesConfirmed.value.join(',')),
240240
date: encodeURIComponent(dateFilter.value.map(d => d.toISOString()).join(',')),
241241
filter: Object.values(filter.value).filter(v => v).length ? encodeURIComponent(JSON.stringify(filter!.value)) : undefined,
242242
}

test/auth.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ describe('Auth', () => {
9090
},
9191
},
9292
});
93-
const assert = Template.fromStack(stack);
94-
} catch (e) {
93+
Template.fromStack(stack);
94+
} catch (e: any) {
9595
expect(e.message).toBe('Specify only `basicAuth` or `cognito` for `auth` but not both');
9696
}
9797
});

0 commit comments

Comments
 (0)