[feat] update drizzle to v2 and add card-price relations

This commit is contained in:
2026-02-13 21:28:33 -05:00
parent 18b8774a89
commit a774360065
8 changed files with 1029 additions and 603 deletions

1450
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,14 +14,14 @@
"bootstrap": "^5.3.8", "bootstrap": "^5.3.8",
"chalk": "^5.6.2", "chalk": "^5.6.2",
"dotenv": "^17.2.4", "dotenv": "^17.2.4",
"drizzle-orm": "^0.45.1", "drizzle-orm": "^1.0.0-beta.15-859cf75",
"mysql2": "^3.16.3", "mysql2": "^3.16.3",
"sass": "^1.97.3" "sass": "^1.97.3"
}, },
"devDependencies": { "devDependencies": {
"@types/bootstrap": "^5.2.10", "@types/bootstrap": "^5.2.10",
"@types/node": "^25.2.1", "@types/node": "^25.2.1",
"drizzle-kit": "^0.31.8", "drizzle-kit": "^1.0.0-beta.15-859cf75",
"typescript": "^5.9.3" "typescript": "^5.9.3"
} }
} }

View File

@@ -48,4 +48,4 @@
// @import 'bootstrap/scss/helpers'; // @import 'bootstrap/scss/helpers';
// Optional utilities // Optional utilities
// @import 'bootstrap/scss/utilities/api'; @import 'bootstrap/scss/utilities/api';

View File

@@ -1,3 +1,5 @@
@import '_bootstrap'; @import '_bootstrap';
.copy-small {
font-size: 0.8rem;
}

View File

@@ -1,11 +1,11 @@
// src/db/index.ts // src/db/index.ts
import 'dotenv/config'; import 'dotenv/config';
import { relations } from './relations';
import { drizzle } from 'drizzle-orm/mysql2'; import { drizzle } from 'drizzle-orm/mysql2';
import mysql from 'mysql2/promise'; //import mysql from 'mysql2/promise';
import * as schema from './schema'; //import * as schema from './schema';
const poolConnection = mysql.createPool({ //const poolConnection = mysql.createPool(process.env.DATABASE_URL!);
uri: process.env.DATABASE_URL,
}); export const db = drizzle(process.env.DATABASE_URL!, { relations });
export const db = drizzle(poolConnection, { schema, mode: 'default' });

16
src/db/relations.ts Normal file
View File

@@ -0,0 +1,16 @@
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
skus: {
card: r.one.cards({
from: r.skus.productId,
to: r.cards.productId,
}),
},
cards: {
prices: r.many.skus(),
},
}));

View File

