feat: implement auth
This commit is contained in:
parent
7901dd3f1f
commit
7f7d77749e
12
components/Button.tsx
Normal file
12
components/Button.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { JSX } from "preact";
|
||||||
|
|
||||||
|
export default function Button(props: JSX.HTMLAttributes<HTMLButtonElement>) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
{...props}
|
||||||
|
class={`px-3 py-2 bg-white rounded border(gray-500 2) hover:bg-gray-200 active:bg-gray-300 disabled:(opacity-50 cursor-not-allowed) ${
|
||||||
|
props.class ?? ""
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
12
components/Center.tsx
Normal file
12
components/Center.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { JSX } from "preact";
|
||||||
|
|
||||||
|
export default function Center(props: JSX.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
class={`flex items-center justify-center h-full w-full ${
|
||||||
|
props.class ?? ""
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
12
components/Input.tsx
Normal file
12
components/Input.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { JSX } from "preact";
|
||||||
|
|
||||||
|
export default function Input(props: JSX.HTMLAttributes<HTMLInputElement>) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
{...props}
|
||||||
|
class={`px-3 py-2 bg-white rounded border(gray-500 2) disabled:(opacity-50 cursor-not-allowed) ${
|
||||||
|
props.class ?? ""
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -76,6 +76,9 @@
|
|||||||
"https://deno.land/std@0.178.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
|
"https://deno.land/std@0.178.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
|
||||||
"https://deno.land/std@0.178.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba",
|
"https://deno.land/std@0.178.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba",
|
||||||
"https://deno.land/std@0.178.0/semver/mod.ts": "409a2691f5a411c34e917c1e6d445a6d1d53f3fadf660e44a99dd0bf9b2ef412",
|
"https://deno.land/std@0.178.0/semver/mod.ts": "409a2691f5a411c34e917c1e6d445a6d1d53f3fadf660e44a99dd0bf9b2ef412",
|
||||||
|
"https://deno.land/std@0.182.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462",
|
||||||
|
"https://deno.land/std@0.182.0/datetime/to_imf.ts": "8f9c0af8b167031ffe2e03da01a12a3b0672cc7562f89c61942a0ab0129771b2",
|
||||||
|
"https://deno.land/std@0.182.0/http/cookie.ts": "934f92d871d50852dbd7a836d721df5a9527b14381db16001b40991d30174ee4",
|
||||||
"https://deno.land/x/code_block_writer@11.0.3/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5",
|
"https://deno.land/x/code_block_writer@11.0.3/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5",
|
||||||
"https://deno.land/x/code_block_writer@11.0.3/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff",
|
"https://deno.land/x/code_block_writer@11.0.3/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff",
|
||||||
"https://deno.land/x/denoflate@1.2.1/mod.ts": "f5628e44b80b3d80ed525afa2ba0f12408e3849db817d47a883b801f9ce69dd6",
|
"https://deno.land/x/denoflate@1.2.1/mod.ts": "f5628e44b80b3d80ed525afa2ba0f12408e3849db817d47a883b801f9ce69dd6",
|
||||||
|
@ -4,10 +4,14 @@
|
|||||||
|
|
||||||
import config from "./deno.json" assert { type: "json" };
|
import config from "./deno.json" assert { type: "json" };
|
||||||
import * as $0 from "./routes/index.tsx";
|
import * as $0 from "./routes/index.tsx";
|
||||||
|
import * as $1 from "./routes/library.tsx";
|
||||||
|
import * as $2 from "./routes/login.tsx";
|
||||||
|
|
||||||
const manifest = {
|
const manifest = {
|
||||||
routes: {
|
routes: {
|
||||||
"./routes/index.tsx": $0,
|
"./routes/index.tsx": $0,
|
||||||
|
"./routes/library.tsx": $1,
|
||||||
|
"./routes/login.tsx": $2,
|
||||||
},
|
},
|
||||||
islands: {},
|
islands: {},
|
||||||
baseUrl: import.meta.url,
|
baseUrl: import.meta.url,
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"@preact/signals": "https://esm.sh/*@preact/signals@1.1.3",
|
"@preact/signals": "https://esm.sh/*@preact/signals@1.1.3",
|
||||||
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.2.3",
|
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.2.3",
|
||||||
"twind": "https://esm.sh/twind@0.16.19",
|
"twind": "https://esm.sh/twind@0.16.19",
|
||||||
"twind/": "https://esm.sh/twind@0.16.19/"
|
"twind/": "https://esm.sh/twind@0.16.19/",
|
||||||
|
"$std/": "https://deno.land/std@0.182.0/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,19 @@
|
|||||||
import { Head } from "$fresh/runtime.ts";
|
import { getCookies } from "$std/http/cookie.ts";
|
||||||
|
|
||||||
export default function Home() {
|
export function handler(req: Request): Response {
|
||||||
return (
|
const cookies = getCookies(req.headers);
|
||||||
<>
|
const username = cookies["username"];
|
||||||
<Head>
|
const password = cookies["password"];
|
||||||
<title>Fresh App</title>
|
|
||||||
</Head>
|
if (username != null && password != null) {
|
||||||
<div class="p-4 mx-auto max-w-screen-md">
|
return new Response("", {
|
||||||
<img
|
status: 303,
|
||||||
src="/logo.svg"
|
headers: { location: "/library" },
|
||||||
class="w-32 h-32"
|
});
|
||||||
alt="the fresh logo: a sliced lemon dripping with juice"
|
} else {
|
||||||
/>
|
return new Response("", {
|
||||||
<p class="my-6">
|
status: 303,
|
||||||
Welcome to `fresh`. Try updating this message in the ./routes/index.tsx
|
headers: { location: "/login" },
|
||||||
file, and refresh.
|
});
|
||||||
</p>
|
}
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
37
routes/library.tsx
Normal file
37
routes/library.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||||
|
import { getCookies } from "$std/http/cookie.ts";
|
||||||
|
import { Head } from "$fresh/runtime.ts";
|
||||||
|
|
||||||
|
interface Data {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handler: Handlers<Data> = {
|
||||||
|
GET(req, ctx) {
|
||||||
|
const cookies = getCookies(req.headers);
|
||||||
|
const username = cookies["username"];
|
||||||
|
const password = cookies["password"];
|
||||||
|
|
||||||
|
if (username && password) {
|
||||||
|
return ctx.render({ username, password });
|
||||||
|
} else {
|
||||||
|
return new Response("", {
|
||||||
|
status: 303,
|
||||||
|
headers: { location: "/login" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LibraryPage({ data }: PageProps<Data>) {
|
||||||
|
const { username, password } = data;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>musikspider</title>
|
||||||
|
</Head>
|
||||||
|
<p>Library {username} {password}</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
74
routes/login.tsx
Normal file
74
routes/login.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { setCookie } from "$std/http/cookie.ts";
|
||||||
|
import { Handlers } from "$fresh/server.ts";
|
||||||
|
import { Head } from "$fresh/runtime.ts";
|
||||||
|
|
||||||
|
import Button from "../components/Button.tsx";
|
||||||
|
import Input from "../components/Input.tsx";
|
||||||
|
|
||||||
|
const LOGIN_MAX_AGE = 60 * 60 * 24 * 7;
|
||||||
|
|
||||||
|
export const handler: Handlers = {
|
||||||
|
GET(_, ctx) {
|
||||||
|
return ctx.render();
|
||||||
|
},
|
||||||
|
async POST(req, _) {
|
||||||
|
const form = await req.formData();
|
||||||
|
const username = form.get("username")!.toString();
|
||||||
|
const password = form.get("password")!.toString();
|
||||||
|
|
||||||
|
const response = new Response("", {
|
||||||
|
status: 303,
|
||||||
|
headers: { location: "/library" },
|
||||||
|
});
|
||||||
|
setCookie(response.headers, {
|
||||||
|
name: "username",
|
||||||
|
value: username,
|
||||||
|
maxAge: LOGIN_MAX_AGE,
|
||||||
|
httpOnly: true,
|
||||||
|
});
|
||||||
|
setCookie(response.headers, {
|
||||||
|
name: "password",
|
||||||
|
value: password,
|
||||||
|
maxAge: LOGIN_MAX_AGE,
|
||||||
|
httpOnly: true,
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LoginPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>musikspider</title>
|
||||||
|
</Head>
|
||||||
|
<div class="flex items-center justify-center p-4 mx-auto max-w-screen-md h-screen">
|
||||||
|
<form method="POST">
|
||||||
|
<div class="flex flex-col gap-2 max-w-xs">
|
||||||
|
<div class="flex gap-4 place-items-center">
|
||||||
|
<label for="username" class="w-1/3">username</label>
|
||||||
|
<Input
|
||||||
|
class="w-2/3"
|
||||||
|
id="username"
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-4 place-items-center">
|
||||||
|
<label for="password" class="w-1/3">password</label>
|
||||||
|
<Input
|
||||||
|
class="w-2/3"
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button class="w-min" type="submit">login</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user