2022-07-08 18:17:56 +00:00
import "dotenv/config"
import express from "express" ;
2022-07-10 14:04:03 +00:00
import cors from "cors" ;
2022-07-08 18:17:56 +00:00
import * as fs from "fs" ;
import rateLimit from "express-rate-limit" ;
2022-07-17 11:21:51 +00:00
import { shortCommit } from "./modules/sub/currentCommit.js" ;
2023-02-09 14:45:17 +00:00
import { appName , genericUserAgent , version } from "./modules/config.js" ;
2022-07-08 18:17:56 +00:00
import { getJSON } from "./modules/api.js" ;
2022-08-12 13:36:19 +00:00
import renderPage from "./modules/pageRender/page.js" ;
2022-11-12 16:40:11 +00:00
import { apiJSON , checkJSONPost , languageCode } from "./modules/sub/utils.js" ;
2022-12-17 11:09:49 +00:00
import { Bright , Cyan , Green , Red } from "./modules/sub/consoleText.js" ;
2022-07-08 18:17:56 +00:00
import stream from "./modules/stream/stream.js" ;
2022-08-12 13:36:19 +00:00
import loc from "./localization/manager.js" ;
2022-07-30 09:01:54 +00:00
import { buildFront } from "./modules/build.js" ;
2022-09-11 15:04:06 +00:00
import { changelogHistory } from "./modules/pageRender/onDemand.js" ;
2023-01-13 18:34:48 +00:00
import { sha256 } from "./modules/sub/crypto.js" ;
2022-07-08 18:17:56 +00:00
2022-07-13 20:32:00 +00:00
const commitHash = shortCommit ( ) ;
2022-07-08 18:17:56 +00:00
const app = express ( ) ;
2022-07-10 14:04:03 +00:00
app . disable ( 'x-powered-by' ) ;
2022-09-20 07:54:44 +00:00
if ( fs . existsSync ( './.env' ) && process . env . selfURL && process . env . streamSalt && process . env . port ) {
2022-07-08 18:17:56 +00:00
const apiLimiter = rateLimit ( {
2022-12-06 19:21:07 +00:00
windowMs : 1 * 60 * 1000 ,
max : 12 ,
2022-07-08 18:17:56 +00:00
standardHeaders : true ,
legacyHeaders : false ,
handler : ( req , res , next , opt ) => {
2022-07-24 10:54:05 +00:00
res . status ( 429 ) . json ( { "status" : "error" , "text" : loc ( languageCode ( req ) , 'ErrorRateLimit' ) } ) ;
2022-07-08 18:17:56 +00:00
}
2022-12-06 19:21:07 +00:00
} ) ;
2022-07-08 18:17:56 +00:00
const apiLimiterStream = rateLimit ( {
2022-12-06 19:21:07 +00:00
windowMs : 1 * 60 * 1000 ,
max : 12 ,
2022-07-08 18:17:56 +00:00
standardHeaders : true ,
legacyHeaders : false ,
handler : ( req , res , next , opt ) => {
2022-07-24 10:54:05 +00:00
res . status ( 429 ) . json ( { "status" : "error" , "text" : loc ( languageCode ( req ) , 'ErrorRateLimit' ) } ) ;
2022-07-08 18:17:56 +00:00
}
2022-12-06 19:21:07 +00:00
} ) ;
2022-07-08 18:17:56 +00:00
2022-07-30 09:01:54 +00:00
await buildFront ( ) ;
2022-07-08 18:17:56 +00:00
app . use ( '/api/' , apiLimiter ) ;
app . use ( '/api/stream' , apiLimiterStream ) ;
2022-07-30 09:01:54 +00:00
app . use ( '/' , express . static ( './min' ) ) ;
app . use ( '/' , express . static ( './src/front' ) ) ;
2022-07-08 18:17:56 +00:00
app . use ( ( req , res , next ) => {
try {
decodeURIComponent ( req . path )
}
2022-08-16 07:14:19 +00:00
catch ( e ) {
return res . redirect ( process . env . selfURL ) ;
2022-07-08 18:17:56 +00:00
}
next ( ) ;
} ) ;
2023-02-09 14:45:17 +00:00
app . use ( ( req , res , next ) => {
if ( req . header ( "user-agent" ) && req . header ( "user-agent" ) . includes ( "Trident" ) ) {
res . destroy ( )
}
next ( ) ;
} ) ;
2022-11-12 16:40:11 +00:00
app . use ( '/api/json' , express . json ( {
verify : ( req , res , buf ) => {
try {
JSON . parse ( buf ) ;
if ( buf . length > 720 ) throw new Error ( ) ;
if ( req . header ( 'Content-Type' ) != "application/json" ) res . status ( 500 ) . json ( { 'status' : 'error' , 'text' : 'invalid content type header' } )
if ( req . header ( 'Accept' ) != "application/json" ) res . status ( 500 ) . json ( { 'status' : 'error' , 'text' : 'invalid accept header' } )
} catch ( e ) {
res . status ( 500 ) . json ( { 'status' : 'error' , 'text' : 'invalid json body.' } )
}
}
} ) ) ;
app . post ( '/api/:type' , cors ( { origin : process . env . selfURL , optionsSuccessStatus : 200 } ) , async ( req , res ) => {
try {
2023-01-13 18:34:48 +00:00
let ip = sha256 ( req . header ( 'x-forwarded-for' ) ? req . header ( 'x-forwarded-for' ) : req . ip . replace ( '::ffff:' , '' ) , process . env . streamSalt ) ;
2022-11-12 16:40:11 +00:00
switch ( req . params . type ) {
case 'json' :
try {
let request = req . body ;
let chck = checkJSONPost ( request ) ;
if ( request . url && chck ) {
chck [ "ip" ] = ip ;
2022-12-06 19:21:07 +00:00
let j = await getJSON ( chck [ "url" ] , languageCode ( req ) , chck )
res . status ( j . status ) . json ( j . body ) ;
} else if ( request . url && ! chck ) {
let j = apiJSON ( 3 , { t : loc ( languageCode ( req ) , 'ErrorCouldntFetch' ) } ) ;
2022-11-12 16:40:11 +00:00
res . status ( j . status ) . json ( j . body ) ;
} else {
2022-12-06 19:21:07 +00:00
let j = apiJSON ( 3 , { t : loc ( languageCode ( req ) , 'ErrorNoLink' ) } )
res . status ( j . status ) . json ( j . body ) ;
2022-11-12 16:40:11 +00:00
}
} catch ( e ) {
res . status ( 500 ) . json ( { 'status' : 'error' , 'text' : loc ( languageCode ( req ) , 'ErrorCantProcess' ) } )
}
break ;
default :
let j = apiJSON ( 0 , { t : "unknown response type" } )
res . status ( j . status ) . json ( j . body ) ;
break ;
}
} catch ( e ) {
res . status ( 500 ) . json ( { 'status' : 'error' , 'text' : loc ( languageCode ( req ) , 'ErrorCantProcess' ) } )
}
} ) ;
2022-12-17 11:31:31 +00:00
app . get ( '/api/:type' , cors ( { origin : process . env . selfURL , optionsSuccessStatus : 200 } ) , ( req , res ) => {
2022-07-08 18:17:56 +00:00
try {
2023-01-13 18:34:48 +00:00
let ip = sha256 ( req . header ( 'x-forwarded-for' ) ? req . header ( 'x-forwarded-for' ) : req . ip . replace ( '::ffff:' , '' ) , process . env . streamSalt ) ;
2022-07-08 18:17:56 +00:00
switch ( req . params . type ) {
case 'json' :
2022-12-17 11:29:40 +00:00
res . status ( 405 ) . json ( { 'status' : 'error' , 'text' : 'GET method for this request has been deprecated. see https://github.com/wukko/cobalt/blob/current/docs/API.md for up-to-date API documentation.' } ) ;
2022-07-08 18:17:56 +00:00
break ;
case 'stream' :
if ( req . query . p ) {
res . status ( 200 ) . json ( { "status" : "continue" } ) ;
2022-11-12 16:40:11 +00:00
} else if ( req . query . t && req . query . h && req . query . e ) {
2022-08-14 17:09:06 +00:00
stream ( res , ip , req . query . t , req . query . h , req . query . e ) ;
2022-07-08 18:17:56 +00:00
} else {
2022-09-03 15:32:39 +00:00
let j = apiJSON ( 0 , { t : "no stream id" } )
2022-07-08 18:17:56 +00:00
res . status ( j . status ) . json ( j . body ) ;
}
break ;
2022-09-11 15:04:06 +00:00
case 'onDemand' :
if ( req . query . blockId ) {
let blockId = req . query . blockId . slice ( 0 , 3 )
let r , j ;
switch ( blockId ) {
case "0" :
r = changelogHistory ( ) ;
j = r ? apiJSON ( 3 , { t : r } ) : apiJSON ( 0 , { t : "couldn't render this block" } )
break ;
default :
j = apiJSON ( 0 , { t : "couldn't find a block with this id" } )
break ;
}
res . status ( j . status ) . json ( j . body ) ;
} else {
let j = apiJSON ( 0 , { t : "no block id" } )
res . status ( j . status ) . json ( j . body ) ;
}
break ;
2022-07-08 18:17:56 +00:00
default :
2022-09-11 15:04:06 +00:00
let j = apiJSON ( 0 , { t : "unknown response type" } )
2022-07-08 18:17:56 +00:00
res . status ( j . status ) . json ( j . body ) ;
break ;
}
} catch ( e ) {
2022-11-12 16:40:11 +00:00
res . status ( 500 ) . json ( { 'status' : 'error' , 'text' : loc ( languageCode ( req ) , 'ErrorCantProcess' ) } )
2022-07-08 18:17:56 +00:00
}
} ) ;
2022-09-01 13:51:18 +00:00
app . get ( "/api" , ( req , res ) => {
2022-07-08 18:17:56 +00:00
res . redirect ( '/api/json' )
} ) ;
2022-09-01 13:51:18 +00:00
app . get ( "/" , ( req , res ) => {
2023-02-09 14:45:17 +00:00
res . send ( renderPage ( {
"hash" : commitHash ,
"type" : "default" ,
"lang" : languageCode ( req ) ,
"useragent" : req . header ( 'user-agent' ) ? req . header ( 'user-agent' ) : genericUserAgent
} ) )
2022-07-08 18:17:56 +00:00
} ) ;
2022-09-01 13:51:18 +00:00
app . get ( "/favicon.ico" , ( req , res ) => {
2022-07-08 18:17:56 +00:00
res . redirect ( '/icons/favicon.ico' ) ;
} ) ;
2022-09-01 13:51:18 +00:00
app . get ( "/*" , ( req , res ) => {
2022-07-08 18:17:56 +00:00
res . redirect ( '/' )
} ) ;
app . listen ( process . env . port , ( ) => {
2022-12-17 11:40:20 +00:00
let startTime = new Date ( ) ;
console . log ( ` \n ${ Cyan ( appName ) } ${ Bright ( ` v. ${ version } - ${ commitHash } ` ) } \n Start time: ${ Bright ( ` ${ startTime . toUTCString ( ) } ( ${ Math . floor ( new Date ( ) . getTime ( ) ) } ) ` ) } \n \n URL: ${ Cyan ( ` ${ process . env . selfURL } ` ) } \n Port: ${ process . env . port } \n ` )
2022-07-08 18:17:56 +00:00
} ) ;
} else {
2022-12-17 11:09:49 +00:00
console . log ( Red ( ` cobalt hasn't been configured yet or configuration is invalid. \n ` ) + Bright ( ` please run the setup script to fix this: ` ) + Green ( ` npm run setup ` ) )
2022-08-01 15:48:37 +00:00
}