const express = require("express") const router = express.Router() const { requireAuth } = require("./../middleware/authmiddleware") const User = require("./../model/user.js") const games = require("./../model/games.js") const catalog = require("./../model/item.js") const { requirediscord } = require("./../middleware/requirediscord.js") var multer = require("multer") const fs = require("fs") const path = require("path") var numbtest = /^\d+\.?\d*$/ const bodyParser = require("body-parser") const { pngValidator } = require("png-validator") const fileTypeChecker = require("file-type-checker") const rateLimit = require("express-rate-limit") const limiter = rateLimit({ windowMs: 3 * 1000, // 3 seconds max: 1, // Limit each IP to 1 requests per `window` standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers handler: (request, response, next, options) => { response.json({ status: "error", error: "Too many requests try again later.", }) }, }) async function validateImage(itemid, res) { return new Promise(async resolve => { try { const myArrayBuffer = await fs.promises.readFile( path.resolve(`assets/ugc/itemfile-${itemid}.rbxm`), null, ) pngValidator(myArrayBuffer) // success } catch { // file is invalid or corrupt fs.unlink( path.resolve(`assets/ugc/itemfile-${itemid}.rbxm`), err => { if (err) console.log(err) }, ) return res.json({ status: "error", error: "Image is invalid." }) } resolve() }) } const pages = ["shirts", "pants", "audios", "games", "badges", "meshes"] router.use(bodyParser.json()) router.post("/creations", requireAuth, async (req, res) => { const { type } = req.body let items = await catalog .find({ Creator: req.userdocument.userid, Type: type }) .lean() .select(["Name", "Description", "ItemId"]) if (type === "games") { items = await games .find({ useridofowner: req.userdocument.userid }) .lean() .select([ "nameofgame", "idofgame", "Description", "avatartype", "gearallowed", ]) } if (type === "audios") { items = await catalog .find({ Creator: req.userdocument.userid, Type: "Audio" }) .lean() .select(["Name", "Description", "ItemId"]) } else if (type === "badges") { items = await catalog .find({ Creator: req.userdocument.userid, Type: "Badge" }) .lean() .select(["Name", "Description", "ItemId"]) } else if (type === "meshes") { items = await catalog .find({ Creator: req.userdocument.userid, Type: "Mesh" }) .lean() .select(["Name", "Description", "ItemId"]) } else if (type === "userads") { items = await catalog .find({ Creator: req.userdocument.userid, Type: "User Ad" }) .lean() .select(["Name", "Description", "ItemId"]) } else if (type === "gamepasses") { items = await catalog .find({ Creator: req.userdocument.userid, Type: "Gamepass" }) .lean() .select(["Name", "Description", "ItemId"]) } else if (type === "videos") { items = await catalog .find({ Creator: req.userdocument.userid, Type: "Video" }) .lean() .select(["Name", "Description", "ItemId"]) } return res.json(items) }) var storage = multer.diskStorage({ destination: function (req, file, cb) { // Uploads is the Upload_folder_name if (file.fieldname === "thumbnail") { // is a game thumbnail cb(null, "./assets/gameassets") } else { cb(null, "./assets/ugc") } }, filename: async function (req, file, cb) { //console.log(path.basename(file.originalname,'.png')) if (path.extname(file.originalname) === ".rbxl") { const placeid = await games.countDocuments() cb( null, "gamefile" + "-" + placeid + path.extname(file.originalname), ) } else if (file.fieldname === "thumbnail") { // is a game thumbnail const placeid = await games.countDocuments() cb(null, "thumbnail" + "-" + placeid + ".png") } else if (file.mimetype == "image/png") { const itemid = await catalog.countDocuments() cb(null, "itemfile" + "-" + itemid + ".rbxm") } else if (path.extname(file.originalname) === ".mp3") { const itemid = await catalog.countDocuments() cb(null, "itemfile" + "-" + itemid + ".rbxm") } else if (path.extname(file.originalname) === ".mesh") { const itemid = await catalog.countDocuments() cb(null, "itemfile" + "-" + itemid + ".rbxm") } else if (path.extname(file.originalname) === ".webm") { const itemid = await catalog.countDocuments() cb(null, "itemfile" + "-" + itemid + ".rbxm") } }, }) const uploadcloth = multer({ storage: storage, fileFilter: function (req, file, callback) { if ( file.mimetype !== "image/png" /*&& ext !== '.mp3' && ext !== '.rbxl'*/ ) { return callback("Invalid file type") } callback(null, true) }, limits: { fileSize: 1024 * 1024 }, // 1mb }) router.post( "/uploadclothing", requireAuth, requirediscord, async (req, res) => { uploadcloth.single("clothingfile")(req, res, async function (err) { if (err) { if (err?.message === "File too large") { return res .status(400) .send({ status: "error", error: "File too large! 1MB Limit", }) } if (err === "Invalid file type") { return res .status(400) .send({ status: "error", error: "Invalid file type" }) } return res .status(400) .send({ status: "error", error: err.message }) } var xss = require("xss") const { clothingname, description, price, type } = req.body // save shirt template if (!clothingname) { return res.json({ status: "error", error: "Clothing name needs to be sent.", }) } if (!description) { return res.json({ status: "error", error: "Description needs to be sent.", }) } if (!price) { return res.json({ status: "error", error: "Price needs to be sent.", }) } if (type != "Shirts" && type != "Pants") { return res.json({ status: "error", error: "Type needs to be a shirt or pant value", }) } if (numbtest.test(price) == false) { return res.json({ status: "error", error: "Price can only be a number!", }) } if (price < 5) { return res.json({ status: "error", error: "Minimum price is 5 rocks.", }) } if (req.userdocument.coins < 5) { // less than return res.json({ status: "error", error: "You don't have 5 rocks >:(!", }) } else if (req.userdocument.admin === false) { req.userdocument.coins -= 5 req.userdocument.markModified("coins") await req.userdocument.save() } const itemid = await catalog.countDocuments() // check if the file they just uploaded is valid await validateImage(itemid, res) let approved = req.userdocument.admin === false ? false : true try { await catalog.create({ Name: xss(clothingname), Description: xss(description), Price: Math.ceil(price), Type: "Image", Hidden: true, ItemId: itemid, approved, }) } catch {} // save actual item let xml = ` null nil http://mete0r.xyz/asset/?id=` + itemid + ` Shirt true ` if (type === "Pants") { xml = ` null nil http://mete0r.xyz/asset/?id=` + itemid + ` Pants true ` } let shirtid = itemid + 1 // prevent any race conditions shirtid = shirtid.toString() fs.writeFile( "./assets/ugc/itemfile-" + shirtid + ".rbxm", xml, async function (err) { if (err) { return console.log(err) } let approved = req.userdocument.admin === false ? false : true try { await catalog.create({ Name: xss(clothingname), Description: xss(description), Price: Math.ceil(price), Creator: req.userdocument.userid, Type: type, ItemId: shirtid, approved, }) } catch {} }, ) // give player shirt User.updateOne( { userid: req.userdocument.userid }, { $push: { inventory: { Type: type, ItemId: shirtid, ItemName: xss(clothingname), Equipped: false, }, }, }, function (err, doc) { //console.log(err) }, ) return res.json({ status: "success", message: "Done!" }) }) }, ) // upload game WOW const uploadgame = multer({ storage: storage, fileFilter: function (req, file, callback) { var ext = path.extname(file.originalname) if (ext !== ".png" && ext !== ".rbxl" /* && ext !== '.mp3'*/) { return callback("Invalid file type") } callback(null, true) }, limits: { fileSize: 5120 * 1024 * 2 }, // 10mb }) const uploadaudio = multer({ storage: storage, fileFilter: function (req, file, callback) { var ext = path.extname(file.originalname) if (ext !== ".mp3" && ext !== ".ogg") { return callback("Invalid file type") } callback(null, true) }, limits: { fileSize: 5120 * 1024 }, // 5mb }) var editgamestorage = multer.diskStorage({ destination: function (req, file, cb) { // Uploads is the Upload_folder_name if (file.fieldname === "thumbnail") { // is a game thumbnail cb(null, "./assets/gameassets") } else { cb(null, "./assets/ugc") } }, filename: async function (req, file, cb) { //console.log(path.basename(file.originalname,'.png')) if (path.extname(file.originalname) === ".rbxl") { const item = await games .findOne({ idofgame: req.body.gameid }) .lean() if (!item) { return cb("Item doesn't exist!") } //console.log(item) if (item.useridofowner != req.userdocument.userid) { // player doesn't own this item return cb("You don't own this") } cb( null, "gamefile" + "-" + req.body.gameid + path.extname(file.originalname), ) } }, }) const editgame = multer({ storage: editgamestorage, fileFilter: function (req, file, callback) { var ext = path.extname(file.originalname) if (ext !== ".rbxl" /* && ext !== '.mp3'*/) { return callback("Invalid file type") } callback(null, true) }, limits: { fileSize: 5120 * 1024 * 2 }, // 10mb }) router.post("/editgame", requireAuth, requirediscord, async (req, res) => { const { nameofgame, description, gameid } = req.body var xss = require("xss") if (!gameid) { return res.json({ status: "error", error: "GameID required" }) } const item = await games.findOne({ idofgame: gameid }) if (!item) { return res.json({ status: "error", error: "Game doesn't exist." }) } //console.log(item) if (item.useridofowner != req.userdocument.userid) { // player doesn't own this item return res.json({ status: "error", error: "You don't have permissions for this!", }) } if (nameofgame && nameofgame != "") { item.nameofgame = xss(nameofgame) item.markModified("nameofgame") await item.save() } if (description && description != "") { item.descrption = xss(description) item.markModified("descrption") await item.save() } return res.json({ status: "success", message: "Done!" }) }) router.post( "/editavatartype", requireAuth, requirediscord, async (req, res) => { const { avatartype, gameid } = req.body if (!gameid) { return res.json({ status: "error", error: "GameID required" }) } if (!avatartype) { return res.json({ status: "error", error: "Avatar type required" }) } if (avatartype != "R6" && avatartype != "R15" && avatartype != "PC") { return res.json({ status: "error", error: "Avatar type required" }) } const item = await games.findOne({ idofgame: gameid }) if (!item) { return res.json({ status: "error", error: "Game doesn't exist." }) } //console.log(item) if (item.useridofowner != req.userdocument.userid) { // player doesn't own this item return res.json({ status: "error", error: "You don't have permissions for this!", }) } item.avatartype = avatartype item.markModified("avatartype") await item.save() return res.json({ status: "success", message: "Done!" }) }, ) router.post( "/editgearstatus", requireAuth, requirediscord, async (req, res) => { const { newgearstatus, gameid } = req.body if (!gameid) { return res.json({ status: "error", error: "GameID required" }) } if (newgearstatus != true && newgearstatus != false) { return res.json({ status: "error", error: "Gear status required" }) } const item = await games.findOne({ idofgame: gameid }) if (!item) { return res.json({ status: "error", error: "Game doesn't exist." }) } //console.log(item) if (item.useridofowner != req.userdocument.userid) { // player doesn't own this item return res.json({ status: "error", error: "You don't have permissions for this!", }) } item.gearallowed = newgearstatus item.markModified("gearallowed") await item.save() return res.json({ status: "success", message: "Done!" }) }, ) router.post( "/editgameupload", requireAuth, requirediscord, async (req, res) => { editgame.single("gamefile")(req, res, async function (err) { if (err) { if (err?.message === "File too large") { return res .status(400) .send({ status: "error", error: "File too large! 10MB Limit", }) } if (err === "Invalid file type") { return res .status(400) .send({ status: "error", error: "Invalid file type" }) } return res .status(400) .send({ status: "error", error: err.message }) } return res.json({ status: "success", message: "Done!" }) }) }, ) router.post("/uploadgame", requireAuth, requirediscord, async (req, res) => { uploadgame.fields([ { name: "gamefile", maxCount: 1 }, { name: "thumbnail", maxCount: 1 }, ])(req, res, async function (err) { if (err) { if (err?.message === "File too large") { return res .status(400) .send({ status: "error", error: "File too large! 10MB Limit", }) } if (err === "Invalid file type") { return res .status(400) .send({ status: "error", error: "Invalid file type" }) } return res.status(400).send({ status: "error", error: err.message }) } var xss = require("xss") const { gamename, description, version } = req.body // save game if (!gamename) { return res.json({ status: "error", error: "Game name needs to be sent.", }) } if (gamename?.length > 50) { return res.json({ status: "error", error: "Game name can not be more than 50 characters", }) } if (!description) { return res.json({ status: "error", error: "Description needs to be sent.", }) } if (description?.length > 1000) { return res.json({ status: "error", error: "Description can not be more than 1000 characters", }) } if (!version) { return res.json({ status: "error", error: "Version needs to be sent.", }) } const versions = [ //"2014", "2016", "2018", "2020", ] if (versions.includes(version) === false) { return res.json({ status: "error", error: "Invalid version sent." }) } if (req.userdocument.coins < 5) { // less than return res.json({ status: "error", error: "You don't have 5 rocks >:(!", }) } else if (req.userdocument.admin === false) { req.userdocument.coins -= 5 req.userdocument.markModified("coins") await req.userdocument.save() } const placeid = await games.countDocuments() try { await games.create({ useridofowner: req.userdocument.userid, idofgame: placeid, nameofgame: xss(gamename), numberofplayers: "0", descrption: xss(description), version: version, }) } catch { throw error } return res.json({ status: "success", message: "Done!" }) }) }) const uploadasset = multer({ storage: storage, fileFilter: function (req, file, callback) { var ext = path.extname(file.originalname) if (ext !== ".png" && ext !== ".mesh") { return callback("Invalid file type") } callback(null, true) }, limits: { fileSize: 5120 * 1024 }, // 1mb }) router.post( "/uploadmeshes", requireAuth, requirediscord, limiter, async (req, res) => { uploadasset.single("assetfile")(req, res, async function (err) { if (err) { if (err?.message === "File too large") { return res .status(400) .send({ status: "error", error: "File too large! 1MB Limit", }) } if (err === "Invalid file type") { return res .status(400) .send({ status: "error", error: "Invalid file type" }) } return res .status(400) .send({ status: "error", error: err.message }) } var xss = require("xss") const { itemname } = req.body // save mesh if (!itemname) { return res.json({ status: "error", error: "Mesh name needs to be sent.", }) } const itemid = await catalog.countDocuments() const myArrayBuffer = await fs.promises.readFile( path.resolve(`assets/ugc/itemfile-${itemid}.rbxm`), null, ) if (fileTypeChecker.detectFile(myArrayBuffer)) { // not a mesh fs.unlink( path.resolve(`assets/ugc/itemfile-${itemid}.rbxm`), err => { if (err) console.log(err) }, ) return res.json({ status: "error", error: "Mesh is invalid." }) } try { await catalog.create({ Name: xss(itemname), Price: "0", Type: "Mesh", Creator: req.userdocument.userid, Hidden: true, ItemId: itemid, approved: true, }) } catch (err) { throw err } return res.json({ status: "success", message: "Done! Mesh ID : " + itemid, }) }) }, ) router.post( "/uploadbadges", requireAuth, requirediscord, limiter, async (req, res) => { uploadasset.single("assetfile")(req, res, async function (err) { if (err) { if (err?.message === "File too large") { return res .status(400) .json({ status: "error", error: "File too large! 1MB Limit", }) } if (err === "Invalid file type") { return res .status(400) .json({ status: "error", error: "Invalid file type" }) } return res .status(400) .json({ status: "error", error: err.message }) } var xss = require("xss") const { itemname } = req.body // save badge if (!itemname) { return res.json({ status: "error", error: "Badge name needs to be sent.", }) } const itemid = await catalog.countDocuments() //check if the file thye just uploaded is valid await validateImage(itemid, res) try { let approved = req.userdocument.admin === false ? false : true await catalog.create({ Name: xss(itemname), Price: "0", Type: "Badge", Creator: req.userdocument.userid, Hidden: true, ItemId: itemid, approved, }) } catch (err) { throw err } return res.json({ status: "success", message: "Done! Badge ID : " + itemid, }) }) }, ) router.post( "/uploaduserads", requireAuth, requirediscord, limiter, async (req, res) => { uploadasset.single("assetfile")(req, res, async function (err) { if (err) { if (err?.message === "File too large") { return res .status(400) .send({ status: "error", error: "File too large! 1MB Limit", }) } if (err === "Invalid file type") { return res .status(400) .send({ status: "error", error: "Invalid file type" }) } return res .status(400) .send({ status: "error", error: err.message }) } var xss = require("xss") const { itemname } = req.body // save userad if (!itemname) { return res.json({ status: "error", error: "User Ad name needs to be sent.", }) } const itemid = await catalog.countDocuments() // check if the file they just uploaded is valid await validateImage(itemid, res) try { let approved = req.userdocument.admin === false ? false : true await catalog.create({ Name: xss(itemname), Price: "0", Type: "User Ad", Creator: req.userdocument.userid, ItemId: itemid, ActiveAd: false, approved, }) } catch (err) { throw err } return res.json({ status: "success", message: "Done!" }) }) }, ) router.post( "/uploadgamepasses", requireAuth, requirediscord, limiter, async (req, res) => { uploadasset.single("assetfile")(req, res, async function (err) { if (err) { if (err?.message === "File too large") { return res .status(400) .send({ status: "error", error: "File too large! 1MB Limit", }) } if (err === "Invalid file type") { return res .status(400) .send({ status: "error", error: "Invalid file type" }) } return res .status(400) .send({ status: "error", error: err.message }) } var xss = require("xss") const { itemname, price, gameid } = req.body // save game pass if (!itemname) { return res.json({ status: "error", error: "Gamepass name needs to be sent.", }) } if (!price) { return res.json({ status: "error", error: "Price needs to be sent.", }) } if (!gameid) { return res.json({ status: "error", error: "Gameid needs to be sent.", }) } if (numbtest.test(price) == false) { return res.json({ status: "error", error: "Price can only be a number!", }) } if (price < 1) { return res.json({ status: "error", error: "Minimum price is 1 rock.", }) } const gamedoc = await games.findOne({ idofgame: gameid }) if (!gamedoc) { return res.json({ status: "error", error: "Game not found" }) } if (gamedoc.useridofowner != req.userdocument.userid) { return res.json({ status: "error", error: "You don't own this game!", }) } const currentgamepasscount = await catalog.countDocuments({ associatedgameid: gamedoc.idofgame, }) if (currentgamepasscount >= 20) { return res.json({ status: "error", error: "No more than 20 game passes per game.", }) } const itemid = await catalog.countDocuments() // check if the file they just uploaded is valid await validateImage(itemid, res) try { let approved = req.userdocument.admin === false ? false : true await catalog.create({ Name: xss(itemname), Description: "", Price: Math.ceil(price), Creator: req.userdocument.userid, Type: "Gamepass", ItemId: itemid, approved, associatedgameid: gamedoc.idofgame, }) } catch (err) { throw err } return res.json({ status: "success", message: "Done!" }) }) }, ) router.post( "/uploadaudios", requireAuth, requirediscord, limiter, async (req, res) => { uploadaudio.single("assetfile")(req, res, async function (err) { if (err) { if (err?.message === "File too large") { return res .status(400) .send({ status: "error", error: "File too large! 5MB Limit", }) } if (err === "Invalid file type") { return res .status(400) .send({ status: "error", error: "Invalid file type" }) } return res .status(400) .send({ status: "error", error: err.message }) } var xss = require("xss") const { itemname } = req.body // save audio if (!itemname) { return res.json({ status: "error", error: "Audio name needs to be sent.", }) } const itemid = await catalog.countDocuments() const myArrayBuffer = await fs.promises.readFile( path.resolve(`assets/ugc/itemfile-${itemid}.rbxm`), null, ) if ( fileTypeChecker.isMP3(myArrayBuffer) === false && fileTypeChecker.isOGG(myArrayBuffer) === false ) { fs.unlink( path.resolve(`assets/ugc/itemfile-${itemid}.rbxm`), err => { if (err) console.log(err) }, ) return res.json({ status: "error", error: "Audio is invalid." }) } try { let approved = req.userdocument.admin === false ? false : true await catalog.create({ Name: xss(itemname), Price: "0", Type: "Audio", Creator: req.userdocument.userid, Hidden: true, ItemId: itemid, approved, }) } catch (err) { throw err } return res.json({ status: "success", message: "Done! Audio ID : " + itemid, }) }) }, ) const uploadvideo = multer({ storage: storage, fileFilter: function (req, file, callback) { var ext = path.extname(file.originalname) if (ext !== ".webm") { return callback("Invalid file type") } callback(null, true) }, limits: { fileSize: 10240 * 1024 }, // 10mb }) router.post( "/uploadvideos", requireAuth, requirediscord, limiter, async (req, res) => { uploadvideo.single("assetfile")(req, res, async function (err) { if (err) { if (err?.message === "File too large") { return res .status(400) .send({ status: "error", error: "File too large! 10MB Limit", }) } if (err === "Invalid file type") { return res .status(400) .send({ status: "error", error: "Invalid file type" }) } return res .status(400) .send({ status: "error", error: err.message }) } var xss = require("xss") const { itemname } = req.body // save audio if (!itemname) { return res.json({ status: "error", error: "Video name needs to be sent.", }) } const itemid = await catalog.countDocuments() const myArrayBuffer = await fs.promises.readFile( path.resolve(`assets/ugc/itemfile-${itemid}.rbxm`), null, ) if (fileTypeChecker.isWEBM(myArrayBuffer) === false) { fs.unlink( path.resolve(`assets/ugc/itemfile-${itemid}.rbxm`), err => { if (err) console.log(err) }, ) return res.json({ status: "error", error: "Video is invalid." }) } try { let approved = req.userdocument.admin === false ? false : true await catalog.create({ Name: xss(itemname), Price: "0", Type: "Video", Creator: req.userdocument.userid, ItemId: itemid, approved, }) } catch (err) { throw err } return res.json({ status: "success", message: "Done! Video ID : " + itemid, }) }) }, ) module.exports = router