diff --git a/app/lib/LevelBus.coffee b/app/lib/LevelBus.coffee index 16a4e9b48..0792e3b44 100644 --- a/app/lib/LevelBus.coffee +++ b/app/lib/LevelBus.coffee @@ -28,12 +28,11 @@ module.exports = class LevelBus extends Bus constructor: -> super(arguments...) @changedSessionProperties = {} - highLoad = false + saveDelay = window.serverConfig?.sessionSaveDelay [wait, maxWait] = switch - when not application.isProduction() then [1, 5] # Save quickly in development. - when not highLoad then [4, 10] # Save slowly when in production. - when not me.isAnonymous() then [10, 30] # Save even more slowly during HoC scaling. - else [20, 60] # Save super slowly if anonymous during HoC scaling. + when not application.isProduction or not saveDelay then [1, 5] # Save quickly in development. + when me.isAnonymous() then [saveDelay.anonymous.min, saveDelay.anonymous.max] + else [saveDelay.registered.min, saveDelay.registered.max] @saveSession = _.debounce @reallySaveSession, wait * 1000, {maxWait: maxWait * 1000} @playerIsIdle = false diff --git a/app/schemas/models/mandate.schema.coffee b/app/schemas/models/mandate.schema.coffee index decb979b8..6614ed1a1 100644 --- a/app/schemas/models/mandate.schema.coffee +++ b/app/schemas/models/mandate.schema.coffee @@ -5,14 +5,39 @@ module.exports = MandateSchema = { additionalProperties: false default: simulationThroughputRatio: 1 - properties: { + sessionSaveDelay: + registered: {min: 4, max: 10} + anonymous: {min: 5, max: 15} + #registered: {min: 10, max: 30} # High load, like during HoC scaling + #anonymous: {min: 20, max: 60} + properties: simulationThroughputRatio: name: 'Simulation Throughput Ratio' description: '0-1 fraction of requests for a match to simulate that should be granted.' type: 'number' minimum: 0 maximum: 1 - } + sessionSaveDelay: + name: 'Session Save Delay' + description: 'How often we save level sessions after code changes--min and max wait in seconds.' + type: 'object' + properties: + registered: + description: 'How often to save for registered players.' + type: 'object' + additionalProperties: false + requiredProperties: ['min', 'max'] + properties: + min: {type: 'number', minimum: 1, exclusiveMinimum: true, format: 'seconds'} + max: {type: 'number', minimum: 5, exclusiveMinimum: true, format: 'seconds'} + anonymous: + description: 'How often to save for anonymous players.' + type: 'object' + additionalProperties: false + requiredProperties: ['min', 'max'] + properties: + min: {type: 'number', minimum: 1, exclusiveMinimum: true, format: 'seconds'} + max: {type: 'number', minimum: 5, exclusiveMinimum: true, format: 'seconds'} } c.extendBasicProperties MandateSchema, 'Mandate' diff --git a/app/styles/core/loading-error.sass b/app/styles/core/loading-error.sass index e9a51fc98..98db1c5c8 100644 --- a/app/styles/core/loading-error.sass +++ b/app/styles/core/loading-error.sass @@ -5,11 +5,14 @@ margin-top: 20px .login-btn - margin-right: 10px + margin-right: 0px + + #create-account-btn + margin-left: 10px #not-found-img max-width: 20% margin: 20px 0 #links-row - margin-top: 50px \ No newline at end of file + margin-top: 50px diff --git a/app/templates/play/level/tome/spell_list_tab_entry.jade b/app/templates/play/level/tome/spell_list_tab_entry.jade index 3b1067d35..d1144f3a0 100644 --- a/app/templates/play/level/tome/spell_list_tab_entry.jade +++ b/app/templates/play/level/tome/spell_list_tab_entry.jade @@ -18,12 +18,12 @@ if includeSpellList .glyphicon.glyphicon-repeat span.spl(data-i18n="play_level.reload") Reload - if levelType !== 'hero' && levelType !== 'hero-ladder' && levelType !== 'hero-coop' + if me.level() >= 15 .btn.btn-small.btn-illustrated.fullscreen-code(title=maximizeShortcutVerbose) .glyphicon.glyphicon-fullscreen .glyphicon.glyphicon-resize-small - if codeLanguage === 'javascript' && levelType !== 'hero' && levelType !== 'hero-ladder' && levelType !== 'hero-coop' + if codeLanguage === 'javascript' && me.level() >= 15 .btn.btn-small.btn-illustrated.beautify-code(title=beautifyShortcutVerbose) .glyphicon.glyphicon-magnet diff --git a/scripts/updateCloseIoLeads.js b/scripts/updateCloseIoLeads.js index db16260a6..5dd3ac9e5 100644 --- a/scripts/updateCloseIoLeads.js +++ b/scripts/updateCloseIoLeads.js @@ -40,15 +40,13 @@ findLeads((err, leads) => { console.error(err); return; } - // TEMP + // // TEMP // for (const email in leads) { - // if (leads[email].coco_schoolName) { - // console.log(email); - // console.log(leads[email]); - // // break; - // } + // console.log(email); + // console.log(leads[email]); + // break; // } - // TEMP + // // TEMP log(`Num leads created ${numLeadsCreated}`); log("Script runtime: " + (new Date() - scriptStartTime)); }); @@ -187,7 +185,7 @@ function addIntercomData(leads, done) { } function getUser(email) { const options = { - url: `https://api.intercom.io/users?email=${email}`, + url: `https://api.intercom.io/users?email=${encodeURIComponent(email)}`, auth: { user: intercomAppId, pass: intercomApiKey @@ -256,7 +254,7 @@ function updateLeads(leads, done) { }); } function updateLead(email) { - const url = `https://${closeIoApiKey}:X@app.close.io/api/v1/lead/?query=email_address:${email}`; + const url = `https://${closeIoApiKey}:X@app.close.io/api/v1/lead/?query=email_address:${encodeURIComponent(email)}`; request.get(url, (error, response, body) => { if (error) return done(error); const data = JSON.parse(body); diff --git a/server_setup.coffee b/server_setup.coffee index f23860f0f..0c676664b 100644 --- a/server_setup.coffee +++ b/server_setup.coffee @@ -17,6 +17,7 @@ auth = require './server/routes/auth' routes = require './server/routes' UserHandler = require './server/users/user_handler' hipchat = require './server/hipchat' +Mandate = require './server/models/Mandate' global.tv4 = require 'tv4' # required for TreemaUtils to work global.jsondiffpatch = require 'jsondiffpatch' global.stripe = require('stripe')(config.stripe.secretKey) @@ -58,15 +59,15 @@ setupErrorMiddleware = (app) -> err = new errors.UnprocessableEntity(err.response) if err.code is 409 and err.response err = new errors.Conflict(err.response) - + # TODO: Make all errors use this if err instanceof errors.NetworkError return res.status(err.code).send(err.toJSON()) - + if err.status and 400 <= err.status < 500 res.status(err.status).send("Error #{err.status}") return - + res.status(err.status ? 500).send(error: "Something went wrong!") message = "Express error: #{req.method} #{req.path}: #{err.message}" log.error "#{message}, stack: #{err.stack}" @@ -180,17 +181,22 @@ setupFallbackRouteToIndex = (app) -> log.error "Error modifying main.html: #{err}" if err # insert the user object directly into the html so the application can have it immediately. Sanitize user = if req.user then JSON.stringify(UserHandler.formatEntity(req, req.user)).replace(/\//g, '\\/') else '{}' - - data = data.replace '"serverConfigTag"', JSON.stringify - picoCTF: config.picoCTF, - production: config.isProduction - - data = data.replace('"userObjectTag"', user) - data = data.replace('"amActuallyTag"', JSON.stringify(req.session.amActually)) - res.header 'Cache-Control', 'no-cache, no-store, must-revalidate' - res.header 'Pragma', 'no-cache' - res.header 'Expires', 0 - res.send 200, data + + Mandate.findOne({}).cache(5 * 60 * 1000).exec (err, mandate) -> + if err + log.error "Error getting mandate config: #{err}" + configData = {} + else + configData = _.omit mandate?.toObject() or {}, '_id' + configData.picoCTF = config.picoCTF + configData.production = config.isProduction + data = data.replace '"serverConfigTag"', JSON.stringify configData + data = data.replace('"userObjectTag"', user) + data = data.replace('"amActuallyTag"', JSON.stringify(req.session.amActually)) + res.header 'Cache-Control', 'no-cache, no-store, must-revalidate' + res.header 'Pragma', 'no-cache' + res.header 'Expires', 0 + res.send 200, data setupFacebookCrossDomainCommunicationRoute = (app) -> app.get '/channel.html', (req, res) ->