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') }}
+
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')}`);