Intial Version
This commit is contained in:
7
backend/src/database/prisma.ts
Normal file
7
backend/src/database/prisma.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient({
|
||||
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
|
||||
});
|
||||
|
||||
export default prisma;
|
||||
195
backend/src/database/repository.ts
Normal file
195
backend/src/database/repository.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import prisma from './prisma';
|
||||
import { VendorListing } from '../scrapers/types';
|
||||
|
||||
export class PriceRepository {
|
||||
async saveListings(listings: VendorListing[]): Promise<void> {
|
||||
// Use a single timestamp for all listings in this batch
|
||||
const batchTimestamp = new Date();
|
||||
|
||||
await prisma.vendorPrice.createMany({
|
||||
data: listings.map(listing => ({
|
||||
vendor: listing.vendor,
|
||||
sellerName: listing.seller,
|
||||
usdPrice: listing.priceUSD,
|
||||
auecAmount: listing.amountAUEC,
|
||||
usdPerMillion: listing.pricePerMillion,
|
||||
deliveryTime: listing.deliveryTime,
|
||||
url: listing.url,
|
||||
timestamp: batchTimestamp,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
async savePriceIndex(lowestPrice: number, vendor: string, sellerName?: string): Promise<void> {
|
||||
await prisma.priceIndex.create({
|
||||
data: {
|
||||
lowestPrice,
|
||||
vendor,
|
||||
sellerName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async logScrape(status: string, message?: string, runtimeMs?: number): Promise<void> {
|
||||
await prisma.scrapeLog.create({
|
||||
data: {
|
||||
status,
|
||||
message,
|
||||
runtimeMs,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getLatestPrices() {
|
||||
// Get the most recent timestamp across all vendors
|
||||
const latest = await prisma.vendorPrice.findFirst({
|
||||
orderBy: { timestamp: 'desc' },
|
||||
select: { timestamp: true },
|
||||
});
|
||||
|
||||
if (!latest) return [];
|
||||
|
||||
// Get all prices from that timestamp (all vendors)
|
||||
return prisma.vendorPrice.findMany({
|
||||
where: { timestamp: latest.timestamp },
|
||||
orderBy: { usdPerMillion: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
async getLatestPricesOld() {
|
||||
// Get the most recent timestamp for each vendor
|
||||
const latestEldorado = await prisma.vendorPrice.findFirst({
|
||||
where: { vendor: 'eldorado' },
|
||||
orderBy: { timestamp: 'desc' },
|
||||
select: { timestamp: true },
|
||||
});
|
||||
|
||||
const latestPlayerAuctions = await prisma.vendorPrice.findFirst({
|
||||
where: { vendor: 'playerauctions' },
|
||||
orderBy: { timestamp: 'desc' },
|
||||
select: { timestamp: true },
|
||||
});
|
||||
|
||||
// Get all prices from the latest scrape of each vendor
|
||||
const prices = [];
|
||||
|
||||
if (latestEldorado) {
|
||||
const eldoradoPrices = await prisma.vendorPrice.findMany({
|
||||
where: {
|
||||
vendor: 'eldorado',
|
||||
timestamp: latestEldorado.timestamp,
|
||||
},
|
||||
});
|
||||
prices.push(...eldoradoPrices);
|
||||
}
|
||||
|
||||
if (latestPlayerAuctions) {
|
||||
// Get all PlayerAuctions listings within 30 seconds of the latest timestamp
|
||||
const timeWindow = new Date(latestPlayerAuctions.timestamp.getTime() - 30000);
|
||||
const paPrices = await prisma.vendorPrice.findMany({
|
||||
where: {
|
||||
vendor: 'playerauctions',
|
||||
timestamp: {
|
||||
gte: timeWindow,
|
||||
lte: latestPlayerAuctions.timestamp,
|
||||
},
|
||||
},
|
||||
});
|
||||
prices.push(...paPrices);
|
||||
}
|
||||
|
||||
// Sort by price
|
||||
return prices.sort((a, b) =>
|
||||
Number(a.usdPerMillion) - Number(b.usdPerMillion)
|
||||
);
|
||||
}
|
||||
|
||||
async getLowestPrice() {
|
||||
const latest = await prisma.priceIndex.findFirst({
|
||||
orderBy: { timestamp: 'desc' },
|
||||
});
|
||||
|
||||
return latest;
|
||||
}
|
||||
|
||||
async getPricesBySeller(seller: string, platform?: string) {
|
||||
return prisma.vendorPrice.findMany({
|
||||
where: {
|
||||
sellerName: seller,
|
||||
...(platform && { vendor: platform }),
|
||||
},
|
||||
orderBy: { timestamp: 'desc' },
|
||||
take: 100,
|
||||
});
|
||||
}
|
||||
|
||||
async getPricesByPlatform(platform: string) {
|
||||
const latest = await prisma.vendorPrice.findFirst({
|
||||
where: { vendor: platform },
|
||||
orderBy: { timestamp: 'desc' },
|
||||
select: { timestamp: true },
|
||||
});
|
||||
|
||||
if (!latest) return [];
|
||||
|
||||
return prisma.vendorPrice.findMany({
|
||||
where: {
|
||||
vendor: platform,
|
||||
timestamp: latest.timestamp,
|
||||
},
|
||||
orderBy: { usdPerMillion: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
async getPriceHistory(from: Date, to: Date, seller?: string, platform?: string) {
|
||||
return prisma.vendorPrice.findMany({
|
||||
where: {
|
||||
timestamp: {
|
||||
gte: from,
|
||||
lte: to,
|
||||
},
|
||||
...(seller && { sellerName: seller }),
|
||||
...(platform && { vendor: platform }),
|
||||
},
|
||||
orderBy: { timestamp: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
async getIndexHistory(range: string) {
|
||||
const now = new Date();
|
||||
let from = new Date();
|
||||
|
||||
switch (range) {
|
||||
case '1h':
|
||||
from = new Date(now.getTime() - 60 * 60 * 1000);
|
||||
break;
|
||||
case '6h':
|
||||
from = new Date(now.getTime() - 6 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '24h':
|
||||
from = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '7d':
|
||||
from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '30d':
|
||||
from = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
||||
break;
|
||||
case '90d':
|
||||
from = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
|
||||
break;
|
||||
case 'all':
|
||||
from = new Date(0);
|
||||
break;
|
||||
default:
|
||||
from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
return prisma.priceIndex.findMany({
|
||||
where: {
|
||||
timestamp: { gte: from },
|
||||
},
|
||||
orderBy: { timestamp: 'asc' },
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user