Meteor-back/routes/api/renderthumbnail.js

249 lines
8.3 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