@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
|
@@ -2,41 +2,72 @@ import { Construct } from "constructs";
|
|
|
2
2
|
import { ArnFormat, CfnOutput, Duration, Fn, Stack, StackProps } from "aws-cdk-lib";
|
|
3
3
|
import * as iam from "aws-cdk-lib/aws-iam";
|
|
4
4
|
import * as amplify from "aws-cdk-lib/aws-amplify";
|
|
5
|
+
import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
|
|
5
6
|
import { GITHUB_OIDC_PROVIDER_HOST } from "./github-oidc-provider";
|
|
6
7
|
import { cmsProductionBuildSpec, cmsStagingBuildSpec, readerBuildSpec } from "./build-specs";
|
|
7
|
-
import { AmplifyAppShellSiteConfig, isStagingEnabled, resolveStackName, resolveStagingDomainName } from "./site-config";
|
|
8
|
+
import { AmplifyAppShellSiteConfig, PROSPECTUS_PAPYRUS_SITE_ROUTES, ReaderCustomHeaderRule, hasCmsApp, isStagingEnabled, resolveRedirectSubDomainPrefix, resolveCmsHostName, resolveProspectusAccountsEndpoint, resolveRevalidateSecretParameterName, resolveStackName, resolveStagingDomainName, resolveStoragePreviewPrefix } from "./site-config";
|
|
8
9
|
|
|
9
10
|
function environmentVariables(variables: Record<string, string>): amplify.CfnBranch.EnvironmentVariableProperty[] {
|
|
10
11
|
return Object.entries(variables).map(([name, value]) => ({ name, value }));
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
function readerCustomHeaders(rules: ReaderCustomHeaderRule[] | undefined): string | undefined {
|
|
15
|
+
if (!rules) return undefined;
|
|
16
|
+
const lines = ["customHeaders:"];
|
|
17
|
+
for (const rule of rules) {
|
|
18
|
+
lines.push(` - pattern: ${JSON.stringify(rule.pattern)}`, " headers:");
|
|
19
|
+
for (const [key, value] of Object.entries(rule.headers)) {
|
|
20
|
+
lines.push(` - key: ${JSON.stringify(key)}`, ` value: ${JSON.stringify(value)}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return lines.join("\n") + "\n";
|
|
24
|
+
}
|
|
25
|
+
|
|
13
26
|
/**
|
|
14
27
|
* Provisions the Amplify *app shell* of a Papyrus publication from its
|
|
15
28
|
* site.json:
|
|
16
29
|
*
|
|
17
30
|
* - CMS app (WEB_COMPUTE) with branches `main` (production, owns the backend)
|
|
18
31
|
* and `staging` (frontend only, reads the production backend)
|
|
19
|
-
* - for `markus-static` sites, a reader app (WEB) with branch `main
|
|
32
|
+
* - for `markus-static` sites, a reader app (WEB) with branch `main`; a
|
|
33
|
+
* `markus-static` site may omit `cms` to get the reader app alone (no CMS
|
|
34
|
+
* app, no CMS roles, no authoring role)
|
|
20
35
|
* - a domain association per branch/app when a domain is configured (without
|
|
21
36
|
* one, no AWS::Amplify::Domain is created and the default amplifyapp.com
|
|
22
37
|
* URL is used), IAM service and compute roles
|
|
23
38
|
* - `cms.staging: false` omits the staging branch and its domain
|
|
24
39
|
*
|
|
40
|
+
* Google sign-in is always on: the branches never set PAPYRUS_DISABLE_GOOGLE_OAUTH,
|
|
41
|
+
and `cms.cognitoDomainPrefix` becomes the hosted-UI domain
|
|
42
|
+
(PAPYRUS_COGNITO_DOMAIN_PREFIX) only when `cms.applyCognitoDomainPrefix` is true
|
|
43
|
+
(PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX); otherwise Amplify's generated domain is kept. The secrets GOOGLE_CLIENT_ID and
|
|
44
|
+
GOOGLE_CLIENT_SECRET are Amplify backend secrets set in the console, never here.
|
|
45
|
+
The stack also creates the `<siteId>-papyrus-authoring` role. The CLI signs
|
|
46
|
+
* AppSync requests (SigV4) with credentials for that role (SSO, OIDC or any
|
|
47
|
+
* principal in the account that may assume it); no token is stored anywhere.
|
|
48
|
+
*
|
|
25
49
|
* No repository and no access token are configured: the apps are manual-deploy
|
|
26
50
|
* until Amplify's GitHub App is connected once in the console
|
|
27
51
|
* (docs/site-hosting.md). The Papyrus backend itself is created inside the CMS
|
|
28
52
|
* app by `ampx pipeline-deploy` from the production build.
|
|
29
53
|
*/
|
|
30
54
|
export class AmplifyAppShellStack extends Stack {
|
|
31
|
-
public readonly cmsAppId
|
|
55
|
+
public readonly cmsAppId?: string;
|
|
32
56
|
public readonly readerAppId?: string;
|
|
33
57
|
|
|
34
58
|
constructor(scope: Construct, id: string, config: AmplifyAppShellSiteConfig, props?: StackProps) {
|
|
35
59
|
super(scope, id, props);
|
|
36
60
|
|
|
61
|
+
if (!hasCmsApp(config)) {
|
|
62
|
+
if (!config.reader) throw new Error(`site ${config.siteId} has neither a cms section nor a reader`);
|
|
63
|
+
this.readerAppId = this.addReaderApp(config, config.reader);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
37
67
|
const cmsAppName = config.cms.appName ?? `${config.siteId}-cms`;
|
|
38
68
|
const stagingEnabled = isStagingEnabled(config);
|
|
39
69
|
const stagingDomainName = resolveStagingDomainName(config);
|
|
70
|
+
const cmsHostName = resolveCmsHostName(config);
|
|
40
71
|
|
|
41
72
|
const cmsServiceRole = new iam.Role(this, "AmplifyServiceRole", {
|
|
42
73
|
assumedBy: new iam.ServicePrincipal("amplify.amazonaws.com"),
|
|
@@ -53,6 +84,52 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
53
84
|
],
|
|
54
85
|
});
|
|
55
86
|
|
|
87
|
+
const revalidateSecretParameterName = config.frontend === "pretext" ? resolveRevalidateSecretParameterName(config) : undefined;
|
|
88
|
+
if (revalidateSecretParameterName) {
|
|
89
|
+
computeRole.addToPolicy(
|
|
90
|
+
new iam.PolicyStatement({
|
|
91
|
+
sid: "ReadRevalidateSecret",
|
|
92
|
+
actions: ["ssm:GetParameter"],
|
|
93
|
+
resources: [
|
|
94
|
+
this.formatArn({ service: "ssm", resource: "parameter", resourceName: revalidateSecretParameterName.slice(1), arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
95
|
+
],
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const prospectusAccountsApiStageArn = config.cms.prospectusAccountsApiStageArn;
|
|
101
|
+
const prospectusSessionSealingSecret = prospectusAccountsApiStageArn
|
|
102
|
+
? new secretsmanager.Secret(this, "ProspectusSessionSealingSecret", {
|
|
103
|
+
description: `Seals the Prospectus sign-in cookies of ${cmsAppName}; replacing it signs everyone out`,
|
|
104
|
+
generateSecretString: { passwordLength: 64, excludePunctuation: true },
|
|
105
|
+
})
|
|
106
|
+
: undefined;
|
|
107
|
+
const prospectusEnvironment = prospectusAccountsApiStageArn && prospectusSessionSealingSecret
|
|
108
|
+
? {
|
|
109
|
+
PAPYRUS_PROSPECTUS_ACCOUNTS_ENDPOINT: resolveProspectusAccountsEndpoint(prospectusAccountsApiStageArn),
|
|
110
|
+
PAPYRUS_PROSPECTUS_SESSION_SECRET_ARN: prospectusSessionSealingSecret.secretArn,
|
|
111
|
+
PAPYRUS_PROSPECTUS_SITE_PRODUCT: `papyrus-${config.siteId}`,
|
|
112
|
+
}
|
|
113
|
+
: {};
|
|
114
|
+
if (prospectusSessionSealingSecret) {
|
|
115
|
+
computeRole.addToPolicy(
|
|
116
|
+
new iam.PolicyStatement({
|
|
117
|
+
sid: "ReadProspectusSessionSealingSecret",
|
|
118
|
+
actions: ["secretsmanager:GetSecretValue"],
|
|
119
|
+
resources: [prospectusSessionSealingSecret.secretArn],
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (prospectusAccountsApiStageArn) {
|
|
124
|
+
computeRole.addToPolicy(
|
|
125
|
+
new iam.PolicyStatement({
|
|
126
|
+
sid: "InvokeProspectusAccountsAsRegisteredSite",
|
|
127
|
+
actions: ["execute-api:Invoke"],
|
|
128
|
+
resources: PROSPECTUS_PAPYRUS_SITE_ROUTES.map((route) => `${prospectusAccountsApiStageArn}/${route}`),
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
56
133
|
const cmsApp = new amplify.CfnApp(this, "App", {
|
|
57
134
|
name: cmsAppName,
|
|
58
135
|
description: `Papyrus newsroom CMS for ${config.siteId} (WEB_COMPUTE)`,
|
|
@@ -61,19 +138,27 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
61
138
|
computeRoleArn: computeRole.roleArn,
|
|
62
139
|
buildSpec: cmsProductionBuildSpec(config),
|
|
63
140
|
jobConfig: config.cms.buildComputeType ? { buildComputeType: config.cms.buildComputeType } : undefined,
|
|
64
|
-
customRules: [
|
|
141
|
+
customRules: [
|
|
142
|
+
...(config.cms.redirects ?? []).map((redirect) => ({
|
|
143
|
+
source: `https://${redirect.source}`,
|
|
144
|
+
target: `https://${resolveCmsHostName(config)}`,
|
|
145
|
+
status: String(redirect.status),
|
|
146
|
+
})),
|
|
147
|
+
{ source: "/<*>", target: "/index.html", status: "404-200" },
|
|
148
|
+
],
|
|
65
149
|
});
|
|
66
150
|
|
|
67
151
|
const stagingMode: Record<string, string> = config.frontend === "markus-static"
|
|
68
152
|
? { PAPYRUS_STAGING_PREVIEW: "static" }
|
|
69
153
|
: { PAPYRUS_CONTENT_SOURCE: "drafts" };
|
|
70
154
|
|
|
71
|
-
const productionOrigin =
|
|
72
|
-
? `https://${
|
|
155
|
+
const productionOrigin = cmsHostName
|
|
156
|
+
? `https://${cmsHostName}/`
|
|
73
157
|
: Fn.join("", ["https://main.", cmsApp.attrDefaultDomain, "/"]);
|
|
74
158
|
const stagingOrigin = stagingDomainName
|
|
75
159
|
? `https://${stagingDomainName}/`
|
|
76
160
|
: Fn.join("", ["https://staging.", cmsApp.attrDefaultDomain, "/"]);
|
|
161
|
+
const sharedRootDomain = config.cms.domainPrefix !== undefined;
|
|
77
162
|
const defaultOriginsToAllow = [
|
|
78
163
|
...(config.cms.domainName ? [] : [productionOrigin]),
|
|
79
164
|
...(stagingEnabled && !stagingDomainName ? [stagingOrigin] : []),
|
|
@@ -90,15 +175,20 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
90
175
|
enablePerformanceMode: false,
|
|
91
176
|
environmentVariables: environmentVariables({
|
|
92
177
|
...config.cms.environment,
|
|
178
|
+
PAPYRUS_COGNITO_DOMAIN_PREFIX: config.cms.cognitoDomainPrefix,
|
|
179
|
+
...(config.cms.applyCognitoDomainPrefix ? { PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX: "true" } : {}),
|
|
93
180
|
PAPYRUS_OAUTH_REDIRECT_URLS: oauthRedirectUrls,
|
|
94
181
|
PAPYRUS_SITE_BRAND: config.brand,
|
|
95
182
|
SITE_ENV: "production",
|
|
96
183
|
PAPYRUS_CONTENT_SOURCE: "published",
|
|
184
|
+
...(revalidateSecretParameterName ? { PAPYRUS_REVALIDATE_SECRET_PARAMETER: revalidateSecretParameterName } : {}),
|
|
185
|
+
...prospectusEnvironment,
|
|
97
186
|
}),
|
|
98
187
|
});
|
|
99
188
|
|
|
189
|
+
let stagingBranch: amplify.CfnBranch | undefined;
|
|
100
190
|
if (stagingEnabled) {
|
|
101
|
-
|
|
191
|
+
stagingBranch = new amplify.CfnBranch(this, "StagingBranch", {
|
|
102
192
|
appId: cmsApp.attrAppId,
|
|
103
193
|
branchName: "staging",
|
|
104
194
|
stage: "BETA",
|
|
@@ -107,17 +197,20 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
107
197
|
buildSpec: cmsStagingBuildSpec(config),
|
|
108
198
|
environmentVariables: environmentVariables({
|
|
109
199
|
...config.cms.environment,
|
|
200
|
+
PAPYRUS_COGNITO_DOMAIN_PREFIX: config.cms.cognitoDomainPrefix,
|
|
201
|
+
...(config.cms.applyCognitoDomainPrefix ? { PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX: "true" } : {}),
|
|
110
202
|
PAPYRUS_OAUTH_REDIRECT_URLS: oauthRedirectUrls,
|
|
111
203
|
PAPYRUS_SITE_BRAND: config.brand,
|
|
112
204
|
SITE_ENV: "staging",
|
|
113
205
|
...stagingMode,
|
|
206
|
+
...prospectusEnvironment,
|
|
114
207
|
NEXT_PUBLIC_PAPYRUS_STAGING_URL: stagingDomainName
|
|
115
208
|
? `https://${stagingDomainName}`
|
|
116
209
|
: Fn.join("", ["https://staging.", cmsApp.attrDefaultDomain]),
|
|
117
210
|
}),
|
|
118
211
|
});
|
|
119
212
|
|
|
120
|
-
if (stagingDomainName) {
|
|
213
|
+
if (stagingDomainName && !sharedRootDomain) {
|
|
121
214
|
const stagingDomain = new amplify.CfnDomain(this, "StagingDomain", {
|
|
122
215
|
appId: cmsApp.attrAppId,
|
|
123
216
|
domainName: stagingDomainName,
|
|
@@ -128,17 +221,34 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
128
221
|
}
|
|
129
222
|
|
|
130
223
|
if (config.cms.domainName) {
|
|
224
|
+
const subDomainSettings = sharedRootDomain
|
|
225
|
+
? [
|
|
226
|
+
{ branchName: "main", prefix: config.cms.domainPrefix as string },
|
|
227
|
+
...(config.cms.redirects ?? []).map((redirect) => ({
|
|
228
|
+
branchName: "main",
|
|
229
|
+
prefix: resolveRedirectSubDomainPrefix(config.cms.domainName as string, redirect.source),
|
|
230
|
+
})),
|
|
231
|
+
...(stagingBranch ? [{ branchName: "staging", prefix: config.cms.stagingDomainPrefix as string }] : []),
|
|
232
|
+
]
|
|
233
|
+
: [{ branchName: "main", prefix: "" }];
|
|
131
234
|
const productionDomain = new amplify.CfnDomain(this, "Domain", {
|
|
132
235
|
appId: cmsApp.attrAppId,
|
|
133
236
|
domainName: config.cms.domainName,
|
|
134
|
-
subDomainSettings
|
|
237
|
+
subDomainSettings,
|
|
135
238
|
});
|
|
136
239
|
productionDomain.addResourceDependency(productionBranch);
|
|
240
|
+
if (sharedRootDomain && stagingBranch) {
|
|
241
|
+
productionDomain.addResourceDependency(stagingBranch);
|
|
242
|
+
}
|
|
137
243
|
}
|
|
138
244
|
|
|
139
245
|
this.cmsAppId = cmsApp.attrAppId;
|
|
140
246
|
new CfnOutput(this, "CmsAppId", { value: cmsApp.attrAppId, description: `Amplify app id for ${cmsAppName}` });
|
|
141
247
|
new CfnOutput(this, "CmsOrigin", { value: productionOrigin });
|
|
248
|
+
new CfnOutput(this, "CmsComputeRoleArn", {
|
|
249
|
+
value: computeRole.roleArn,
|
|
250
|
+
description: `SSR compute role for ${cmsAppName}; the roleArn of its Prospectus site registration`,
|
|
251
|
+
});
|
|
142
252
|
if (stagingEnabled) {
|
|
143
253
|
new CfnOutput(this, "StagingOrigin", { value: stagingOrigin });
|
|
144
254
|
}
|
|
@@ -147,14 +257,65 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
147
257
|
this.readerAppId = this.addReaderApp(config, config.reader);
|
|
148
258
|
}
|
|
149
259
|
|
|
260
|
+
const amplifyAppIds = [cmsApp.attrAppId, ...(this.readerAppId ? [this.readerAppId] : [])];
|
|
261
|
+
this.addAuthoringRole(config, cmsApp.attrAppId, amplifyAppIds);
|
|
262
|
+
|
|
150
263
|
if (config.github) {
|
|
151
|
-
this.addGithubCiRole(config, config.github,
|
|
264
|
+
this.addGithubCiRole(config, config.github, cmsApp.attrAppId, amplifyAppIds);
|
|
152
265
|
}
|
|
153
266
|
}
|
|
154
267
|
|
|
268
|
+
private authoringStatements(config: AmplifyAppShellSiteConfig, cmsAppId: string, amplifyAppIds: string[]): iam.PolicyStatement[] {
|
|
269
|
+
const mediaBucketArn = `arn:${this.partition}:s3:::amplify-${cmsAppId}-*`;
|
|
270
|
+
return [
|
|
271
|
+
new iam.PolicyStatement({
|
|
272
|
+
sid: "SignAppSyncAuthoringRequests",
|
|
273
|
+
actions: ["appsync:GraphQL"],
|
|
274
|
+
resources: ["Query", "Mutation"].map((typeName) =>
|
|
275
|
+
this.formatArn({ service: "appsync", resource: "apis", resourceName: `*/types/${typeName}/fields/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
276
|
+
),
|
|
277
|
+
}),
|
|
278
|
+
new iam.PolicyStatement({
|
|
279
|
+
sid: "ReadWriteMediaAndPreview",
|
|
280
|
+
actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
|
|
281
|
+
resources: [`${mediaBucketArn}/media/*`, `${mediaBucketArn}/${resolveStoragePreviewPrefix(config)}*`],
|
|
282
|
+
}),
|
|
283
|
+
new iam.PolicyStatement({
|
|
284
|
+
sid: "ListMediaAndPreview",
|
|
285
|
+
actions: ["s3:ListBucket"],
|
|
286
|
+
resources: [mediaBucketArn],
|
|
287
|
+
conditions: { StringLike: { "s3:prefix": ["media/*", `${resolveStoragePreviewPrefix(config)}*`] } },
|
|
288
|
+
}),
|
|
289
|
+
new iam.PolicyStatement({
|
|
290
|
+
sid: "StartAndInspectAmplifyJobs",
|
|
291
|
+
actions: ["amplify:StartJob", "amplify:ListJobs", "amplify:GetJob", "amplify:ListBranches"],
|
|
292
|
+
resources: amplifyAppIds.flatMap((appId) => [
|
|
293
|
+
this.formatArn({ service: "amplify", resource: "apps", resourceName: appId, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
294
|
+
this.formatArn({ service: "amplify", resource: "apps", resourceName: `${appId}/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
295
|
+
]),
|
|
296
|
+
}),
|
|
297
|
+
];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private addAuthoringRole(config: AmplifyAppShellSiteConfig, cmsAppId: string, amplifyAppIds: string[]): void {
|
|
301
|
+
const authoringRole = new iam.Role(this, "PapyrusAuthoringRole", {
|
|
302
|
+
roleName: `${config.siteId}-papyrus-authoring`,
|
|
303
|
+
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.`,
|
|
304
|
+
assumedBy: new iam.AccountRootPrincipal(),
|
|
305
|
+
});
|
|
306
|
+
for (const statement of this.authoringStatements(config, cmsAppId, amplifyAppIds)) {
|
|
307
|
+
authoringRole.addToPolicy(statement);
|
|
308
|
+
}
|
|
309
|
+
new CfnOutput(this, "PapyrusAuthoringRoleArn", {
|
|
310
|
+
value: authoringRole.roleArn,
|
|
311
|
+
description: `Role the Papyrus CLI assumes for ${config.siteId} (profile role_arn)`,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
155
315
|
private addGithubCiRole(
|
|
156
316
|
config: AmplifyAppShellSiteConfig,
|
|
157
317
|
github: NonNullable<AmplifyAppShellSiteConfig["github"]>,
|
|
318
|
+
cmsAppId: string,
|
|
158
319
|
amplifyAppIds: string[],
|
|
159
320
|
): void {
|
|
160
321
|
const provider = iam.OpenIdConnectProvider.fromOpenIdConnectProviderArn(
|
|
@@ -183,26 +344,9 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
183
344
|
}),
|
|
184
345
|
});
|
|
185
346
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
actions: ["amplify:StartJob", "amplify:ListJobs", "amplify:GetJob", "amplify:ListBranches"],
|
|
190
|
-
resources: amplifyAppIds.flatMap((appId) => [
|
|
191
|
-
this.formatArn({ service: "amplify", resource: "apps", resourceName: appId, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
192
|
-
this.formatArn({ service: "amplify", resource: "apps", resourceName: `${appId}/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
193
|
-
]),
|
|
194
|
-
}),
|
|
195
|
-
);
|
|
196
|
-
ciRole.addToPolicy(
|
|
197
|
-
new iam.PolicyStatement({
|
|
198
|
-
sid: "ReadSiteSecretsForJwtMinting",
|
|
199
|
-
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
200
|
-
resources: [
|
|
201
|
-
this.formatArn({ service: "ssm", resource: "parameter", resourceName: `amplify/${this.cmsAppId}/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
202
|
-
this.formatArn({ service: "ssm", resource: "parameter", resourceName: `amplify/shared/${this.cmsAppId}/*`, arnFormat: ArnFormat.SLASH_RESOURCE_NAME }),
|
|
203
|
-
],
|
|
204
|
-
}),
|
|
205
|
-
);
|
|
347
|
+
for (const statement of this.authoringStatements(config, cmsAppId, amplifyAppIds)) {
|
|
348
|
+
ciRole.addToPolicy(statement);
|
|
349
|
+
}
|
|
206
350
|
ciRole.addToPolicy(
|
|
207
351
|
new iam.PolicyStatement({
|
|
208
352
|
sid: "WhoAmI",
|
|
@@ -250,6 +394,11 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
250
394
|
platform: "WEB",
|
|
251
395
|
iamServiceRole: readerServiceRole.roleArn,
|
|
252
396
|
buildSpec: readerBuildSpec(config),
|
|
397
|
+
customRules: [
|
|
398
|
+
...(reader.redirects ?? []),
|
|
399
|
+
{ source: "/<*>", target: "/404.html", status: "404-200" },
|
|
400
|
+
],
|
|
401
|
+
customHeaders: readerCustomHeaders(reader.customHeaders),
|
|
253
402
|
});
|
|
254
403
|
|
|
255
404
|
const readerBranch = new amplify.CfnBranch(this, "ReaderBranch", {
|
|
@@ -268,7 +417,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
268
417
|
const readerDomain = new amplify.CfnDomain(this, "ReaderDomain", {
|
|
269
418
|
appId: readerApp.attrAppId,
|
|
270
419
|
domainName: reader.domainName,
|
|
271
|
-
subDomainSettings: [{ branchName: readerBranchName, prefix
|
|
420
|
+
subDomainSettings: (reader.domainPrefixes ?? [""]).map((prefix) => ({ branchName: readerBranchName, prefix })),
|
|
272
421
|
});
|
|
273
422
|
readerDomain.addResourceDependency(readerBranch);
|
|
274
423
|
}
|
package/infra/build-specs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveStoragePreviewPrefix } from "./site-config.js";
|
|
1
|
+
import { hasCmsApp, resolveStoragePreviewPrefix } from "./site-config.js";
|
|
2
2
|
export function papyrusVersionToPep440(version) {
|
|
3
3
|
const match = /^(\d+\.\d+\.\d+)(?:-[0-9A-Za-z-]+\.(\d+))?$/.exec(version);
|
|
4
4
|
if (!match)
|
|
@@ -14,6 +14,8 @@ function commandLines(commands, indent) {
|
|
|
14
14
|
}
|
|
15
15
|
const NPM_CLEAN_INSTALL = "npm ci --cache .npm --prefer-offline";
|
|
16
16
|
const FRONTEND_ENVIRONMENT_CAPTURE = "env | grep -e '^PAPYRUS_' -e '^SITE_ENV=' -e '^NEXT_PUBLIC_' >> .env.production";
|
|
17
|
+
const CHECKOUT_AS_PAPYRUS_ROOT = "PAPYRUS_ROOT=\"$PWD\"";
|
|
18
|
+
const RUNTIME_OUTPUTS_COPY = "cp amplify_outputs.json .next/amplify_outputs.json";
|
|
17
19
|
function pythonProvisioningCommands(config) {
|
|
18
20
|
return [
|
|
19
21
|
"export UV_CACHE_DIR=\"$PWD/.uv-cache\"",
|
|
@@ -30,6 +32,9 @@ function reader(config) {
|
|
|
30
32
|
throw new Error(`site ${config.siteId} has no reader app (frontend ${config.frontend})`);
|
|
31
33
|
return config.reader;
|
|
32
34
|
}
|
|
35
|
+
function stagingPreviewFor(config) {
|
|
36
|
+
return config.cms?.stagingPreview ?? config.reader;
|
|
37
|
+
}
|
|
33
38
|
function frontendSection(preBuildCommands, buildCommands, artifactsDirectory, cachePaths) {
|
|
34
39
|
const lines = ["frontend:", " phases:"];
|
|
35
40
|
if (preBuildCommands.length > 0) {
|
|
@@ -50,20 +55,28 @@ export function cmsProductionBuildSpec(config) {
|
|
|
50
55
|
"if [ -z \"${PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI:-}\" ]; then export PAPYRUS_CONSOLE_RESPONDER_ALLOW_LOCAL_BUILD=true; fi",
|
|
51
56
|
"npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID",
|
|
52
57
|
], " "),
|
|
53
|
-
...frontendSection([], [
|
|
58
|
+
...frontendSection([], [
|
|
59
|
+
FRONTEND_ENVIRONMENT_CAPTURE,
|
|
60
|
+
"npx papyrus-app sync && npm run build",
|
|
61
|
+
...(config.frontend === "pretext" ? [RUNTIME_OUTPUTS_COPY] : []),
|
|
62
|
+
], ".next", [".next/cache/**/*", ".npm/**/*"]),
|
|
54
63
|
].join("\n") + "\n";
|
|
55
64
|
}
|
|
56
65
|
export function cmsStagingBuildSpec(config) {
|
|
57
66
|
const preBuild = [NPM_CLEAN_INSTALL, "npx ampx generate outputs --branch main --app-id $AWS_APP_ID"];
|
|
58
67
|
const cachePaths = [".next/cache/**/*", ".npm/**/*"];
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
preBuild.push(...pythonProvisioningCommands(config),
|
|
68
|
+
const preview = config.frontend === "markus-static" ? stagingPreviewFor(config) : undefined;
|
|
69
|
+
if (preview !== undefined) {
|
|
70
|
+
preBuild.push(...pythonProvisioningCommands(config), `${CHECKOUT_AS_PAPYRUS_ROOT} papyrus ops content export-published --drafts --out content-export --clean`, preview.buildCommand, `${CHECKOUT_AS_PAPYRUS_ROOT} papyrus ops content upload-preview --dir ${preview.baseDirectory} --prefix ${resolveStoragePreviewPrefix(config)}`);
|
|
62
71
|
cachePaths.push(".uv-cache/**/*");
|
|
63
72
|
}
|
|
64
73
|
return [
|
|
65
74
|
"version: 1",
|
|
66
|
-
...frontendSection(preBuild, [
|
|
75
|
+
...frontendSection(preBuild, [
|
|
76
|
+
FRONTEND_ENVIRONMENT_CAPTURE,
|
|
77
|
+
"npx papyrus-app sync && npm run build",
|
|
78
|
+
RUNTIME_OUTPUTS_COPY,
|
|
79
|
+
], ".next", cachePaths),
|
|
67
80
|
].join("\n") + "\n";
|
|
68
81
|
}
|
|
69
82
|
export function readerBuildSpec(config) {
|
|
@@ -72,11 +85,14 @@ export function readerBuildSpec(config) {
|
|
|
72
85
|
"version: 1",
|
|
73
86
|
...frontendSection([
|
|
74
87
|
...pythonProvisioningCommands(config),
|
|
75
|
-
|
|
88
|
+
...(readerConfig.preBuildCommands ?? []),
|
|
89
|
+
...(readerConfig.skipContentExport ? [] : ["papyrus ops content export-published --auth guest --out content-export --clean"]),
|
|
76
90
|
], [readerConfig.buildCommand], readerConfig.baseDirectory, [".uv-cache/**/*"]),
|
|
77
91
|
].join("\n") + "\n";
|
|
78
92
|
}
|
|
79
93
|
export function buildSpecFor(config, app) {
|
|
94
|
+
if (app !== "reader" && !hasCmsApp(config))
|
|
95
|
+
throw new Error(`site ${config.siteId} has no cms app`);
|
|
80
96
|
if (app === "cms-production")
|
|
81
97
|
return cmsProductionBuildSpec(config);
|
|
82
98
|
if (app === "cms-staging")
|
package/infra/build-specs.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AmplifyAppShellSiteConfig, resolveStoragePreviewPrefix } from "./site-config";
|
|
1
|
+
import { AmplifyAppShellSiteConfig, hasCmsApp, resolveStoragePreviewPrefix } from "./site-config";
|
|
2
2
|
|
|
3
3
|
export type BuildSpecAppName = "cms-production" | "cms-staging" | "reader";
|
|
4
4
|
|
|
@@ -22,6 +22,10 @@ const NPM_CLEAN_INSTALL = "npm ci --cache .npm --prefer-offline";
|
|
|
22
22
|
const FRONTEND_ENVIRONMENT_CAPTURE =
|
|
23
23
|
"env | grep -e '^PAPYRUS_' -e '^SITE_ENV=' -e '^NEXT_PUBLIC_' >> .env.production";
|
|
24
24
|
|
|
25
|
+
const CHECKOUT_AS_PAPYRUS_ROOT = "PAPYRUS_ROOT=\"$PWD\"";
|
|
26
|
+
|
|
27
|
+
const RUNTIME_OUTPUTS_COPY = "cp amplify_outputs.json .next/amplify_outputs.json";
|
|
28
|
+
|
|
25
29
|
function pythonProvisioningCommands(config: AmplifyAppShellSiteConfig): string[] {
|
|
26
30
|
return [
|
|
27
31
|
"export UV_CACHE_DIR=\"$PWD/.uv-cache\"",
|
|
@@ -39,6 +43,10 @@ function reader(config: AmplifyAppShellSiteConfig): NonNullable<AmplifyAppShellS
|
|
|
39
43
|
return config.reader;
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
function stagingPreviewFor(config: AmplifyAppShellSiteConfig): { buildCommand: string; baseDirectory: string } | undefined {
|
|
47
|
+
return config.cms?.stagingPreview ?? config.reader;
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
function frontendSection(preBuildCommands: string[], buildCommands: string[], artifactsDirectory: string, cachePaths: string[]): string[] {
|
|
43
51
|
const lines = ["frontend:", " phases:"];
|
|
44
52
|
if (preBuildCommands.length > 0) {
|
|
@@ -76,7 +84,11 @@ export function cmsProductionBuildSpec(config: AmplifyAppShellSiteConfig): strin
|
|
|
76
84
|
),
|
|
77
85
|
...frontendSection(
|
|
78
86
|
[],
|
|
79
|
-
[
|
|
87
|
+
[
|
|
88
|
+
FRONTEND_ENVIRONMENT_CAPTURE,
|
|
89
|
+
"npx papyrus-app sync && npm run build",
|
|
90
|
+
...(config.frontend === "pretext" ? [RUNTIME_OUTPUTS_COPY] : []),
|
|
91
|
+
],
|
|
80
92
|
".next",
|
|
81
93
|
[".next/cache/**/*", ".npm/**/*"],
|
|
82
94
|
),
|
|
@@ -86,14 +98,13 @@ export function cmsProductionBuildSpec(config: AmplifyAppShellSiteConfig): strin
|
|
|
86
98
|
export function cmsStagingBuildSpec(config: AmplifyAppShellSiteConfig): string {
|
|
87
99
|
const preBuild = [NPM_CLEAN_INSTALL, "npx ampx generate outputs --branch main --app-id $AWS_APP_ID"];
|
|
88
100
|
const cachePaths = [".next/cache/**/*", ".npm/**/*"];
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
const preview = config.frontend === "markus-static" ? stagingPreviewFor(config) : undefined;
|
|
102
|
+
if (preview !== undefined) {
|
|
91
103
|
preBuild.push(
|
|
92
104
|
...pythonProvisioningCommands(config),
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`papyrus content upload-preview --dir ${readerConfig.baseDirectory} --prefix ${resolveStoragePreviewPrefix(config)}`,
|
|
105
|
+
`${CHECKOUT_AS_PAPYRUS_ROOT} papyrus ops content export-published --drafts --out content-export --clean`,
|
|
106
|
+
preview.buildCommand,
|
|
107
|
+
`${CHECKOUT_AS_PAPYRUS_ROOT} papyrus ops content upload-preview --dir ${preview.baseDirectory} --prefix ${resolveStoragePreviewPrefix(config)}`,
|
|
97
108
|
);
|
|
98
109
|
cachePaths.push(".uv-cache/**/*");
|
|
99
110
|
}
|
|
@@ -101,7 +112,11 @@ export function cmsStagingBuildSpec(config: AmplifyAppShellSiteConfig): string {
|
|
|
101
112
|
"version: 1",
|
|
102
113
|
...frontendSection(
|
|
103
114
|
preBuild,
|
|
104
|
-
[
|
|
115
|
+
[
|
|
116
|
+
FRONTEND_ENVIRONMENT_CAPTURE,
|
|
117
|
+
"npx papyrus-app sync && npm run build",
|
|
118
|
+
RUNTIME_OUTPUTS_COPY,
|
|
119
|
+
],
|
|
105
120
|
".next",
|
|
106
121
|
cachePaths,
|
|
107
122
|
),
|
|
@@ -115,7 +130,8 @@ export function readerBuildSpec(config: AmplifyAppShellSiteConfig): string {
|
|
|
115
130
|
...frontendSection(
|
|
116
131
|
[
|
|
117
132
|
...pythonProvisioningCommands(config),
|
|
118
|
-
|
|
133
|
+
...(readerConfig.preBuildCommands ?? []),
|
|
134
|
+
...(readerConfig.skipContentExport ? [] : ["papyrus ops content export-published --auth guest --out content-export --clean"]),
|
|
119
135
|
],
|
|
120
136
|
[readerConfig.buildCommand],
|
|
121
137
|
readerConfig.baseDirectory,
|
|
@@ -125,6 +141,7 @@ export function readerBuildSpec(config: AmplifyAppShellSiteConfig): string {
|
|
|
125
141
|
}
|
|
126
142
|
|
|
127
143
|
export function buildSpecFor(config: AmplifyAppShellSiteConfig, app: BuildSpecAppName): string {
|
|
144
|
+
if (app !== "reader" && !hasCmsApp(config)) throw new Error(`site ${config.siteId} has no cms app`);
|
|
128
145
|
if (app === "cms-production") return cmsProductionBuildSpec(config);
|
|
129
146
|
if (app === "cms-staging") return cmsStagingBuildSpec(config);
|
|
130
147
|
return readerBuildSpec(config);
|