compare versions

Compare veresions and  update sets -> Cards
This commit is contained in:
2026-03-13 22:28:39 +01:00
parent 16c504dfb5
commit 87897295f0
9 changed files with 269 additions and 137 deletions
+38
View File
@@ -0,0 +1,38 @@
const { setAmountOwned } = require('../models/collectionModel');
async function updateAmountOwned(req, res) {
try {
const { card_id, set_id, rarity_id, amount_owned } = req.body;
if (
card_id == null ||
set_id == null ||
rarity_id == null ||
amount_owned == null
) {
return res.status(400).json({ error: 'Missing required fields' });
}
if (amount_owned < 0) {
return res.status(400).json({ error: 'Amount cannot be negative' });
}
const result = await setAmountOwned({
card_id,
set_id,
rarity_id,
amount_owned
});
res.json({
message: 'Amount updated',
...result
});
} catch (err) {
console.error('Error updating amount:', err);
res.status(500).json({ error: 'Failed to update amount owned' });
}
}
module.exports = { updateAmountOwned };
+29 -37
View File
@@ -1,23 +1,17 @@
const { const {upsertCard} = require('../models/cardModel');
upsertCard const {upsertRarity,getRarityId} = require('../models/rarityModel');
} = require('../models/cardModel'); const { upsertSet } = require('../models/setModel');
const {
upsertRarity,
getRarityId
} = require('../models/rarityModel');
const { insertCardSetRarity } = require('../models/cardSetRarityModel'); const { insertCardSetRarity } = require('../models/cardSetRarityModel');
const { insertCardImage } = require('../models/cardImageModel'); const { insertCardImage } = require('../models/cardImageModel');
const { fetchAllCards } = require('../services/ygoproService'); const { fetchAllCards, fetchAllSets } = require('../services/ygoproService');
const db = require('../config/db'); const db = require('../config/db');
const BATCH_SIZE = 50; const BATCH_SIZE = 50;
async function importSets(req, res) { async function importSetsInternal() {
try {
const sets = await fetchAllSets(); const sets = await fetchAllSets();
let added = 0; let added = 0;
// Parallelized batch insert
for (let i = 0; i < sets.length; i += BATCH_SIZE) { for (let i = 0; i < sets.length; i += BATCH_SIZE) {
const batch = sets.slice(i, i + BATCH_SIZE); const batch = sets.slice(i, i + BATCH_SIZE);
await Promise.all(batch.map(async (set) => { await Promise.all(batch.map(async (set) => {
@@ -26,52 +20,50 @@ async function importSets(req, res) {
})); }));
} }
res.json({ return {
added, added,
total: sets.length total: sets.length
}); };
}
// Express handler for /sets endpoint
async function importSets(req, res) {
try {
const result = await importSetsInternal();
res.json(result);
} catch (err) { } catch (err) {
console.error('Error importing sets:', err); console.error('Error importing sets:', err);
res.status(500).json({ error: 'Failed to import sets' }); res.status(500).json({ error: 'Failed to import sets' });
} }
} }
async function importCards(req, res) { async function importCardsInternal() {
const startTime = Date.now(); const startTime = Date.now();
try {
const cards = await fetchAllCards(); const cards = await fetchAllCards();
const totalCards = cards.length; const totalCards = cards.length;
let addedCards = 0, addedImages = 0, addedRarities = 0; let addedCards = 0, addedImages = 0, addedRarities = 0;
// Cache sets and rarities to reduce repeated DB lookups
const rarityCache = {}; const rarityCache = {};
const setCache = {}; const setCache = {};
// Preload sets into cache const [setsRows] = await db.execute('SELECT id, set_name FROM sets');
const [setsRows] = await db.execute('SELECT id, set_code FROM sets');
for (const row of setsRows) { for (const row of setsRows) {
setCache[row.set_code] = row.id; setCache[row.set_name] = row.id;
} }
// Preload rarities into cache
const [rarityRows] = await db.execute('SELECT id, rarity_name FROM rarities'); const [rarityRows] = await db.execute('SELECT id, rarity_name FROM rarities');
for (const row of rarityRows) { for (const row of rarityRows) {
rarityCache[row.rarity_name] = row.id; rarityCache[row.rarity_name] = row.id;
} }
// Process cards in batches
for (let i = 0; i < totalCards; i += BATCH_SIZE) { for (let i = 0; i < totalCards; i += BATCH_SIZE) {
const batch = cards.slice(i, i + BATCH_SIZE); const batch = cards.slice(i, i + BATCH_SIZE);
// Wrap batch in a transaction
const conn = await db.getConnection(); const conn = await db.getConnection();
try { try {
await conn.beginTransaction(); await conn.beginTransaction();
await Promise.all(batch.map(async (card) => { await Promise.all(batch.map(async (card) => {
// Upsert card
await conn.execute(` await conn.execute(`
INSERT INTO cards INSERT INTO cards
(id, name, card_type, frame_type, level, race, attribute, link_val, tcg_date, ocg_date) (id, name, card_type, frame_type, level, race, attribute, link_val, tcg_date, ocg_date)
@@ -100,10 +92,8 @@ async function importCards(req, res) {
]); ]);
addedCards++; addedCards++;
// Process rarities and card_sets_rarity
if (Array.isArray(card.card_sets)) { if (Array.isArray(card.card_sets)) {
for (const set of card.card_sets) { for (const set of card.card_sets) {
// Upsert rarity if not cached
if (!rarityCache[set.set_rarity]) { if (!rarityCache[set.set_rarity]) {
await conn.execute(` await conn.execute(`
INSERT INTO rarities (rarity_name, rarity_code) INSERT INTO rarities (rarity_name, rarity_code)
@@ -115,9 +105,7 @@ async function importCards(req, res) {
addedRarities++; addedRarities++;
} }
const rarity_id = rarityCache[set.set_rarity]; const rarity_id = rarityCache[set.set_rarity];
const set_id = setCache[set.set_name];
// Get set_id from cache
const set_id = setCache[set.set_code];
if (set_id) { if (set_id) {
await conn.execute(` await conn.execute(`
INSERT INTO card_sets_rarity (card_id, set_id, rarity_id, card_set_code) INSERT INTO card_sets_rarity (card_id, set_id, rarity_id, card_set_code)
@@ -128,7 +116,6 @@ async function importCards(req, res) {
} }
} }
// Process card images
if (Array.isArray(card.card_images)) { if (Array.isArray(card.card_images)) {
for (const img of card.card_images) { for (const img of card.card_images) {
await conn.execute(` await conn.execute(`
@@ -148,25 +135,30 @@ async function importCards(req, res) {
conn.release(); conn.release();
} }
// Release batch memory
batch.length = 0; batch.length = 0;
} }
const endTime = Date.now(); const durationSeconds = ((Date.now() - startTime) / 1000).toFixed(2);
const durationSeconds = ((endTime - startTime) / 1000).toFixed(2);
res.json({ return {
total_cards: totalCards, total_cards: totalCards,
cards_added: addedCards, cards_added: addedCards,
images_added: addedImages, images_added: addedImages,
rarities_added: addedRarities, rarities_added: addedRarities,
duration_seconds: parseFloat(durationSeconds) duration_seconds: parseFloat(durationSeconds)
}); };
}
// Express handler for /cards
async function importCards(req, res) {
try {
const result = await importCardsInternal();
res.json(result);
} catch (err) { } catch (err) {
console.error('Error importing cards:', err); console.error('Error importing cards:', err);
res.status(500).json({ error: 'Failed to import cards' }); res.status(500).json({ error: 'Failed to import cards' });
} }
} }
module.exports = { importCards, importSets };
module.exports = { importCards, importCardsInternal, importSets, importSetsInternal };
+42
View File
@@ -0,0 +1,42 @@
const { fetchDatabaseVersion } = require('../services/ygoproService');
const { getLocalDBVersion, setLocalDBVersion } = require('../models/dbVersionModel');
const { importSetsInternal } = require('./importController');
const { importCardsInternal } = require('./importController');
async function importFullDatabase(req, res) {
const startTime = Date.now();
try {
const remoteVersion = await fetchDatabaseVersion();
const localVersion = await getLocalDBVersion();
if (localVersion && localVersion.database_version === remoteVersion.database_version) {
return res.json({
message: 'Database is already up to date.',
version: remoteVersion.database_version
});
}
const setsStats = await importSetsInternal();
const cardsStats = await importCardsInternal();
await setLocalDBVersion(remoteVersion.database_version, remoteVersion.last_update);
const totalDuration = ((Date.now() - startTime) / 1000).toFixed(2);
res.json({
message: 'Database import completed successfully.',
version: remoteVersion.database_version,
sets: setsStats,
cards: cardsStats,
duration_seconds: parseFloat(totalDuration)
});
} catch (err) {
console.error('Error in full database import:', err);
res.status(500).json({ error: 'Failed to import database' });
}
}
module.exports = { importFullDatabase };
+3
View File
@@ -11,3 +11,6 @@ const PORT = process.env.PORT || 3000;
app.listen(PORT, () => { app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`); console.log(`Server running on http://localhost:${PORT}`);
}); });
const collectionRoutes = require('./routes/collectionRoutes');
app.use('/collection', collectionRoutes);
+14
View File
@@ -0,0 +1,14 @@
const db = require('../config/db');
async function setAmountOwned({ card_id, set_id, rarity_id, amount_owned }) {
await db.execute(`
INSERT INTO card_sets_rarity (card_id, set_id, rarity_id, amount_owned)
VALUES (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
amount_owned = VALUES(amount_owned)
`, [card_id, set_id, rarity_id, amount_owned]);
return { card_id, set_id, rarity_id, amount_owned };
}
module.exports = { setAmountOwned };
+17
View File
@@ -0,0 +1,17 @@
const db = require('../config/db');
async function getLocalDBVersion() {
const [rows] = await db.execute(
'SELECT database_version, last_update FROM db_version ORDER BY last_update DESC LIMIT 1'
);
return rows.length ? rows[0] : null;
}
async function setLocalDBVersion(version, date) {
await db.execute(
'INSERT INTO db_version (database_version, last_update) VALUES (?, ?)',
[version ?? null, date ?? null]
);
}
module.exports = { getLocalDBVersion, setLocalDBVersion };
+7
View File
@@ -0,0 +1,7 @@
const express = require('express');
const router = express.Router();
const { updateAmountOwned } = require('../controllers/collectionController');
router.put('/amount', updateAmountOwned);
module.exports = router;
+2
View File
@@ -1,8 +1,10 @@
const express = require('express'); const express = require('express');
const router = express.Router(); const router = express.Router();
const { importSets, importCards} = require('../controllers/importController'); const { importSets, importCards} = require('../controllers/importController');
const { importFullDatabase } = require('../controllers/newVersionController');
router.post('/sets', importSets); router.post('/sets', importSets);
router.post('/cards', importCards); router.post('/cards', importCards);
router.post('/full-import', importFullDatabase);
module.exports = router; module.exports = router;
+19 -2
View File
@@ -22,12 +22,29 @@ async function fetchAllSets() {
async function fetchAllCards() { async function fetchAllCards() {
try { try {
const response = await axios.get(`${API_BASE}/cardinfo.php?misc=yes`); const response = await axios.get(`${API_BASE}/cardinfo.php?misc=yes`);
return response.data.data; // array of card objects return response.data.data;
} catch (err) { } catch (err) {
console.error('Error fetching cards from YGOPRODeck:', err.message); console.error('Error fetching cards from YGOPRODeck:', err.message);
throw err; throw err;
} }
} }
async function fetchDatabaseVersion() {
const response = await axios.get(`${API_BASE}/checkDBVer.php`);
const data = response.data;
module.exports = { fetchAllCards, fetchAllSets }; // Handle array response from API
const versionInfo = Array.isArray(data) ? data[0] : data;
if (!versionInfo || !versionInfo.database_version || !versionInfo.last_update) {
throw new Error('Invalid database version response from YGOPRODeck API');
}
return {
database_version: versionInfo.database_version,
last_update: versionInfo.last_update
};
}
module.exports = { fetchAllCards, fetchAllSets, fetchDatabaseVersion };