2019-10-15 22:42:47 -04:00
const express = require ( 'express' ) ;
const fs = require ( "fs" )
2019-11-06 23:07:31 -05:00
const compression = require ( 'compression' ) ;
2020-10-28 21:25:13 -04:00
const timeout = require ( 'connect-timeout' )
2020-09-10 09:02:40 -04:00
const rateLimit = require ( "express-rate-limit" ) ;
const app = express ( ) ;
2020-11-07 19:20:44 -05:00
2020-09-10 09:02:40 -04:00
app . offline = false // set to true to go into "offline" mode (in case of ip ban from rob)
2020-09-29 21:42:18 -04:00
app . secret = "Wmfd2893gb7" // lol
2020-11-07 19:20:44 -05:00
app . config = require ( './settings' ) // tweak settings in this file if you're using a GDPS
2020-11-01 15:29:32 -05:00
app . endpoint = app . config . endpoint // default is boomlings.com/database/
2020-11-07 19:20:44 -05:00
app . accountCache = { } // account IDs are cached here to shave off requests to getgjusers
let rlMessage = "Rate limited ¯\\_(ツ)_/¯<br><br>Please do not spam my servers with a crazy amount of requests. It slows things down on my end and stresses RobTop's servers just as much." +
" If you really want to send a zillion requests for whatever reason, please download the GDBrowser repository locally - or even just send the request directly to the GD servers.<br><br>" +
"This kind of spam usually leads to GDBrowser getting IP banned by RobTop, and every time that happens I have to start making the rate limit even stricter. Please don't be the reason for that.<br><br>" +
"(also, keep in mind that most endpoints have a ?count parameter that let you fetch a LOT more stuff in just one request)"
2020-09-10 09:02:40 -04:00
const RL = rateLimit ( {
2020-11-01 15:29:32 -05:00
windowMs : app . config . rateLimiting ? 5 * 60 * 1000 : 0 ,
max : app . config . rateLimiting ? 100 : 0 , // max requests per 5 minutes
2020-11-07 19:20:44 -05:00
message : rlMessage ,
keyGenerator : function ( req ) { return req . headers [ 'x-real-ip' ] || req . headers [ 'x-forwarded-for' ] } ,
2020-09-10 09:02:40 -04:00
skip : function ( req ) { return ( ( req . url . includes ( "api/level" ) && ! req . query . hasOwnProperty ( "download" ) ) ? true : false ) }
} )
2019-11-06 23:07:31 -05:00
2020-11-07 19:20:44 -05:00
const RL2 = rateLimit ( {
windowMs : app . config . rateLimiting ? 2 * 60 * 1000 : 0 ,
max : app . config . rateLimiting ? 200 : 0 , // max requests per 1 minute
message : rlMessage ,
keyGenerator : function ( req ) { return req . headers [ 'x-real-ip' ] || req . headers [ 'x-forwarded-for' ] }
} )
2019-10-15 22:42:47 -04:00
let api = true ;
2020-09-24 22:07:53 -04:00
let gdIcons = fs . readdirSync ( './icons/iconkit' )
let sampleIcons = require ( './misc/sampleIcons.json' )
2019-10-21 08:20:25 -07:00
app . use ( compression ( ) ) ;
2019-10-15 22:42:47 -04:00
app . use ( express . json ( ) ) ;
app . use ( express . urlencoded ( { extended : true } ) ) ;
2020-11-01 15:29:32 -05:00
app . use ( timeout ( '20s' ) ) ;
2019-10-15 22:42:47 -04:00
app . set ( 'json spaces' , 2 )
2020-11-01 15:29:32 -05:00
app . use ( function ( req , res , next ) {
req . gdParams = function ( obj = { } ) {
Object . keys ( app . config . params ) . forEach ( x => { if ( ! obj [ x ] ) obj [ x ] = app . config . params [ x ] } )
let ip = req . headers [ 'x-real-ip' ] || req . headers [ 'x-forwarded-for' ]
return { form : obj , headers : app . config . ipForwarding && ip ? { 'x-forwarded-for' : ip , 'x-real-ip' : ip } : { } }
}
next ( )
} )
2020-10-28 21:25:13 -04:00
let directories = [ "" ]
fs . readdirSync ( './api' ) . filter ( x => ! x . includes ( "." ) ) . forEach ( x => directories . push ( x ) )
2019-12-25 16:20:32 -05:00
app . run = { }
directories . forEach ( d => {
fs . readdirSync ( './api/' + d ) . forEach ( x => { if ( x . includes ( '.' ) ) app . run [ x . split ( '.' ) [ 0 ] ] = require ( './api/' + d + "/" + x ) } )
2019-10-16 18:47:53 -04:00
} )
2019-10-15 22:42:47 -04:00
2019-12-21 22:52:09 -05:00
try {
const secrets = require ( "./misc/secretStuff.json" )
app . id = secrets . id
app . gjp = secrets . gjp
2020-04-24 15:26:29 -04:00
app . sheetsKey = secrets . sheetsKey
2020-02-20 20:09:40 -05:00
if ( app . id == "account id goes here" || app . gjp == "account gjp goes here" ) console . warn ( "Warning: No account ID and/or GJP has been provided in secretStuff.json! These are required for level leaderboards to work." )
2020-04-24 15:26:29 -04:00
if ( app . sheetsKey . startsWith ( "google sheets api key" ) ) app . sheetsKey = undefined
2019-12-21 22:52:09 -05:00
}
2019-10-15 22:42:47 -04:00
2019-12-25 16:20:32 -05:00
catch ( e ) {
2019-12-21 22:52:09 -05:00
app . id = 0
app . gjp = 0
2020-02-20 20:09:40 -05:00
console . warn ( "Warning: secretStuff.json has not been created! These are required for level leaderboards to work." )
2019-10-15 22:42:47 -04:00
}
app . parseResponse = function ( responseBody , splitter ) {
2019-10-23 14:48:01 -07:00
if ( ! responseBody || responseBody == "-1" ) return { } ;
2019-10-15 22:42:47 -04:00
let response = responseBody . split ( '#' ) [ 0 ] . split ( splitter || ':' ) ;
let res = { } ;
for ( let i = 0 ; i < response . length ; i += 2 ) {
res [ response [ i ] ] = response [ i + 1 ] }
return res }
2019-10-16 18:47:53 -04:00
//xss bad
2019-10-25 17:22:29 -04:00
app . clean = function ( text ) { if ( ! text || typeof text != "string" ) return text ; else return text . replace ( /&/g , "&" ) . replace ( /</g , "<" ) . replace ( />/g , ">" ) . replace ( /=/g , "=" ) . replace ( /"/g , """ ) . replace ( /'/g , "'" ) }
2019-10-15 22:42:47 -04:00
2019-12-16 08:48:05 -05:00
// ASSETS
2020-11-12 20:32:15 -05:00
let assets = [ 'css' , 'assets' , 'blocks' , 'deatheffects' , 'difficulty' , 'gauntlets' , 'gdicon' , 'iconkitbuttons' , 'levelstyle' , 'objects' , 'trophies' ]
2019-10-19 00:27:17 -04:00
app . use ( '/css' , express . static ( _ _dirname + '/assets/css' ) ) ;
2019-12-16 08:48:05 -05:00
app . use ( '/assets' , express . static ( _ _dirname + '/assets' , { maxAge : "7d" } ) ) ;
2019-10-18 16:48:33 -04:00
app . use ( '/blocks' , express . static ( _ _dirname + '/assets/blocks' , { maxAge : "7d" } ) ) ;
2019-12-21 14:33:32 -05:00
app . use ( '/deatheffects' , express . static ( _ _dirname + '/assets/deatheffects' , { maxAge : "7d" } ) ) ;
2019-10-18 16:48:33 -04:00
app . use ( '/difficulty' , express . static ( _ _dirname + '/assets/gdfaces' , { maxAge : "7d" } ) ) ;
2019-12-16 08:48:05 -05:00
app . use ( '/gauntlets' , express . static ( _ _dirname + '/assets/gauntlets' , { maxAge : "7d" } ) ) ;
2019-10-18 16:48:33 -04:00
app . use ( '/gdicon' , express . static ( _ _dirname + '/icons/iconkit' , { maxAge : "7d" } ) ) ;
2019-12-16 08:48:05 -05:00
app . use ( '/iconkitbuttons' , express . static ( _ _dirname + '/assets/iconkitbuttons' , { maxAge : "7d" } ) ) ;
app . use ( '/levelstyle' , express . static ( _ _dirname + '/assets/initial' , { maxAge : "7d" } ) ) ;
app . use ( '/objects' , express . static ( _ _dirname + '/assets/objects' , { maxAge : "7d" } ) ) ;
2020-11-12 20:32:15 -05:00
app . use ( '/trophies' , express . static ( _ _dirname + '/assets/trophies' , { maxAge : "7d" } ) ) ;
2019-10-15 22:42:47 -04:00
2019-12-16 08:48:05 -05:00
// POST REQUESTS
2019-10-15 22:42:47 -04:00
2020-09-10 09:02:40 -04:00
app . post ( "/like" , RL , function ( req , res ) { app . run . like ( app , req , res ) } )
app . post ( "/postComment" , RL , function ( req , res ) { app . run . postComment ( app , req , res ) } )
app . post ( "/postProfileComment" , RL , function ( req , res ) { app . run . postProfileComment ( app , req , res ) } )
2019-12-25 16:20:32 -05:00
2020-09-10 09:02:40 -04:00
app . post ( "/messages" , RL , async function ( req , res ) { app . run . getMessages ( app , req , res ) } )
app . post ( "/messages/:id" , RL , async function ( req , res ) { app . run . fetchMessage ( app , req , res ) } )
app . post ( "/deleteMessage" , RL , function ( req , res ) { app . run . deleteMessage ( app , req , res ) } )
app . post ( "/sendMessage" , RL , function ( req , res ) { app . run . sendMessage ( app , req , res ) } )
2019-10-15 22:42:47 -04:00
2020-10-28 21:25:13 -04:00
app . post ( "/accurateLeaderboard" , function ( req , res ) { app . run . accurate ( app , req , res , true ) } )
2020-10-02 14:33:24 -04:00
2019-10-15 22:42:47 -04:00
2019-12-16 08:48:05 -05:00
// HTML
2019-10-15 22:42:47 -04:00
2020-09-10 09:02:40 -04:00
app . get ( "/" , function ( req , res ) {
if ( app . offline && ! req . query . hasOwnProperty ( "home" ) ) res . sendFile ( _ _dirname + "/html/offline.html" )
else res . sendFile ( _ _dirname + "/html/home.html" )
} )
2019-12-16 08:48:05 -05:00
app . get ( "/analyze/:id" , async function ( req , res ) { res . sendFile ( _ _dirname + "/html/analyze.html" ) } )
app . get ( "/api" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/api.html" ) } )
app . get ( "/comments/:id" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/comments.html" ) } )
2020-10-28 21:25:13 -04:00
app . get ( "/demon/:id" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/demon.html" ) } )
2019-12-16 08:48:05 -05:00
app . get ( "/gauntlets" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/gauntlets.html" ) } )
2019-12-16 15:01:46 -05:00
app . get ( "/iconkit" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/iconkit.html" ) } )
2019-12-16 08:48:05 -05:00
app . get ( "/leaderboard" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/leaderboard.html" ) } )
app . get ( "/leaderboard/:text" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/levelboard.html" ) } )
app . get ( "/mappacks" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/mappacks.html" ) } )
2019-12-29 18:59:29 -05:00
app . get ( "/messages" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/messages.html" ) } )
2019-12-16 08:48:05 -05:00
app . get ( "/search" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/filters.html" ) } )
app . get ( "/search/:text" , function ( req , res ) { res . sendFile ( _ _dirname + "/html/search.html" ) } )
2019-10-15 22:42:47 -04:00
2019-12-16 08:48:05 -05:00
// API
2019-10-20 23:33:01 -04:00
2020-09-10 09:02:40 -04:00
app . get ( "/api/analyze/:id" , RL , async function ( req , res ) { app . run . level ( app , req , res , api , true ) } )
2020-11-07 19:20:44 -05:00
app . get ( "/api/comments/:id" , RL2 , function ( req , res ) { app . run . comments ( app , req , res ) } )
2019-12-16 08:48:05 -05:00
app . get ( "/api/credits" , function ( req , res ) { res . send ( require ( './misc/credits.json' ) ) } )
2020-10-28 21:25:13 -04:00
app . get ( "/api/leaderboard" , function ( req , res ) { app . run [ req . query . hasOwnProperty ( "accurate" ) ? "accurate" : "scores" ] ( app , req , res ) } )
2020-11-07 19:20:44 -05:00
app . get ( "/api/leaderboardLevel/:id" , RL2 , function ( req , res ) { app . run . leaderboardLevel ( app , req , res ) } )
2020-09-10 09:02:40 -04:00
app . get ( "/api/level/:id" , RL , async function ( req , res ) { app . run . level ( app , req , res , api ) } )
2020-11-02 11:16:35 -05:00
app . get ( "/api/mappacks" , async function ( req , res ) { app . run . mappack ( app , req , res ) } )
2020-11-07 19:20:44 -05:00
app . get ( "/api/profile/:id" , RL2 , function ( req , res ) { app . run . profile ( app , req , res , api ) } )
app . get ( "/api/search/:text" , RL2 , function ( req , res ) { app . run . search ( app , req , res ) } )
2019-12-16 08:48:05 -05:00
2019-10-15 22:42:47 -04:00
2019-12-16 08:48:05 -05:00
// REDIRECTS
2019-10-15 22:42:47 -04:00
2019-12-16 08:48:05 -05:00
app . get ( "/icon" , function ( req , res ) { res . redirect ( '/iconkit' ) } )
app . get ( "/iconkit/:text" , function ( req , res ) { res . redirect ( '/icon/' + req . params . text ) } )
2020-10-13 10:25:07 -04:00
app . get ( "/obj/:text" , function ( req , res ) { res . redirect ( '/obj/' + req . params . text ) } )
2019-12-16 08:48:05 -05:00
app . get ( "/leaderboards/:id" , function ( req , res ) { res . redirect ( '/leaderboard/' + req . params . id ) } )
2020-09-29 21:42:18 -04:00
app . get ( "/profile/:id" , function ( req , res ) { res . redirect ( '/u/' + req . params . id ) } )
app . get ( "/p/:id" , function ( req , res ) { res . redirect ( '/u/' + req . params . id ) } )
2019-12-16 08:48:05 -05:00
app . get ( "/l/:id" , function ( req , res ) { res . redirect ( '/leaderboard/' + req . params . id ) } )
app . get ( "/a/:id" , function ( req , res ) { res . redirect ( '/analyze/' + req . params . id ) } )
app . get ( "/c/:id" , function ( req , res ) { res . redirect ( '/comments/' + req . params . id ) } )
2020-10-28 21:25:13 -04:00
app . get ( "/d/:id" , function ( req , res ) { res . redirect ( '/demon/' + req . params . id ) } )
2019-10-15 22:42:47 -04:00
2020-09-24 22:07:53 -04:00
// API AND HTML
2020-09-29 21:42:18 -04:00
app . get ( "/u/:id" , function ( req , res ) { app . run . profile ( app , req , res ) } )
2020-09-24 22:07:53 -04:00
app . get ( "/:id" , function ( req , res ) { app . run . level ( app , req , res ) } )
2019-12-16 08:48:05 -05:00
// MISC
2019-10-15 22:42:47 -04:00
2019-12-16 08:48:05 -05:00
app . get ( "/assets/sizecheck.js" , function ( req , res ) { res . sendFile ( _ _dirname + "/misc/sizecheck.js" ) } )
2019-12-25 16:20:32 -05:00
app . get ( "/icon/:text" , function ( req , res ) { app . run . icon ( app , req , res ) } )
2020-10-13 10:25:07 -04:00
app . get ( "/object/:text" , function ( req , res ) { app . run . object ( app , req , res ) } )
2020-09-24 22:07:53 -04:00
app . get ( '/api/icons' , function ( req , res ) {
let sample = [ JSON . stringify ( sampleIcons [ Math . floor ( Math . random ( ) * sampleIcons . length ) ] . slice ( 1 ) ) ]
res . send ( gdIcons . concat ( sample ) ) ;
} ) ;
2019-10-15 22:42:47 -04:00
app . get ( '*' , function ( req , res ) {
2019-12-16 08:48:05 -05:00
if ( req . path . startsWith ( '/api' ) ) res . send ( '-1' )
2019-12-21 14:33:32 -05:00
if ( assets . some ( x => req . path . startsWith ( "/" + x ) ) ) res . send ( "Looks like this file doesn't exist ¯\\_(ツ)_/¯<br>You can check out all of the assets on <a target='_blank' href='https://github.com/GDColon/GDBrowser/tree/master/assets'>GitHub</a>" )
2019-12-15 19:11:35 -05:00
else res . redirect ( '/search/404%20' )
2019-10-15 22:42:47 -04:00
} ) ;
2020-11-01 15:29:32 -05:00
app . use ( function ( err , req , res , next ) {
if ( err && err . message == "Response timeout" ) res . status ( 500 ) . send ( 'Internal server error! (Timed out)' )
} )
2020-04-09 10:42:08 -04:00
app . listen ( 2000 , ( ) => console . log ( "Site online!" ) )