2021-12-29 06:52:17 -05:00
const express = require ( 'express' )
2022-01-06 04:09:35 -05:00
const nodemailer = require ( 'nodemailer' )
2021-12-29 12:05:25 -05:00
const bodyParser = require ( 'body-parser' )
2021-12-29 06:52:17 -05:00
const path = require ( 'path' )
2022-01-05 13:12:28 -05:00
const { URLSafeTimedSerializer } = require ( "@constemi/itsdangerjs" )
2021-12-29 06:52:17 -05:00
2021-12-29 06:35:51 -05:00
require ( 'dotenv' ) . config ( )
2021-12-29 09:15:21 -05:00
// set up debug
2021-12-29 06:35:51 -05:00
let DEBUG
if ( process . env . DEBUG || process . env . CROWDFUNDING _SITE _DEBUG ) {
DEBUG = true
} else {
DEBUG = false
}
2023-03-12 04:39:23 -04:00
if ( DEBUG ) console . log ( 'Loading website in debug mode' )
2021-12-29 06:35:51 -05:00
2022-01-05 13:12:28 -05:00
// set up secret key
let secretKey
if ( process . env . CROWDFUNDING _SITE _SECRET _KEY ) {
secretKey = process . env . CROWDFUNDING _SITE _SECRET _KEY
} else {
if ( DEBUG ) {
secretKey = 'NotReallyASecret'
console . warn ( "Secret key is not set! We're falling back to the default one which is INSECURE and NOT TO BE USED IN PRODUCTION" )
} else {
console . error ( 'No secret key is set. You cannot run this in production without setting a secret key because that would be very insecure. Sorry.' )
process . exit ( )
}
}
2023-03-12 04:39:23 -04:00
// helper function to make the serialiser
function makeSerialiser ( value ) {
return URLSafeTimedSerializer ( secretKey , { salt : value } )
}
2022-01-06 04:09:35 -05:00
// set up nodemailer (if configured)
let mailer
let emailFrom
if (
! ! process . env . CROWDFUNDING _SITE _EMAIL _HOST &&
! ! process . env . CROWDFUNDING _SITE _EMAIL _USER &&
! ! process . env . CROWDFUNDING _SITE _EMAIL _PASSWORD
) {
mailer = nodemailer . createTransport ( {
host : process . env . CROWDFUNDING _SITE _EMAIL _HOST ,
port : process . env . CROWDFUNDING _SITE _EMAIL _PORT || 587 ,
secure : process . env . CROWDFUNDING _SITE _EMAIL _SECURE == 'false' ? false : true ,
auth : {
user : process . env . CROWDFUNDING _SITE _EMAIL _USER ,
pass : process . env . CROWDFUNDING _SITE _EMAIL _PASSWORD ,
}
} )
if ( ! ! process . env . CROWDFUNDING _SITE _EMAIL _FROM ) {
emailFrom = process . env . CROWDFUNDING _SITE _EMAIL _FROM
} else {
emailFrom = process . env . CROWDFUNDING _SITE _EMAIL _USER
console . warn ( ` No "From" address set! Using ${ emailFrom } as a default. ` )
}
} else {
console . log ( "Email has not been configured! Some features won't work properly until this is set up :/" )
}
async function sendMail ( message ) {
if ( DEBUG || ! mailer ) {
console . log ( ` \n \n -------------------------- BEGIN EMAIL -------------------------- \n ` )
console . log ( ` From: ${ message . from } \n To: ${ message . to } \n Subject: ${ message . subject || '(no subject)' } \n \n ${ message . text } ` )
console . log ( ` \n --------------------------- END EMAIL --------------------------- ` )
return
} else {
let receipt = await mailer . sendMail ( {
from : message . from || emailFrom ,
to : message . to ,
subject : message . subject ,
text : message . text ,
html : message . html ,
} )
return receipt
}
}
2021-12-29 09:15:21 -05:00
// get goal details
const goalPeople = Number ( process . env . CROWDFUNDING _SITE _GOAL _PEOPLE ) || 750
const goalRupees = Number ( process . env . CROWDFUNDING _SITE _GOAL _RUPEES ) || 500000
2021-12-29 06:35:51 -05:00
// set up knex (for database)
const knex = require ( 'knex' ) ( {
client : 'sqlite3' ,
connection : {
filename : './donors.sqlite'
} ,
useNullAsDefault : true
} )
2022-01-05 08:51:10 -05:00
// schema to save pledges
let pledgeSchema = function ( t ) {
t . increments ( 'id' ) . primary ( )
t . timestamp ( 'created_at' ) . defaultTo ( knex . fn . now ( ) ) . notNullable ( )
t . boolean ( 'was_robot' ) . defaultTo ( true )
t . integer ( 'amount' ) . notNullable ( )
t . boolean ( 'overseas' )
t . string ( 'name' , 128 )
t . boolean ( 'anonymous' )
t . string ( 'email' , 128 )
t . string ( 'phone' , 32 )
t . integer ( 'retry_times' ) . defaultTo ( 10 )
t . boolean ( 'get_newsletter' ) . defaultTo ( false )
t . text ( 'other_message' )
2022-02-20 05:16:01 -05:00
t . string ( 'referral_code' , 256 )
t . string ( 'referrer' , 128 )
}
// function to make sure referral columns exist
// we need this because they were added later in the code, so
// if there are old databases they'd need to be updated.
function ensureReferralColumns ( table ) {
knex . schema . hasColumn ( table , 'referral_code' ) . then ( function ( exists ) {
if ( ! exists ) {
console . debug ( ` No referral_code column in ${ table } ! Adding now... ` )
knex . schema . alterTable ( table , t => {
t . string ( 'referral_code' , 256 )
} ) . then ( r => console . debug ( ` referral_code column added to ${ table } : ` , r ) )
}
} )
knex . schema . hasColumn ( table , 'referrer' ) . then ( function ( exists ) {
if ( ! exists ) {
console . debug ( ` No referrer column in ${ table } ! Adding it now... ` )
knex . schema . alterTable ( table , t => {
t . string ( 'referrer' , 128 )
} ) . then ( r => console . debug ( ` referrer column added to ${ table } : ` , r ) )
}
} )
2022-01-05 08:51:10 -05:00
}
// make sure pledges table exists
2021-12-29 06:35:51 -05:00
knex . schema . hasTable ( 'pledges' ) . then ( function ( exists ) {
if ( ! exists ) {
2022-01-05 08:51:10 -05:00
if ( DEBUG ) console . debug ( 'No pledge table exists! Creating one now...' )
return knex . schema . createTable ( 'pledges' , pledgeSchema )
2022-02-20 05:16:01 -05:00
} else {
ensureReferralColumns ( 'pledges' )
2022-01-05 08:51:10 -05:00
}
} )
// make sure unverified pledge table exists
knex . schema . hasTable ( 'unverified_pledges' ) . then ( function ( exists ) {
if ( ! exists ) {
if ( DEBUG ) console . debug ( 'No unverified pledge table exists! Creating one now...' )
return knex . schema . createTable ( 'unverified_pledges' , pledgeSchema )
2022-02-20 05:16:01 -05:00
} else {
ensureReferralColumns ( 'unverified_pledges' )
2021-12-29 06:35:51 -05:00
}
} )
// set up bookshelf (for easy interface for database)
bookshelf = require ( 'bookshelf' ) ( knex )
const Pledge = bookshelf . model ( 'Pledge' , {
tableName : 'pledges' ,
} )
2022-01-05 08:51:10 -05:00
const UnverifiedPledge = bookshelf . model ( 'UnverifiedPledge' , {
tableName : 'unverified_pledges' ,
} )
2021-12-29 06:52:17 -05:00
// decide base url and port for app (can be configured)
const baseUrl = process . env . CROWDFUNDING _SITE _BASE _URL || '/'
const port = process . env . CROWDFUNDING _SITE _PORT || 5000
2021-12-29 09:15:21 -05:00
// set up twing
const { TwingEnvironment , TwingLoaderFilesystem } = require ( 'twing' )
2022-01-06 05:37:14 -05:00
let loader = new TwingLoaderFilesystem ( [
path . resolve ( _ _dirname , '..' , 'src' ) ,
] )
2021-12-29 09:15:21 -05:00
let twing = new TwingEnvironment ( loader )
2021-12-29 06:52:17 -05:00
// set up express
const app = express ( )
const router = express . Router ( )
app . use ( baseUrl , router )
2021-12-29 12:05:25 -05:00
router . use ( bodyParser . urlencoded ( {
extended : true ,
} ) )
2021-12-29 06:52:17 -05:00
// main views
2022-02-20 05:20:06 -05:00
// the home view is used in a couple of places, so we've moved it out
let homeView = async ( req , res ) => {
2021-12-29 06:52:17 -05:00
if ( DEBUG ) console . debug ( 'Returning home page' )
2021-12-29 09:15:21 -05:00
// count people
2022-01-11 02:18:28 -05:00
let { total _people , total _rupees } = ( await knex ( 'pledges' )
. sum ( 'amount as total_rupees' )
. count ( '* as total_people' ) ) [ 0 ]
2021-12-29 09:15:21 -05:00
2022-01-11 03:40:14 -05:00
// get latest pledges
let recentPledges = (
await Pledge
. forge ( )
. orderBy ( 'created_at' , 'DESC' )
. fetchPage ( { limit : 5 } )
) . models
if ( DEBUG ) console . log ( ` Listing ${ recentPledges . length } pledges ` )
2022-02-20 05:20:06 -05:00
if ( DEBUG && req . params . referralCode ) console . log ( ` Referral code: ${ req . params . referralCode } ` )
let referrer
if ( req . params . referralCode ) {
try {
let pledge = await ( Pledge
. query ( {
where : {
referral _code : req . params . referralCode ,
}
} )
. fetch ( ) )
referrer = pledge . get ( 'anonymous' ) ? 'Anonymous' : pledge . get ( 'name' )
} catch ( e ) {
if ( DEBUG ) console . debug ( ` Skipping referral ${ req . params . referralCode } because not found: ${ e } ` )
2022-02-20 05:26:38 -05:00
// if the referrer doesn't exist, send people to the normal homepage!
res . redirect ( '/' )
return
2022-02-20 05:20:06 -05:00
}
}
2022-01-11 03:40:14 -05:00
2022-01-06 05:37:14 -05:00
twing . render ( 'index.htm.twig' , {
2021-12-29 09:15:21 -05:00
'goal_rupees' : Number ( goalRupees ) . toLocaleString ( 'en-IN' ) ,
'goal_people' : Number ( goalPeople ) . toLocaleString ( 'en-IN' ) ,
'progress_rupees' : Number ( total _rupees ) . toLocaleString ( 'en-IN' ) ,
'progress_people' : Number ( total _people ) . toLocaleString ( 'en-IN' ) ,
'percent_rupees' : ` style="width: ${ total _rupees / goalRupees * 100 } %" ` ,
'percent_people' : ` style="width: ${ total _people / goalPeople * 100 } %" ` ,
2022-01-11 03:40:14 -05:00
'recent_pledges' : recentPledges ,
2022-02-20 05:20:06 -05:00
'referral_code' : req . params . referralCode || null , // if it's there
'referrer' : referrer ,
2021-12-29 09:15:21 -05:00
} ) . then ( ( output ) => {
res . end ( output )
} )
2022-02-20 05:20:06 -05:00
}
router . get ( '/' , homeView )
router . get ( '/r/:referralCode' , homeView )
// helpers to generate and decode referrals
function randomCode ( ) {
let chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
let code = ''
for ( let i = 0 ; i < 10 ; i ++ ) {
code += chars . charAt ( Math . floor ( Math . random ( ) * chars . length ) )
}
return code
}
async function findReferralCode ( email ) {
let referral _code
// fetch existing referral code, if exists
try {
let pledge = await ( Pledge
. query ( {
where : {
email : email ,
}
} )
. fetch ( ) )
referral _code = pledge . get ( 'referral_code' )
if ( DEBUG ) console . log ( ` Using existing referral code: ${ referral _code } ` )
} catch ( e ) {
if ( e . message == 'EmptyResponse' && DEBUG ) {
console . log ( 'No matching referrals found' )
}
}
if ( ! referral _code ) {
// no code found; create a new one and ensure uniqueness
console . log ( ` Generating new referral code for: ${ email } ` )
async function checkUniqueness ( referral _code ) {
let a = await UnverifiedPledge . query ( { where : { referral _code : referral _code } } ) . count ( )
let b = await Pledge . query ( { where : { referral _code : referral _code } } ) . count ( )
return a == 0 && b == 0
}
do {
referral _code = randomCode ( )
} while ( ! checkUniqueness ( referral _code ) )
}
return referral _code
}
async function findReferrer ( code ) {
let email
// first, check for an entry with this referral code
try {
let pledge = await ( Pledge
. query ( {
where : {
referral _code : code ,
}
} )
. fetch ( ) )
email = pledge . get ( 'email' )
console . log ( ` Found: ${ email } ` )
} catch ( e ) {
if ( e . message == 'EmptyResponse' && DEBUG ) {
console . log ( 'No matching referrals found' )
} else {
console . error ( e )
}
// next, check for an entry with this email
// (maybe it's already been converted from code to email)
try {
let pledge = await ( Pledge
. query ( {
where : {
email : code ,
}
} )
. fetch ( ) )
email = pledge . get ( 'email' )
console . log ( 'it seems to be' )
} catch ( e ) {
if ( e . message == 'EmptyResponse' && DEBUG ) {
console . debug ( 'No matching emails found either' )
} else {
console . error ( e )
}
return null
}
}
return email
}
2021-12-29 06:52:17 -05:00
2022-01-05 08:40:01 -05:00
// function to validate pledges before saving
2022-02-20 05:20:06 -05:00
async function validatePledge ( body , PledgeModel = Pledge ) {
2022-01-05 08:40:01 -05:00
// errors get saved here
2021-12-29 12:05:25 -05:00
let errors = [ ]
2022-01-05 09:07:21 -05:00
let was _robot = body . was _robot
if ( was _robot != 'no' ) {
2022-01-06 07:50:50 -05:00
errors . push ( 'Only humans are allowed to donate money. Robots are too digital 🤖' )
2021-12-29 12:05:25 -05:00
}
2022-01-05 08:40:01 -05:00
let amount = body . amount
2021-12-29 12:05:25 -05:00
if ( ! amount || amount == 'custom' ) {
2022-01-05 08:40:01 -05:00
amount = body [ 'amount-custom' ]
2021-12-29 12:05:25 -05:00
}
if ( ! amount || amount <= 0 ) {
errors . push ( 'Pledge amount too small. Please choose at least a rupee!' )
}
try {
amount = Number ( amount )
} catch ( err ) {
errors . push ( 'Invalid amount. Please choose a positive number!' )
}
2022-01-05 08:40:01 -05:00
let name = body . name
2021-12-29 12:05:25 -05:00
if ( name . length <= 0 ) {
errors . push ( 'What is your name? You can be anonymous to the world but at least we should know...' )
}
2022-01-05 08:40:01 -05:00
let anonymous = body . anonymous == 'on' ? true : false
2021-12-29 12:05:25 -05:00
2022-01-05 08:40:01 -05:00
let email = body . email
2021-12-29 12:05:25 -05:00
if ( email . length < 5 ) {
errors . push ( 'Please enter a valid email address' )
}
2022-01-05 08:40:01 -05:00
let phone = body . phone
2022-01-05 08:26:13 -05:00
2022-01-05 09:07:21 -05:00
let retry _times
2022-01-05 08:26:13 -05:00
try {
2022-01-05 09:07:21 -05:00
retry _times = body . retry _times
2022-01-05 08:26:13 -05:00
} catch ( err ) {
errors . push ( 'Invalid retry count. Please choose a positive number!' )
}
2022-01-05 09:07:21 -05:00
let get _newsletter = body . get _newsletter == 'yes' ? true : false
2022-01-05 08:40:01 -05:00
let overseas = body . overseas == 'yes' ? true : false
2022-01-05 09:07:21 -05:00
let other _message = body . other _message
2021-12-29 12:05:25 -05:00
2022-02-20 05:20:06 -05:00
// manage referrals
let referral _code = await findReferralCode ( email )
let referrer = null
console . log ( 'ref co' , body . referral _code )
if ( body . referral _code ) {
try {
referrer = await findReferrer ( body . referral _code )
} catch ( e ) {
console . debug ( ` Error loading referrer: ${ e } ` )
}
}
2022-01-05 08:40:01 -05:00
// enter the info
2022-01-05 08:51:10 -05:00
let pledge = new PledgeModel ( ) // may be Pledge or UnverifiedPledge
2022-01-05 09:07:21 -05:00
pledge . set ( 'was_robot' , was _robot )
2021-12-29 12:05:25 -05:00
pledge . set ( 'amount' , amount )
2021-12-29 12:06:40 -05:00
pledge . set ( 'overseas' , overseas )
2021-12-29 12:05:25 -05:00
pledge . set ( 'name' , name )
pledge . set ( 'anonymous' , anonymous )
pledge . set ( 'email' , email )
pledge . set ( 'phone' , phone )
2022-01-05 09:07:21 -05:00
pledge . set ( 'retry_times' , retry _times )
pledge . set ( 'get_newsletter' , get _newsletter )
pledge . set ( 'other_message' , other _message )
2021-12-29 12:05:25 -05:00
2022-02-20 05:20:06 -05:00
pledge . set ( 'referral_code' , referral _code )
pledge . set ( 'referrer' , referrer )
2022-01-05 08:40:01 -05:00
// return it all!
return {
pledge : pledge ,
errors : errors ,
}
}
2021-12-29 12:05:25 -05:00
2022-01-05 08:40:01 -05:00
router . post ( '/pledge' , async ( req , res ) => {
if ( DEBUG ) console . debug ( 'New pledge:' , req . body )
2022-01-05 09:06:13 -05:00
// check that the right submit button was pressed
let submit = req . body . submit
if ( submit != 'Save Pledge' ) {
errors . push ( "This request seems to have been tampered with. Are you sure it wasn't you doing the tampering?" )
}
2022-01-05 08:40:01 -05:00
// process the pledge with our handy function
2022-02-20 05:20:06 -05:00
let { pledge , errors } = await validatePledge ( req . body , UnverifiedPledge )
2022-01-05 08:40:01 -05:00
// fail if there were errors
if ( ! ! errors . length ) {
2022-01-06 07:50:50 -05:00
let output = await twing . render ( 'form-errors.htm.twig' , {
errors : errors ,
} )
2022-01-06 08:48:05 -05:00
res . send ( output )
2022-01-06 07:50:50 -05:00
return
2022-01-05 08:40:01 -05:00
}
// save if there weren't
if ( DEBUG ) console . debug ( ` Saving pledge: ${ JSON . stringify ( pledge ) } ` )
2021-12-29 12:05:25 -05:00
try {
await pledge . save ( )
} catch ( err ) {
2022-01-06 07:50:50 -05:00
let output = await twing . render ( 'error.htm.twig' , {
error : "Sorry, something went wrong while saving your pledge and we don't know what in was. This website is still in beta so we do have a glitches once in a while. Apologies and please try again...🙁" ,
} )
2022-01-06 08:48:05 -05:00
res . send ( output )
2022-01-06 07:50:50 -05:00
return
2021-12-29 12:05:25 -05:00
}
2022-01-05 13:12:28 -05:00
// check for existing pledge
let existingPledge
try {
existingPledge = await ( Pledge
. query ( {
where : {
2022-01-06 04:09:35 -05:00
'email' : pledge . get ( 'email' ) ,
'amount' : pledge . get ( 'amount' ) ,
2022-01-05 13:12:28 -05:00
} ,
} )
. orderBy ( 'created_at' , 'DESC' )
. fetch ( ) )
} catch ( e ) {
2022-01-06 04:09:35 -05:00
if ( e . message == 'EmptyResponse' && DEBUG ) {
2022-01-05 13:12:28 -05:00
console . debug ( 'No existing pledge' )
} else {
console . warn ( ` Weird error happened: ${ e } ` )
}
}
// generate verification link
2023-03-12 04:39:23 -04:00
let serialiser = makeSerialiser ( pledge . get ( 'email' ) )
2022-01-05 13:12:28 -05:00
let verificationLink = ` ${ req . protocol } :// ${ req . hostname } /verify?email= ${ encodeURIComponent ( pledge . get ( 'email' ) ) } &key= ${ encodeURIComponent ( serialiser . dumps ( pledge . get ( 'amount' ) ) ) } `
2022-01-06 04:09:35 -05:00
// send out the email, along with existing pledge deets
2022-01-05 13:12:28 -05:00
console . debug ( ` Verification link generated: ${ verificationLink } ` )
2022-01-06 04:09:35 -05:00
let text = ` Hi ${ pledge . get ( 'name' ) } ,
Thank you so much for your pledge of ₹ $ { Number ( pledge . get ( 'amount' ) ) . toLocaleString ( 'en-IN' ) } to Snipette !
To finish the pledge , please verify your email by clicking the link below :
$ { verificationLink }
If you have any questions , don ' t hesitate to reach out : you can drop a line
anytime to editors @ snipettemag . com .
Thanks ,
The Snipette Team `
2022-01-06 07:50:50 -05:00
// to make things snappier, we don't `await` for the sending to finish.
let receipt = sendMail ( {
2022-01-06 04:09:35 -05:00
to : req . body . email ,
subject : ` Finish ${ existingPledge ? 'updating' : 'saving' } your pledge to Snipette ` ,
text : text ,
} )
2022-01-06 07:50:50 -05:00
// return the success page
twing . render ( 'pledge.htm.twig' , {
'email' : pledge . get ( 'email' ) ,
} ) . then ( ( output ) => {
2022-01-06 08:48:05 -05:00
res . send ( output )
2022-01-06 07:50:50 -05:00
} )
2021-12-29 12:05:25 -05:00
} )
2022-01-05 13:12:28 -05:00
// save pledge after verification complete
router . get ( '/verify' , async ( req , res ) => {
if ( DEBUG ) console . debug ( 'Validating pledge:' , req . query )
// unpack verification link (unless it's expired)
2023-03-12 04:39:23 -04:00
let serialiser = makeSerialiser ( req . query . email )
2022-01-05 13:12:28 -05:00
let amount
try {
amount = serialiser . loads ( req . query . key , 300 ) // number in seconds
} catch ( e ) {
if ( e . name == 'SignatureExpired' ) {
2022-01-06 07:50:50 -05:00
let output = await twing . render ( 'error.htm.twig' , {
error : "Sorry, looks like your link has expired. Please go back and try pledging again. Hopefully you'll manage it quicker this time 😛" ,
} )
2022-01-06 08:48:05 -05:00
res . send ( output )
2022-01-05 13:12:28 -05:00
return
} else {
2022-01-06 07:50:50 -05:00
let output = await twing . render ( 'error.htm.twig' , {
error : "An unknown error occurred. Please generate a new link and try again. Sorry for the inconvenience 🤦" ,
} )
2022-01-06 08:48:05 -05:00
res . send ( output )
2022-01-05 13:12:28 -05:00
return
}
}
// check against database
let pledge
try {
pledge = await ( UnverifiedPledge
. query ( {
where : {
'email' : req . query . email ,
'amount' : amount ,
} ,
} )
. orderBy ( 'created_at' , 'DESC' )
. fetch ( ) )
} catch ( e ) {
if ( e . name == 'EmptyResponse' ) {
2022-01-06 07:50:50 -05:00
let output = await twing . render ( 'error.htm.twig' , {
error : "That pledge was not found in our records. Are you sure you made it? 🔎" ,
} )
2022-01-06 08:48:05 -05:00
res . send ( output )
2022-01-05 13:12:28 -05:00
return
} else {
2022-01-06 07:50:50 -05:00
let output = await twing . render ( 'error.htm.twig' , {
error : "An unknown error occurred. Please generate a new link and try again. Sorry for the inconvenience 🤦" ,
} )
2022-01-06 08:48:05 -05:00
res . send ( output )
2022-01-05 13:12:28 -05:00
return
}
}
// prepare our new pledge
let newPledge = new Pledge ( )
newPledge . set ( 'was_robot' , pledge . get ( 'was_robot' ) )
newPledge . set ( 'amount' , pledge . get ( 'amount' ) )
newPledge . set ( 'overseas' , pledge . get ( 'overseas' ) )
newPledge . set ( 'name' , pledge . get ( 'name' ) )
newPledge . set ( 'anonymous' , pledge . get ( 'anonymous' ) )
newPledge . set ( 'email' , pledge . get ( 'email' ) )
newPledge . set ( 'phone' , pledge . get ( 'phone' ) )
newPledge . set ( 'retry_times' , pledge . get ( 'retry_times' ) )
newPledge . set ( 'get_newsletter' , pledge . get ( 'get_newsletter' ) )
newPledge . set ( 'other_message' , pledge . get ( 'other_message' ) )
2022-02-20 05:20:06 -05:00
newPledge . set ( 'referral_code' , await findReferralCode ( pledge . get ( 'email' ) ) )
// set referrer in priority: old pledge > new pledge
try {
let oldPledge = await ( Pledge
. query ( {
where : {
email : req . query . email ,
}
} )
. fetch ( ) )
newPledge . set ( 'referrer' ,
oldPledge . get ( 'referrer' ) || pledge . get ( 'referrer' ) || null )
} catch ( e ) {
if ( e . message == 'EmptyResponse' && DEBUG ) {
console . log ( 'No matching referrals found' )
} else {
console . error ( e )
}
newPledge . set ( 'referrer' , pledge . get ( 'referrer' ) || null )
}
console . log ( ` Saved referrer: ${ newPledge . get ( 'referrer' ) } ` )
2022-01-05 13:12:28 -05:00
// destroy previous pledges by that user
try {
await ( Pledge
. query ( {
where : {
email : req . query . email ,
}
} )
. destroy ( ) )
} catch ( e ) {
if ( e . message != 'No Rows Deleted' ) throw e
}
// save the new pledge
await newPledge . save ( )
2022-01-06 04:09:35 -05:00
// send the confirmation email
2022-02-20 05:20:06 -05:00
let referralLink = ` ${ req . protocol } :// ${ req . hostname } /?ref= ${ newPledge . get ( 'referral_code' ) } `
2022-01-06 04:09:35 -05:00
let text = ` Hi ${ pledge . get ( 'name' ) } ,
Your pledge of ₹ $ { Number ( pledge . get ( 'amount' ) ) . toLocaleString ( 'en-IN' ) } to Snipette Analog has been saved !
Thank you so much for your support . Once we hit our goals , we will contact
you to make the final payment . Meanwhile , here are the details of your
pledge for reference :
Name : $ { pledge . get ( 'name' ) }
Pledge amount : $ { Number ( pledge . get ( 'amount' ) ) . toLocaleString ( 'en-IN' ) } $ { pledge . overseas ? ' (plus $25 for shipping)' : '' }
Email : $ { pledge . get ( 'email' ) }
Phone : $ { pledge . get ( 'phone' ) || 'not provided' }
Extra message :
$ { pledge . get ( 'other_message' ) || '[no message sent]' }
We will attempt to contact you $ { pledge . get ( 'retry_times' ) <= 1 ? 'only once' : String ( pledge . get ( 'retry_times' ) ) + ' times' } before giving up .
You can update your pledge by going to the crowdfunding homepage and saving
a pledge with the same email address as you used before .
If you have any questions , don ' t hesitate to reach out : you can drop a line
anytime to editors @ snipettemag . com .
2022-02-20 05:20:06 -05:00
# # # Share for Stickers !
Money helps , but we need people too — and that 's where you can help! Here' s your
unique referral link :
$ { referralLink }
Get 5 or more of your friends to sign up using that link , and , once we collect
the money and start printing , we ' ll send you a free pack of Snipette stickers !
2022-01-06 04:09:35 -05:00
Thanks ,
The Snipette Team `
2022-01-06 07:50:50 -05:00
// to make things snappier, we don't `await` for the sending to finish
let receipt = sendMail ( {
2022-01-06 04:09:35 -05:00
to : pledge . get ( 'email' ) ,
subject : ` Your pledge has been saved! ` ,
text : text ,
} )
// Send confirmation message
2022-02-20 05:20:06 -05:00
res . redirect ( ` /thanks?ref= ${ newPledge . get ( 'referral_code' ) } ` )
2022-01-06 07:50:50 -05:00
} )
router . get ( '/thanks' , async ( req , res ) => {
2022-02-20 05:26:38 -05:00
// validate referral code
if ( ! ! req . query . ref && ! await findReferrer ( req . query . ref ) ) {
if ( DEBUG ) console . debug ( ` Invalid referral: redirecting to normal page ` )
res . redirect ( '/thanks' )
return
}
2022-02-20 05:20:06 -05:00
let output = await twing . render ( 'thanks.htm.twig' , {
referral _code : req . query . ref ,
} )
2022-01-06 07:50:50 -05:00
2022-01-06 08:48:05 -05:00
res . send ( output )
2022-01-06 07:50:50 -05:00
return
2022-01-05 13:12:28 -05:00
} )
2022-01-11 03:40:14 -05:00
router . get ( '/pledges' , async ( req , res ) => {
if ( DEBUG ) console . debug ( 'Returning pledges list' )
// count people
let { total _people , total _rupees } = ( await knex ( 'pledges' )
. sum ( 'amount as total_rupees' )
. count ( '* as total_people' ) ) [ 0 ]
// get latest pledges
let recentPledges = (
await Pledge
. forge ( )
. orderBy ( 'created_at' , 'DESC' )
2022-01-11 03:53:51 -05:00
. fetchAll ( )
2022-01-11 03:40:14 -05:00
) . models
if ( DEBUG ) console . log ( ` Listing ${ recentPledges . length } pledges ` )
twing . render ( 'pledges.htm.twig' , {
'recent_pledges' : recentPledges ,
} ) . then ( ( output ) => {
2022-01-11 03:44:52 -05:00
res . send ( output )
2022-01-11 03:40:14 -05:00
} )
} )
2023-03-12 04:41:32 -04:00
router . get ( '/back-out' , async ( req , res ) => {
if ( DEBUG ) console . debug ( 'Backing out:' , req . query )
// unpack verification link (unless it's expired)
let serialiser = makeSerialiser ( req . query . email )
let staySubscribed
try {
// we allow them a month to do this
staySubscribed = serialiser . loads ( req . query . key , 60 * 60 * 24 * 30 )
// clean up the variable
if ( staySubscribed == 'yes' || staySubscribed == 'true' ) {
staySubscribed = true
} else {
staySubscribed = false
}
} catch ( e ) {
if ( e . name == 'SignatureExpired' ) {
let output = await twing . render ( 'error.htm.twig' , {
error : "It looks like you've clicked on the link of a very old email! Nothing lasts forever, including verification links, so if you're having trouble please contact <a href='mailto:editors@snipettemag.com'>editors@snipettemag.com</a>. Sorry for the inconvenience 🙁" ,
} )
} else {
let output = await twing . render ( 'error.htm.twig' , {
error : "An unknown error occurred, and our robots can't figure out what it is. Please contact <a href='mailto:editors@snipettemag.com'>editors@snipettemag.com</a> for help. Sorry for the inconvenience 🤦" ,
} )
}
res . send ( output )
return
}
// now that we've got past that check...
let pledge
try {
pledge = await ( Pledge
. query ( {
where : {
'email' : req . query . email ,
} ,
} )
. orderBy ( 'created_at' , 'DESC' )
. fetch ( ) )
} catch ( e ) {
if ( e . name == 'EmptyResponse' ) {
let output = await twing . render ( 'error.htm.twig' , {
error : "That pledge was not found in our records. Are you sure you made it? 🔎" ,
} )
res . send ( output )
return
} else {
let output = await twing . render ( 'error.htm.twig' , {
error : "An unknown error occurred, and we can't figure out what it is. Please contact <a href='mailto:editors@snipettemag.com'>editors@snipettemag.com</a> for help (we're guessing you're trying to unsubscribe, in which case we'll handle it immediately upon receiving your email). Sorry for the inconvenience 🤦" ,
} )
res . send ( output )
return
}
}
// update settings for our new pledge
pledge . set ( 'backed_out' , true )
pledge . set ( 'unsubscribed' , ! staySubscribed )
// save the new pledge
await pledge . save ( )
// send the confirmation email
let text = ` Hi ${ pledge . get ( 'name' ) } ,
We have received your back - out request for your pledge of $ { Number ( pledge . get ( 'amount' ) ) . toLocaleString ( 'en-IN' ) } $ { pledge . get ( 'overseas' ) ? ' (plus $25 for shipping)' : '' } . We have removed you from our list and you will receive no further emails regarding collecting your contribution . $ { staySubscribed ? ' However, you will still receive general crowdfunding updates and are welcome to join back again later.' : '' }
If you think this was a mistake , please reply to this email and one of us will get back to you .
We realise that some people may want to continue supporting Snipette even if it doesn ' t make sense for them to go with the original pledge . You are always welcome to subscribe directly at : https : //snipettemag.com/subscribe/
Thank you for your support so far , and apologies for the fact that it didn ' t work out quite as expected this time .
Thanks ,
The Snipette Team `
// to make things snappier, we won't `await` for the sending to finish
let receipt = sendMail ( {
to : pledge . get ( 'email' ) ,
subject : 'Pledge withdrawn from Snipette crowdfunding' ,
text : text ,
} )
// Send confirmation message
res . redirect ( '/bye' )
} )
router . get ( '/bye' , async ( req , res ) => {
let output = await twing . render ( 'bye.htm.twig' , { } )
res . send ( output )
return
} )
2022-01-06 05:37:14 -05:00
router . use ( express . static ( 'src/assets' ) )
2021-12-29 12:05:25 -05:00
2021-12-29 06:52:17 -05:00
// start the listener!
2023-03-12 04:41:32 -04:00
if ( ! module . parent ) {
app . listen ( port , ( ) => {
console . log ( ` Server is up at port ${ port } ` )
} )
}
2021-12-29 06:52:17 -05:00
// end note: in case we want to import this somewhere for testing
2021-12-29 06:35:51 -05:00
module . exports = {
knex ,
bookshelf ,
Pledge ,
2022-01-05 13:12:28 -05:00
UnverifiedPledge ,
2021-12-29 06:52:17 -05:00
router ,
2023-03-12 04:39:23 -04:00
makeSerialiser ,
2021-12-29 06:35:51 -05:00
}