diff --git a/config.json.example b/config.json.example index 62c3439cd2..4a0bb02756 100644 --- a/config.json.example +++ b/config.json.example @@ -87,5 +87,6 @@ "REDIS_HOST": "aaabbbcccdddeeefff", "REDIS_PORT": "1234", "REDIS_PASSWORD": "12345678", - "TRUSTED_DOMAINS": "localhost,https://habitica.com" + "TRUSTED_DOMAINS": "localhost,https://habitica.com", + "ENABLE_TIME_TRAVEL": "false" } diff --git a/website/client/src/components/appFooter.vue b/website/client/src/components/appFooter.vue index b0f19266d5..f1bb0163de 100644 --- a/website/client/src/components/appFooter.vue +++ b/website/client/src/components/appFooter.vue @@ -290,6 +290,31 @@ >{{ $t('terms') }} +
+ -1 Day + -7 Days + -30 Days +
+ Time Traveling! It is {{ new Date().toLocaleString() }} +
+ +1 Day + +7 Days + +30 Days +
+
Toggle Debug Menu -
- Today is {{ new Date() }} -
0) { + Vue.config.clock.jump(amount * 24 * 60 * 60 * 1000); + } else { + Vue.config.clock.setSystemTime(moment().add(amount, 'days').toDate()); + } + }, addExp () { // @TODO: Name these variables better let exp = 0; diff --git a/website/client/src/main.js b/website/client/src/main.js index 756e2d60d7..929d636a6e 100644 --- a/website/client/src/main.js +++ b/website/client/src/main.js @@ -1,5 +1,4 @@ import Vue from 'vue'; -import sinon from 'sinon'; import BootstrapVue from 'bootstrap-vue'; import Fragment from 'vue-fragment'; import AppComponent from './app'; @@ -14,7 +13,6 @@ import './filters/registerGlobals'; import i18n from './libs/i18n'; import 'smartbanner.js/dist/smartbanner'; -let jumped = false; const IS_PRODUCTION = process.env.NODE_ENV === 'production'; // eslint-disable-line no-process-env // Configure Vue global options, see https://vuejs.org/v2/api/#Global-Config @@ -37,20 +35,21 @@ setUpLogging(); setupAnalytics(); // just create queues for analytics, no scripts loaded at this time const store = getStore(); -const time = new Date(2024, 2, 18); -const clock = sinon.useFakeTimers({ - now: time, - shouldAdvanceTime: true, -}); +if (process.env.ENABLE_TIME_TRAVEL) { + (async () => { + const sinon = await import('sinon'); + const axios = await import('axios'); + const response = await axios.get('/api/v4/debug/time-travel-time'); + console.log(response.data.data.time); + const time = new Date(response.data.data.time); + Vue.config.clock = sinon.useFakeTimers({ + now: time, + shouldAdvanceTime: true, + }); -setInterval(() => { - if (jumped) { - jumped = false; - return; - } - jumped = true; - clock.jump(36000); -}, 1000); + console.log('Time travel mode activated. It is now', new Date()); + })(); +} const vueInstance = new Vue({ el: '#app', diff --git a/website/client/vue.config.js b/website/client/vue.config.js index 516fe93398..f6673adbcf 100644 --- a/website/client/vue.config.js +++ b/website/client/vue.config.js @@ -28,6 +28,7 @@ const envVars = [ 'AMPLITUDE_KEY', 'LOGGLY_CLIENT_TOKEN', 'TRUSTED_DOMAINS', + 'ENABLE_TIME_TRAVEL', // TODO necessary? if yes how not to mess up with vue cli? 'NODE_ENV' ]; diff --git a/website/server/controllers/api-v3/debug.js b/website/server/controllers/api-v3/debug.js index 0b80667343..5c23f32f6d 100644 --- a/website/server/controllers/api-v3/debug.js +++ b/website/server/controllers/api-v3/debug.js @@ -1,4 +1,6 @@ import _ from 'lodash'; +import nconf from 'nconf'; +import moment from 'moment'; import { authWithHeaders } from '../../middlewares/auth'; import ensureDevelpmentMode from '../../middlewares/ensureDevelpmentMode'; import { BadRequest } from '../../libs/errors'; @@ -201,4 +203,57 @@ api.questProgress = { }, }; +let clock; +if (nconf.get('ENABLE_TIME_TRAVEL')) { + (async () => { + const sinon = await import('sinon'); + const time = new Date(); + clock = sinon.useFakeTimers({ + now: time, + shouldAdvanceTime: true, + }); + })(); + + api.timeTravelTime = { + method: 'GET', + url: '/debug/time-travel-time', + middlewares: [authWithHeaders()], + async handler (req, res) { + const { user } = res.locals; + + if (!user.permissions.fullAccess) { + throw new BadRequest('You do not have permission to time travel.'); + } + + res.respond(200, { + time: new Date(), + }); + }, + } + + api.timeTravelAdjust = { + method: 'POST', + url: '/debug/jump-time', + middlewares: [authWithHeaders()], + async handler (req, res) { + const { user } = res.locals; + + if (!user.permissions.fullAccess) { + throw new BadRequest('You do not have permission to time travel.'); + } + + const { offsetDays } = req.body; + if (offsetDays > 0) { + clock.jump(offsetDays * 24 * 60 * 60 * 1000) + } else { + clock.setSystemTime(moment().add(offsetDays, 'days').toDate()); + } + + res.respond(200, { + time: new Date(), + }); + }, + } +} + export default api; diff --git a/website/server/server.js b/website/server/server.js index 852a1ccbd9..c952da4b04 100644 --- a/website/server/server.js +++ b/website/server/server.js @@ -2,7 +2,6 @@ import nconf from 'nconf'; import express from 'express'; import http from 'http'; import logger from './libs/logger'; -import sinon from 'sinon'; // Setup translations // Must come before attach middlewares so Mongoose validations can use translations @@ -26,23 +25,6 @@ app.set('port', nconf.get('PORT')); attachMiddlewares(app, server); -const time = new Date(2024, 2, 18); -const clock = sinon.useFakeTimers({ - now: time, - shouldAdvanceTime: true, -}); - -var jumped = false; -setInterval(() => { - if (jumped) { - jumped = false; - return; - } - jumped = true; - console.log('Jumping time'); - clock.jump(36000); -}, 1000); - server.on('request', app); server.listen(app.get('port'), () => { logger.info(`Express server listening on port ${app.get('port')}`);