diff --git a/package.json b/package.json index 69abefe..0200b60 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/compile-handlebars.js b/scripts/compile-handlebars.js index 076ccd0..7758efc 100644 --- a/scripts/compile-handlebars.js +++ b/scripts/compile-handlebars.js @@ -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'); diff --git a/src/handlers/strategies/directoryListing.ts b/src/handlers/strategies/directoryListing.ts index 34a7933..3eaa002 100644 --- a/src/handlers/strategies/directoryListing.ts +++ b/src/handlers/strategies/directoryListing.ts @@ -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 * @@ -35,17 +49,7 @@ 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('/') ? '' : '/'}`; @@ -53,16 +57,26 @@ export function renderDirectoryListing( 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 => { @@ -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, 47) + '..>'; + } 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, }); }); @@ -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', }, diff --git a/src/templates/directoryListing.hbs b/src/templates/directoryListing.hbs index b9a7e4c..60c8888 100644 --- a/src/templates/directoryListing.hbs +++ b/src/templates/directoryListing.hbs @@ -1,31 +1,11 @@ - - Index of {{pathname}} - - - - - -

Index of {{pathname}}

- - - - - - - {{#each entries}} - - - - - - {{/each}} -
FilenameModifiedSize
{{name}}{{lastModified}}{{size}}
- +Index of {{pathname}} + +

Index of {{pathname}}


../
+{{#each entries}}
+{{name}}{{displayNamePaddingRight}}
+{{lastModified}}
+{{size}}
+{{/each}}
+

\ No newline at end of file diff --git a/src/templates/directoryListing.out.js b/src/templates/directoryListing.out.js index ec461bf..9772bed 100644 --- a/src/templates/directoryListing.out.js +++ b/src/templates/directoryListing.out.js @@ -1 +1 @@ -export default{1:function(t,e,n,l,a){var o,i=t.strict,r=t.lambda;return""+(null!=(o=r(i(e,"name",{start:{line:24,column:35},end:{line:24,column:39}}),e))?o:"")+""+(null!=(o=r(i(e,"lastModified",{start:{line:25,column:16},end:{line:25,column:28}}),e))?o:"")+""+(null!=(o=r(i(e,"size",{start:{line:26,column:16},end:{line:26,column:20}}),e))?o:"")+""},compiler:[8,">= 4.3.0"],main:function(t,e,n,l,a){var o,i=t.strict,r=t.lambda,c=t.lookupProperty||function(t,e){if(Object.prototype.hasOwnProperty.call(t,e))return t[e]};return"Index of "+(null!=(o=r(i(e,"pathname",{start:{line:3,column:22},end:{line:3,column:30}}),e))?o:"")+"

Index of "+(null!=(o=r(i(e,"pathname",{start:{line:15,column:19},end:{line:15,column:27}}),e))?o:"")+"

"+(null!=(o=c(n,"each").call(null!=e?e:t.nullContext||{},c(e,"entries"),{name:"each",hash:{},fn:t.program(1,a,0),inverse:t.noop,data:a,loc:{start:{line:22,column:6},end:{line:28,column:15}}}))?o:"")+"
FilenameModifiedSize
"},useData:!0}; +export default{1:function(n,l,e,t,a){var r,o=n.strict,u=n.lambda;return""+(null!=(r=u(o(l,"name",{start:{line:6,column:21},end:{line:6,column:25}}),l))?r:"")+""+(null!=(r=u(o(l,"displayNamePaddingRight",{start:{line:6,column:33},end:{line:6,column:56}}),l))?r:"")+" "+(null!=(r=u(o(l,"lastModified",{start:{line:6,column:61},end:{line:6,column:73}}),l))?r:"")+" "+(null!=(r=u(o(l,"size",{start:{line:6,column:78},end:{line:6,column:82}}),l))?r:"")+"\r\n"},compiler:[8,">= 4.3.0"],main:function(n,l,e,t,a){var r,o=n.strict,u=n.lambda,i=n.lookupProperty||function(n,l){if(Object.prototype.hasOwnProperty.call(n,l))return n[l]};return"\r\nIndex of "+(null!=(r=u(o(l,"pathname",{start:{line:2,column:24},end:{line:2,column:32}}),l))?r:"")+"\r\n\r\n

Index of "+(null!=(r=u(o(l,"pathname",{start:{line:4,column:15},end:{line:4,column:23}}),l))?r:"")+"


../\r\n"+(null!=(r=i(e,"each").call(null!=l?l:n.nullContext||{},i(l,"entries"),{name:"each",hash:{},fn:n.program(1,a,0),inverse:n.noop,data:a,loc:{start:{line:5,column:0},end:{line:7,column:9}}}))?r:"")+"

\r\n"},useData:!0}; diff --git a/tests/e2e/directory.test.ts b/tests/e2e/directory.test.ts index 71889aa..61f852c 100644 --- a/tests/e2e/directory.test.ts +++ b/tests/e2e/directory.test.ts @@ -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'; @@ -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 () => { diff --git a/tests/e2e/test-data/expected-html/dist.txt b/tests/e2e/test-data/expected-html/dist.txt index 0909a6b..815770c 100644 --- a/tests/e2e/test-data/expected-html/dist.txt +++ b/tests/e2e/test-data/expected-html/dist.txt @@ -1 +1,8 @@ -Index of /dist/

Index of /dist/

FilenameModifiedSize
../--
latest/--
index.json2023-09-12 05:43Z18 B
\ No newline at end of file + +Index of /dist/ + +

Index of /dist/


../
+latest/                                                           -                   -
+index.json                                         2-Oct-2023 5:43                 18 B
+

+ \ No newline at end of file