CloudFront¶
The results bucket is served through a CloudFront distribution, which is what
$AWS_S3_BUCKET names — utilities.sh builds the results URL as
https://$AWS_S3_BUCKET/$S3_RUN_PREFIX/<uid>/index.html.
One CloudFront function sits in front of it, on the viewer request event of the default behavior. Without it every folder link in a published report is a 404, because S3 stores objects and a request for a folder matches none of them.
Two filenames, two meanings¶
Everything the function does follows from this. Across a published run these two names never collide, and neither is ever the other:
index.html |
a page something wrote to be read — the run's landing page at the top of a run, a Krona chart under a shotgun run's krona/ |
directory_listing.html |
the listing of the folder it sits in, written into every directory of the results tree by index_directories.sh |
So the function has one decision to make: a request for a folder is either the top of a run, which means the dashboard, or it is any other folder, which means that folder's listing.
Every link this system writes names the file outright — a folder row in a
listing points at subfolder/directory_listing.html, not at subfolder/. The
function is for the links it does not write: the folder links inside
nf-core's own report, and a reader editing the address bar. Naming the file is
also what lets an unpacked copy of the run's zip
browse the same way the published run does, with no server involved at all.
The function¶
function handler(event) {
var request = event.request;
var uri = request.uri;
// 1. A folder URL missing its trailing slash. Answer with a 301 that adds
// one, so the browser ends up on the folder itself and the relative
// links inside the page it gets resolve against that folder rather than
// against its parent.
if (!uri.endsWith('/') && !uri.includes('.')) {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
'location': { value: uri + '/' }
}
};
}
// 2. A folder URL. The top of a run is its dashboard; every folder below
// one is the listing written into it. Rewritten internally, so the
// address bar keeps saying what the reader asked for.
if (uri.endsWith('/')) {
request.uri += isRunRoot(uri) ? 'index.html' : 'directory_listing.html';
}
return request;
}
// "/nxf/<uid>/" and nothing deeper. Splitting that on "/" gives four fields,
// the last of them empty, and a uid is eight characters. RUN_PREFIX below has
// to match S3_RUN_PREFIX in .env.
function isRunRoot(uri) {
var RUN_PREFIX = 'nxf';
var parts = uri.split('/');
return parts.length === 4 && parts[1] === RUN_PREFIX && parts[2].length === 8;
}
Worked through, for a run published at /nxf/vg7dyqwv/:
| Request | Answer |
|---|---|
/nxf/vg7dyqwv/index.html |
the dashboard, untouched — this is the address on the Wrike task |
/nxf/vg7dyqwv/ |
the dashboard, rewritten to index.html |
/nxf/vg7dyqwv |
301 to /nxf/vg7dyqwv/, then as above |
/nxf/vg7dyqwv/qiime2/ |
qiime2/directory_listing.html |
/nxf/vg7dyqwv/qiime2 |
301, then as above |
/nxf/vg7dyqwv/krona/kraken2.html |
a tool's own page, untouched |
/nxf/vg7dyqwv/summary_report/summary_report.html |
untouched |
The one address the function cannot get right is /nxf/<uid>/, because two
readers mean two different things by it: someone trimming a results link wants
the dashboard, and ampliseq's report — which writes "the base results folder" as
href="../" — wants the file listing. The dashboard wins, since that is the
address a person types. ampliseq_upload.sh repoints that one link at
../directory_listing.html before the upload, so the report reaches the listing
by name.
Setting it up¶
CloudFront → Functions → Create function. Name it nxf-directory-index,
runtime cloudfront-js-2.0.
- Paste the code above into Build, substituting
RUN_PREFIXifS3_RUN_PREFIXin.envis notnxf. Save changes. - Publish tab → Publish function. An unpublished function cannot be associated with anything.
-
Associate tab → Add association:
Distribution the one in front of the results bucket Event type Viewer request Cache behavior Default (*)
Associate it with the default behavior only. A CloudFront function is configured per cache behavior. If you add behaviors later, this function belongs on the behavior that serves published results, and on no other — an address with no trailing slash and no dot would otherwise be redirected once by rule 1 before it reached whatever the new behavior points at.
Checking it¶
Against any published run:
Expect 301 and a location: ending in qiime2/. Then:
Expect <title>qiime2 — the listing names the folder it describes. The run root
should answer with the task name instead: