home index
This commit is contained in:
1 parent
a4d8608a3e
commit
b6c6e54ef8
8 files changed
+98
-174
No files matched your search
@@ -3,8 +3,9 @@ import { getCookies } from "$std/http/cookie.ts"
|
||||
import { CratesCtx } from 'api/mw.service.ts'
|
||||
|
||||
const PROTECTED = [
|
||||
"/api/auth",
|
||||
"/",
|
||||
"/home",
|
||||
"/api/auth",
|
||||
]
|
||||
|
||||
// Open Deno KV database
|
||||
|
||||
@@ -1,46 +1,10 @@
|
||||
import { h } from 'preact';
|
||||
import { route } from 'preact-router';
|
||||
import { useEffect } from 'preact/hooks';
|
||||
import { useSelector } from 'react-redux'
|
||||
import LogRocket from 'logrocket';
|
||||
import { User } from 'prismaClient'
|
||||
|
||||
import http from './../http';
|
||||
import { State } from './../store';
|
||||
|
||||
import Swirl from './swirl';
|
||||
|
||||
function showAdmin(user) {
|
||||
function showAdmin(user: User) {
|
||||
return user && user.email === 'ntr@mnml.gg';
|
||||
}
|
||||
|
||||
const DEFAULT_ROUTE = '/';
|
||||
|
||||
const Nav = () => {
|
||||
const user = useSelector((state: State) => state.user);
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
route('/login');
|
||||
return;
|
||||
}
|
||||
route(DEFAULT_ROUTE)
|
||||
|
||||
if (window.location.host === 'crates.ntwl.xyz') {
|
||||
LogRocket.identify(user.id, {
|
||||
name: user.email,
|
||||
email: user.email,
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}, [user])
|
||||
|
||||
async function logoutClick(e) {
|
||||
e.preventDefault();
|
||||
await http.postLogout();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
const Nav = ({ user }) => {
|
||||
const nav = () => (
|
||||
<nav>
|
||||
<a href="/">Offers</a>
|
||||
@@ -65,7 +29,7 @@ const Nav = () => {
|
||||
const userNav = () => (
|
||||
<nav>
|
||||
<a href="/info">Info</a>
|
||||
<a href="/auth/logout" onClick={logoutClick}>Logout</a>
|
||||
<a href="/api/auth/logout" >Logout</a>
|
||||
</nav>
|
||||
)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import * as $api_auth_status from './routes/api/auth/status.ts'
|
||||
import * as $api_offers from './routes/api/offers.ts'
|
||||
import * as $api_wants from './routes/api/wants.ts'
|
||||
import * as $home from './routes/home.tsx'
|
||||
import * as $index from './routes/index.tsx'
|
||||
import * as $login from './routes/login.tsx'
|
||||
import * as $register from './routes/register.tsx'
|
||||
|
||||
@@ -31,6 +32,7 @@ const manifest = {
|
||||
'./routes/api/offers.ts': $api_offers,
|
||||
'./routes/api/wants.ts': $api_wants,
|
||||
'./routes/home.tsx': $home,
|
||||
'./routes/index.tsx': $index,
|
||||
'./routes/login.tsx': $login,
|
||||
'./routes/register.tsx': $register,
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type PageProps } from "$fresh/server.ts";
|
||||
|
||||
export default function App({ Component }: PageProps) {
|
||||
return (
|
||||
<html>
|
||||
|
||||
@@ -6,12 +6,7 @@ import { z } from "npm:zod"
|
||||
import { zx } from "npm:zodix"
|
||||
|
||||
import { createSession } from "api/mw.auth.ts"
|
||||
import {
|
||||
ValidationError,
|
||||
InvalidCredentialsError,
|
||||
NotLoggedInError,
|
||||
ServerError
|
||||
} from "api/errors.ts"
|
||||
import { InvalidCredentialsError } from "api/errors.ts"
|
||||
import { CratesState } from 'api/mw.service.ts'
|
||||
|
||||
const LoginSchema = z.object({
|
||||
@@ -21,70 +16,37 @@ const LoginSchema = z.object({
|
||||
|
||||
export const handler: Handlers<{email: string}, CratesState> = {
|
||||
async POST(req, ctx) {
|
||||
try {
|
||||
const { email, password } = await zx.parseForm(req, LoginSchema)
|
||||
const { email, password } = await zx.parseForm(req, LoginSchema)
|
||||
|
||||
// Create pg instance for database operations
|
||||
const { pg } = ctx.state
|
||||
// Create pg instance for database operations
|
||||
const { pg } = ctx.state
|
||||
|
||||
// Find user by email
|
||||
const user = await pg.userFindEmail(email)
|
||||
if (!user) throw new InvalidCredentialsError()
|
||||
// Find user by email
|
||||
const user = await pg.userFindEmail(email)
|
||||
if (!user) throw new InvalidCredentialsError()
|
||||
|
||||
// Verify password
|
||||
const match = await bcrypt.compare(password, user.password)
|
||||
if (!match) throw new InvalidCredentialsError()
|
||||
// Verify password
|
||||
const match = await bcrypt.compare(password, user.password)
|
||||
if (!match) throw new InvalidCredentialsError()
|
||||
|
||||
// Create session using Deno KV
|
||||
const { sessionId, expiresAt } = await createSession(user.id, 24 * 7) // 7 days in hours
|
||||
// Create session using Deno KV
|
||||
const { sessionId, expiresAt } = await createSession(user.id, 24 * 7) // 7 days in hours
|
||||
|
||||
// Set cookie
|
||||
const headers = new Headers({ "Content-Type": "application/json" })
|
||||
setCookie(headers, {
|
||||
name: "session",
|
||||
value: sessionId,
|
||||
expires: expiresAt,
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "Lax"
|
||||
})
|
||||
|
||||
// Return user data
|
||||
return new Response(JSON.stringify({
|
||||
email: user.email
|
||||
}), {
|
||||
status: 200,
|
||||
headers
|
||||
})
|
||||
} catch (error) {
|
||||
// Let the error middleware handle custom errors
|
||||
if (error instanceof ValidationError ||
|
||||
error instanceof InvalidCredentialsError) {
|
||||
throw error
|
||||
}
|
||||
|
||||
console.error("Login error:", error)
|
||||
throw new ServerError("An error occurred during login")
|
||||
}
|
||||
},
|
||||
|
||||
// GET handler to check login status
|
||||
async GET(req, ctx) {
|
||||
// This would be handled by the auth middleware
|
||||
// If the request reaches here, the user is authenticated
|
||||
const user = ctx.state.user
|
||||
|
||||
if (!user) {
|
||||
throw new NotLoggedInError()
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
id: user.id,
|
||||
email: user.email
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" }
|
||||
// Set cookie
|
||||
const headers = new Headers({ "Content-Type": "application/json" })
|
||||
setCookie(headers, {
|
||||
name: "session",
|
||||
value: sessionId,
|
||||
expires: expiresAt,
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "Lax"
|
||||
})
|
||||
}
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: { Location: "/home" }
|
||||
});
|
||||
},
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Handlers } from "$fresh/server.ts"
|
||||
import { setCookie } from "$std/http/cookie.ts"
|
||||
import { z } from "https://deno.land/x/zod@v3.22.4/mod.ts"
|
||||
|
||||
import { z } from "npm:zod"
|
||||
import { zx } from "npm:zodix"
|
||||
|
||||
import { createPg } from "api/pg.ts"
|
||||
import { createSession } from "api/mw.auth.ts"
|
||||
import {
|
||||
ValidationError,
|
||||
ResourceExistsError,
|
||||
ServerError
|
||||
} from "api/errors.ts"
|
||||
import { ResourceExistsError } from "api/errors.ts"
|
||||
import { CratesState } from 'api/mw.service.ts'
|
||||
|
||||
// Define validation schema with Zod
|
||||
const RegisterSchema = z.object({
|
||||
@@ -16,67 +16,43 @@ const RegisterSchema = z.object({
|
||||
discogs: z.string().optional()
|
||||
})
|
||||
|
||||
export const handler: Handlers = {
|
||||
export const handler: Handlers<{email: string}, CratesState> = {
|
||||
async POST(req, _ctx) {
|
||||
try {
|
||||
const data = await req.formData()
|
||||
const { discogs, email, password } = await zx.parseForm(req, RegisterSchema)
|
||||
|
||||
// Validate input data with Zod
|
||||
const result = RegisterSchema.safeParse(data)
|
||||
// Create pg instance for database operations
|
||||
const pg = createPg(console)
|
||||
|
||||
if (!result.success) {
|
||||
// Throw validation error
|
||||
throw new ValidationError("Validation failed", result.error.format())
|
||||
}
|
||||
// Check if user exists
|
||||
const existingUser = await pg.userFindEmail(email)
|
||||
if (existingUser) throw new ResourceExistsError("User with this email")
|
||||
|
||||
const { discogs, email, password } = result.data
|
||||
// Create user
|
||||
const params = { discogs, email, password, data: {} }
|
||||
const user = await pg.userCreate(params)
|
||||
|
||||
// Create pg instance for database operations
|
||||
const pg = createPg(console)
|
||||
// Create session using Deno KV
|
||||
const { sessionId, expiresAt } = await createSession(user.id, 24 * 7) // 7 days in hours
|
||||
|
||||
// Check if user exists
|
||||
const existingUser = await pg.userFindEmail(email)
|
||||
// Set cookie
|
||||
const headers = new Headers({ "Content-Type": "application/json" })
|
||||
setCookie(headers, {
|
||||
name: "session",
|
||||
value: sessionId,
|
||||
expires: expiresAt,
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "Lax"
|
||||
})
|
||||
|
||||
if (existingUser) {
|
||||
throw new ResourceExistsError("User with this email")
|
||||
}
|
||||
|
||||
// Create user
|
||||
const params = { discogs, email, password, data: {} }
|
||||
const user = await pg.userCreate(params)
|
||||
|
||||
// Create session using Deno KV
|
||||
const { sessionId, expiresAt } = await createSession(user.id, 24 * 7) // 7 days in hours
|
||||
|
||||
// Set cookie
|
||||
const headers = new Headers({ "Content-Type": "application/json" })
|
||||
setCookie(headers, {
|
||||
name: "session",
|
||||
value: sessionId,
|
||||
expires: expiresAt,
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "Lax"
|
||||
})
|
||||
|
||||
// Return user data
|
||||
return new Response(JSON.stringify({
|
||||
id: user.id,
|
||||
email: user.email
|
||||
}), {
|
||||
status: 200,
|
||||
headers
|
||||
})
|
||||
} catch (error) {
|
||||
// Let the error middleware handle custom errors
|
||||
if (error instanceof ValidationError ||
|
||||
error instanceof ResourceExistsError) {
|
||||
throw error
|
||||
}
|
||||
|
||||
console.error("Registration error:", error)
|
||||
throw new ServerError("An error occurred during registration")
|
||||
}
|
||||
// Return user data
|
||||
return new Response(JSON.stringify({
|
||||
id: user.id,
|
||||
email: user.email
|
||||
}), {
|
||||
status: 200,
|
||||
headers
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import Main from "../components/main.tsx";
|
||||
|
||||
import { CratesState } from "api/mw.service.ts"
|
||||
import { Offer, Want } from "prismaClient"
|
||||
import Nav from '../components/nav.tsx'
|
||||
|
||||
interface HomeData {
|
||||
wants: Want[],
|
||||
@@ -13,21 +14,20 @@ export const handler: Handlers<HomeData, CratesState> = {
|
||||
async GET(_req, ctx) {
|
||||
const { pg, offers, user } = ctx.state
|
||||
|
||||
console.log(user)
|
||||
|
||||
const wants = await pg.userWants(user);
|
||||
const uOffers = await offers.userOffers(user);
|
||||
|
||||
return await ctx.render({ wants, offers: uOffers });
|
||||
return await ctx.render({ user, wants, offers: uOffers });
|
||||
},
|
||||
};
|
||||
|
||||
export default function Home({ data }: PageProps) {
|
||||
const { wants, offers } = data || {};
|
||||
const { user, wants, offers } = data
|
||||
|
||||
return (
|
||||
<div id="mcrates">
|
||||
<>
|
||||
<Nav user={user} />
|
||||
<Main wants={wants} offers={offers} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
import { Handlers } from "$fresh/server.ts"
|
||||
import { CratesState } from 'api/mw.service.ts'
|
||||
|
||||
export const handler: Handlers<{email: string}, CratesState> = {
|
||||
GET(_req, ctx) {
|
||||
const { user } = ctx.state
|
||||
|
||||
if (user) return new Response(null, {
|
||||
status: 302,
|
||||
headers: { Location: "/home" }
|
||||
});
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: { Location: "/login" }
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user