Closer
This commit is contained in:
parent
34b440213f
commit
c771628263
|
@ -40,7 +40,7 @@ const tables: Record<string, TableSpec> = {
|
||||||
...timestamps,
|
...timestamps,
|
||||||
],
|
],
|
||||||
additionalTableStatements: [
|
additionalTableStatements: [
|
||||||
"constraint valid_username check (username ~* '^[a-z\d\\-_]{2,38}$')",
|
"constraint valid_username check (username ~* '^[a-z\\d\\-_]{2,38}$')",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"user_token": {
|
"user_token": {
|
||||||
|
|
|
@ -8,9 +8,8 @@ interface MyAppProps extends AppProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handler: Handlers<unknown, ContextState> = {
|
export const handler: Handlers<unknown, ContextState> = {
|
||||||
async GET(_request: Request, context) {
|
async GET(request: Request, context) {
|
||||||
const user: Partial<ContextState["user"]> = context.state.user;
|
console.log("AppHandler:", request, context);
|
||||||
if (user && "passwordDigest" in user) delete user.passwordDigest;
|
|
||||||
return await context.render(context.state.user);
|
return await context.render(context.state.user);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
import { MiddlewareHandlerContext } from "$fresh/server.ts";
|
import { MiddlewareHandlerContext } from "$fresh/server.ts";
|
||||||
import { deleteCookie, getCookies } from "$std/http/cookie.ts";
|
import { deleteCookie, getCookies } from "$std/http/cookie.ts";
|
||||||
import { getUserFromNonExpiredLoginToken } from "@/db/mod.ts";
|
import { getUserFromNonExpiredLoginToken } from "@/db/mod.ts";
|
||||||
import { type ContextState } from "@/types.ts";
|
import { type ContextState, type PublicUser, type User } from "@/types.ts";
|
||||||
|
|
||||||
|
function toPublicUser(user: User): PublicUser {
|
||||||
|
const {
|
||||||
|
createdAt: _createdAt,
|
||||||
|
updatedAt: _updatedAt,
|
||||||
|
passwordDigest: _passwordDigest,
|
||||||
|
...publicUser
|
||||||
|
} = user;
|
||||||
|
return publicUser;
|
||||||
|
}
|
||||||
|
|
||||||
async function currentUser(
|
async function currentUser(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
@ -14,10 +24,7 @@ async function currentUser(
|
||||||
const user = await getUserFromNonExpiredLoginToken(lsauth);
|
const user = await getUserFromNonExpiredLoginToken(lsauth);
|
||||||
if (!user) hasBadAuthCookie = true;
|
if (!user) hasBadAuthCookie = true;
|
||||||
else {
|
else {
|
||||||
context.state.user = user;
|
context.state.user = toPublicUser(user);
|
||||||
delete context.state.user.createdAt;
|
|
||||||
delete context.state.user.updatedAt;
|
|
||||||
delete context.state.user.passwordDigest;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const resp = await context.next();
|
const resp = await context.next();
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import Counter from "@/islands/Counter.tsx";
|
import Counter from "@/islands/Counter.tsx";
|
||||||
import { Page } from "@/components/Page.tsx";
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<>
|
||||||
<img
|
<img
|
||||||
src="/logo.svg"
|
src="/logo.svg"
|
||||||
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"
|
||||||
/>
|
/>
|
||||||
<Counter start={3} />
|
<Counter start={3} />
|
||||||
</Page>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { HandlerContext, Handlers, PageProps } from "$fresh/server.ts";
|
import { HandlerContext, Handlers, PageProps } from "$fresh/server.ts";
|
||||||
import { compare } from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
|
import { compare } from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
|
||||||
import { Page } from "@/components/Page.tsx";
|
|
||||||
import { createToken, getUser } from "@/db/mod.ts";
|
import { createToken, getUser } from "@/db/mod.ts";
|
||||||
import * as base64 from "$std/encoding/base64.ts";
|
import * as base64 from "$std/encoding/base64.ts";
|
||||||
import { setCookie } from "$std/http/cookie.ts";
|
import { setCookie } from "$std/http/cookie.ts";
|
||||||
|
@ -58,18 +57,16 @@ export default function Login({ data }: PageProps) {
|
||||||
|
|
||||||
function LoginSuccessful(_userId: UserID) {
|
function LoginSuccessful(_userId: UserID) {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<p>
|
||||||
<p>
|
You are now logged in. Let's go to your{" "}
|
||||||
You are now logged in. Let's go to your{" "}
|
<a href="/dashboard">dashboard</a>!
|
||||||
<a href="/dashboard">dashboard</a>!
|
</p>
|
||||||
</p>
|
|
||||||
</Page>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LoginForm(props?: LoginError | null) {
|
function LoginForm(props?: LoginError | null) {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<form class="flex flex-col max-w-lg" method="post">
|
||||||
{props != null &&
|
{props != null &&
|
||||||
(
|
(
|
||||||
<p class="text-red-500">
|
<p class="text-red-500">
|
||||||
|
@ -79,13 +76,11 @@ function LoginForm(props?: LoginError | null) {
|
||||||
<h1 class="text-4xl mb-4 outline-white">
|
<h1 class="text-4xl mb-4 outline-white">
|
||||||
Log in to your account
|
Log in to your account
|
||||||
</h1>
|
</h1>
|
||||||
<form class="flex flex-col max-w-lg" method="post">
|
<label for="username">Username</label>
|
||||||
<label for="username">Username</label>
|
<input type="text" name="username" />
|
||||||
<input type="text" name="username" />
|
<label for="password">Password</label>
|
||||||
<label for="password">Password</label>
|
<input type="password" name="password" />
|
||||||
<input type="password" name="password" />
|
<input class="mt-2" type="submit" value="Login" />
|
||||||
<input class="mt-2" type="submit" value="Login" />
|
</form>
|
||||||
</form>
|
|
||||||
</Page>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||||
import { listNotes } from "@/db/mod.ts";
|
import { listNotes } from "@/db/mod.ts";
|
||||||
import { Page } from "@/components/Page.tsx";
|
|
||||||
import { type Note } from "@/types.ts";
|
import { type Note } from "@/types.ts";
|
||||||
import { NoteItem } from "@/components/Note.tsx";
|
import { NoteItem } from "@/components/Note.tsx";
|
||||||
|
|
||||||
|
@ -12,7 +11,7 @@ export const handler: Handlers<Note[]> = {
|
||||||
|
|
||||||
export default function NotesPage({ data: notes }: PageProps<Note[]>) {
|
export default function NotesPage({ data: notes }: PageProps<Note[]>) {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<>
|
||||||
<h1>List of Notes</h1>
|
<h1>List of Notes</h1>
|
||||||
<p>Create a note:</p>
|
<p>Create a note:</p>
|
||||||
<form class="flex flex-col" action="./note/create" method="POST">
|
<form class="flex flex-col" action="./note/create" method="POST">
|
||||||
|
@ -21,6 +20,6 @@ export default function NotesPage({ data: notes }: PageProps<Note[]>) {
|
||||||
<input class="mt-2" type="submit" value="Post" />
|
<input class="mt-2" type="submit" value="Post" />
|
||||||
</form>
|
</form>
|
||||||
{notes.map(NoteItem)}
|
{notes.map(NoteItem)}
|
||||||
</Page>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||||
import { getNote } from "@/db/mod.ts";
|
import { getNote } from "@/db/mod.ts";
|
||||||
import { Page } from "@/components/Page.tsx";
|
|
||||||
import { type Note } from "@/types.ts";
|
import { type Note } from "@/types.ts";
|
||||||
|
|
||||||
export const handler: Handlers<Note> = {
|
export const handler: Handlers<Note> = {
|
||||||
|
@ -16,7 +15,7 @@ export default function NotesPage(
|
||||||
{ data: { id, createdAt, content } }: PageProps<Note>,
|
{ data: { id, createdAt, content } }: PageProps<Note>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<>
|
||||||
<a href="/note">Back to notes</a>
|
<a href="/note">Back to notes</a>
|
||||||
<h1>Note {id} created at {createdAt.toLocaleString()}</h1>
|
<h1>Note {id} created at {createdAt.toLocaleString()}</h1>
|
||||||
<div class="my-4" key={id}>
|
<div class="my-4" key={id}>
|
||||||
|
@ -24,6 +23,6 @@ export default function NotesPage(
|
||||||
<pre>{content}</pre>
|
<pre>{content}</pre>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
</Page>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||||
import { createNote } from "@/db/mod.ts";
|
import { createNote } from "@/db/mod.ts";
|
||||||
import { Page } from "@/components/Page.tsx";
|
|
||||||
import { type ContextState, type Note } from "@/types.ts";
|
import { type ContextState, type Note } from "@/types.ts";
|
||||||
|
|
||||||
export const handler: Handlers<Note, ContextState> = {
|
export const handler: Handlers<Note, ContextState> = {
|
||||||
|
@ -16,10 +15,10 @@ export const handler: Handlers<Note, ContextState> = {
|
||||||
|
|
||||||
export default function NotesPage({ data: { id } }: PageProps<Note>) {
|
export default function NotesPage({ data: { id } }: PageProps<Note>) {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<>
|
||||||
<h1>You created a note!</h1>
|
<h1>You created a note!</h1>
|
||||||
<a href="/note">Back to notes</a>
|
<a href="/note">Back to notes</a>
|
||||||
<a href={`/note/${id}`}>View your note</a>
|
<a href={`/note/${id}`}>View your note</a>
|
||||||
</Page>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||||
import { Page } from "@/components/Page.tsx";
|
|
||||||
import { createUser, PostgresError } from "@/db/mod.ts";
|
import { createUser, PostgresError } from "@/db/mod.ts";
|
||||||
import { hash } from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
|
import { hash } from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
|
||||||
|
|
||||||
type UserID = string;
|
|
||||||
|
|
||||||
interface RegistrationError {
|
interface RegistrationError {
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +28,7 @@ export const handler: Handlers<UserID | RegistrationError | null> = {
|
||||||
if (!result) throw "insert failed";
|
if (!result) throw "insert failed";
|
||||||
const [user, _team] = result;
|
const [user, _team] = result;
|
||||||
if (!user) throw "insert failed";
|
if (!user) throw "insert failed";
|
||||||
return await context.render(id);
|
return await context.render(user.id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (
|
if (
|
||||||
err instanceof PostgresError && err.fields.code == "23505" &&
|
err instanceof PostgresError && err.fields.code == "23505" &&
|
||||||
|
@ -66,18 +63,16 @@ export default function Register(
|
||||||
|
|
||||||
function RegistrationSuccessful(_userId: UserID) {
|
function RegistrationSuccessful(_userId: UserID) {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<p>
|
||||||
<p>
|
You're all signed up! Let's go <a href="/login">log in</a>!
|
||||||
You're all signed up! Let's go <a href="/login">log in</a>!
|
</p>
|
||||||
</p>
|
|
||||||
</Page>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RegistrationForm(props?: RegistrationError | null) {
|
function RegistrationForm(props?: RegistrationError | null) {
|
||||||
console.log(props);
|
console.log(props);
|
||||||
return (
|
return (
|
||||||
<Page>
|
<form class="flex flex-col max-w-lg" method="post">
|
||||||
<h1 class="text-4xl mb-4">Register your account</h1>
|
<h1 class="text-4xl mb-4">Register your account</h1>
|
||||||
{props != null &&
|
{props != null &&
|
||||||
(
|
(
|
||||||
|
@ -85,17 +80,15 @@ function RegistrationForm(props?: RegistrationError | null) {
|
||||||
<strong>Error</strong>: {props.message}
|
<strong>Error</strong>: {props.message}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<form class="flex flex-col max-w-lg" method="post">
|
<label for="username">Username</label>
|
||||||
<label for="username">Username</label>
|
<input type="text" name="username" />
|
||||||
<input type="text" name="username" />
|
<label for="password">Password</label>
|
||||||
<label for="password">Password</label>
|
<input type="password" name="password" />
|
||||||
<input type="password" name="password" />
|
<input
|
||||||
<input
|
class="bg-blue-800 px-4 p-2 mt-2"
|
||||||
class="bg-blue-800 px-4 p-2 mt-2"
|
type="submit"
|
||||||
type="submit"
|
value="Register"
|
||||||
value="Register"
|
/>
|
||||||
/>
|
</form>
|
||||||
</form>
|
|
||||||
</Page>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue