import koa from 'koa'; import bodyParser from 'koa-body'; import koaRouter from '@koa/router' import cors from '@koa/cors'; import http from 'http'; import { groupBy } from 'lodash'; import Bluebird from 'bluebird'; import type Log from 'bunyan'; import { Pg } from './pg'; import type { Listing, User, Want, Prisma } from '@prisma/client'; export default function createApi(log: Log, pg: Pg, discogs) { const PORT = 41337; const app = new koa(); app.use(async (ctx, next) => { try { await next(); } catch (err) { log.error(err); ctx.status = err.statusCode || err.status || 500; ctx.body = { message: err.message }; } }); const router = koaRouter(); interface ListingData extends Listing { data: any, want: { user: User }, }; async function getBundles() { const bundles = await pg.bundleCandidates(); return bundles.map((b: ListingData[]) => { const total = b.reduce((acc, l) => acc + l.data.original_price.value, 0); const items = b.map(l => ({ id: l.data.id, description: l.data.release.description, price: l.data.original_price.value, currency: l.data.original_price.curr_abbr, condition: l.data.condition, uri: l.data.uri, user: l.want.user, })); const shipping = b[0]?.data.seller.shipping; const bundle = { username: b[0]?.data.seller.username, seller: b[0]?.data.seller.id, shipsFrom: b[0]?.data.ships_from, total, currency: b[0]?.data.original_price.curr_abbr, shipping: '...', items, mentionsAustralia: shipping.toLowerCase().includes('australia'), mentionsWorldwide: shipping.toLowerCase().includes('world'), } log.info(bundle); return bundle; }); } async function populateUserListings(user: User) { log.info(user); const wants = await discogs.wantlist(user); // const latest = await discogs.wantlistLatest(user); // if (wants.length !== latest.length) log.info({ username: user.discogs, wantCount: wants.length }); return Bluebird.mapSeries(wants, async want => { const listings = await discogs.listings(want); const populated = listings.filter(l => l); log.info({ id: want.id, release: want.release, populated: populated.length, total: listings.length }); return populated; }); } function listen() { return new Promise((resolve, reject) => { app.on('error', err => { reject(err); }) app.listen(PORT, () => { log.info('listening', { PORT }); }); }); } app.use(cors()); app.use(bodyParser()) router.get('/bundles', async (ctx, next) => { const bundles = await getBundles(); const search = await discogs.searchMaster('nine inch nails', 'the fragile'); log.info(search); ctx.response.body = bundles; }) router.get('/user/:id/offers', async function userOffersGet(ctx, next) { const { id } = ctx.request.params; const user = await pg.userFindId(id); // if (!user) return ctx.response.status = 404; const bundles = (await getBundles()) .map(b => { b.items = b.items.filter(i => i.user.id === user.id) return b; }) .filter(b => b.items.length); ctx.response.body = bundles; }); router.post('/user', async function usersPost(ctx, next) { const { name } = ctx.request.body; const user = await pg.userUpsert(null, name, null); ctx.response.body = user; }); router.post('/user/populate', async function usersPopulate(ctx, next) { const { userId } = ctx.request.body; const user = await pg.userFindId(userId); const listings = await populateUserListings(user); ctx.response.body = listings; }); router.post('/want', async function wantPost(ctx, next) { const { userId, releaseId } = ctx.request.body; const user = await pg.userFindId(userId); const release = await discogs.release(releaseId); const want = await pg.wantUpsert(release, user); ctx.response.body = want; }); router.post('/want/search', async function wantPost(ctx, next) { const { artist, title } = ctx.request.body; const results = await discogs.search(artist, title); ctx.response.body = results; }); app.use(router.routes()); return { app, listen, }; }