@anthusai/papyrus 1.0.0-next.9 → 1.1.0
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/amplify/auth/prospectus-google-client.js +66 -0
- package/amplify/auth/prospectus-google-client.ts +71 -0
- package/amplify/auth/redirect-urls.js +54 -0
- package/amplify/auth/redirect-urls.ts +66 -0
- package/amplify/auth/resource.js +23 -5
- package/amplify/auth/resource.ts +31 -5
- package/amplify/data/resource.js +42 -80
- package/amplify/data/resource.ts +42 -80
- package/amplify/data/schema.js +45 -62
- package/amplify/data/schema.ts +47 -62
- package/amplify/functions/category-action/handler.ts +5 -3
- package/amplify/functions/console-chat-responder/py/execute_tactus_runner.py +0 -10
- package/amplify/functions/console-chat-responder/resource.js +0 -7
- package/amplify/functions/console-chat-responder/resource.ts +0 -8
- package/amplify/functions/console-chat-responder/src/main.rs +0 -9
- package/amplify/functions/content-actions/handler.py +143 -1
- package/amplify/functions/content-actions/requirements.txt +2 -1
- package/amplify/functions/content-actions/resource.js +6 -1
- package/amplify/functions/content-actions/resource.ts +9 -2
- package/amplify/functions/email-submission-processor/handler.py +3 -1
- package/amplify/functions/renditions/handler.py +120 -0
- package/amplify/functions/renditions/requirements.txt +4 -0
- package/amplify/functions/ses-inbound-receive/handler.ts +32 -4
- package/amplify/functions/shared/email-submission.ts +109 -1
- package/amplify/functions/shared/python-bundle.js +42 -2
- package/amplify/functions/shared/python-bundle.ts +47 -2
- package/amplify/functions/shared/reference-curation-metadata.ts +29 -0
- package/amplify/inbound-email/ses-intake.js +86 -0
- package/amplify/inbound-email/ses-intake.ts +110 -0
- package/amplify/plugins/registry.js +19 -0
- package/amplify/plugins/registry.ts +23 -0
- package/amplify/plugins/signup/function/handler.py +200 -0
- package/amplify/plugins/signup/function/resource.js +27 -0
- package/amplify/plugins/signup/function/resource.ts +35 -0
- package/amplify/plugins/signup/papyrus.plugin.js +59 -0
- package/amplify/plugins/signup/papyrus.plugin.ts +74 -0
- package/amplify/plugins/signup/schema.js +40 -0
- package/amplify/plugins/signup/schema.ts +41 -0
- package/amplify/plugins/signup/waf.js +48 -0
- package/amplify/plugins/signup/waf.ts +51 -0
- package/amplify/plugins/types.js +1 -0
- package/amplify/plugins/types.ts +39 -0
- package/amplify/plugins/wire.js +24 -0
- package/amplify/plugins/wire.ts +26 -0
- package/amplify/publication/default-publication.js +18 -0
- package/amplify/publication/default-publication.ts +20 -0
- package/amplify/publication/invalidation-alarm.js +26 -0
- package/amplify/publication/invalidation-alarm.ts +30 -0
- package/amplify/publication/redirect-and-index.js +37 -0
- package/amplify/publication/renditions-function.js +74 -0
- package/amplify/publication/renditions-function.ts +112 -0
- package/amplify/publication/renditions-settings.js +51 -0
- package/amplify/publication/renditions-settings.ts +58 -0
- package/amplify/publication/reproject-on-deploy.js +73 -0
- package/amplify/publication/reproject-on-deploy.ts +78 -0
- package/amplify/publication/s3-projection-settings.js +67 -0
- package/amplify/publication/s3-projection-settings.ts +101 -0
- package/amplify/publication/s3-projection.js +151 -0
- package/amplify/publication/s3-projection.ts +201 -0
- package/amplify/resolver-waves.js +26 -0
- package/amplify/resolver-waves.ts +34 -0
- package/amplify/revalidate-invalidation-settings.js +30 -0
- package/amplify/revalidate-invalidation-settings.ts +35 -0
- package/amplify/revalidate-secret-policy.js +13 -0
- package/amplify/revalidate-secret-policy.ts +19 -0
- package/amplify/site-backend-config.js +65 -1
- package/amplify/site-backend-config.ts +153 -2
- package/amplify/site-backend-names.js +103 -0
- package/amplify/site-backend-names.ts +166 -0
- package/amplify/site-backend.js +150 -176
- package/amplify/site-backend.ts +189 -201
- package/amplify/storage-backups/construct.js +31 -0
- package/amplify/storage-backups/construct.ts +47 -0
- package/app/[year]/[month]/[day]/[articleSlug]/page.tsx +8 -1
- package/app/[year]/[month]/[day]/edition-route-page.tsx +2 -0
- package/app/[year]/[month]/[day]/page/[pageNumber]/page.tsx +6 -1
- package/app/[year]/[month]/[day]/page.tsx +1 -1
- package/app/[year]/[month]/[day]/section/[sectionKey]/page.tsx +6 -1
- package/app/api/archive/editions/route.ts +1 -1
- package/app/api/auth/prospectus/callback/route.ts +9 -0
- package/app/api/auth/prospectus/selection/route.ts +9 -0
- package/app/api/auth/prospectus/session/route.ts +9 -0
- package/app/api/auth/prospectus/sign-in/route.ts +9 -0
- package/app/api/auth/prospectus/sign-out/route.ts +9 -0
- package/app/api/cyclotron/priming-proof/route.ts +11 -0
- package/app/api/media/[...path]/route.ts +5 -16
- package/app/api/revalidate/route.ts +15 -7
- package/app/archive/page.tsx +1 -1
- package/app/articles/[slug]/page.tsx +7 -5
- package/app/blog-rhythm.css +278 -0
- package/app/cyclotron/page.tsx +13 -0
- package/app/globals.css +2 -0
- package/app/layout.tsx +16 -0
- package/app/newsroom/[section]/[...selection]/page.tsx +9 -6
- package/app/newsroom/[section]/page.tsx +6 -3
- package/app/newsroom/layout.tsx +4 -9
- package/app/newsroom/settings/page.tsx +6 -0
- package/app/newsroom/work/page.tsx +10 -0
- package/app/page.tsx +22 -8
- package/app/robots.ts +2 -5
- package/app/tailwind.css +1 -6
- package/components/amplify-client-provider.tsx +5 -22
- package/components/archive-shell.tsx +2 -22
- package/components/article-video.tsx +152 -5
- package/components/blog-page-background.tsx +23 -6
- package/components/news-desk-page.tsx +4 -15
- package/components/news-desk-taxonomy-client.ts +90 -3
- package/components/newspaper.tsx +6 -2
- package/components/newsroom-access-gate.tsx +2 -1
- package/components/newsroom-board-view.tsx +99 -0
- package/components/newsroom-client-shell.tsx +12 -0
- package/components/newsroom-curation-provider.tsx +17 -0
- package/components/newsroom-fonts/playfair-display-700.ttf +0 -0
- package/components/newsroom-fonts/playfair-display-900.ttf +0 -0
- package/components/newsroom-fonts/playfair-display-OFL.txt +93 -0
- package/components/newsroom-fonts/plus-jakarta-sans-400.ttf +0 -0
- package/components/newsroom-fonts/plus-jakarta-sans-600.ttf +0 -0
- package/components/newsroom-fonts/plus-jakarta-sans-OFL.txt +93 -0
- package/components/newsroom-newsprint-board.css +48 -0
- package/components/newsroom-ops-overview.tsx +28 -11
- package/components/newsroom-ops-shell.tsx +7 -1
- package/components/newsroom-organizer-shell.tsx +130 -0
- package/components/newsroom-organizer.css +63 -0
- package/components/newsroom-references-view.tsx +92 -8
- package/components/papyrus-console-shell.tsx +4 -2
- package/components/pictogram-figure.tsx +9 -2
- package/components/presentation-header.tsx +135 -0
- package/components/presentation-rhythm-hrule.tsx +17 -0
- package/components/priming-proof-page.tsx +94 -0
- package/components/publication-cyclotron-panel.tsx +57 -0
- package/components/reader-auth-control.tsx +8 -5
- package/components/reference-curation-controls.tsx +77 -0
- package/components/reference-cyclotron-status.tsx +109 -0
- package/components/reference-priming.tsx +18 -0
- package/components/reference-relevance-review.tsx +75 -0
- package/components/settings-page.tsx +2 -1
- package/components/signup-entries-panel.tsx +94 -0
- package/components/topic-steering-workspace.tsx +78 -8
- package/components/use-blog-header-obstacles.ts +81 -0
- package/components/use-brand-fonts-ready.ts +28 -0
- package/components/use-near-viewport.ts +30 -0
- package/components/use-rhythm-overlay.ts +29 -0
- package/infra/amplify-app-shell.js +166 -27
- package/infra/amplify-app-shell.ts +180 -31
- package/infra/build-specs.js +23 -7
- package/infra/build-specs.ts +27 -10
- package/infra/site-config.js +248 -36
- package/infra/site-config.ts +313 -51
- package/lib/amplify-outputs-path.ts +5 -1
- package/lib/amplify-server-runtime.ts +36 -1
- package/lib/approved-priming-proof-reports.ts +6 -0
- package/lib/blog-feature-solver.ts +459 -0
- package/lib/blog-rhythm.ts +134 -0
- package/lib/brand-fonts-watch.ts +43 -0
- package/lib/brand-fonts.ts +112 -0
- package/lib/cached-content-repository.ts +12 -0
- package/lib/category-repository.ts +43 -93
- package/lib/content-item-lifecycle.ts +55 -0
- package/lib/content-repository.ts +3 -0
- package/lib/content-types.ts +4 -0
- package/lib/curation-client.ts +61 -0
- package/lib/edition-routes.ts +31 -29
- package/lib/graphql-content-repository.ts +56 -22
- package/lib/media-route-response.ts +45 -0
- package/lib/newspaper-layout.ts +3 -2
- package/lib/newsroom-base-path.ts +12 -3
- package/lib/newsroom-board.ts +99 -0
- package/lib/newsroom-category-drilldown.ts +3 -2
- package/lib/newsroom-forum-routes.ts +6 -4
- package/lib/newsroom-index-filters.ts +14 -10
- package/lib/newsroom-nav.ts +3 -1
- package/lib/newsroom-organizer-nav.ts +37 -0
- package/lib/newsroom-section-routing.ts +20 -0
- package/lib/oauth-redirect-priority.ts +40 -0
- package/lib/papyrus-web-locations.ts +7 -6
- package/lib/plugin-settings.ts +11 -0
- package/lib/presentation-footer.ts +3 -2
- package/lib/pretext-layout.ts +12 -3
- package/lib/priming-proof-report.ts +90 -0
- package/lib/prospectus-publication-access.ts +53 -0
- package/lib/prospectus-session.ts +196 -0
- package/lib/prospectus-sign-in-routes.ts +284 -0
- package/lib/prospectus-site-identity.ts +303 -0
- package/lib/publication-cyclotron.ts +74 -0
- package/lib/rate-limit-retry.ts +55 -0
- package/lib/reader-base-path.ts +24 -0
- package/lib/reader-cache-lifetime.ts +5 -0
- package/lib/reader-static-params.ts +85 -4
- package/lib/reader-storage-url.ts +9 -12
- package/lib/reference-policy.ts +5 -0
- package/lib/relevance-decisions.ts +103 -0
- package/lib/relevance-status.ts +63 -0
- package/lib/renderer.ts +7 -0
- package/lib/revalidate-prewarm.ts +53 -0
- package/lib/revalidate-secret.ts +59 -0
- package/lib/robots-policy.ts +14 -0
- package/lib/root-route-redirect.ts +16 -0
- package/lib/semantic-graph.ts +7 -0
- package/lib/shared-credentials-provider.ts +61 -0
- package/lib/signup-csv.ts +24 -0
- package/lib/site-brand.ts +59 -0
- package/lib/staging-gated-path.ts +23 -0
- package/lib/staging-preview-object.ts +21 -5
- package/lib/test-editor-override.ts +4 -0
- package/lib/video-loading.ts +22 -0
- package/lib/video-script.ts +14 -0
- package/middleware.ts +11 -19
- package/package.json +73 -8
- package/renderers/pretext/article-page.tsx +39 -3
- package/renderers/pretext/presentation-shared.tsx +113 -0
- package/renderers/pretext/presentation-shell.tsx +15 -102
- package/renderers/pretext/rhythm-article-page.tsx +165 -0
- package/renderers/pretext/rhythm-blog-presentation.tsx +279 -0
- package/renderers/pretext/rhythm-shell-style.ts +35 -0
- package/routes.manifest.json +123 -12
- package/src/sync.mjs +30 -6
- package/src/with-papyrus.d.mts +9 -0
- package/src/with-papyrus.mjs +48 -3
- package/amplify/functions/graphql-jwt-authorizer/handler.ts +0 -193
- package/amplify/functions/graphql-jwt-authorizer/resource.js +0 -14
- package/amplify/functions/graphql-jwt-authorizer/resource.ts +0 -15
- package/components/ai-elements/message.tsx +0 -360
|
@@ -1,24 +1,48 @@
|
|
|
1
1
|
import { ArnFormat, CfnOutput, Duration, Fn, Stack } from "aws-cdk-lib";
|
|
2
2
|
import * as iam from "aws-cdk-lib/aws-iam";
|
|
3
3
|
import * as amplify from "aws-cdk-lib/aws-amplify";
|
|
4
|
+
import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
|
|
4
5
|
import { GITHUB_OIDC_PROVIDER_HOST } from "./github-oidc-provider.js";
|
|
5
6
|
import { cmsProductionBuildSpec, cmsStagingBuildSpec, readerBuildSpec } from "./build-specs.js";
|
|
6
|
-
import { isStagingEnabled, resolveStackName, resolveStagingDomainName } from "./site-config.js";
|
|
7
|
+
import { PROSPECTUS_PAPYRUS_SITE_ROUTES, hasCmsApp, isStagingEnabled, resolveRedirectSubDomainPrefix, resolveCmsHostName, resolveProspectusAccountsEndpoint, resolveRevalidateSecretParameterName, resolveStackName, resolveStagingDomainName, resolveStoragePreviewPrefix } from "./site-config.js";
|
|
7
8
|
function environmentVariables(variables) {
|
|
8
9
|
return Object.entries(variables).map(([name, value]) => ({ name, value }));
|
|
9
10
|
}
|
|
11
|
+
function readerCustomHeaders(rules) {
|
|
12
|
+
if (!rules)
|
|
13
|
+
return undefined;
|
|
14
|
+
const lines = ["customHeaders:"];
|
|
15
|
+
for (const rule of rules) {
|
|
16
|
+
lines.push(` - pattern: ${JSON.stringify(rule.pattern)}`, " headers:");
|
|
17
|
+
for (const [key, value] of Object.entries(rule.headers)) {
|
|
18
|
+
lines.push(` - key: ${JSON.stringify(key)}`, ` value: ${JSON.stringify(value)}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return lines.join("\n") + "\n";
|
|
22
|
+
}
|
|
10
23
|
/**
|
|
11
24
|
* Provisions the Amplify *app shell* of a Papyrus publication from its
|
|
12
25
|
* site.json:
|
|
13
26
|
*
|
|
14
27
|
* - CMS app (WEB_COMPUTE) with branches `main` (production, owns the backend)
|
|
15
28
|
* and `staging` (frontend only, reads the production backend)
|
|
16
|
-
* - for `markus-static` sites, a reader app (WEB) with branch `main
|
|
29
|
+
* - for `markus-static` sites, a reader app (WEB) with branch `main`; a
|
|
30
|
+
* `markus-static` site may omit `cms` to get the reader app alone (no CMS
|
|
31
|
+
* app, no CMS roles, no authoring role)
|
|
17
32
|
* - a domain association per branch/app when a domain is configured (without
|
|
18
33
|
* one, no AWS::Amplify::Domain is created and the default amplifyapp.com
|
|
19
34
|
* URL is used), IAM service and compute roles
|
|
20
35
|
* - `cms.staging: false` omits the staging branch and its domain
|
|
21
36
|
*
|
|
37
|
+
* Google sign-in is always on: the branches never set PAPYRUS_DISABLE_GOOGLE_OAUTH,
|
|
38
|
+
and `cms.cognitoDomainPrefix` becomes the hosted-UI domain
|
|
39
|
+
(PAPYRUS_COGNITO_DOMAIN_PREFIX) only when `cms.applyCognitoDomainPrefix` is true
|
|
40
|
+
(PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX); otherwise Amplify's generated domain is kept. The secrets GOOGLE_CLIENT_ID and
|
|
41
|
+
GOOGLE_CLIENT_SECRET are Amplify backend secrets set in the console, never here.
|
|
42
|
+
The stack also creates the `<siteId>-papyrus-authoring` role. The CLI signs
|
|
43
|
+
* AppSync requests (SigV4) with credentials for that role (SSO, OIDC or any
|
|
44
|
+
* principal in the account that may assume it); no token is stored anywhere.
|
|
45
|
+
*
|
|
22
46
|
* No repository and no access token are configured: the apps are manual-deploy
|
|
23
47
|
* until Amplify's GitHub App is connected once in the console
|
|
24
48
|
* (docs/site-hosting.md). The Papyrus backend itself is created inside the CMS
|
|
@@ -29,9 +53,16 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
29
53
|
readerAppId;
|
|
30
54
|
constructor(scope, id, config, props) {
|
|
31
55
|
super(scope, id, props);
|
|
56
|
+
if (!hasCmsApp(config)) {
|
|
57
|
+
if (!config.reader)
|
|
58
|
+
throw new Error(`site ${config.siteId} has neither a cms section nor a reader`);
|
|
59
|
+
this.readerAppId = this.addReaderApp(config, config.reader);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
32
62
|
const cmsAppName = config.cms.appName ?? `${config.siteId}-cms`;
|
|
33
63
|
const stagingEnabled = isStagingEnabled(config);
|
|
34
64
|
const stagingDomainName = resolveStagingDomainName(config);
|
|
65
|
+
const cmsHostName = resolveCmsHostName(config);
|
|
35
66
|
const cmsServiceRole = new iam.Role(this, "AmplifyServiceRole", {
|
|
36
67
|
assumedBy: new iam.ServicePrincipal("amplify.amazonaws.com"),
|
|
37
68
|
description: `Backend deploy and staging build service role for Amplify app ${cmsAppName} (AdministratorAccess: documented risk)`,
|
|
@@ -45,6 +76,44 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
45
76
|
iam.ManagedPolicy.fromAwsManagedPolicyName("CloudWatchLogsFullAccess"),
|
|
46
77
|
],
|
|
47
78
|
});
|
|
79
|
+
const revalidateSecretParameterName = config.frontend === "pretext" ? resolveRevalidateSecretParameterName(config) : undefined;
|
|
80
|
+
if (revalidateSecretParameterName) {
|
|
81
|
+
computeRole.addToPolicy(new iam.PolicyStatement({
|
|
82
|
+
sid: "ReadRevalidateSecret",
|
|
83
|
+
actions: ["ssm:GetParameter"],
|
|
84
|
+
resources: [
|
|
85
|
+
this.formatArn({ service: "ssm", resource: "parameter", resourceName: revalidateSecretParameterName.slice(1), arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
86
|
+
],
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
const prospectusAccountsApiStageArn = config.cms.prospectusAccountsApiStageArn;
|
|
90
|
+
const prospectusSessionSealingSecret = prospectusAccountsApiStageArn
|
|
91
|
+
? new secretsmanager.Secret(this, "ProspectusSessionSealingSecret", {
|
|
92
|
+
description: `Seals the Prospectus sign-in cookies of ${cmsAppName}; replacing it signs everyone out`,
|
|
93
|
+
generateSecretString: { passwordLength: 64, excludePunctuation: true },
|
|
94
|
+
})
|
|
95
|
+
: undefined;
|
|
96
|
+
const prospectusEnvironment = prospectusAccountsApiStageArn && prospectusSessionSealingSecret
|
|
97
|
+
? {
|
|
98
|
+
PAPYRUS_PROSPECTUS_ACCOUNTS_ENDPOINT: resolveProspectusAccountsEndpoint(prospectusAccountsApiStageArn),
|
|
99
|
+
PAPYRUS_PROSPECTUS_SESSION_SECRET_ARN: prospectusSessionSealingSecret.secretArn,
|
|
100
|
+
PAPYRUS_PROSPECTUS_SITE_PRODUCT: `papyrus-${config.siteId}`,
|
|
101
|
+
}
|
|
102
|
+
: {};
|
|
103
|
+
if (prospectusSessionSealingSecret) {
|
|
104
|
+
computeRole.addToPolicy(new iam.PolicyStatement({
|
|
105
|
+
sid: "ReadProspectusSessionSealingSecret",
|
|
106
|
+
actions: ["secretsmanager:GetSecretValue"],
|
|
107
|
+
resources: [prospectusSessionSealingSecret.secretArn],
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
if (prospectusAccountsApiStageArn) {
|
|
111
|
+
computeRole.addToPolicy(new iam.PolicyStatement({
|
|
112
|
+
sid: "InvokeProspectusAccountsAsRegisteredSite",
|
|
113
|
+
actions: ["execute-api:Invoke"],
|
|
114
|
+
resources: PROSPECTUS_PAPYRUS_SITE_ROUTES.map((route) => `${prospectusAccountsApiStageArn}/${route}`),
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
48
117
|
const cmsApp = new amplify.CfnApp(this, "App", {
|
|
49
118
|
name: cmsAppName,
|
|
50
119
|
description: `Papyrus newsroom CMS for ${config.siteId} (WEB_COMPUTE)`,
|
|
@@ -53,17 +122,25 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
53
122
|
computeRoleArn: computeRole.roleArn,
|
|
54
123
|
buildSpec: cmsProductionBuildSpec(config),
|
|
55
124
|
jobConfig: config.cms.buildComputeType ? { buildComputeType: config.cms.buildComputeType } : undefined,
|
|
56
|
-
customRules: [
|
|
125
|
+
customRules: [
|
|
126
|
+
...(config.cms.redirects ?? []).map((redirect) => ({
|
|
127
|
+
source: `https://${redirect.source}`,
|
|
128
|
+
target: `https://${resolveCmsHostName(config)}`,
|
|
129
|
+
status: String(redirect.status),
|
|
130
|
+
})),
|
|
131
|
+
{ source: "/<*>", target: "/index.html", status: "404-200" },
|
|
132
|
+
],
|
|
57
133
|
});
|
|
58
134
|
const stagingMode = config.frontend === "markus-static"
|
|
59
135
|
? { PAPYRUS_STAGING_PREVIEW: "static" }
|
|
60
136
|
: { PAPYRUS_CONTENT_SOURCE: "drafts" };
|
|
61
|
-
const productionOrigin =
|
|
62
|
-
? `https://${
|
|
137
|
+
const productionOrigin = cmsHostName
|
|
138
|
+
? `https://${cmsHostName}/`
|
|
63
139
|
: Fn.join("", ["https://main.", cmsApp.attrDefaultDomain, "/"]);
|
|
64
140
|
const stagingOrigin = stagingDomainName
|
|
65
141
|
? `https://${stagingDomainName}/`
|
|
66
142
|
: Fn.join("", ["https://staging.", cmsApp.attrDefaultDomain, "/"]);
|
|
143
|
+
const sharedRootDomain = config.cms.domainPrefix !== undefined;
|
|
67
144
|
const defaultOriginsToAllow = [
|
|
68
145
|
...(config.cms.domainName ? [] : [productionOrigin]),
|
|
69
146
|
...(stagingEnabled && !stagingDomainName ? [stagingOrigin] : []),
|
|
@@ -79,14 +156,19 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
79
156
|
enablePerformanceMode: false,
|
|
80
157
|
environmentVariables: environmentVariables({
|
|
81
158
|
...config.cms.environment,
|
|
159
|
+
PAPYRUS_COGNITO_DOMAIN_PREFIX: config.cms.cognitoDomainPrefix,
|
|
160
|
+
...(config.cms.applyCognitoDomainPrefix ? { PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX: "true" } : {}),
|
|
82
161
|
PAPYRUS_OAUTH_REDIRECT_URLS: oauthRedirectUrls,
|
|
83
162
|
PAPYRUS_SITE_BRAND: config.brand,
|
|
84
163
|
SITE_ENV: "production",
|
|
85
164
|
PAPYRUS_CONTENT_SOURCE: "published",
|
|
165
|
+
...(revalidateSecretParameterName ? { PAPYRUS_REVALIDATE_SECRET_PARAMETER: revalidateSecretParameterName } : {}),
|
|
166
|
+
...prospectusEnvironment,
|
|
86
167
|
}),
|
|
87
168
|
});
|
|
169
|
+
let stagingBranch;
|
|
88
170
|
if (stagingEnabled) {
|
|
89
|
-
|
|
171
|
+
stagingBranch = new amplify.CfnBranch(this, "StagingBranch", {
|
|
90
172
|
appId: cmsApp.attrAppId,
|
|
91
173
|
branchName: "staging",
|
|
92
174
|
stage: "BETA",
|
|
@@ -95,16 +177,19 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
95
177
|
buildSpec: cmsStagingBuildSpec(config),
|
|
96
178
|
environmentVariables: environmentVariables({
|
|
97
179
|
...config.cms.environment,
|
|
180
|
+
PAPYRUS_COGNITO_DOMAIN_PREFIX: config.cms.cognitoDomainPrefix,
|
|
181
|
+
...(config.cms.applyCognitoDomainPrefix ? { PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX: "true" } : {}),
|
|
98
182
|
PAPYRUS_OAUTH_REDIRECT_URLS: oauthRedirectUrls,
|
|
99
183
|
PAPYRUS_SITE_BRAND: config.brand,
|
|
100
184
|
SITE_ENV: "staging",
|
|
101
185
|
...stagingMode,
|
|
186
|
+
...prospectusEnvironment,
|
|
102
187
|
NEXT_PUBLIC_PAPYRUS_STAGING_URL: stagingDomainName
|
|
103
188
|
? `https://${stagingDomainName}`
|
|
104
189
|
: Fn.join("", ["https://staging.", cmsApp.attrDefaultDomain]),
|
|
105
190
|
}),
|
|
106
191
|
});
|
|
107
|
-
if (stagingDomainName) {
|
|
192
|
+
if (stagingDomainName && !sharedRootDomain) {
|
|
108
193
|
const stagingDomain = new amplify.CfnDomain(this, "StagingDomain", {
|
|
109
194
|
appId: cmsApp.attrAppId,
|
|
110
195
|
domainName: stagingDomainName,
|
|
@@ -114,27 +199,89 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
114
199
|
}
|
|
115
200
|
}
|
|
116
201
|
if (config.cms.domainName) {
|
|
202
|
+
const subDomainSettings = sharedRootDomain
|
|
203
|
+
? [
|
|
204
|
+
{ branchName: "main", prefix: config.cms.domainPrefix },
|
|
205
|
+
...(config.cms.redirects ?? []).map((redirect) => ({
|
|
206
|
+
branchName: "main",
|
|
207
|
+
prefix: resolveRedirectSubDomainPrefix(config.cms.domainName, redirect.source),
|
|
208
|
+
})),
|
|
209
|
+
...(stagingBranch ? [{ branchName: "staging", prefix: config.cms.stagingDomainPrefix }] : []),
|
|
210
|
+
]
|
|
211
|
+
: [{ branchName: "main", prefix: "" }];
|
|
117
212
|
const productionDomain = new amplify.CfnDomain(this, "Domain", {
|
|
118
213
|
appId: cmsApp.attrAppId,
|
|
119
214
|
domainName: config.cms.domainName,
|
|
120
|
-
subDomainSettings
|
|
215
|
+
subDomainSettings,
|
|
121
216
|
});
|
|
122
217
|
productionDomain.addResourceDependency(productionBranch);
|
|
218
|
+
if (sharedRootDomain && stagingBranch) {
|
|
219
|
+
productionDomain.addResourceDependency(stagingBranch);
|
|
220
|
+
}
|
|
123
221
|
}
|
|
124
222
|
this.cmsAppId = cmsApp.attrAppId;
|
|
125
223
|
new CfnOutput(this, "CmsAppId", { value: cmsApp.attrAppId, description: `Amplify app id for ${cmsAppName}` });
|
|
126
224
|
new CfnOutput(this, "CmsOrigin", { value: productionOrigin });
|
|
225
|
+
new CfnOutput(this, "CmsComputeRoleArn", {
|
|
226
|
+
value: computeRole.roleArn,
|
|
227
|
+
description: `SSR compute role for ${cmsAppName}; the roleArn of its Prospectus site registration`,
|
|
228
|
+
});
|
|
127
229
|
if (stagingEnabled) {
|
|
128
230
|
new CfnOutput(this, "StagingOrigin", { value: stagingOrigin });
|
|
129
231
|
}
|
|
130
232
|
if (config.reader) {
|
|
131
233
|
this.readerAppId = this.addReaderApp(config, config.reader);
|
|
132
234
|
}
|
|
235
|
+
const amplifyAppIds = [cmsApp.attrAppId, ...(this.readerAppId ? [this.readerAppId] : [])];
|
|
236
|
+
this.addAuthoringRole(config, cmsApp.attrAppId, amplifyAppIds);
|
|
133
237
|
if (config.github) {
|
|
134
|
-
this.addGithubCiRole(config, config.github,
|
|
238
|
+
this.addGithubCiRole(config, config.github, cmsApp.attrAppId, amplifyAppIds);
|
|
135
239
|
}
|
|
136
240
|
}
|
|
137
|
-
|
|
241
|
+
authoringStatements(config, cmsAppId, amplifyAppIds) {
|
|
242
|
+
const mediaBucketArn = `arn:${this.partition}:s3:::amplify-${cmsAppId}-*`;
|
|
243
|
+
return [
|
|
244
|
+
new iam.PolicyStatement({
|
|
245
|
+
sid: "SignAppSyncAuthoringRequests",
|
|
246
|
+
actions: ["appsync:GraphQL"],
|
|
247
|
+
resources: ["Query", "Mutation"].map((typeName) => this.formatArn({ service: "appsync", resource: "apis", resourceName: `*/types/${typeName}/fields/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME })),
|
|
248
|
+
}),
|
|
249
|
+
new iam.PolicyStatement({
|
|
250
|
+
sid: "ReadWriteMediaAndPreview",
|
|
251
|
+
actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
|
|
252
|
+
resources: [`${mediaBucketArn}/media/*`, `${mediaBucketArn}/${resolveStoragePreviewPrefix(config)}*`],
|
|
253
|
+
}),
|
|
254
|
+
new iam.PolicyStatement({
|
|
255
|
+
sid: "ListMediaAndPreview",
|
|
256
|
+
actions: ["s3:ListBucket"],
|
|
257
|
+
resources: [mediaBucketArn],
|
|
258
|
+
conditions: { StringLike: { "s3:prefix": ["media/*", `${resolveStoragePreviewPrefix(config)}*`] } },
|
|
259
|
+
}),
|
|
260
|
+
new iam.PolicyStatement({
|
|
261
|
+
sid: "StartAndInspectAmplifyJobs",
|
|
262
|
+
actions: ["amplify:StartJob", "amplify:ListJobs", "amplify:GetJob", "amplify:ListBranches"],
|
|
263
|
+
resources: amplifyAppIds.flatMap((appId) => [
|
|
264
|
+
this.formatArn({ service: "amplify", resource: "apps", resourceName: appId, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
265
|
+
this.formatArn({ service: "amplify", resource: "apps", resourceName: `${appId}/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
266
|
+
]),
|
|
267
|
+
}),
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
addAuthoringRole(config, cmsAppId, amplifyAppIds) {
|
|
271
|
+
const authoringRole = new iam.Role(this, "PapyrusAuthoringRole", {
|
|
272
|
+
roleName: `${config.siteId}-papyrus-authoring`,
|
|
273
|
+
description: `Papyrus CLI authoring role for ${config.siteId}: AppSync (SigV4), media and preview storage, rebuild jobs. Assumable by principals in this account that are allowed sts:AssumeRole on it.`,
|
|
274
|
+
assumedBy: new iam.AccountRootPrincipal(),
|
|
275
|
+
});
|
|
276
|
+
for (const statement of this.authoringStatements(config, cmsAppId, amplifyAppIds)) {
|
|
277
|
+
authoringRole.addToPolicy(statement);
|
|
278
|
+
}
|
|
279
|
+
new CfnOutput(this, "PapyrusAuthoringRoleArn", {
|
|
280
|
+
value: authoringRole.roleArn,
|
|
281
|
+
description: `Role the Papyrus CLI assumes for ${config.siteId} (profile role_arn)`,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
addGithubCiRole(config, github, cmsAppId, amplifyAppIds) {
|
|
138
285
|
const provider = iam.OpenIdConnectProvider.fromOpenIdConnectProviderArn(this, "GithubOidcProvider", this.formatArn({
|
|
139
286
|
service: "iam",
|
|
140
287
|
region: "",
|
|
@@ -153,22 +300,9 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
153
300
|
},
|
|
154
301
|
}),
|
|
155
302
|
});
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
resources: amplifyAppIds.flatMap((appId) => [
|
|
160
|
-
this.formatArn({ service: "amplify", resource: "apps", resourceName: appId, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
161
|
-
this.formatArn({ service: "amplify", resource: "apps", resourceName: `${appId}/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
162
|
-
]),
|
|
163
|
-
}));
|
|
164
|
-
ciRole.addToPolicy(new iam.PolicyStatement({
|
|
165
|
-
sid: "ReadSiteSecretsForJwtMinting",
|
|
166
|
-
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
167
|
-
resources: [
|
|
168
|
-
this.formatArn({ service: "ssm", resource: "parameter", resourceName: `amplify/${this.cmsAppId}/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
169
|
-
this.formatArn({ service: "ssm", resource: "parameter", resourceName: `amplify/shared/${this.cmsAppId}/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
170
|
-
],
|
|
171
|
-
}));
|
|
303
|
+
for (const statement of this.authoringStatements(config, cmsAppId, amplifyAppIds)) {
|
|
304
|
+
ciRole.addToPolicy(statement);
|
|
305
|
+
}
|
|
172
306
|
ciRole.addToPolicy(new iam.PolicyStatement({
|
|
173
307
|
sid: "WhoAmI",
|
|
174
308
|
actions: ["sts:GetCallerIdentity"],
|
|
@@ -203,6 +337,11 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
203
337
|
platform: "WEB",
|
|
204
338
|
iamServiceRole: readerServiceRole.roleArn,
|
|
205
339
|
buildSpec: readerBuildSpec(config),
|
|
340
|
+
customRules: [
|
|
341
|
+
...(reader.redirects ?? []),
|
|
342
|
+
{ source: "/<*>", target: "/404.html", status: "404-200" },
|
|
343
|
+
],
|
|
344
|
+
customHeaders: readerCustomHeaders(reader.customHeaders),
|
|
206
345
|
});
|
|
207
346
|
const readerBranch = new amplify.CfnBranch(this, "ReaderBranch", {
|
|
208
347
|
appId: readerApp.attrAppId,
|
|
@@ -219,7 +358,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
219
358
|
const readerDomain = new amplify.CfnDomain(this, "ReaderDomain", {
|
|
220
359
|
appId: readerApp.attrAppId,
|
|
221
360
|
domainName: reader.domainName,
|
|
222
|
-
subDomainSettings: [{ branchName: readerBranchName, prefix
|
|
361
|
+
subDomainSettings: (reader.domainPrefixes ?? [""]).map((prefix) => ({ branchName: readerBranchName, prefix })),
|
|
223
362
|
});
|
|
224
363
|
readerDomain.addResourceDependency(readerBranch);
|
|
225
364
|
}
|