Compare commits
2 Commits
d959076d28
...
312964f22c
Author | SHA1 | Date | |
---|---|---|---|
312964f22c | |||
8b3db1e2f1 |
@ -9,7 +9,7 @@ const TypeToExt = {
|
|||||||
"md": new Set([".md"]),
|
"md": new Set([".md"]),
|
||||||
"text": new Set([".txt"]),
|
"text": new Set([".txt"]),
|
||||||
"code": new Set([".json", ".js", ".ts", ".css", ".tsx", ".jsx"]),
|
"code": new Set([".json", ".js", ".ts", ".css", ".tsx", ".jsx"]),
|
||||||
}
|
};
|
||||||
|
|
||||||
function extToType(ext: string) {
|
function extToType(ext: string) {
|
||||||
for (const [type, exts] of Object.entries(TypeToExt)) {
|
for (const [type, exts] of Object.entries(TypeToExt)) {
|
||||||
@ -20,12 +20,12 @@ function extToType(ext: string) {
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
function FetchAndRender(props: {src: string, type: string}){
|
function FetchAndRender(props: { src: string; type: string }) {
|
||||||
const src = props.src;
|
const src = props.src;
|
||||||
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);
|
fetch(src).then((res) => res.text()).then(setContent);
|
||||||
}, [src]);
|
}, [src]);
|
||||||
switch (props.type) {
|
switch (props.type) {
|
||||||
case "text":
|
case "text":
|
||||||
@ -33,7 +33,11 @@ function FetchAndRender(props: {src: string, type: string}){
|
|||||||
case "md":
|
case "md":
|
||||||
return <MarkdownRenderer text={content} />;
|
return <MarkdownRenderer text={content} />;
|
||||||
case "code":
|
case "code":
|
||||||
return <pre style={{ whiteSpace: "pre-wrap" }}><code lang={ext.slice(1)}>{content}</code></pre>;
|
return (
|
||||||
|
<pre
|
||||||
|
style={{ whiteSpace: "pre-wrap" }}
|
||||||
|
><code lang={ext.slice(1)}>{content}</code></pre>
|
||||||
|
);
|
||||||
//case "csv":
|
//case "csv":
|
||||||
// return <CsvRenderer content={content} />;
|
// return <CsvRenderer content={content} />;
|
||||||
default:
|
default:
|
||||||
@ -48,7 +52,7 @@ export function RenderView(props: {src: string}) {
|
|||||||
case "text":
|
case "text":
|
||||||
case "md":
|
case "md":
|
||||||
case "code":
|
case "code":
|
||||||
return <FetchAndRender src={src} type={type}></FetchAndRender>
|
return <FetchAndRender src={src} type={type}></FetchAndRender>;
|
||||||
//case "csv":
|
//case "csv":
|
||||||
// return <CsvRenderer content={content} />;
|
// return <CsvRenderer content={content} />;
|
||||||
case "image":
|
case "image":
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Head, asset } from "$fresh/runtime.ts";
|
import { asset, Head } from "$fresh/runtime.ts";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { extname, join } from "path/posix.ts";
|
import { extname, join } from "path/posix.ts";
|
||||||
import { ComponentChild } from "preact";
|
import { ComponentChild } from "preact";
|
||||||
@ -7,14 +7,16 @@ import {extToIcon} from "../src/media.ts";
|
|||||||
import { encodePath } from "../util/util.ts";
|
import { encodePath } from "../util/util.ts";
|
||||||
|
|
||||||
function ListItem(props: {
|
function ListItem(props: {
|
||||||
href: string,
|
href: string;
|
||||||
icon: string,
|
icon: string;
|
||||||
children: ComponentChild
|
children: ComponentChild;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<li class="p-1 hover:bg-gray-400 transition-colors">
|
<li class="p-1 hover:bg-gray-400 transition-colors">
|
||||||
<a class="flex gap-2" href={props.href}>
|
<a class="flex gap-2" href={props.href}>
|
||||||
<img src={asset(props.icon)}/><p class="">{props.children}</p></a>
|
<img src={asset(props.icon)} />
|
||||||
|
<p class="">{props.children}</p>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -37,7 +39,8 @@ export function DirList(props: DirListProps) {
|
|||||||
const data = props;
|
const data = props;
|
||||||
const [files, setFiles] = useState(data.files);
|
const [files, setFiles] = useState(data.files);
|
||||||
|
|
||||||
return (<div>
|
return (
|
||||||
|
<div>
|
||||||
<UpList path={data.path}></UpList>
|
<UpList path={data.path}></UpList>
|
||||||
<ul class="border-2 rounded-md">
|
<ul class="border-2 rounded-md">
|
||||||
<li class="p-1 flex gap-2">
|
<li class="p-1 flex gap-2">
|
||||||
@ -48,20 +51,30 @@ export function DirList(props: DirListProps) {
|
|||||||
<img src={asset("/icon/sort-alpha-down.svg")} /> Sort Alphabet
|
<img src={asset("/icon/sort-alpha-down.svg")} /> Sort Alphabet
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<ListItem key=".." href={`/dir/${encodePath(join(data.path,".."))}`}
|
<ListItem
|
||||||
|
key=".."
|
||||||
|
href={`/dir/${encodePath(join(data.path, ".."))}`}
|
||||||
icon="/icon/back.svg"
|
icon="/icon/back.svg"
|
||||||
>...</ListItem>
|
>
|
||||||
|
...
|
||||||
|
</ListItem>
|
||||||
{files.map((file) => (
|
{files.map((file) => (
|
||||||
<ListItem key={file.name} href={`/dir/${encodePath(join(data.path,file.name))}`}
|
<ListItem
|
||||||
icon={file.isDirectory ? "/icon/folder.svg": extToIcon(extname(file.name))}
|
key={file.name}
|
||||||
>{file.name}</ListItem>
|
href={`/dir/${encodePath(join(data.path, file.name))}`}
|
||||||
|
icon={file.isDirectory
|
||||||
|
? "/icon/folder.svg"
|
||||||
|
: extToIcon(extname(file.name))}
|
||||||
|
>
|
||||||
|
{file.name}
|
||||||
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>)
|
</div>
|
||||||
|
);
|
||||||
function sortDir() {
|
function sortDir() {
|
||||||
// sort by directory first then by index
|
// sort by directory first then by index
|
||||||
const sorted_files = files.map((x,i)=>
|
const sorted_files = files.map((x, i) => ([x, i] as [EntryInfo, number]))
|
||||||
([x,i] as [EntryInfo, number]))
|
|
||||||
.sort(
|
.sort(
|
||||||
([a, ai], [b, bi]) => {
|
([a, ai], [b, bi]) => {
|
||||||
if (a.isDirectory && !b.isDirectory) {
|
if (a.isDirectory && !b.isDirectory) {
|
||||||
@ -71,13 +84,13 @@ export function DirList(props: DirListProps) {
|
|||||||
} else {
|
} else {
|
||||||
return ai - bi;
|
return ai - bi;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
setFiles(sorted_files.map(([x, _]) => x));
|
setFiles(sorted_files.map(([x, _]) => x));
|
||||||
}
|
}
|
||||||
function sortAlpha() {
|
function sortAlpha() {
|
||||||
// sort by alphabet first then by index
|
// sort by alphabet first then by index
|
||||||
const sorted_files = files.map((x,i)=>
|
const sorted_files = files.map((x, i) => ([x, i] as [EntryInfo, number]))
|
||||||
([x,i] as [EntryInfo, number]))
|
|
||||||
.sort(
|
.sort(
|
||||||
([a, ai], [b, bi]) => {
|
([a, ai], [b, bi]) => {
|
||||||
const ret = a.name.localeCompare(b.name);
|
const ret = a.name.localeCompare(b.name);
|
||||||
@ -86,7 +99,8 @@ export function DirList(props: DirListProps) {
|
|||||||
} else {
|
} else {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
setFiles(sorted_files.map(([x, _]) => x));
|
setFiles(sorted_files.map(([x, _]) => x));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,28 +10,37 @@ function SearchBar(props:{
|
|||||||
const [search, setSearch] = useState(props.search ?? "");
|
const [search, setSearch] = useState(props.search ?? "");
|
||||||
return (
|
return (
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<input type="text" class="w-1/2 px-4 py-2 border-2 rounded-lg" placeholder="Search..."
|
<input
|
||||||
|
type="text"
|
||||||
|
class="w-1/2 px-4 py-2 border-2 rounded-lg"
|
||||||
|
placeholder="Search..."
|
||||||
//onChange={(event)=>{}}
|
//onChange={(event)=>{}}
|
||||||
onKeyUp={(event) => {
|
onKeyUp={(event) => {
|
||||||
if (event.currentTarget.value === search) return;
|
if (event.currentTarget.value === search) return;
|
||||||
setSearch(event.currentTarget.value);
|
setSearch(event.currentTarget.value);
|
||||||
props.onSearch?.(event.currentTarget.value);
|
props.onSearch?.(event.currentTarget.value);
|
||||||
}}>{search}</input>
|
}}
|
||||||
|
>
|
||||||
|
{search}
|
||||||
|
</input>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocSearch(props: {
|
export default function DocSearch(props: {
|
||||||
docs: Doc[]
|
docs: Doc[];
|
||||||
}) {
|
}) {
|
||||||
const [docs, setDocs] = useState(props.docs);
|
const [docs, setDocs] = useState(props.docs);
|
||||||
const index = Index.createIndex(props.docs);
|
const index = Index.createIndex(props.docs);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SearchBar onSearch={(s)=>{
|
<SearchBar
|
||||||
|
onSearch={(s) => {
|
||||||
setDocs(index.search(s));
|
setDocs(index.search(s));
|
||||||
}}></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) => (
|
||||||
@ -41,5 +50,5 @@ export default function DocSearch(props: {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
@ -14,5 +14,5 @@ export default function FileViewer(props: { path: string }) {
|
|||||||
<RenderView src={srcPath} />
|
<RenderView src={srcPath} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
@ -5,9 +5,14 @@ export function MarkdownRenderer(props: { text: string | undefined }) {
|
|||||||
if (text === undefined) {
|
if (text === undefined) {
|
||||||
text = "";
|
text = "";
|
||||||
}
|
}
|
||||||
const index = text.indexOf('\n---', 3);
|
const index = text.indexOf("\n---", 3);
|
||||||
const c = text.slice(index + 4, text.length);
|
const c = text.slice(index + 4, text.length);
|
||||||
return <div class="markdown-body" dangerouslySetInnerHTML={{ __html: marked.parse(c) }} />;
|
return (
|
||||||
|
<div
|
||||||
|
class="markdown-body"
|
||||||
|
dangerouslySetInnerHTML={{ __html: marked.parse(c) }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MarkdownRenderer;
|
export default MarkdownRenderer;
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { Head, asset } from "$fresh/runtime.ts";
|
import { asset, Head } from "$fresh/runtime.ts";
|
||||||
import { join } from "path/posix.ts";
|
import { join } from "path/posix.ts";
|
||||||
import { ComponentChild } from "preact";
|
import { ComponentChild } from "preact";
|
||||||
import { encodePath } from "../util/util.ts";
|
import { encodePath } from "../util/util.ts";
|
||||||
|
|
||||||
|
|
||||||
function stairs(path: string) {
|
function stairs(path: string) {
|
||||||
if (path === ".") return [];
|
if (path === ".") return [];
|
||||||
const uplist = path.split("/");
|
const uplist = path.split("/");
|
||||||
@ -20,21 +19,26 @@ export default function UpList(props:{path: string}) {
|
|||||||
const data = props;
|
const data = props;
|
||||||
const uplist = stairs(data.path);
|
const uplist = stairs(data.path);
|
||||||
|
|
||||||
return (<div>
|
return (
|
||||||
|
<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/" 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></a>
|
<span class="ml-1">Home</span>
|
||||||
{
|
</a>
|
||||||
uplist.map(([cur, up], i) => (
|
{uplist.map(([cur, up], i) => (
|
||||||
<>
|
<>
|
||||||
<span class="p-2">/</span>
|
<span class="p-2">/</span>
|
||||||
<a class="flex flex-wrap p-2 hover:bg-gray-400 rounded-sm" href={`/dir/${encodePath(cur)}`}>
|
<a
|
||||||
|
class="flex flex-wrap p-2 hover:bg-gray-400 rounded-sm"
|
||||||
|
href={`/dir/${encodePath(cur)}`}
|
||||||
|
>
|
||||||
<img src={asset("/icon/folder.svg")} />
|
<img src={asset("/icon/folder.svg")} />
|
||||||
<span class="ml-1">{up}</span>
|
<span class="ml-1">{up}</span>
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
))
|
))}
|
||||||
}</h1>
|
</h1>
|
||||||
</div>)
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
@ -5,7 +5,10 @@ import { prepareSecretKey } from "./util/secret.ts";
|
|||||||
export const key_out_cmd = new Command();
|
export const key_out_cmd = new Command();
|
||||||
key_out_cmd.name("keyout")
|
key_out_cmd.name("keyout")
|
||||||
.description("Output the secret key.")
|
.description("Output the secret key.")
|
||||||
.option("-f, --file <file:string>", "The file to output the key to. (default: stdout)")
|
.option(
|
||||||
|
"-f, --file <file:string>",
|
||||||
|
"The file to output the key to. (default: stdout)",
|
||||||
|
)
|
||||||
.option("--dotenv", "Output the key in dotenv format.")
|
.option("--dotenv", "Output the key in dotenv format.")
|
||||||
.action(async ({ file, dotenv }) => {
|
.action(async ({ file, dotenv }) => {
|
||||||
const key = await prepareSecretKey();
|
const key = await prepareSecretKey();
|
||||||
|
47
main.ts
47
main.ts
@ -4,14 +4,19 @@
|
|||||||
/// <reference lib="dom.asynciterable" />
|
/// <reference lib="dom.asynciterable" />
|
||||||
/// <reference lib="deno.ns" />
|
/// <reference lib="deno.ns" />
|
||||||
|
|
||||||
import { PluginRenderResult, Plugin, ServerContext, Manifest, StartOptions } from "$fresh/server.ts";
|
import {
|
||||||
|
Manifest,
|
||||||
|
Plugin,
|
||||||
|
PluginRenderResult,
|
||||||
|
ServerContext,
|
||||||
|
StartOptions,
|
||||||
|
} from "$fresh/server.ts";
|
||||||
import manifest from "./fresh.gen.ts";
|
import manifest from "./fresh.gen.ts";
|
||||||
|
|
||||||
import twindPlugin from "$fresh/plugins/twind.ts";
|
import twindPlugin from "$fresh/plugins/twind.ts";
|
||||||
import twindConfig from "./twind.config.ts";
|
import twindConfig from "./twind.config.ts";
|
||||||
import "https://deno.land/std@0.170.0/dotenv/load.ts";
|
import "https://deno.land/std@0.170.0/dotenv/load.ts";
|
||||||
|
|
||||||
|
|
||||||
import { Command } from "https://deno.land/x/cliffy@v0.25.6/mod.ts";
|
import { Command } from "https://deno.land/x/cliffy@v0.25.6/mod.ts";
|
||||||
import { fromFileUrl, join } from "path/mod.ts";
|
import { fromFileUrl, join } from "path/mod.ts";
|
||||||
import { prepareSecretKey } from "./util/secret.ts";
|
import { prepareSecretKey } from "./util/secret.ts";
|
||||||
@ -19,8 +24,11 @@ import { serve } from "http/server.ts";
|
|||||||
|
|
||||||
import { user_command } from "./user.ts";
|
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";
|
||||||
|
|
||||||
const github_markdown= await Deno.readTextFile(join(fromFileUrl(import.meta.url), "..", "static", "github-markdown.css"))
|
const github_markdown = await Deno.readTextFile(
|
||||||
|
join(fromFileUrl(import.meta.url), "..", "static", "github-markdown.css"),
|
||||||
|
);
|
||||||
|
|
||||||
const CSSPlugin: Plugin = {
|
const CSSPlugin: Plugin = {
|
||||||
name: "css plugin",
|
name: "css plugin",
|
||||||
@ -30,9 +38,9 @@ const CSSPlugin: Plugin = {
|
|||||||
return {
|
return {
|
||||||
styles: [{
|
styles: [{
|
||||||
cssText: github_markdown,
|
cssText: github_markdown,
|
||||||
}]
|
}],
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
await prepareSecretKey();
|
await prepareSecretKey();
|
||||||
|
|
||||||
@ -48,8 +56,14 @@ async function startServer(manifest: Manifest, options: StartOptions = {}){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function start({port = 8000, hostname = "localhost"}: {port?: number, hostname?: string} = {}){
|
async function start(
|
||||||
await startServer(manifest, { plugins: [twindPlugin(twindConfig), CSSPlugin],
|
{ port = 8000, hostname = "localhost" }: {
|
||||||
|
port?: number;
|
||||||
|
hostname?: string;
|
||||||
|
} = {},
|
||||||
|
) {
|
||||||
|
await startServer(manifest, {
|
||||||
|
plugins: [twindPlugin(twindConfig), CSSPlugin],
|
||||||
port: port,
|
port: port,
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
});
|
});
|
||||||
@ -58,12 +72,15 @@ async function start({port = 8000, hostname = "localhost"}: {port?: number, host
|
|||||||
if (import.meta.main) {
|
if (import.meta.main) {
|
||||||
const cmd = new Command();
|
const cmd = new Command();
|
||||||
cmd.name("fs-server")
|
cmd.name("fs-server")
|
||||||
.description("A simple file server that supports search, upload, and download.")
|
.description(
|
||||||
|
"A simple file server that supports search, upload, and download.",
|
||||||
|
)
|
||||||
.version("0.0.1")
|
.version("0.0.1")
|
||||||
.globalOption("-d, --debug", "Enable debug mode.")
|
.globalOption("-d, --debug", "Enable debug mode.")
|
||||||
.command("start", "Start the server.")
|
.command("start", "Start the server.")
|
||||||
.option("-p, --port <port:number>", "The port to listen on.",
|
.option("-p, --port <port:number>", "The port to listen on.", {
|
||||||
{ default: 8000 })
|
default: 8000,
|
||||||
|
})
|
||||||
.option("--auth", "Enable authentication.")
|
.option("--auth", "Enable authentication.")
|
||||||
.arguments("[hostname:string]")
|
.arguments("[hostname:string]")
|
||||||
.action(async ({ debug, port, auth }, hostname) => {
|
.action(async ({ debug, port, auth }, hostname) => {
|
||||||
@ -74,17 +91,17 @@ if (import.meta.main){
|
|||||||
if (debug) {
|
if (debug) {
|
||||||
console.log("Debug mode enabled.");
|
console.log("Debug mode enabled.");
|
||||||
}
|
}
|
||||||
|
await prepareDocs();
|
||||||
await start({
|
await start({
|
||||||
port: port,
|
port: port,
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
});
|
});
|
||||||
}
|
})
|
||||||
)
|
|
||||||
.command("user", user_command)
|
.command("user", user_command)
|
||||||
.command("keyout", key_out_cmd);
|
.command("keyout", key_out_cmd);
|
||||||
|
|
||||||
await cmd.parse(Deno.args);
|
await cmd.parse(Deno.args);
|
||||||
}
|
} else {
|
||||||
else {
|
await prepareDocs();
|
||||||
await start();
|
await start();
|
||||||
}
|
}
|
@ -1,20 +1,21 @@
|
|||||||
import { MiddlewareHandlerContext } from "$fresh/server.ts";
|
import { MiddlewareHandlerContext } from "$fresh/server.ts";
|
||||||
import { getCookies } from "http/cookie.ts";
|
import { getCookies } from "http/cookie.ts";
|
||||||
import { decode, verify } from "djwt";
|
import { verify } from "djwt";
|
||||||
import { prepareSecretKey } from "../util/secret.ts";
|
import { prepareSecretKey } from "../util/secret.ts";
|
||||||
|
|
||||||
const secret_key = await prepareSecretKey();
|
const secret_key = await prepareSecretKey();
|
||||||
|
|
||||||
export const handler =
|
export const handler = async (
|
||||||
async (req: Request , ctx: MiddlewareHandlerContext<Record<string, unknown>>) => {
|
req: Request,
|
||||||
|
ctx: MiddlewareHandlerContext<Record<string, unknown>>,
|
||||||
|
) => {
|
||||||
const cookies = getCookies(req.headers);
|
const cookies = getCookies(req.headers);
|
||||||
const jwt = cookies["auth"];
|
const jwt = cookies["auth"];
|
||||||
try {
|
try {
|
||||||
const payload = await verify(jwt, secret_key);
|
const payload = await verify(jwt, secret_key);
|
||||||
ctx.state["login"] = payload;
|
ctx.state["login"] = payload;
|
||||||
}
|
} catch (e) {
|
||||||
catch (e){
|
|
||||||
ctx.state["login"] = null;
|
ctx.state["login"] = null;
|
||||||
}
|
}
|
||||||
return await ctx.next();
|
return await ctx.next();
|
||||||
}
|
};
|
||||||
|
@ -6,7 +6,6 @@ import { getUser, verifyUser } from "../../src/user/user.ts";
|
|||||||
import { create as createJWT } from "djwt";
|
import { create as createJWT } from "djwt";
|
||||||
import { prepareSecretKey } from "../../util/secret.ts";
|
import { prepareSecretKey } from "../../util/secret.ts";
|
||||||
|
|
||||||
|
|
||||||
const SECRET_KEY = await prepareSecretKey();
|
const SECRET_KEY = await prepareSecretKey();
|
||||||
|
|
||||||
async function POST(req: Request, ctx: HandlerContext): Promise<Response> {
|
async function POST(req: Request, ctx: HandlerContext): Promise<Response> {
|
||||||
@ -21,7 +20,7 @@ async function POST(req: Request, ctx: HandlerContext): Promise<Response> {
|
|||||||
if (await verifyUser(user, password.toString())) {
|
if (await verifyUser(user, password.toString())) {
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
const jwt = await createJWT({ alg: "HS512", typ: "JWT" }, {
|
const jwt = await createJWT({ alg: "HS512", typ: "JWT" }, {
|
||||||
username: user.name
|
username: user.name,
|
||||||
}, SECRET_KEY);
|
}, SECRET_KEY);
|
||||||
setCookie(headers, {
|
setCookie(headers, {
|
||||||
name: "auth",
|
name: "auth",
|
||||||
@ -31,19 +30,20 @@ async function POST(req: Request, ctx: HandlerContext): Promise<Response> {
|
|||||||
maxAge: 60 * 60 * 24 * 7,
|
maxAge: 60 * 60 * 24 * 7,
|
||||||
domain: url.hostname,
|
domain: url.hostname,
|
||||||
path: "/",
|
path: "/",
|
||||||
secure: url.protocol === "https:"
|
secure: url.protocol === "https:",
|
||||||
});
|
});
|
||||||
|
|
||||||
headers.set("Location", "/");
|
headers.set("Location", "/");
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: Status.SeeOther, // See Other
|
status: Status.SeeOther, // See Other
|
||||||
headers: headers
|
headers: headers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response(`<!DOCTYPE html><html>
|
return new Response(
|
||||||
|
`<!DOCTYPE html><html>
|
||||||
<head> <title> Login Failed </title> </head>
|
<head> <title> Login Failed </title> </head>
|
||||||
<body>
|
<body>
|
||||||
<h1> Login Failed </h1>
|
<h1> Login Failed </h1>
|
||||||
@ -52,14 +52,16 @@ async function POST(req: Request, ctx: HandlerContext): Promise<Response> {
|
|||||||
document.location.href = "/login";
|
document.location.href = "/login";
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`, {
|
</html>`,
|
||||||
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/html"
|
"Content-Type": "text/html",
|
||||||
},
|
},
|
||||||
status: Status.Forbidden,
|
status: Status.Forbidden,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handler = {
|
export const handler = {
|
||||||
POST
|
POST,
|
||||||
};
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import { PageProps, Handlers, HandlerContext } from "$fresh/server.ts";
|
import { HandlerContext, Handlers, PageProps } from "$fresh/server.ts";
|
||||||
import { Head, asset } from "$fresh/runtime.ts";
|
import { asset, Head } from "$fresh/runtime.ts";
|
||||||
import { removePrefixFromPathname } from "../../util/util.ts";
|
import { 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";
|
||||||
@ -10,12 +10,12 @@ type DirProps = {
|
|||||||
path: string;
|
path: string;
|
||||||
stat: Deno.FileInfo;
|
stat: Deno.FileInfo;
|
||||||
files: EntryInfo[];
|
files: EntryInfo[];
|
||||||
}
|
};
|
||||||
type FileProps = {
|
type FileProps = {
|
||||||
type: "file";
|
type: "file";
|
||||||
path: string;
|
path: string;
|
||||||
stat: Deno.FileInfo;
|
stat: Deno.FileInfo;
|
||||||
}
|
};
|
||||||
|
|
||||||
type DirOrFileProps = DirProps | FileProps;
|
type DirOrFileProps = DirProps | FileProps;
|
||||||
|
|
||||||
@ -25,13 +25,15 @@ async function GET(req: Request, ctx: HandlerContext): Promise<Response>{
|
|||||||
const login = ctx.state["login"];
|
const login = ctx.state["login"];
|
||||||
if (!login) {
|
if (!login) {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 401,
|
status: 302,
|
||||||
headers: {
|
headers: {
|
||||||
|
"Location": "/login",
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
|
"Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
|
||||||
"Access-Control-Allow-Headers": "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"
|
"Access-Control-Allow-Headers":
|
||||||
}
|
"Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,43 +42,45 @@ async function GET(req: Request, ctx: HandlerContext): Promise<Response>{
|
|||||||
const stat = await Deno.stat(path);
|
const stat = await Deno.stat(path);
|
||||||
if (stat.isDirectory) {
|
if (stat.isDirectory) {
|
||||||
const filesIter = await Deno.readDir(path);
|
const filesIter = await Deno.readDir(path);
|
||||||
const files: EntryInfo[] = []
|
const files: EntryInfo[] = [];
|
||||||
for await (const file of filesIter) {
|
for await (const file of filesIter) {
|
||||||
const fileStat = await Deno.stat(join(path, file.name));
|
const fileStat = await Deno.stat(join(path, file.name));
|
||||||
files.push({
|
files.push({
|
||||||
...file,
|
...file,
|
||||||
lastModified: fileStat.mtime ? new Date(fileStat.mtime) : undefined,
|
lastModified: fileStat.mtime ? new Date(fileStat.mtime) : undefined,
|
||||||
size: fileStat.size
|
size: fileStat.size,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return await ctx.render({
|
return await ctx.render({
|
||||||
type: "dir",
|
type: "dir",
|
||||||
stat,
|
stat,
|
||||||
files
|
files,
|
||||||
, path
|
path,
|
||||||
})
|
});
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
return await ctx.render({
|
return await ctx.render({
|
||||||
type: "file",
|
type: "file",
|
||||||
stat, path
|
stat,
|
||||||
|
path,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handler: Handlers = {
|
export const handler: Handlers = {
|
||||||
GET
|
GET,
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function DirLists(props: PageProps<DirOrFileProps>) {
|
export default function DirLists(props: PageProps<DirOrFileProps>) {
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
return (<>
|
return (
|
||||||
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Simple file server : {data.path}</title>
|
<title>Simple file server : {data.path}</title>
|
||||||
</Head>
|
</Head>
|
||||||
<div class="p-4 mx-auto max-w-screen-md">
|
<div class="p-4 mx-auto max-w-screen-md">
|
||||||
{data.type === "dir" ? (<DirList path={data.path} files={data.files}></DirList>) :
|
{data.type === "dir"
|
||||||
(<FileViewer path={data.path}></FileViewer>)}
|
? <DirList path={data.path} files={data.files}></DirList>
|
||||||
|
: <FileViewer path={data.path}></FileViewer>}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,17 +1,34 @@
|
|||||||
import { Head } from "$fresh/runtime.ts";
|
import { Head } from "$fresh/runtime.ts";
|
||||||
import { PageProps, Handlers, HandlerContext } from "$fresh/server.ts";
|
import { HandlerContext, Handlers, PageProps } from "$fresh/server.ts";
|
||||||
import DocSearch from "../../islands/DocSearch.tsx";
|
import DocSearch from "../../islands/DocSearch.tsx";
|
||||||
import { Doc } from "../../src/collect.ts";
|
import { Doc } from "../../src/collect.ts";
|
||||||
import { docCollector } from "../../src/store/doc.ts";
|
import { docCollector } from "../../src/store/doc.ts";
|
||||||
|
|
||||||
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";
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
const docs = docCollector.getDocs();
|
const docs = docCollector.getDocs();
|
||||||
return await ctx.render({ docs });
|
return await ctx.render({ docs });
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handler: Handlers = {
|
export const handler: Handlers = {
|
||||||
GET,
|
GET,
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function Docs(props: PageProps<{ docs: Doc[] }>) {
|
export default function Docs(props: PageProps<{ docs: Doc[] }>) {
|
||||||
const { docs } = props.data;
|
const { docs } = props.data;
|
||||||
|
@ -2,7 +2,10 @@ import { HandlerContext, Handlers } from "$fresh/server.ts";
|
|||||||
import { serveFile } from "http/file_server.ts";
|
import { serveFile } from "http/file_server.ts";
|
||||||
import { removePrefixFromPathname } from "../../util/util.ts";
|
import { removePrefixFromPathname } from "../../util/util.ts";
|
||||||
|
|
||||||
export async function GET(req: Request, ctx: HandlerContext): Promise<Response> {
|
export 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), "/fs");
|
const path = removePrefixFromPathname(decodeURI(url.pathname), "/fs");
|
||||||
// if auth is required, check if the user is logged in.
|
// if auth is required, check if the user is logged in.
|
||||||
@ -12,19 +15,20 @@ export async function GET(req: Request, ctx: HandlerContext): Promise<Response>
|
|||||||
const login = ctx.state["login"];
|
const login = ctx.state["login"];
|
||||||
if (!login) {
|
if (!login) {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 401,
|
status: 302,
|
||||||
headers: {
|
headers: {
|
||||||
|
"Location": "/login",
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
|
"Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
|
||||||
"Access-Control-Allow-Headers": "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"
|
"Access-Control-Allow-Headers":
|
||||||
}
|
"Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const fileInfo = await Deno.stat(path);
|
const fileInfo = await Deno.stat(path);
|
||||||
if (fileInfo.isDirectory) {
|
if (fileInfo.isDirectory) {
|
||||||
// if index.html exists, serve it.
|
// if index.html exists, serve it.
|
||||||
@ -32,32 +36,38 @@ export async function GET(req: Request, ctx: HandlerContext): Promise<Response>
|
|||||||
const indexPath = path + "/index.html";
|
const indexPath = path + "/index.html";
|
||||||
try {
|
try {
|
||||||
await Deno.stat(indexPath);
|
await Deno.stat(indexPath);
|
||||||
const res = await serveFile(req, indexPath)
|
const res = await serveFile(req, indexPath);
|
||||||
return res;
|
return res;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Deno.errors.NotFound) {
|
if (e instanceof Deno.errors.NotFound) {
|
||||||
const list: Deno.DirEntry[] = []
|
const list: Deno.DirEntry[] = [];
|
||||||
for await (const entry of Deno.readDir(path)) {
|
for await (const entry of Deno.readDir(path)) {
|
||||||
list.push(entry);
|
list.push(entry);
|
||||||
}
|
}
|
||||||
return new Response(JSON.stringify(
|
return new Response(
|
||||||
list
|
JSON.stringify(
|
||||||
), {
|
list,
|
||||||
headers: { "content-type": "application/json",
|
),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
|
"Access-Control-Allow-Methods":
|
||||||
"Access-Control-Allow-Headers": "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"},
|
"GET,HEAD,PUT,PATCH,POST,DELETE",
|
||||||
|
"Access-Control-Allow-Headers":
|
||||||
|
"Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With",
|
||||||
|
},
|
||||||
status: 200,
|
status: 200,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const res = await serveFile(req, path, {
|
const res = await serveFile(req, path, {
|
||||||
fileInfo
|
fileInfo,
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
if (e instanceof Deno.errors.NotFound) {
|
if (e instanceof Deno.errors.NotFound) {
|
||||||
return new Response("Not Found", {
|
return new Response("Not Found", {
|
||||||
status: 404,
|
status: 404,
|
||||||
@ -67,8 +77,6 @@ export async function GET(req: Request, ctx: HandlerContext): Promise<Response>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const handler: Handlers = {
|
export const handler: Handlers = {
|
||||||
GET
|
GET,
|
||||||
}
|
};
|
||||||
|
@ -13,7 +13,8 @@ export default function Home() {
|
|||||||
alt="the fresh logo: a sliced lemon dripping with juice"
|
alt="the fresh logo: a sliced lemon dripping with juice"
|
||||||
/>
|
/>
|
||||||
<p class="my-6">
|
<p class="my-6">
|
||||||
This is a simple file server. It serves files from the <code>CWD</code>.
|
This is a simple file server. It serves files from the{" "}
|
||||||
|
<code>CWD</code>.
|
||||||
</p>
|
</p>
|
||||||
<a href="/dir/">Go To CWD</a> | <a href="/doc/">Doc</a>
|
<a href="/dir/">Go To CWD</a> | <a href="/doc/">Doc</a>
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
@ -15,21 +15,38 @@ export default function Login() {
|
|||||||
class="w-32 h-32"
|
class="w-32 h-32"
|
||||||
alt="the fresh logo: a sliced lemon dripping with juice"
|
alt="the fresh logo: a sliced lemon dripping with juice"
|
||||||
/>
|
/>
|
||||||
<form action="/api/login" method="POST" class="flex flex-col gap-2 items-stretch">
|
<form
|
||||||
|
action="/api/login"
|
||||||
|
method="POST"
|
||||||
|
class="flex flex-col gap-2 items-stretch"
|
||||||
|
>
|
||||||
<div class="flex gap-2 flex-wrap">
|
<div class="flex gap-2 flex-wrap">
|
||||||
<div class="basis-40 flex items-center flex-1">
|
<div class="basis-40 flex items-center flex-1">
|
||||||
<label for="username" class="w-20">Username</label>
|
<label for="username" class="w-20">Username</label>
|
||||||
<input type="text" name="username" id="username"
|
<input
|
||||||
class="border-b-2 focus:border-green-500 transition-colors flex-1" />
|
type="text"
|
||||||
|
name="username"
|
||||||
|
id="username"
|
||||||
|
class="border-b-2 focus:border-green-500 transition-colors flex-1"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center flex-1">
|
<div class="flex items-center flex-1">
|
||||||
<label for="password" class="w-20">Password</label>
|
<label for="password" class="w-20">Password</label>
|
||||||
<input type="password" name="password" id="password"
|
<input
|
||||||
class="border-b-2 focus:border-green-500 transition-colors flex-1" />
|
type="password"
|
||||||
|
name="password"
|
||||||
|
id="password"
|
||||||
|
class="border-b-2 focus:border-green-500 transition-colors flex-1"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="bg-gray-400 p-2 rounded
|
<button
|
||||||
m-auto">Login</button>
|
type="submit"
|
||||||
|
class="bg-gray-400 p-2 rounded
|
||||||
|
m-auto"
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
14
search.ts
14
search.ts
@ -10,7 +10,7 @@ export async function collectDocuments(path: string, out: string){
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function watchDocuments(doc: string, options?: {
|
export async function watchDocuments(doc: string, options?: {
|
||||||
abort?: AbortSignal,
|
abort?: AbortSignal;
|
||||||
}) {
|
}) {
|
||||||
const doc_dump = await loadDocuments(doc);
|
const doc_dump = await loadDocuments(doc);
|
||||||
const collector = new DocCollector({ summaryOnly: true, dropContent: true });
|
const collector = new DocCollector({ summaryOnly: true, dropContent: true });
|
||||||
@ -44,7 +44,9 @@ search.name("search")
|
|||||||
.command("collect", "Collect a document.")
|
.command("collect", "Collect a document.")
|
||||||
.description("Collect documents and index documents.")
|
.description("Collect documents and index documents.")
|
||||||
.arguments("<path:string>")
|
.arguments("<path:string>")
|
||||||
.option("-o, --out <path:string>", "out file to write the index to.", {default: "collected_docs.json"})
|
.option("-o, --out <path:string>", "out file to write the index to.", {
|
||||||
|
default: "collected_docs.json",
|
||||||
|
})
|
||||||
.action(async ({ out }, path: string) => {
|
.action(async ({ out }, path: string) => {
|
||||||
console.log("collecting", path);
|
console.log("collecting", path);
|
||||||
await collectDocuments(path, out);
|
await collectDocuments(path, out);
|
||||||
@ -52,7 +54,9 @@ search.name("search")
|
|||||||
.command("search", "Search for a document.")
|
.command("search", "Search for a document.")
|
||||||
.description("Search for a document.")
|
.description("Search for a document.")
|
||||||
.arguments("<query:string>")
|
.arguments("<query:string>")
|
||||||
.option("-d, --docs <path:string>", "collected document file to search", {default: "collected_docs.json"})
|
.option("-d, --docs <path:string>", "collected document file to search", {
|
||||||
|
default: "collected_docs.json",
|
||||||
|
})
|
||||||
.action(async ({ docs }, query: string) => {
|
.action(async ({ docs }, query: string) => {
|
||||||
console.log("searching", query);
|
console.log("searching", query);
|
||||||
const doc_dump = await loadDocuments(docs);
|
const doc_dump = await loadDocuments(docs);
|
||||||
@ -61,7 +65,9 @@ search.name("search")
|
|||||||
})
|
})
|
||||||
.command("interact", "watch the index and search for a document.")
|
.command("interact", "watch the index and search for a document.")
|
||||||
.description("watch the index and search for a document.")
|
.description("watch the index and search for a document.")
|
||||||
.option("-d, --doc <path:string>", "doc file to read the docs from.", {default: "collected_docs.json"})
|
.option("-d, --doc <path:string>", "doc file to read the docs from.", {
|
||||||
|
default: "collected_docs.json",
|
||||||
|
})
|
||||||
.action(async ({ doc }) => {
|
.action(async ({ doc }) => {
|
||||||
console.log("interacting");
|
console.log("interacting");
|
||||||
const index = await watchDocuments(doc);
|
const index = await watchDocuments(doc);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//// @deno-types="https://deno.land/x/fuse@v6.4.1/dist/fuse.d.ts"
|
//// @deno-types="https://deno.land/x/fuse@v6.4.1/dist/fuse.d.ts"
|
||||||
import Fuse from 'https://deno.land/x/fuse@v6.4.1/dist/fuse.esm.min.js';
|
import Fuse from "https://deno.land/x/fuse@v6.4.1/dist/fuse.esm.min.js";
|
||||||
import { Doc } from "./collect.ts";
|
import { Doc } from "./collect.ts";
|
||||||
|
|
||||||
export class Index {
|
export class Index {
|
||||||
@ -15,16 +15,23 @@ export class Index{
|
|||||||
|
|
||||||
public search(query: string): Doc[] {
|
public search(query: string): Doc[] {
|
||||||
return this.index.search(query, { limit: 10 }).map((
|
return this.index.search(query, { limit: 10 }).map((
|
||||||
result) =>
|
result,
|
||||||
result.item as Doc);
|
) => result.item as Doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createIndex(docs: Doc[]) {
|
public static createIndex(docs: Doc[]) {
|
||||||
const index = new Fuse(docs, {
|
const index = new Fuse(docs, {
|
||||||
keys: ["attributes.title", "attributes.japanese_title", "attributes.tags", "attributes.rjcode", "attributes.author", "path"],
|
keys: [
|
||||||
|
"attributes.title",
|
||||||
|
"attributes.japanese_title",
|
||||||
|
"attributes.tags",
|
||||||
|
"attributes.rjcode",
|
||||||
|
"attributes.author",
|
||||||
|
"path",
|
||||||
|
],
|
||||||
includeScore: true,
|
includeScore: true,
|
||||||
includeMatches: true,
|
includeMatches: true,
|
||||||
})
|
});
|
||||||
|
|
||||||
return new Index(index);
|
return new Index(index);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {join, extname, relative, basename} from "path/mod.ts";
|
import { basename, extname, join, relative } from "path/mod.ts";
|
||||||
import { readMarkdownDoc } from "./readDoc.ts";
|
import { readMarkdownDoc } from "./readDoc.ts";
|
||||||
import { Index } from "./client_search.ts";
|
import { Index } from "./client_search.ts";
|
||||||
|
|
||||||
@ -60,19 +60,19 @@ export class DocCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fileList.some((entry) => entry.name === "SUMMARY.md")) {
|
if (fileList.some((entry) => entry.name === "SUMMARY.md")) {
|
||||||
const {content, metadata} = await readMarkdownDoc(join(path, "SUMMARY.md"));
|
const { content, metadata } = await readMarkdownDoc(
|
||||||
|
join(path, "SUMMARY.md"),
|
||||||
|
);
|
||||||
this.setDoc({
|
this.setDoc({
|
||||||
path: join(path, "SUMMARY.md"),
|
path: join(path, "SUMMARY.md"),
|
||||||
content: content,
|
content: content,
|
||||||
attributes: metadata,
|
attributes: metadata,
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
for (const entry of fileList) {
|
for (const entry of fileList) {
|
||||||
if (entry.isDirectory) {
|
if (entry.isDirectory) {
|
||||||
await this.walkDir(join(path, entry.name));
|
await this.walkDir(join(path, entry.name));
|
||||||
}
|
} else if (entry.isFile && !this.options.summaryOnly) {
|
||||||
else if (entry.isFile && !this.options.summaryOnly){
|
|
||||||
const doc = await this.readDoc(join(path, entry.name));
|
const doc = await this.readDoc(join(path, entry.name));
|
||||||
this.setDoc(doc);
|
this.setDoc(doc);
|
||||||
}
|
}
|
||||||
@ -84,19 +84,16 @@ export class DocCollector {
|
|||||||
const ext = extname(path);
|
const ext = extname(path);
|
||||||
if (ext === ".md") {
|
if (ext === ".md") {
|
||||||
return await this.readMarkdown(path);
|
return await this.readMarkdown(path);
|
||||||
}
|
} else if (ext === ".html" || ext === ".htm" || ext === ".xhtml") {
|
||||||
else if (ext === ".html" || ext === ".htm" || ext === ".xhtml"){
|
|
||||||
return await this.readHTML(path);
|
return await this.readHTML(path);
|
||||||
}
|
} else if (ext === ".txt") {
|
||||||
else if (ext === ".txt"){
|
|
||||||
return await this.readText(path);
|
return await this.readText(path);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return {
|
return {
|
||||||
path: path,
|
path: path,
|
||||||
content: "",
|
content: "",
|
||||||
attributes: {}
|
attributes: {},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +103,7 @@ export class DocCollector {
|
|||||||
path: path,
|
path: path,
|
||||||
content: content,
|
content: content,
|
||||||
attributes: {},
|
attributes: {},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
public async readText(path: string): Promise<Doc> {
|
public async readText(path: string): Promise<Doc> {
|
||||||
const content = await Deno.readTextFile(path);
|
const content = await Deno.readTextFile(path);
|
||||||
@ -114,7 +111,7 @@ export class DocCollector {
|
|||||||
path: path,
|
path: path,
|
||||||
content: content,
|
content: content,
|
||||||
attributes: {},
|
attributes: {},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
public async readMarkdown(path: string): Promise<Doc> {
|
public async readMarkdown(path: string): Promise<Doc> {
|
||||||
const { content, metadata } = await readMarkdownDoc(path);
|
const { content, metadata } = await readMarkdownDoc(path);
|
||||||
@ -123,7 +120,7 @@ export class DocCollector {
|
|||||||
path: path,
|
path: path,
|
||||||
content: content,
|
content: content,
|
||||||
attributes: metadata,
|
attributes: metadata,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
async watchDir(path: string, {
|
async watchDir(path: string, {
|
||||||
onRemove = (_path: string) => {},
|
onRemove = (_path: string) => {},
|
||||||
@ -131,10 +128,10 @@ export class DocCollector {
|
|||||||
onChange = (_doc: Doc) => {},
|
onChange = (_doc: Doc) => {},
|
||||||
abort = undefined,
|
abort = undefined,
|
||||||
}: {
|
}: {
|
||||||
onRemove?: (path: string) => void | Promise<void>,
|
onRemove?: (path: string) => void | Promise<void>;
|
||||||
onAdd?: (doc: Doc) => void | Promise<void>,
|
onAdd?: (doc: Doc) => void | Promise<void>;
|
||||||
onChange?: (doc: Doc) => void | Promise<void>,
|
onChange?: (doc: Doc) => void | Promise<void>;
|
||||||
abort?: AbortSignal,
|
abort?: AbortSignal;
|
||||||
}) {
|
}) {
|
||||||
const watcher = Deno.watchFs(path);
|
const watcher = Deno.watchFs(path);
|
||||||
if (abort) {
|
if (abort) {
|
||||||
@ -143,7 +140,10 @@ export class DocCollector {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
for await (const event of watcher) {
|
for await (const event of watcher) {
|
||||||
if (event.kind === "access" || event.kind === "other" || event.kind === "any"){
|
if (
|
||||||
|
event.kind === "access" || event.kind === "other" ||
|
||||||
|
event.kind === "any"
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (event.paths.length === 0) {
|
if (event.paths.length === 0) {
|
||||||
@ -156,8 +156,7 @@ export class DocCollector {
|
|||||||
if (event.kind === "remove") {
|
if (event.kind === "remove") {
|
||||||
this.doc_map.delete(relpath);
|
this.doc_map.delete(relpath);
|
||||||
await onRemove(relpath);
|
await onRemove(relpath);
|
||||||
}
|
} else if (event.kind === "create" || event.kind === "modify") {
|
||||||
else if (event.kind === "create" || event.kind === "modify"){
|
|
||||||
const { content, metadata } = await readMarkdownDoc(relpath);
|
const { content, metadata } = await readMarkdownDoc(relpath);
|
||||||
const doc = {
|
const doc = {
|
||||||
path: relpath,
|
path: relpath,
|
||||||
@ -167,8 +166,7 @@ export class DocCollector {
|
|||||||
this.setDoc(doc);
|
this.setDoc(doc);
|
||||||
if (event.kind === "create") {
|
if (event.kind === "create") {
|
||||||
await onAdd(doc);
|
await onAdd(doc);
|
||||||
}
|
} else if (event.kind === "modify") {
|
||||||
else if (event.kind === "modify"){
|
|
||||||
await onChange(doc);
|
await onChange(doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,9 +176,9 @@ export class DocCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
makeIndex(options?: {
|
makeIndex(options?: {
|
||||||
onUpdate?: (() => void) | (() => Promise<void>),
|
onUpdate?: (() => void) | (() => Promise<void>);
|
||||||
abort?: AbortSignal,
|
abort?: AbortSignal;
|
||||||
watch?: boolean,
|
watch?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const opt = options ?? {};
|
const opt = options ?? {};
|
||||||
const index = Index.createIndex(this.getDocs());
|
const index = Index.createIndex(this.getDocs());
|
||||||
@ -192,7 +190,7 @@ export class DocCollector {
|
|||||||
if (opt.onUpdate) {
|
if (opt.onUpdate) {
|
||||||
await opt.onUpdate();
|
await opt.onUpdate();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
this.watchDir(".", {
|
this.watchDir(".", {
|
||||||
async onAdd(_doc) {
|
async onAdd(_doc) {
|
||||||
await update();
|
await update();
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
const ICON_MAP: Record<string, string> = {
|
const ICON_MAP: Record<string, string> = {
|
||||||
".pdf": "file-pdf",
|
".pdf": "file-pdf",
|
||||||
".zip": "file-zip",
|
".zip": "file-zip",
|
||||||
@ -53,7 +52,7 @@ const ICON_MAP: Record<string, string> = {
|
|||||||
".tif": "file-image",
|
".tif": "file-image",
|
||||||
".webp": "file-image",
|
".webp": "file-image",
|
||||||
".psd": "file-image",
|
".psd": "file-image",
|
||||||
}
|
};
|
||||||
|
|
||||||
export function extToIcon(s: string): string {
|
export function extToIcon(s: string): string {
|
||||||
return `/icon/${(ICON_MAP[s] ?? "file")}.svg`;
|
return `/icon/${(ICON_MAP[s] ?? "file")}.svg`;
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import {parse as parseYaml, stringify} from "https://deno.land/std@0.170.0/encoding/yaml.ts";
|
import {
|
||||||
|
parse as parseYaml,
|
||||||
|
stringify,
|
||||||
|
} from "https://deno.land/std@0.170.0/encoding/yaml.ts";
|
||||||
|
|
||||||
function trimSubstring(str: string, start: number, end: number) {
|
function trimSubstring(str: string, start: number, end: number) {
|
||||||
while (str[start] === ' ' || str[start] === '\t' || str[start] === '\r' || str[start] === '\n') {
|
while (
|
||||||
|
str[start] === " " || str[start] === "\t" || str[start] === "\r" ||
|
||||||
|
str[start] === "\n"
|
||||||
|
) {
|
||||||
start++;
|
start++;
|
||||||
}
|
}
|
||||||
return str.substring(start, end);
|
return str.substring(start, end);
|
||||||
@ -13,24 +19,23 @@ interface Doc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parse(content: string): Doc {
|
export function parse(content: string): Doc {
|
||||||
if(!content.startsWith('---')){
|
if (!content.startsWith("---")) {
|
||||||
return {
|
return {
|
||||||
metadata: {},
|
metadata: {},
|
||||||
content: content,
|
content: content,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
const index = content.indexOf("\n---", 3);
|
||||||
const index = content.indexOf('\n---', 3);
|
|
||||||
const meta = content.substring(3, index);
|
const meta = content.substring(3, index);
|
||||||
const c = trimSubstring(content, index + 4, content.length);
|
const c = trimSubstring(content, index + 4, content.length);
|
||||||
const metadata = parseYaml(meta) as Record<string, string | string[]>;
|
const metadata = parseYaml(meta) as Record<string, string | string[]>;
|
||||||
return {
|
return {
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
content: c,
|
content: c,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readMarkdownDoc(path: string): Promise<Doc> {
|
export async function readMarkdownDoc(path: string): Promise<Doc> {
|
||||||
const doc = await Deno.readTextFile(path);
|
const doc = await Deno.readTextFile(path);
|
||||||
return parse(doc);
|
return parse(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { Index } from "../client_search.ts";
|
import { Index } from "../client_search.ts";
|
||||||
import { Doc, DocCollector } from "../collect.ts";
|
import { Doc, DocCollector } from "../collect.ts";
|
||||||
|
|
||||||
|
|
||||||
export const docCollector = new DocCollector(
|
export const docCollector = new DocCollector(
|
||||||
{
|
{
|
||||||
dropContent: true,
|
dropContent: true,
|
||||||
summaryOnly: true,
|
summaryOnly: true,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export let docIndex: Index | undefined = undefined;
|
export let docIndex: Index | undefined = undefined;
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export async function prepareDocs() {
|
|||||||
if (!docPath) {
|
if (!docPath) {
|
||||||
await docCollector.walkDir(".");
|
await docCollector.walkDir(".");
|
||||||
docIndex = docCollector.makeIndex({
|
docIndex = docCollector.makeIndex({
|
||||||
watch: true
|
watch: true,
|
||||||
});
|
});
|
||||||
return docIndex;
|
return docIndex;
|
||||||
}
|
}
|
||||||
@ -28,8 +28,7 @@ export async function prepareDocs() {
|
|||||||
if (error instanceof Deno.errors.NotFound) {
|
if (error instanceof Deno.errors.NotFound) {
|
||||||
await docCollector.walkDir(".");
|
await docCollector.walkDir(".");
|
||||||
await Deno.writeTextFile(docPath, JSON.stringify(docCollector.getDocs()));
|
await Deno.writeTextFile(docPath, JSON.stringify(docCollector.getDocs()));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +37,7 @@ export async function prepareDocs() {
|
|||||||
watch: true,
|
watch: true,
|
||||||
onUpdate: async () => {
|
onUpdate: async () => {
|
||||||
await Deno.writeTextFile(docPath, JSON.stringify(docCollector.getDocs()));
|
await Deno.writeTextFile(docPath, JSON.stringify(docCollector.getDocs()));
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
return docIndex;
|
return docIndex;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {DB} from 'sqlite';
|
import { DB } from "sqlite";
|
||||||
import {createSchema} from './user.ts';
|
import { createSchema } from "./user.ts";
|
||||||
|
|
||||||
export function connectDB(): DB {
|
export function connectDB(): DB {
|
||||||
let DB_path = Deno.env.get("DB_PATH");
|
let DB_path = Deno.env.get("DB_PATH");
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {genSalt, hash, compare} from "bcrypt";
|
import { compare, genSalt, hash } from "bcrypt";
|
||||||
import { DB } from "sqlite";
|
import { DB } from "sqlite";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
@ -13,8 +13,8 @@ export async function createUser(name: string, password: string){
|
|||||||
const user: User = {
|
const user: User = {
|
||||||
name: name,
|
name: name,
|
||||||
salted_password: salted_password,
|
salted_password: salted_password,
|
||||||
salt: salt
|
salt: salt,
|
||||||
}
|
};
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,8 +23,9 @@ export async function verifyUser(user: User, password: string){
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllUsers(db: DB): Promise<User[]> {
|
export async function getAllUsers(db: DB): Promise<User[]> {
|
||||||
const users = await db.query<[string, string, string]>
|
const users = await db.query<[string, string, string]>(
|
||||||
("SELECT name, salted_password, salt FROM users");
|
"SELECT name, salted_password, salt FROM users",
|
||||||
|
);
|
||||||
return users.map(([name, salted_password, salt]) => ({
|
return users.map(([name, salted_password, salt]) => ({
|
||||||
name,
|
name,
|
||||||
salted_password,
|
salted_password,
|
||||||
@ -33,8 +34,10 @@ export async function getAllUsers(db :DB): Promise<User[]>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getUser(db: DB, name: string): Promise<User | undefined> {
|
export async function getUser(db: DB, name: string): Promise<User | undefined> {
|
||||||
const users = await db.query<[string, string, string]>
|
const users = await db.query<[string, string, string]>(
|
||||||
("SELECT name, salted_password, salt FROM users WHERE name = ?", [name]);
|
"SELECT name, salted_password, salt FROM users WHERE name = ?",
|
||||||
|
[name],
|
||||||
|
);
|
||||||
if (users === undefined || users.length === 0) {
|
if (users === undefined || users.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -43,17 +46,21 @@ export async function getUser(db: DB, name: string): Promise<User | undefined>{
|
|||||||
name: user[0],
|
name: user[0],
|
||||||
salted_password: user[1],
|
salted_password: user[1],
|
||||||
salt: user[2],
|
salt: user[2],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addUser(db: DB, user: User) {
|
export async function addUser(db: DB, user: User) {
|
||||||
await db.query("INSERT INTO users (name, salted_password, salt) VALUES (?, ?, ?)",
|
await db.query(
|
||||||
[user.name, user.salted_password, user.salt]);
|
"INSERT INTO users (name, salted_password, salt) VALUES (?, ?, ?)",
|
||||||
|
[user.name, user.salted_password, user.salt],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateUser(db: DB, user: User) {
|
export async function updateUser(db: DB, user: User) {
|
||||||
await db.query("UPDATE users SET salted_password = ?, salt = ? WHERE name = ?",
|
await db.query(
|
||||||
[user.salted_password, user.salt, user.name]);
|
"UPDATE users SET salted_password = ?, salt = ? WHERE name = ?",
|
||||||
|
[user.salted_password, user.salt, user.name],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteUser(db: DB, name: string) {
|
export async function deleteUser(db: DB, name: string) {
|
||||||
@ -61,5 +68,7 @@ export async function deleteUser(db: DB, name: string){
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function createSchema(db: DB) {
|
export async function createSchema(db: DB) {
|
||||||
await db.query("CREATE TABLE IF NOT EXISTS users (name TEXT PRIMARY KEY, salted_password TEXT, salt TEXT)");
|
await db.query(
|
||||||
|
"CREATE TABLE IF NOT EXISTS users (name TEXT PRIMARY KEY, salted_password TEXT, salt TEXT)",
|
||||||
|
);
|
||||||
}
|
}
|
@ -5,4 +5,4 @@ module.exports = {
|
|||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
title: "hello"
|
title: "hello"
|
||||||
tags: ["asdf","wer"]
|
tags: ["asdf","wer"]
|
||||||
---
|
---
|
||||||
|
|
||||||
# hello
|
# hello
|
||||||
|
|
||||||
- [hello](hello.md)
|
- [hello](hello.md)
|
||||||
|
@ -3,8 +3,12 @@ rjcode: RJ130512
|
|||||||
title: Summary of the 13th Meeting of the Joint Committee on the Safety of Nuclear Installations
|
title: Summary of the 13th Meeting of the Joint Committee on the Safety of Nuclear Installations
|
||||||
tags: ["summary", "meeting", "joint committee", "safety", "nuclear installations"]
|
tags: ["summary", "meeting", "joint committee", "safety", "nuclear installations"]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Summary of the 13th Meeting of the Joint Committee on the Safety of Nuclear Installations
|
# Summary of the 13th Meeting of the Joint Committee on the Safety of Nuclear Installations
|
||||||
|
|
||||||
## 1. Opening of the meeting
|
## 1. Opening of the meeting
|
||||||
|
|
||||||
The 13th meeting of the Joint Committee on the Safety of Nuclear Installations (JCSNI) was held in Vienna on 12 May 2013. The meeting was chaired by Mr. J. M. Sánchez, Director of the Nuclear Safety Department of the Spanish Ministry of Industry, Energy and Tourism, and the Vice-Chairman of the JCSNI.
|
The 13th meeting of the Joint Committee on the Safety of Nuclear Installations
|
||||||
|
(JCSNI) was held in Vienna on 12 May 2013. The meeting was chaired by Mr. J. M.
|
||||||
|
Sánchez, Director of the Nuclear Safety Department of the Spanish Ministry of
|
||||||
|
Industry, Energy and Tourism, and the Vice-Chairman of the JCSNI.
|
||||||
|
4
test_data/한글/SUMMARY.md
Normal file
4
test_data/한글/SUMMARY.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "SUMMARY"
|
||||||
|
tags: ["SUMMARY"]
|
||||||
|
---
|
9
user.ts
9
user.ts
@ -1,4 +1,8 @@
|
|||||||
import { Command, Input, Secret } from "https://deno.land/x/cliffy@v0.25.6/mod.ts";
|
import {
|
||||||
|
Command,
|
||||||
|
Input,
|
||||||
|
Secret,
|
||||||
|
} from "https://deno.land/x/cliffy@v0.25.6/mod.ts";
|
||||||
import { connectDB } from "./src/user/db.ts";
|
import { connectDB } from "./src/user/db.ts";
|
||||||
import * as users from "./src/user/user.ts";
|
import * as users from "./src/user/user.ts";
|
||||||
|
|
||||||
@ -8,8 +12,7 @@ export const user_command = new Command()
|
|||||||
.arguments("[username:string]")
|
.arguments("[username:string]")
|
||||||
.option("-p, --password <password:string>", "The password for the user.")
|
.option("-p, --password <password:string>", "The password for the user.")
|
||||||
.option("-q, --quiet", "quiet output.")
|
.option("-q, --quiet", "quiet output.")
|
||||||
.action(async ({quiet, password}
|
.action(async ({ quiet, password }, username) => {
|
||||||
, username) => {
|
|
||||||
if (username === undefined) {
|
if (username === undefined) {
|
||||||
username = await Input.prompt("Username: ");
|
username = await Input.prompt("Username: ");
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export async function generateSecretKey() {
|
export async function generateSecretKey() {
|
||||||
const key = await crypto.subtle.generateKey(
|
const key = await crypto.subtle.generateKey(
|
||||||
{ name: "HMAC", hash: "SHA-512" },
|
{ name: "HMAC", hash: "SHA-512" },
|
||||||
@ -14,12 +13,16 @@ export async function prepareSecretKey(){
|
|||||||
if (key) {
|
if (key) {
|
||||||
const jwk = JSON.parse(key) as JsonWebKey;
|
const jwk = JSON.parse(key) as JsonWebKey;
|
||||||
{
|
{
|
||||||
const key = await crypto.subtle.importKey("jwk", jwk,
|
const key = await crypto.subtle.importKey(
|
||||||
{ name: "HMAC", hash: "SHA-512" }, true, ["sign", "verify"]);
|
"jwk",
|
||||||
|
jwk,
|
||||||
|
{ name: "HMAC", hash: "SHA-512" },
|
||||||
|
true,
|
||||||
|
["sign", "verify"],
|
||||||
|
);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const key = await generateSecretKey();
|
const key = await generateSecretKey();
|
||||||
const out = await crypto.subtle.exportKey("jwk", key);
|
const out = await crypto.subtle.exportKey("jwk", key);
|
||||||
Deno.env.set("SECRET_KEY", JSON.stringify(out));
|
Deno.env.set("SECRET_KEY", JSON.stringify(out));
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
export function removePrefixFromPathname(pathname: string, prefix: string): string {
|
export function removePrefixFromPathname(
|
||||||
|
pathname: string,
|
||||||
|
prefix: string,
|
||||||
|
): string {
|
||||||
let ret = pathname;
|
let ret = pathname;
|
||||||
ret = ret.slice(prefix.length);
|
ret = ret.slice(prefix.length);
|
||||||
if (ret.startsWith("/")) {
|
if (ret.startsWith("/")) {
|
||||||
|
Loading…
Reference in New Issue
Block a user