@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
package/amplify/site-backend.js
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
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, siteAuthRedirectUrls } from "./auth/resource.js";
|
|
21
|
+
import { PROSPECTUS_SIGN_IN_CALLBACK_PATH, addProspectusGoogleOnlyClient } from "./auth/prospectus-google-client.js";
|
|
22
|
+
import { defineSiteData } from "./data/resource.js";
|
|
23
|
+
import { resolvePlugins } from "./plugins/registry.js";
|
|
24
|
+
import { wirePlugins } from "./plugins/wire.js";
|
|
18
25
|
import { assignmentAction } from "./functions/assignment-action/resource.js";
|
|
19
26
|
import { categoryAction } from "./functions/category-action/resource.js";
|
|
20
27
|
import { ConsoleChatResponderStack } from "./functions/console-chat-responder/resource.js";
|
|
21
|
-
import {
|
|
22
|
-
import { contentActions } from "./functions/content-actions/resource.js";
|
|
28
|
+
import { contentActions, setSiteFilesForContentActionsBundle } from "./functions/content-actions/resource.js";
|
|
23
29
|
import { knowledgeQuery } from "./functions/knowledge-query/resource.js";
|
|
24
30
|
import { manageUserRole } from "./functions/manage-user-role/resource.js";
|
|
25
31
|
import { modelAttachmentUpload } from "./functions/model-attachment-upload/resource.js";
|
|
@@ -30,7 +36,10 @@ import { emailSubmissionProcessor } from "./functions/email-submission-processor
|
|
|
30
36
|
import { sesInboundReceive } from "./functions/ses-inbound-receive/resource.js";
|
|
31
37
|
import { slackDelivery } from "./functions/slack-delivery/resource.js";
|
|
32
38
|
import { slackEvents } from "./functions/slack-events/resource.js";
|
|
39
|
+
import { addInboundEmailSesIntake } from "./inbound-email/ses-intake.js";
|
|
33
40
|
import { InboundEmailStack } from "./inbound-email/stack.js";
|
|
41
|
+
import { deriveKnowledgeVectorIndexName, deriveReceiptRuleName, deriveReceiptRuleSetName, deriveStorageBackupVaultName, isLegacyProductionPipeline, planInboundEmailSes, resolveSiteBackendFeatureFlags, } from "./site-backend-names.js";
|
|
42
|
+
import { addStorageBackups } from "./storage-backups/construct.js";
|
|
34
43
|
import { storage } from "./storage/resource.js";
|
|
35
44
|
export function defineSiteBackend(papyrusSite) {
|
|
36
45
|
// `papyrus.config.ts`'s default export. Backend options live under `backend`;
|
|
@@ -40,50 +49,46 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
40
49
|
const knowledgeEmbeddingModel = "text-embedding-3-small";
|
|
41
50
|
const amplifyBranch = (process.env.AWS_BRANCH ?? "").trim();
|
|
42
51
|
const amplifyAppId = (process.env.AWS_APP_ID ?? "").trim();
|
|
43
|
-
// Amplify app id whose `main` branch is
|
|
44
|
-
//
|
|
52
|
+
// Amplify app id whose `main` branch is the original p.apyr.us backend; only
|
|
53
|
+
// that app keeps the historical account-global names (see site-backend-names.ts).
|
|
45
54
|
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);
|
|
55
|
+
const siteBackendIdentity = {
|
|
56
|
+
brandId: (site.brandId
|
|
57
|
+
?? process.env.PAPYRUS_SITE_BRAND
|
|
58
|
+
?? process.env.NEXT_PUBLIC_PAPYRUS_SITE_BRAND
|
|
59
|
+
?? "papyrus").trim().toLowerCase(),
|
|
60
|
+
amplifyAppId,
|
|
61
|
+
amplifyBranch,
|
|
62
|
+
productionAppId,
|
|
63
|
+
};
|
|
64
|
+
const isAmplifyProductionPipeline = isLegacyProductionPipeline(siteBackendIdentity);
|
|
65
|
+
const knowledgeVectorIndexName = deriveKnowledgeVectorIndexName(siteBackendIdentity);
|
|
66
|
+
const featureFlags = resolveSiteBackendFeatureFlags(site.features, process.env, siteBackendIdentity);
|
|
67
|
+
const enableInboundEmail = featureFlags.inboundEmail;
|
|
68
|
+
const enableSlackAgent = featureFlags.slack;
|
|
69
|
+
const enableStorageBackups = featureFlags.storageBackups;
|
|
70
|
+
const baseAuthConfig = site.auth ?? authConfigFromEnv();
|
|
71
|
+
const activeBrand = (papyrusSite.brands ?? []).find((brand) => brand.id.toLowerCase() === siteBackendIdentity.brandId);
|
|
72
|
+
const authConfig = {
|
|
73
|
+
...baseAuthConfig,
|
|
74
|
+
newsroomBasePath: baseAuthConfig.newsroomBasePath ?? activeBrand?.newsroomBasePath,
|
|
75
|
+
};
|
|
76
|
+
const publication = resolveSitePublication(site, activeBrand?.renderer?.kind);
|
|
77
|
+
if (publication !== undefined)
|
|
78
|
+
assertValidPublicationConfig(publication);
|
|
79
|
+
const publicationPlan = planS3ProjectionBackend(publication);
|
|
80
|
+
const renditionsPlan = planRenditions(publication, process.cwd());
|
|
81
|
+
setSiteFilesForContentActionsBundle(publicationPlan.enabled ? publicationPlan.siteFiles : undefined);
|
|
82
|
+
const plugins = resolvePlugins(site.plugins);
|
|
83
|
+
const pluginDefinitions = Object.assign({}, ...plugins.map(({ plugin }) => plugin.schemaDefinitions()));
|
|
84
|
+
const pluginFunctions = Object.assign({}, ...plugins.map(({ plugin }) => plugin.functions()));
|
|
80
85
|
const backend = defineBackend({
|
|
86
|
+
...pluginFunctions,
|
|
81
87
|
assignmentAction,
|
|
82
|
-
auth: defineSiteAuth(
|
|
88
|
+
auth: defineSiteAuth(authConfig),
|
|
83
89
|
categoryAction,
|
|
84
90
|
contentActions,
|
|
85
|
-
data,
|
|
86
|
-
graphqlJwtAuthorizer,
|
|
91
|
+
data: defineSiteData(pluginDefinitions),
|
|
87
92
|
knowledgeQuery,
|
|
88
93
|
manageUserRole,
|
|
89
94
|
modelAttachmentUpload,
|
|
@@ -96,10 +101,20 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
96
101
|
slackDelivery,
|
|
97
102
|
storage,
|
|
98
103
|
});
|
|
104
|
+
orderModelStacksInWaves(backend.data.resources.graphqlApi.node.scope, Object.keys(backend.data.resources.tables), site.resolverWaveSize ?? DEFAULT_RESOLVER_WAVE_SIZE);
|
|
105
|
+
const requestedCognitoDomainPrefix = (authConfig.cognitoDomainPrefix ?? "").trim();
|
|
106
|
+
if (authConfig.applyCognitoDomainPrefix === true
|
|
107
|
+
&& requestedCognitoDomainPrefix !== ""
|
|
108
|
+
&& authConfig.disableGoogleOAuth !== true) {
|
|
109
|
+
applyCognitoDomainPrefix(backend.auth.stack, requestedCognitoDomainPrefix);
|
|
110
|
+
}
|
|
111
|
+
const prospectusGoogleOnlyClient = authConfig.disableGoogleOAuth === true
|
|
112
|
+
? undefined
|
|
113
|
+
: addProspectusGoogleOnlyClient(backend.auth.stack, backend.auth.resources.userPool, siteAuthRedirectUrls(authConfig));
|
|
99
114
|
const amplifyBackendDir = dirname(fileURLToPath(import.meta.url));
|
|
100
115
|
// Repo root in a Papyrus checkout; package root when installed from npm.
|
|
101
116
|
const projectRoot = resolve(amplifyBackendDir, "..");
|
|
102
|
-
const enableConsoleResponder =
|
|
117
|
+
const enableConsoleResponder = featureFlags.consoleResponder;
|
|
103
118
|
if (enableConsoleResponder
|
|
104
119
|
&& !process.env.PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI?.trim()
|
|
105
120
|
&& !process.env.PAPYRUS_CONSOLE_RESPONDER_ALLOW_LOCAL_BUILD?.trim()
|
|
@@ -139,36 +154,18 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
139
154
|
const messageThreadTable = backend.data.resources.tables.MessageThread;
|
|
140
155
|
const dataStack = Stack.of(messageTable);
|
|
141
156
|
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
|
-
|| "";
|
|
157
|
+
const amplifySsmEnvConfig = process.env.AMPLIFY_SSM_ENV_CONFIG?.trim() || "";
|
|
152
158
|
if (enableConsoleResponder) {
|
|
153
|
-
|
|
159
|
+
new ConsoleChatResponderStack(dataStack, "ConsoleChatResponder", {
|
|
154
160
|
messageTable,
|
|
155
161
|
messageStreamArn,
|
|
156
162
|
threadTable: messageThreadTable,
|
|
157
163
|
projectRoot,
|
|
158
164
|
graphqlEndpoint,
|
|
159
|
-
amplifySsmEnvConfig: jwtSsmEnvConfig || undefined,
|
|
160
165
|
responseTarget: process.env.PAPYRUS_CONSOLE_RESPONSE_TARGET,
|
|
161
166
|
model: process.env.PAPYRUS_CONSOLE_MODEL,
|
|
162
167
|
prebuiltImageUri: process.env.PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI,
|
|
163
168
|
});
|
|
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
169
|
}
|
|
173
170
|
if (enableSlackAgent) {
|
|
174
171
|
const slackEventsLambda = backend.slackEvents.resources.lambda;
|
|
@@ -180,9 +177,9 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
180
177
|
backend.slackEvents.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", graphqlEndpoint);
|
|
181
178
|
backend.slackEvents.addEnvironment("PAPYRUS_CONSOLE_RESPONSE_TARGET", (process.env.PAPYRUS_CONSOLE_RESPONSE_TARGET ?? "cloud").trim() || "cloud");
|
|
182
179
|
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",
|
|
180
|
+
if (amplifySsmEnvConfig) {
|
|
181
|
+
backend.slackEvents.addEnvironment("AMPLIFY_SSM_ENV_CONFIG", amplifySsmEnvConfig);
|
|
182
|
+
backend.slackDelivery.addEnvironment("AMPLIFY_SSM_ENV_CONFIG", amplifySsmEnvConfig);
|
|
186
183
|
}
|
|
187
184
|
backend.slackDelivery.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", graphqlEndpoint);
|
|
188
185
|
backend.slackDelivery.addEnvironment("PAPYRUS_CONSOLE_RESPONSE_TARGET", (process.env.PAPYRUS_CONSOLE_RESPONSE_TARGET ?? "cloud").trim() || "cloud");
|
|
@@ -244,37 +241,19 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
244
241
|
let storageBackupPlan;
|
|
245
242
|
let storageBackupVault;
|
|
246
243
|
const storageBucketCfn = storageBucket.node.defaultChild;
|
|
247
|
-
if ((enableStorageBackups || enableInboundEmail) && storageBucketCfn) {
|
|
248
|
-
// Backups
|
|
244
|
+
if ((enableStorageBackups || enableInboundEmail || renditionsPlan.enabled) && storageBucketCfn) {
|
|
245
|
+
// Backups, inbound-email intake and upload-time renditions all rely on S3 → EventBridge notifications.
|
|
249
246
|
storageBucketCfn.addPropertyOverride("NotificationConfiguration.EventBridgeConfiguration.EventBridgeEnabled", true);
|
|
250
247
|
}
|
|
251
248
|
if (enableStorageBackups) {
|
|
252
249
|
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", {
|
|
250
|
+
const storageBackupVaultName = deriveStorageBackupVaultName(siteBackendIdentity, process.env.PAPYRUS_STORAGE_BACKUP_VAULT_NAME);
|
|
251
|
+
const storageBackups = addStorageBackups(storageBackupsStack, {
|
|
252
|
+
storageBucket,
|
|
258
253
|
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
254
|
});
|
|
255
|
+
storageBackupVault = storageBackups.vault;
|
|
256
|
+
storageBackupPlan = storageBackups.plan;
|
|
278
257
|
}
|
|
279
258
|
// Grant S3 access on Lambda roles only (not storage bucket policies) to avoid
|
|
280
259
|
// storage ↔ data nested-stack circular dependencies from allow.resource().
|
|
@@ -347,9 +326,58 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
347
326
|
if (rebuildSettings.resources.length > 0) {
|
|
348
327
|
contentActionsLambda.addToRolePolicy(new PolicyStatement({ actions: rebuildSettings.actions, resources: rebuildSettings.resources }));
|
|
349
328
|
}
|
|
329
|
+
let s3ProjectionPublication;
|
|
330
|
+
if (publication !== undefined) {
|
|
331
|
+
s3ProjectionPublication = addS3ProjectionPublication(backend.createStack("s3-projection"), publication, {
|
|
332
|
+
mediaBucket: storageBucket,
|
|
333
|
+
renditions: renditionsPlan,
|
|
334
|
+
});
|
|
335
|
+
contentActionsLambda.node.defaultChild.memorySize = publicationPlan.contentActionsMemorySize;
|
|
336
|
+
if (publication.reprojectOnDeploy === true) {
|
|
337
|
+
contentActionsLambda.node.defaultChild.timeout = REPROJECT_ON_DEPLOY_TIMEOUT_SECONDS;
|
|
338
|
+
addReprojectOnDeploy(backend.createStack("reproject-on-deploy"), contentActionsLambda, reprojectDeployToken(process.env));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (s3ProjectionPublication !== undefined) {
|
|
342
|
+
addProjectionRetries(contentActionsLambda, s3ProjectionPublication.deadLetterQueue);
|
|
343
|
+
}
|
|
344
|
+
const publicationWiring = contentActionsPublicationWiring(s3ProjectionPublication);
|
|
345
|
+
for (const [name, value] of Object.entries(publicationWiring.environment)) {
|
|
346
|
+
contentActionsLambda.addEnvironment(name, value);
|
|
347
|
+
}
|
|
348
|
+
for (const statement of publicationWiring.policyStatements) {
|
|
349
|
+
contentActionsLambda.addToRolePolicy(statement);
|
|
350
|
+
}
|
|
350
351
|
if ((site.revalidateBaseUrl ?? "").trim() !== "") {
|
|
351
352
|
backend.contentActions.addEnvironment("PAPYRUS_REVALIDATE_BASE_URL", (site.revalidateBaseUrl ?? "").trim());
|
|
352
|
-
|
|
353
|
+
const revalidateSecretParameter = (process.env.PAPYRUS_REVALIDATE_SECRET_PARAMETER ?? "").trim();
|
|
354
|
+
if (!revalidateSecretParameter.startsWith("/")) {
|
|
355
|
+
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.");
|
|
356
|
+
}
|
|
357
|
+
backend.contentActions.addEnvironment("PAPYRUS_REVALIDATE_SECRET_PARAMETER", revalidateSecretParameter);
|
|
358
|
+
const revalidateStack = Stack.of(contentActionsLambda);
|
|
359
|
+
contentActionsLambda.addToRolePolicy(new PolicyStatement(revalidateSecretReadStatement({
|
|
360
|
+
partition: revalidateStack.partition,
|
|
361
|
+
region: revalidateStack.region,
|
|
362
|
+
account: revalidateStack.account,
|
|
363
|
+
parameterName: revalidateSecretParameter,
|
|
364
|
+
})));
|
|
365
|
+
const distributionProblems = validateRevalidateDistributionIds(site.revalidateDistributionIds, site.revalidateBaseUrl);
|
|
366
|
+
if (distributionProblems.length > 0)
|
|
367
|
+
throw new Error(`Invalid backend config: ${distributionProblems.join("; ")}`);
|
|
368
|
+
const distributionIds = site.revalidateDistributionIds ?? [];
|
|
369
|
+
if (distributionIds.length > 0) {
|
|
370
|
+
for (const [name, value] of Object.entries(revalidateDistributionEnvironment(distributionIds))) {
|
|
371
|
+
backend.contentActions.addEnvironment(name, value);
|
|
372
|
+
}
|
|
373
|
+
contentActionsLambda.addToRolePolicy(new PolicyStatement(revalidateInvalidationStatement({ partition: revalidateStack.partition, account: revalidateStack.account, distributionIds })));
|
|
374
|
+
for (const id of distributionIds) {
|
|
375
|
+
addInvalidationPathAlarm(revalidateStack, `ReaderEdgeInvalidationPaths${id}`, id);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else if (site.revalidateDistributionIds !== undefined) {
|
|
380
|
+
throw new Error("Invalid backend config: revalidateDistributionIds requires revalidateBaseUrl");
|
|
353
381
|
}
|
|
354
382
|
grantNewsroomReadWriteDelete(backend.modelAttachmentUpload.resources.lambda);
|
|
355
383
|
grantMediaReadWriteDelete(backend.modelAttachmentUpload.resources.lambda);
|
|
@@ -416,83 +444,25 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
416
444
|
resources: ["*"],
|
|
417
445
|
}));
|
|
418
446
|
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", {
|
|
447
|
+
// SES allows one active receipt rule set per region, so a non-legacy site
|
|
448
|
+
// creates its own brand-named rule set and never activates it here.
|
|
449
|
+
const inboundSesPlan = planInboundEmailSes(siteBackendIdentity, enableInboundEmail, site.inboundEmailSes);
|
|
450
|
+
if (inboundSesPlan.createReceiptRules) {
|
|
451
|
+
addInboundEmailSesIntake(storageStack, {
|
|
452
|
+
storageBucket,
|
|
457
453
|
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),
|
|
454
|
+
ruleSetName: deriveReceiptRuleSetName(siteBackendIdentity, inboundEmailDomain),
|
|
455
|
+
existingRuleSetName: site.inboundEmailSes?.existingReceiptRuleSetName,
|
|
456
|
+
ruleName: deriveReceiptRuleName(siteBackendIdentity),
|
|
457
|
+
activateRuleSet: inboundSesPlan.activateReceiptRuleSet,
|
|
458
|
+
domainIdentity: inboundSesPlan.manageDomainIdentity
|
|
459
|
+
? {
|
|
460
|
+
domain: inboundEmailDomain,
|
|
461
|
+
hostedZoneId: inboundDnsZoneId,
|
|
462
|
+
hostedZoneName: inboundDnsZoneName,
|
|
463
|
+
recordName: inboundDnsRecordName,
|
|
464
|
+
}
|
|
465
|
+
: undefined,
|
|
496
466
|
});
|
|
497
467
|
}
|
|
498
468
|
new InboundEmailStack(inboundEventStack, "InboundEmail", {
|
|
@@ -580,6 +550,9 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
580
550
|
}));
|
|
581
551
|
backend.addOutput({
|
|
582
552
|
custom: {
|
|
553
|
+
prospectusSignIn: prospectusGoogleOnlyClient
|
|
554
|
+
? { clientId: prospectusGoogleOnlyClient.ref, callbackPath: PROSPECTUS_SIGN_IN_CALLBACK_PATH }
|
|
555
|
+
: null,
|
|
583
556
|
inboundEmail: enableInboundEmail
|
|
584
557
|
? {
|
|
585
558
|
domain: inboundEmailDomain,
|
|
@@ -611,5 +584,6 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
611
584
|
: null,
|
|
612
585
|
},
|
|
613
586
|
});
|
|
587
|
+
wirePlugins(backend, plugins, contentActionsLambda);
|
|
614
588
|
return backend;
|
|
615
589
|
}
|