[API v3] Maintenance Mode (#7367)

* WIP(maintenance): maintenance

* WIP(maintenance): working locale features

* fix(maintenance): don't translate info page target

* WIP(maintenance): start adding info page

* fix(maintenance): linting

* feat: Add container to maintenance info page

* fix(maintenance): add config.json edits
Also DRY variables for main vs info pages

* fix(maintenance): linting

* refactor(maintenance): further slim down variables

* refactor: Remove unnecessary variables

* fix: Correct string interpolation in maintenace view

* feat: Dynamically add time to maintenance pages

* maintenance mode: do not connect to mongodb

* fix(maintenance): clean up timezones etc.

* fix(maintenance): remove unneeded sprite
This commit is contained in:
Sabe Jones
2016-05-21 05:24:13 -05:00
committed by Matteo Pagliazzi
parent f4ca97ffc3
commit 237bc062ea
9 changed files with 198 additions and 10 deletions

View File

@@ -0,0 +1,34 @@
{
"habiticaBackSoon": "Don't worry, Habitica will be back soon!",
"importantMaintenance": "We are doing important maintenance that we estimate will last until 10pm Pacific Time (5am UTC).",
"maintenance": "Maintenance",
"maintenanceMoreInfo": "Want more information about the maintenance? <%= linkStart %>Check out our info page<%= linkEnd %>.",
"noDamageKeepStreaks": "You will NOT take damage or lose streaks!",
"thanksForPatience": "Thanks for your patience!",
"twitterMaintenanceUpdates": "For the most recent updates, watch our <a href='https://twitter.com/habitica'>Twitter</a>, where we will be posting status information.",
"veteranPetAward": "At the end, you will receive a Veteran pet!",
"maintenanceInfoTitle": "Information about Upcoming Maintenance to Habitica",
"maintenanceInfoWhat": "What is happening?",
"maintenanceInfoWhatText": "On May 21, Habitica will be down for maintenance for most of the day. You will not take any damage or have your account harmed during that weekend, even if you cant log in to check off your Dailies in time! We will be working very hard to make the downtime as short as possible, and will be posting regular updates on <a href='https://twitter.com/habitica' target='_blank'>our Twitter account</a>. At the end of the downtime, to thank everyone for their patience, you will all receive a rare pet!",
"maintenanceInfoWhy": "Why is this happening?",
"maintenanceInfoWhyText": "For the past several months, we have been thoroughly revamping Habitica behind-the-scenes. Specifically, we have rewritten the API. While it may not look much different on the surface, its a whole new world underneath. This will allow us WAY more flexibility when we want to build features in the future, and lead to improved performance!",
"maintenanceInfoTechDetails": "Want more details on the technical side of the process? Visit <a href='http://devs.habitica.com/' target='_blank'>The Forge, our dev blog</a>.",
"maintenanceInfoMore": "More Information",
"maintenanceInfoAccountChanges": "What changes will I see to my account after the rewrite is complete?",
"maintenanceInfoAccountChangesText": "At first, there wont be any notable changes aside from performance improvements for features such as Challenges. If you notice any changes that shouldnt be there, email us at <a href='mailto:admin@habitica.com'>admin@habitica.com</a> and we will investigate them for you!",
"maintenanceInfoAddFeatures": "What kind of features will this allow Habitica to add?",
"maintenanceInfoAddFeaturesText": "Completing this rewrite will allow us to start building out improved chat and Guilds, plans for organizations and families, and additional productivity features like Monthlies and the ability to record yesterdays activity! Those are all involved features on their own, so it will take time to build them, but until we were finished with this rewrite, there was no way we could start them.",
"maintenanceInfoHowLong": "How long will the maintenance take?",
"maintenanceInfoHowLongText": "We have to migrate tasks and data for all 1.3 million Habitica users -- not an easy task! We anticipate that it will take place between approximately 1pm Pacific Time (8pm UTC) and 10pm Pacific Time (5am UTC). Rest assured that were doing everything we can to make it go as quickly as possible! You can follow <a href='https://twitter.com/habitica' target='_blank'>updates on our Twitter</a>.",
"maintenanceInfoStatsAffected": "How will my Dailies, Streaks, Buffs, and Quests be affected?",
"maintenanceInfoStatsAffectedText1": "You will NOT take any damage or lose any streaks that weekend, but otherwise, your day will reset normally! Dailies that you checked will become unchecked, buffs will reset, etc. If you are in a Collection Quest, you will still find items. If you are in a Boss Battle, you will still deal damage to the Boss, but the Boss will not deal damage to you. (Even monsters need a break!)",
"maintenanceInfoStatsAffectedText2": "After a lot of thought, our team concluded that this was the most fair way to handle the fact that many users will not be able to check off their Dailies normally during the maintenance. Were sorry for any inconvenience this causes!",
"maintenanceInfoSeeTasks": "What if I need to see my task list?",
"maintenanceInfoSeeTasksText": "If you know that you will need to see your task list on Saturday to remind yourself what you have to do, we recommend that before the maintenance begins, you take a screenshot of your tasks so that you can use it as a reference.",
"maintenanceInfoRarePet": "What kind of rare pet will I receive?",
"maintenanceInfoRarePetText": "To thank you for your patience during the downtime, everyone will get a rare Veteran Pet. If youve never received a Veteran Pet before, you will receive a Veteran Wolf. If you already have a Veteran Wolf, you will receive a Veteran Tiger. And if you already have a Veteran Wolf and a Veteran Tiger, you will receive a never-before-seen Veteran pet! After the migration is completed, it may take several hours for your pet to show up, but never fear, everyone will get one.",
"maintenanceInfoWho": "Who worked on this massive project?",
"maintenanceInfoWhoText": "Were glad you asked! It was spearheaded by our amazing contributor paglias, with lots of help from Blade, TheHollidayInn, SabreCat, Victor Pudeyev, TheUnknown, and Alys.",
"maintenanceInfoTesting": "The new version was also tirelessly tested by a bunch of our amazing open-source volunteers. Thank you -- we couldn't have done this without you."
}

View File

@@ -10,6 +10,7 @@
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
"NODE_ENV":"development",
"CRON_SAFE_MODE":"false",
"MAINTENANCE_MODE": "false",
"SESSION_SECRET":"YOUR SECRET HERE",
"ADMIN_EMAIL": "you@example.com",
"SMTP_USER":"user@example.com",

View File

@@ -0,0 +1,58 @@
import {
generateRes,
generateReq,
generateNext,
} from '../../../../helpers/api-unit.helper';
import nconf from 'nconf';
import requireAgain from 'require-again';
describe('maintenance mode middleware', () => {
let res, req, next;
let pathToMaintenanceModeMiddleware = '../../../../../website/server/middlewares/api-v3/maintenanceMode';
beforeEach(() => {
res = generateRes();
next = generateNext();
});
it('does not return 503 error when maintenance mode is off', () => {
req = generateReq();
sandbox.stub(nconf, 'get').withArgs('MAINTENANCE_MODE').returns('false');
let attachMaintenanceMode = requireAgain(pathToMaintenanceModeMiddleware);
attachMaintenanceMode(req, res, next);
expect(next).to.have.been.called.once;
expect(res.status).to.not.have.been.called;
});
it('returns 503 error when maintenance mode is on', () => {
req = generateReq();
sandbox.stub(nconf, 'get').withArgs('MAINTENANCE_MODE').returns('true');
let attachMaintenanceMode = requireAgain(pathToMaintenanceModeMiddleware);
attachMaintenanceMode(req, res, next);
expect(next).to.not.have.been.called;
expect(res.status).to.have.been.calledOnce;
expect(res.status).to.have.been.calledWith(503);
});
it('renders maintenance page when request type is HTML', () => {
req = generateReq({headers: {accept: 'text/html'}});
sandbox.stub(nconf, 'get').withArgs('MAINTENANCE_MODE').returns('true');
let attachMaintenanceMode = requireAgain(pathToMaintenanceModeMiddleware);
attachMaintenanceMode(req, res, next);
expect(res.render).to.have.been.calledOnce;
});
it('sends error message when request type is JSON', () => {
req = generateReq({headers: {accept: 'application/json'}});
sandbox.stub(nconf, 'get').withArgs('MAINTENANCE_MODE').returns('true');
let attachMaintenanceMode = requireAgain(pathToMaintenanceModeMiddleware);
attachMaintenanceMode(req, res, next);
expect(res.send).to.have.been.calledOnce;
});
});

View File

@@ -25,6 +25,7 @@ export function generateGroup (options = {}) {
export function generateRes (options = {}) {
let defaultRes = {
render: sandbox.stub(),
send: sandbox.stub(),
status: sandbox.stub().returnsThis(),
sendStatus: sandbox.stub().returnsThis(),

View File

@@ -5,19 +5,24 @@ import mongoose from 'mongoose';
import Bluebird from 'bluebird';
const IS_PROD = nconf.get('IS_PROD');
const MAINTENANCE_MODE = nconf.get('MAINTENANCE_MODE');
// Use Q promises instead of mpromise in mongoose
mongoose.Promise = Bluebird;
let mongooseOptions = !IS_PROD ? {} : {
// Do not connect to MongoDB when in maintenance mode
if (MAINTENANCE_MODE !== 'true') {
let mongooseOptions = !IS_PROD ? {} : {
replset: { socketOptions: { keepAlive: 120, connectTimeoutMS: 30000 } },
server: { socketOptions: { keepAlive: 120, connectTimeoutMS: 30000 } },
};
};
const NODE_DB_URI = nconf.get('IS_TEST') ? nconf.get('TEST_DB_URI') : nconf.get('NODE_DB_URI');
let db = mongoose.connect(NODE_DB_URI, mongooseOptions, (err) => {
const NODE_DB_URI = nconf.get('IS_TEST') ? nconf.get('TEST_DB_URI') : nconf.get('NODE_DB_URI');
let db = mongoose.connect(NODE_DB_URI, mongooseOptions, (err) => {
if (err) throw err;
logger.info('Connected with Mongoose.');
});
});
autoinc.init(db);
autoinc.init(db);
}

View File

@@ -14,6 +14,7 @@ import favicon from 'serve-favicon';
import methodOverride from 'method-override';
import passport from 'passport';
import path from 'path';
import maintenanceMode from './maintenanceMode';
import {
forceSSL,
forceHabitica,
@@ -48,6 +49,8 @@ module.exports = function attachMiddlewares (app, server) {
app.use(compression());
app.use(favicon(`${PUBLIC_DIR}/favicon.ico`));
app.use(maintenanceMode);
app.use(cors);
app.use(forceSSL);
app.use(forceHabitica);

View File

@@ -0,0 +1,31 @@
import { getUserLanguage } from './language';
import nconf from 'nconf';
const MAINTENANCE_MODE = nconf.get('MAINTENANCE_MODE');
module.exports = function maintenanceMode (req, res, next) {
if (MAINTENANCE_MODE !== 'true') return next();
getUserLanguage(req, res, (err) => {
if (err) return next(err);
let pageVariables = {
maintenanceStart: nconf.get('MAINTENANCE_START'),
maintenanceEnd: nconf.get('MAINTENANCE_END'),
translation: res.t,
};
if (req.headers && req.headers.accept && req.headers.accept.indexOf('text/html') !== -1) {
if (req.path === '/views/static/maintenance-info') {
return res.status(503).render('../../../views/static/maintenance-info', pageVariables);
} else {
return res.status(503).render('../../../views/static/maintenance', pageVariables);
}
} else {
return res.status(503).send({
error: 'Maintenance',
message: 'Server offline for maintenance.',
});
}
});
};

View File

@@ -0,0 +1,37 @@
- var t = env ? env.t : translation;
title Habitica &VerticalLine;&nbsp;
=t('maintenance')
head
link(rel='stylesheet', type='text/css', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css')
body
.container
h1.text-center=t('maintenanceInfoTitle')
img.img-rendering-auto.center-block.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/assets/scene_maintenance.png')
h2=t('maintenanceInfoWhat')
p!=t('maintenanceInfoWhatText')
img.pull-left(src='https://d2afqr2xdmyzvu.cloudfront.net/assets/scene_new.png')
h2=t('maintenanceInfoWhy')
p=t('maintenanceInfoWhyText')
p!=t('maintenanceInfoTechDetails')
h2=t('maintenanceInfoMore')
h3=t('maintenanceInfoAccountChanges')
p!=t('maintenanceInfoAccountChangesText')
h3=t('maintenanceInfoAddFeatures')
p=t('maintenanceInfoAddFeaturesText')
h3=t('maintenanceInfoHowLong')
img.pull-right(src='https://d2afqr2xdmyzvu.cloudfront.net/assets/scene_chatter.png')
p!=t('maintenanceInfoHowLongText')
h3=t('maintenanceInfoStatsAffected')
p=t('maintenanceInfoStatsAffectedText1')
p=t('maintenanceInfoStatsAffectedText2')
h3=t('maintenanceInfoSeeTasks')
p=t('maintenanceInfoSeeTasksText')
h3=t('maintenanceInfoRarePet')
p=t('maintenanceInfoRarePetText')
img.pull-left(src='https://d2afqr2xdmyzvu.cloudfront.net/assets/scene_coding.png')
h3=t('maintenanceInfoWho')
p=t('maintenanceInfoWhoText')
p=t('maintenanceInfoTesting')

View File

@@ -0,0 +1,18 @@
- var t = env ? env.t : translation;
title Habitica &VerticalLine;&nbsp;
=t('maintenance')
head
link(rel='stylesheet', type='text/css', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css')
body.text-center
h1=t('habiticaBackSoon')
img.img-rendering-auto.center-block.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/assets/scene_maintenance.png')
p!=t('importantMaintenance')
p!=t('twitterMaintenanceUpdates')
ul.lead(style='list-style-position:inside')
li=t('noDamageKeepStreaks')
li=t('veteranPetAward')
p!=t('maintenanceMoreInfo', {linkStart: '<a href="/views/static/maintenance-info">', linkEnd: '</a>',})
p.lead=t('thanksForPatience')