2021-08-12 17:10:36 +00:00
const fastify = require ( 'fastify' ) ( { logger : true } )
2021-08-12 18:17:10 +00:00
var uniqid = require ( 'uniqid' ) ;
2021-08-14 08:15:59 +00:00
require ( 'dotenv' ) . config ( ) ;
const argon2 = require ( 'argon2' ) ;
2021-09-25 15:14:34 +00:00
const isBot = require ( 'isbot' )
2022-02-02 14:25:55 +00:00
const { makeBadge , ValidationError } = require ( 'badge-maker' )
2021-08-12 18:17:10 +00:00
2021-08-12 18:24:26 +00:00
let config = {
domain : process . env . DOMAIN || "localhost:3000" ,
2021-08-12 18:26:33 +00:00
https : ( process . env . SSL === 'true' ) || false ,
2021-08-18 13:36:16 +00:00
env : process . env . NODE _ENV || 'development' ,
2021-08-14 08:26:44 +00:00
recognizeProviders : ! ( process . env . DISABLE _PROVIDERS === 'true' ) ,
registrationEnabled : ( process . env . ENABLE _REGISTER === 'true' ) ,
2021-08-18 13:57:23 +00:00
jwt _secret : process . env . JWT _SECRET || "pleaseneverusethisdefaultsecret" ,
2021-08-12 19:44:58 +00:00
getBaseUrl ( ) {
if ( config . https ) {
2021-08-12 18:24:26 +00:00
return ` https:// ${ config . domain } ` ;
}
return ` http:// ${ config . domain } ` ;
}
}
2021-08-18 13:36:16 +00:00
const knexConfiguration = require ( '../knexfile' ) [ config . env ] ;
2021-08-14 11:23:11 +00:00
const knex = require ( 'knex' ) ( knexConfiguration ) ;
2021-08-12 17:10:36 +00:00
2021-08-14 08:09:05 +00:00
const authenticate = { realm : 'Short' }
2021-08-18 13:36:16 +00:00
fastify . register ( require ( 'fastify-auth' ) )
2021-08-14 08:09:05 +00:00
fastify . register ( require ( 'fastify-basic-auth' ) , { validate , authenticate } ) ;
2021-08-18 13:57:23 +00:00
fastify . register ( require ( 'fastify-jwt' ) , {
secret : config . jwt _secret
} ) ;
2021-08-18 13:36:16 +00:00
fastify . register ( require ( 'fastify-cors' ) , {
2021-08-14 12:19:08 +00:00
origin : true ,
preflight : true ,
preflightContinue : true
2021-08-18 13:36:16 +00:00
} )
2021-08-12 17:10:36 +00:00
2021-08-18 14:22:50 +00:00
fastify . decorate ( 'verifyJWT' , function async ( request , reply , done ) {
2021-08-18 13:57:23 +00:00
let token = request . headers . authorization ;
2021-08-18 14:09:34 +00:00
if ( ! token || token == "" || token == "Bearer" ) {
2021-08-18 14:22:50 +00:00
done ( new Error ( "No jwt provided" ) ) ;
2021-08-18 13:57:23 +00:00
}
2021-08-18 14:09:34 +00:00
if ( token . startsWith ( "Bearer" ) ) {
token = token . replace ( "Bearer " , "" ) ;
2021-08-18 13:57:23 +00:00
fastify . log . info ( "Detected bearer and replaced it" )
}
2021-08-18 14:09:34 +00:00
fastify . jwt . verify ( token , async ( err , decoded ) => {
2021-08-18 13:57:23 +00:00
if ( err ) {
2021-08-21 05:35:13 +00:00
fastify . log . error ( "JWT validation failed:" )
done ( new Error ( "JWT Validation failed" ) ) ;
2021-08-18 13:57:23 +00:00
}
2021-08-21 05:35:13 +00:00
else {
if ( ! decoded . payload ) {
done ( new Error ( "JWT is empty" ) ) ;
}
fastify . log . info ( ` Token verified. User is ${ decoded . payload . user } ` ) ;
2021-08-18 14:09:34 +00:00
2021-08-21 05:35:13 +00:00
const jwtcount = ( await knex . select ( 'jwtcount' )
. from ( 'users' )
. where ( 'username' , '=' , decoded . payload . user )
. limit ( 1 ) ) [ 0 ] . jwtcount ;
2021-08-18 14:09:34 +00:00
2021-08-21 05:35:13 +00:00
if ( decoded . payload . jwtcount < jwtcount ) {
fastify . log . error ( "Auth ended at jwtcount" )
done ( new Error ( "JWT in no longer valid" ) )
}
else {
fastify . log . info ( ` JWT count verified ` ) ;
request . user = decoded . payload . user ;
done ( )
}
2021-08-18 14:09:34 +00:00
}
2021-08-18 13:57:23 +00:00
} )
} )
2021-08-12 17:22:14 +00:00
//Automagic Amazn redirects on /a/
fastify . get ( '/a/:id' , async ( req , res ) => {
2021-08-12 17:10:36 +00:00
res . redirect ( 302 , ` https://amazon.de/dp/ ${ req . params . id } ` )
2021-08-21 07:54:55 +00:00
await knex ( 'visits' ) . insert ( { shortcode : req . params . id , provider : 'a' } ) ;
2021-08-12 17:10:36 +00:00
} )
2021-08-12 17:22:14 +00:00
//Automagic Youtube redirects on /yt/
fastify . get ( '/yt/:id' , async ( req , res ) => {
res . redirect ( 302 , ` https://youtu.be/ ${ req . params . id } ` )
2021-08-21 07:54:55 +00:00
await knex ( 'visits' ) . insert ( { shortcode : req . params . id , provider : 'yt' } ) ;
2021-08-12 17:10:36 +00:00
} )
2021-08-14 07:03:18 +00:00
//Automagic Youtube Playlist redirects on /ytpl/
2021-08-12 19:44:58 +00:00
fastify . get ( '/ytpl/:id' , async ( req , res ) => {
res . redirect ( 302 , ` https://youtube.com/playlist?list= ${ req . params . id } ` )
2021-08-21 07:54:55 +00:00
await knex ( 'visits' ) . insert ( { shortcode : req . params . id , provider : 'ytpl' } ) ;
2021-08-12 19:44:58 +00:00
} )
2021-08-12 17:10:36 +00:00
2021-08-14 13:47:58 +00:00
//Automagic ebay item redirects on /e/
fastify . get ( '/e/:id' , async ( req , res ) => {
res . redirect ( 302 , ` https://ebay.de/itm/ ${ req . params . id } ` )
2021-08-21 07:54:55 +00:00
await knex ( 'visits' ) . insert ( { shortcode : req . params . id , provider : 'e' } ) ;
2021-08-14 13:47:58 +00:00
} )
2022-01-12 16:54:50 +00:00
//Automagic reddit redirects on /r/
fastify . get ( '/r/:id' , async ( req , res ) => {
res . redirect ( 302 , ` https://redd.it/ ${ req . params . id } ` )
await knex ( 'visits' ) . insert ( { shortcode : req . params . id , provider : 'r' } ) ;
} )
2021-08-12 17:33:59 +00:00
//Normal shorturls
2021-08-12 18:13:46 +00:00
fastify . get ( '/:shortcode' , async ( req , res ) => {
const shortcode = req . params . shortcode ;
2021-08-12 19:21:23 +00:00
//This should never happen but better safe than 500
2021-08-12 18:13:46 +00:00
if ( ! shortcode ) {
2021-08-12 17:22:14 +00:00
return 404 ;
}
2021-09-25 16:28:49 +00:00
const target = await knex . select ( 'target' , 'no_preview' , 'clientside' )
2021-08-12 18:13:46 +00:00
. from ( 'urls' )
. where ( 'shortcode' , '=' , shortcode )
. limit ( 1 ) ;
if ( ! target [ 0 ] ) {
return 404
}
2021-09-25 15:19:36 +00:00
2021-09-25 15:44:34 +00:00
if ( isBot ( req . headers [ 'user-agent' ] ) && target [ 0 ] . no _preview ) {
2021-09-25 16:45:52 +00:00
res . type ( "text/html" ) ;
return bot _html ;
}
2021-09-25 15:35:52 +00:00
2021-09-25 16:45:52 +00:00
if ( target [ 0 ] . clientside ) {
res . type ( "text/html" ) ;
return clientside _html . replace ( "{{targeturl}}" , target [ 0 ] . target )
2021-09-25 15:14:34 +00:00
}
2021-09-25 15:17:42 +00:00
2021-08-14 07:23:55 +00:00
res . redirect ( 302 , target [ 0 ] . target ) ;
2021-08-21 07:49:45 +00:00
await knex ( 'visits' ) . insert ( { shortcode , provider : 'native' } ) ;
2021-08-12 17:22:14 +00:00
} )
2021-08-12 17:10:36 +00:00
2021-08-12 19:21:23 +00:00
//Create new url schema
2021-08-12 17:39:10 +00:00
const newUrlSchema = {
body : {
type : 'object' ,
properties : {
target : { type : 'string' } ,
shortcode : { type : 'string' } ,
2021-09-25 15:19:36 +00:00
no _preview : { type : 'boolean' } ,
2021-09-25 16:28:49 +00:00
clientside : { type : 'boolean' }
2021-08-12 17:39:10 +00:00
}
}
} ;
2021-08-14 07:03:18 +00:00
//Create new url api route
fastify . post ( '/api' , { newUrlSchema } , async ( req , res ) => {
2021-08-12 17:33:59 +00:00
const target = req . body ? . target ;
2021-08-12 17:34:36 +00:00
let shortcode = req . body ? . shortcode ;
2021-09-25 15:19:36 +00:00
let no _preview = req . body ? . no _preview || false ;
2021-09-25 16:28:49 +00:00
let clientside = req . body ? . clientside || false ;
2021-08-12 19:21:23 +00:00
//Check if the user provided a target
2021-08-12 17:39:10 +00:00
if ( ! target ) {
2021-08-12 17:33:59 +00:00
res . statusCode = 400 ;
return "Missing target" ;
}
2021-08-12 18:13:46 +00:00
2021-08-12 19:21:23 +00:00
/ * *
* If no custom shortcode is provided : Check if a code for the target already exists .
* If it exists : No new get 's generated => The existing code get' s used .
* If it doesn ' t exist : Generate a new code and proceed to creating a new db entry .
* /
2021-08-12 17:33:59 +00:00
if ( ! shortcode ) {
2021-08-12 19:44:58 +00:00
if ( config . recognizeProviders ) {
2021-08-12 19:23:40 +00:00
const response = checkKnownProviders ( target ) ;
2021-08-12 19:44:58 +00:00
if ( response ) {
2021-08-12 19:23:40 +00:00
return response ;
}
}
2021-09-25 16:28:49 +00:00
const exists = await knex . select ( 'shortcode' , 'no_preview' , 'clientside' )
2021-08-12 18:13:46 +00:00
. from ( 'urls' )
. where ( 'target' , '=' , target )
. limit ( 1 ) ;
if ( exists . length != 0 ) {
shortcode = exists [ 0 ] . shortcode ;
return {
2021-08-12 18:24:26 +00:00
url : ` ${ config . getBaseUrl ( ) } / ${ shortcode } ` ,
2021-08-12 18:13:46 +00:00
shortcode ,
2021-09-25 15:27:41 +00:00
target ,
2021-09-25 16:28:49 +00:00
no _preview : exists [ 0 ] . no _preview ,
clientside : exists [ 0 ] . clientside
2021-08-12 18:13:46 +00:00
}
}
2021-08-12 18:17:10 +00:00
shortcode = uniqid ( ) ;
2021-08-12 17:33:59 +00:00
}
2021-08-12 19:21:23 +00:00
/ * *
* If a custom shortcode is provided : Check for collisions .
* Collision detected : Warn user
* No collision : Proceed to db entry creation
* /
2021-08-12 17:39:10 +00:00
else {
2021-08-12 18:13:46 +00:00
const exists = await knex . select ( 'shortcode' )
. from ( 'urls' )
. where ( 'shortcode' , '=' , shortcode )
. limit ( 1 ) ;
if ( exists . length != 0 ) {
res . statusCode = 400 ;
return "Shortcode already exists, please choose another code" ;
2021-08-12 17:33:59 +00:00
}
}
2021-08-12 19:21:23 +00:00
//Create a new db entry
2021-09-25 16:28:49 +00:00
await knex ( 'urls' ) . insert ( { target , shortcode , no _preview , clientside } ) ;
2021-08-12 18:13:46 +00:00
2021-08-12 17:33:59 +00:00
return {
2021-08-12 18:24:26 +00:00
url : ` ${ config . getBaseUrl ( ) } / ${ shortcode } ` ,
2021-08-12 17:33:59 +00:00
shortcode ,
2021-09-25 15:19:36 +00:00
target ,
2021-09-25 16:28:49 +00:00
no _preview ,
clientside
2021-08-12 18:13:46 +00:00
}
2021-08-12 19:23:40 +00:00
} ) ;
2021-08-18 13:21:36 +00:00
//Get stats api route
fastify . get ( '/api/stats' , async ( req , res ) => {
const urls = await knex . select ( 'shortcode' )
. from ( 'urls' ) ;
const visits = await knex . select ( 'timestamp' )
. from ( 'visits' ) ;
return {
urls : urls . length ,
visits : visits . length ,
}
} ) ;
2021-08-14 07:03:18 +00:00
//Get url api route
fastify . get ( '/api/:shortcode' , async ( req , res ) => {
const shortcode = req . params . shortcode ;
//This should never happen but better safe than 500
if ( ! shortcode ) {
return 404 ;
}
2021-09-25 16:28:49 +00:00
const exists = await knex . select ( 'shortcode' , 'target' , 'no_preview' , 'clientside' )
2021-08-14 07:03:18 +00:00
. from ( 'urls' )
. where ( 'shortcode' , '=' , shortcode )
. limit ( 1 ) ;
if ( exists . length == 0 ) {
return 404 ;
}
2021-08-14 07:27:59 +00:00
const visits = await knex . select ( 'timestamp' )
. from ( 'visits' )
. where ( 'shortcode' , '=' , shortcode ) ;
2021-08-14 07:03:18 +00:00
return {
url : ` ${ config . getBaseUrl ( ) } / ${ exists [ 0 ] . shortcode } ` ,
shortcode : exists [ 0 ] . shortcode ,
2021-08-14 07:27:59 +00:00
target : exists [ 0 ] . target ,
2021-09-25 15:21:30 +00:00
no _preview : exists [ 0 ] . no _preview ,
2021-09-25 16:28:49 +00:00
clientside : exists [ 0 ] . clientside ,
2021-08-14 07:27:59 +00:00
visits : visits . length
}
} ) ;
2021-08-14 07:28:10 +00:00
2022-02-02 14:25:55 +00:00
//Get url api route
fastify . get ( '/api/badge/:shortcode' , async ( req , res ) => {
const shortcode = req . params . shortcode ;
const label = req . query . label || 'vists' ;
const color = req . query . color || 'green' ;
const style = req . query . style || 'for-the-badge' ;
//This should never happen but better safe than 500
if ( ! shortcode ) {
return 404 ;
}
const exists = await knex . select ( 'shortcode' , 'target' , 'no_preview' , 'clientside' )
. from ( 'urls' )
. where ( 'shortcode' , '=' , shortcode )
. limit ( 1 ) ;
if ( exists . length == 0 ) {
return 404 ;
}
const visits = await knex . select ( 'timestamp' )
. from ( 'visits' )
. where ( 'shortcode' , '=' , shortcode ) ;
const format = {
label ,
message : visits . length . toString ( ) ,
color ,
style
}
res . type ( 'image/svg+xml' )
return makeBadge ( format ) ;
} ) ;
2021-08-18 13:21:36 +00:00
2021-08-14 08:26:44 +00:00
//User registration
2021-08-18 15:46:49 +00:00
fastify . post ( '/api/auth/register' , async ( req , res ) => {
2021-08-14 08:26:44 +00:00
if ( ! config . registrationEnabled ) {
res . statusCode = 400 ;
return "Registration was disabled by your admin" ;
}
const username = req . body ? . username ;
let password = req . body ? . password ;
//Check
if ( ! username || ! password ) {
res . statusCode = 400 ;
return "Missing username or password" ;
}
const exists = await knex . select ( 'username' )
. from ( 'users' )
. where ( 'username' , '=' , username )
. limit ( 1 ) ;
if ( exists . length != 0 ) {
res . statusCode = 400 ;
return "User already exists" ;
}
password = await argon2 . hash ( password ) ;
//Create a new db entry
await knex ( 'users' ) . insert ( { username , password } ) ;
return "Done!"
} ) ;
2021-08-16 13:19:04 +00:00
//Anything in here has some kind of auth
2021-08-14 08:09:05 +00:00
fastify . after ( ( ) => {
2021-08-21 07:59:14 +00:00
//Get url visits api route
2021-08-18 14:10:59 +00:00
fastify . get ( '/api/:shortcode/visits' , { onRequest : fastify . auth ( [ fastify . basicAuth , fastify . verifyJWT ] ) } , async ( req , res ) => {
2021-08-14 08:09:05 +00:00
const shortcode = req . params . shortcode ;
2021-08-14 07:28:10 +00:00
2021-08-14 08:09:05 +00:00
//This should never happen but better safe than 500
if ( ! shortcode ) {
return 404 ;
}
2021-08-14 07:28:10 +00:00
2021-08-21 07:51:25 +00:00
const visits = await knex . select ( 'timestamp' , 'provider' )
2021-08-14 08:09:05 +00:00
. from ( 'visits' )
. where ( 'shortcode' , '=' , shortcode ) ;
2021-08-14 07:28:10 +00:00
2021-08-14 08:09:05 +00:00
return visits ;
} ) ;
2021-08-14 07:03:18 +00:00
2021-08-21 07:59:14 +00:00
//Get all visits api route
fastify . get ( '/api/visits' , { onRequest : fastify . auth ( [ fastify . basicAuth , fastify . verifyJWT ] ) } , async ( req , res ) => {
2021-09-25 15:44:34 +00:00
if ( req . query . provider ) {
2021-08-21 08:02:52 +00:00
return await knex . select ( 'shortcode' , 'provider' , 'timestamp' )
2021-09-25 15:44:34 +00:00
. from ( 'visits' )
. where ( "provider" , "=" , req . query . provider ) ;
2021-08-21 08:02:52 +00:00
}
2021-08-21 08:01:24 +00:00
return await knex . select ( 'shortcode' , 'provider' , 'timestamp' )
2021-08-21 07:59:14 +00:00
. from ( 'visits' ) ;
} ) ;
2021-08-14 08:09:05 +00:00
//Get url api route
2021-08-18 14:10:59 +00:00
fastify . delete ( '/api/:shortcode' , { onRequest : fastify . auth ( [ fastify . basicAuth , fastify . verifyJWT ] ) } , async ( req , res ) => {
2021-08-14 08:09:05 +00:00
const shortcode = req . params . shortcode ;
2021-08-14 07:30:16 +00:00
2021-08-14 08:09:05 +00:00
//This should never happen but better safe than 500
if ( ! shortcode ) {
return 404 ;
}
2021-08-14 07:30:16 +00:00
2021-08-14 08:09:05 +00:00
await knex ( 'urls' )
. where ( 'shortcode' , '=' , shortcode )
. delete ( ) ;
res . statusCode = 204 ;
return true ;
} ) ;
2021-08-14 07:30:16 +00:00
2021-08-14 09:50:29 +00:00
//Get all urls api route
2021-08-18 14:10:59 +00:00
fastify . get ( '/api' , { onRequest : fastify . auth ( [ fastify . basicAuth , fastify . verifyJWT ] ) } , async ( req , res ) => {
2021-09-25 16:28:49 +00:00
urls = await knex . select ( 'target' , 'shortcode' , 'no_preview' , 'clientside' )
2021-08-14 09:50:29 +00:00
. from ( 'urls' ) ;
for ( let url of urls ) {
url . url = ` ${ config . getBaseUrl ( ) } / ${ url . shortcode } `
2021-08-18 13:36:16 +00:00
if ( req . query . showVisits ) {
2021-08-14 09:50:29 +00:00
url . visits = ( await knex . select ( 'timestamp' )
. from ( 'visits' )
. where ( 'shortcode' , '=' , url . shortcode ) ) . length ;
}
}
return urls ;
} ) ;
2021-08-18 14:09:34 +00:00
fastify . post ( '/api/auth/login' , { onRequest : fastify . auth ( [ fastify . basicAuth ] ) } , async ( req , reply ) => {
const jwtcount = ( await knex . select ( 'jwtcount' )
. from ( 'users' )
. where ( 'username' , '=' , req . user )
. limit ( 1 ) ) [ 0 ] . jwtcount ;
2021-08-18 13:57:23 +00:00
const payload = {
2021-08-18 14:09:34 +00:00
user : req . user ,
jwtcount
2021-08-18 13:57:23 +00:00
} ;
const token = fastify . jwt . sign ( { payload } )
2021-08-18 14:09:34 +00:00
reply . send ( { token } ) ;
2021-08-18 13:57:23 +00:00
} ) ;
fastify . post ( '/api/auth/check' , { onRequest : fastify . auth ( [ fastify . basicAuth , fastify . verifyJWT ] ) } , ( req , reply ) => {
return "logged in" ;
2021-08-18 14:22:50 +00:00
} ) ;
fastify . post ( '/api/auth/logout' , { onRequest : fastify . auth ( [ fastify . basicAuth , fastify . verifyJWT ] ) } , async ( req , reply ) => {
let jwtcount = ( await knex . select ( 'jwtcount' )
. from ( 'users' )
. where ( 'username' , '=' , req . user )
. limit ( 1 ) ) [ 0 ] . jwtcount ;
jwtcount += 1 ;
await knex ( 'users' )
. where ( 'username' , '=' , req . user )
. update ( {
jwtcount
} ) ;
return "Done!" ;
} ) ;
2021-08-18 13:57:23 +00:00
2021-08-18 15:46:49 +00:00
fastify . post ( '/api/auth/deleteme' , { onRequest : fastify . auth ( [ fastify . basicAuth , fastify . verifyJWT ] ) } , async ( req , reply ) => {
await knex ( 'users' )
. where ( 'username' , '=' , req . user )
. delete ( ) ;
return "Done!" ;
} ) ;
2021-08-14 07:30:16 +00:00
} ) ;
2021-08-14 08:09:05 +00:00
2021-08-12 19:23:40 +00:00
/ * *
* Checks for some default providers with custom url schemes ( amazon and youtube r / n )
* @ param { string } target The target URL
* @ returns Standard shortening response if provider recognized or null
* /
2021-08-12 19:44:58 +00:00
function checkKnownProviders ( target ) {
2021-08-14 07:03:18 +00:00
target = decodeURIComponent ( target ) ;
2021-08-12 19:44:58 +00:00
const youtubeVideoID = target . match ( /(?:youtube(?:-nocookie)?\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/ )
if ( youtubeVideoID ) {
const shortcode = ` yt/ ${ youtubeVideoID [ 1 ] } `
return {
url : ` ${ config . getBaseUrl ( ) } / ${ shortcode } ` ,
shortcode ,
target
}
}
const youtubePlaylistID = target . match ( /(?:youtube(?:-nocookie)?\.com\/)playlist\?(?:.*)list=([a-zA-Z0-9_-]*)(?:&.*|)/ )
if ( youtubePlaylistID ) {
const shortcode = ` ytpl/ ${ youtubePlaylistID [ 1 ] } `
2021-08-12 19:23:40 +00:00
return {
url : ` ${ config . getBaseUrl ( ) } / ${ shortcode } ` ,
shortcode ,
target
}
}
2021-08-14 13:46:14 +00:00
const amazonID = target . match ( /(?:https?:\/\/|)(www|smile|)\.?(amazon|smile)\.(de)(?:(?:\/.*\/|\/)(?:dp|gp))(\/product\/|\/)([A-Z0-9]+)/ ) ;
2021-08-12 19:44:58 +00:00
if ( amazonID ) {
const shortcode = ` a/ ${ amazonID [ 5 ] } `
2021-08-12 19:23:49 +00:00
return {
url : ` ${ config . getBaseUrl ( ) } / ${ shortcode } ` ,
shortcode ,
target
}
}
2021-08-14 13:47:03 +00:00
const ebayID = target . match ( /(?:[ebay]*(?:[\/]|[itm=])|^)([0-9]{9,12})/ ) ;
if ( ebayID ) {
const shortcode = ` e/ ${ ebayID [ 1 ] } `
return {
url : ` ${ config . getBaseUrl ( ) } / ${ shortcode } ` ,
shortcode ,
target
}
}
2022-01-12 16:51:21 +00:00
const redditID = target . match ( /(((((?:https?:)?\/\/)((?!about\.)[\w-]+?\.)?([rc]edd(?:it\.com|\.it)))(?!\/(?:blog|about|code|advertising|jobs|rules|wiki|contact|buttons|gold|page|help|prefs|user|message|widget)\b)((?:\/r\/[\w-]+\b(?<!\/pcmasterrace))|(?:\/tb))?(\/comments)??(\/\w{2,7}\b(?<!\/46ijrl)(?<!\/wiki))((?:(?!\))\S)*)))/ ) ;
2022-01-12 17:19:15 +00:00
if ( redditID ) {
2022-01-12 16:51:21 +00:00
const shortcode = ` r ${ redditID [ 9 ] } `
return {
url : ` ${ config . getBaseUrl ( ) } / ${ shortcode } ` ,
shortcode ,
target
}
}
2021-08-12 19:23:49 +00:00
return null ;
2021-08-12 19:23:40 +00:00
}
2021-08-12 17:10:36 +00:00
2021-08-14 08:09:05 +00:00
async function validate ( username , password , req , reply ) {
2021-08-14 08:15:59 +00:00
if ( ! username || ! password ) {
2021-08-14 08:09:05 +00:00
return new Error ( 'Sorry only authorized users can do that.' )
}
2021-08-14 08:15:59 +00:00
2021-08-14 08:26:44 +00:00
const user = await knex . select ( 'username' , 'password' )
2021-08-14 08:15:59 +00:00
. from ( 'users' )
2021-08-14 08:26:44 +00:00
. where ( 'username' , '=' , username )
2021-08-14 08:15:59 +00:00
. limit ( 1 ) ;
if ( user . length == 0 ) {
return new Error ( 'Sorry m8, looks like you are not on the inivtation list' ) ;
}
2021-08-14 08:26:44 +00:00
if ( ! ( await argon2 . verify ( user [ 0 ] . password , password ) ) ) {
2021-08-14 08:15:59 +00:00
return new Error ( 'Wrong credentials' ) ;
}
2021-08-18 13:57:23 +00:00
req . user = username ;
2021-08-14 08:09:05 +00:00
}
2021-09-25 16:45:52 +00:00
const bot _html = ` <!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta http - equiv = "X-UA-Compatible" content = "IE=edge" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< meta property = "og:title" content = "LinkyLinky" >
< meta property = "og:site_name" content = "LinkyLinky by Kauft.es" >
< meta property = "og:url" content = "https://kauft.es/" >
< meta property = "og:description" content = "LinkyLinky by Kauft.es is a custom url shortener. You're reading this, b/c someone doesn't want their shorturl to be indexed by bots/crawlers/spiders." >
< meta property = "og:type" content = "article" >
< meta property = "og:image" content = "https://kauft.es/dashboard/icon_128.png" >
< title > LinkyLinky < / t i t l e >
< / h e a d >
< body >
< p align = "center" >
< img height = "150" src = "https://kauft.es/dashboard/icon_128.png" >
< h1 align = "center" > LinkyLinky 🔗 < / h 1 >
< h3 align = "center" > A small url shortener , originaly developed for kauft . es < / h 3 >
< p > LinkyLinky by Kauft . es is a custom url shortener . < br >
You 're reading this, b/c someone doesn' t want their shorturl to be indexed by bots / crawlers / spiders . < / p >
< / p >
< / b o d y >
< / h t m l >
` ;
const clientside _html = ` <!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< meta http - equiv = "X-UA-Compatible" content = "ie=edge" >
< meta name = "robot" content = "no-index" >
< / h e a d >
< body >
< style >
body {
background : black ;
overflow : hidden ;
}
. containCube {
position : relative ;
height : 100 vh ;
width : 100 % ;
perspective : 800 px ;
}
. containCube . cube {
position : absolute ;
height : 300 px ;
width : 300 px ;
top : 0 ;
right : 0 ;
bottom : 0 ;
left : 0 ;
margin : auto ;
box - sizing : border - box ;
transform - style : preserve - 3 d ;
transform - origin : 50 % 50 % ;
- webkit - animation : rotate 20 s ease - in - out infinite alternate ;
animation : rotate 20 s ease - in - out infinite alternate ;
}
. containCube . cubeGroup {
position : absolute ;
display : grid ;
box - sizing : border - box ;
height : 100 % ;
width : 100 % ;
color : white ;
text - shadow : 0 0 1 px black ;
border : 3 px dashed white ;
}
. containCube . cubeGroup h1 {
margin : auto ;
}
. containCube . cube - front {
transform : translatez ( 150 px ) ;
}
. containCube . cube - rear {
transform : translatez ( - 150 px ) rotatey ( 180 deg ) ;
}
. containCube . cube - right {
transform - origin : 100 % ;
transform : rotatey ( 90 deg ) translatex ( 150 px ) ;
}
. containCube . cube - left {
transform - origin : 0 % ;
transform : rotatey ( - 90 deg ) translatex ( - 150 px ) ;
}
. containCube . cube - bottom {
transform - origin : 50 % 100 % ;
transform : rotatex ( - 90 deg ) translatey ( 150 px ) ;
}
. containCube . cube - top {
transform - origin : 50 % 0 % ;
transform : rotatex ( 90 deg ) translatey ( - 150 px ) ;
}
. containCube . cube - 1 {
background : red ;
}
. containCube . cube - 2 {
background : red ;
}
. containCube . cube - 3 {
background : red ;
}
. containCube . cube - 4 {
background : red ;
}
. containCube . cube - 5 {
background : red ;
}
. containCube . cube - 6 {
background : red ;
}
@ - webkit - keyframes rotate {
10 % {
transform : rotate3d ( 1 , 1 , 0 , 320 deg ) ;
}
20 % {
transform : rotate3d ( 1 , 0 , 0 , - 90 deg ) ;
}
30 % {
transform : rotate3d ( 1 , 1 , 0 , 440 deg ) ;
}
40 % {
transform : rotate3d ( 1 , 0 , 0 , - 180 deg ) ;
}
50 % {
transform : rotate3d ( 1 , 1 , 0 , 460 deg ) ;
}
60 % {
transform : rotate3d ( 0 , 1 , 0 , - 195 deg ) ;
}
70 % {
transform : rotate3d ( 1 , 1 , 0 , 172 deg ) ;
}
80 % {
transform : rotate3d ( 0 , 1 , 0 , - 360 deg ) ;
}
90 % {
transform : rotate3d ( 1 , 1 , 0 , 280 deg ) ;
}
}
@ keyframes rotate {
10 % {
transform : rotate3d ( 1 , 1 , 0 , 320 deg ) ;
}
20 % {
transform : rotate3d ( 1 , 0 , 0 , - 90 deg ) ;
}
30 % {
transform : rotate3d ( 1 , 1 , 0 , 440 deg ) ;
}
40 % {
transform : rotate3d ( 1 , 0 , 0 , - 180 deg ) ;
}
50 % {
transform : rotate3d ( 1 , 1 , 0 , 460 deg ) ;
}
60 % {
transform : rotate3d ( 0 , 1 , 0 , - 195 deg ) ;
}
70 % {
transform : rotate3d ( 1 , 1 , 0 , 172 deg ) ;
}
80 % {
transform : rotate3d ( 0 , 1 , 0 , - 360 deg ) ;
}
90 % {
transform : rotate3d ( 1 , 1 , 0 , 280 deg ) ;
}
}
@ - webkit - keyframes rotateZed {
20 % {
transform : translatez ( 100 px ) ;
}
40 % {
transform : translatez ( - 100 px ) ;
}
60 % {
transform : translatez ( 100 px ) ;
}
80 % {
transform : translatez ( - 100 px ) ;
}
}
@ keyframes rotateZed {
20 % {
transform : translatez ( 100 px ) ;
}
40 % {
transform : translatez ( - 100 px ) ;
}
60 % {
transform : translatez ( 100 px ) ;
}
80 % {
transform : translatez ( - 100 px ) ;
}
}
< / s t y l e >
< div class = "containCube" >
< div class = "cube" >
< div class = "cubeGroup cube-front cube-1" >
< h1 > kauft . es < / h 1 >
< / d i v >
< div class = "cubeGroup cube-top cube-2" >
< h1 > kauft . es < / h 1 >
< / d i v >
< div class = "cubeGroup cube-left cube-3" >
< h1 > kauft . es < / h 1 >
< / d i v >
< div class = "cubeGroup cube-right cube-4" >
< h1 > kauft . es < / h 1 >
< / d i v >
< div class = "cubeGroup cube-rear cube-5" >
< h1 > kauft . es < / h 1 >
< / d i v >
< div class = "cubeGroup cube-bottom cube-6" >
< h1 > kauft . es < / h 1 >
< / d i v >
< / d i v >
< / d i v >
< script >
setTimeout ( function ( ) {
location . replace ( "{{targeturl}}" ) ;
} , 3000 ) ; //Delay 3 seconds
< / s c r i p t >
< / b o d y >
< / h t m l >
` ;
2021-08-12 17:10:36 +00:00
// Run the server!
const start = async ( ) => {
2021-08-14 07:56:43 +00:00
try {
await fastify . listen ( 3000 , '0.0.0.0' )
2021-08-12 17:22:14 +00:00
} catch ( err ) {
fastify . log . error ( err )
process . exit ( 1 )
}
2021-08-12 17:10:36 +00:00
}
start ( )