Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use same directory listing format as nginx #65

Merged
merged 2 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"type": "module",
"scripts": {
"start": "wrangler dev --remote",
"format": "prettier --check --write \"**/*.{ts,js,json,md,hbs}\"",
"prettier": "prettier --check \"**/*.{ts,js,json,md,hbs}\"",
"format": "prettier --check --write \"**/*.{ts,js,json,md}\"",
"prettier": "prettier --check \"**/*.{ts,js,json,md}\"",
"lint": "eslint ./src",
"test:unit": "node --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout --loader=tsx ./tests/unit/index.test.ts",
"test:e2e": "wrangler deploy --dry-run --outdir=dist && node --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout --loader=tsx ./tests/e2e/index.test.ts",
Expand Down
4 changes: 1 addition & 3 deletions scripts/compile-handlebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ templatesToParse.then(files => {

// The Handlebars.precompile returns a JavaScript object
// and then we make a default export of the object
const javascriptTemplate = `export default ${removeStringIndents(
compiledTemplate
)}`;
const javascriptTemplate = `export default ${compiledTemplate}`;

const outputFilename = filePath.replace('.hbs', '.out.js');

Expand Down
81 changes: 56 additions & 25 deletions src/handlers/strategies/directoryListing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ import { S3_MAX_KEYS, S3_RETRY_LIMIT } from '../../constants/limits';
// Applies the Template into a Handlebars Template Function
const handleBarsTemplate = Handlebars.template(htmlTemplate);

const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];

/**
* @TODO: Simplify the iteration logic or make it more readable
*
Expand All @@ -35,34 +49,34 @@ export function renderDirectoryListing(
env: Env
): Response {
// Holds the contents of the listing (directories and files)
const tableElements = [];

// There shouldn't really be a case where we're listing the root
// directory (/) when this is deployed, so always add the option
// to go up a directory
tableElements.push({
href: '../',
name: '../',
lastModified: '-',
size: '-',
});
const tableElements: object[] = [];

const urlPathname = `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}`;

// Renders all the subdirectories within the Directory
delimitedPrefixes.forEach(name => {
const extra = encodeURIComponent(name.substring(0, name.length - 1));

let displayName: string;
let displayNamePaddingRight: string = ''; // hate this
if (name.length > 50) {
displayName = name.substring(0, 49) + '>';
} else {
displayName = name;
displayNamePaddingRight = ' '.repeat(50 - name.length);
}

tableElements.push({
href: `${urlPathname}${extra}/`,
name,
lastModified: '-',
size: '-',
href: `${extra}/`,
displayNamePaddingRight,
name: displayName,
lastModified: ' -',
size: ' -',
});
});

// Last time any of the files within the directory got modified
let lastModified: Date | undefined = undefined;
let directoryLastModified: Date | undefined = undefined;

// Renders all the Files within the Directory
objects.forEach(object => {
Expand All @@ -71,20 +85,35 @@ export function renderDirectoryListing(
// Find the most recent date a file in this
// directory was modified, we'll use it
// in the `Last-Modified` header
if (lastModified === undefined || object.LastModified! > lastModified) {
lastModified = object.LastModified!;
if (
directoryLastModified === undefined ||
object.LastModified! > directoryLastModified
) {
directoryLastModified = object.LastModified!;
}

let dateStr = object.LastModified!.toISOString();
const lastModified = object.LastModified!;
const dateStr = `${lastModified.getUTCDay()}-${months.at(
lastModified.getUTCMonth()
)}-${lastModified.getUTCFullYear()} ${lastModified.getUTCHours()}:${lastModified.getUTCMinutes()}`;

let displayName: string = '';
let displayNamePaddingRight: string = ''; // hate this
if (name!.length > 50) {
displayName = name!.substring(0, 49) + '>';
ovflowd marked this conversation as resolved.
Show resolved Hide resolved
} else {
displayName = name!;
displayNamePaddingRight = ' '.repeat(50 - name!.length);
}

dateStr = dateStr.split('.')[0].replace('T', ' ');
dateStr = dateStr.slice(0, dateStr.lastIndexOf(':')) + 'Z';
const bytes = niceBytes(object.Size!);

tableElements.push({
href: `${urlPathname}${encodeURIComponent(name ?? '')}`,
name,
name: displayName,
displayNamePaddingRight,
lastModified: dateStr,
size: niceBytes(object.Size!),
size: ' '.repeat(20 - bytes.length) + bytes,
});
});

Expand All @@ -95,11 +124,13 @@ export function renderDirectoryListing(
});

// Gets an UTC-string on the ISO-8901 format of last modified date
const lastModifiedUTC = (lastModified ?? new Date()).toUTCString();
const directoryLastModifiedUtc = (
directoryLastModified ?? new Date()
).toUTCString();

return new Response(request.method === 'GET' ? renderedListing : null, {
headers: {
'last-modified': lastModifiedUTC,
'last-modified': directoryLastModifiedUtc,
'content-type': 'text/html',
'cache-control': env.DIRECTORY_CACHE_CONTROL || 'no-store',
},
Expand Down
38 changes: 9 additions & 29 deletions src/templates/directoryListing.hbs
Original file line number Diff line number Diff line change
@@ -1,31 +1,11 @@
<html>
<head>
<title>Index of {{pathname}}</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<meta charset='utf-8' />
<style type='text/css'>
td { padding-right: 16px; text-align: right; font-family: monospace }
td:nth-of-type(1) { text-align: left; overflow-wrap: anywhere }
td:nth-of-type(3) { white-space: nowrap } th { text-align: left; } @media
(prefers-color-scheme: dark) { body { color: white; background-color:
#1c1b22; } a { color: #3391ff; } a:visited { color: #C63B65; } }
</style>
</head>
<body>
<h1>Index of {{pathname}}</h1>
<table>
<tr>
<th>Filename</th>
<th>Modified</th>
<th>Size</th>
</tr>
{{#each entries}}
<tr>
<td><a href='{{href}}'>{{name}}</a></td>
<td>{{lastModified}}</td>
<td>{{size}}</td>
</tr>
{{/each}}
</table>
</body>
<head><title>Index of {{pathname}}</title></head>
<body>
<h1>Index of {{pathname}}</h1><hr /><pre><a href='../'>../</a>
{{#each entries}}
<a href='{{href}}'>{{name}}</a>{{displayNamePaddingRight}}
{{lastModified}}
{{size}}
{{/each}}
</pre><hr /></body>
</html>
2 changes: 1 addition & 1 deletion src/templates/directoryListing.out.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions tests/e2e/directory.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { after, before, describe, it } from 'node:test';
import assert from 'node:assert';
import { readFileSync } from 'node:fs';
import { readFileSync, writeFileSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import http from 'http';
import { Miniflare } from 'miniflare';
Expand Down Expand Up @@ -93,7 +93,10 @@ describe('Directory Tests (Restricted Directory Listing)', () => {
// it'll pass for the other listings and therefore
// don't need to test it over and over again
const body = await res.text();
assert.strictEqual(body, expectedHtml.replaceAll('\n', ''));
assert.strictEqual(
body.replaceAll('\r', ''),
expectedHtml.replaceAll('\r', '')
);
});

it('allows `/dist/`', async () => {
Expand Down
9 changes: 8 additions & 1 deletion tests/e2e/test-data/expected-html/dist.txt
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
<!DOCTYPE html><html><head><title>Index of /dist/</title><meta name='viewport' content='width=device-width, initial-scale=1.0' /><meta charset='utf-8' /><style type='text/css'>td { padding-right: 16px; text-align: right; font-family: monospace }td:nth-of-type(1) { text-align: left; overflow-wrap: anywhere }td:nth-of-type(3) { white-space: nowrap } th { text-align: left; } @media(prefers-color-scheme: dark) { body { color: white; background-color:#1c1b22; } a { color: #3391ff; } a:visited { color: #C63B65; } }</style></head><body><h1>Index of /dist/</h1><table><tr><th>Filename</th><th>Modified</th><th>Size</th></tr><tr><td><a href='../'>../</a></td><td>-</td><td>-</td></tr><tr><td><a href='/dist/latest/'>latest/</a></td><td>-</td><td>-</td></tr><tr><td><a href='/dist/index.json'>index.json</a></td><td>2023-09-12 05:43Z</td><td>18 B</td></tr></table></body></html>
<!DOCTYPE html><html>
<head><title>Index of /dist/</title></head>
<body>
<h1>Index of /dist/</h1><hr><pre><a href='../'>../</a>
<a href='latest/'>latest/</a> - -
<a href='/dist/index.json'>index.json</a> 2-Oct-2023 5:43 18 B
</pre><hr /></body>
</html>
Loading