@anthusai/papyrus 1.0.0-next.17 → 1.0.0-next.18
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/resource.js +17 -4
- package/amplify/auth/resource.ts +21 -4
- package/amplify/site-backend-config.ts +6 -1
- package/amplify/site-backend.js +9 -2
- package/amplify/site-backend.ts +12 -2
- package/app/api/revalidate/route.ts +5 -4
- package/app/page.tsx +5 -2
- package/components/newspaper.tsx +2 -1
- package/components/newsroom-ops-overview.tsx +28 -11
- package/components/settings-page.tsx +2 -1
- package/infra/amplify-app-shell.js +24 -12
- package/infra/amplify-app-shell.ts +24 -12
- package/infra/build-specs.js +2 -1
- package/infra/build-specs.ts +4 -2
- package/infra/site-config.js +37 -11
- package/infra/site-config.ts +40 -12
- package/lib/blog-feature-solver.ts +459 -0
- package/lib/blog-rhythm.ts +134 -0
- package/lib/edition-routes.ts +31 -29
- package/lib/newspaper-layout.ts +3 -2
- package/lib/newsroom-nav.ts +1 -0
- package/lib/papyrus-web-locations.ts +7 -6
- package/lib/presentation-footer.ts +3 -2
- package/lib/pretext-layout.ts +7 -3
- package/lib/reader-base-path.ts +24 -0
- package/lib/site-brand.ts +2 -0
- package/lib/staging-gated-path.ts +22 -0
- package/middleware.ts +5 -16
- package/package.json +5 -1
- package/routes.manifest.json +9 -1
- package/src/sync.mjs +28 -4
package/amplify/auth/resource.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineAuth, secret } from "@aws-amplify/backend";
|
|
2
2
|
import { manageUserRole } from "../functions/manage-user-role/resource.js";
|
|
3
|
+
import { CfnUserPoolDomain } from "aws-cdk-lib/aws-cognito";
|
|
3
4
|
// Default OAuth redirect URLs for the p.apyr.us app. Other publications pass
|
|
4
5
|
// `auth.redirectUrls` to `defineSiteBackend` (or, for Papyrus's own backend,
|
|
5
6
|
// the PAPYRUS_OAUTH_REDIRECT_URLS branch env var, comma-separated).
|
|
@@ -25,15 +26,13 @@ export function authConfigFromEnv() {
|
|
|
25
26
|
return {
|
|
26
27
|
redirectUrls: resolveAuthRedirectUrls(),
|
|
27
28
|
disableGoogleOAuth: process.env.PAPYRUS_DISABLE_GOOGLE_OAUTH === "1",
|
|
28
|
-
// Stable Cognito hosted-UI domain prefix for Google OAuth redirect URIs.
|
|
29
|
-
// See docs/google-oauth-setup.md.
|
|
30
29
|
cognitoDomainPrefix: (process.env.PAPYRUS_COGNITO_DOMAIN_PREFIX ?? "").trim(),
|
|
30
|
+
applyCognitoDomainPrefix: process.env.PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX === "true",
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
export function defineSiteAuth(config) {
|
|
34
34
|
const authRedirectUrls = config.redirectUrls ?? DEFAULT_AUTH_REDIRECT_URLS;
|
|
35
35
|
const disableGoogleOAuth = config.disableGoogleOAuth === true;
|
|
36
|
-
const cognitoDomainPrefix = (config.cognitoDomainPrefix ?? "").trim();
|
|
37
36
|
return defineAuth({
|
|
38
37
|
loginWith: {
|
|
39
38
|
email: true,
|
|
@@ -49,7 +48,6 @@ export function defineSiteAuth(config) {
|
|
|
49
48
|
scopes: ["EMAIL", "PROFILE", "OPENID"],
|
|
50
49
|
callbackUrls: authRedirectUrls,
|
|
51
50
|
logoutUrls: authRedirectUrls,
|
|
52
|
-
...(cognitoDomainPrefix ? { domainPrefix: cognitoDomainPrefix } : {}),
|
|
53
51
|
},
|
|
54
52
|
}),
|
|
55
53
|
},
|
|
@@ -59,3 +57,18 @@ export function defineSiteAuth(config) {
|
|
|
59
57
|
],
|
|
60
58
|
});
|
|
61
59
|
}
|
|
60
|
+
// Amplify Gen2's auth factory overwrites `externalProviders.domainPrefix` with a
|
|
61
|
+
// hash of the backend id, so the prefix cannot be passed through `defineAuth`.
|
|
62
|
+
// This sets the user pool domain on the synthesized resource instead. Cognito
|
|
63
|
+
// cannot rename a pool domain in place: CloudFormation replaces the domain,
|
|
64
|
+
// which changes the hosted-UI URL and the Google redirect URI. Callers must
|
|
65
|
+
// only use it when the site opted in (see docs/google-oauth-setup.md).
|
|
66
|
+
export function applyCognitoDomainPrefix(authStack, cognitoDomainPrefix) {
|
|
67
|
+
const domainResources = authStack.node
|
|
68
|
+
.findAll()
|
|
69
|
+
.filter((construct) => construct instanceof CfnUserPoolDomain);
|
|
70
|
+
if (domainResources.length !== 1) {
|
|
71
|
+
throw new Error(`Expected exactly one Cognito user pool domain in the auth stack to apply cognitoDomainPrefix, found ${domainResources.length}`);
|
|
72
|
+
}
|
|
73
|
+
domainResources[0].domain = cognitoDomainPrefix;
|
|
74
|
+
}
|
package/amplify/auth/resource.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { defineAuth, secret } from "@aws-amplify/backend";
|
|
2
2
|
import { manageUserRole } from "../functions/manage-user-role/resource";
|
|
3
|
+
import { Stack } from "aws-cdk-lib";
|
|
4
|
+
import { CfnUserPoolDomain } from "aws-cdk-lib/aws-cognito";
|
|
3
5
|
import type { PapyrusAuthConfig } from "../site-backend-config";
|
|
4
6
|
|
|
5
7
|
// Default OAuth redirect URLs for the p.apyr.us app. Other publications pass
|
|
@@ -28,16 +30,14 @@ export function authConfigFromEnv(): PapyrusAuthConfig {
|
|
|
28
30
|
return {
|
|
29
31
|
redirectUrls: resolveAuthRedirectUrls(),
|
|
30
32
|
disableGoogleOAuth: process.env.PAPYRUS_DISABLE_GOOGLE_OAUTH === "1",
|
|
31
|
-
// Stable Cognito hosted-UI domain prefix for Google OAuth redirect URIs.
|
|
32
|
-
// See docs/google-oauth-setup.md.
|
|
33
33
|
cognitoDomainPrefix: (process.env.PAPYRUS_COGNITO_DOMAIN_PREFIX ?? "").trim(),
|
|
34
|
+
applyCognitoDomainPrefix: process.env.PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX === "true",
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export function defineSiteAuth(config: PapyrusAuthConfig) {
|
|
38
39
|
const authRedirectUrls = config.redirectUrls ?? DEFAULT_AUTH_REDIRECT_URLS;
|
|
39
40
|
const disableGoogleOAuth = config.disableGoogleOAuth === true;
|
|
40
|
-
const cognitoDomainPrefix = (config.cognitoDomainPrefix ?? "").trim();
|
|
41
41
|
return defineAuth({
|
|
42
42
|
loginWith: {
|
|
43
43
|
email: true,
|
|
@@ -53,7 +53,6 @@ export function defineSiteAuth(config: PapyrusAuthConfig) {
|
|
|
53
53
|
scopes: ["EMAIL", "PROFILE", "OPENID"],
|
|
54
54
|
callbackUrls: authRedirectUrls,
|
|
55
55
|
logoutUrls: authRedirectUrls,
|
|
56
|
-
...(cognitoDomainPrefix ? { domainPrefix: cognitoDomainPrefix } : {}),
|
|
57
56
|
},
|
|
58
57
|
}),
|
|
59
58
|
},
|
|
@@ -63,3 +62,21 @@ export function defineSiteAuth(config: PapyrusAuthConfig) {
|
|
|
63
62
|
],
|
|
64
63
|
});
|
|
65
64
|
}
|
|
65
|
+
|
|
66
|
+
// Amplify Gen2's auth factory overwrites `externalProviders.domainPrefix` with a
|
|
67
|
+
// hash of the backend id, so the prefix cannot be passed through `defineAuth`.
|
|
68
|
+
// This sets the user pool domain on the synthesized resource instead. Cognito
|
|
69
|
+
// cannot rename a pool domain in place: CloudFormation replaces the domain,
|
|
70
|
+
// which changes the hosted-UI URL and the Google redirect URI. Callers must
|
|
71
|
+
// only use it when the site opted in (see docs/google-oauth-setup.md).
|
|
72
|
+
export function applyCognitoDomainPrefix(authStack: Stack, cognitoDomainPrefix: string): void {
|
|
73
|
+
const domainResources = authStack.node
|
|
74
|
+
.findAll()
|
|
75
|
+
.filter((construct): construct is CfnUserPoolDomain => construct instanceof CfnUserPoolDomain);
|
|
76
|
+
if (domainResources.length !== 1) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Expected exactly one Cognito user pool domain in the auth stack to apply cognitoDomainPrefix, found ${domainResources.length}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
domainResources[0].domain = cognitoDomainPrefix;
|
|
82
|
+
}
|
|
@@ -7,8 +7,13 @@ export type PapyrusAuthConfig = {
|
|
|
7
7
|
/** OAuth callback/logout URLs for Google sign-in (ignored when Google is disabled). */
|
|
8
8
|
redirectUrls?: string[];
|
|
9
9
|
disableGoogleOAuth?: boolean;
|
|
10
|
-
/**
|
|
10
|
+
/** Desired Cognito hosted-UI domain prefix. Only applied when `applyCognitoDomainPrefix` is true. */
|
|
11
11
|
cognitoDomainPrefix?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Opt in to the prefix. Off by default because changing a live pool's
|
|
14
|
+
* domain replaces it and changes the Google redirect URI.
|
|
15
|
+
*/
|
|
16
|
+
applyCognitoDomainPrefix?: boolean;
|
|
12
17
|
};
|
|
13
18
|
|
|
14
19
|
export type PapyrusSiteBackendConfig = {
|
package/amplify/site-backend.js
CHANGED
|
@@ -13,7 +13,7 @@ import { CfnIndex, CfnVectorBucket, CfnVectorBucketPolicy } from "aws-cdk-lib/aw
|
|
|
13
13
|
import { dirname, resolve } from "node:path";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { rebuildTriggerSettings } from "./rebuild-trigger-settings.js";
|
|
16
|
-
import { authConfigFromEnv, defineSiteAuth } from "./auth/resource.js";
|
|
16
|
+
import { applyCognitoDomainPrefix, authConfigFromEnv, defineSiteAuth } from "./auth/resource.js";
|
|
17
17
|
import { data } from "./data/resource.js";
|
|
18
18
|
import { assignmentAction } from "./functions/assignment-action/resource.js";
|
|
19
19
|
import { categoryAction } from "./functions/category-action/resource.js";
|
|
@@ -76,9 +76,10 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
76
76
|
const enableInboundEmail = site.features?.inboundEmail ?? readEnvFlag("PAPYRUS_ENABLE_INBOUND_EMAIL", isAmplifyProductionPipeline);
|
|
77
77
|
const enableSlackAgent = site.features?.slack ?? readEnvFlag("PAPYRUS_ENABLE_SLACK", false);
|
|
78
78
|
const enableStorageBackups = site.features?.storageBackups ?? readEnvFlag("PAPYRUS_ENABLE_STORAGE_BACKUPS", isAmplifyProductionPipeline);
|
|
79
|
+
const authConfig = site.auth ?? authConfigFromEnv();
|
|
79
80
|
const backend = defineBackend({
|
|
80
81
|
assignmentAction,
|
|
81
|
-
auth: defineSiteAuth(
|
|
82
|
+
auth: defineSiteAuth(authConfig),
|
|
82
83
|
categoryAction,
|
|
83
84
|
contentActions,
|
|
84
85
|
data,
|
|
@@ -94,6 +95,12 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
94
95
|
slackDelivery,
|
|
95
96
|
storage,
|
|
96
97
|
});
|
|
98
|
+
const requestedCognitoDomainPrefix = (authConfig.cognitoDomainPrefix ?? "").trim();
|
|
99
|
+
if (authConfig.applyCognitoDomainPrefix === true
|
|
100
|
+
&& requestedCognitoDomainPrefix !== ""
|
|
101
|
+
&& authConfig.disableGoogleOAuth !== true) {
|
|
102
|
+
applyCognitoDomainPrefix(backend.auth.stack, requestedCognitoDomainPrefix);
|
|
103
|
+
}
|
|
97
104
|
const amplifyBackendDir = dirname(fileURLToPath(import.meta.url));
|
|
98
105
|
// Repo root in a Papyrus checkout; package root when installed from npm.
|
|
99
106
|
const projectRoot = resolve(amplifyBackendDir, "..");
|
package/amplify/site-backend.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { CfnIndex, CfnVectorBucket, CfnVectorBucketPolicy } from "aws-cdk-lib/aw
|
|
|
14
14
|
import { dirname, resolve } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import { rebuildTriggerSettings } from "./rebuild-trigger-settings";
|
|
17
|
-
import { authConfigFromEnv, defineSiteAuth } from "./auth/resource";
|
|
17
|
+
import { applyCognitoDomainPrefix, authConfigFromEnv, defineSiteAuth } from "./auth/resource";
|
|
18
18
|
import { data } from "./data/resource";
|
|
19
19
|
import { assignmentAction } from "./functions/assignment-action/resource";
|
|
20
20
|
import { categoryAction } from "./functions/category-action/resource";
|
|
@@ -87,9 +87,10 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
|
|
|
87
87
|
const enableSlackAgent = site.features?.slack ?? readEnvFlag("PAPYRUS_ENABLE_SLACK", false);
|
|
88
88
|
const enableStorageBackups = site.features?.storageBackups ?? readEnvFlag("PAPYRUS_ENABLE_STORAGE_BACKUPS", isAmplifyProductionPipeline);
|
|
89
89
|
|
|
90
|
+
const authConfig = site.auth ?? authConfigFromEnv();
|
|
90
91
|
const backend = defineBackend({
|
|
91
92
|
assignmentAction,
|
|
92
|
-
auth: defineSiteAuth(
|
|
93
|
+
auth: defineSiteAuth(authConfig),
|
|
93
94
|
categoryAction,
|
|
94
95
|
contentActions,
|
|
95
96
|
data,
|
|
@@ -106,6 +107,15 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
|
|
|
106
107
|
storage,
|
|
107
108
|
});
|
|
108
109
|
|
|
110
|
+
const requestedCognitoDomainPrefix = (authConfig.cognitoDomainPrefix ?? "").trim();
|
|
111
|
+
if (
|
|
112
|
+
authConfig.applyCognitoDomainPrefix === true
|
|
113
|
+
&& requestedCognitoDomainPrefix !== ""
|
|
114
|
+
&& authConfig.disableGoogleOAuth !== true
|
|
115
|
+
) {
|
|
116
|
+
applyCognitoDomainPrefix(backend.auth.stack, requestedCognitoDomainPrefix);
|
|
117
|
+
}
|
|
118
|
+
|
|
109
119
|
const amplifyBackendDir = dirname(fileURLToPath(import.meta.url));
|
|
110
120
|
// Repo root in a Papyrus checkout; package root when installed from npm.
|
|
111
121
|
const projectRoot = resolve(amplifyBackendDir, "..");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toPublicReaderPath } from "../../../lib/reader-base-path";
|
|
1
2
|
import { revalidatePath, revalidateTag } from "next/cache";
|
|
2
3
|
import { NextResponse } from "next/server";
|
|
3
4
|
import {
|
|
@@ -44,8 +45,8 @@ export async function POST(request: Request) {
|
|
|
44
45
|
if (!normalized) continue;
|
|
45
46
|
revalidateTag(articleCacheTag(normalized));
|
|
46
47
|
revalidatedTags.add(articleCacheTag(normalized));
|
|
47
|
-
revalidatePath(`/articles/${encodeURIComponent(normalized)}`);
|
|
48
|
-
revalidatedPaths.add(`/articles/${encodeURIComponent(normalized)}`);
|
|
48
|
+
revalidatePath(toPublicReaderPath(`/articles/${encodeURIComponent(normalized)}`));
|
|
49
|
+
revalidatedPaths.add(toPublicReaderPath(`/articles/${encodeURIComponent(normalized)}`));
|
|
49
50
|
if (editionDate) {
|
|
50
51
|
revalidateTag(editionItemCacheTag(editionDate, normalized));
|
|
51
52
|
revalidatedTags.add(editionItemCacheTag(editionDate, normalized));
|
|
@@ -76,8 +77,8 @@ export async function POST(request: Request) {
|
|
|
76
77
|
revalidatedTags.add(ARCHIVE_CACHE_TAG);
|
|
77
78
|
revalidateTag(ARTICLES_CACHE_TAG);
|
|
78
79
|
revalidatedTags.add(ARTICLES_CACHE_TAG);
|
|
79
|
-
revalidatePath("/archive");
|
|
80
|
-
revalidatedPaths.add("/archive");
|
|
80
|
+
revalidatePath(toPublicReaderPath("/archive"));
|
|
81
|
+
revalidatedPaths.add(toPublicReaderPath("/archive"));
|
|
81
82
|
|
|
82
83
|
for (const path of body.paths ?? []) {
|
|
83
84
|
const normalized = path.trim();
|
package/app/page.tsx
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import type { PublicationItem } from "../lib/publication-items";
|
|
17
17
|
import { SITE_BRAND, rootRoute } from "../lib/site-brand";
|
|
18
18
|
import { notFound, redirect } from "next/navigation";
|
|
19
|
+
import { getReaderBasePath } from "../lib/reader-base-path";
|
|
19
20
|
import { buildRootRedirectTarget } from "../lib/root-route-redirect";
|
|
20
21
|
import NewsDeskRootPage, { type NewsDeskRootSearchParams } from "./newsroom/page";
|
|
21
22
|
|
|
@@ -38,11 +39,13 @@ export default async function Home({ searchParams }: HomePageProps) {
|
|
|
38
39
|
// co-hosts reader + CMS uses the default "reader" (render the publication
|
|
39
40
|
// home page here). A CMS-only site points the root at the newsroom or
|
|
40
41
|
// anywhere else. Preserve the scenario and OAuth-callback paths.
|
|
41
|
-
|
|
42
|
+
const publicationOwnsRoot = getReaderBasePath() !== "";
|
|
43
|
+
|
|
44
|
+
if (!publicationOwnsRoot && rootRoute.kind === "newsroom" && !scenarioId) {
|
|
42
45
|
return <NewsDeskRootPage searchParams={searchParams} />;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
if (rootRoute.kind === "redirect") {
|
|
48
|
+
if (!publicationOwnsRoot && rootRoute.kind === "redirect") {
|
|
46
49
|
redirect(buildRootRedirectTarget(rootRoute.destination, resolvedSearchParams));
|
|
47
50
|
}
|
|
48
51
|
|
package/components/newspaper.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import { ARCHIVE_PREVIEW_HEIGHT, ARCHIVE_PREVIEW_WIDTH } from "../lib/archive-ty
|
|
|
12
12
|
import type { EditionContent, NewsDeskAppendix, NewsDeskCategoryTreeNode } from "../lib/content-types";
|
|
13
13
|
import { shouldBypassImageOptimization } from "../lib/image-url";
|
|
14
14
|
import type { PublicationItem } from "../lib/publication-items";
|
|
15
|
+
import { toPublicReaderPath } from "../lib/reader-base-path";
|
|
15
16
|
import { SITE_BRAND } from "../lib/site-brand";
|
|
16
17
|
import { resolveThemedImageSrc, type ResolvedTheme } from "../lib/themed-image";
|
|
17
18
|
import { loadEditorCategoryTreeState } from "./news-desk-taxonomy-client";
|
|
@@ -1877,7 +1878,7 @@ function getPageHref(pageNumber: number, editionBasePath?: string): string {
|
|
|
1877
1878
|
}
|
|
1878
1879
|
|
|
1879
1880
|
function getArticleHref(articleSlug: string, editionBasePath?: string): string {
|
|
1880
|
-
return editionBasePath ? `${normalizePath(editionBasePath)}/${encodeURIComponent(articleSlug)}` : `/articles/${articleSlug}
|
|
1881
|
+
return editionBasePath ? `${normalizePath(editionBasePath)}/${encodeURIComponent(articleSlug)}` : toPublicReaderPath(`/articles/${articleSlug}`);
|
|
1881
1882
|
}
|
|
1882
1883
|
|
|
1883
1884
|
function getItemAnchorHref(articleSlug: string, pageNumber: number, editionBasePath?: string): string {
|
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
import {
|
|
4
4
|
BookOpenIcon,
|
|
5
5
|
ClipboardListIcon,
|
|
6
|
+
FileTextIcon,
|
|
6
7
|
LayersIcon,
|
|
8
|
+
LightbulbIcon,
|
|
7
9
|
MessageSquareIcon,
|
|
10
|
+
NetworkIcon,
|
|
11
|
+
SearchIcon,
|
|
12
|
+
SettingsIcon,
|
|
8
13
|
} from "lucide-react";
|
|
9
14
|
import Link from "next/link";
|
|
10
15
|
import { usePathname } from "next/navigation";
|
|
@@ -12,7 +17,7 @@ import { useMemo } from "react";
|
|
|
12
17
|
import { Badge } from "./ui/badge";
|
|
13
18
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card";
|
|
14
19
|
import type { AssignmentRecord, MessageRecord, NewsroomSectionRecord, ReferenceRecord } from "../lib/category-repository";
|
|
15
|
-
import { getNewsroomNavHref } from "../lib/newsroom-nav";
|
|
20
|
+
import { getNewsroomNavHref, NEWSROOM_OPS_NAV, type NewsroomNavTabId } from "../lib/newsroom-nav";
|
|
16
21
|
import {
|
|
17
22
|
countReferencesByStatus,
|
|
18
23
|
filterReferencesByStatus,
|
|
@@ -37,15 +42,28 @@ type NewsroomOpsOverviewProps = {
|
|
|
37
42
|
references: ReferenceRecord[];
|
|
38
43
|
};
|
|
39
44
|
|
|
40
|
-
type DestinationKey =
|
|
45
|
+
type DestinationKey = Exclude<NewsroomNavTabId, "overview" | "desks">;
|
|
41
46
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
assignments:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
const DESTINATION_ICONS: Record<DestinationKey, typeof BookOpenIcon> = {
|
|
48
|
+
articles: FileTextIcon,
|
|
49
|
+
assignments: ClipboardListIcon,
|
|
50
|
+
references: BookOpenIcon,
|
|
51
|
+
messages: MessageSquareIcon,
|
|
52
|
+
insights: LightbulbIcon,
|
|
53
|
+
topics: LayersIcon,
|
|
54
|
+
concepts: NetworkIcon,
|
|
55
|
+
administration: SettingsIcon,
|
|
56
|
+
search: SearchIcon,
|
|
47
57
|
};
|
|
48
58
|
|
|
59
|
+
const DESTINATIONS = NEWSROOM_OPS_NAV.filter((item) => item.id !== "overview").map((item) => ({
|
|
60
|
+
key: item.id as DestinationKey,
|
|
61
|
+
label: item.label,
|
|
62
|
+
detail: item.detail,
|
|
63
|
+
href: item.href,
|
|
64
|
+
icon: DESTINATION_ICONS[item.id as DestinationKey],
|
|
65
|
+
}));
|
|
66
|
+
|
|
49
67
|
function openAssignmentCount(assignments: AssignmentRecord[]): number {
|
|
50
68
|
return assignments.filter((assignment) => {
|
|
51
69
|
const status = assignment.status?.trim().toLowerCase() ?? "";
|
|
@@ -85,11 +103,10 @@ export function NewsroomOpsOverview({
|
|
|
85
103
|
[newsroomSections],
|
|
86
104
|
);
|
|
87
105
|
|
|
88
|
-
const destinationCounts: Record<DestinationKey, number | null
|
|
106
|
+
const destinationCounts: Partial<Record<DestinationKey, number | null>> = {
|
|
89
107
|
references: referenceCounts.pending ?? 0,
|
|
90
108
|
assignments: openAssignmentCount(assignments),
|
|
91
109
|
messages: recentMessageCount(messages),
|
|
92
|
-
topics: null,
|
|
93
110
|
};
|
|
94
111
|
|
|
95
112
|
return (
|
|
@@ -102,8 +119,8 @@ export function NewsroomOpsOverview({
|
|
|
102
119
|
<section aria-label="Destinations" className="space-y-3">
|
|
103
120
|
<h2 className="text-sm font-medium text-muted-foreground">Go to</h2>
|
|
104
121
|
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
|
105
|
-
{
|
|
106
|
-
const
|
|
122
|
+
{DESTINATIONS.map((meta) => {
|
|
123
|
+
const key = meta.key;
|
|
107
124
|
const Icon = meta.icon;
|
|
108
125
|
const count = destinationCounts[key];
|
|
109
126
|
const href = getNewsroomNavHref(meta.href, demo);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { toPublicReaderPath } from "../lib/reader-base-path";
|
|
3
4
|
import { signOut } from "aws-amplify/auth";
|
|
4
5
|
import Link from "next/link";
|
|
5
6
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
@@ -114,7 +115,7 @@ export function SettingsPage() {
|
|
|
114
115
|
return (
|
|
115
116
|
<main className="settings-page">
|
|
116
117
|
<nav className="edition-progress edition-progress--newsroom settings-progress" aria-label="Settings navigation">
|
|
117
|
-
<Link className="edition-progress__button edition-progress__button--previous" href="/">
|
|
118
|
+
<Link className="edition-progress__button edition-progress__button--previous" href={toPublicReaderPath("/")}>
|
|
118
119
|
<svg aria-hidden="true" className="edition-progress__icon" focusable="false" viewBox="0 0 10 10">
|
|
119
120
|
<path d="M7.5 1 2.5 5 7.5 9Z" fill="currentColor" />
|
|
120
121
|
</svg>
|
|
@@ -3,7 +3,7 @@ import * as iam from "aws-cdk-lib/aws-iam";
|
|
|
3
3
|
import * as amplify from "aws-cdk-lib/aws-amplify";
|
|
4
4
|
import { GITHUB_OIDC_PROVIDER_HOST } from "./github-oidc-provider.js";
|
|
5
5
|
import { cmsProductionBuildSpec, cmsStagingBuildSpec, readerBuildSpec } from "./build-specs.js";
|
|
6
|
-
import { isStagingEnabled, resolveStackName, resolveStagingDomainName, resolveStoragePreviewPrefix } from "./site-config.js";
|
|
6
|
+
import { isStagingEnabled, resolveCmsHostName, resolveStackName, resolveStagingDomainName, resolveStoragePreviewPrefix } from "./site-config.js";
|
|
7
7
|
function environmentVariables(variables) {
|
|
8
8
|
return Object.entries(variables).map(([name, value]) => ({ name, value }));
|
|
9
9
|
}
|
|
@@ -20,8 +20,9 @@ function environmentVariables(variables) {
|
|
|
20
20
|
* - `cms.staging: false` omits the staging branch and its domain
|
|
21
21
|
*
|
|
22
22
|
* Google sign-in is always on: the branches never set PAPYRUS_DISABLE_GOOGLE_OAUTH,
|
|
23
|
-
and `cms.cognitoDomainPrefix` becomes the
|
|
24
|
-
(PAPYRUS_COGNITO_DOMAIN_PREFIX)
|
|
23
|
+
and `cms.cognitoDomainPrefix` becomes the hosted-UI domain
|
|
24
|
+
(PAPYRUS_COGNITO_DOMAIN_PREFIX) only when `cms.applyCognitoDomainPrefix` is true
|
|
25
|
+
(PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX); otherwise Amplify's generated domain is kept. The secrets GOOGLE_CLIENT_ID and
|
|
25
26
|
GOOGLE_CLIENT_SECRET are Amplify backend secrets set in the console, never here.
|
|
26
27
|
The stack also creates the `<siteId>-papyrus-authoring` role. The CLI signs
|
|
27
28
|
* AppSync requests (SigV4) with credentials for that role (SSO, OIDC or any
|
|
@@ -40,6 +41,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
40
41
|
const cmsAppName = config.cms.appName ?? `${config.siteId}-cms`;
|
|
41
42
|
const stagingEnabled = isStagingEnabled(config);
|
|
42
43
|
const stagingDomainName = resolveStagingDomainName(config);
|
|
44
|
+
const cmsHostName = resolveCmsHostName(config);
|
|
43
45
|
const cmsServiceRole = new iam.Role(this, "AmplifyServiceRole", {
|
|
44
46
|
assumedBy: new iam.ServicePrincipal("amplify.amazonaws.com"),
|
|
45
47
|
description: `Backend deploy and staging build service role for Amplify app ${cmsAppName} (AdministratorAccess: documented risk)`,
|
|
@@ -66,12 +68,13 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
66
68
|
const stagingMode = config.frontend === "markus-static"
|
|
67
69
|
? { PAPYRUS_STAGING_PREVIEW: "static" }
|
|
68
70
|
: { PAPYRUS_CONTENT_SOURCE: "drafts" };
|
|
69
|
-
const productionOrigin =
|
|
70
|
-
? `https://${
|
|
71
|
+
const productionOrigin = cmsHostName
|
|
72
|
+
? `https://${cmsHostName}/`
|
|
71
73
|
: Fn.join("", ["https://main.", cmsApp.attrDefaultDomain, "/"]);
|
|
72
74
|
const stagingOrigin = stagingDomainName
|
|
73
75
|
? `https://${stagingDomainName}/`
|
|
74
76
|
: Fn.join("", ["https://staging.", cmsApp.attrDefaultDomain, "/"]);
|
|
77
|
+
const sharedRootDomain = config.cms.domainPrefix !== undefined;
|
|
75
78
|
const defaultOriginsToAllow = [
|
|
76
79
|
...(config.cms.domainName ? [] : [productionOrigin]),
|
|
77
80
|
...(stagingEnabled && !stagingDomainName ? [stagingOrigin] : []),
|
|
@@ -88,14 +91,16 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
88
91
|
environmentVariables: environmentVariables({
|
|
89
92
|
...config.cms.environment,
|
|
90
93
|
PAPYRUS_COGNITO_DOMAIN_PREFIX: config.cms.cognitoDomainPrefix,
|
|
94
|
+
...(config.cms.applyCognitoDomainPrefix ? { PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX: "true" } : {}),
|
|
91
95
|
PAPYRUS_OAUTH_REDIRECT_URLS: oauthRedirectUrls,
|
|
92
96
|
PAPYRUS_SITE_BRAND: config.brand,
|
|
93
97
|
SITE_ENV: "production",
|
|
94
98
|
PAPYRUS_CONTENT_SOURCE: "published",
|
|
95
99
|
}),
|
|
96
100
|
});
|
|
101
|
+
let stagingBranch;
|
|
97
102
|
if (stagingEnabled) {
|
|
98
|
-
|
|
103
|
+
stagingBranch = new amplify.CfnBranch(this, "StagingBranch", {
|
|
99
104
|
appId: cmsApp.attrAppId,
|
|
100
105
|
branchName: "staging",
|
|
101
106
|
stage: "BETA",
|
|
@@ -105,6 +110,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
105
110
|
environmentVariables: environmentVariables({
|
|
106
111
|
...config.cms.environment,
|
|
107
112
|
PAPYRUS_COGNITO_DOMAIN_PREFIX: config.cms.cognitoDomainPrefix,
|
|
113
|
+
...(config.cms.applyCognitoDomainPrefix ? { PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX: "true" } : {}),
|
|
108
114
|
PAPYRUS_OAUTH_REDIRECT_URLS: oauthRedirectUrls,
|
|
109
115
|
PAPYRUS_SITE_BRAND: config.brand,
|
|
110
116
|
SITE_ENV: "staging",
|
|
@@ -114,7 +120,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
114
120
|
: Fn.join("", ["https://staging.", cmsApp.attrDefaultDomain]),
|
|
115
121
|
}),
|
|
116
122
|
});
|
|
117
|
-
if (stagingDomainName) {
|
|
123
|
+
if (stagingDomainName && !sharedRootDomain) {
|
|
118
124
|
const stagingDomain = new amplify.CfnDomain(this, "StagingDomain", {
|
|
119
125
|
appId: cmsApp.attrAppId,
|
|
120
126
|
domainName: stagingDomainName,
|
|
@@ -124,12 +130,21 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
124
130
|
}
|
|
125
131
|
}
|
|
126
132
|
if (config.cms.domainName) {
|
|
133
|
+
const subDomainSettings = sharedRootDomain
|
|
134
|
+
? [
|
|
135
|
+
{ branchName: "main", prefix: config.cms.domainPrefix },
|
|
136
|
+
...(stagingBranch ? [{ branchName: "staging", prefix: config.cms.stagingDomainPrefix }] : []),
|
|
137
|
+
]
|
|
138
|
+
: [{ branchName: "main", prefix: "" }];
|
|
127
139
|
const productionDomain = new amplify.CfnDomain(this, "Domain", {
|
|
128
140
|
appId: cmsApp.attrAppId,
|
|
129
141
|
domainName: config.cms.domainName,
|
|
130
|
-
subDomainSettings
|
|
142
|
+
subDomainSettings,
|
|
131
143
|
});
|
|
132
144
|
productionDomain.addResourceDependency(productionBranch);
|
|
145
|
+
if (sharedRootDomain && stagingBranch) {
|
|
146
|
+
productionDomain.addResourceDependency(stagingBranch);
|
|
147
|
+
}
|
|
133
148
|
}
|
|
134
149
|
this.cmsAppId = cmsApp.attrAppId;
|
|
135
150
|
new CfnOutput(this, "CmsAppId", { value: cmsApp.attrAppId, description: `Amplify app id for ${cmsAppName}` });
|
|
@@ -262,10 +277,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
262
277
|
const readerDomain = new amplify.CfnDomain(this, "ReaderDomain", {
|
|
263
278
|
appId: readerApp.attrAppId,
|
|
264
279
|
domainName: reader.domainName,
|
|
265
|
-
subDomainSettings: [
|
|
266
|
-
{ branchName: readerBranchName, prefix: "" },
|
|
267
|
-
...(reader.includeWww ? [{ branchName: readerBranchName, prefix: "www" }] : []),
|
|
268
|
-
],
|
|
280
|
+
subDomainSettings: [{ branchName: readerBranchName, prefix: "" }],
|
|
269
281
|
});
|
|
270
282
|
readerDomain.addResourceDependency(readerBranch);
|
|
271
283
|
}
|
|
@@ -4,7 +4,7 @@ import * as iam from "aws-cdk-lib/aws-iam";
|
|
|
4
4
|
import * as amplify from "aws-cdk-lib/aws-amplify";
|
|
5
5
|
import { GITHUB_OIDC_PROVIDER_HOST } from "./github-oidc-provider";
|
|
6
6
|
import { cmsProductionBuildSpec, cmsStagingBuildSpec, readerBuildSpec } from "./build-specs";
|
|
7
|
-
import { AmplifyAppShellSiteConfig, isStagingEnabled, resolveStackName, resolveStagingDomainName, resolveStoragePreviewPrefix } from "./site-config";
|
|
7
|
+
import { AmplifyAppShellSiteConfig, isStagingEnabled, resolveCmsHostName, resolveStackName, resolveStagingDomainName, resolveStoragePreviewPrefix } from "./site-config";
|
|
8
8
|
|
|
9
9
|
function environmentVariables(variables: Record<string, string>): amplify.CfnBranch.EnvironmentVariableProperty[] {
|
|
10
10
|
return Object.entries(variables).map(([name, value]) => ({ name, value }));
|
|
@@ -23,8 +23,9 @@ function environmentVariables(variables: Record<string, string>): amplify.CfnBra
|
|
|
23
23
|
* - `cms.staging: false` omits the staging branch and its domain
|
|
24
24
|
*
|
|
25
25
|
* Google sign-in is always on: the branches never set PAPYRUS_DISABLE_GOOGLE_OAUTH,
|
|
26
|
-
and `cms.cognitoDomainPrefix` becomes the
|
|
27
|
-
(PAPYRUS_COGNITO_DOMAIN_PREFIX)
|
|
26
|
+
and `cms.cognitoDomainPrefix` becomes the hosted-UI domain
|
|
27
|
+
(PAPYRUS_COGNITO_DOMAIN_PREFIX) only when `cms.applyCognitoDomainPrefix` is true
|
|
28
|
+
(PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX); otherwise Amplify's generated domain is kept. The secrets GOOGLE_CLIENT_ID and
|
|
28
29
|
GOOGLE_CLIENT_SECRET are Amplify backend secrets set in the console, never here.
|
|
29
30
|
The stack also creates the `<siteId>-papyrus-authoring` role. The CLI signs
|
|
30
31
|
* AppSync requests (SigV4) with credentials for that role (SSO, OIDC or any
|
|
@@ -45,6 +46,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
45
46
|
const cmsAppName = config.cms.appName ?? `${config.siteId}-cms`;
|
|
46
47
|
const stagingEnabled = isStagingEnabled(config);
|
|
47
48
|
const stagingDomainName = resolveStagingDomainName(config);
|
|
49
|
+
const cmsHostName = resolveCmsHostName(config);
|
|
48
50
|
|
|
49
51
|
const cmsServiceRole = new iam.Role(this, "AmplifyServiceRole", {
|
|
50
52
|
assumedBy: new iam.ServicePrincipal("amplify.amazonaws.com"),
|
|
@@ -76,12 +78,13 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
76
78
|
? { PAPYRUS_STAGING_PREVIEW: "static" }
|
|
77
79
|
: { PAPYRUS_CONTENT_SOURCE: "drafts" };
|
|
78
80
|
|
|
79
|
-
const productionOrigin =
|
|
80
|
-
? `https://${
|
|
81
|
+
const productionOrigin = cmsHostName
|
|
82
|
+
? `https://${cmsHostName}/`
|
|
81
83
|
: Fn.join("", ["https://main.", cmsApp.attrDefaultDomain, "/"]);
|
|
82
84
|
const stagingOrigin = stagingDomainName
|
|
83
85
|
? `https://${stagingDomainName}/`
|
|
84
86
|
: Fn.join("", ["https://staging.", cmsApp.attrDefaultDomain, "/"]);
|
|
87
|
+
const sharedRootDomain = config.cms.domainPrefix !== undefined;
|
|
85
88
|
const defaultOriginsToAllow = [
|
|
86
89
|
...(config.cms.domainName ? [] : [productionOrigin]),
|
|
87
90
|
...(stagingEnabled && !stagingDomainName ? [stagingOrigin] : []),
|
|
@@ -99,6 +102,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
99
102
|
environmentVariables: environmentVariables({
|
|
100
103
|
...config.cms.environment,
|
|
101
104
|
PAPYRUS_COGNITO_DOMAIN_PREFIX: config.cms.cognitoDomainPrefix,
|
|
105
|
+
...(config.cms.applyCognitoDomainPrefix ? { PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX: "true" } : {}),
|
|
102
106
|
PAPYRUS_OAUTH_REDIRECT_URLS: oauthRedirectUrls,
|
|
103
107
|
PAPYRUS_SITE_BRAND: config.brand,
|
|
104
108
|
SITE_ENV: "production",
|
|
@@ -106,8 +110,9 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
106
110
|
}),
|
|
107
111
|
});
|
|
108
112
|
|
|
113
|
+
let stagingBranch: amplify.CfnBranch | undefined;
|
|
109
114
|
if (stagingEnabled) {
|
|
110
|
-
|
|
115
|
+
stagingBranch = new amplify.CfnBranch(this, "StagingBranch", {
|
|
111
116
|
appId: cmsApp.attrAppId,
|
|
112
117
|
branchName: "staging",
|
|
113
118
|
stage: "BETA",
|
|
@@ -117,6 +122,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
117
122
|
environmentVariables: environmentVariables({
|
|
118
123
|
...config.cms.environment,
|
|
119
124
|
PAPYRUS_COGNITO_DOMAIN_PREFIX: config.cms.cognitoDomainPrefix,
|
|
125
|
+
...(config.cms.applyCognitoDomainPrefix ? { PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX: "true" } : {}),
|
|
120
126
|
PAPYRUS_OAUTH_REDIRECT_URLS: oauthRedirectUrls,
|
|
121
127
|
PAPYRUS_SITE_BRAND: config.brand,
|
|
122
128
|
SITE_ENV: "staging",
|
|
@@ -127,7 +133,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
127
133
|
}),
|
|
128
134
|
});
|
|
129
135
|
|
|
130
|
-
if (stagingDomainName) {
|
|
136
|
+
if (stagingDomainName && !sharedRootDomain) {
|
|
131
137
|
const stagingDomain = new amplify.CfnDomain(this, "StagingDomain", {
|
|
132
138
|
appId: cmsApp.attrAppId,
|
|
133
139
|
domainName: stagingDomainName,
|
|
@@ -138,12 +144,21 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
138
144
|
}
|
|
139
145
|
|
|
140
146
|
if (config.cms.domainName) {
|
|
147
|
+
const subDomainSettings = sharedRootDomain
|
|
148
|
+
? [
|
|
149
|
+
{ branchName: "main", prefix: config.cms.domainPrefix as string },
|
|
150
|
+
...(stagingBranch ? [{ branchName: "staging", prefix: config.cms.stagingDomainPrefix as string }] : []),
|
|
151
|
+
]
|
|
152
|
+
: [{ branchName: "main", prefix: "" }];
|
|
141
153
|
const productionDomain = new amplify.CfnDomain(this, "Domain", {
|
|
142
154
|
appId: cmsApp.attrAppId,
|
|
143
155
|
domainName: config.cms.domainName,
|
|
144
|
-
subDomainSettings
|
|
156
|
+
subDomainSettings,
|
|
145
157
|
});
|
|
146
158
|
productionDomain.addResourceDependency(productionBranch);
|
|
159
|
+
if (sharedRootDomain && stagingBranch) {
|
|
160
|
+
productionDomain.addResourceDependency(stagingBranch);
|
|
161
|
+
}
|
|
147
162
|
}
|
|
148
163
|
|
|
149
164
|
this.cmsAppId = cmsApp.attrAppId;
|
|
@@ -312,10 +327,7 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
312
327
|
const readerDomain = new amplify.CfnDomain(this, "ReaderDomain", {
|
|
313
328
|
appId: readerApp.attrAppId,
|
|
314
329
|
domainName: reader.domainName,
|
|
315
|
-
subDomainSettings: [
|
|
316
|
-
{ branchName: readerBranchName, prefix: "" },
|
|
317
|
-
...(reader.includeWww ? [{ branchName: readerBranchName, prefix: "www" }] : []),
|
|
318
|
-
],
|
|
330
|
+
subDomainSettings: [{ branchName: readerBranchName, prefix: "" }],
|
|
319
331
|
});
|
|
320
332
|
readerDomain.addResourceDependency(readerBranch);
|
|
321
333
|
}
|
package/infra/build-specs.js
CHANGED
|
@@ -14,6 +14,7 @@ function commandLines(commands, indent) {
|
|
|
14
14
|
}
|
|
15
15
|
const NPM_CLEAN_INSTALL = "npm ci --cache .npm --prefer-offline";
|
|
16
16
|
const FRONTEND_ENVIRONMENT_CAPTURE = "env | grep -e '^PAPYRUS_' -e '^SITE_ENV=' -e '^NEXT_PUBLIC_' >> .env.production";
|
|
17
|
+
const CHECKOUT_AS_PAPYRUS_ROOT = "PAPYRUS_ROOT=\"$PWD\"";
|
|
17
18
|
function pythonProvisioningCommands(config) {
|
|
18
19
|
return [
|
|
19
20
|
"export UV_CACHE_DIR=\"$PWD/.uv-cache\"",
|
|
@@ -58,7 +59,7 @@ export function cmsStagingBuildSpec(config) {
|
|
|
58
59
|
const cachePaths = [".next/cache/**/*", ".npm/**/*"];
|
|
59
60
|
if (config.frontend === "markus-static") {
|
|
60
61
|
const readerConfig = reader(config);
|
|
61
|
-
preBuild.push(...pythonProvisioningCommands(config),
|
|
62
|
+
preBuild.push(...pythonProvisioningCommands(config), `${CHECKOUT_AS_PAPYRUS_ROOT} papyrus ops content export-published --drafts --out content-export --clean`, readerConfig.buildCommand, `${CHECKOUT_AS_PAPYRUS_ROOT} papyrus ops content upload-preview --dir ${readerConfig.baseDirectory} --prefix ${resolveStoragePreviewPrefix(config)}`);
|
|
62
63
|
cachePaths.push(".uv-cache/**/*");
|
|
63
64
|
}
|
|
64
65
|
return [
|
package/infra/build-specs.ts
CHANGED
|
@@ -22,6 +22,8 @@ const NPM_CLEAN_INSTALL = "npm ci --cache .npm --prefer-offline";
|
|
|
22
22
|
const FRONTEND_ENVIRONMENT_CAPTURE =
|
|
23
23
|
"env | grep -e '^PAPYRUS_' -e '^SITE_ENV=' -e '^NEXT_PUBLIC_' >> .env.production";
|
|
24
24
|
|
|
25
|
+
const CHECKOUT_AS_PAPYRUS_ROOT = "PAPYRUS_ROOT=\"$PWD\"";
|
|
26
|
+
|
|
25
27
|
function pythonProvisioningCommands(config: AmplifyAppShellSiteConfig): string[] {
|
|
26
28
|
return [
|
|
27
29
|
"export UV_CACHE_DIR=\"$PWD/.uv-cache\"",
|
|
@@ -90,9 +92,9 @@ export function cmsStagingBuildSpec(config: AmplifyAppShellSiteConfig): string {
|
|
|
90
92
|
const readerConfig = reader(config);
|
|
91
93
|
preBuild.push(
|
|
92
94
|
...pythonProvisioningCommands(config),
|
|
93
|
-
|
|
95
|
+
`${CHECKOUT_AS_PAPYRUS_ROOT} papyrus ops content export-published --drafts --out content-export --clean`,
|
|
94
96
|
readerConfig.buildCommand,
|
|
95
|
-
|
|
97
|
+
`${CHECKOUT_AS_PAPYRUS_ROOT} papyrus ops content upload-preview --dir ${readerConfig.baseDirectory} --prefix ${resolveStoragePreviewPrefix(config)}`,
|
|
96
98
|
);
|
|
97
99
|
cachePaths.push(".uv-cache/**/*");
|
|
98
100
|
}
|