From cfa4f540baac5beaf168de3e818c882ccbd95136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=A8=E6=9D=89?= Date: Tue, 3 Dec 2019 08:14:25 +0800 Subject: better html for file_server (#3423) --- std/http/file_server.ts | 195 ++++++++++++++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 73 deletions(-) (limited to 'std/http/file_server.ts') diff --git a/std/http/file_server.ts b/std/http/file_server.ts index 8dc2ee87a..fb8f28081 100755 --- a/std/http/file_server.ts +++ b/std/http/file_server.ts @@ -7,8 +7,7 @@ // https://github.com/indexzero/http-server/blob/master/test/http-server-test.js const { ErrorKind, cwd, args, stat, readDir, open } = Deno; -import { contentType } from "../media_types/mod.ts"; -import { extname, posix } from "../path/mod.ts"; +import { posix } from "../path/mod.ts"; import { listenAndServe, ServerRequest, @@ -16,33 +15,14 @@ import { Response } from "./server.ts"; -const dirViewerTemplate = ` - - - - - - - Deno File Server - - - -

Index of <%DIRNAME%>

- - - <%CONTENTS%> -
ModeSizeName
- - -`; +interface EntryInfo { + mode: string; + size: string; + url: string; + name: string; +} +const encoder = new TextEncoder(); const serverArgs = args.slice(); let CORSEnabled = false; // TODO: switch to flags if we later want to add more options @@ -58,7 +38,6 @@ const target = posix.isAbsolute(targetArg) ? posix.normalize(targetArg) : posix.join(cwd(), targetArg); const addr = `0.0.0.0:${serverArgs[2] || 4500}`; -const encoder = new TextEncoder(); function modeToString(isDir: boolean, maybeMode: number | null): string { const modeMap = ["---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"]; @@ -99,25 +78,6 @@ function fileLenToString(len: number): string { return `${(len / base).toFixed(2)}${suffix[suffixIndex]}`; } -function createDirEntryDisplay( - name: string, - url: string, - size: number | null, - mode: number | null, - isDir: boolean -): string { - const sizeStr = size === null ? "" : "" + fileLenToString(size!); - return ` - ${modeToString( - isDir, - mode - )}${sizeStr}${name}${ - isDir ? "/" : "" - } - - `; -} - async function serveFile( req: ServerRequest, filePath: string @@ -126,7 +86,7 @@ async function serveFile( const fileInfo = await stat(filePath); const headers = new Headers(); headers.set("content-length", fileInfo.len.toString()); - headers.set("content-type", contentType(extname(filePath)) || "text/plain"); + headers.set("content-type", "text/plain"); const res = { status: 200, @@ -141,12 +101,8 @@ async function serveDir( req: ServerRequest, dirPath: string ): Promise { - interface ListItem { - name: string; - template: string; - } const dirUrl = `/${posix.relative(target, dirPath)}`; - const listEntry: ListItem[] = []; + const listEntry: EntryInfo[] = []; const fileInfos = await readDir(dirPath); for (const fileInfo of fileInfos) { const filePath = posix.join(dirPath, fileInfo.name); @@ -161,29 +117,17 @@ async function serveDir( mode = (await stat(filePath)).mode; } catch (e) {} listEntry.push({ + mode: modeToString(fileInfo.isDirectory(), mode), + size: fileInfo.isFile() ? fileLenToString(fileInfo.len) : "", name: fileInfo.name, - template: createDirEntryDisplay( - fileInfo.name, - fileUrl, - fileInfo.isFile() ? fileInfo.len : null, - mode, - fileInfo.isDirectory() - ) + url: fileUrl }); } - - const formattedDirUrl = `${dirUrl.replace(/\/$/, "")}/`; - const page = new TextEncoder().encode( - dirViewerTemplate.replace("<%DIRNAME%>", formattedDirUrl).replace( - "<%CONTENTS%>", - listEntry - .sort((a, b): number => - a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1 - ) - .map((v): string => v.template) - .join("") - ) + listEntry.sort((a, b) => + a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1 ); + const formattedDirUrl = `${dirUrl.replace(/\/$/, "")}/`; + const page = encoder.encode(dirViewerTemplate(formattedDirUrl, listEntry)); const headers = new Headers(); headers.set("content-type", "text/html"); @@ -232,6 +176,111 @@ function setCORS(res: Response): void { ); } +function dirViewerTemplate(dirname: string, entries: EntryInfo[]): string { + return html` + + + + + + + Deno File Server + + + +
+

Index of ${dirname}

+ + + + + + + ${entries.map( + entry => html` + + + + + + ` + )} +
ModeSizeName
+ ${entry.mode} + + ${entry.size} + + ${entry.name} +
+
+ + + `; +} + +function html(strings: TemplateStringsArray, ...values: unknown[]): string { + const l = strings.length - 1; + let html = ""; + + for (let i = 0; i < l; i++) { + let v = values[i]; + if (v instanceof Array) { + v = v.join(""); + } + const s = strings[i] + v; + html += s; + } + html += strings[l]; + return html; +} + listenAndServe( addr, async (req): Promise => { -- cgit v1.2.3