@anthusai/papyrus 2.0.0-next.2 → 2.0.0-next.20
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/data/resource.js +13 -0
- package/amplify/data/resource.ts +15 -0
- package/amplify/data/schema.js +18 -0
- package/amplify/data/schema.ts +20 -0
- package/amplify/functions/assignment-action/data-access.js +88 -0
- package/amplify/functions/assignment-action/data-access.ts +87 -0
- package/amplify/functions/content-actions/handler.py +22 -0
- package/amplify/functions/editorial-checks/handler.py +14 -0
- package/amplify/functions/editorial-checks/requirements.txt +1 -0
- package/amplify/functions/editorial-checks/resource.js +43 -0
- package/amplify/functions/editorial-checks/resource.ts +60 -0
- package/amplify/functions/editorial-checks/schema.js +92 -0
- package/amplify/functions/editorial-checks/schema.ts +94 -0
- package/amplify/functions/editorial-checks/wire.js +49 -0
- package/amplify/functions/editorial-checks/wire.ts +57 -0
- package/amplify/functions/model-attachment-upload/handler.ts +1 -0
- package/amplify/functions/monitoring-view/handler.py +11 -0
- package/amplify/functions/monitoring-view/resource.js +24 -0
- package/amplify/functions/monitoring-view/resource.ts +34 -0
- package/amplify/functions/monitoring-view/schema.js +16 -0
- package/amplify/functions/monitoring-view/schema.ts +18 -0
- package/amplify/functions/monitoring-view/wire.js +37 -0
- package/amplify/functions/monitoring-view/wire.ts +47 -0
- package/amplify/functions/shared/python-bundle.js +20 -14
- package/amplify/functions/shared/python-bundle.ts +25 -18
- package/amplify/publication/redirect-and-index.js +83 -0
- package/amplify/publication/redirect-rules.js +61 -0
- package/amplify/publication/redirect-rules.ts +79 -0
- package/amplify/publication/s3-projection.js +5 -1
- package/amplify/publication/s3-projection.ts +7 -1
- package/amplify/site-backend.js +18 -0
- package/amplify/site-backend.ts +24 -0
- package/app/api/newsroom/curation/context/route.ts +9 -0
- package/app/api/newsroom/curation/priming/route.ts +9 -0
- package/app/api/newsroom/curation/publication/route.ts +9 -0
- package/bin/papyrus-infra.mjs +20 -1
- package/components/editorial-checks-admin-panel.tsx +347 -0
- package/components/editorial-finding-card.tsx +162 -0
- package/components/editorial-findings-panel.tsx +91 -0
- package/components/editorial-preview-popover.tsx +103 -0
- package/components/markus-body-editor.tsx +213 -0
- package/components/markus-ir-preview.tsx +147 -6
- package/components/monitoring-overview-view.tsx +240 -0
- package/components/monitoring-panel.tsx +40 -0
- package/components/newsroom-article-editor.tsx +136 -11
- package/components/newsroom-board-view.tsx +6 -7
- package/components/newsroom-curation-bootstrap.tsx +45 -0
- package/components/newsroom-ops-shell.tsx +7 -3
- package/components/newsroom-organizer.css +74 -61
- package/components/newsroom-references-view.tsx +14 -14
- package/components/newsroom-theme-fonts/broadsheet-green.css +10 -0
- package/components/newsroom-theme-fonts/broadsheet-green.tsx +6 -0
- package/components/newsroom-theme-fonts/newspaper-gothic.css +9 -0
- package/components/newsroom-theme-fonts/newspaper-gothic.tsx +6 -0
- package/components/newsroom-theme-fonts/night-desk.css +10 -0
- package/components/newsroom-theme-fonts/night-desk.tsx +6 -0
- package/components/newsroom-theme-fonts/refined-newsprint.css +11 -0
- package/components/newsroom-theme-fonts/refined-newsprint.tsx +6 -0
- package/components/newsroom-theme.css +39 -0
- package/components/newsroom-theme.tsx +33 -0
- package/components/publication-cyclotron-panel.tsx +10 -10
- package/components/reference-add-entry.tsx +75 -0
- package/components/reference-curation-controls.tsx +30 -21
- package/components/reference-cyclotron-priming.tsx +4 -4
- package/components/reference-cyclotron-suggestion.tsx +1 -1
- package/components/reference-priming.tsx +14 -10
- package/components/reference-section-suggestion.tsx +1 -1
- package/components/topic-steering-workspace.tsx +27 -2
- package/components/use-editorial-checks.ts +133 -0
- package/lib/category-repository.ts +11 -9
- package/lib/curation-routes.ts +342 -0
- package/lib/editorial-checks-backend.ts +16 -0
- package/lib/editorial-checks-client.ts +194 -0
- package/lib/editorial-checks-demo.ts +147 -0
- package/lib/editorial-checks-settings.ts +27 -0
- package/lib/editorial-checks.ts +171 -0
- package/lib/editorial-preview.ts +228 -0
- package/lib/monitoring-client.ts +29 -0
- package/lib/monitoring-overview-fixture.ts +51 -0
- package/lib/monitoring-overview.ts +144 -0
- package/lib/newsroom-board.ts +27 -2
- package/lib/relevance-status.ts +2 -2
- package/lib/site-brand-tenant.ts +11 -6
- package/lib/site-brand.ts +23 -3
- package/package.json +46 -3
- package/routes.manifest.json +34 -1
- 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 +0 -93
- 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 +0 -93
- package/components/newsroom-newsprint-board.css +0 -7
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
/** Mirrors DIRECTORY_HTML_PATTERN and DIRECTORY_SLASH_PATTERN in standard/config.py. */
|
|
4
|
+
const DIRECTORY_HTML_PATTERN = "^/(?!(?:.*/)?index\\.html$)(.+)\\.html$";
|
|
5
|
+
const DIRECTORY_SLASH_PATTERN = "^/((?:[^/]+/)*[^/.]+)$";
|
|
6
|
+
const START_MARKER = "/*PAPYRUS_PATTERN_RULES*/";
|
|
7
|
+
const END_MARKER = "/*END_PAPYRUS_PATTERN_RULES*/";
|
|
8
|
+
/**
|
|
9
|
+
* The pattern rules a site declares in projection.json: its `redirects.patterns`, plus, for
|
|
10
|
+
* `urls.style: "directory"`, `/x.html` to `/x/`. Mirrors `StandardSettings.edge_rules` in Python.
|
|
11
|
+
*/
|
|
12
|
+
export function edgeRulesFromSettings(settings) {
|
|
13
|
+
const rules = (settings.redirects?.patterns ?? []).map((rule) => ({
|
|
14
|
+
p: rule.pattern,
|
|
15
|
+
t: rule.to,
|
|
16
|
+
s: rule.status ?? 301,
|
|
17
|
+
x: rule.except ?? [],
|
|
18
|
+
}));
|
|
19
|
+
if (settings.urls?.style === "directory") {
|
|
20
|
+
// /x.html to /x/ (a directory index file such as /x/index.html is served as it is), then /x to /x/.
|
|
21
|
+
rules.push({ p: DIRECTORY_HTML_PATTERN, t: "/$1/", s: 301, x: ["/404.html"] });
|
|
22
|
+
rules.push({ p: DIRECTORY_SLASH_PATTERN, t: "/$1/", s: 301, x: ["/404"] });
|
|
23
|
+
}
|
|
24
|
+
return rules;
|
|
25
|
+
}
|
|
26
|
+
/** A standard-front-end site publishes Markdown twins unless it turns `agents.markdown` off. */
|
|
27
|
+
export function negotiatesMarkdown(settings) {
|
|
28
|
+
return settings.site !== undefined && settings.agents?.markdown !== false;
|
|
29
|
+
}
|
|
30
|
+
export function readEdgeConfig(staticDir, baseDirectory = process.cwd()) {
|
|
31
|
+
if (staticDir === undefined)
|
|
32
|
+
return { rules: [], negotiateMarkdown: false };
|
|
33
|
+
const settingsPath = resolve(baseDirectory, staticDir, "projection.json");
|
|
34
|
+
if (!existsSync(settingsPath))
|
|
35
|
+
return { rules: [], negotiateMarkdown: false };
|
|
36
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
37
|
+
return { rules: edgeRulesFromSettings(settings), negotiateMarkdown: negotiatesMarkdown(settings) };
|
|
38
|
+
}
|
|
39
|
+
export function readEdgeRules(staticDir, baseDirectory = process.cwd()) {
|
|
40
|
+
return readEdgeConfig(staticDir, baseDirectory).rules;
|
|
41
|
+
}
|
|
42
|
+
const NEGOTIATE_START = "/*PAPYRUS_NEGOTIATE_MARKDOWN*/";
|
|
43
|
+
const NEGOTIATE_END = "/*END_PAPYRUS_NEGOTIATE_MARKDOWN*/";
|
|
44
|
+
/** The CloudFront Function source with the site's pattern rules and Markdown negotiation flag spliced in. */
|
|
45
|
+
export function compileRedirectFunctionSource(source, rules, options = {}) {
|
|
46
|
+
const start = source.indexOf(START_MARKER);
|
|
47
|
+
const end = source.indexOf(END_MARKER);
|
|
48
|
+
if (start === -1 || end === -1 || end < start) {
|
|
49
|
+
throw new Error("redirect function source has no pattern-rule markers");
|
|
50
|
+
}
|
|
51
|
+
let compiled = rules.length === 0 ? source : source.slice(0, start + START_MARKER.length) + JSON.stringify(rules) + source.slice(end);
|
|
52
|
+
if (options.negotiateMarkdown === true) {
|
|
53
|
+
const from = compiled.indexOf(NEGOTIATE_START);
|
|
54
|
+
const to = compiled.indexOf(NEGOTIATE_END);
|
|
55
|
+
if (from === -1 || to === -1 || to < from) {
|
|
56
|
+
throw new Error("redirect function source has no markdown negotiation markers");
|
|
57
|
+
}
|
|
58
|
+
compiled = compiled.slice(0, from + NEGOTIATE_START.length) + "true" + compiled.slice(to);
|
|
59
|
+
}
|
|
60
|
+
return compiled;
|
|
61
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
/** One pattern redirect as the edge function reads it: regex, target with $1..$9, status, excluded paths. */
|
|
5
|
+
export type EdgeRedirectRule = { p: string; t: string; s: number; x: string[] };
|
|
6
|
+
|
|
7
|
+
/** Mirrors DIRECTORY_HTML_PATTERN and DIRECTORY_SLASH_PATTERN in standard/config.py. */
|
|
8
|
+
const DIRECTORY_HTML_PATTERN = "^/(?!(?:.*/)?index\\.html$)(.+)\\.html$";
|
|
9
|
+
const DIRECTORY_SLASH_PATTERN = "^/((?:[^/]+/)*[^/.]+)$";
|
|
10
|
+
|
|
11
|
+
const START_MARKER = "/*PAPYRUS_PATTERN_RULES*/";
|
|
12
|
+
const END_MARKER = "/*END_PAPYRUS_PATTERN_RULES*/";
|
|
13
|
+
|
|
14
|
+
type ProjectionRedirects = { patterns?: { pattern: string; to: string; status?: number; except?: string[] }[] };
|
|
15
|
+
type ProjectionSettings = { site?: unknown; urls?: { style?: string }; redirects?: ProjectionRedirects; agents?: { markdown?: boolean } };
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The pattern rules a site declares in projection.json: its `redirects.patterns`, plus, for
|
|
19
|
+
* `urls.style: "directory"`, `/x.html` to `/x/`. Mirrors `StandardSettings.edge_rules` in Python.
|
|
20
|
+
*/
|
|
21
|
+
export function edgeRulesFromSettings(settings: ProjectionSettings): EdgeRedirectRule[] {
|
|
22
|
+
const rules = (settings.redirects?.patterns ?? []).map((rule) => ({
|
|
23
|
+
p: rule.pattern,
|
|
24
|
+
t: rule.to,
|
|
25
|
+
s: rule.status ?? 301,
|
|
26
|
+
x: rule.except ?? [],
|
|
27
|
+
}));
|
|
28
|
+
if (settings.urls?.style === "directory") {
|
|
29
|
+
// /x.html to /x/ (a directory index file such as /x/index.html is served as it is), then /x to /x/.
|
|
30
|
+
rules.push({ p: DIRECTORY_HTML_PATTERN, t: "/$1/", s: 301, x: ["/404.html"] });
|
|
31
|
+
rules.push({ p: DIRECTORY_SLASH_PATTERN, t: "/$1/", s: 301, x: ["/404"] });
|
|
32
|
+
}
|
|
33
|
+
return rules;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A standard-front-end site publishes Markdown twins unless it turns `agents.markdown` off. */
|
|
37
|
+
export function negotiatesMarkdown(settings: ProjectionSettings): boolean {
|
|
38
|
+
return settings.site !== undefined && settings.agents?.markdown !== false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type EdgeConfig = { rules: EdgeRedirectRule[]; negotiateMarkdown: boolean };
|
|
42
|
+
|
|
43
|
+
export function readEdgeConfig(staticDir: string | undefined, baseDirectory: string = process.cwd()): EdgeConfig {
|
|
44
|
+
if (staticDir === undefined) return { rules: [], negotiateMarkdown: false };
|
|
45
|
+
const settingsPath = resolve(baseDirectory, staticDir, "projection.json");
|
|
46
|
+
if (!existsSync(settingsPath)) return { rules: [], negotiateMarkdown: false };
|
|
47
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf8")) as ProjectionSettings;
|
|
48
|
+
return { rules: edgeRulesFromSettings(settings), negotiateMarkdown: negotiatesMarkdown(settings) };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function readEdgeRules(staticDir: string | undefined, baseDirectory: string = process.cwd()): EdgeRedirectRule[] {
|
|
52
|
+
return readEdgeConfig(staticDir, baseDirectory).rules;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const NEGOTIATE_START = "/*PAPYRUS_NEGOTIATE_MARKDOWN*/";
|
|
56
|
+
const NEGOTIATE_END = "/*END_PAPYRUS_NEGOTIATE_MARKDOWN*/";
|
|
57
|
+
|
|
58
|
+
/** The CloudFront Function source with the site's pattern rules and Markdown negotiation flag spliced in. */
|
|
59
|
+
export function compileRedirectFunctionSource(
|
|
60
|
+
source: string,
|
|
61
|
+
rules: EdgeRedirectRule[],
|
|
62
|
+
options: { negotiateMarkdown?: boolean } = {},
|
|
63
|
+
): string {
|
|
64
|
+
const start = source.indexOf(START_MARKER);
|
|
65
|
+
const end = source.indexOf(END_MARKER);
|
|
66
|
+
if (start === -1 || end === -1 || end < start) {
|
|
67
|
+
throw new Error("redirect function source has no pattern-rule markers");
|
|
68
|
+
}
|
|
69
|
+
let compiled = rules.length === 0 ? source : source.slice(0, start + START_MARKER.length) + JSON.stringify(rules) + source.slice(end);
|
|
70
|
+
if (options.negotiateMarkdown === true) {
|
|
71
|
+
const from = compiled.indexOf(NEGOTIATE_START);
|
|
72
|
+
const to = compiled.indexOf(NEGOTIATE_END);
|
|
73
|
+
if (from === -1 || to === -1 || to < from) {
|
|
74
|
+
throw new Error("redirect function source has no markdown negotiation markers");
|
|
75
|
+
}
|
|
76
|
+
compiled = compiled.slice(0, from + NEGOTIATE_START.length) + "true" + compiled.slice(to);
|
|
77
|
+
}
|
|
78
|
+
return compiled;
|
|
79
|
+
}
|
|
@@ -7,11 +7,13 @@ import { SqsDestination } from "aws-cdk-lib/aws-lambda-destinations";
|
|
|
7
7
|
import * as route53 from "aws-cdk-lib/aws-route53";
|
|
8
8
|
import * as s3 from "aws-cdk-lib/aws-s3";
|
|
9
9
|
import * as sqs from "aws-cdk-lib/aws-sqs";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
10
11
|
import { dirname, resolve } from "node:path";
|
|
11
12
|
import { fileURLToPath } from "node:url";
|
|
12
13
|
import { assertValidPublicationConfig } from "../site-backend-config.js";
|
|
13
14
|
import { addRenditionsFunction } from "./renditions-function.js";
|
|
14
15
|
import { RENDITIONS_FUNCTION_NAME_ENV } from "./renditions-settings.js";
|
|
16
|
+
import { compileRedirectFunctionSource, readEdgeConfig } from "./redirect-rules.js";
|
|
15
17
|
import { addInvalidationPathAlarm } from "./invalidation-alarm.js";
|
|
16
18
|
import { distributionIdFromArn, s3ProjectionEnvironment, s3ProjectionPolicyStatements } from "./s3-projection-settings.js";
|
|
17
19
|
const redirectFunctionSourcePath = resolve(dirname(fileURLToPath(import.meta.url)), "redirect-and-index.js");
|
|
@@ -70,7 +72,9 @@ export function addS3ProjectionPublication(scope, props, media) {
|
|
|
70
72
|
const keyValueStore = new cloudfront.KeyValueStore(scope, "RedirectsKeyValueStore");
|
|
71
73
|
const redirectFunction = new cloudfront.Function(scope, "RedirectAndIndexFunction", {
|
|
72
74
|
runtime: cloudfront.FunctionRuntime.JS_2_0,
|
|
73
|
-
code: cloudfront.FunctionCode.
|
|
75
|
+
code: cloudfront.FunctionCode.fromInline(compileRedirectFunctionSource(readFileSync(redirectFunctionSourcePath, "utf8"), readEdgeConfig(props.staticDir).rules, {
|
|
76
|
+
negotiateMarkdown: readEdgeConfig(props.staticDir).negotiateMarkdown,
|
|
77
|
+
})),
|
|
74
78
|
keyValueStore,
|
|
75
79
|
});
|
|
76
80
|
const deadLetterQueue = new sqs.Queue(scope, "ProjectionDeadLetterQueue", {
|
|
@@ -10,11 +10,13 @@ import * as route53 from "aws-cdk-lib/aws-route53";
|
|
|
10
10
|
import * as s3 from "aws-cdk-lib/aws-s3";
|
|
11
11
|
import * as sqs from "aws-cdk-lib/aws-sqs";
|
|
12
12
|
import type { Construct } from "constructs";
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
13
14
|
import { dirname, resolve } from "node:path";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
15
16
|
import { assertValidPublicationConfig, type PapyrusPublicationConfig } from "../site-backend-config";
|
|
16
17
|
import { addRenditionsFunction, type RenditionsFunction } from "./renditions-function";
|
|
17
18
|
import { RENDITIONS_FUNCTION_NAME_ENV, type RenditionsPlan } from "./renditions-settings";
|
|
19
|
+
import { compileRedirectFunctionSource, readEdgeConfig } from "./redirect-rules";
|
|
18
20
|
import { addInvalidationPathAlarm } from "./invalidation-alarm";
|
|
19
21
|
import { distributionIdFromArn, s3ProjectionEnvironment, s3ProjectionPolicyStatements } from "./s3-projection-settings";
|
|
20
22
|
|
|
@@ -112,7 +114,11 @@ export function addS3ProjectionPublication(
|
|
|
112
114
|
const keyValueStore = new cloudfront.KeyValueStore(scope, "RedirectsKeyValueStore");
|
|
113
115
|
const redirectFunction = new cloudfront.Function(scope, "RedirectAndIndexFunction", {
|
|
114
116
|
runtime: cloudfront.FunctionRuntime.JS_2_0,
|
|
115
|
-
code: cloudfront.FunctionCode.
|
|
117
|
+
code: cloudfront.FunctionCode.fromInline(
|
|
118
|
+
compileRedirectFunctionSource(readFileSync(redirectFunctionSourcePath, "utf8"), readEdgeConfig(props.staticDir).rules, {
|
|
119
|
+
negotiateMarkdown: readEdgeConfig(props.staticDir).negotiateMarkdown,
|
|
120
|
+
}),
|
|
121
|
+
),
|
|
116
122
|
keyValueStore,
|
|
117
123
|
});
|
|
118
124
|
const deadLetterQueue = new sqs.Queue(scope, "ProjectionDeadLetterQueue", {
|
package/amplify/site-backend.js
CHANGED
|
@@ -20,9 +20,14 @@ import { addInvalidationPathAlarm } from "./publication/invalidation-alarm.js";
|
|
|
20
20
|
import { applyCognitoDomainPrefix, authConfigFromEnv, defineSiteAuth, siteAuthRedirectUrls } from "./auth/resource.js";
|
|
21
21
|
import { PROSPECTUS_SIGN_IN_CALLBACK_PATH, addProspectusGoogleOnlyClient } from "./auth/prospectus-google-client.js";
|
|
22
22
|
import { defineSiteData } from "./data/resource.js";
|
|
23
|
+
import { editorialChecks } from "./functions/editorial-checks/resource.js";
|
|
24
|
+
import { monitoringView } from "./functions/monitoring-view/resource.js";
|
|
25
|
+
import { wireMonitoringView } from "./functions/monitoring-view/wire.js";
|
|
26
|
+
import { wireEditorialChecks } from "./functions/editorial-checks/wire.js";
|
|
23
27
|
import { resolvePlugins } from "./plugins/registry.js";
|
|
24
28
|
import { wirePlugins } from "./plugins/wire.js";
|
|
25
29
|
import { assignmentAction } from "./functions/assignment-action/resource.js";
|
|
30
|
+
import { scopeAssignmentDataAccess } from "./functions/assignment-action/data-access.js";
|
|
26
31
|
import { categoryAction } from "./functions/category-action/resource.js";
|
|
27
32
|
import { ConsoleChatResponderStack } from "./functions/console-chat-responder/resource.js";
|
|
28
33
|
import { contentActions, setSiteFilesForContentActionsBundle } from "./functions/content-actions/resource.js";
|
|
@@ -88,9 +93,11 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
88
93
|
auth: defineSiteAuth(authConfig),
|
|
89
94
|
categoryAction,
|
|
90
95
|
contentActions,
|
|
96
|
+
editorialChecks,
|
|
91
97
|
data: defineSiteData(pluginDefinitions),
|
|
92
98
|
knowledgeQuery,
|
|
93
99
|
manageUserRole,
|
|
100
|
+
monitoringView,
|
|
94
101
|
modelAttachmentUpload,
|
|
95
102
|
newsroomSummary,
|
|
96
103
|
procedureAction,
|
|
@@ -304,10 +311,19 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
304
311
|
}
|
|
305
312
|
grantCorporaRead(backend.knowledgeQuery.resources.lambda);
|
|
306
313
|
grantNewsroomReadWrite(backend.assignmentAction.resources.lambda);
|
|
314
|
+
scopeAssignmentDataAccess(backend.assignmentAction.resources.lambda, backend.data.resources.graphqlApi.arn);
|
|
307
315
|
grantNewsroomReadWrite(backend.categoryAction.resources.lambda);
|
|
308
316
|
grantNewsroomReadWrite(backend.newsroomSummary.resources.lambda);
|
|
309
317
|
const contentActionsLambda = backend.contentActions.resources.lambda;
|
|
310
318
|
grantNewsroomReadWriteDelete(contentActionsLambda);
|
|
319
|
+
if ((activeBrand?.corpusKey ?? "").trim() !== "") {
|
|
320
|
+
// A deploy creates the publication's canonical corpus from the brand when the backend has none.
|
|
321
|
+
contentActionsLambda.addEnvironment("PAPYRUS_PUBLICATION_CORPUS_KEY", (activeBrand?.corpusKey ?? "").trim());
|
|
322
|
+
contentActionsLambda.addEnvironment("PAPYRUS_PUBLICATION_NAME", (activeBrand?.publicationName ?? activeBrand?.corpusKey ?? "").trim());
|
|
323
|
+
}
|
|
324
|
+
// AppSync validates the token; pending intake additionally binds its actor
|
|
325
|
+
// and publication to this deployment's pool and existing API endpoint.
|
|
326
|
+
contentActionsLambda.addEnvironment("PAPYRUS_USER_POOL_ISSUER", `https://cognito-idp.${Stack.of(contentActionsLambda).region}.amazonaws.com/${backend.auth.resources.userPool.userPoolId}`);
|
|
311
327
|
contentActionsLambda.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", backend.data.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl);
|
|
312
328
|
contentActionsLambda.addToRolePolicy(new PolicyStatement({
|
|
313
329
|
actions: ["appsync:GraphQL"],
|
|
@@ -584,6 +600,8 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
584
600
|
: null,
|
|
585
601
|
},
|
|
586
602
|
});
|
|
603
|
+
wireEditorialChecks(backend);
|
|
604
|
+
wireMonitoringView(backend);
|
|
587
605
|
wirePlugins(backend, plugins, contentActionsLambda);
|
|
588
606
|
return backend;
|
|
589
607
|
}
|
package/amplify/site-backend.ts
CHANGED
|
@@ -27,9 +27,14 @@ import { addInvalidationPathAlarm } from "./publication/invalidation-alarm";
|
|
|
27
27
|
import { applyCognitoDomainPrefix, authConfigFromEnv, defineSiteAuth, siteAuthRedirectUrls } from "./auth/resource";
|
|
28
28
|
import { PROSPECTUS_SIGN_IN_CALLBACK_PATH, addProspectusGoogleOnlyClient } from "./auth/prospectus-google-client";
|
|
29
29
|
import { defineSiteData } from "./data/resource";
|
|
30
|
+
import { editorialChecks } from "./functions/editorial-checks/resource";
|
|
31
|
+
import { monitoringView } from "./functions/monitoring-view/resource";
|
|
32
|
+
import { wireMonitoringView } from "./functions/monitoring-view/wire";
|
|
33
|
+
import { wireEditorialChecks } from "./functions/editorial-checks/wire";
|
|
30
34
|
import { resolvePlugins } from "./plugins/registry";
|
|
31
35
|
import { wirePlugins } from "./plugins/wire";
|
|
32
36
|
import { assignmentAction } from "./functions/assignment-action/resource";
|
|
37
|
+
import { scopeAssignmentDataAccess } from "./functions/assignment-action/data-access";
|
|
33
38
|
import { categoryAction } from "./functions/category-action/resource";
|
|
34
39
|
import { ConsoleChatResponderStack } from "./functions/console-chat-responder/resource";
|
|
35
40
|
import { contentActions, setSiteFilesForContentActionsBundle } from "./functions/content-actions/resource";
|
|
@@ -109,9 +114,11 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
|
|
|
109
114
|
auth: defineSiteAuth(authConfig),
|
|
110
115
|
categoryAction,
|
|
111
116
|
contentActions,
|
|
117
|
+
editorialChecks,
|
|
112
118
|
data: defineSiteData(pluginDefinitions),
|
|
113
119
|
knowledgeQuery,
|
|
114
120
|
manageUserRole,
|
|
121
|
+
monitoringView,
|
|
115
122
|
modelAttachmentUpload,
|
|
116
123
|
newsroomSummary,
|
|
117
124
|
procedureAction,
|
|
@@ -387,10 +394,25 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
|
|
|
387
394
|
}
|
|
388
395
|
grantCorporaRead(backend.knowledgeQuery.resources.lambda as LambdaFunction);
|
|
389
396
|
grantNewsroomReadWrite(backend.assignmentAction.resources.lambda as LambdaFunction);
|
|
397
|
+
scopeAssignmentDataAccess(
|
|
398
|
+
backend.assignmentAction.resources.lambda as LambdaFunction,
|
|
399
|
+
backend.data.resources.graphqlApi.arn,
|
|
400
|
+
);
|
|
390
401
|
grantNewsroomReadWrite(backend.categoryAction.resources.lambda as LambdaFunction);
|
|
391
402
|
grantNewsroomReadWrite(backend.newsroomSummary.resources.lambda as LambdaFunction);
|
|
392
403
|
const contentActionsLambda = backend.contentActions.resources.lambda as LambdaFunction;
|
|
393
404
|
grantNewsroomReadWriteDelete(contentActionsLambda);
|
|
405
|
+
if ((activeBrand?.corpusKey ?? "").trim() !== "") {
|
|
406
|
+
// A deploy creates the publication's canonical corpus from the brand when the backend has none.
|
|
407
|
+
contentActionsLambda.addEnvironment("PAPYRUS_PUBLICATION_CORPUS_KEY", (activeBrand?.corpusKey ?? "").trim());
|
|
408
|
+
contentActionsLambda.addEnvironment("PAPYRUS_PUBLICATION_NAME", (activeBrand?.publicationName ?? activeBrand?.corpusKey ?? "").trim());
|
|
409
|
+
}
|
|
410
|
+
// AppSync validates the token; pending intake additionally binds its actor
|
|
411
|
+
// and publication to this deployment's pool and existing API endpoint.
|
|
412
|
+
contentActionsLambda.addEnvironment(
|
|
413
|
+
"PAPYRUS_USER_POOL_ISSUER",
|
|
414
|
+
`https://cognito-idp.${Stack.of(contentActionsLambda).region}.amazonaws.com/${backend.auth.resources.userPool.userPoolId}`,
|
|
415
|
+
);
|
|
394
416
|
contentActionsLambda.addEnvironment(
|
|
395
417
|
"PAPYRUS_GRAPHQL_ENDPOINT",
|
|
396
418
|
backend.data.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl,
|
|
@@ -721,6 +743,8 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
|
|
|
721
743
|
: null,
|
|
722
744
|
},
|
|
723
745
|
});
|
|
746
|
+
wireEditorialChecks(backend);
|
|
747
|
+
wireMonitoringView(backend);
|
|
724
748
|
wirePlugins(backend, plugins, contentActionsLambda);
|
|
725
749
|
|
|
726
750
|
return backend;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { handleCurationRoute } from "../../../../../lib/curation-routes";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const runtime = "nodejs";
|
|
6
|
+
|
|
7
|
+
export function GET(request: NextRequest) {
|
|
8
|
+
return handleCurationRoute(request, "context");
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { handleCurationRoute } from "../../../../../lib/curation-routes";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const runtime = "nodejs";
|
|
6
|
+
|
|
7
|
+
export function POST(request: NextRequest) {
|
|
8
|
+
return handleCurationRoute(request, "priming");
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { handleCurationRoute } from "../../../../../lib/curation-routes";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const runtime = "nodejs";
|
|
6
|
+
|
|
7
|
+
export function POST(request: NextRequest) {
|
|
8
|
+
return handleCurationRoute(request, "publication");
|
|
9
|
+
}
|
package/bin/papyrus-infra.mjs
CHANGED
|
@@ -19,6 +19,7 @@ const USAGE = [
|
|
|
19
19
|
" papyrus-infra synth --account-stack github-oidc [--out cdk.out] [--account A] [--region R]",
|
|
20
20
|
" papyrus-infra print-buildspec --site infra/site.json --app cms-production|cms-staging|reader",
|
|
21
21
|
" papyrus-infra deploy-edge --stack NAME --template FILE [--adoption-template FILE --adopt-resources FILE] [--parameter KEY=VALUE]... [--delete-function NAME] [--only-branch main] [--only-app-id ID] [--region R]",
|
|
22
|
+
" papyrus-infra compile-edge-function --static-dir web [--out FILE]",
|
|
22
23
|
" papyrus-infra deploy --site infra/site.json [--only-branch main] [--only-app-id ID] [--out cdk.out] [--account A] [--region R]",
|
|
23
24
|
].join("\n");
|
|
24
25
|
|
|
@@ -36,11 +37,29 @@ const resolveEnvironmentOrExit = () => {
|
|
|
36
37
|
}
|
|
37
38
|
};
|
|
38
39
|
const command = args[0];
|
|
39
|
-
if (command !== "synth" && command !== "print-buildspec" && command !== "deploy" && command !== "deploy-edge") {
|
|
40
|
+
if (command !== "synth" && command !== "print-buildspec" && command !== "deploy" && command !== "deploy-edge" && command !== "compile-edge-function") {
|
|
40
41
|
console.error(USAGE);
|
|
41
42
|
process.exit(command ? 1 : 0);
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
if (command === "compile-edge-function") {
|
|
46
|
+
// The viewer-request CloudFront Function with the site's projection.json rules compiled in: for an edge stack that is
|
|
47
|
+
// not deployed by the CMS backend, so that no site writes its own redirect function.
|
|
48
|
+
const staticDir = opt("static-dir");
|
|
49
|
+
if (!staticDir) {
|
|
50
|
+
console.error(USAGE);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const { compileRedirectFunctionSource, readEdgeConfig } = await import("../amplify/publication/redirect-rules.js");
|
|
54
|
+
const functionSource = fs.readFileSync(new URL("../amplify/publication/redirect-and-index.js", import.meta.url), "utf8");
|
|
55
|
+
const config = readEdgeConfig(staticDir);
|
|
56
|
+
const compiled = compileRedirectFunctionSource(functionSource, config.rules, { negotiateMarkdown: config.negotiateMarkdown });
|
|
57
|
+
const out = opt("out");
|
|
58
|
+
if (out) fs.writeFileSync(path.resolve(out), compiled);
|
|
59
|
+
else process.stdout.write(compiled);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
if (command === "deploy-edge") {
|
|
45
64
|
const buildAppId = process.env.AWS_APP_ID;
|
|
46
65
|
const buildBranch = process.env.AWS_BRANCH;
|