|
1 | 1 | import { requireUser } from "@/proxy/authenticate"; |
2 | 2 | import clientPromise from "@/utils/mongo.js"; |
3 | 3 | import { ensureSafeUserId } from "@/utils/securityValidators.js"; |
| 4 | +import fs from "fs/promises"; |
| 5 | +import path from "path"; |
| 6 | + |
| 7 | +const CARD_DATA_PATH = path.join( process.cwd(), "public", "card-data", "Yugioh", "card_data.json" ); |
| 8 | + |
| 9 | +let cachedCardImageIndex = null; |
| 10 | + |
| 11 | +const normalizeToken = ( value ) => |
| 12 | + ( value ?? "" ).toString().toLowerCase().replace( /[^a-z0-9]+/g, "" ).trim(); |
| 13 | + |
| 14 | +const getPrimaryCardImage = ( card ) => |
| 15 | + Array.isArray( card?.card_images ) |
| 16 | + ? card.card_images.find( ( image ) => image?.id || image?.image_url || image?.image_url_cropped || image?.image_url_small ) |
| 17 | + : null; |
| 18 | + |
| 19 | +const hasRemoteImageSource = ( card ) => { |
| 20 | + const primaryCardImage = getPrimaryCardImage( card ); |
| 21 | + return Boolean( |
| 22 | + card?.remoteImageUrl || |
| 23 | + card?.image_url || |
| 24 | + primaryCardImage?.image_url || |
| 25 | + primaryCardImage?.image_url_cropped || |
| 26 | + primaryCardImage?.image_url_small |
| 27 | + ); |
| 28 | +}; |
| 29 | + |
| 30 | +const readCardImageIndex = async () => { |
| 31 | + if ( cachedCardImageIndex ) { |
| 32 | + return cachedCardImageIndex; |
| 33 | + } |
| 34 | + |
| 35 | + const fileContents = await fs.readFile( CARD_DATA_PATH, "utf8" ); |
| 36 | + const parsed = JSON.parse( fileContents ); |
| 37 | + const cards = Array.isArray( parsed?.data ) ? parsed.data : Array.isArray( parsed ) ? parsed : []; |
| 38 | + const byId = new Map(); |
| 39 | + const byName = new Map(); |
| 40 | + const bySetCode = new Map(); |
| 41 | + const bySetNameAndCode = new Map(); |
| 42 | + |
| 43 | + cards.forEach( ( card ) => { |
| 44 | + const primaryImage = getPrimaryCardImage( card ); |
| 45 | + if ( !primaryImage?.id && !primaryImage?.image_url ) { |
| 46 | + return; |
| 47 | + } |
| 48 | + |
| 49 | + const imageMeta = { |
| 50 | + cardId: card?.id ? String( card.id ) : primaryImage?.id ? String( primaryImage.id ) : null, |
| 51 | + card_images: [ { |
| 52 | + id: primaryImage?.id ?? card?.id ?? null, |
| 53 | + image_url: primaryImage?.image_url ?? null, |
| 54 | + image_url_small: primaryImage?.image_url_small ?? null, |
| 55 | + image_url_cropped: primaryImage?.image_url_cropped ?? null, |
| 56 | + } ], |
| 57 | + remoteImageUrl: primaryImage?.image_url ?? null, |
| 58 | + }; |
| 59 | + |
| 60 | + if ( imageMeta.cardId ) { |
| 61 | + byId.set( normalizeToken( imageMeta.cardId ), imageMeta ); |
| 62 | + } |
| 63 | + |
| 64 | + const nameKey = normalizeToken( card?.name ); |
| 65 | + if ( nameKey && !byName.has( nameKey ) ) { |
| 66 | + byName.set( nameKey, imageMeta ); |
| 67 | + } |
| 68 | + |
| 69 | + if ( Array.isArray( card?.card_sets ) ) { |
| 70 | + card.card_sets.forEach( ( setEntry ) => { |
| 71 | + const setCodeKey = normalizeToken( setEntry?.set_code ); |
| 72 | + if ( setCodeKey && !bySetCode.has( setCodeKey ) ) { |
| 73 | + bySetCode.set( setCodeKey, imageMeta ); |
| 74 | + } |
| 75 | + |
| 76 | + const setNameCodeKey = `${ normalizeToken( setEntry?.set_name ) }::${ setCodeKey }`; |
| 77 | + if ( setCodeKey && !bySetNameAndCode.has( setNameCodeKey ) ) { |
| 78 | + bySetNameAndCode.set( setNameCodeKey, imageMeta ); |
| 79 | + } |
| 80 | + } ); |
| 81 | + } |
| 82 | + } ); |
| 83 | + |
| 84 | + cachedCardImageIndex = { byId, byName, bySetCode, bySetNameAndCode }; |
| 85 | + return cachedCardImageIndex; |
| 86 | +}; |
| 87 | + |
| 88 | +const findImageMeta = ( card, index ) => { |
| 89 | + const idCandidates = [ card?.cardId, card?.cardImageId, card?.id ]; |
| 90 | + for ( const candidate of idCandidates ) { |
| 91 | + const match = index.byId.get( normalizeToken( candidate ) ); |
| 92 | + if ( match ) return match; |
| 93 | + } |
| 94 | + |
| 95 | + const setCodeKey = normalizeToken( card?.number ); |
| 96 | + const setNameCodeKey = `${ normalizeToken( card?.setName ) }::${ setCodeKey }`; |
| 97 | + return ( |
| 98 | + index.bySetNameAndCode.get( setNameCodeKey ) || |
| 99 | + index.bySetCode.get( setCodeKey ) || |
| 100 | + index.byName.get( normalizeToken( card?.productName ) ) || |
| 101 | + null |
| 102 | + ); |
| 103 | +}; |
| 104 | + |
| 105 | +const enrichCollectionCardImages = async ( cards ) => { |
| 106 | + if ( !Array.isArray( cards ) || cards.every( hasRemoteImageSource ) ) { |
| 107 | + return cards; |
| 108 | + } |
| 109 | + |
| 110 | + try { |
| 111 | + const index = await readCardImageIndex(); |
| 112 | + return cards.map( ( card ) => { |
| 113 | + if ( hasRemoteImageSource( card ) ) { |
| 114 | + return card; |
| 115 | + } |
| 116 | + |
| 117 | + const imageMeta = findImageMeta( card, index ); |
| 118 | + return imageMeta |
| 119 | + ? { |
| 120 | + ...card, |
| 121 | + cardId: card?.cardId ?? imageMeta.cardId, |
| 122 | + cardImageId: card?.cardImageId ?? imageMeta.cardId, |
| 123 | + remoteImageUrl: imageMeta.remoteImageUrl ?? card?.remoteImageUrl ?? null, |
| 124 | + card_images: Array.isArray( card?.card_images ) && card.card_images.length > 0 |
| 125 | + ? card.card_images |
| 126 | + : imageMeta.card_images, |
| 127 | + } |
| 128 | + : card; |
| 129 | + } ); |
| 130 | + } catch ( error ) { |
| 131 | + console.error( "Unable to enrich collection card images:", error ); |
| 132 | + return cards; |
| 133 | + } |
| 134 | +}; |
4 | 135 |
|
5 | 136 | export default async function handler( req, res ) { |
6 | 137 | const auth = await requireUser( req, res ); |
@@ -44,7 +175,8 @@ export default async function handler( req, res ) { |
44 | 175 | ]; |
45 | 176 |
|
46 | 177 | const result = await collection.aggregate( agg ).toArray(); |
47 | | - return res.status( 200 ).json( result ); |
| 178 | + const enrichedResult = await enrichCollectionCardImages( result ); |
| 179 | + return res.status( 200 ).json( enrichedResult ); |
48 | 180 | } |
49 | 181 |
|
50 | 182 | default: |
|
0 commit comments