diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 0000000..d29ff47 --- /dev/null +++ b/global.d.ts @@ -0,0 +1,14 @@ +// global type aliases and whatnot + +type UUID = string; + +type DiscogsName = string; +type DiscogsListing = number; +type DiscogsSeller = number; + +type BundleCandidate = { + seller: DiscogsSeller, + count: number, + listings: DiscogsListing[], +} + diff --git a/index.ts b/index.ts index 3952834..48aca31 100644 --- a/index.ts +++ b/index.ts @@ -1,96 +1,97 @@ import fs from 'fs/promises'; import _ from 'lodash'; -import Promise from 'bluebird'; +import Bunyan from 'bunyan'; import { PrismaClient } from '@prisma/client' +import * as Bluebird from "bluebird"; import discogsService from './src/discogs'; import pgService from './src/pg'; import { logAxiosError } from './src/utils'; +import createLog from './src/log'; -async function start() { +async function start(log) { const prisma = new PrismaClient(); - const pg = pgService(prisma); - const discogs = discogsService(pg); + const pg = pgService(log, prisma); + const discogs = discogsService(log, pg); - const usernames = [ - 'margh', - 'jmcdermid1', - 'vanderbabe', - ]; + async function getBundles() { + const bundles = await pg.bundleCandidates(); - async function fetchListings(w) { - return w.map(discogs.listings) + bundles.forEach(b => { + const data = b.map(l => l.data) as any[]; + const wants = b.map(l => l.wantId); + const total = data.reduce((acc, l) => acc + l.price.value, 0); + const items = data.map(l => ({ + id: l.id, + description: l.release.description, + price: l.price.value, + condition: l.condition, + })); + + const username = data[0]?.seller.username + const seller = data[0]?.seller.id + const shipsFrom = data[0]?.ships_from; + const currency = data[0]?.price.currency; + const shipping = data[0]?.seller.shipping; + const mentionsAustralia = shipping.toLowerCase().includes('australia'); + const mentionsWorldwide = shipping.toLowerCase().includes('world'); + log.info({ count: b.length, currency, total, items, username, seller, shipsFrom, mentionsAustralia, mentionsWorldwide }); + }); + + // return bundles; } - async function populateUserListings(user) { - console.log(user); - const wants = await discogs.wantlist(user); - console.log(user.discogs, 'has', wants.length, 'wants'); + return getBundles(); - for (const want of wants) { - const listings = await discogs.listings(want); - const populated = listings.filter(l => l); - console.log(want.id, 'has', populated.length, '/', listings.length, 'listings'); - } + // const usernames = [ + // 'vanderbabe', + // 'margh', + // 'jmcdermid1', + // ]; - // const listings = await Promise - // .all(releases.map(async want => { - // const listings = (await getListings(want)).filter(l => l); + // async function populateUserListings(user) { + // log.info(user); + // const wants = await discogs.wantlist(user); - // const insertions = await Promise.all(listings.map(async l => { - // const listing = await pg.listingUpsert(l, want); - // console.log(listing.id, listing.data.release.description, want.user, listing.seller); - // return listing; - // })); + // // const latest = await discogs.wantlistLatest(user); + // // if (wants.length !== latest.length) + // log.info({ username: user.discogs, wantCount: wants.length }); - // console.log(insertions.length, want.id); - // })); + // return Promise.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; + // }); + // } - // return listings; - } - - for (const u of usernames) { - const user = await discogs.user(u) - await populateUserListings(user); - } - - // return Promise.map(releases, async w => { - // try { - // const l = await discogs.listings(w.id) - // return l; - // } catch (e) { - // // console.error(e); - // return null; - // } - // }) - // .then(listings => _ - // .chain(listings) - // .filter(l => l) - // .flatten() - // // .map(l => ({ - // // id: l.id, - // // description: l.release.description, - // // seller_id: l.seller.id, - // // seller_name: l.seller.username, - // // })) - // // .groupBy(l => l.seller_id) - // .value() - // ); + // return Promise.mapSeries(usernames, async u => { + // const user = await discogs.user(u) + // return populateUserListings(user); + // }); } -function run() { - start() - .then(r => { - console.log(r); - setTimeout(run, 60 * 1000); - }) - .catch(e => { - console.error(e); - if (e.message === 'rate limited') { - return setTimeout(run, 60 * 1000); - } - process.exit(1); - }); +async function run() { + const log = createLog(); + + process.on('SIGTERM', () => { + log.debug('SIGTERM received, exiting'); + process.exit(0); + }); + + try { + const r = await start(log) + // log.info('done syncing', { totalListings: r.flat().length }); + log.info(r); + setTimeout(run, 60 * 1000); + } catch (e) { + if (e.message === 'rate limited') { + log.info('rate limited, trying again in 60s'); + return setTimeout(run, 60 * 1000); + } + log.error(e); + process.exit(1); + } } run(); diff --git a/knexfile.js b/knexfile.js deleted file mode 100644 index 3831975..0000000 --- a/knexfile.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - client: 'postgresql', - connection: { - host : '0.0.0.0', - port: 45432, - database: 'mcrates', - user: 'mcrates', - password: 'digdeep', - }, - pool: { - min: 2, - max: 10 - }, - migrations: { - tableName: 'knex_migrations' - } -}; diff --git a/package-lock.json b/package-lock.json index a00b628..0d496f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,11 +14,10 @@ "bluebird": "^3.7.2", "bunyan": "^1.8.15", "express": "^4.18.1", - "knex": "^2.2.0", - "lodash": "^4.17.21", - "pg": "^8.7.3" + "lodash": "^4.17.21" }, "devDependencies": { + "@types/bluebird": "^3.5.36", "node-dev": "^7.4.3", "prisma": "^4.1.0", "ts-node-dev": "^2.0.0", @@ -118,6 +117,12 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "node_modules/@types/bluebird": { + "version": "3.5.36", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.36.tgz", + "integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==", + "dev": true + }, "node_modules/@types/node": { "version": "18.0.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", @@ -279,14 +284,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", - "engines": { - "node": ">=4" - } - }, "node_modules/bunyan": { "version": "1.8.15", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", @@ -351,11 +348,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -367,14 +359,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", - "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", - "engines": { - "node": "^12.20.0 || >=14" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -511,27 +495,11 @@ "node": ">= 0.8" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "node_modules/esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "engines": { - "node": ">=6" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -709,15 +677,11 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, "engines": { "node": ">=8.0.0" } }, - "node_modules/getopts": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", - "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -819,14 +783,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -851,6 +807,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -921,77 +878,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/knex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/knex/-/knex-2.2.0.tgz", - "integrity": "sha512-yhm1Qe9Ok0TeXBq3nNHqZYJPrQ4Iw2tq9k/HxjrZ/EWec2ifOjJlkNHr26v8cQrWtk5iG3iwfUazTIWy+VKG5g==", - "dependencies": { - "colorette": "2.0.19", - "commander": "^9.1.0", - "debug": "4.3.4", - "escalade": "^3.1.1", - "esm": "^3.2.25", - "get-package-type": "^0.1.0", - "getopts": "2.3.0", - "interpret": "^2.2.0", - "lodash": "^4.17.21", - "pg-connection-string": "2.5.0", - "rechoir": "^0.8.0", - "resolve-from": "^5.0.0", - "tarn": "^3.0.2", - "tildify": "2.0.0" - }, - "bin": { - "knex": "bin/cli.js" - }, - "engines": { - "node": ">=12" - }, - "peerDependenciesMeta": { - "better-sqlite3": { - "optional": true - }, - "mysql": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-native": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "tedious": { - "optional": true - } - } - }, - "node_modules/knex/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/knex/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -1260,11 +1146,6 @@ "wrappy": "1" } }, - "node_modules/packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1285,87 +1166,14 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, - "node_modules/pg": { - "version": "8.7.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.3.tgz", - "integrity": "sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==", - "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", - "pg-pool": "^3.5.1", - "pg-protocol": "^1.5.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "pg-native": ">=2.0.0" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.1.tgz", - "integrity": "sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", - "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "dependencies": { - "split2": "^4.1.0" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1378,41 +1186,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/prisma": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.1.0.tgz", @@ -1490,21 +1263,11 @@ "node": ">=8.10.0" } }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -1517,14 +1280,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "engines": { - "node": ">=8" - } - }, "node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -1667,14 +1422,6 @@ "source-map": "^0.6.0" } }, - "node_modules/split2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", - "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", - "engines": { - "node": ">= 10.x" - } - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -1705,6 +1452,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -1712,22 +1460,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tarn": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", - "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/tildify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", - "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", - "engines": { - "node": ">=8" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1935,6 +1667,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, "engines": { "node": ">=0.4" } @@ -2030,6 +1763,12 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "@types/bluebird": { + "version": "3.5.36", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.36.tgz", + "integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==", + "dev": true + }, "@types/node": { "version": "18.0.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", @@ -2166,11 +1905,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" - }, "bunyan": { "version": "1.8.15", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", @@ -2212,11 +1946,6 @@ "readdirp": "~3.6.0" } }, - "colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2225,11 +1954,6 @@ "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", - "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2334,21 +2058,11 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" - }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2480,12 +2194,8 @@ "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" - }, - "getopts": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", - "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true }, "glob": { "version": "7.2.3", @@ -2564,11 +2274,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==" - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2587,6 +2292,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, "requires": { "has": "^1.0.3" } @@ -2633,42 +2339,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "knex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/knex/-/knex-2.2.0.tgz", - "integrity": "sha512-yhm1Qe9Ok0TeXBq3nNHqZYJPrQ4Iw2tq9k/HxjrZ/EWec2ifOjJlkNHr26v8cQrWtk5iG3iwfUazTIWy+VKG5g==", - "requires": { - "colorette": "2.0.19", - "commander": "^9.1.0", - "debug": "4.3.4", - "escalade": "^3.1.1", - "esm": "^3.2.25", - "get-package-type": "^0.1.0", - "getopts": "2.3.0", - "interpret": "^2.2.0", - "lodash": "^4.17.21", - "pg-connection-string": "2.5.0", - "rechoir": "^0.8.0", - "resolve-from": "^5.0.0", - "tarn": "^3.0.2", - "tildify": "2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -2873,11 +2543,6 @@ "wrappy": "1" } }, - "packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2892,97 +2557,20 @@ "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, - "pg": { - "version": "8.7.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.3.tgz", - "integrity": "sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==", - "requires": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", - "pg-pool": "^3.5.1", - "pg-protocol": "^1.5.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - } - }, - "pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" - }, - "pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" - }, - "pg-pool": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.1.tgz", - "integrity": "sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==", - "requires": {} - }, - "pg-protocol": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", - "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" - }, - "pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "requires": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - } - }, - "pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "requires": { - "split2": "^4.1.0" - } - }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" - }, - "postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" - }, - "postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" - }, - "postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "requires": { - "xtend": "^4.0.0" - } - }, "prisma": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.1.0.tgz", @@ -3034,29 +2622,17 @@ "picomatch": "^2.2.1" } }, - "rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "requires": { - "resolve": "^1.20.0" - } - }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, "requires": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -3166,11 +2742,6 @@ "source-map": "^0.6.0" } }, - "split2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", - "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" - }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -3191,17 +2762,8 @@ "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "tarn": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", - "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==" - }, - "tildify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", - "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==" + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true }, "to-regex-range": { "version": "5.0.1", @@ -3334,7 +2896,8 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true }, "yallist": { "version": "4.0.0", diff --git a/package.json b/package.json index 76abd19..6d22f5c 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,12 @@ "test": "echo \"Error: no test specified\" && exit 1", "migrate": "knex migrate:latest", "migrate:make": "prisma migrate dev --name", - "start": "ts-node-dev --respawn --transpile-only index.ts" + "start": "ts-node-dev --respawn --transpile-only index.ts | bunyan -o short" }, "author": "", "license": "ISC", "devDependencies": { + "@types/bluebird": "^3.5.36", "node-dev": "^7.4.3", "prisma": "^4.1.0", "ts-node-dev": "^2.0.0", @@ -23,8 +24,6 @@ "bluebird": "^3.7.2", "bunyan": "^1.8.15", "express": "^4.18.1", - "knex": "^2.2.0", - "lodash": "^4.17.21", - "pg": "^8.7.3" + "lodash": "^4.17.21" } } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ee6bb61..033e98f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,50 +1,51 @@ generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" + previewFeatures = ["interactiveTransactions"] } datasource db { - provider = "postgresql" - url = env("DATABASE_URL") + provider = "postgresql" + url = env("DATABASE_URL") } model Listing { - id Int @id - seller Int - data Json + id Int @id + seller Int + data Json - wantId String @db.Uuid - want Want @relation(fields: [wantId], references: [id], onDelete: Cascade, onUpdate: Cascade) + wantId String @db.Uuid + want Want @relation(fields: [wantId], references: [id], onDelete: Cascade, onUpdate: Cascade) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt - @@index([seller]) + @@index([seller]) } model User { - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - discogs String? @unique - data Json + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + discogs String? @unique + data Json - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt - wants Want[] + wants Want[] } model Want { - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - release Int? - data Json + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + release Int? + data Json - userId String @db.Uuid - user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) + userId String @db.Uuid + user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt - listings Listing[] + listings Listing[] - @@unique([userId, release]) - @@index([release]) + @@unique([userId, release]) + @@index([release]) } diff --git a/src/discogs.ts b/src/discogs.ts index 9619351..cbd0958 100644 --- a/src/discogs.ts +++ b/src/discogs.ts @@ -2,12 +2,14 @@ import fs from 'fs/promises'; import axios from 'axios'; import cheerio from 'cheerio'; + +import type Log from 'bunyan'; import type { PrismaClient, Want, User, Listing } from '@prisma/client'; import { logAxiosError } from './utils'; import { Pg } from './pg'; -export default function discogs(pg: Pg) { +export default function discogs(log: Log, pg: Pg) { const TOKEN = 'wRTXuYZGOPsIQgKRGKCAkUkidGtvpSfQgPWkYIpD'; const apiBase = 'https://api.discogs.com' const discogsBase = 'https://www.discogs.com' @@ -18,28 +20,32 @@ export default function discogs(pg: Pg) { }, }); - function handleApiError(e) { + async function handleApiError(e) { logAxiosError(e); if (e.response.status === 429) { throw new Error('rate limited'); } + return null; } async function user(discogs: string) { const user = await pg.userFindName(discogs); if (user) return user; - console.log('fetching user', discogs); + log.debug('fetching user', discogs); const res = await api.get(`${apiBase}/users/${discogs}`); return pg.userUpsert(res.data); - } async function wantlist(user: User): Promise { const stored = await pg.userWants(user); if (stored.length) return stored; + return wantlistLatest(user); + } + + async function wantlistLatest(user: User): Promise { const { username } = user.data as { username: string }; const { data: { wants, pagination }} = await api.get(`${apiBase}/users/${username}/wants`); - return Promise.all(wants.map(r => pg.wantUpsert(r, user))) + return Promise.mapSeries(wants, async r => pg.wantUpsert(r, user)); } async function inventory(user: string) { @@ -56,7 +62,10 @@ export default function discogs(pg: Pg) { return id; }) .get(); - return Promise.all(ids.map(id => listing(id, want))); + + const unlisted = await pg.listingRmUnlisted(want, ids); + if (unlisted) log.info({ wantId: want.id, unlisted: unlisted }, 'missing listings removed'); + return Promise.mapSeries(ids, async id => listing(id, want)); } async function listing(id, want) { @@ -64,17 +73,18 @@ export default function discogs(pg: Pg) { if (listing) return listing; try { + log.debug('fetching listing', id); const res = await api.get(`${apiBase}/marketplace/listings/${id}`); return pg.listingUpsert(res.data, want); } catch (e) { - handleApiError(e); - return null; + return handleApiError(e); } } return { user, wantlist, + wantlistLatest, inventory, listings, listing, diff --git a/src/log.ts b/src/log.ts new file mode 100644 index 0000000..8c48f5a --- /dev/null +++ b/src/log.ts @@ -0,0 +1,10 @@ +import bunyan from 'bunyan'; + +export default function createLog() { + const log = bunyan.createLogger({ + name: 'mcrates', + stream: process.stdout + }); + + return log; +} diff --git a/src/mcrates.d.ts b/src/mcrates.d.ts deleted file mode 100644 index 9f24b2c..0000000 --- a/src/mcrates.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -type UUID = string; - -// type User = { -// id: UUID, -// discogs: string, -// data: any, -// } - -// type Listing = { -// id: number, -// want: UUID, -// seller: number, -// data: any, -// } - -// type Want = { -// id: UUID, -// release: number, -// user: UUID, -// data: any, -// } diff --git a/src/pg.ts b/src/pg.ts index 13abaa6..462b181 100644 --- a/src/pg.ts +++ b/src/pg.ts @@ -1,27 +1,27 @@ -import Knex from 'knex'; - -import config from '../knexfile'; +import type Log from 'bunyan'; import type { PrismaClient, Want, User, Listing, Prisma } from '@prisma/client'; +import * as Bluebird from 'bluebird'; export interface Pg { - userFindId: (id: UUID) => Promise, - userFindName: (name: string) => Promise, - userUpsert: (user: User) => Promise, - userWants: (user: User) => Promise, + userFindId: (id: UUID) => Promise, + userFindName: (name: DiscogsName) => Promise, + userUpsert: (user: User) => Promise, + userWants: (user: User) => Promise, - wantUpsert: (want: Want, user: User) => Promise, - listingUpsert: (listing: Listing, want: Want) => Promise, - listingFind: (id: number) => Promise, + wantUpsert: (want: Want, user: User) => Promise, + listingUpsert: (listing: Listing, want: Want) => Promise, + listingFind: (id: DiscogsListing) => Promise, + listingRmUnlisted: (want: Want, ids: DiscogsListing[]) => Promise, + + bundleCandidates: () => Promise, } -export default function createPg(prisma: PrismaClient): Pg { - // const pg = Knex(config); - +export default function createPg(log: Log, prisma: PrismaClient): Pg { async function userFindId(id) { return prisma.user.findUnique({ where: { id }}); } - async function userFindName(name) { + async function userFindName(name: DiscogsName) { return prisma.user.findUnique({ where: { discogs: name }}); } @@ -45,7 +45,7 @@ export default function createPg(prisma: PrismaClient): Pg { } async function wantUpsert(dcWant, user) { - const values: { userId: string, release: number, data: any } = { + const values: { userId: UUID, release: DiscogsListing, data: any } = { data: dcWant, release: dcWant.id, userId: user.id, @@ -63,6 +63,19 @@ export default function createPg(prisma: PrismaClient): Pg { }); } + async function listingRmUnlisted(want: Want, ids: DiscogsListing[]) { + const { count } = await prisma.listing.deleteMany({ + where: { + wantId: want.id, + id: { + notIn: ids, + } + } + }); + + return count; + } + async function listingUpsert(listing, want) { const values = { id: listing.id, @@ -80,7 +93,7 @@ export default function createPg(prisma: PrismaClient): Pg { }); } - async function listingFind(id) { + async function listingFind(id: DiscogsListing) { return prisma.listing.findUnique({ where: { id, @@ -88,6 +101,27 @@ export default function createPg(prisma: PrismaClient): Pg { }); } + async function bundleCandidates(): Promise { + const candidates = await prisma.$queryRaw` + select + seller, count(distinct("wantId")), array_agg(id) as listings + from + "Listing" + group by + seller + order by + count desc + limit 5; + ` as BundleCandidate[]; + + return Promise.all(candidates.map(async c => prisma.listing.findMany({ + where: { + id: { in: c.listings } + }, + distinct: ['wantId'], + }))); + } + return { userFindId, userFindName, @@ -97,5 +131,8 @@ export default function createPg(prisma: PrismaClient): Pg { wantUpsert, listingUpsert, listingFind, + listingRmUnlisted, + + bundleCandidates, }; } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index ca7fcef..18b36ad 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,12 +27,14 @@ /* Modules */ "module": "commonjs", /* Specify what module code is generated. */ // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - "types": ["./index.d.ts"], /* Specify type package names to be included without being referenced in a source file. */ + "types": [ + "./index.d.ts" + ], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ // "resolveJsonModule": true, /* Enable importing .json files. */