const express = require("express") const router = express.Router() const rcctalk = require("./../../thumbnailrcctalk") const rcctalk2018 = require("./../../rcctalk2018") const fs = require("fs") const assetrenderscript = fs.readFileSync("assetthumbnailrenderer.lua", "utf-8") var path = require("path") const User = require("./../../model/user.js") const item = require("./../../model/item.js") var rgx = /^[0-9]*\.?[0-9]*$/ router.use(express.json({ limit: "200mb" })) const { requireAuth } = require("./../../middleware/authmiddleware.js") const { grabAuth } = require("./../../middleware/grabauth.js") const fetch = (...args) => import("node-fetch").then(({ default: fetch }) => fetch(...args)) require("dotenv").config() const RCC_HOST = process.env.RCC_HOST const rateLimit = require("express-rate-limit") const limiter = rateLimit({ windowMs: 2 * 1000, // 5 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) => { return response.sendFile(path.resolve("./assets/default.png")) }, }) router.get("/", grabAuth, async (req, res) => { if (!req.query.id && !req.query.userId) { return res.status(400) } let headshot = false if (req.query.type === "headshot") { headshot = true } let id = req.query.id ?? req.query.userId var sanitizedid = id.match(rgx) const user = await User.findOne({ userid: sanitizedid }).lean() if (!user) { return res.json({ status: "error", error: "User does not exist" }) } // lets get our file path with sanitized id let path2 = path.resolve( __dirname, "../../assets/userthumbnails/" + sanitizedid + ".png", ) if (headshot === true) { path2 = path.resolve( __dirname, "../../assets/userthumbnailsheadshots/" + sanitizedid + ".png", ) } fs.access(path2, fs.F_OK, async err => { if (err) { let newrender = await rcctalk2018.OpenRender(sanitizedid, headshot) if (newrender.error) { return res.sendFile(path.resolve("./assets/default.png")) } newrender = newrender["SOAP-ENV:Envelope"]["SOAP-ENV:Body"][ "ns1:OpenJobResponse" ]["ns1:OpenJobResult"][0]["ns1:value"]._text res.writeHead(200, { "Content-Type": "image/png" }) fs.writeFile(path2, newrender, "base64", function (err) { if (err) { console.log("error") } }) return res.end(Buffer.from(newrender, "base64")) // if this timeouts and rcc doesn't return the image feor some reason then send the default fallback //return res.sendFile(path.resolve("./assets/default.png")) } //file exists if ( req.query.method && req.userdocument && req.userdocument.userid == sanitizedid ) { // don't allow unauthenticated users to regenerate avatars and don't allow authenticated users to regenerate other peoples avatars if (req.query.method === "regenerate") { fs.unlink(path2, async function (err) { if (err) { console.log(err) } let newrender = await rcctalk2018.OpenRender( sanitizedid, headshot, ) if (newrender.error) { return res.sendFile( path.resolve("./assets/default.png"), ) } newrender = newrender["SOAP-ENV:Envelope"]["SOAP-ENV:Body"][ "ns1:OpenJobResponse" ]["ns1:OpenJobResult"][0]["ns1:value"]._text res.writeHead(200, { "Content-Type": "image/png" }) fs.writeFile(path2, newrender, "base64", function (err) { if (err) { console.log("error") } }) return res.end(Buffer.from(newrender, "base64")) }) } } else { res.sendFile(path.resolve(path2)) return } }) }) router.post("/rcc", (req, res) => { var ip = req.headers["cf-connecting-ip"] || req.socket.remoteAddress if (ip == RCC_HOST || ip == "::ffff:" + RCC_HOST) { const { player, thumbnail } = req.body let path2 = path.resolve( __dirname, "../../assets/userthumbnails/" + player + ".png", ) fs.writeFile(path2, thumbnail, "base64", function (err) { if (err) { console.log("error") // if writing fails we can still fallback return res.sendFile(path.resolve("./../../assets/default.png")) } // if it succeeds then we can send the userthumbnail // close the job after rcctalk.CloseJob("Thumbnailfor" + player) }) } }) router.get(["/asset", "/asset.ashx"], grabAuth, async (req, res) => { if (!req.query.id && !req.query.assetid) { return res.status(400) } let id = req.query.id ?? req.query.assetid var sanitizedid = id.match(rgx) const user = await item.findOne({ ItemId: sanitizedid }).lean() if (!user) { return res.json({ status: "error", error: "Item does not exist" }) } if (user.Type === "Audio") { return res.sendFile(path.resolve("./assets/images/audio.png")) } if (user.Hidden === true) { // if item isn't supposed to have a thumbnail return res.sendFile(path.resolve("./assets/moderated.png")) } if (user.approved === false && !req.query.nonapproved) { return res.sendFile(path.resolve("./assets/approval.png")) } if (req.query.nonapproved && req?.userdocument?.admin === false) { // we only want admins to be able to see non approved assets anyways return res.sendFile(path.resolve("./assets/approval.png")) } if ( req.query.nonapproved && (user.Type === "Pants" || user.Type === "Shirts") ) { sanitizedid -= 1 return res.sendFile( path.resolve( __dirname, "../../assets/ugc/itemfile-" + sanitizedid + ".rbxm", ), ) } if (req.query.nonapproved && user.Type === "Video") { return res.sendFile( path.resolve( __dirname, "../../assets/ugc/itemfile-" + sanitizedid + ".rbxm", ), ) } if (user.Type === "Video") { return res.sendFile(path.resolve("./assets/video.png")) } if (user.Type === "User Ad" || user.Type === "Gamepass") { try { await fs.promises.access( path.resolve( __dirname, "../../assets/ugc/itemfile-" + sanitizedid + ".rbxm", ), fs.constants.W_OK, ) return res.sendFile( path.resolve( __dirname, "../../assets/ugc/itemfile-" + sanitizedid + ".rbxm", ), ) } catch { return res.sendFile( path.resolve("./assets/images/defaultadsky.png"), ) } } // lets get our file path with sanitized id let path2 = path.resolve( __dirname, "../../assets/ugc/asset-" + sanitizedid + ".png", ) fs.access(path2, fs.F_OK, async err => { if (err) { // get our renderscript with the new character app var newrenderscript = assetrenderscript.replace( "local asset = 0", 'local asset = "' + sanitizedid + '"', ) //open a new job for our thumbnail render request var response = await rcctalk.OpenJob( "Thumbnailfor" + sanitizedid, newrenderscript, "120", ) if ( response["SOAP-ENV:Envelope"]["SOAP-ENV:Body"]["SOAP-ENV:Fault"] ) { // if failed then print out error close job then send a fallback image //console.dir(response,{ depth: null }) rcctalk.CloseJob("Thumbnailfor" + sanitizedid) return res.sendFile(path.resolve("./assets/default.png")) } else { // send image to user // wait for image to be uploaded by rcc function check() { setTimeout(() => { fs.access(path2, fs.constants.F_OK, error => { if (error) { check() } else { return res.sendFile(path2) } }) }, 3000) } } check() // if this timeouts and rcc doesn't return the image feor some reason then send the default fallback return res.sendFile(path.resolve("./assets/default.png")) } res.sendFile(path.resolve(path2)) return }) }) router.post("/rccasset", (req, res) => { var ip = req.headers["cf-connecting-ip"] || req.socket.remoteAddress if (ip == RCC_HOST || ip == "::ffff:" + RCC_HOST) { const { asset, thumbnail } = req.body console.log(asset) let path2 = path.resolve( __dirname, "../../assets/ugc/asset-" + asset + ".png", ) fs.writeFile(path2, thumbnail, "base64", function (err) { if (err) { console.log("error") // if writing fails we can still fallback return res.sendFile(path.resolve("./../../assets/default.png")) } // if it succeeds then we can send the userthumbnail // close the job after rcctalk.CloseJob("Thumbnailforasset" + asset) }) } }) module.exports = router