Open birtles opened 3 months ago
Appreciate the details @birtles! Do you just need to inject into the CloudFront function for requests routed to Lambda origins? Or also for requests routed to s3 origins?
Hi @fwang, sorry I'm still new to SST so I please correct me if I get things mixed up.
As I understand it SST will generate two origins and one origin group:
regionalServer
- the Lambda function that runs the Astro server-side codestaticsServer
- the S3 bucket with the static assetsfallthroughServer
- an origin group including staticServer
(primary) and falling through to regionalServer
on 403/404.Then there are three behaviors:
_astro/*
- origin is staticsServer
(the S3 bucket). This seems to be where various JS/CSS bundles live._image
- origin is regionalServer
(the server Lambda). Runs the image service function as a viewer request CF function.fallthroughServer
. Runs the server viewer request CF function to perform various redirects such as adding /index.html
when necessary.If that summary is right, then I think the answer to your question is I want to inject into the CF function to redirect to S3 origins.
Basically Astro will generate:
client/
├─ en/
│ ├─ index.html
│ ├─ some-path/
│ │ ├─ index.html
├─ ja/
│ ├─ index.html
│ ├─ some-path/
│ │ ├─ index.html
If the incoming request is for /some-path
then I want to return a 302 redirect to /en/some-path
or /ja/some-path
based on the Accept-Language
.
(In future, I'd also like to internally redirect some incoming requests based on UA string when we have different resources optimized for different browsers but I might be able to set up specific behaviors for those routes without touching the viewer request function on the default behavior, I'm not sure.)
I made a start on trying to address this with the current API. I'm completely new to SST and Pulumi but as far as I can tell, it turns out to be a bit of work just to get to the starting line.
@pulumi/pulumi
and @pulumi/aws
as dev dependencies.sst.config.ts
something like the following:/// <reference path="./.sst/platform/config.d.ts" />
import { cloudfront } from '@pulumi/aws';
import { all, type Input } from '@pulumi/pulumi';
import type { BuildMetaConfig } from 'astro-sst/build-meta';
import * as fs from 'node:fs';
import * as path from 'node:path';
export default $config({
app(input) {
return {
name: 'my-app',
removal: input?.stage === 'production' ? 'retain' : 'remove',
home: 'aws',
};
},
async run() {
const astroSite = new sst.aws.Astro('AstroSite', {
transform: {
cdn: (cdn) => {
const viewerRequest = new cloudfront.Function(
// TODO: Generate a more suitable name
'AstroSiteTestViewerRequest',
{
runtime: 'cloudfront-js-1.0',
code: getViewerRequestCode(),
},
{ parent: astroSite }
);
cdn.defaultCacheBehavior = all([
cdn.defaultCacheBehavior,
viewerRequest.arn,
]).apply(([behavior, viewerRequestArn]) => {
// TODO: Don't clobber other functions
behavior.functionAssociations = [
{
eventType: 'viewer-request',
functionArn: viewerRequestArn,
},
];
return behavior;
});
},
},
});
},
});
function getViewerRequestCode(): Input<string> {
// TODO: Handle the site path correctly
const filePath = path.join('.', 'dist', 'sst.buildMeta.json');
if (!fs.existsSync(filePath)) {
throw new Error(`Could not find build meta file at ${filePath}.`);
}
const buildMeta = JSON.parse(
fs.readFileSync(filePath, 'utf-8')
) as BuildMetaConfig;
// XXX Fork all the handling of the buildMeta.routes here
// (and the forwarded host header handling etc.)
return Promise.resolve(`function handler(event) {
var request = event.request;
// Viewer request logic here
return request;
}`);
}
After doing that you still need to fork a bunch of code from astro.ts
and elsewhere and hope it doesn't get any important updates/fixes in the future.
It's also going to break if, for example, the build meta filename or format changes. And then there are all the TODOs too.
@birtles is the locale redirect something that can be baked into the Astro
component, or is it something custom to ur usecase?
Can u share the code that need to be injected into the CF function?
@birtles is the locale redirect something that can be baked into the
Astro
component, or is it something custom to ur usecase?
@fwang I'm afraid I don't think you can bake it into the Astro
component (see my first message). Basically, there are all sorts of different behaviors you might want:
Avail-Language
headerAnd then there are plenty of other cases for wanting to customize the viewer request function like UA-string based redirects.
As mentioned on Discord, in my Astro site I'd like to run a Cloudfront function to perform redirects based on the user's
Accept-Language
header. However, It looks like by the time the transform for the distribution runs the "injections" for that function have already been merged into a CF function.As I understand it, my options are (1) fork all the code in astro.ts and add the necessary logic, (2) introduce an extra behavior that runs before the fallback one and do the redirect there (assuming that's possible).
I'm not sure what the solution is. I'm new to SST but it appears that v2 might have passed the "plan" to the transform function so it was easier to add further injections?
Non-solutions:
Accept-Language
will make this even more complex. Furthermore, there are other use cases for wanting to customize the viewer request function."hybrid"
site it really should be possible to do the redirect as a CloudFront function at the edge instead of running a Lambda. In my case I'm usingmanual: true
so I don't know if the server Lambda would work anyway.