This commit is contained in:
Daniel Flanagan 2022-10-11 17:12:32 -05:00
parent 34b440213f
commit c771628263
Signed by: lytedev
GPG key ID: 5B2020A0F9921EF4
10 changed files with 49 additions and 62 deletions

View file

@ -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": {

View file

@ -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);
}, },
}; };

View file

@ -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();

View file

@ -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> </>
); );
} }

View file

@ -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>
); );
} }

View file

@ -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> </>
); );
} }

View file

@ -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> </>
); );
} }

View file

@ -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> </>
); );
} }

View file

@ -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>
); );
} }

View file

@ -51,6 +51,3 @@ export type TokenDigest = string;
export interface ContextState { export interface ContextState {
user?: PublicUser; user?: PublicUser;
} }
function toPublicUser(user: User): PublicUser {
}