Skip to content

Commit

Permalink
Adds support for showing the Pulumi Copilot sidebar on the docs site (#…
Browse files Browse the repository at this point in the history
…12905)

- Adds a route to the copilot host so that the iframe is SAMEORIGIN with the docs
- This allows it to React.portal into the doc and interact w/ the page a bit.
- This requires some tweaks to SecurityPolicy to allow the Copilot path to be iframe'd too
  • Loading branch information
foot authored Sep 27, 2024
1 parent 6ae0bd7 commit bae4315
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 26 deletions.
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ content/docs/reference/pkg
tsconfig.json
package.json
typedoc.json

# Ignore the files we generate during the build and deployment process.
origin-bucket-metadata.json
20 changes: 20 additions & 0 deletions assets/css/bundle.css
Original file line number Diff line number Diff line change
Expand Up @@ -3127,6 +3127,26 @@ div.highlight.line-numbers pre.chroma code span.line::before{
}
}

#ai-sidebar-host{
display:none
}

#ai-sidebar-target{
position:fixed;
z-index:10;
top:0;
right:0;
bottom:0
}

.section- #ai-sidebar-target{
top:200px
}

.section-docs #ai-sidebar-target{
top:108px
}

div.highlight{
display:flex
}
Expand Down
1 change: 1 addition & 0 deletions config/_default/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ security:
- ASSET_BUNDLE_ID
- PULUMI_CONVERT_URL
- PULUMI_AI_WS_URL
- PULUMI_COPILOT_URL
- GITHUB_TOKEN
- ALGOLIA_APP_ID
- ALGOLIA_APP_SEARCH_KEY
Expand Down
1 change: 1 addition & 0 deletions infrastructure/Pulumi.www-production.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
config:
aws:region: us-west-2
www.pulumi.com:addSecurityHeaders: "true"
www.pulumi.com:copilotUrl: https://app.pulumi.com/ai
www.pulumi.com:certificateArn: "arn:aws:acm:us-east-1:388588623842:certificate/9db6a76b-f7ba-465b-ab96-ce1d3b8ae02c"
www.pulumi.com:doAIAnswersRewrites: "true"
www.pulumi.com:doEdgeRedirects: "true"
Expand Down
104 changes: 78 additions & 26 deletions infrastructure/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const config = {

const aiAppStack = new pulumi.StackReference('pulumi/pulumi-ai-app-infra/prod');
const aiAppDomain = aiAppStack.requireOutput('aiAppDistributionDomain');
const cloudAiAppDomain = aiAppStack.requireOutput('cloudAiAppDistributionDomain');

// originBucketName is the name of the S3 bucket to use as the CloudFront origin for the
// website. This bucket is presumed to exist prior to the Pulumi run; if it doesn't, this
Expand Down Expand Up @@ -277,33 +278,40 @@ const allViewerExceptHostHeaderId = "b689b0a8-53d0-40ab-baf2-68738e2966ac";
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
const cachingDisabledId = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad";

const SecurityHeadersPolicy = new aws.cloudfront.ResponseHeadersPolicy('security-headers', {
securityHeadersConfig: {
frameOptions: {
frameOption: config.addSecurityHeaders ? 'DENY' : 'SAMEORIGIN',
override: false,
},
// These remaining options are derived from:
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-response-headers-policies.html#managed-response-headers-policies-security
// "SecurityHeadersPolicy" with ID "67f7725c-6f97-4210-82d7-5512b31e9d03"
referrerPolicy: {
referrerPolicy: 'strict-origin-when-cross-origin',
override: false,
},
contentTypeOptions: {
override: true,
},
strictTransportSecurity: {
accessControlMaxAgeSec: 31536000,
override: false,
},
xssProtection: {
protection: true,
modeBlock: true,
override: false,
function newSecurityHeadersPolicy(name: string, frameOption: string) {
return new aws.cloudfront.ResponseHeadersPolicy(name, {
securityHeadersConfig: {
frameOptions: {
frameOption,
override: false,
},
// These remaining options are derived from:
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-response-headers-policies.html#managed-response-headers-policies-security
// "SecurityHeadersPolicy" with ID "67f7725c-6f97-4210-82d7-5512b31e9d03"
referrerPolicy: {
referrerPolicy: 'strict-origin-when-cross-origin',
override: false,
},
contentTypeOptions: {
override: true,
},
strictTransportSecurity: {
accessControlMaxAgeSec: 31536000,
override: false,
},
xssProtection: {
protection: true,
modeBlock: true,
override: false,
}
}
}
})
});
}

// Most of the site
const SecurityHeadersPolicy = newSecurityHeadersPolicy('security-headers', config.addSecurityHeaders ? 'DENY' : 'SAMEORIGIN');
// Copilot lives in an iframe
const CopilotSecurityHeadersPolicy = newSecurityHeadersPolicy('copilot-security-headers', 'SAMEORIGIN');

const baseCacheBehavior: aws.types.input.cloudfront.DistributionDefaultCacheBehavior = {
targetOriginId: originBucket.arn,
Expand Down Expand Up @@ -421,6 +429,18 @@ const distributionArgs: aws.cloudfront.DistributionArgs = {
originKeepaliveTimeout: 60,
},
},
{
originId: cloudAiAppDomain,
domainName: cloudAiAppDomain,
customOriginConfig: {
originProtocolPolicy: "https-only",
httpPort: 80,
httpsPort: 443,
originSslProtocols: ["TLSv1.2"],
originReadTimeout: 60,
originKeepaliveTimeout: 60,
},
},
...registryOrigins,
],

Expand Down Expand Up @@ -580,6 +600,38 @@ const distributionArgs: aws.cloudfront.DistributionArgs = {
cachePolicyId: cachingDisabledId,
lambdaFunctionAssociations: config.doAIAnswersRewrites ? [getAIAnswersRewriteAssociation()] : [],
forwardedValues: undefined, // forwardedValues conflicts with cachePolicyId, so we unset it.
},

// Copilot app
{
...baseCacheBehavior,
// allow all methods
allowedMethods: ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"],
cachedMethods: [
"GET", "HEAD", "OPTIONS",
],
targetOriginId: cloudAiAppDomain,
pathPattern: '/_pulumi/cloud-ai',
originRequestPolicyId: allViewerExceptHostHeaderId,
cachePolicyId: cachingDisabledId,
lambdaFunctionAssociations: [],
forwardedValues: undefined, // forwardedValues conflicts with cachePolicyId, so we unset it.
responseHeadersPolicyId: CopilotSecurityHeadersPolicy.id,
},
{
...baseCacheBehavior,
// allow all methods
allowedMethods: ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"],
cachedMethods: [
"GET", "HEAD", "OPTIONS",
],
targetOriginId: cloudAiAppDomain,
pathPattern: '/_pulumi/cloud-ai/*',
originRequestPolicyId: allViewerExceptHostHeaderId,
cachePolicyId: cachingDisabledId,
lambdaFunctionAssociations: [],
forwardedValues: undefined, // forwardedValues conflicts with cachePolicyId, so we unset it.
responseHeadersPolicyId: CopilotSecurityHeadersPolicy.id,
}
],

