add readme preview
This commit is contained in:
parent
312964f22c
commit
2f91fb771e
@ -5,13 +5,17 @@ export function MarkdownRenderer(props: { text: string | undefined }) {
|
|||||||
if (text === undefined) {
|
if (text === undefined) {
|
||||||
text = "";
|
text = "";
|
||||||
}
|
}
|
||||||
const index = text.indexOf("\n---", 3);
|
let c = text;
|
||||||
const c = text.slice(index + 4, text.length);
|
if (text.startsWith("---")) {
|
||||||
|
const index = text.indexOf("\n---", 3);
|
||||||
|
c = text.slice(index + 4, text.length);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class="markdown-body"
|
class="markdown-body"
|
||||||
dangerouslySetInnerHTML={{ __html: marked.parse(c) }}
|
dangerouslySetInnerHTML={{ __html: marked.parse(c) }}
|
||||||
/>
|
>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
34
fresh.gen.ts
34
fresh.gen.ts
@ -5,34 +5,29 @@
|
|||||||
import config from "./deno.json" assert { type: "json" };
|
import config from "./deno.json" assert { type: "json" };
|
||||||
import * as $0 from "./routes/_404.tsx";
|
import * as $0 from "./routes/_404.tsx";
|
||||||
import * as $1 from "./routes/_middleware.ts";
|
import * as $1 from "./routes/_middleware.ts";
|
||||||
import * as $2 from "./routes/api/doc.ts";
|
import * as $2 from "./routes/api/login.ts";
|
||||||
import * as $3 from "./routes/api/login.ts";
|
import * as $3 from "./routes/api/logout.ts";
|
||||||
import * as $4 from "./routes/api/logout.ts";
|
import * as $4 from "./routes/dir/[...path].tsx";
|
||||||
import * as $5 from "./routes/dir/[...path].tsx";
|
import * as $5 from "./routes/doc/index.tsx";
|
||||||
import * as $6 from "./routes/doc/index.tsx";
|
import * as $6 from "./routes/index.tsx";
|
||||||
import * as $7 from "./routes/fs/[...path].ts";
|
import * as $7 from "./routes/login.tsx";
|
||||||
import * as $8 from "./routes/index.tsx";
|
|
||||||
import * as $9 from "./routes/login.tsx";
|
|
||||||
import * as $$0 from "./islands/ContentRenderer.tsx";
|
import * as $$0 from "./islands/ContentRenderer.tsx";
|
||||||
import * as $$1 from "./islands/Counter.tsx";
|
import * as $$1 from "./islands/Counter.tsx";
|
||||||
import * as $$2 from "./islands/DirList.tsx";
|
import * as $$2 from "./islands/DirList.tsx";
|
||||||
import * as $$3 from "./islands/DocSearch.tsx";
|
import * as $$3 from "./islands/DocSearch.tsx";
|
||||||
import * as $$4 from "./islands/FileViewer.tsx";
|
import * as $$4 from "./islands/FileViewer.tsx";
|
||||||
import * as $$5 from "./islands/MarkdownRenderer.tsx";
|
import * as $$5 from "./islands/UpList.tsx";
|
||||||
import * as $$6 from "./islands/UpList.tsx";
|
|
||||||
|
|
||||||
const manifest = {
|
const manifest = {
|
||||||
routes: {
|
routes: {
|
||||||
"./routes/_404.tsx": $0,
|
"./routes/_404.tsx": $0,
|
||||||
"./routes/_middleware.ts": $1,
|
"./routes/_middleware.ts": $1,
|
||||||
"./routes/api/doc.ts": $2,
|
"./routes/api/login.ts": $2,
|
||||||
"./routes/api/login.ts": $3,
|
"./routes/api/logout.ts": $3,
|
||||||
"./routes/api/logout.ts": $4,
|
"./routes/dir/[...path].tsx": $4,
|
||||||
"./routes/dir/[...path].tsx": $5,
|
"./routes/doc/index.tsx": $5,
|
||||||
"./routes/doc/index.tsx": $6,
|
"./routes/index.tsx": $6,
|
||||||
"./routes/fs/[...path].ts": $7,
|
"./routes/login.tsx": $7,
|
||||||
"./routes/index.tsx": $8,
|
|
||||||
"./routes/login.tsx": $9,
|
|
||||||
},
|
},
|
||||||
islands: {
|
islands: {
|
||||||
"./islands/ContentRenderer.tsx": $$0,
|
"./islands/ContentRenderer.tsx": $$0,
|
||||||
@ -40,8 +35,7 @@ const manifest = {
|
|||||||
"./islands/DirList.tsx": $$2,
|
"./islands/DirList.tsx": $$2,
|
||||||
"./islands/DocSearch.tsx": $$3,
|
"./islands/DocSearch.tsx": $$3,
|
||||||
"./islands/FileViewer.tsx": $$4,
|
"./islands/FileViewer.tsx": $$4,
|
||||||
"./islands/MarkdownRenderer.tsx": $$5,
|
"./islands/UpList.tsx": $$5,
|
||||||
"./islands/UpList.tsx": $$6,
|
|
||||||
},
|
},
|
||||||
baseUrl: import.meta.url,
|
baseUrl: import.meta.url,
|
||||||
config,
|
config,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { extname } from "path/posix.ts";
|
import { extname } from "path/posix.ts";
|
||||||
import MarkdownRenderer from "./MarkdownRenderer.tsx";
|
import MarkdownRenderer from "../components/MarkdownRenderer.tsx";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
const TypeToExt = {
|
const TypeToExt = {
|
||||||
@ -25,8 +25,13 @@ function FetchAndRender(props: { src: string; type: string }) {
|
|||||||
const ext = extname(src);
|
const ext = extname(src);
|
||||||
const [content, setContent] = useState("");
|
const [content, setContent] = useState("");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch(src).then((res) => res.text()).then(setContent);
|
(async () => {
|
||||||
|
const res = await fetch(src);
|
||||||
|
const content = await res.text();
|
||||||
|
setContent(content);
|
||||||
|
})();
|
||||||
}, [src]);
|
}, [src]);
|
||||||
|
|
||||||
switch (props.type) {
|
switch (props.type) {
|
||||||
case "text":
|
case "text":
|
||||||
return <div style={{ whiteSpace: "pre-wrap" }}>{content}</div>;
|
return <div style={{ whiteSpace: "pre-wrap" }}>{content}</div>;
|
||||||
@ -41,7 +46,7 @@ function FetchAndRender(props: { src: string; type: string }) {
|
|||||||
//case "csv":
|
//case "csv":
|
||||||
// return <CsvRenderer content={content} />;
|
// return <CsvRenderer content={content} />;
|
||||||
default:
|
default:
|
||||||
return <>error: invalid type: {props.type} content: {content}</>;
|
return <p>error: invalid type: {props.type} content: {content}</p>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +67,7 @@ export function RenderView(props: { src: string }) {
|
|||||||
case "audio":
|
case "audio":
|
||||||
return <audio style={{ width: "100%" }} controls src={src} />;
|
return <audio style={{ width: "100%" }} controls src={src} />;
|
||||||
default:
|
default:
|
||||||
return <>error: invalid type: {type} src: {src}</>;
|
return <p>error: invalid type: {type} src: {src}</p>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ export function DirList(props: DirListProps) {
|
|||||||
</li>
|
</li>
|
||||||
<ListItem
|
<ListItem
|
||||||
key=".."
|
key=".."
|
||||||
href={`/dir/${encodePath(join(data.path, ".."))}`}
|
href={`/dir/${encodePath(join(data.path, ".."))}?pretty`}
|
||||||
icon="/icon/back.svg"
|
icon="/icon/back.svg"
|
||||||
>
|
>
|
||||||
...
|
...
|
||||||
@ -61,7 +61,7 @@ export function DirList(props: DirListProps) {
|
|||||||
{files.map((file) => (
|
{files.map((file) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={file.name}
|
key={file.name}
|
||||||
href={`/dir/${encodePath(join(data.path, file.name))}`}
|
href={`/dir/${encodePath(join(data.path, file.name))}?pretty`}
|
||||||
icon={file.isDirectory
|
icon={file.isDirectory
|
||||||
? "/icon/folder.svg"
|
? "/icon/folder.svg"
|
||||||
: extToIcon(extname(file.name))}
|
: extToIcon(extname(file.name))}
|
||||||
|
@ -2,6 +2,7 @@ import { Doc } from "../src/collect.ts";
|
|||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import { Index } from "../src/client_search.ts";
|
import { Index } from "../src/client_search.ts";
|
||||||
import { encodePath } from "../util/util.ts";
|
import { encodePath } from "../util/util.ts";
|
||||||
|
import { join } from "path/mod.ts";
|
||||||
|
|
||||||
function SearchBar(props: {
|
function SearchBar(props: {
|
||||||
search?: string;
|
search?: string;
|
||||||
@ -43,11 +44,14 @@ export default function DocSearch(props: {
|
|||||||
</SearchBar>
|
</SearchBar>
|
||||||
<h1 class="text-2xl font-bold">Doc</h1>
|
<h1 class="text-2xl font-bold">Doc</h1>
|
||||||
<ul class="mt-4">
|
<ul class="mt-4">
|
||||||
{docs.map((doc) => (
|
{docs.map((doc) => {
|
||||||
<li class="mt-2">
|
const path = join(doc.path, "..");
|
||||||
<a href={`/dir/${encodePath(doc.path)}`}>{doc.path}</a>
|
return (
|
||||||
</li>
|
<li class="mt-2" key={path}>
|
||||||
))}
|
<a href={`/dir/${encodePath(path)}?pretty`}>{path}</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,7 @@ import { encodePath } from "../util/util.ts";
|
|||||||
|
|
||||||
export default function FileViewer(props: { path: string }) {
|
export default function FileViewer(props: { path: string }) {
|
||||||
const { path } = props;
|
const { path } = props;
|
||||||
const srcPath = `/fs/${encodePath(path)}`;
|
const srcPath = `/dir/${encodePath(path)}`;
|
||||||
return (
|
return (
|
||||||
<div class="p-4 mx-auto max-w-screen-md">
|
<div class="p-4 mx-auto max-w-screen-md">
|
||||||
<UpList path={path} />
|
<UpList path={path} />
|
||||||
|
@ -22,7 +22,10 @@ export default function UpList(props: { path: string }) {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 class={"text-2xl flex flex-wrap"}>
|
<h1 class={"text-2xl flex flex-wrap"}>
|
||||||
<a href="/dir/" class="flex flex-wrap p-2 hover:bg-gray-400 rounded-sm">
|
<a
|
||||||
|
href="/dir/?pretty"
|
||||||
|
class="flex flex-wrap p-2 hover:bg-gray-400 rounded-sm"
|
||||||
|
>
|
||||||
<img src={asset("/icon/house.svg")} />
|
<img src={asset("/icon/house.svg")} />
|
||||||
<span class="ml-1">Home</span>
|
<span class="ml-1">Home</span>
|
||||||
</a>
|
</a>
|
||||||
@ -31,7 +34,7 @@ export default function UpList(props: { path: string }) {
|
|||||||
<span class="p-2">/</span>
|
<span class="p-2">/</span>
|
||||||
<a
|
<a
|
||||||
class="flex flex-wrap p-2 hover:bg-gray-400 rounded-sm"
|
class="flex flex-wrap p-2 hover:bg-gray-400 rounded-sm"
|
||||||
href={`/dir/${encodePath(cur)}`}
|
href={`/dir/${encodePath(cur)}?pretty`}
|
||||||
>
|
>
|
||||||
<img src={asset("/icon/folder.svg")} />
|
<img src={asset("/icon/folder.svg")} />
|
||||||
<span class="ml-1">{up}</span>
|
<span class="ml-1">{up}</span>
|
||||||
|
5
main.ts
5
main.ts
@ -26,13 +26,12 @@ import { user_command } from "./user.ts";
|
|||||||
import { key_out_cmd } from "./keyout.ts";
|
import { key_out_cmd } from "./keyout.ts";
|
||||||
import { prepareDocs } from "./src/store/doc.ts";
|
import { prepareDocs } from "./src/store/doc.ts";
|
||||||
|
|
||||||
const github_markdown = await Deno.readTextFile(
|
const github_markdown = (await Deno.readTextFile(
|
||||||
join(fromFileUrl(import.meta.url), "..", "static", "github-markdown.css"),
|
join(fromFileUrl(import.meta.url), "..", "static", "github-markdown.css"),
|
||||||
);
|
)).replaceAll("\n", "");
|
||||||
|
|
||||||
const CSSPlugin: Plugin = {
|
const CSSPlugin: Plugin = {
|
||||||
name: "css plugin",
|
name: "css plugin",
|
||||||
// deno-lint-ignore require-await
|
|
||||||
render(ctx): PluginRenderResult {
|
render(ctx): PluginRenderResult {
|
||||||
ctx.render();
|
ctx.render();
|
||||||
return {
|
return {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { HandlerContext, Handlers, PageProps } from "$fresh/server.ts";
|
import { HandlerContext, Handlers, PageProps } from "$fresh/server.ts";
|
||||||
import { asset, Head } from "$fresh/runtime.ts";
|
import { asset, Head } from "$fresh/runtime.ts";
|
||||||
import { removePrefixFromPathname } from "../../util/util.ts";
|
import { encodePath, removePrefixFromPathname } from "../../util/util.ts";
|
||||||
import { join } from "path/posix.ts";
|
import { join } from "path/posix.ts";
|
||||||
import DirList, { EntryInfo } from "../../islands/DirList.tsx";
|
import DirList, { EntryInfo } from "../../islands/DirList.tsx";
|
||||||
import FileViewer from "../../islands/FileViewer.tsx";
|
import FileViewer from "../../islands/FileViewer.tsx";
|
||||||
|
import RenderView from "../../islands/ContentRenderer.tsx";
|
||||||
|
import { serveFile } from "http/file_server.ts";
|
||||||
|
|
||||||
type DirProps = {
|
type DirProps = {
|
||||||
type: "dir";
|
type: "dir";
|
||||||
@ -19,10 +21,97 @@ type FileProps = {
|
|||||||
|
|
||||||
type DirOrFileProps = DirProps | FileProps;
|
type DirOrFileProps = DirProps | FileProps;
|
||||||
|
|
||||||
|
async function renderFile(req: Request, path: string) {
|
||||||
|
try {
|
||||||
|
const fileInfo = await Deno.stat(path);
|
||||||
|
if (fileInfo.isDirectory) {
|
||||||
|
// if index.html exists, serve it.
|
||||||
|
// otherwise, serve a directory listing.
|
||||||
|
const indexPath = join(path, "/index.html");
|
||||||
|
try {
|
||||||
|
await Deno.stat(indexPath);
|
||||||
|
const res = await serveFile(req, indexPath);
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Deno.errors.NotFound) {
|
||||||
|
const list: Deno.DirEntry[] = [];
|
||||||
|
for await (const entry of Deno.readDir(path)) {
|
||||||
|
list.push(entry);
|
||||||
|
}
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify(
|
||||||
|
list,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods":
|
||||||
|
"GET,HEAD,PUT,PATCH,POST,DELETE",
|
||||||
|
"Access-Control-Allow-Headers":
|
||||||
|
"Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With",
|
||||||
|
},
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const res = await serveFile(req, path, {
|
||||||
|
fileInfo,
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Deno.errors.NotFound) {
|
||||||
|
return new Response("Not Found", {
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderPage(_req: Request, path: string, ctx: HandlerContext) {
|
||||||
|
try {
|
||||||
|
const stat = await Deno.stat(path);
|
||||||
|
|
||||||
|
if (stat.isDirectory) {
|
||||||
|
const filesIter = await Deno.readDir(path);
|
||||||
|
const files: EntryInfo[] = [];
|
||||||
|
for await (const file of filesIter) {
|
||||||
|
const fileStat = await Deno.stat(join(path, file.name));
|
||||||
|
files.push({
|
||||||
|
...file,
|
||||||
|
lastModified: fileStat.mtime ? new Date(fileStat.mtime) : undefined,
|
||||||
|
size: fileStat.size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return await ctx.render({
|
||||||
|
type: "dir",
|
||||||
|
stat,
|
||||||
|
files,
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return await ctx.render({
|
||||||
|
type: "file",
|
||||||
|
stat,
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Deno.errors.NotFound) {
|
||||||
|
return await ctx.renderNotFound();
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function GET(req: Request, ctx: HandlerContext): Promise<Response> {
|
async function GET(req: Request, ctx: HandlerContext): Promise<Response> {
|
||||||
const authRequired = Deno.env.get("AUTH_REQUIRED") === "true";
|
const authRequired = Deno.env.get("AUTH_REQUIRED") === "true";
|
||||||
if (authRequired) {
|
if (authRequired) {
|
||||||
const login = ctx.state["login"];
|
const login = ctx.state["login"];
|
||||||
|
//console.log("login", login);
|
||||||
if (!login) {
|
if (!login) {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 302,
|
status: 302,
|
||||||
@ -39,30 +128,10 @@ async function GET(req: Request, ctx: HandlerContext): Promise<Response> {
|
|||||||
}
|
}
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const path = removePrefixFromPathname(decodeURI(url.pathname), "/dir");
|
const path = removePrefixFromPathname(decodeURI(url.pathname), "/dir");
|
||||||
const stat = await Deno.stat(path);
|
if (url.searchParams.has("pretty")) {
|
||||||
if (stat.isDirectory) {
|
return await renderPage(req, path, ctx);
|
||||||
const filesIter = await Deno.readDir(path);
|
|
||||||
const files: EntryInfo[] = [];
|
|
||||||
for await (const file of filesIter) {
|
|
||||||
const fileStat = await Deno.stat(join(path, file.name));
|
|
||||||
files.push({
|
|
||||||
...file,
|
|
||||||
lastModified: fileStat.mtime ? new Date(fileStat.mtime) : undefined,
|
|
||||||
size: fileStat.size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return await ctx.render({
|
|
||||||
type: "dir",
|
|
||||||
stat,
|
|
||||||
files,
|
|
||||||
path,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
return await ctx.render({
|
return await renderFile(req, path);
|
||||||
type: "file",
|
|
||||||
stat,
|
|
||||||
path,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,8 +139,36 @@ export const handler: Handlers = {
|
|||||||
GET,
|
GET,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isImageFile(path: string) {
|
||||||
|
return /\.(jpg|jpeg|png|gif|webp|svg|bmp|ico|tiff)$/i.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchFiles(path: EntryInfo[], fn: (path: EntryInfo) => boolean) {
|
||||||
|
const candiate = path.filter(fn);
|
||||||
|
if (candiate.length > 0) {
|
||||||
|
return candiate[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
export default function DirLists(props: PageProps<DirOrFileProps>) {
|
export default function DirLists(props: PageProps<DirOrFileProps>) {
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
|
let cover = null, index = null, content = null;
|
||||||
|
if (data.type === "dir") {
|
||||||
|
cover = searchFiles(data.files, (f) => isImageFile(f.name));
|
||||||
|
index = searchFiles(data.files, (f) => f.name === "index.html");
|
||||||
|
const contentFilenameCandidate = new Set([
|
||||||
|
"SUMMARY.md",
|
||||||
|
"README.md",
|
||||||
|
"readme.md",
|
||||||
|
"README.txt",
|
||||||
|
"readme.txt",
|
||||||
|
]);
|
||||||
|
content = searchFiles(
|
||||||
|
data.files,
|
||||||
|
(f) => contentFilenameCandidate.has(f.name),
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@ -81,6 +178,38 @@ export default function DirLists(props: PageProps<DirOrFileProps>) {
|
|||||||
{data.type === "dir"
|
{data.type === "dir"
|
||||||
? <DirList path={data.path} files={data.files}></DirList>
|
? <DirList path={data.path} files={data.files}></DirList>
|
||||||
: <FileViewer path={data.path}></FileViewer>}
|
: <FileViewer path={data.path}></FileViewer>}
|
||||||
|
{index
|
||||||
|
? (
|
||||||
|
<a
|
||||||
|
href={`/dir/${encodePath(join(data.path, index.name))}`}
|
||||||
|
>
|
||||||
|
{cover
|
||||||
|
? (
|
||||||
|
<img
|
||||||
|
src={`/dir/${encodePath(join(data.path, cover.name))}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<span class="border-2 border-gray-300 rounded-md p-2 block mt-2">
|
||||||
|
Index
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
{content
|
||||||
|
? (
|
||||||
|
<div
|
||||||
|
class="border-2 border-gray-300 rounded-md p-2 mt-2"
|
||||||
|
id="README"
|
||||||
|
>
|
||||||
|
<RenderView
|
||||||
|
src={`/dir/${encodePath(join(data.path, content.name))}`}
|
||||||
|
>
|
||||||
|
</RenderView>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
import { HandlerContext, Handlers } from "$fresh/server.ts";
|
|
||||||
import { serveFile } from "http/file_server.ts";
|
|
||||||
import { removePrefixFromPathname } from "../../util/util.ts";
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: Request,
|
|
||||||
ctx: HandlerContext,
|
|
||||||
): Promise<Response> {
|
|
||||||
const url = new URL(req.url);
|
|
||||||
const path = removePrefixFromPathname(decodeURI(url.pathname), "/fs");
|
|
||||||
// if auth is required, check if the user is logged in.
|
|
||||||
// if not, return a 401.
|
|
||||||
const authRequired = Deno.env.get("AUTH_REQUIRED") === "true";
|
|
||||||
if (authRequired) {
|
|
||||||
const login = ctx.state["login"];
|
|
||||||
if (!login) {
|
|
||||||
return new Response(null, {
|
|
||||||
status: 302,
|
|
||||||
headers: {
|
|
||||||
"Location": "/login",
|
|
||||||
"content-type": "text/plain",
|
|
||||||
"Access-Control-Allow-Origin": "*",
|
|
||||||
"Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
|
|
||||||
"Access-Control-Allow-Headers":
|
|
||||||
"Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fileInfo = await Deno.stat(path);
|
|
||||||
if (fileInfo.isDirectory) {
|
|
||||||
// if index.html exists, serve it.
|
|
||||||
// otherwise, serve a directory listing.
|
|
||||||
const indexPath = path + "/index.html";
|
|
||||||
try {
|
|
||||||
await Deno.stat(indexPath);
|
|
||||||
const res = await serveFile(req, indexPath);
|
|
||||||
return res;
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof Deno.errors.NotFound) {
|
|
||||||
const list: Deno.DirEntry[] = [];
|
|
||||||
for await (const entry of Deno.readDir(path)) {
|
|
||||||
list.push(entry);
|
|
||||||
}
|
|
||||||
return new Response(
|
|
||||||
JSON.stringify(
|
|
||||||
list,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
"content-type": "application/json",
|
|
||||||
"Access-Control-Allow-Origin": "*",
|
|
||||||
"Access-Control-Allow-Methods":
|
|
||||||
"GET,HEAD,PUT,PATCH,POST,DELETE",
|
|
||||||
"Access-Control-Allow-Headers":
|
|
||||||
"Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With",
|
|
||||||
},
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const res = await serveFile(req, path, {
|
|
||||||
fileInfo,
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof Deno.errors.NotFound) {
|
|
||||||
return new Response("Not Found", {
|
|
||||||
status: 404,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const handler: Handlers = {
|
|
||||||
GET,
|
|
||||||
};
|
|
@ -16,7 +16,7 @@ export default function Home() {
|
|||||||
This is a simple file server. It serves files from the{" "}
|
This is a simple file server. It serves files from the{" "}
|
||||||
<code>CWD</code>.
|
<code>CWD</code>.
|
||||||
</p>
|
</p>
|
||||||
<a href="/dir/">Go To CWD</a> | <a href="/doc/">Doc</a>
|
<a href="/dir/?pretty">Go To CWD</a> | <a href="/doc/">Doc</a>
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
<a href="/login">Login</a> | <a href="/api/logout">Logout</a>
|
<a href="/login">Login</a> | <a href="/api/logout">Logout</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,3 +2,5 @@
|
|||||||
title: "SUMMARY"
|
title: "SUMMARY"
|
||||||
tags: ["SUMMARY"]
|
tags: ["SUMMARY"]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
11
test_data/한글/index.html
Normal file
11
test_data/한글/index.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>한글</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>한글</h1>
|
||||||
|
<p>한글</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user