@@ -1,63 +1,63 @@
// src/db/schema.ts import { mysqlTable, int, varchar, boolean, decimal, datetime, index } from "drizzle-orm/mysql-core"
import { mysqlTable, varchar, int, boolean, decimal, datetime, index } from 'drizzle-orm/mysql-core';
export const cards = mysqlTable('cards', { export const cards = mysqlTable("cards", {
productId: int().notNull().primaryKey(), productId: int().primaryKey(),
productName: varchar({ length: 255 }).notNull(), productName: varchar({ length: 255 }).notNull(),
productLineName: varchar({ length: 255 }).notNull().default(''), productLineName: varchar({ length: 255 }).default("").notNull(),
productLineUrlName: varchar({ length: 255 }).notNull().default(''), productLineUrlName: varchar({ length: 255 }).default("").notNull(),
productStatusId: int().notNull().default(0), productStatusId: int().default(0).notNull(),
productTypeId: int().notNull().default(0), productTypeId: int().default(0).notNull(),
productUrlName: varchar({ length: 255 }).notNull().default(''), productUrlName: varchar({ length: 255 }).default("").notNull(),
rarityName: varchar({ length: 100 }).notNull().default(''), rarityName: varchar({ length: 100 }).default("").notNull(),
score: decimal({ precision: 10, scale: 2 }).notNull().default('0'), sealed: boolean().default(false).notNull(),
sealed: boolean().notNull().default(false), sellerListable: boolean().default(false).notNull(),
sellerListable: boolean().notNull().default(false), setId: int().default(0).notNull(),
setId: int().notNull().default(0), shippingCategoryId: int().default(0).notNull(),
shippingCategoryId: int().notNull().default(0), duplicate: boolean().default(false).notNull(),
duplicate: boolean().notNull().default(false), foilOnly: boolean().default(false).notNull(),
foilOnly: boolean().notNull().default(false), maxFulfillableQuantity: int().default(0).notNull(),
attack1: varchar({ length: 1024 }), totalListings: int().default(0).notNull(),
attack2: varchar({ length: 1024 }), score: decimal({ precision: 10, scale: 2, mode: 'number' }).default(0).notNull(),
attack3: varchar({ length: 1024 }), lowestPrice: decimal({ precision: 10, scale: 2, mode: 'number' }).default(0).notNull(),
attack4: varchar({ length: 1024 }), lowestPriceWithShipping: decimal({ precision: 10, scale: 2, mode: 'number' }).default(0).notNull(),
cardType: varchar({ length: 100 }), marketPrice: decimal({ precision: 10, scale: 2, mode: 'number' }).default(0).notNull(),
cardTypeB: varchar({ length: 100 }), medianPrice: decimal({ precision: 10, scale: 2, mode: 'number' }).default(0).notNull(),
energyType: varchar({ length: 100 }), attack1: varchar({ length: 1024 }),
flavorText: varchar({ length: 1000 }), attack2: varchar({ length: 1024 }),
hp: int().notNull().default(0), attack3: varchar({ length: 1024 }),
number: varchar({ length: 50 }).notNull().default(''), attack4: varchar({ length: 1024 }),
releaseDate: datetime(), cardType: varchar({ length: 100 }),
resistance: varchar({ length: 100 }), cardTypeB: varchar({ length: 100 }),
retreatCost: varchar({ length: 100 }), energyType: varchar({ length: 100 }),
stage: varchar({ length: 100 }), flavorText: varchar({ length: 1000 }),
weakness: varchar({ length: 100 }), hp: int().default(0).notNull(),
lowestPrice: decimal({ precision: 10, scale: 2 }).notNull().default('0'), number: varchar({ length: 50 }).default("").notNull(),
lowestPriceWithShipping: decimal({ precision: 10, scale: 2 }).notNull().default('0'), releaseDate: datetime(),
marketPrice: decimal({ precision: 10, scale: 2 }).notNull().default('0'), resistance: varchar({ length: 100 }),
maxFulfillableQuantity: int().notNull().default(0), retreatCost: varchar({ length: 100 }),
medianPrice: decimal({ precision: 10, scale: 2 }).notNull().default('0'), stage: varchar({ length: 100 }),
totalListings: int().notNull().default(0), weakness: varchar({ length: 100 }),
}); });
export const sets = mysqlTable('sets', { export const sets = mysqlTable("sets", {
setId: int().notNull().primaryKey(), setId: int().primaryKey(),
setCode: varchar({ length: 100 }).notNull(), setName: varchar({ length: 255 }).notNull(),
setName: varchar({ length: 255 }).notNull(), setUrlName: varchar({ length: 255 }).notNull(),
setUrlName: varchar({ length: 255 }).notNull(), setCode: varchar({ length: 100 }).notNull(),
}); });
export const skus = mysqlTable('skus', { export const skus = mysqlTable("skus", {
skuId: int().notNull().primaryKey(), skuId: int().primaryKey(),
productId: int().notNull(), productId: int().notNull(),
condition: varchar({ length: 255 }).notNull(), condition: varchar({ length: 255 }).notNull(),
language: varchar({ length: 100 }).notNull(), language: varchar({ length: 100 }).notNull(),
variant: varchar({ length: 100 }).notNull(), variant: varchar({ length: 100 }).notNull(),
calculatedAt: datetime(), calculatedAt: datetime(),
highestPrice: decimal({ precision: 10, scale: 2 }), highestPrice: decimal({ precision: 10, scale: 2 }),
lowestPrice: decimal({ precision: 10, scale: 2 }), lowestPrice: decimal({ precision: 10, scale: 2 }),
marketPrice: decimal({ precision: 10, scale: 2 }), marketPrice: decimal({ precision: 10, scale: 2 }),
priceCount: int(), priceCount: int(),
},(table) => ({ },
productIdIdx: index('productIdIdx').on(table.productId), (table) => [
})); index("productIdIdx").on(table.productId),
]);

View File

@@ -1,12 +1,20 @@
--- ---
import Layout from '../layouts/Main.astro'; import Layout from '../layouts/Main.astro';
import * as schema from '../db/schema.ts'; //import { eq } from 'drizzle-orm';
import { eq } from 'drizzle-orm';
import { db } from '../db'; import { db } from '../db';
//import * as schema from '../db/schema.ts';
// Get some sample Pokemon data from the database // Get some sample Pokemon data from the database
const pokemon = await db.select().from(schema.cards).where(eq(schema.cards.productLineName, "pokemon")).limit(10); //const pokemon = await db.select().from(schema.cards).where(eq(schema.cards.productLineName, "pokemon")).limit(16);
const pokemon = await db.query.cards.findMany({
where: { productLineName: "pokemon" },
limit: 16,
with: {
prices: true,
}
});
--- ---
<Layout> <Layout>
@@ -16,11 +24,19 @@ const pokemon = await db.select().from(schema.cards).where(eq(schema.cards.produ
<h1>Pokemon</h1> <h1>Pokemon</h1>
</div> </div>
</div> </div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4"> <div class="row gy-4 row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4">
{pokemon.map((card) => ( {pokemon.map((card) => (
<div class="col"> <div class="col">
<img src={`/public/cards/${card.productId}.jpg`} alt={card.productName} class="img-fluid" /> <img src={`/cards/${card.productId}.jpg`} alt={card.productName} class="img-fluid" />
{card.productName} - {card.number} <div class="row justify-content-between">
<div class="col-auto"><strong>{card.productName}</strong></div>
<div class="col-auto">{card.number}</div>
</div>
<div class="row">
{card.prices.map((price) => (
<div class="col-auto copy-small">{price.condition.split(' ').map((word) => word.charAt(0)).join('')}<br />{price.marketPrice}</div>
))}
</div>
</div> </div>
))} ))}