Expand Down
2 changes: 2 additions & 0 deletions layouts/_default/baseof.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
<main>
{{ block "main" . }}
{{ end }}

{{ partial "copilot/sidebar.html" . }}
</main>

{{ block "footer" . }}
Expand Down
5 changes: 5 additions & 0 deletions layouts/partials/copilot/sidebar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{ $copilotApiUrl := getenv "PULUMI_COPILOT_URL" }}
{{ if $copilotApiUrl }}
<aside id="ai-sidebar-target"></aside>
<iframe id="ai-sidebar-host" src="/_pulumi/cloud-ai/sidebar?hostApp=docs&apiHost={{ $copilotApiUrl | urlquery }}"></iframe>
{{ end }}
7 changes: 7 additions & 0 deletions scripts/build-site.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ source ./scripts/common.sh
export PULUMI_CONVERT_URL="${PULUMI_CONVERT_URL:-$(pulumi stack output --stack pulumi/tf2pulumi-service/production url)}"
export PULUMI_AI_WS_URL=${PULUMI_AI_WS_URL:-$(pulumi stack output --stack pulumi/pulumigpt-api/corp websocketUri)}

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INFRA_PATH="$SCRIPT_DIR/../infrastructure"
# Read Copilot API URL from Pulumi config, ignoring any errors.
# If the config value is not set Copilot will not be available.
export PULUMI_COPILOT_URL=${PULUMI_COPILOT_URL:-$(pulumi --cwd "$INFRA_PATH" config get copilotUrl 2>/dev/null || echo "")}
printf "Copilot URL: $PULUMI_COPILOT_URL\n"

printf "Compiling theme JavaScript and CSS...\n\n"
export ASSET_BUNDLE_ID="$(build_identifier)"

Expand Down
38 changes: 38 additions & 0 deletions theme/src/scss/_copilot.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Host iframe
// Should not be shown, when loaded it will portal the content to the sidebar target
#ai-sidebar-host {
display: none
}

// Element that will be the target of the portal
// If empty it will have a width of 0 and will not be visible.
#ai-sidebar-target {
// Relative to the viewport so copilot moves w/ the scrolling
position: fixed;

// Popovers on site use z-index: 20
z-index: 10;

// Default use up the full height of the viewport
top: 0;
right: 0;
bottom: 0;
}

// Per page positions
// ------------------

// Main page
// ---

/* FIXME: no section suffix? */
.section- #ai-sidebar-target {
top: 200px;
}

// Docs pages
// ---

.section-docs #ai-sidebar-target {
top: calc(38px + 8px + 8px + 54px);
}
1 change: 1 addition & 0 deletions theme/src/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
@import "marketing/dismissable-banner";
@import "code";
@import "container";
@import "copilot";
@import "copy-button";
@import "docs/cloud-overview";
@import "docs/continuous-delivery";
Expand Down

0 comments on commit bae4315

Please sign in to comment.