Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
413c1f4840 | ||
|
|
02aadcf2db | ||
|
|
baeaead650 | ||
|
|
8700b15f39 | ||
|
|
adcc19564f |
No files matched your search
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
/build
|
||||
/*.log
|
||||
@@ -0,0 +1,19 @@
|
||||
# mcrates
|
||||
|
||||
## CLI Commands
|
||||
* `npm install`: Installs dependencies
|
||||
|
||||
* `npm run dev`: Run a development, HMR server
|
||||
|
||||
* `npm run serve`: Run a production-like server
|
||||
|
||||
* `npm run build`: Production-ready build
|
||||
|
||||
* `npm run lint`: Pass TypeScript files using ESLint
|
||||
|
||||
* `npm run test`: Run Jest and Enzyme with
|
||||
[`enzyme-adapter-preact-pure`](https://github.com/preactjs/enzyme-adapter-preact-pure) for
|
||||
your tests
|
||||
|
||||
|
||||
For detailed explanation on how things work, checkout the [CLI Readme](https://github.com/developit/preact-cli/blob/master/README.md).
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "mcrates-client",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "preact build",
|
||||
"start": "preact watch",
|
||||
"lint": "eslint src"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"extends": [
|
||||
"preact",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"build/"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"preact": "^10.10.0",
|
||||
"preact-render-to-string": "^5.2.1",
|
||||
"preact-router": "^3.2.1",
|
||||
"react-redux": "^8.0.2",
|
||||
"redux": "^4.2.0",
|
||||
"unistore": "^3.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||
"@typescript-eslint/parser": "^5.30.6",
|
||||
"eslint": "^8.20.0",
|
||||
"eslint-config-preact": "^1.3.0",
|
||||
"eslint-plugin-jest": "^26.7.0",
|
||||
"jest": "^28.1.3",
|
||||
"less": "^4.1.3",
|
||||
"less-loader": "^7.3.0",
|
||||
"preact-cli": "^3.4.1",
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const actions = store => ({
|
||||
getBundles: async () => {
|
||||
const { data: bundles } = await axios.get('http://localhost:41337/bundles');
|
||||
return { bundles };
|
||||
},
|
||||
});
|
||||
|
||||
export default actions;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { h } from 'preact';
|
||||
import { Route, Router } from 'preact-router';
|
||||
import { Provider } from 'unistore/preact'
|
||||
|
||||
import mcratesStore from './store';
|
||||
import actions from './actions';
|
||||
|
||||
import Header from './components/header';
|
||||
|
||||
import Home from './routes/home';
|
||||
import Profile from './routes/profile';
|
||||
|
||||
const store = mcratesStore();
|
||||
|
||||
console.log(Home);
|
||||
|
||||
const App = () => (
|
||||
<Provider store={store}>
|
||||
<div id="app">
|
||||
<Header />
|
||||
<Router>
|
||||
<Route path="/" component={Home} />
|
||||
<Route path="/profile/" component={Profile} user="me" />
|
||||
<Route path="/profile/:user" component={Profile} />
|
||||
</Router>
|
||||
</div>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 626 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
@@ -0,0 +1,15 @@
|
||||
import { h } from 'preact';
|
||||
import { Link } from 'preact-router/match';
|
||||
|
||||
const Header = () => (
|
||||
<header class={'header'}>
|
||||
<h1>Mates Crates</h1>
|
||||
<nav>
|
||||
<Link activeClassName={'active'} href="/">Home</Link>
|
||||
<Link activeClassName={'active'} href="/profile">Me</Link>
|
||||
<Link activeClassName={'active'} href="/profile/john">John</Link>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
|
||||
export default Header;
|
||||
@@ -0,0 +1,4 @@
|
||||
declare module "*.less" {
|
||||
const mapping: Record<string, string>;
|
||||
export default mapping;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import './style/index.less';
|
||||
import App from './app';
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "mcrates",
|
||||
"short_name": "mcrates",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"background_color": "#fff",
|
||||
"theme_color": "#673ab8",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/icons/android-chrome-192x192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "/assets/icons/android-chrome-512x512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { h } from 'preact';
|
||||
import { connect } from 'unistore/preact'
|
||||
|
||||
import actions from './../actions';
|
||||
|
||||
// import Bundle from './../components/bundle';
|
||||
|
||||
interface Props {
|
||||
bundles: any[];
|
||||
getBundles: () => void,
|
||||
}
|
||||
|
||||
function Bundle(bundle) {
|
||||
return (
|
||||
<div class="bundle">
|
||||
<h2>{bundle.username} - {bundle.seller}</h2>
|
||||
<h3>{bundle.total} {bundle.currency}</h3>
|
||||
{bundle.items.map(i => (
|
||||
<div key={i.id}>
|
||||
<a href={i.uri}>
|
||||
<h4>{i.description}</h4>
|
||||
</a>
|
||||
<h5>{i.price} {i.currency}</h5>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
function Home({ bundles, getBundles }: Props) {
|
||||
if (!bundles.length) {
|
||||
getBundles();
|
||||
|
||||
return (
|
||||
<div class={'home'}>
|
||||
loading...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={'home'}>
|
||||
{bundles.map(Bundle)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect('bundles', actions)(Home);
|
||||
@@ -0,0 +1,35 @@
|
||||
import { h } from 'preact';
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import style from './style.less';
|
||||
|
||||
interface Props {
|
||||
user: string;
|
||||
}
|
||||
|
||||
const Profile = ({ user }: Props) => {
|
||||
const [time, setTime] = useState<number>(Date.now());
|
||||
const [count, setCount] = useState<number>(0);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
let timer = setInterval(() => setTime(Date.now()), 1000);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div class={'profile'}>
|
||||
<h1>Profile: {user}</h1>
|
||||
<p>This is the user profile for a user named { user }.</p>
|
||||
|
||||
<div>Current time: {new Date(time).toLocaleString()}</div>
|
||||
|
||||
<p>
|
||||
<button onClick={() => setCount((count) => count + 1)}>Click Me</button>
|
||||
{' '}
|
||||
Clicked {count} times.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Profile;
|
||||
@@ -0,0 +1,6 @@
|
||||
import createStore from 'unistore'
|
||||
|
||||
export default function mcratesStore() {
|
||||
return createStore({ bundles: [] })
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
html, body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: #FAFAFA;
|
||||
font-family: 'Helvetica Neue', arial, sans-serif;
|
||||
font-weight: 400;
|
||||
color: #444;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.home {
|
||||
padding: 56px 20px;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
|
||||
.bundle {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.profile {
|
||||
padding: 56px 20px;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
padding: 0;
|
||||
background: #673AB7;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.header {
|
||||
h1 {
|
||||
float: left;
|
||||
margin: 0;
|
||||
padding: 0 15px;
|
||||
font-size: 24px;
|
||||
line-height: 56px;
|
||||
font-weight: 400;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
nav {
|
||||
float: right;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: inline-block;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
padding: 0 15px;
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
background: rgba(255,255,255,0);
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
will-change: background-color;
|
||||
}
|
||||
|
||||
nav a:hover,
|
||||
nav a:active {
|
||||
background: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
nav a.active {
|
||||
background: rgba(0,0,0,0.4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
|
||||
"module": "ESNext", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation: */
|
||||
"allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
"jsxFactory": "h", /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */
|
||||
"jsxFragmentFactory": "Fragment", /* Specify the JSX fragment factory function to use when targeting react JSX emit with jsxFactory compiler option is specified, e.g. Fragment. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
"noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": false, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
"esModuleInterop": true, /* */
|
||||
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
"paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
"react": ["./node_modules/preact/compat"],
|
||||
"react-dom": ["./node_modules/preact/compat"]
|
||||
},
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true /* Skip type checking of declaration files. */
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
# #!/bin/bash
|
||||
# set -e
|
||||
|
||||
# psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
# CREATE USER mcrates;
|
||||
# CREATE DATABASE mcrates;
|
||||
# GRANT ALL PRIVILEGES ON DATABASE mcrates TO mcrates;
|
||||
# EOSQL
|
||||
@@ -1,93 +0,0 @@
|
||||
import fs from 'fs/promises';
|
||||
import _ from 'lodash';
|
||||
import Promise from 'bluebird';
|
||||
|
||||
import discogsService from './src/discogs';
|
||||
import pgService from './src/pg';
|
||||
import { logAxiosError } from './src/utils';
|
||||
|
||||
async function start() {
|
||||
const pg = pgService();
|
||||
const discogs = discogsService(pg);
|
||||
|
||||
const usernames = [
|
||||
'margh',
|
||||
'jmcdermid1',
|
||||
'vanderbabe',
|
||||
];
|
||||
|
||||
async function fetchListings(w) {
|
||||
return w.map(discogs.listings)
|
||||
}
|
||||
|
||||
async function populateUserListings(user) {
|
||||
console.log(user);
|
||||
const wants = await discogs.wantlist(user);
|
||||
console.log(user.discogs, 'has', wants.length, 'wants');
|
||||
|
||||
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 listings = await Promise
|
||||
// .all(releases.map(async want => {
|
||||
// const listings = (await getListings(want)).filter(l => l);
|
||||
|
||||
// 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;
|
||||
// }));
|
||||
|
||||
// console.log(insertions.length, want.id);
|
||||
// }));
|
||||
|
||||
// return listings;
|
||||
}
|
||||
|
||||
return Promise.resolve(usernames)
|
||||
.map(u => discogs.user(u))
|
||||
.map(populateUserListings);
|
||||
|
||||
// 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()
|
||||
// );
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
run();
|
||||
@@ -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'
|
||||
}
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
exports.up = async knex => {
|
||||
await knex.schema.createTable('users', table => {
|
||||
table.uuid('id')
|
||||
.primary()
|
||||
.defaultTo(knex.raw('gen_random_uuid()'));
|
||||
|
||||
table.string('discogs')
|
||||
.unique();
|
||||
|
||||
table.jsonb('data')
|
||||
.notNullable();
|
||||
|
||||
table.timestamps(true, true);
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = async () => true;
|
||||
@@ -1,27 +0,0 @@
|
||||
exports.up = async knex => {
|
||||
await knex.schema.createTable('wants', table => {
|
||||
table.uuid('id')
|
||||
.primary()
|
||||
.defaultTo(knex.raw('gen_random_uuid()'));
|
||||
|
||||
table.integer('release')
|
||||
.notNullable()
|
||||
.index();
|
||||
|
||||
table.uuid('user')
|
||||
table.foreign('user')
|
||||
.references('id')
|
||||
.inTable('users')
|
||||
.onDelete('CASCADE');
|
||||
|
||||
table.jsonb('data')
|
||||
.notNullable();
|
||||
|
||||
// user can only want a release once
|
||||
table.unique(['user', 'release']);
|
||||
|
||||
table.timestamps(true, true);
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = async () => true;
|
||||
@@ -1,23 +0,0 @@
|
||||
exports.up = async knex => {
|
||||
await knex.schema.createTable('listings', table => {
|
||||
table.integer('id')
|
||||
.primary();
|
||||
|
||||
table.uuid('want')
|
||||
table.foreign('want')
|
||||
.references('id')
|
||||
.inTable('wants')
|
||||
.onDelete('CASCADE');
|
||||
|
||||
table.integer('seller')
|
||||
.notNullable()
|
||||
.index();
|
||||
|
||||
table.jsonb('data')
|
||||
.notNullable();
|
||||
|
||||
table.timestamps(true, true);
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = async () => true;
|
||||
@@ -1,13 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"discogs" TEXT,
|
||||
"data" JSONB NOT NULL,
|
||||
"creaedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_discogs_key" ON "User"("discogs");
|
||||
@@ -1,3 +0,0 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
@@ -1,20 +0,0 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
discogs String? @unique
|
||||
data Json
|
||||
|
||||
creaedAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
@@ -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[],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import fs from 'fs/promises';
|
||||
import _ from 'lodash';
|
||||
import Bunyan from 'bunyan';
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import Promise from "bluebird";
|
||||
|
||||
import discogsService from './src/discogs';
|
||||
import pgService from './src/pg';
|
||||
import { logAxiosError } from './src/utils';
|
||||
import createLog from './src/log';
|
||||
import createApi from './src/api';
|
||||
|
||||
async function start(log) {
|
||||
const prisma = new PrismaClient();
|
||||
const pg = pgService(log, prisma);
|
||||
const discogs = discogsService(log, pg);
|
||||
const api = createApi(log, pg);
|
||||
|
||||
const usernames = [
|
||||
'vanderbabe',
|
||||
'margh',
|
||||
'jmcdermid1',
|
||||
];
|
||||
|
||||
async function populateUserListings(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 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;
|
||||
});
|
||||
}
|
||||
|
||||
// await Promise.mapSeries(usernames, async u => {
|
||||
// const user = await discogs.user(u)
|
||||
// return populateUserListings(user);
|
||||
// });
|
||||
|
||||
await api.listen();
|
||||
|
||||
// return getBundles();
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -13,12 +13,13 @@
|
||||
"axios": "^0.27.2",
|
||||
"bluebird": "^3.7.2",
|
||||
"bunyan": "^1.8.15",
|
||||
"cors": "^2.8.5",
|
||||
"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",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"node-dev": "^7.4.3",
|
||||
"prisma": "^4.1.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
@@ -118,6 +119,18 @@
|
||||
"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/lodash": {
|
||||
"version": "4.14.182",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
|
||||
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
|
||||
@@ -279,14 +292,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 +356,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 +367,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",
|
||||
@@ -413,6 +405,18 @@
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
@@ -511,27 +515,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 +697,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 +803,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 +827,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 +898,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",
|
||||
@@ -1232,6 +1138,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
|
||||
@@ -1260,11 +1174,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 +1194,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 +1214,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 +1291,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 +1308,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 +1450,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 +1480,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 +1488,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 +1695,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 +1791,18 @@
|
||||
"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/lodash": {
|
||||
"version": "4.14.182",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
|
||||
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
|
||||
@@ -2166,11 +1939,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 +1980,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 +1988,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",
|
||||
@@ -2259,6 +2017,15 @@
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"requires": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
}
|
||||
},
|
||||
"create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
@@ -2334,21 +2101,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 +2237,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 +2317,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 +2335,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 +2382,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",
|
||||
@@ -2851,6 +2564,11 @@
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
|
||||
},
|
||||
"object-inspect": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
|
||||
@@ -2873,11 +2591,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 +2605,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 +2670,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 +2790,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 +2810,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 +2944,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",
|
||||
@@ -7,11 +7,13 @@
|
||||
"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",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"node-dev": "^7.4.3",
|
||||
"prisma": "^4.1.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
@@ -22,9 +24,8 @@
|
||||
"axios": "^0.27.2",
|
||||
"bluebird": "^3.7.2",
|
||||
"bunyan": "^1.8.15",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.1",
|
||||
"knex": "^2.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"pg": "^8.7.3"
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["interactiveTransactions"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model Listing {
|
||||
id Int @id
|
||||
seller Int
|
||||
data Json
|
||||
|
||||
wantId String @db.Uuid
|
||||
want Want @relation(fields: [wantId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([seller])
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
discogs String? @unique
|
||||
data Json
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
wants Want[]
|
||||
}
|
||||
|
||||
model Want {
|
||||
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)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
listings Listing[]
|
||||
|
||||
@@unique([userId, release])
|
||||
@@index([release])
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import express from 'express';
|
||||
import http from 'http';
|
||||
import cors from 'cors';
|
||||
import { groupBy } from 'lodash';
|
||||
|
||||
import type Log from 'bunyan';
|
||||
|
||||
import { Pg } from './pg';
|
||||
import type { Listing, User, Prisma } from '@prisma/client';
|
||||
|
||||
export default function createApi(log: Log, pg: Pg) {
|
||||
const PORT = 41337;
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
|
||||
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.discogs,
|
||||
}));
|
||||
|
||||
const shipping = b[0]?.data.seller.shipping;
|
||||
|
||||
console.log(b.map(l => l.data));
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function listen() {
|
||||
return new Promise((resolve, reject) => {
|
||||
server.on('error', err => {
|
||||
reject(err);
|
||||
})
|
||||
|
||||
server.listen(PORT, () => {
|
||||
log.info('listening', { PORT });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.use(cors());
|
||||
|
||||
app.get('/bundles', async (req, res, next) => {
|
||||
const bundles = await getBundles();
|
||||
// log.info(bundles);
|
||||
res.json(bundles);
|
||||
})
|
||||
|
||||
return {
|
||||
app,
|
||||
listen,
|
||||
};
|
||||
}
|
||||
@@ -2,11 +2,15 @@ import fs from 'fs/promises';
|
||||
|
||||
import axios from 'axios';
|
||||
import cheerio from 'cheerio';
|
||||
import Bluebird from 'bluebird';
|
||||
|
||||
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'
|
||||
@@ -14,20 +18,23 @@ export default function discogs(pg: Pg) {
|
||||
const api = axios.create({
|
||||
headers: {
|
||||
'Authorization': TOKEN,
|
||||
// discogs only returns the first page without a desktop u/a
|
||||
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0',
|
||||
},
|
||||
});
|
||||
|
||||
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.userGetName(discogs);
|
||||
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);
|
||||
}
|
||||
@@ -35,8 +42,13 @@ export default function discogs(pg: Pg) {
|
||||
async function wantlist(user: User): Promise<Want[]> {
|
||||
const stored = await pg.userWants(user);
|
||||
if (stored.length) return stored;
|
||||
const { data: { wants, pagination }} = await api.get(`${apiBase}/users/${user.data.username}/wants`);
|
||||
return Promise.all(wants.map(r => pg.wantUpsert(r, user)))
|
||||
return wantlistLatest(user);
|
||||
}
|
||||
|
||||
async function wantlistLatest(user: User): Promise<Want[]> {
|
||||
const { username } = user.data as { username: string };
|
||||
const { data: { wants, pagination }} = await api.get(`${apiBase}/users/${username}/wants`);
|
||||
return Bluebird.mapSeries(wants, async r => pg.wantUpsert(r, user));
|
||||
}
|
||||
|
||||
async function inventory(user: string) {
|
||||
@@ -44,8 +56,8 @@ export default function discogs(pg: Pg) {
|
||||
return res.data.listings;
|
||||
}
|
||||
|
||||
async function listings(want: Want): Promise<Listing[]> {
|
||||
const res = await api.get(`https://www.discogs.com/sell/release/${want.release}`);
|
||||
async function listings(want: Want) {
|
||||
const res = await api.get(`https://www.discogs.com/sell/release/${want.release}?limit=250`);
|
||||
const $ = cheerio.load(res.data);
|
||||
const ids = $('.item_description_title[href]')
|
||||
.map(function getListingId() {
|
||||
@@ -53,25 +65,29 @@ 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 Bluebird.mapSeries(ids, async id => listing(id, want));
|
||||
}
|
||||
|
||||
async function listing(id, want) {
|
||||
const listing = await pg.listingGet(id);
|
||||
const listing = await pg.listingFind(id);
|
||||
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,
|
||||
@@ -0,0 +1,10 @@
|
||||
import bunyan from 'bunyan';
|
||||
|
||||
export default function createLog() {
|
||||
const log = bunyan.createLogger({
|
||||
name: 'mcrates',
|
||||
stream: process.stdout
|
||||
});
|
||||
|
||||
return log;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
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<User>,
|
||||
userFindName: (name: DiscogsName) => Promise<User>,
|
||||
userUpsert: (user: User) => Promise<User>,
|
||||
userWants: (user: User) => Promise<Want[]>,
|
||||
|
||||
wantUpsert: (want: Want, user: User) => Promise<Want>,
|
||||
listingUpsert: (listing: Listing, want: Want) => Promise<Listing>,
|
||||
listingFind: (id: DiscogsListing) => Promise<Listing>,
|
||||
listingRmUnlisted: (want: Want, ids: DiscogsListing[]) => Promise<number>,
|
||||
|
||||
bundleCandidates: () => Promise<Listing[][]>,
|
||||
}
|
||||
|
||||
export default function createPg(log: Log, prisma: PrismaClient): Pg {
|
||||
async function userFindId(id) {
|
||||
return prisma.user.findUnique({ where: { id }});
|
||||
}
|
||||
|
||||
async function userFindName(name: DiscogsName) {
|
||||
return prisma.user.findUnique({ where: { discogs: name }});
|
||||
}
|
||||
|
||||
async function userWants(user: User) {
|
||||
return prisma.want.findMany({ where: { userId: user.id }});
|
||||
}
|
||||
|
||||
async function userUpsert(user) {
|
||||
const data = {
|
||||
discogs: user.username,
|
||||
data: user as Prisma.JsonObject,
|
||||
};
|
||||
|
||||
return prisma.user.upsert({
|
||||
where: {
|
||||
discogs: user.username,
|
||||
},
|
||||
update: data,
|
||||
create: data,
|
||||
});
|
||||
}
|
||||
|
||||
async function wantUpsert(dcWant, user) {
|
||||
const values: { userId: UUID, release: DiscogsListing, data: any } = {
|
||||
data: dcWant,
|
||||
release: dcWant.id,
|
||||
userId: user.id,
|
||||
};
|
||||
|
||||
return prisma.want.upsert({
|
||||
// get these from the generated types
|
||||
// ./node_modules/.prisma/client/index.d.ts
|
||||
where: { userId_release: {
|
||||
userId: values.userId,
|
||||
release: values.release || null,
|
||||
}},
|
||||
update: values,
|
||||
create: values,
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
wantId: want.id,
|
||||
seller: listing.seller.id,
|
||||
data: listing,
|
||||
};
|
||||
|
||||
return prisma.listing.upsert({
|
||||
where: {
|
||||
id: listing.id
|
||||
},
|
||||
update: values,
|
||||
create: values,
|
||||
});
|
||||
}
|
||||
|
||||
async function listingFind(id: DiscogsListing) {
|
||||
return prisma.listing.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
include: {
|
||||
want: {
|
||||
include: { user: true },
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function bundleCandidates(): Promise<Listing[][]> {
|
||||
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 }
|
||||
},
|
||||
include: {
|
||||
want: {
|
||||
include: { user: true },
|
||||
}
|
||||
},
|
||||
distinct: ['wantId'],
|
||||
})));
|
||||
}
|
||||
|
||||
return {
|
||||
userFindId,
|
||||
userFindName,
|
||||
userUpsert,
|
||||
userWants,
|
||||
|
||||
wantUpsert,
|
||||
listingUpsert,
|
||||
listingFind,
|
||||
listingRmUnlisted,
|
||||
|
||||
bundleCandidates,
|
||||
};
|
||||
}
|
||||
@@ -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. */
|
||||
@@ -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,
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import Knex from 'knex';
|
||||
|
||||
import config from '../knexfile';
|
||||
|
||||
export interface Pg {
|
||||
userGetId: (id: UUID) => Promise<User>,
|
||||
userGetName: (name: string) => Promise<User>,
|
||||
userUpsert: (user: User) => Promise<User>,
|
||||
userWants: (user: User) => Promise<Want[]>,
|
||||
|
||||
wantUpsert: (want: Want, user: User) => Promise<Want>,
|
||||
listingUpsert: (listing: Listing, want: Want) => Promise<Listing>,
|
||||
listingGet: (id: number) => Promise<Listing>,
|
||||
}
|
||||
|
||||
export default function createPg(): Pg {
|
||||
const pg = Knex(config);
|
||||
|
||||
async function userGetId(id): Promise<User> {
|
||||
return pg('users')
|
||||
.where({ id })
|
||||
.first();
|
||||
}
|
||||
|
||||
async function userGetName(name): Promise<User> {
|
||||
return pg('users')
|
||||
.where({ discogs: name })
|
||||
.first();
|
||||
}
|
||||
|
||||
async function userWants(user: User): Promise<Want[]> {
|
||||
return pg('wants')
|
||||
.where({ user: user.id });
|
||||
}
|
||||
|
||||
async function userUpsert(user): Promise<User> {
|
||||
const [row] = await pg('users')
|
||||
.insert({
|
||||
discogs: user.username,
|
||||
data: user,
|
||||
})
|
||||
.onConflict('id')
|
||||
.merge()
|
||||
.onConflict('discogs')
|
||||
.merge()
|
||||
.returning('*');
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
async function wantUpsert(want, user): Promise<Want> {
|
||||
const [row] = await pg('wants')
|
||||
.insert({
|
||||
data: want,
|
||||
release: want.id,
|
||||
user: user.id,
|
||||
})
|
||||
.onConflict('id')
|
||||
.merge()
|
||||
.onConflict(['user', 'release'])
|
||||
.merge()
|
||||
.returning('*');
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
async function listingUpsert(listing, want): Promise<Listing> {
|
||||
const [row] = await pg('listings')
|
||||
.insert({
|
||||
id: listing.id,
|
||||
want: want.id,
|
||||
seller: listing.seller.id,
|
||||
data: listing,
|
||||
})
|
||||
.onConflict('id')
|
||||
.merge()
|
||||
.returning('*');
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
async function listingGet(id): Promise<Listing> {
|
||||
return pg('listings')
|
||||
.where({ id })
|
||||
.first();
|
||||
}
|
||||
|
||||
return {
|
||||
userGetId,
|
||||
userGetName,
|
||||
userUpsert,
|
||||
userWants,
|
||||
|
||||
wantUpsert,
|
||||
listingUpsert,
|
||||
listingGet,
|
||||
};
|
||||
}
|
||||