Add sets endpoints (GET /sets and GET /sets/:setId/cards)
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -1 +1,53 @@
|
|||||||
## Start Api: node src/index.js
|
# YuGiOh Collection Manager — API
|
||||||
|
|
||||||
|
Node.js/Express API for the YuGiOh collection manager. Handles card data, set browsing, image serving, and DB imports from YGOPRODeck.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
- **Node.js** + **Express 5**
|
||||||
|
- **MySQL2** (connection pool)
|
||||||
|
- **Axios** — YGOPRODeck API requests + card image fetching
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | `/health` | Health check |
|
||||||
|
| GET | `/exportCards` | All cards with printings and owned counts |
|
||||||
|
| GET | `/cardImage/:cardId` | Card image (fetches + caches blob on first request) |
|
||||||
|
| GET | `/sets` | All sets with owned card counts |
|
||||||
|
| GET | `/sets/:setId/cards` | Cards in a specific set |
|
||||||
|
| PUT | `/collection/amount` | Update owned count for a printing |
|
||||||
|
| POST | `/import/full-import` | Full DB import from YGOPRODeck |
|
||||||
|
| POST | `/import/sets` | Import sets only |
|
||||||
|
| POST | `/import/cards` | Import cards only |
|
||||||
|
| GET | `/db-version` | Current local DB version |
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
cp .env.example .env # fill in DB credentials
|
||||||
|
node src/index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
Built as a Docker image. CI/CD via Woodpecker on push to `main`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t yugioh-api .
|
||||||
|
docker run -d --name yugioh-api --network yugioh --env-file .env yugioh-api
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|---|---|
|
||||||
|
| `DB_HOST` | MySQL host |
|
||||||
|
| `DB_PORT` | MySQL port (default 3306) |
|
||||||
|
| `DB_USER` | MySQL user |
|
||||||
|
| `DB_PASSWORD` | MySQL password |
|
||||||
|
| `DB_NAME` | Database name |
|
||||||
|
| `YGOPRO_API_BASE` | YGOPRODeck API base URL |
|
||||||
|
| `PORT` | Server port (default 3000) |
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
const db = require('../config/db');
|
||||||
|
|
||||||
|
async function getSets(req, res) {
|
||||||
|
try {
|
||||||
|
const [rows] = await db.execute(`
|
||||||
|
SELECT
|
||||||
|
s.id, s.set_name, s.set_code, s.num_of_cards, s.tcg_date,
|
||||||
|
COUNT(DISTINCT csr.card_id) AS total_in_db,
|
||||||
|
COUNT(DISTINCT CASE WHEN csr.amount_owned > 0 THEN csr.card_id END) AS owned_count
|
||||||
|
FROM sets s
|
||||||
|
LEFT JOIN card_sets_rarity csr ON s.id = csr.set_id
|
||||||
|
GROUP BY s.id
|
||||||
|
ORDER BY s.tcg_date DESC, s.set_name ASC
|
||||||
|
`);
|
||||||
|
res.json(rows);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching sets:', err);
|
||||||
|
res.status(500).json({ error: 'Failed to fetch sets' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSetCards(req, res) {
|
||||||
|
const { setId } = req.params;
|
||||||
|
try {
|
||||||
|
const [rows] = await db.execute(`
|
||||||
|
SELECT
|
||||||
|
c.id, c.name, c.card_type AS type, c.frame_type,
|
||||||
|
csr.card_set_code AS set_code,
|
||||||
|
r.id AS rarity_id, r.rarity_name, r.rarity_code,
|
||||||
|
csr.amount_owned
|
||||||
|
FROM card_sets_rarity csr
|
||||||
|
JOIN cards c ON csr.card_id = c.id
|
||||||
|
JOIN rarities r ON csr.rarity_id = r.id
|
||||||
|
WHERE csr.set_id = ?
|
||||||
|
ORDER BY c.name ASC
|
||||||
|
`, [setId]);
|
||||||
|
res.json(rows);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching set cards:', err);
|
||||||
|
res.status(500).json({ error: 'Failed to fetch set cards' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { getSets, getSetCards };
|
||||||
@@ -7,6 +7,7 @@ const collectionRoutes = require('./routes/collectionRoutes');
|
|||||||
const exportCardsRoutes = require('./routes/exportCardsRoutes');
|
const exportCardsRoutes = require('./routes/exportCardsRoutes');
|
||||||
const cardImageRoutes = require('./routes/cardImageRoutes');
|
const cardImageRoutes = require('./routes/cardImageRoutes');
|
||||||
const versionRoutes = require('./routes/versionRoutes');
|
const versionRoutes = require('./routes/versionRoutes');
|
||||||
|
const setsRoutes = require('./routes/setsRoutes');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
@@ -19,6 +20,7 @@ app.use('/collection', collectionRoutes);
|
|||||||
app.use('/exportCards', exportCardsRoutes);
|
app.use('/exportCards', exportCardsRoutes);
|
||||||
app.use('/cardImage', cardImageRoutes);
|
app.use('/cardImage', cardImageRoutes);
|
||||||
app.use('/db-version', versionRoutes);
|
app.use('/db-version', versionRoutes);
|
||||||
|
app.use('/sets', setsRoutes);
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { getSets, getSetCards } = require('../controllers/setsController');
|
||||||
|
|
||||||
|
router.get('/', getSets);
|
||||||
|
router.get('/:setId/cards', getSetCards);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
Reference in New Issue
Block a user