@anthusai/papyrus 1.0.0-next.8 → 1.0.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/redirect-urls.js +54 -0
- package/amplify/auth/redirect-urls.ts +66 -0
- package/amplify/auth/resource.js +20 -5
- package/amplify/auth/resource.ts +27 -5
- package/amplify/data/resource.js +42 -80
- package/amplify/data/resource.ts +42 -80
- package/amplify/data/schema.js +45 -60
- package/amplify/data/schema.ts +47 -60
- 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/renditions/handler.py +120 -0
- package/amplify/functions/renditions/requirements.txt +4 -0
- 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 +143 -176
- package/amplify/site-backend.ts +181 -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/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 -0
- package/bin/papyrus-infra.mjs +3 -3
- 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 +92 -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 -6
- 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 +219 -71
- package/infra/amplify-app-shell.ts +231 -77
- package/infra/build-specs.js +23 -8
- package/infra/build-specs.ts +27 -11
- package/infra/site-config.js +277 -34
- package/infra/site-config.ts +340 -43
- 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 +89 -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 +64 -1
- 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 +68 -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/amplify/site-backend.js
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import { defineBackend, secret } from "@aws-amplify/backend";
|
|
2
|
-
import {
|
|
3
|
-
import * as backup from "aws-cdk-lib/aws-backup";
|
|
4
|
-
import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from "aws-cdk-lib/custom-resources";
|
|
2
|
+
import { Stack } from "aws-cdk-lib";
|
|
5
3
|
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
|
|
6
|
-
import * as events from "aws-cdk-lib/aws-events";
|
|
7
|
-
import * as route53 from "aws-cdk-lib/aws-route53";
|
|
8
|
-
import * as ses from "aws-cdk-lib/aws-ses";
|
|
9
|
-
import * as sesActions from "aws-cdk-lib/aws-ses-actions";
|
|
10
4
|
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
11
5
|
import { CfnEventSourceMapping, FunctionUrlAuthType } from "aws-cdk-lib/aws-lambda";
|
|
12
6
|
import { CfnIndex, CfnVectorBucket, CfnVectorBucketPolicy } from "aws-cdk-lib/aws-s3vectors";
|
|
13
7
|
import { dirname, resolve } from "node:path";
|
|
14
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { DEFAULT_RESOLVER_WAVE_SIZE, orderModelStacksInWaves } from "./resolver-waves.js";
|
|
10
|
+
import { resolveSitePublication } from "./publication/default-publication.js";
|
|
11
|
+
import { REPROJECT_ON_DEPLOY_TIMEOUT_SECONDS, addReprojectOnDeploy, reprojectDeployToken } from "./publication/reproject-on-deploy.js";
|
|
12
|
+
import { addProjectionRetries, addS3ProjectionPublication } from "./publication/s3-projection.js";
|
|
13
|
+
import { planRenditions } from "./publication/renditions-settings.js";
|
|
14
|
+
import { contentActionsPublicationWiring, planS3ProjectionBackend } from "./publication/s3-projection-settings.js";
|
|
15
15
|
import { rebuildTriggerSettings } from "./rebuild-trigger-settings.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import { assertValidPublicationConfig } from "./site-backend-config.js";
|
|
17
|
+
import { revalidateSecretReadStatement } from "./revalidate-secret-policy.js";
|
|
18
|
+
import { revalidateDistributionEnvironment, revalidateInvalidationStatement, validateRevalidateDistributionIds, } from "./revalidate-invalidation-settings.js";
|
|
19
|
+
import { addInvalidationPathAlarm } from "./publication/invalidation-alarm.js";
|
|
20
|
+
import { applyCognitoDomainPrefix, authConfigFromEnv, defineSiteAuth } from "./auth/resource.js";
|
|
21
|
+
import { defineSiteData } from "./data/resource.js";
|
|
22
|
+
import { resolvePlugins } from "./plugins/registry.js";
|
|
23
|
+
import { wirePlugins } from "./plugins/wire.js";
|
|
18
24
|
import { assignmentAction } from "./functions/assignment-action/resource.js";
|
|
19
25
|
import { categoryAction } from "./functions/category-action/resource.js";
|
|
20
26
|
import { ConsoleChatResponderStack } from "./functions/console-chat-responder/resource.js";
|
|
21
|
-
import {
|
|
22
|
-
import { contentActions } from "./functions/content-actions/resource.js";
|
|
27
|
+
import { contentActions, setSiteFilesForContentActionsBundle } from "./functions/content-actions/resource.js";
|
|
23
28
|
import { knowledgeQuery } from "./functions/knowledge-query/resource.js";
|
|
24
29
|
import { manageUserRole } from "./functions/manage-user-role/resource.js";
|
|
25
30
|
import { modelAttachmentUpload } from "./functions/model-attachment-upload/resource.js";
|
|
@@ -30,7 +35,10 @@ import { emailSubmissionProcessor } from "./functions/email-submission-processor
|
|
|
30
35
|
import { sesInboundReceive } from "./functions/ses-inbound-receive/resource.js";
|
|
31
36
|
import { slackDelivery } from "./functions/slack-delivery/resource.js";
|
|
32
37
|
import { slackEvents } from "./functions/slack-events/resource.js";
|
|
38
|
+
import { addInboundEmailSesIntake } from "./inbound-email/ses-intake.js";
|
|
33
39
|
import { InboundEmailStack } from "./inbound-email/stack.js";
|
|
40
|
+
import { deriveKnowledgeVectorIndexName, deriveReceiptRuleName, deriveReceiptRuleSetName, deriveStorageBackupVaultName, isLegacyProductionPipeline, planInboundEmailSes, resolveSiteBackendFeatureFlags, } from "./site-backend-names.js";
|
|
41
|
+
import { addStorageBackups } from "./storage-backups/construct.js";
|
|
34
42
|
import { storage } from "./storage/resource.js";
|
|
35
43
|
export function defineSiteBackend(papyrusSite) {
|
|
36
44
|
// `papyrus.config.ts`'s default export. Backend options live under `backend`;
|
|
@@ -40,50 +48,46 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
40
48
|
const knowledgeEmbeddingModel = "text-embedding-3-small";
|
|
41
49
|
const amplifyBranch = (process.env.AWS_BRANCH ?? "").trim();
|
|
42
50
|
const amplifyAppId = (process.env.AWS_APP_ID ?? "").trim();
|
|
43
|
-
// Amplify app id whose `main` branch is
|
|
44
|
-
//
|
|
51
|
+
// Amplify app id whose `main` branch is the original p.apyr.us backend; only
|
|
52
|
+
// that app keeps the historical account-global names (see site-backend-names.ts).
|
|
45
53
|
const productionAppId = (site.productionAppId ?? "").trim();
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// A feature flag in the site config wins over the environment.
|
|
77
|
-
const enableInboundEmail = site.features?.inboundEmail ?? readEnvFlag("PAPYRUS_ENABLE_INBOUND_EMAIL", isAmplifyProductionPipeline);
|
|
78
|
-
const enableSlackAgent = site.features?.slack ?? readEnvFlag("PAPYRUS_ENABLE_SLACK", false);
|
|
79
|
-
const enableStorageBackups = site.features?.storageBackups ?? readEnvFlag("PAPYRUS_ENABLE_STORAGE_BACKUPS", isAmplifyProductionPipeline);
|
|
54
|
+
const siteBackendIdentity = {
|
|
55
|
+
brandId: (site.brandId
|
|
56
|
+
?? process.env.PAPYRUS_SITE_BRAND
|
|
57
|
+
?? process.env.NEXT_PUBLIC_PAPYRUS_SITE_BRAND
|
|
58
|
+
?? "papyrus").trim().toLowerCase(),
|
|
59
|
+
amplifyAppId,
|
|
60
|
+
amplifyBranch,
|
|
61
|
+
productionAppId,
|
|
62
|
+
};
|
|
63
|
+
const isAmplifyProductionPipeline = isLegacyProductionPipeline(siteBackendIdentity);
|
|
64
|
+
const knowledgeVectorIndexName = deriveKnowledgeVectorIndexName(siteBackendIdentity);
|
|
65
|
+
const featureFlags = resolveSiteBackendFeatureFlags(site.features, process.env, siteBackendIdentity);
|
|
66
|
+
const enableInboundEmail = featureFlags.inboundEmail;
|
|
67
|
+
const enableSlackAgent = featureFlags.slack;
|
|
68
|
+
const enableStorageBackups = featureFlags.storageBackups;
|
|
69
|
+
const baseAuthConfig = site.auth ?? authConfigFromEnv();
|
|
70
|
+
const activeBrand = (papyrusSite.brands ?? []).find((brand) => brand.id.toLowerCase() === siteBackendIdentity.brandId);
|
|
71
|
+
const authConfig = {
|
|
72
|
+
...baseAuthConfig,
|
|
73
|
+
newsroomBasePath: baseAuthConfig.newsroomBasePath ?? activeBrand?.newsroomBasePath,
|
|
74
|
+
};
|
|
75
|
+
const publication = resolveSitePublication(site, activeBrand?.renderer?.kind);
|
|
76
|
+
if (publication !== undefined)
|
|
77
|
+
assertValidPublicationConfig(publication);
|
|
78
|
+
const publicationPlan = planS3ProjectionBackend(publication);
|
|
79
|
+
const renditionsPlan = planRenditions(publication, process.cwd());
|
|
80
|
+
setSiteFilesForContentActionsBundle(publicationPlan.enabled ? publicationPlan.siteFiles : undefined);
|
|
81
|
+
const plugins = resolvePlugins(site.plugins);
|
|
82
|
+
const pluginDefinitions = Object.assign({}, ...plugins.map(({ plugin }) => plugin.schemaDefinitions()));
|
|
83
|
+
const pluginFunctions = Object.assign({}, ...plugins.map(({ plugin }) => plugin.functions()));
|
|
80
84
|
const backend = defineBackend({
|
|
85
|
+
...pluginFunctions,
|
|
81
86
|
assignmentAction,
|
|
82
|
-
auth: defineSiteAuth(
|
|
87
|
+
auth: defineSiteAuth(authConfig),
|
|
83
88
|
categoryAction,
|
|
84
89
|
contentActions,
|
|
85
|
-
data,
|
|
86
|
-
graphqlJwtAuthorizer,
|
|
90
|
+
data: defineSiteData(pluginDefinitions),
|
|
87
91
|
knowledgeQuery,
|
|
88
92
|
manageUserRole,
|
|
89
93
|
modelAttachmentUpload,
|
|
@@ -96,10 +100,17 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
96
100
|
slackDelivery,
|
|
97
101
|
storage,
|
|
98
102
|
});
|
|
103
|
+
orderModelStacksInWaves(backend.data.resources.graphqlApi.node.scope, Object.keys(backend.data.resources.tables), site.resolverWaveSize ?? DEFAULT_RESOLVER_WAVE_SIZE);
|
|
104
|
+
const requestedCognitoDomainPrefix = (authConfig.cognitoDomainPrefix ?? "").trim();
|
|
105
|
+
if (authConfig.applyCognitoDomainPrefix === true
|
|
106
|
+
&& requestedCognitoDomainPrefix !== ""
|
|
107
|
+
&& authConfig.disableGoogleOAuth !== true) {
|
|
108
|
+
applyCognitoDomainPrefix(backend.auth.stack, requestedCognitoDomainPrefix);
|
|
109
|
+
}
|
|
99
110
|
const amplifyBackendDir = dirname(fileURLToPath(import.meta.url));
|
|
100
111
|
// Repo root in a Papyrus checkout; package root when installed from npm.
|
|
101
112
|
const projectRoot = resolve(amplifyBackendDir, "..");
|
|
102
|
-
const enableConsoleResponder =
|
|
113
|
+
const enableConsoleResponder = featureFlags.consoleResponder;
|
|
103
114
|
if (enableConsoleResponder
|
|
104
115
|
&& !process.env.PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI?.trim()
|
|
105
116
|
&& !process.env.PAPYRUS_CONSOLE_RESPONDER_ALLOW_LOCAL_BUILD?.trim()
|
|
@@ -139,36 +150,18 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
139
150
|
const messageThreadTable = backend.data.resources.tables.MessageThread;
|
|
140
151
|
const dataStack = Stack.of(messageTable);
|
|
141
152
|
const graphqlEndpoint = backend.data.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl;
|
|
142
|
-
const
|
|
143
|
-
const jwtAuthorizerEnvironment = jwtAuthorizerCfn?.environment;
|
|
144
|
-
const jwtAuthorizerVariables = jwtAuthorizerEnvironment
|
|
145
|
-
&& typeof jwtAuthorizerEnvironment === "object"
|
|
146
|
-
&& "variables" in jwtAuthorizerEnvironment
|
|
147
|
-
? jwtAuthorizerEnvironment.variables
|
|
148
|
-
: undefined;
|
|
149
|
-
const jwtSsmEnvConfig = process.env.AMPLIFY_SSM_ENV_CONFIG?.trim()
|
|
150
|
-
|| jwtAuthorizerVariables?.AMPLIFY_SSM_ENV_CONFIG?.trim()
|
|
151
|
-
|| "";
|
|
153
|
+
const amplifySsmEnvConfig = process.env.AMPLIFY_SSM_ENV_CONFIG?.trim() || "";
|
|
152
154
|
if (enableConsoleResponder) {
|
|
153
|
-
|
|
155
|
+
new ConsoleChatResponderStack(dataStack, "ConsoleChatResponder", {
|
|
154
156
|
messageTable,
|
|
155
157
|
messageStreamArn,
|
|
156
158
|
threadTable: messageThreadTable,
|
|
157
159
|
projectRoot,
|
|
158
160
|
graphqlEndpoint,
|
|
159
|
-
amplifySsmEnvConfig: jwtSsmEnvConfig || undefined,
|
|
160
161
|
responseTarget: process.env.PAPYRUS_CONSOLE_RESPONSE_TARGET,
|
|
161
162
|
model: process.env.PAPYRUS_CONSOLE_MODEL,
|
|
162
163
|
prebuiltImageUri: process.env.PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI,
|
|
163
164
|
});
|
|
164
|
-
if (jwtSsmEnvConfig) {
|
|
165
|
-
consoleChatResponder.responderFunction.addEnvironment("AMPLIFY_SSM_ENV_CONFIG", jwtSsmEnvConfig);
|
|
166
|
-
}
|
|
167
|
-
const jwtSecretSsmParam = process.env.PAPYRUS_JWT_SECRET_SSM_PARAM?.trim()
|
|
168
|
-
|| (productionAppId !== "" && amplifyAppId === productionAppId
|
|
169
|
-
? `/amplify/${amplifyAppId}/main-branch-cb38ada667/PAPYRUS_JWT_SECRET`
|
|
170
|
-
: "/amplify/papyrus/ryan-sandbox-adcd88a186/PAPYRUS_JWT_SECRET");
|
|
171
|
-
consoleChatResponder.responderFunction.addEnvironment("PAPYRUS_JWT_SECRET_SSM_PARAM", jwtSecretSsmParam);
|
|
172
165
|
}
|
|
173
166
|
if (enableSlackAgent) {
|
|
174
167
|
const slackEventsLambda = backend.slackEvents.resources.lambda;
|
|
@@ -180,9 +173,9 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
180
173
|
backend.slackEvents.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", graphqlEndpoint);
|
|
181
174
|
backend.slackEvents.addEnvironment("PAPYRUS_CONSOLE_RESPONSE_TARGET", (process.env.PAPYRUS_CONSOLE_RESPONSE_TARGET ?? "cloud").trim() || "cloud");
|
|
182
175
|
backend.slackEvents.addEnvironment("PAPYRUS_SLACK_ALLOWED_USER_IDS", (process.env.PAPYRUS_SLACK_ALLOWED_USER_IDS ?? "").trim());
|
|
183
|
-
if (
|
|
184
|
-
backend.slackEvents.addEnvironment("AMPLIFY_SSM_ENV_CONFIG",
|
|
185
|
-
backend.slackDelivery.addEnvironment("AMPLIFY_SSM_ENV_CONFIG",
|
|
176
|
+
if (amplifySsmEnvConfig) {
|
|
177
|
+
backend.slackEvents.addEnvironment("AMPLIFY_SSM_ENV_CONFIG", amplifySsmEnvConfig);
|
|
178
|
+
backend.slackDelivery.addEnvironment("AMPLIFY_SSM_ENV_CONFIG", amplifySsmEnvConfig);
|
|
186
179
|
}
|
|
187
180
|
backend.slackDelivery.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", graphqlEndpoint);
|
|
188
181
|
backend.slackDelivery.addEnvironment("PAPYRUS_CONSOLE_RESPONSE_TARGET", (process.env.PAPYRUS_CONSOLE_RESPONSE_TARGET ?? "cloud").trim() || "cloud");
|
|
@@ -244,37 +237,19 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
244
237
|
let storageBackupPlan;
|
|
245
238
|
let storageBackupVault;
|
|
246
239
|
const storageBucketCfn = storageBucket.node.defaultChild;
|
|
247
|
-
if ((enableStorageBackups || enableInboundEmail) && storageBucketCfn) {
|
|
248
|
-
// Backups
|
|
240
|
+
if ((enableStorageBackups || enableInboundEmail || renditionsPlan.enabled) && storageBucketCfn) {
|
|
241
|
+
// Backups, inbound-email intake and upload-time renditions all rely on S3 → EventBridge notifications.
|
|
249
242
|
storageBucketCfn.addPropertyOverride("NotificationConfiguration.EventBridgeConfiguration.EventBridgeEnabled", true);
|
|
250
243
|
}
|
|
251
244
|
if (enableStorageBackups) {
|
|
252
245
|
const storageBackupsStack = backend.createStack("storage-backups");
|
|
253
|
-
const storageBackupVaultName =
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
: `papyrus-${sanitizeAwsName(storageBackupsStack.stackName, 32)}-media-vault`));
|
|
257
|
-
storageBackupVault = new backup.BackupVault(storageBackupsStack, "PapyrusStorageBackupVault", {
|
|
246
|
+
const storageBackupVaultName = deriveStorageBackupVaultName(siteBackendIdentity, process.env.PAPYRUS_STORAGE_BACKUP_VAULT_NAME);
|
|
247
|
+
const storageBackups = addStorageBackups(storageBackupsStack, {
|
|
248
|
+
storageBucket,
|
|
258
249
|
backupVaultName: storageBackupVaultName,
|
|
259
|
-
removalPolicy: RemovalPolicy.RETAIN,
|
|
260
|
-
});
|
|
261
|
-
storageBackupPlan = new backup.BackupPlan(storageBackupsStack, "PapyrusStorageBackupPlan", {
|
|
262
|
-
backupVault: storageBackupVault,
|
|
263
|
-
});
|
|
264
|
-
storageBackupPlan.addRule(new backup.BackupPlanRule({
|
|
265
|
-
ruleName: "papyrus-storage-pitr-35d",
|
|
266
|
-
enableContinuousBackup: true,
|
|
267
|
-
deleteAfter: Duration.days(35),
|
|
268
|
-
}));
|
|
269
|
-
storageBackupPlan.addRule(new backup.BackupPlanRule({
|
|
270
|
-
ruleName: "papyrus-storage-daily-365d",
|
|
271
|
-
scheduleExpression: events.Schedule.cron({ minute: "0", hour: "5" }),
|
|
272
|
-
deleteAfter: Duration.days(365),
|
|
273
|
-
}));
|
|
274
|
-
storageBackupPlan.addSelection("PapyrusStorageBackupSelection", {
|
|
275
|
-
allowRestores: true,
|
|
276
|
-
resources: [backup.BackupResource.fromArn(storageBucket.bucketArn)],
|
|
277
250
|
});
|
|
251
|
+
storageBackupVault = storageBackups.vault;
|
|
252
|
+
storageBackupPlan = storageBackups.plan;
|
|
278
253
|
}
|
|
279
254
|
// Grant S3 access on Lambda roles only (not storage bucket policies) to avoid
|
|
280
255
|
// storage ↔ data nested-stack circular dependencies from allow.resource().
|
|
@@ -347,9 +322,58 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
347
322
|
if (rebuildSettings.resources.length > 0) {
|
|
348
323
|
contentActionsLambda.addToRolePolicy(new PolicyStatement({ actions: rebuildSettings.actions, resources: rebuildSettings.resources }));
|
|
349
324
|
}
|
|
325
|
+
let s3ProjectionPublication;
|
|
326
|
+
if (publication !== undefined) {
|
|
327
|
+
s3ProjectionPublication = addS3ProjectionPublication(backend.createStack("s3-projection"), publication, {
|
|
328
|
+
mediaBucket: storageBucket,
|
|
329
|
+
renditions: renditionsPlan,
|
|
330
|
+
});
|
|
331
|
+
contentActionsLambda.node.defaultChild.memorySize = publicationPlan.contentActionsMemorySize;
|
|
332
|
+
if (publication.reprojectOnDeploy === true) {
|
|
333
|
+
contentActionsLambda.node.defaultChild.timeout = REPROJECT_ON_DEPLOY_TIMEOUT_SECONDS;
|
|
334
|
+
addReprojectOnDeploy(backend.createStack("reproject-on-deploy"), contentActionsLambda, reprojectDeployToken(process.env));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (s3ProjectionPublication !== undefined) {
|
|
338
|
+
addProjectionRetries(contentActionsLambda, s3ProjectionPublication.deadLetterQueue);
|
|
339
|
+
}
|
|
340
|
+
const publicationWiring = contentActionsPublicationWiring(s3ProjectionPublication);
|
|
341
|
+
for (const [name, value] of Object.entries(publicationWiring.environment)) {
|
|
342
|
+
contentActionsLambda.addEnvironment(name, value);
|
|
343
|
+
}
|
|
344
|
+
for (const statement of publicationWiring.policyStatements) {
|
|
345
|
+
contentActionsLambda.addToRolePolicy(statement);
|
|
346
|
+
}
|
|
350
347
|
if ((site.revalidateBaseUrl ?? "").trim() !== "") {
|
|
351
348
|
backend.contentActions.addEnvironment("PAPYRUS_REVALIDATE_BASE_URL", (site.revalidateBaseUrl ?? "").trim());
|
|
352
|
-
|
|
349
|
+
const revalidateSecretParameter = (process.env.PAPYRUS_REVALIDATE_SECRET_PARAMETER ?? "").trim();
|
|
350
|
+
if (!revalidateSecretParameter.startsWith("/")) {
|
|
351
|
+
throw new Error("revalidateBaseUrl is set but PAPYRUS_REVALIDATE_SECRET_PARAMETER is not (an SSM parameter name starting with /). The app-shell template sets it on the production branch for Pretext sites; see docs/site-hosting.md.");
|
|
352
|
+
}
|
|
353
|
+
backend.contentActions.addEnvironment("PAPYRUS_REVALIDATE_SECRET_PARAMETER", revalidateSecretParameter);
|
|
354
|
+
const revalidateStack = Stack.of(contentActionsLambda);
|
|
355
|
+
contentActionsLambda.addToRolePolicy(new PolicyStatement(revalidateSecretReadStatement({
|
|
356
|
+
partition: revalidateStack.partition,
|
|
357
|
+
region: revalidateStack.region,
|
|
358
|
+
account: revalidateStack.account,
|
|
359
|
+
parameterName: revalidateSecretParameter,
|
|
360
|
+
})));
|
|
361
|
+
const distributionProblems = validateRevalidateDistributionIds(site.revalidateDistributionIds, site.revalidateBaseUrl);
|
|
362
|
+
if (distributionProblems.length > 0)
|
|
363
|
+
throw new Error(`Invalid backend config: ${distributionProblems.join("; ")}`);
|
|
364
|
+
const distributionIds = site.revalidateDistributionIds ?? [];
|
|
365
|
+
if (distributionIds.length > 0) {
|
|
366
|
+
for (const [name, value] of Object.entries(revalidateDistributionEnvironment(distributionIds))) {
|
|
367
|
+
backend.contentActions.addEnvironment(name, value);
|
|
368
|
+
}
|
|
369
|
+
contentActionsLambda.addToRolePolicy(new PolicyStatement(revalidateInvalidationStatement({ partition: revalidateStack.partition, account: revalidateStack.account, distributionIds })));
|
|
370
|
+
for (const id of distributionIds) {
|
|
371
|
+
addInvalidationPathAlarm(revalidateStack, `ReaderEdgeInvalidationPaths${id}`, id);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
else if (site.revalidateDistributionIds !== undefined) {
|
|
376
|
+
throw new Error("Invalid backend config: revalidateDistributionIds requires revalidateBaseUrl");
|
|
353
377
|
}
|
|
354
378
|
grantNewsroomReadWriteDelete(backend.modelAttachmentUpload.resources.lambda);
|
|
355
379
|
grantMediaReadWriteDelete(backend.modelAttachmentUpload.resources.lambda);
|
|
@@ -416,83 +440,25 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
416
440
|
resources: ["*"],
|
|
417
441
|
}));
|
|
418
442
|
const inboundEventStack = Stack.of(receiveLambda);
|
|
419
|
-
// SES receipt rule
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const verifyInboundEmailDomain = new AwsCustomResource(storageStack, "VerifyInboundEmailDomain", {
|
|
426
|
-
onCreate: {
|
|
427
|
-
service: "SES",
|
|
428
|
-
action: "verifyDomainIdentity",
|
|
429
|
-
parameters: {
|
|
430
|
-
Domain: inboundEmailDomain,
|
|
431
|
-
},
|
|
432
|
-
physicalResourceId: PhysicalResourceId.of(`ses-domain-verify-${inboundEmailDomain}`),
|
|
433
|
-
},
|
|
434
|
-
onUpdate: {
|
|
435
|
-
service: "SES",
|
|
436
|
-
action: "verifyDomainIdentity",
|
|
437
|
-
parameters: {
|
|
438
|
-
Domain: inboundEmailDomain,
|
|
439
|
-
},
|
|
440
|
-
physicalResourceId: PhysicalResourceId.of(`ses-domain-verify-${inboundEmailDomain}`),
|
|
441
|
-
},
|
|
442
|
-
policy: AwsCustomResourcePolicy.fromSdkCalls({
|
|
443
|
-
resources: AwsCustomResourcePolicy.ANY_RESOURCE,
|
|
444
|
-
}),
|
|
445
|
-
timeout: Duration.minutes(2),
|
|
446
|
-
});
|
|
447
|
-
new route53.TxtRecord(storageStack, "PapyrusInboundEmailDomainVerification", {
|
|
448
|
-
zone: inboundDnsZone,
|
|
449
|
-
recordName: `_amazonses.${inboundDnsRecordName}`,
|
|
450
|
-
ttl: Duration.minutes(5),
|
|
451
|
-
values: [verifyInboundEmailDomain.getResponseField("VerificationToken")],
|
|
452
|
-
});
|
|
453
|
-
const inboundRuleSet = new ses.ReceiptRuleSet(storageStack, "PapyrusInboundEmailRuleSet", {
|
|
454
|
-
receiptRuleSetName: `papyrus-inbound-${inboundEmailDomain.replace(/\./g, "-")}`,
|
|
455
|
-
});
|
|
456
|
-
inboundRuleSet.addRule("PapyrusInboundSubmissions", {
|
|
443
|
+
// SES allows one active receipt rule set per region, so a non-legacy site
|
|
444
|
+
// creates its own brand-named rule set and never activates it here.
|
|
445
|
+
const inboundSesPlan = planInboundEmailSes(siteBackendIdentity, enableInboundEmail, site.inboundEmailSes);
|
|
446
|
+
if (inboundSesPlan.createReceiptRules) {
|
|
447
|
+
addInboundEmailSesIntake(storageStack, {
|
|
448
|
+
storageBucket,
|
|
457
449
|
recipients: inboundRecipients,
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
service: "SES",
|
|
471
|
-
action: "setActiveReceiptRuleSet",
|
|
472
|
-
parameters: {
|
|
473
|
-
RuleSetName: inboundRuleSet.receiptRuleSetName,
|
|
474
|
-
},
|
|
475
|
-
physicalResourceId: PhysicalResourceId.of(`activate-${inboundRuleSet.receiptRuleSetName}`),
|
|
476
|
-
},
|
|
477
|
-
onUpdate: {
|
|
478
|
-
service: "SES",
|
|
479
|
-
action: "setActiveReceiptRuleSet",
|
|
480
|
-
parameters: {
|
|
481
|
-
RuleSetName: inboundRuleSet.receiptRuleSetName,
|
|
482
|
-
},
|
|
483
|
-
physicalResourceId: PhysicalResourceId.of(`activate-${inboundRuleSet.receiptRuleSetName}`),
|
|
484
|
-
},
|
|
485
|
-
onDelete: {
|
|
486
|
-
service: "SES",
|
|
487
|
-
action: "setActiveReceiptRuleSet",
|
|
488
|
-
parameters: {
|
|
489
|
-
RuleSetName: null,
|
|
490
|
-
},
|
|
491
|
-
},
|
|
492
|
-
policy: AwsCustomResourcePolicy.fromSdkCalls({
|
|
493
|
-
resources: AwsCustomResourcePolicy.ANY_RESOURCE,
|
|
494
|
-
}),
|
|
495
|
-
timeout: Duration.minutes(2),
|
|
450
|
+
ruleSetName: deriveReceiptRuleSetName(siteBackendIdentity, inboundEmailDomain),
|
|
451
|
+
existingRuleSetName: site.inboundEmailSes?.existingReceiptRuleSetName,
|
|
452
|
+
ruleName: deriveReceiptRuleName(siteBackendIdentity),
|
|
453
|
+
activateRuleSet: inboundSesPlan.activateReceiptRuleSet,
|
|
454
|
+
domainIdentity: inboundSesPlan.manageDomainIdentity
|
|
455
|
+
? {
|
|
456
|
+
domain: inboundEmailDomain,
|
|
457
|
+
hostedZoneId: inboundDnsZoneId,
|
|
458
|
+
hostedZoneName: inboundDnsZoneName,
|
|
459
|
+
recordName: inboundDnsRecordName,
|
|
460
|
+
}
|
|
461
|
+
: undefined,
|
|
496
462
|
});
|
|
497
463
|
}
|
|
498
464
|
new InboundEmailStack(inboundEventStack, "InboundEmail", {
|
|
@@ -611,5 +577,6 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
611
577
|
: null,
|
|
612
578
|
},
|
|
613
579
|
});
|
|
580
|
+
wirePlugins(backend, plugins, contentActionsLambda);
|
|
614
581
|
return backend;
|
|
615
582
|
}
|