2ee2b05d1c
* feat(security): implement CSP * fix(csp): update helmet version to latest * Squashed commit of the following: commit cc6a35e61db07759c1f32716185543bc48bce760 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 17:27:50 2025 -0600 fix(CSP): more Amazon domains commit 985b86c29af866b2df942c21217d99390a2c6e92 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 17:18:08 2025 -0600 fix(csp): more loggly allowance commit 166bd315272f9c3a42652f3a026c27a88ed1a549 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 17:12:00 2025 -0600 fix(csp): data, inline, some refactoring commit 1a0a6c1806a53d43a7199bb2ef72cff610e908be Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 17:05:44 2025 -0600 fix(CSP): override default script-src commit 023d9886c835989da9c5901c168d66b572097dcf Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:56:24 2025 -0600 fix(CSP): unsafe-eval in default-src commit f51f0a0c93b60dfec7ce02be0ecd2587fc882fe0 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:52:14 2025 -0600 fix(CSP): move trusted list to default-src commit 83b2ba7688dea38abb651cf5c27482a7a3648374 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:38:05 2025 -0600 fix(CSP): explicit habitica/aws in script-src commit d5ca5172d5ad2fd8cec9402d7d2c9452c6ece7a1 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:31:38 2025 -0600 fix(CSP): need escaped single quotes commit c677a1ffeff5793b6da228924e68d2c2e47794b2 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:27:46 2025 -0600 fix(CSP): unsafe-eval commit 6ef35c3f7281c8426d9c333686be6bb65f00b3a8 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:15:07 2025 -0600 fix(CSP): might need to skip entirely in dev but try no 'self' commit 5759fb37d82fa61b474f01e9ce5e2dc461f6ceba Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 15:51:26 2025 -0600 fix(csp): permit AWS in default-src commit 9f238abf9373bc29715657b945b2247ef23c9224 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 5 17:22:25 2025 -0600 fix(csp): update helmet version to latest commit 9462e90f4f3058f4014137b3178b9751c5280e97 Author: Kalista Payne <kalista@habitica.com> Date: Tue Nov 25 09:27:05 2025 -0600 feat(security): implement CSP commit72539f9ba3Author: Kalista Payne <kalista@habitica.com> Date: Wed Dec 10 14:16:53 2025 -0600 5.42.2 commitdabd466719Author: Kalista Payne <kalista@habitica.com> Date: Wed Dec 10 14:16:48 2025 -0600 Revert "Chat optimization (#15545)" This reverts commit2917955ef0. commit8bf2304330Author: Kalista Payne <kalista@habitica.com> Date: Wed Dec 10 14:15:48 2025 -0600 chore(event): G1G1 date tweaks commit6937dc4e4eAuthor: Kalista Payne <kalista@habitica.com> Date: Mon Dec 8 16:37:04 2025 -0600 fix(subscription): couple more layout tweaks * fix(csp): move unsafe-eval to default? ig? * Revert "fix(csp): move unsafe-eval to default? ig?" This reverts commit 90476cbf6c59132024d4fbaeceb7cdb19a876303. * fix(security): no unsafe! yay! * fix(packages): remove webpack * fix(lint): object destructuring * fix(csp): remove Vue-Fragment * wip(i18n): load Moment locale from cache * fix(gulp): remove unneeded cache task * fix(i18n): add Moment weekday abbrevs to translations * fix(lint): destructuring ...why is this happening here and not develop lol * fix(csp): add amplitude to whitelist --------- Co-authored-by: Phillip Thelen <phillip@thelen.space>
105 lines
3.0 KiB
JavaScript
105 lines
3.0 KiB
JavaScript
import forEach from 'lodash/forEach';
|
|
import isEqual from 'lodash/isEqual';
|
|
import keys from 'lodash/keys';
|
|
import pick from 'lodash/pick';
|
|
import amplitude from 'amplitude-js';
|
|
import Vue from 'vue';
|
|
import getStore from '@/store';
|
|
|
|
const { AMPLITUDE_KEY } = import.meta.env;
|
|
const REQUIRED_FIELDS = ['eventCategory', 'eventAction'];
|
|
|
|
let analyticsLoading = false;
|
|
let analyticsReady = false;
|
|
|
|
function _getConsentedUser () {
|
|
const store = getStore();
|
|
const user = store.state.user.data;
|
|
if (!user?.preferences?.analyticsConsent) {
|
|
return false;
|
|
}
|
|
return user;
|
|
}
|
|
|
|
function _doesNotHaveRequiredFields (properties) {
|
|
if (!isEqual(keys(pick(properties, REQUIRED_FIELDS)), REQUIRED_FIELDS)) {
|
|
// @TODO: Log with Winston?
|
|
// console.log('Analytics tracking calls must include
|
|
// the following properties: ' + JSON.stringify(REQUIRED_FIELDS));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function _gatherUserStats (properties) {
|
|
const store = getStore();
|
|
const user = store.state.user.data;
|
|
const tasks = store.state.tasks.data;
|
|
|
|
properties.UUID = user._id;
|
|
|
|
properties.Class = user.stats.class;
|
|
properties.Experience = Math.floor(user.stats.exp);
|
|
properties.Gold = Math.floor(user.stats.gp);
|
|
properties.Health = Math.ceil(user.stats.hp);
|
|
properties.Level = user.stats.lvl;
|
|
properties.Mana = Math.floor(user.stats.mp);
|
|
|
|
properties.balance = user.balance;
|
|
properties.balanceGemAmount = properties.balance * 4;
|
|
|
|
properties.tutorialComplete = user.flags.tour.intro === -2;
|
|
|
|
properties['Number Of Tasks'] = {
|
|
habits: tasks.habits.length,
|
|
dailys: tasks.dailys.length,
|
|
todos: tasks.todos.length,
|
|
rewards: tasks.rewards.length,
|
|
};
|
|
|
|
if (user.contributor.level) properties.contributorLevel = user.contributor.level;
|
|
if (user.purchased.plan.planId) properties.subscription = user.purchased.plan.planId;
|
|
}
|
|
|
|
export function safeSetup (userId) {
|
|
if (analyticsLoading || analyticsReady) return;
|
|
analyticsLoading = true;
|
|
amplitude.getInstance().init(AMPLITUDE_KEY, userId);
|
|
analyticsReady = true;
|
|
analyticsLoading = false;
|
|
}
|
|
|
|
export function track (properties, options = {}) {
|
|
const user = _getConsentedUser();
|
|
if (!user) return;
|
|
safeSetup(user._id);
|
|
// Use nextTick to avoid blocking the UI
|
|
Vue.nextTick(() => {
|
|
if (_doesNotHaveRequiredFields(properties)) return;
|
|
|
|
const trackOnClient = options && options.trackOnClient === true;
|
|
// Track events on the server by default
|
|
if (trackOnClient === true) {
|
|
amplitude.getInstance().logEvent(properties.eventAction, properties);
|
|
} else {
|
|
const store = getStore();
|
|
store.dispatch('analytics:trackEvent', properties);
|
|
}
|
|
});
|
|
}
|
|
|
|
export function updateUser (properties = {}) {
|
|
const user = _getConsentedUser();
|
|
if (!user) return;
|
|
safeSetup(user._id);
|
|
// Use nextTick to avoid blocking the UI
|
|
Vue.nextTick(() => {
|
|
_gatherUserStats(properties);
|
|
forEach(properties, (value, key) => {
|
|
const identify = new amplitude.Identify().set(key, value);
|
|
amplitude.getInstance().identify(identify);
|
|
});
|
|
});
|
|
}
|