From 323c7877b10206933f436bdb802cdf29c229969c Mon Sep 17 00:00:00 2001 From: ntr Date: Thu, 18 Aug 2022 00:34:46 +1000 Subject: [PATCH] yeh boi --- api/index.ts | 4 +++ api/src/api.ts | 9 ++---- api/src/discogs.ts | 2 +- api/src/events.ts | 31 ++++++++++++++++++-- api/src/pg.ts | 6 ++++ client/src/actions.ts | 26 ++++++++--------- client/src/app.tsx | 1 - client/src/components/bundle.tsx | 5 ++-- client/src/components/want.tsx | 1 - client/src/routes/home.tsx | 1 - client/src/routes/list.tsx | 1 - client/src/routes/search.tsx | 50 ++++++++++++++++++-------------- client/src/style/index.less | 9 +++++- 13 files changed, 94 insertions(+), 52 deletions(-) diff --git a/api/index.ts b/api/index.ts index c4dac5b..3e4c7bf 100644 --- a/api/index.ts +++ b/api/index.ts @@ -1,5 +1,6 @@ import createServices from './src/services'; import createLog from './src/log'; +import { Events } from './src/events'; async function start() { const log = createLog(); @@ -14,8 +15,11 @@ async function start() { pg, discogs, api, + events, } = createServices(log); + events.emit(Events.StartupSync); + await api.listen(); } diff --git a/api/src/api.ts b/api/src/api.ts index be75b45..aa40dd6 100644 --- a/api/src/api.ts +++ b/api/src/api.ts @@ -153,12 +153,9 @@ export default function createApi(log: Log, pg: Pg, discogs, events) { const { releaseId } = ctx.request.body; const user = await pg.userFindId(ctx.state.user.id); const release = await discogs.release(releaseId); - const want = await pg.wantCreate([release], user); - - // fetch listings - const listings = await discogs.listings(want); - - ctx.response.body = { want, listings }; + const want = await pg.wantCreate(release, user); + events.emit(Events.WantsAdded, [want]); + ctx.response.body = [want]; }); router.post('/want/discogs', async function wantListPost(ctx, next) { diff --git a/api/src/discogs.ts b/api/src/discogs.ts index fdc81c9..c5c12bd 100644 --- a/api/src/discogs.ts +++ b/api/src/discogs.ts @@ -84,7 +84,7 @@ export default function discogs(log: Log, pg: Pg) { try { log.debug('fetching listing', id); const res = await api.get(`${apiBase}/marketplace/listings/${id}`); - log.info(res.data); + log.debug(res.data); return pg.listingUpsert(res.data, want); } catch (e) { return handleApiError(e); diff --git a/api/src/events.ts b/api/src/events.ts index 38d462f..e4127c0 100644 --- a/api/src/events.ts +++ b/api/src/events.ts @@ -1,25 +1,50 @@ import { EventEmitter } from 'node:events'; import type Log from 'bunyan'; -import type { Listing, User, Want, Prisma } from '@prisma/client'; +import type { Want } from '@prisma/client'; import { Pg } from './pg'; import routerAuth from './routers/router.auth'; export enum Events { WantsAdded = 'WantsAdded', + StartupSync = 'StartupSync', } export function eventService(log: Log, pg: Pg, discogs) { const events = new EventEmitter({ captureRejections: true }); events.on('error', function eventError(e) { - log.error(e); + log.error('event error', e); }); events.on(Events.WantsAdded, async function wantsAdded(wants: Want[]) { - log.info('wants added', wants); + for (const want of wants) { + const listings = await discogs.listings(want); + log.info({ count: listings.length, title: want.id }, 'listings added') + } + + log.info('finished fetching listings', { wants: wants.length }); }); + events.on(Events.StartupSync, async function wantsAdded() { + const wants = await pg.wantsToSync(); + + try { + for (const want of wants) { + const listings = await discogs.listings(want); + log.info({ count: listings.length, title: want.id }, 'listings added') + } + + log.info('finished syncing', { wants: wants.length }); + } catch (e) { + if (e.message === 'rate limited') { + log.warn('rate limited, trying again in 60s'); + return setTimeout(() => events.emit(Events.StartupSync), 60 * 1000); + } + } + }); + + return events; } \ No newline at end of file diff --git a/api/src/pg.ts b/api/src/pg.ts index 8484f1e..5b01037 100644 --- a/api/src/pg.ts +++ b/api/src/pg.ts @@ -18,6 +18,7 @@ export interface Pg { wantCreate: (discogsWant: any, user: User) => Promise, wantList: (user: User) => Promise, + wantsToSync: () => Promise, listingUpsert: (listing: Listing, want: Want) => Promise, listingFind: (id: DiscogsListing) => Promise, listingRmUnlisted: (want: Want, ids: DiscogsListing[]) => Promise, @@ -60,6 +61,10 @@ export default function createPg(log: Log, prisma: PrismaClient): Pg { }); } + async function wantsToSync() { + return prisma.want.findMany({}); + } + async function wantCreate(discogsRelease, user) { const data: { userId: UUID, release: DiscogsListing, data: any } = { data: discogsRelease, @@ -149,6 +154,7 @@ export default function createPg(log: Log, prisma: PrismaClient): Pg { wantCreate, wantList, + wantsToSync, listingUpsert, listingFind, listingRmUnlisted, diff --git a/client/src/actions.ts b/client/src/actions.ts index a979840..9db8dd5 100644 --- a/client/src/actions.ts +++ b/client/src/actions.ts @@ -1,5 +1,4 @@ -import axios from 'axios'; -import type { AxiosInstance } from 'axios'; +import axios, { AxiosInstance } from 'axios'; type ApiResponse = [string | null, any | null]; @@ -20,20 +19,17 @@ const api = axios.create({ withCredentials: true }); -// axios.interceptors.request.use(function onReq(config) { -// return config; -// }, function onError(error) { -// // console.error(error); -// Promise.resolve(['Network Error', null]); -// }); - -api.interceptors.response.use(function onSuccess(response) { +function onSuccess(response) { return [null, response.data]; -}, function onError(error) { - // console.error(error); +} + +function onError(error) { if (error.response) return [error.response.data.error, null]; if (error.request) return ['Network Error', null]; -}); + return ['Unknown Error', null]; +} + +api.interceptors.response.use(onSuccess, onError); const actions = { getBundles: async function getBundles() { @@ -52,6 +48,10 @@ const actions = { return api.get('/want/all'); }, + postWantSearch: async function postWantSearch(artist, title) { + return api.post('/want/search', { artist, title }); + }, + postWant: async function postWant(releaseId) { return api.post('/want', { releaseId }); }, diff --git a/client/src/app.tsx b/client/src/app.tsx index 25a51e6..fd46dca 100644 --- a/client/src/app.tsx +++ b/client/src/app.tsx @@ -18,7 +18,6 @@ function App() { useEffect(() => { async function getLogin() { const [error, user] = await actions.getLogin(); - console.log(error, user); if (error) return route('/login'); setUser(user); route('/search'); diff --git a/client/src/components/bundle.tsx b/client/src/components/bundle.tsx index e1a84da..06bc6a8 100644 --- a/client/src/components/bundle.tsx +++ b/client/src/components/bundle.tsx @@ -1,15 +1,14 @@ import { h } from 'preact'; export default function Bundle(bundle) { - console.log(bundle); return (

{bundle.username} - {bundle.seller}

-

{bundle.items.length} items - {bundle.total} {bundle.currency}

+

{bundle.items.length} items - {bundle.total.toFixed(2)} {bundle.currency}

Ships: {bundle.mentionsAustralia && 'AU'} {bundle.mentionsWorldwide && 'WORLD'}

{bundle.items.map(i => (
- +

{i.description}

{i.user.name}
diff --git a/client/src/components/want.tsx b/client/src/components/want.tsx index f8d432e..fc2b982 100644 --- a/client/src/components/want.tsx +++ b/client/src/components/want.tsx @@ -9,7 +9,6 @@ function Want(want) {

{r.artists_sort} - {r.title}

{r.year}
-
{r.country}
{r.formats[0].descriptions.join(' ')}
diff --git a/client/src/routes/home.tsx b/client/src/routes/home.tsx index 807320c..2489edf 100644 --- a/client/src/routes/home.tsx +++ b/client/src/routes/home.tsx @@ -11,7 +11,6 @@ function Home() { async function getBundles() { const [error, bundles] = await actions.getBundles(); if (error) return console.error(error); - console.log(bundles, 'bundles') setBundles(bundles); } getBundles(); diff --git a/client/src/routes/list.tsx b/client/src/routes/list.tsx index 2a585dd..46d34f1 100644 --- a/client/src/routes/list.tsx +++ b/client/src/routes/list.tsx @@ -18,7 +18,6 @@ const List = ({ user }: Props) => { async function getWants() { const [error, wants] = await actions.getWants(); if (error) return console.error(error); - console.log(wants, 'wants') setOffers(wants); } getWants(); diff --git a/client/src/routes/search.tsx b/client/src/routes/search.tsx index 5dcdfd5..477614d 100644 --- a/client/src/routes/search.tsx +++ b/client/src/routes/search.tsx @@ -1,20 +1,29 @@ import { h } from 'preact'; import { useState } from 'preact/hooks'; import axios from 'axios'; -import { ExternalLink } from 'react-feather'; +import { ExternalLink, Check } from 'react-feather'; import actions from '../actions'; -function result(r) { - function onClick() { - actions.postWant(r.id); +function Result(r) { + const [added, setAdded] = useState(false) + + async function onClick() { + const [error, want] = await actions.postWant(r.id); + if (error) return false; + setAdded(true); } + const classes = `${added ? 'added' : ''}`; + const btnText = added + ? + : '+'; + return (
- +
-

{r.title}

+

{r.title}

{r.year}
{r.country}
{r.format.join(' ')}
@@ -25,33 +34,32 @@ function result(r) { } const User = () => { - const [artist, setArtist] = useState('nine inch nails') - const [title, setTitle] = useState('the fragile'); + const [artist, setArtist] = useState('') + const [title, setTitle] = useState(''); const [username, setUsername] = useState(''); const [results, setResults] = useState([]); const [noResults, setNoResults] = useState(false); async function fetchResults() { - const { data } = await axios.post(`http://localhost:41337/want/search`, { - artist, - title, - }); + const [error, results] = await actions.postWantSearch(artist, title); - console.log(data); - - if (data.length === 0) { + if (results.length === 0) { setResults([]); return setNoResults(true); } setNoResults(false); - setResults(data); + setResults(results); } async function submitWantlist() { const [error, wants] = await actions.postDiscogs(username); - // setNoResults(false); - // setResults(data); + if (results.length === 0) { + setResults([]); + return setNoResults(true); + } + + console.log('synced', wants.length, 'wants'); } function handleSubmit(e) { @@ -88,15 +96,15 @@ const User = () => {

Sync Discogs Wantlist

- setUsername(e.target.value) } required /> - + setUsername(e.target.value) } required /> +
{noResultsEl} - {results.map(result)} + {results.map(Result)} {topEl}
diff --git a/client/src/style/index.less b/client/src/style/index.less index e160985..0b463da 100644 --- a/client/src/style/index.less +++ b/client/src/style/index.less @@ -56,6 +56,7 @@ html { --color-accent-blue: #5E9ED6; --color-blue-gray: #506485; + --color-green: #537a34; --base-accent-color: var(--color-accent-blue); --fg-color: var(--color-off-white); @@ -200,14 +201,20 @@ article { margin-right: var(--base-spacing); background: var(--tertiary-background-color); border-color: var(--secondary-background-color); + + &.added { + background: var(--color-green); + cursor: auto; + } } } .list { display: flex; + flex-flow: row wrap; article { - flex: 1; + flex: 1 1 20%; padding: var(--base-spacing); margin: var(--base-spacing-half); }