Files
games-express-api/controllers/adminGames.js
T
john-okeefe ee7aa9921b
Release / build-and-push (push) Successful in 1m1s
fix(adminGames): apply Steam URL normalization and scraper guard to create/update
Admin create had no URL handling at all (and crashed on missing steamId
via .length), and admin update passed req.body straight to the scraper.
Reuse normalizeSteamId in both, 400 on unrecognized input, and reject
failed scrapes (non-object / missing title+frontImage) before
Game.create/findOneAndUpdate so failures report as 'Steam lookup failed'
instead of schema validation noise.
2026-09-06 10:11:07 -04:00

173 lines
6.2 KiB
JavaScript

import Game from '../models/Game.js'
import steamScraper from '../scripts/scraper.js'
import asyncHandler from '../middleware/async.js'
import ErrorResponse from '../utils/errorResponse.js'
import normalizeSteamId from '../utils/normalizeSteamId.js'
const checkForHexRegExp = new RegExp('^[0-9a-fA-F]{24}$')
const checkForTwelveRegExp = new RegExp('^[0-9a-fA-F]{12}$')
import {decode, isValid} from 'js-base64'
/**
* games.js
*
* @description :: Server-side logic for managing games.
*/
/**
* gameController.list()
*/
export const list = asyncHandler(async (req, res, next) => {
return res.status(200).json(res.advancedResults)
})
/**
* gameController.show()
*/
export const show = asyncHandler(async (req, res, next) => {
const { id } = req.params
const gameId =
id === id.match(checkForTwelveRegExp) || id.match(checkForHexRegExp)
? '_id'
: 'steamId'
const game = await Game.findOne({ [gameId]: id }).select('-__v')
return res.status(200).json({
success: true,
data: game,
})
})
/**
* gameController.create()
*/
export const create = asyncHandler(async (req, res, next) => {
let oldGame
const steamId = normalizeSteamId(req.body.steamId)
if (steamId === null)
return next(
new ErrorResponse(
'Enter a Steam app ID or a store.steampowered.com URL.',
400,
)
)
req.body.steamId = steamId
steamId.length > 0
? (oldGame = await Game.findOne({ steamId }))
: (oldGame = await Game.findOne({ title: req.body.title }))
if (oldGame)
return next(
new ErrorResponse(`The game ${oldGame.title} already exists`, 400)
)
req.body.createdBy = req.user.id
req.body.lastModifiedBy = req.user.id
req.body.accessedBy[0].user = req.user.id
const data =
req.body.scrape === true ? await steamScraper(req.body) : req.body
if (
req.body.scrape === true &&
(!data || typeof data !== 'object' || !data.title || !data.frontImage)
)
return next(
new ErrorResponse('Steam lookup failed for that ID/URL.', 400)
)
if (req.body.scrape === false) {
if (req.body.shortDesc && isValid(req.body.shortDesc)) req.body.shortDesc = decode(req.body.shortDesc)
if (req.body.reviews && isValid(req.body.reviews)) req.body.reviews = decode(req.body.reviews)
if (req.body.summary && isValid(req.body.summary)) req.body.summary = decode(req.body.summary)
isValid(req.body.systemRequirements?.windows?.minimum) ? req.body.systemRequirements.windows.minimum = decode(req.body.systemRequirements.windows.minimum) : ''
isValid(req.body.systemRequirements?.windows?.recommended) ? req.body.systemRequirements.windows.recommended = decode(req.body.systemRequirements.windows.recommended) : ''
isValid(req.body.systemRequirements?.mac?.minimum) ? req.body.systemRequirements.mac.minimum = decode(req.body.systemRequirements.mac.minimum) : ''
isValid(req.body.systemRequirements?.mac?.recommended) ? req.body.systemRequirements.mac.recommended = decode(req.body.systemRequirements.mac.recommended) : ''
isValid(req.body.systemRequirements?.linux?.minimum) ? req.body.systemRequirements.linux.minimum = decode(req.body.systemRequirements.linux.minimum) : ''
isValid(req.body.systemRequirements?.linux?.recommended) ? req.body.systemRequirements.linux.recommended = decode(req.body.systemRequirements.linux.recommended) : ''
}
const game = await Game.create(data)
res.status(200).json({
success: true,
data: game,
})
})
/**
* gameController.update()
*/
export const update = asyncHandler(async (req, res, next) => {
const { id } = req.params
const gameId =
id === id.match(checkForTwelveRegExp) || id.match(checkForHexRegExp)
? '_id'
: 'steamId'
let game = await Game.findOne({ [gameId]: id })
if (!game)
return next(
ErrorResponse(`A game with the id of ${id} does not exist`, 401),
)
req.body.lastModifiedBy = req.user.id
if (req.body.steamId !== undefined) {
const normalizedSteamId = normalizeSteamId(req.body.steamId)
if (normalizedSteamId === null)
return next(
new ErrorResponse(
'Enter a Steam app ID or a store.steampowered.com URL.',
400,
)
)
req.body.steamId = normalizedSteamId
}
if (req.body.shortDesc && isValid(req.body.shortDesc)) req.body.shortDesc = decode(req.body.shortDesc)
if (req.body.reviews && isValid(req.body.reviews)) req.body.reviews = decode(req.body.reviews)
if (req.body.summary && isValid(req.body.summary)) req.body.summary = decode(req.body.summary)
isValid(req.body.systemRequirements?.windows?.minimum) ? req.body.systemRequirements.windows.minimum = decode(req.body.systemRequirements.windows.minimum) : ''
isValid(req.body.systemRequirements?.windows?.recommended) ? req.body.systemRequirements.windows.recommended = decode(req.body.systemRequirements.windows.recommended) : ''
isValid(req.body.systemRequirements?.mac?.minimum) ? req.body.systemRequirements.mac.minimum = decode(req.body.systemRequirements.mac.minimum) : ''
isValid(req.body.systemRequirements?.mac?.recommended) ? req.body.systemRequirements.mac.recommended = decode(req.body.systemRequirements.mac.recommended) : ''
isValid(req.body.systemRequirements?.linux?.minimum) ? req.body.systemRequirements.linux.minimum = decode(req.body.systemRequirements.linux.minimum) : ''
isValid(req.body.systemRequirements?.linux?.recommended) ? req.body.systemRequirements.linux.recommended = decode(req.body.systemRequirements.linux.recommended) : ''
const data =
req.body.scrape === true ? await steamScraper(req.body) : req.body
if (
req.body.scrape === true &&
(!data || typeof data !== 'object' || !data.title || !data.frontImage)
)
return next(
new ErrorResponse('Steam lookup failed for that ID/URL.', 400)
)
game = await Game.findOneAndUpdate({ [gameId]: id }, data, {
new: true,
runValidators: true,
})
res.status(200).json({
success: true,
data: game,
})
})
/**
* gameController.remove()
*/
export const remove = asyncHandler(async (req, res, next) => {
const { id } = req.params
const gameId =
id === id.match(checkForTwelveRegExp) || id.match(checkForHexRegExp)
? '_id'
: 'steamId'
await Game.findOneAndDelete({ [gameId]: id })
res.status(200).json({ success: true, data: {} })
})