diff --git a/app/schemas/models/mail_sent.coffee b/app/schemas/models/mail_sent.coffee new file mode 100644 index 000000000..4236ad94b --- /dev/null +++ b/app/schemas/models/mail_sent.coffee @@ -0,0 +1,16 @@ +c = require './../schemas' +#This will represent transactional emails which have been sent + +MailSentSchema = c.object { + title: 'Sent mail' + description: 'Emails which have been sent through the system' +} +_.extend MailSentSchema.properties, + mailTask: c.objectId {} + user: c.objectId links: [{rel: 'extra', href: '/db/user/{($)}'}] + sent: c.date title: 'Sent', readOnly: true + +c.extendBasicProperties MailSentSchema, 'mail.sent' + +module.exports = MailSentSchema + \ No newline at end of file diff --git a/app/schemas/models/mail_task.coffee b/app/schemas/models/mail_task.coffee new file mode 100644 index 000000000..ac450e0ee --- /dev/null +++ b/app/schemas/models/mail_task.coffee @@ -0,0 +1,21 @@ +c = require './../schemas' +#This will represent transactional emails which have been sent + +MailTaskSchema = c.object { + title: 'Mail task' + description: 'Mail tasks to call at certain intervals' +} +_.extend MailTaskSchema.properties, + url: + title: 'URL' + description: 'The associated URL of the mailtask to call' + type: 'string' + frequency: + title: 'Frequency' + description: 'The number of seconds the servers should check whether or not to send the email' + type: 'integer' + +c.extendBasicProperties MailTaskSchema, 'mail.task' + +module.exports = MailTaskSchema + \ No newline at end of file diff --git a/server/commons/LockManager.coffee b/server/commons/LockManager.coffee new file mode 100644 index 000000000..83dd43efc --- /dev/null +++ b/server/commons/LockManager.coffee @@ -0,0 +1,24 @@ +config = require '../../server_config' +redis = require 'redis' +class LockManager + constructor: -> + unless config.isProduction or config.redis.host isnt "localhost" + throw "You shouldn't be instantiating distributed locks unless in production." + @redisClient = redis.createClient config.redis.port, config.redis.host + @lockValues = {} + @unlockScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end" + + setLock: (lockName, timeoutMs, cb) => + randomNumber = Math.floor(Math.random() * 1000000000) + @redisClient.set [lockName,randomNumber, "NX", "PX", timeoutMs], (err, res) -> + if err? then return cb err, null + @lockValues[lockName] = randomNumber + cb null, res + + releaseLock: (lockName, cb) => + @redisClient.eval [@unlockScript, 1, lockName, @lockValues[lockName]], (err, res) -> + if err? then return cb err, null + #1 represents success, 0 failure + cb null, Boolean(Number(res)) + +module.exports = new RedisLock() \ No newline at end of file diff --git a/server/commons/mapping.coffee b/server/commons/mapping.coffee index 802b33790..faa0ef551 100644 --- a/server/commons/mapping.coffee +++ b/server/commons/mapping.coffee @@ -9,6 +9,8 @@ module.exports.handlers = 'thang_type': 'levels/thangs/thang_type_handler' 'user': 'users/user_handler' 'user_remark': 'users/remarks/user_remark_handler' + 'mail_task': 'mail/tasks/mail_task_handler' + 'mail_sent': 'mail/sent/mail_sent_handler' 'achievement': 'achievements/achievement_handler' 'earned_achievement': 'achievements/earned_achievement_handler' diff --git a/server/mail/sent/MailSent.coffee b/server/mail/sent/MailSent.coffee new file mode 100644 index 000000000..7f59dd5b0 --- /dev/null +++ b/server/mail/sent/MailSent.coffee @@ -0,0 +1,11 @@ +mongoose = require 'mongoose' +plugins = require '../../plugins/plugins' +jsonschema = require '../../../app/schemas/models/mail_sent' + +MailSent = new mongoose.Schema({ + sent: + type: Date + 'default': Date.now +}, {strict: false}) + +module.exports = MailSent = mongoose.model('mailSent', MailSent) diff --git a/server/mail/sent/mail_sent_handler.coffee b/server/mail/sent/mail_sent_handler.coffee new file mode 100644 index 000000000..35e920c2b --- /dev/null +++ b/server/mail/sent/mail_sent_handler.coffee @@ -0,0 +1,12 @@ +MailSent = require './MailSent' +Handler = require '../../commons/Handler' + +class MailSentHandler extends Handler + modelClass: MailSent + editableProperties: ['mailTask','user','sent'] + jsonSchema: require '../../../app/schemas/models/mail_sent' + + hasAccess: (req) -> + req.user?.isAdmin() + +module.exports = new MailSentHandler() diff --git a/server/mail/tasks/MailTask.coffee b/server/mail/tasks/MailTask.coffee new file mode 100644 index 000000000..10a536787 --- /dev/null +++ b/server/mail/tasks/MailTask.coffee @@ -0,0 +1,7 @@ +mongoose = require 'mongoose' +plugins = require '../../plugins/plugins' +jsonschema = require '../../../app/schemas/models/mail_task' + +MailTaskSchema = new mongoose.Schema({}, {strict: false}) + +module.exports = MailTask = mongoose.model('mail.task', MailTaskSchema) diff --git a/server/mail/tasks/mail_task_handler.coffee b/server/mail/tasks/mail_task_handler.coffee new file mode 100644 index 000000000..12b6be53a --- /dev/null +++ b/server/mail/tasks/mail_task_handler.coffee @@ -0,0 +1,12 @@ +MailTask = require './MailTask' +Handler = require '../../commons/Handler' + +class MailTaskHandler extends Handler + modelClass: MailTask + editableProperties: ['url','frequency'] + jsonSchema: require '../../../app/schemas/models/mail_task' + + hasAccess: (req) -> + req.user?.isAdmin() + +module.exports = new MailTaskHandler() diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index 4fdcef128..ae8b62e86 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -1,4 +1,5 @@ mail = require '../commons/mail' +MailTask = require '../mail/tasks/MailTask' User = require '../users/User' errors = require '../commons/errors' config = require '../../server_config' @@ -6,11 +7,16 @@ LevelSession = require '../levels/sessions/LevelSession' Level = require '../levels/Level' log = require 'winston' sendwithus = require '../sendwithus' - +if config.isProduction || true + redis = require 'redis' + redisClient = redis.createClient(config.redis.port,config.redis.host) module.exports.setup = (app) -> app.all config.mail.mailchimpWebhook, handleMailchimpWebHook app.get '/mail/cron/ladder-update', handleLadderUpdate + app.post '/mail/task', createMailTask + + #setInterval(handleScheduledMail, 5000) DEBUGGING = false @@ -28,6 +34,18 @@ isRequestFromDesignatedCronHandler = (req, res) -> return false return true +createMailTask = (req, res) -> + #unless req.user?.isAdmin() then return errors.forbidden(res) + unless req.body.url and req.body.frequency then return errors.badInput(res) + console.log "Creating mail task with url #{req.body.url} and frequency #{req.body.frequency}" + newMailTask = new MailTask {} + newMailTask.set("url",req.body.url) + newMailTask.set("frequency",req.body.frequency) + newMailTask.save (err) -> + if err? then return errors.serverError(res, err) + res.send("Created mail task!") + res.end() + handleLadderUpdate = (req, res) -> log.info('Going to see about sending ladder update emails.') requestIsFromDesignatedCronHandler = isRequestFromDesignatedCronHandler req, res diff --git a/server_config.coffee b/server_config.coffee index 6018bf84f..13fc4e7d1 100644 --- a/server_config.coffee +++ b/server_config.coffee @@ -12,6 +12,10 @@ config.mongo = host: process.env.COCO_MONGO_HOST or 'localhost' db: process.env.COCO_MONGO_DATABASE_NAME or 'coco' mongoose_replica_string: process.env.COCO_MONGO_MONGOOSE_REPLICA_STRING or '' + +config.redis = + port: process.env.COCO_REDIS_PORT or 6379 + host: process.env.COCO_REDIS_HOST or 'localhost' if config.unittest config.port += 1