home back
This commit is contained in:
1 parent
a79c11496b
commit
bdc22c5c42
26 files changed
+170
-107
No files matched your search
@@ -1,29 +0,0 @@
|
||||
import { FreshContext } from "$fresh/server.ts"
|
||||
|
||||
export async function errorMiddleware(req: Request, ctx: FreshContext): Promise<Response> {
|
||||
try {
|
||||
const response = await ctx.next()
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error(`Error handling ${req.method} ${new URL(req.url).pathname}:`, error)
|
||||
|
||||
// Check if this is an API route
|
||||
const isApiRoute = new URL(req.url).pathname.startsWith("/api/")
|
||||
|
||||
if (isApiRoute) {
|
||||
return new Response(JSON.stringify({
|
||||
error: "Internal server error",
|
||||
message: error.message
|
||||
}), {
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" }
|
||||
})
|
||||
}
|
||||
|
||||
// For non-API routes, return a redirect to an error page or render an error component
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: { Location: "/?error=server_error" }
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,33 @@
|
||||
import { FreshContext } from "$fresh/server.ts"
|
||||
import { getCookies, setCookie } from "$std/http/cookie.ts"
|
||||
import { createPg } from "api/pg.ts"
|
||||
import { getCookies } from "$std/http/cookie.ts"
|
||||
import { CratesCtx } from 'api/mw.service.ts'
|
||||
|
||||
// Define public paths that don't require authentication
|
||||
const PROTECTED = [
|
||||
"/home",
|
||||
"/api/auth",
|
||||
"/home",
|
||||
]
|
||||
|
||||
// Sessions storage shared between routes
|
||||
// In production, this would be Redis or a database
|
||||
export const sessions = new Map<string, { userId: string, expiresAt: Date }>()
|
||||
|
||||
export async function authMiddleware(req: Request, ctx: FreshContext): Promise<Response> {
|
||||
export async function authMiddleware(req: Request, ctx: CratesCtx): Promise<Response> {
|
||||
const { pg } = ctx.state
|
||||
|
||||
const url = new URL(req.url)
|
||||
const path = url.pathname
|
||||
|
||||
// Allow public paths without authentication
|
||||
if (!PROTECTED.includes(path)) {
|
||||
return await ctx.next()
|
||||
}
|
||||
|
||||
// Check for authentication
|
||||
const cookies = getCookies(req.headers)
|
||||
const sessionId = cookies.session
|
||||
|
||||
// Allow public paths without authentication
|
||||
if (!PROTECTED.includes(path)) {
|
||||
|
||||
console.log('skip', path)
|
||||
return await ctx.next()
|
||||
}
|
||||
|
||||
if (!sessionId) {
|
||||
if (path.startsWith("/api/")) {
|
||||
return new Response(JSON.stringify({ error: "Not logged in" }), {
|
||||
@@ -51,7 +54,6 @@ export async function authMiddleware(req: Request, ctx: FreshContext): Promise<R
|
||||
}
|
||||
|
||||
// Get user from database
|
||||
const pg = createPg(console)
|
||||
const user = await pg.userFindId(session.userId)
|
||||
|
||||
if (!user) {
|
||||
@@ -59,8 +61,5 @@ export async function authMiddleware(req: Request, ctx: FreshContext): Promise<R
|
||||
}
|
||||
|
||||
ctx.state.user = user
|
||||
ctx.state.pg = pg
|
||||
|
||||
|
||||
return await ctx.next()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { FreshContext } from "$fresh/server.ts"
|
||||
|
||||
export async function errorMiddleware(req: Request, ctx: FreshContext): Promise<Response> {
|
||||
try {
|
||||
const response = await ctx.next()
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error(`Error handling ${req.method} ${new URL(req.url).pathname}:`, error)
|
||||
}
|
||||
}
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
import { User } from 'prisma/generated/index.ts'
|
||||
|
||||
import { Pg } from 'api/pg.ts'
|
||||
import { Offers } from 'api/offers.ts'
|
||||
import { createPg } from 'api/pg.ts'
|
||||
import createOffers from 'api/offers.ts'
|
||||
|
||||
export interface CratesState {
|
||||
user: User
|
||||
pg: Pg;
|
||||
offers: Offers
|
||||
}
|
||||
|
||||
export type CratesCtx = FreshContext<CratesState>
|
||||
|
||||
export async function serviceMiddleware(_req: Request, ctx: FreshContext): Promise<Response> {
|
||||
const pg = createPg(console)
|
||||
const offers = createOffers(pg)
|
||||
|
||||
ctx.state.pg = pg
|
||||
ctx.state.offers = offers
|
||||
|
||||
return await ctx.next()
|
||||
}
|
||||
+19
-18
@@ -1,13 +1,9 @@
|
||||
import type Log from 'bunyan';
|
||||
import type { PrismaClient, Want, User, Listing, Offer, } from '@prisma/client';
|
||||
import assert from 'assert/strict';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import flatMap from 'lodash/flatMap';
|
||||
import { Context } from 'koa';
|
||||
import { assert } from "$std/assert/mod.ts";
|
||||
import { groupBy, sortBy, flatMap } from 'lodash'
|
||||
|
||||
import { logAxiosError, wantDetails } from './utils';
|
||||
import { Pg } from './pg';
|
||||
import type { User } from 'prismaClient';
|
||||
import { Pg } from "api/pg.ts";
|
||||
import { UUID } from 'node:crypto'
|
||||
|
||||
export enum Status {
|
||||
Complete = 'Complete',
|
||||
@@ -21,10 +17,10 @@ function sortOffers(o) {
|
||||
return Object.values(Status).indexOf(o.status);
|
||||
}
|
||||
|
||||
export default function createOffers(log: Log, pg: Pg) {
|
||||
export default function createOffers(pg: Pg) {
|
||||
async function generate() {
|
||||
const listings = flatMap(await pg.interestingListings());
|
||||
log.debug('generateOffers', { listings: listings.length });
|
||||
console.debug('generateOffers', { listings: listings.length });
|
||||
|
||||
const shipping = 7.5; // yolt
|
||||
|
||||
@@ -51,30 +47,35 @@ export default function createOffers(log: Log, pg: Pg) {
|
||||
// TODO probably need to take one of each condition of record from a store
|
||||
// eg. they have a VG+ and a M we should make offers for each
|
||||
|
||||
log.trace('generated offers', sorted);
|
||||
console.trace('generated offers', sorted);
|
||||
const inserts = await pg.offersUpdate(sorted);
|
||||
|
||||
return inserts;
|
||||
}
|
||||
|
||||
async function accept(ctx: Context) {
|
||||
const { id } = ctx.request.body;
|
||||
ctx.assert(id, 401, 'no id supplied');
|
||||
async function accept(user: User, id: UUID) {
|
||||
assert(id, 'no id supplied');
|
||||
|
||||
// check the user actually has this offer
|
||||
const offer = await pg.userOfferFind(ctx.state.user, id);
|
||||
const offer = await pg.userOfferFind(user, id);
|
||||
return pg.offerStatusSet([offer.id], Status.Accepted);
|
||||
}
|
||||
|
||||
async function statusSet(user: User, ids: UUID[], status: Status) {
|
||||
log.info({ status, ids }, 'setting offer status');
|
||||
console.info({ status, ids }, 'setting offer status');
|
||||
|
||||
// Validate the inputs
|
||||
assert(Array.isArray(ids), "ids must be an array");
|
||||
assert(ids.length > 0, "ids array cannot be empty");
|
||||
assert(ids.every(id => isUUID(id)), "All ids must be valid UUIDs");
|
||||
assert(Object.values(Status).includes(status), "Invalid status value");
|
||||
|
||||
// const offer = await pg.offerFindId(ids);
|
||||
const update = await pg.offerStatusSet(ids, status);
|
||||
|
||||
// forget about this for now
|
||||
// if (status === Status.Complete) {
|
||||
// log.info({ id, status }, 'completed offer, removing want...');
|
||||
// console.info({ id, status }, 'completed offer, removing want...');
|
||||
// await pg.wantRm(offer.wantId);
|
||||
// }
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts"
|
||||
import { load } from "$std/dotenv/mod.ts"
|
||||
import { PrismaClient } from "prisma/generated/index.ts";
|
||||
import { PrismaClient } from "prismaClient"
|
||||
|
||||
// Load environment variables
|
||||
await load({ export: true })
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
import type { Want, User } from "prismaClient";
|
||||
|
||||
export function logFetchError(e: Response, requestUrl: string, responseData: any) {
|
||||
console.warn({
|
||||
status: e.status,
|
||||
statusText: e.statusText,
|
||||
url: requestUrl,
|
||||
data: responseData,
|
||||
rateLimiting: {
|
||||
limit: e.headers.get('x-discogs-ratelimit'),
|
||||
remaining: e.headers.get('x-discogs-ratelimit-remaining'),
|
||||
used: e.headers.get('x-discogs-ratelimit-used'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function wantDetails(want: Want) {
|
||||
const data = want.data as any;
|
||||
|
||||
if (!data) console.warn(want);
|
||||
|
||||
return {
|
||||
title: data.title,
|
||||
release: data.id,
|
||||
artist: data.artists_sort,
|
||||
id: want.id
|
||||
}
|
||||
}
|
||||
|
||||
export function isAdmin(user: User) {
|
||||
return user.email === 'ntr@mnml.gg';
|
||||
}
|
||||
|
||||
export function offerDetails() {}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -936,7 +936,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "api/auth.middleware.ts",
|
||||
"file": "api/mw.auth.ts",
|
||||
"settings":
|
||||
{
|
||||
"buffer_size": 2440,
|
||||
@@ -1546,7 +1546,7 @@
|
||||
[
|
||||
"/home/ntr/crates/crates/deno.jsonc",
|
||||
"/home/ntr/crates/crates/crates.sublime-project",
|
||||
"/home/ntr/crates/crates/api/auth.middleware.ts",
|
||||
"/home/ntr/crates/crates/api/mw.auth.ts",
|
||||
"/home/ntr/crates/crates/import_map.json",
|
||||
"/home/ntr/crates/crates/api/pg.ts",
|
||||
"/home/ntr/crates/crates.sublime-project",
|
||||
@@ -1816,7 +1816,7 @@
|
||||
},
|
||||
{
|
||||
"buffer": 2,
|
||||
"file": "api/auth.middleware.ts",
|
||||
"file": "api/mw.auth.ts",
|
||||
"semi_transient": false,
|
||||
"settings":
|
||||
{
|
||||
@@ -1850,7 +1850,7 @@
|
||||
}
|
||||
],
|
||||
"lsp_active": true,
|
||||
"lsp_uri": "file:///home/ntr/crates/crates/api/auth.middleware.ts",
|
||||
"lsp_uri": "file:///home/ntr/crates/crates/api/mw.auth.ts",
|
||||
"show_definitions": false,
|
||||
"syntax": "Packages/JavaScript/TypeScript.sublime-syntax",
|
||||
"tab_size": 4,
|
||||
@@ -1941,7 +1941,7 @@
|
||||
],
|
||||
[
|
||||
"middle",
|
||||
"api/auth.middleware.ts"
|
||||
"api/mw.auth.ts"
|
||||
],
|
||||
[
|
||||
"pg",
|
||||
|
||||
+4
-2
@@ -4,7 +4,7 @@
|
||||
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
|
||||
"cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
|
||||
"manifest": "deno task cli manifest $(pwd)",
|
||||
"start": "deno run -A --watch=static/,routes/ dev.ts",
|
||||
"start": "deno run -A --watch=routes/,.env dev.ts",
|
||||
"build": "deno run -A dev.ts build",
|
||||
"preview": "deno run -A main.ts",
|
||||
"prisma-clean": "rm -rf prisma/generated",
|
||||
@@ -24,6 +24,7 @@
|
||||
"@twind/preset-tailwind": "https://esm.sh/@twind/preset-tailwind@1.1.4/",
|
||||
"@twind/preset-autoprefix": "https://esm.sh/@twind/preset-autoprefix@1.0.7/",
|
||||
|
||||
"lodash": "https://deno.land/x/lodash@4.17.15-es/lodash.js",
|
||||
"less": "npm:less@4.2.0",
|
||||
"lucide-preact": "npm:lucide-preact@^0.475.0",
|
||||
"@prisma/client": "npm:@prisma/client@^6.6.0",
|
||||
@@ -31,7 +32,8 @@
|
||||
"api/": "./api/",
|
||||
"api": "./api/",
|
||||
"prisma/": "./prisma/",
|
||||
"prisma": "./prisma/"
|
||||
"prisma": "./prisma/",
|
||||
"prismaClient": "./prisma/generated/index.ts",
|
||||
},
|
||||
|
||||
"fmt": {
|
||||
|
||||
@@ -3,6 +3,9 @@ import { defineConfig } from "$fresh/server.ts";
|
||||
// import twindConfig from "./twind.config.ts";
|
||||
import lessPlugin from "./less.plugin.ts";
|
||||
|
||||
const less = lessPlugin()
|
||||
await less.buildStart()
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
// twind(twindConfig),
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
// This file is automatically updated during development when running `dev.ts`.
|
||||
|
||||
import * as $_404 from './routes/_404.tsx'
|
||||
import * as $_500 from './routes/_500.tsx'
|
||||
import * as $_app from './routes/_app.tsx'
|
||||
import * as $_middleware from './routes/_middleware.ts'
|
||||
import * as $api_middleware from './routes/api/_middleware.ts'
|
||||
import * as $api_auth_login from './routes/api/auth/login.ts'
|
||||
import * as $api_auth_logout from './routes/api/auth/logout.ts'
|
||||
import * as $api_auth_register from './routes/api/auth/register.ts'
|
||||
@@ -20,8 +22,10 @@ import type { Manifest } from '$fresh/server.ts'
|
||||
const manifest = {
|
||||
routes: {
|
||||
'./routes/_404.tsx': $_404,
|
||||
'./routes/_500.tsx': $_500,
|
||||
'./routes/_app.tsx': $_app,
|
||||
'./routes/_middleware.ts': $_middleware,
|
||||
'./routes/api/_middleware.ts': $api_middleware,
|
||||
'./routes/api/auth/login.ts': $api_auth_login,
|
||||
'./routes/api/auth/logout.ts': $api_auth_logout,
|
||||
'./routes/api/auth/register.ts': $api_auth_register,
|
||||
|
||||
+2
-10
@@ -1,5 +1,5 @@
|
||||
import { Plugin } from "$fresh/server.ts";
|
||||
import { ensureDir, exists } from "$std/fs/mod.ts";
|
||||
import { ensureDir } from "$std/fs/mod.ts";
|
||||
import { join } from "$std/path/mod.ts";
|
||||
import { dirname } from "$std/path/mod.ts";
|
||||
|
||||
@@ -12,7 +12,7 @@ interface LessPluginOptions {
|
||||
}
|
||||
|
||||
export default function lessPlugin(): Plugin {
|
||||
const path = "static/style"
|
||||
const path = "components/style"
|
||||
const outDir = "static/css"
|
||||
|
||||
// Compile the Less file using the Less package
|
||||
@@ -47,13 +47,5 @@ export default function lessPlugin(): Plugin {
|
||||
await compileLess(inputPath, outputPath);
|
||||
console.log("Less styles compiled during build start");
|
||||
},
|
||||
|
||||
// async renderAsync(ctx) {
|
||||
// await ctx.renderAsync();
|
||||
|
||||
// // Add stylesheet link to the result links
|
||||
// const cssPath = `/css/style.css`;
|
||||
// return { links: [{ rel: "stylesheet", href: cssPath }] }
|
||||
// }
|
||||
};
|
||||
}
|
||||
@@ -10,9 +10,4 @@ import { start } from "$fresh/server.ts";
|
||||
import manifest from "./fresh.gen.ts";
|
||||
import config from "./fresh.config.ts";
|
||||
|
||||
import lessPlugin from './less.plugin.ts'
|
||||
|
||||
const less = lessPlugin()
|
||||
await less.buildStart()
|
||||
|
||||
await start(manifest, config);
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
import { PageProps } from "$fresh/server.ts";
|
||||
import { Head } from "$fresh/runtime.ts";
|
||||
|
||||
export default function Error500Page({ error }: PageProps) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>500 - Server Error</title>
|
||||
</Head>
|
||||
<div class="px-4 py-8 mx-auto bg-[#86efac]">
|
||||
<div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
|
||||
<img
|
||||
class="my-6"
|
||||
src="/swirl.svg"
|
||||
width="128"
|
||||
height="128"
|
||||
alt="the Fresh logo: a sliced lemon dripping with juice"
|
||||
/>
|
||||
<h1 class="text-4xl font-bold">500 - Server Error</h1>
|
||||
<p class="my-4">
|
||||
{(error as Error).message}
|
||||
</p>
|
||||
<a href="/" class="underline">Go back home</a>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,5 @@
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
import { Pg } from 'api/pg.ts'
|
||||
import { User } from 'prisma/generated/index.ts'
|
||||
import { authMiddleware } from 'api/auth.middleware.ts'
|
||||
import { errorMiddleware } from 'api/error.middleware.ts'
|
||||
import { errorMiddleware } from 'api/mw.error.ts'
|
||||
import { serviceMiddleware } from 'api/mw.service.ts'
|
||||
import { authMiddleware } from 'api/mw.auth.ts'
|
||||
|
||||
interface State {
|
||||
pg: Pg;
|
||||
user: User
|
||||
}
|
||||
|
||||
export const handler = [errorMiddleware, authMiddleware]
|
||||
export const handler = [serviceMiddleware, authMiddleware]
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
import { authMiddleware } from 'api/mw.auth.ts'
|
||||
export const handler = [authMiddleware]
|
||||
@@ -3,7 +3,7 @@ import { setCookie } from "$std/http/cookie.ts"
|
||||
import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts"
|
||||
|
||||
import { createPg } from "api/pg.ts"
|
||||
import { sessions } from "api/auth.middleware.ts"
|
||||
import { sessions } from "api/mw.auth.ts"
|
||||
|
||||
export const handler: Handlers = {
|
||||
async POST(req, _ctx) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Handlers } from "$fresh/server.ts"
|
||||
import { getCookies, setCookie } from "$std/http/cookie.ts"
|
||||
import { sessions } from "../../../api/auth.middleware.ts"
|
||||
import { sessions } from "../../../api/mw.auth.ts"
|
||||
|
||||
export const handler: Handlers = {
|
||||
async POST(req, _ctx) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Handlers } from "$fresh/server.ts"
|
||||
import { setCookie } from "$std/http/cookie.ts"
|
||||
import { createPg } from "../../../api/pg.ts"
|
||||
import { sessions } from "../../../api/auth.middleware.ts"
|
||||
import { sessions } from "../../../api/mw.auth.ts"
|
||||
|
||||
export const handler: Handlers = {
|
||||
async POST(req, _ctx) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Handlers } from "$fresh/server.ts"
|
||||
|
||||
import * as offersService from "api/offers.ts"
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET(_req, ctx) {
|
||||
const user = ctx.state.user
|
||||
const offers = await pg.userOffers(user)
|
||||
const offers = await offersService.userOffers(user)
|
||||
|
||||
return new Response(JSON.stringify(offers), {
|
||||
status: 200,
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { createPg, Pg } from "../api/pg.ts";
|
||||
import Main from "../components/main.tsx";
|
||||
import { User } from 'prisma/generated/index.ts'
|
||||
|
||||
import { CratesCtx } from "api/mw.service.ts"
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET(req, ctx) {
|
||||
const user: User = ctx.state.user;
|
||||
const pg: Pg = createPg(console);
|
||||
async GET(_req, ctx: CratesCtx) {
|
||||
const { pg, offers, user } = ctx.state
|
||||
|
||||
console.log(user)
|
||||
|
||||
const wants = await pg.userWants(user);
|
||||
const offers = await pg.userOffers(user);
|
||||
const uOffers = await offers.userOffers(user);
|
||||
|
||||
return ctx.render({ wants, offers });
|
||||
return ctx.render({ wants, offers: uOffers });
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { getCookies, setCookie } from "$std/http/cookie.ts";
|
||||
import { z } from "npm:zod";
|
||||
import Swirl from "../components/swirl.tsx";
|
||||
import { sessions } from "api/auth.middleware.ts";
|
||||
import { sessions } from "api/mw.auth.ts";
|
||||
import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
|
||||
import { createPg } from "api/pg.ts";
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { getCookies, setCookie } from "$std/http/cookie.ts";
|
||||
import { z } from "npm:zod";
|
||||
import Swirl from "../components/swirl.tsx";
|
||||
import { sessions } from "../api/auth.middleware.ts";
|
||||
import { sessions } from "../api/mw.auth.ts";
|
||||
import { createPg } from "../api/pg.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
|
||||
Reference in new issue
Block a user