MeteoriteH/Back/routes/api/renderthumbnail.js

293 lines
8.2 KiB
JavaScript

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