Change ads provider, and add consent system for advertising (#155)

* Add GAM integration & base for GPDR consent

* Moved consent to a specific page.

* Added functionality to the privacy page, and desactivate tracking if consent is not given.

* Added GeoEdge support, and fixed auth issues

* Fix actions issue

* Fix actions issue, attempt 2

* Added a module for analytics with consent support.

* Remove unnecessary function

* Add support for runtime config
This commit is contained in:
Redblueflame 2021-04-09 04:44:25 +02:00 committed by GitHub
parent 103ce44ba9
commit 03b2d02742
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1729 additions and 46 deletions

3
.eslintignore Normal file
View File

@ -0,0 +1,3 @@
node_modules/
jspm_packages/
modules/*

View File

@ -420,3 +420,56 @@
}
}
.switch {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
-webkit-tap-highlight-color: transparent;
cursor: pointer;
&:focus {
outline: 0;
}
}
.stylized-toggle {
height: 32px;
width: 52px;
border-radius: 16px;
display: inline-block;
position: relative;
margin: 0;
border: 2px solid var(--color-button-bg);
transition: all .2s ease;
background: var(--color-button-bg);
&:after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 24px;
height: 24px;
border-radius: 50%;
background: white;
box-shadow: 0 1px 2px rgba(44,44,44,.2);
transition: all .2s cubic-bezier(.5,.1,.75,1.35);
}
&:checked {
background: var(--color-brand);
border: 2px solid var(--color-brand);
&:after {
transform: translatex(20px);
}
}
&:hover &:focus {
background: var(--color-button-bg);
border: 2px solid var(--color-button-bg);
}
&:hover:checked &:focus:checked {
background: var(--color-brand);
border: 2px solid var(--color-brand);
}
}

View File

@ -1,25 +1,23 @@
<template>
<div class="ad-wrapper">
<adsbygoogle
ad-slot="7510690716"
:ad-format="format"
:page-url="pageUrl ? pageUrl : undefined"
/>
<div class="ad">
<GptAd :ad-unit="adUnit" :size="size" />
</div>
</div>
</template>
<script>
/* eslint-disable no-undef */
export default {
name: 'Advertisement',
props: {
format: {
size: {
type: String,
default: 'horizontal',
required: true,
},
pageUrl: {
adUnit: {
type: String,
required: false,
default: '',
required: true,
},
},
}
@ -29,6 +27,9 @@ export default {
.ad-wrapper {
width: 100%;
@extend %card;
display: flex;
flex-direction: row;
margin-bottom: var(--spacing-card-md);
justify-content: center;
}
</style>

View File

@ -0,0 +1,77 @@
<template>
<div>
<ReviewPopup ref="popup" />
<div
ref="container"
class="container"
:style="{ visibility: shown ? 'visible' : 'hidden' }"
>
<div class="banner">
<span>
Modrinth uses cookies for various purposes, including advertising.<br />
We encourage you to review your privacy settings by clicking on the
button below:
</span>
<div class="actions">
<button class="btn button" @click="hide">Accept all</button>
<button class="btn brand-button" @click="review">Review</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CookieConsent',
data() {
return {
shown: false,
}
},
mounted() {
// Get informations in the store
this.$store.dispatch('consent/loadFromCookies', this.$cookies)
if (
!this.$store.state.consent.is_consent_given &&
this.$route.path !== '/dashboard/privacy'
) {
this.shown = true
}
},
methods: {
hide() {
this.shown = false
},
review() {
this.hide()
this.$router.push('/dashboard/privacy')
},
},
}
</script>
<style scoped lang="scss">
.container {
z-index: 20;
position: fixed;
bottom: 0;
right: 0;
.banner {
@extend %card;
margin: 0 2rem 2rem 0;
padding: 1rem;
max-width: 18vw;
border-left: solid 5px var(--color-brand);
font-size: 1.05rem;
}
.actions {
display: flex;
flex-direction: row;
margin-top: 1rem;
.btn {
margin-right: 0.5rem;
}
}
}
</style>

View File

@ -0,0 +1,24 @@
<template>
<Popup :show-popup="shown"> </Popup>
</template>
<script>
export default {
name: 'ReviewPopup',
data() {
return {
shown: false,
}
},
methods: {
show() {
this.shown = true
},
hide() {
this.shown = false
},
},
}
</script>
<style scoped lang="scss"></style>

View File

@ -26,6 +26,11 @@
<a target="_blank" href="https://twitter.com/modrinth">Twitter</a>
</li>
</ul>
<ul>
<li>
<nuxt-link to="/dashboard/privacy">Set privacy preferences</nuxt-link>
</li>
</ul>
<span> © Guavy LLC </span><br />
<span v-if="version !== 'unknown'">Version: {{ version }}</span>
</footer>

View File

@ -65,9 +65,8 @@
</div>
<Advertisement
v-if="mod.status === 'approved' || mod.status === 'unlisted'"
:page-url="
'https://modrinth.com/mod/' + (mod.slug ? mod.slug : mod.id)
"
ad-unit="banner"
size="728x90,468x60"
/>
<div class="mod-navigation">
<div class="tabs">
@ -133,9 +132,8 @@
<slot />
<Advertisement
v-if="mod.status === 'approved' || mod.status === 'unlisted'"
:page-url="
'https://modrinth.com/mod/' + (mod.slug ? mod.slug : mod.id)
"
ad-unit="banner"
size="728x90,468x60"
/>
</div>
</div>
@ -333,10 +331,8 @@
</div>
<Advertisement
v-if="mod.status === 'approved' || mod.status === 'unlisted'"
format="rectangle"
:page-url="
'https://modrinth.com/mod/' + (mod.slug ? mod.slug : mod.id)
"
ad-unit="square"
size="250x250,200x200"
/>
<m-footer class="footer" />
</section>
@ -364,7 +360,7 @@ import ExternalIcon from '~/assets/images/utils/external.svg?inline'
import ForgeIcon from '~/assets/images/categories/forge.svg?inline'
import FabricIcon from '~/assets/images/categories/fabric.svg?inline'
import Advertisement from '~/components/Advertisement'
import Advertisement from '~/components/ads/Advertisement'
export default {
name: 'ModPage',

View File

@ -23,7 +23,7 @@ export default {
.popup-overlay {
top: 0;
left: 0;
z-index: 1;
z-index: 10;
position: fixed;
width: 100%;
height: 100%;
@ -38,11 +38,11 @@ export default {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
z-index: 11;
box-shadow: 0 2px 3px 1px var(--color-button-bg);
border-radius: 10px;
max-height: 80%;
overflow-y: auto;
background-color: var(--color-bg);
background-color: var(--color-raised-bg);
}
</style>

View File

@ -96,6 +96,7 @@
</section>
</header>
<main>
<CookieConsent />
<notifications group="main" position="bottom right" />
<!--<notifications
group="ads"

View File

@ -0,0 +1,8 @@
/* eslint-disable no-undef */
export default function ({ route }) {
if (process.client) {
googletag.cmd.push(function () {
googletag.pubads().setTargeting('path', route.path)
})
}
}

View File

@ -9,7 +9,9 @@ export default async function (context) {
context.app.$cookies.set('auth-token', context.route.query.code, {
secure: true,
sameSite: 'Strict',
maxAge: 60 * 60 * 2, // 2 hours
httpOnly: true,
path: '/',
})
await context.store.dispatch('auth/fetchUser', {

View File

@ -0,0 +1,6 @@
export const DEFAULT_OPTIONS = {
enabled: false,
script_url: 'https://example.com',
tracking_code: 'xxx'
}
export const UNAMI_LIB_TAG_ID = 'unami-import'

View File

@ -0,0 +1,22 @@
import {
DEFAULT_OPTIONS,
UNAMI_LIB_TAG_ID
} from './constants';
const { resolve } = require('path');
module.exports = async function module(moduleOptions) {
const options = Object.assign(DEFAULT_OPTIONS, this.options.analytics, moduleOptions);
const templatesOptions = {
...options,
UNAMI_LIB_TAG_ID
};
this.addPlugin({
src: resolve(__dirname, 'templates/plugin.js'),
fileName: 'analytics/plugin.js',
options: templatesOptions,
});
};
module.exports.meta = require('../package.json');

View File

@ -0,0 +1,64 @@
import Vue from 'vue';
function isAnalyticsOn(ctx) {
let cookies = null
if (ctx.req != null) {
//Server side rendering
cookies = ctx.req.headers.cookie;
} else {
// Rely on the client
cookies = document.cookie;
}
let processed = {}
cookies.split(';').forEach((e) => {
let val = e.trim().split('=');
processed[val[0]] = decodeURI(val[1]);
})
let scopes = decodeURIComponent(processed['modrinth-scopes']).split(",");
return (scopes !== null && scopes.includes('analytics'));
}
export default async function (ctx, inject) {
const { app } = ctx;
const config = ctx.$config && ctx.$config.analytics || {};
const url = config.script_url ?? '<%= options.script_url %>';
const tag = config.tracking_code ?? '<%= options.tracking_code %>';
const enabled = config.enabled ?? <%= options.enabled || false %>;
// Check if the parameters are not changed by runtime config:
const UNAMI_LIB_TAG_ID = '<%= options.UNAMI_LIB_TAG_ID %>';
if (!enabled) {
console.log("Analytics are not enabled.")
return;
}
const injectScript = (script) => {
const scriptIndex = ctx.app.head.script.findIndex(s => s.hid === script.hid);
if (scriptIndex !== -1) {
ctx.app.head.script[scriptIndex] = script;
} else {
ctx.app.head.script.push(script);
}
};
if (isAnalyticsOn(ctx)) {
// Inject unami
const analyticsScript = {
hid: UNAMI_LIB_TAG_ID,
src: url,
'data-website-id': 'c37613de-245d-4767-90e7-ba7980a4f1a2',
async: true,
defer: true,
};
injectScript(analyticsScript);
} else {
// console.log("Analytics scope was denied.")
}
}

View File

@ -0,0 +1,31 @@
{
"name": "@modrinth/analytics",
"version": "0.1.0",
"description": "Unami integration for Nuxtjs",
"license": "MIT",
"contributors": [
{
"name": "redblueflame <contact@redblueflame.com>"
}
],
"main": "lib/module.js",
"repository": "https://github.com/modrinth/knossos/",
"homepage": "https://github.com/modrinth/knossos/tree/master/modules/analytics",
"files": [
"lib"
],
"devDependencies": {
"codecov": "latest",
"eslint": "5.13.0",
"eslint-config-airbnb-base": "13.1.0",
"eslint-loader": "2.1.2",
"eslint-plugin-import": "2.16.0",
"eslint-plugin-jest": "22.3.0",
"eslint-plugin-vue": "5.2.1",
"jest": "24.1.0",
"jsdom": "14.0.0",
"nuxt": "2.4.3",
"request-promise-native": "1.0.5",
"standard-version": "latest"
}
}

View File

@ -0,0 +1,16 @@
export const DEFAULT_OPTIONS = {
networkCode: null,
debug: false,
ghostMode: false,
componentName: 'GptAd',
individualRefresh: false,
responsive: false,
collapseEmptyDivs: false,
emptyClass: 'is-empty',
geoEdgeId: ''
};
export const GPT_LIB_SCRIPT_ID = 'google-publisher-tag-lib-script';
export const GPT_INIT_SCRIPT_ID = 'google-publisher-tag-init-script';
export const GEOEDGE_CONF_SCRIPT_ID = 'geoedge-config-script';
export const GEOEDGE_LIB_SCRIPT_ID = 'geoedge-lib-script';

View File

@ -0,0 +1,34 @@
import {
DEFAULT_OPTIONS,
GPT_LIB_SCRIPT_ID,
GPT_INIT_SCRIPT_ID,
GEOEDGE_CONF_SCRIPT_ID,
GEOEDGE_LIB_SCRIPT_ID,
} from './constants';
const { resolve } = require('path');
module.exports = async function module(moduleOptions) {
const options = Object.assign(DEFAULT_OPTIONS, this.options.ads, moduleOptions);
const templatesOptions = {
...options,
GPT_LIB_SCRIPT_ID,
GPT_INIT_SCRIPT_ID,
GEOEDGE_CONF_SCRIPT_ID,
GEOEDGE_LIB_SCRIPT_ID,
};
this.addPlugin({
src: resolve(__dirname, 'templates/plugin.js'),
fileName: 'gpt-ads-module/plugin.js',
options: templatesOptions,
});
this.addTemplate({
src: resolve(__dirname, 'templates/component.js'),
fileName: 'gpt-ads-module/component.js',
options: templatesOptions,
});
};
module.exports.meta = require('../package.json');

View File

@ -0,0 +1,256 @@
export default {
name: '<%= options.componentName %>',
data: () => ({
adSlot: null,
mapping: [],
currentSizeMappingIndex: null,
windowResizeListenerDebounce: null,
isEmpty: true,
}),
props: {
adUnit: {
type: String,
required: true,
},
size: {
type: [Array, String],
required: true,
},
sizeMapping: {
type: Array,
required: false,
default: () => [],
},
id: {
type: [Number, String],
required: false,
default: () => Math.random().toString(36).substring(5),
},
isResponsive: {
type: Boolean,
required: false,
default: <%= options.responsive %>,
},
windowResizeDebounce: {
type: Number,
required: false,
default: 300,
},
collapseEmptyDiv: {
type: Boolean,
required: false,
default: null,
},
},
computed: {
ghostMode() {
return this.$config.ads.ghostMode ?? <%= options.ghostMode %>;
},
networkCode() {
const { $gptAds } = this;
return $gptAds ? $gptAds.networkCode : null;
},
adUnitPath() {
const { networkCode, adUnit } = this;
return `/${networkCode}/${adUnit}`;
},
divId() {
const { id } = this;
return `div-gpt-ad-${id}-0`;
},
formattedSize() {
return this.formatSizeList(this.size);
},
style() {
if (this.ghostMode) {
const { formattedSize, currentSizeMappingIndex, mapping } = this;
let baseSize = formattedSize;
if (currentSizeMappingIndex !== null) {
baseSize = mapping[currentSizeMappingIndex][1];
}
const size = Array.isArray(baseSize[0]) ? baseSize[0] : [baseSize[0], baseSize[1]];
const [width, height] = size;
return {
margin: '0 auto',
width: `${width}px`,
height: `${height}px`,
border: '1px solid black',
};
}
return null;
},
},
methods: {
/**
* Formats a given size to make it compatible with GPT
* If size is an Array, it is returned as is
* If size is a string, it is formatted so that 123x456 becomes [123, 456]
*
* @param {Array,string} size The size
* @return {Array} Formatted size
*/
formatSize(size) {
if (Array.isArray(size)) {
return size;
}
if (typeof size === 'string') {
return size.split('x').map(value => parseInt(value, 10));
}
return [];
},
/**
* Formats a given list of sizes to make it compatible with GPT API
* If sizesList is an Array, it is returned as is
* If sizesList is a string, it is formatted so that
* 123x456,654x321 becomes [[123, 456], [654, 321]]
*
* @param {Array,string} sizesList The sizes
* @return {Array} Formatted sizes list
*/
formatSizeList(sizesList) {
if (Array.isArray(sizesList)) {
return sizesList;
}
if (typeof sizesList === 'string') {
return sizesList
.split(',')
.map(size => this.formatSize(size));
}
return [];
},
/**
* Refresh ad slot
*/
refreshSlot() {
googletag.pubads().refresh([this.adSlot]);
},
handleSlotRenderEnded (event) {
if (event.slot.getSlotId().getDomId() !== this.divId) {
return;
}
this.isEmpty = !!event.isEmpty;
},
/**
* Window resize event listener
* Attached only when responsive mode is enabled, it checks wether a different size
* mapping can be activated after resize and forces the slot to be refreshed if it's
* the case
*/
handleWindowResize() {
const { windowResizeDebounce } = this;
clearTimeout(this.windowResizeListenerDebounce);
this.windowResizeListenerDebounce = setTimeout(() => {
const currentSizeMappingIndex = this.getCurrentSizeMappingIndex();
if (currentSizeMappingIndex !== this.currentSizeMappingIndex) {
if (!this.ghostMode) {
this.refreshSlot();
}
this.currentSizeMappingIndex = currentSizeMappingIndex;
}
}, windowResizeDebounce);
},
/**
* Gets the current size mapping index
*
* @return {Number} The current size mapping index
*/
getCurrentSizeMappingIndex() {
const mapping = this.mapping || [];
let index = null;
mapping.some((size, i) => {
const [browserSize] = size;
const [width, height] = browserSize;
const mediaQuery = `(min-width: ${width}px) and (min-height: ${height}px)`;
if (window.matchMedia(mediaQuery).matches) {
index = i;
return true;
}
return false;
});
return index;
},
},
mounted() {
if (!window.googletag) {
return;
}
const {
ghostMode,
adUnitPath,
divId,
sizeMapping,
isResponsive,
collapseEmptyDiv,
} = this;
// Init Ad slot
googletag.cmd.push(() => {
const pubadsService = googletag.pubads()
pubadsService.addEventListener('slotRenderEnded', this.handleSlotRenderEnded);
pubadsService.setTargeting('path', this.$route.path);
const adSlot = googletag
.defineSlot(adUnitPath, this.formattedSize, divId)
.addService(pubadsService);
// Collapse empty div slot-level override
if (collapseEmptyDiv !== null) {
adSlot.setCollapseEmptyDiv(collapseEmptyDiv);
}
// Build size mapping if any
if (sizeMapping.length > 0) {
const mapping = googletag.sizeMapping();
sizeMapping.forEach((size) => {
const browserSize = this.formatSize(size[0]);
const adSizes = this.formatSizeList(size[1]);
mapping.addSize(browserSize, adSizes);
this.mapping.push([browserSize, adSizes]);
});
adSlot.defineSizeMapping(mapping.build());
}
// Init responsive behavior
if (this.sizeMapping.length > 0 && isResponsive) {
const currentSizeMappingIndex = this.getCurrentSizeMappingIndex();
this.currentSizeMappingIndex = currentSizeMappingIndex;
window.addEventListener('resize', this.handleWindowResize);
}
this.adSlot = adSlot;
this.$gptAds.slots.push(adSlot);
if (!this.ghostMode) {
googletag.display(divId);
if (this.$gptAds.individualRefresh) {
this.refreshSlot();
}
}
});
},
beforeDestroy() {
if (!googletag) {
return;
}
// Destroy ad slot
googletag.cmd.push(() => {
const destroyed = googletag.destroySlots([this.adSlot]);
});
// Remove window resize listener
window.removeEventListener('resize', this.handleWindowResize);
},
render(h) {
const { divId, style, isEmpty } = this;
let classAttr = isEmpty ? '<%= options.emptyClass %>' : '';
return h('div', {
style,
attrs: {
id: divId,
class: classAttr,
},
domProps: { innerHTML: '' },
});
},
};

View File

@ -0,0 +1,111 @@
import Vue from 'vue';
function isPersonalizedAdsOn(ctx) {
let cookies = null
if (ctx.req != null) {
//Server side rendering
cookies = ctx.req.headers.cookie;
} else {
// Rely on the client
cookies = document.cookie;
}
let processed = {}
cookies.split(';').forEach((e) => {
let val = e.trim().split('=')
processed[val[0]] = decodeURI(val[1])
})
let scopes = decodeURIComponent(processed['modrinth-scopes']).split(",")
return (scopes !== null && scopes.includes('ads'))
}
export default async function (ctx, inject) {
const { app } = ctx;
const config = ctx.$config && ctx.$config.ads || {};
// Module options
const debug = config.debug ?? <%= options.debug || false %>;
const individualRefresh = config.individualRefresh ?? <%= options.individualRefresh || false %>;
const collapseEmptyDivs = config.collapseEmptyDivs ?? <%= options.collapseEmptyDivs || false %>;
const GeoEdgeId = config.GeoEdgeId ?? '<%= options.geoEdgeId %>';
const networkCode = config.networkCode ?? '<%= options.networkCode %>';
const GPT_LIB_SCRIPT_ID = '<%= options.GPT_LIB_SCRIPT_ID %>';
const GPT_INIT_SCRIPT_ID = '<%= options.GPT_INIT_SCRIPT_ID %>';
const GEOEDGE_CONF_SCRIPT_ID = '<%= options.GEOEDGE_CONF_SCRIPT_ID %>';
const GEOEDGE_LIB_SCRIPT_ID = '<%= options.GEOEDGE_LIB_SCRIPT_ID %>';
// Instance options
const gptAdsOptions = {
networkCode,
individualRefresh,
slots: [],
};
const injectScript = (script) => {
const scriptIndex = ctx.app.head.script.findIndex(s => s.hid === script.hid);
if (scriptIndex !== -1) {
ctx.app.head.script[scriptIndex] = script;
} else {
ctx.app.head.script.push(script);
}
};
let no_consent = !isPersonalizedAdsOn(ctx)
// GeoEdge support
if (GeoEdgeId !== '') {
// Unfortunately these lines are needed to prevent vue-meta from esacping quotes in the init script
ctx.app.head.__dangerouslyDisableSanitizersByTagID = ctx.app.head.__dangerouslyDisableSanitizersByTagID || {}
ctx.app.head.__dangerouslyDisableSanitizersByTagID[GEOEDGE_CONF_SCRIPT_ID] = ['innerHTML']
const geoEdgeConfig = {
hid: GEOEDGE_CONF_SCRIPT_ID,
innerHTML: "window.grumi = { key: '" + encodeURIComponent(GeoEdgeId) +"'};"
};
injectScript(geoEdgeConfig);
const geoEdgeImport = {
hid: GEOEDGE_LIB_SCRIPT_ID,
src: `https://rumcdn.geoedge.be/${GeoEdgeId}/grumi-ip.js`,
async: true,
};
injectScript(geoEdgeImport)
}
// Inject GPT lib
const gptLibScript = {
hid: GPT_LIB_SCRIPT_ID,
src: 'https://www.googletagservices.com/tag/js/gpt.js',
async: true,
};
injectScript(gptLibScript);
// Inject GPT init script
let gptInitScriptHtml = 'var googletag = googletag || {};googletag.cmd = googletag.cmd || [];';
if (debug) {
gptInitScriptHtml += 'googletag.cmd.push(function(){googletag.openConsole();});';
}
// Disable initial load
const gptDisableInitialLoad = individualRefresh ? 'googletag.pubads().disableInitialLoad();' : '';
// Collapse empty div
const gptCollapseEmptyDivs = collapseEmptyDivs ? 'googletag.pubads().collapseEmptyDivs();' : '';
// Desactivate personalization
const gptDisablePersonalization = no_consent ? 'googletag.pubads().setRequestNonPersonalizedAds(1);' : '';
gptInitScriptHtml += `
googletag.cmd.push(function(){
googletag.pubads().enableSingleRequest();
${gptDisableInitialLoad}
${gptCollapseEmptyDivs}
${gptDisablePersonalization}
googletag.enableServices();
});
`;
const gptInitScript = {
hid: GPT_INIT_SCRIPT_ID,
innerHTML: gptInitScriptHtml,
};
injectScript(gptInitScript);
const component = require('./component.js');
Vue.component('<%= options.componentName %>', component.default || component);
inject('gptAds', gptAdsOptions);
}

View File

@ -0,0 +1,35 @@
{
"name": "@ax2/gpt-ads-module",
"version": "0.6.0",
"description": "Google Publisher Tag ads integration for Nuxt",
"license": "MIT",
"contributors": [
{
"name": "Paul Gascou-Vaillancourt <paul@ax2.ca>"
}
],
"main": "lib/module.js",
"repository": "https://github.com/ax2inc/nuxt-modules",
"homepage": "https://github.com/ax2inc/nuxt-modules/tree/master/packages/gpt-ads#readme",
"publishConfig": {
"access": "public"
},
"files": [
"lib"
],
"devDependencies": {
"codecov": "latest",
"eslint": "5.13.0",
"eslint-config-airbnb-base": "13.1.0",
"eslint-loader": "2.1.2",
"eslint-plugin-import": "2.16.0",
"eslint-plugin-jest": "22.3.0",
"eslint-plugin-vue": "5.2.1",
"jest": "24.1.0",
"jsdom": "14.0.0",
"nuxt": "2.4.3",
"request-promise-native": "1.0.5",
"standard-version": "latest"
},
"gitHead": "db930bf38f5eec5696ad7978a68656436831fc59"
}

View File

@ -61,14 +61,7 @@ export default {
'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;800&display=swap',
},
],
script: [
{
src: 'https://analytics.modrinth.com/umami.js',
'data-website-id': 'c37613de-245d-4767-90e7-ba7980a4f1a2',
async: true,
defer: true,
},
],
script: [],
},
vue: {
@ -78,7 +71,7 @@ export default {
},
},
router: {
middleware: 'auth',
middleware: ['auth', 'ads_tracking'],
},
/*
** Global CSS
@ -119,9 +112,20 @@ export default {
'@nuxtjs/robots',
'@nuxtjs/sitemap',
'@nuxtjs/style-resources',
'@nuxtjs/google-adsense',
'cookie-universal-nuxt',
'~/modules/gpt-ads',
'~/modules/analytics',
],
ads: {
// Module options
ghostMode: true,
geoEdgeId: '',
},
analytics: {
enabled: false,
script_url: '',
tracking_code: '',
},
robots: {
Sitemap: 'https://modrinth.com/sitemap.xml',
},
@ -139,9 +143,6 @@ export default {
},
},
},
'google-adsense': {
id: 'ca-pub-4615302805870170',
},
dayjs: {
locales: ['en'],
defaultLocale: 'en',
@ -170,4 +171,16 @@ export default {
env: {
version: process.env.VERSION_ID || 'unknown',
},
publicRuntimeConfig: {
ads: {
ghostMode: process.env.ENABLE_ADS == null,
GeoEdgeId: process.env.GEOEDGE_ID,
networkCode: process.env.GAM_ID,
},
analytics: {
enabled: process.env.ENABLE_ANALYTICS,
script_url: process.env.ANALYTICS_URL,
tracking_code: process.env.ANALYTICS_ID,
},
},
}

671
package-lock.json generated
View File

@ -5,7 +5,6 @@
"requires": true,
"packages": {
"": {
"name": "knossos",
"version": "1.0.0",
"dependencies": {
"@nuxtjs/axios": "^5.12.5",
@ -26,6 +25,7 @@
"xss": "^1.0.8"
},
"devDependencies": {
"@nuxt/types": "^2.14.12",
"@nuxtjs/color-mode": "^1.1.1",
"@nuxtjs/eslint-config": "^3.1.0",
"@nuxtjs/eslint-module": "^2.0.0",
@ -2106,6 +2106,61 @@
"resolved": "https://registry.npmjs.org/defu/-/defu-2.0.4.tgz",
"integrity": "sha512-G9pEH1UUMxShy6syWk01VQSRVs3CDWtlxtZu7A+NyqjxaCA4gSlWAKDBx6QiUEKezqS8+DUlXLI14Fp05Hmpwg=="
},
"node_modules/@nuxt/types": {
"version": "2.14.12",
"resolved": "https://registry.npmjs.org/@nuxt/types/-/types-2.14.12.tgz",
"integrity": "sha512-x58uEVygHual/kHDTrLAwXjKNYn+5udR4HJOmHd2gXgYonZu8E2UpsShIkyMRZ0nRoEAZ72i4OfcHKqGsVSI6w==",
"dev": true,
"dependencies": {
"@types/autoprefixer": "^9.7.2",
"@types/babel__core": "^7.1.12",
"@types/compression": "^1.7.0",
"@types/connect": "^3.4.33",
"@types/etag": "^1.8.0",
"@types/file-loader": "^4.2.0",
"@types/html-minifier": "^4.0.0",
"@types/less": "^3.0.1",
"@types/node": "^12.19.8",
"@types/node-sass": "^4.11.1",
"@types/optimize-css-assets-webpack-plugin": "^5.0.1",
"@types/pug": "^2.0.4",
"@types/serve-static": "^1.13.8",
"@types/terser-webpack-plugin": "^2.2.0",
"@types/webpack": "^4.41.25",
"@types/webpack-bundle-analyzer": "^3.9.0",
"@types/webpack-dev-middleware": "^3.7.2",
"@types/webpack-hot-middleware": "^2.25.3"
}
},
"node_modules/@nuxt/types/node_modules/@types/node": {
"version": "12.20.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.7.tgz",
"integrity": "sha512-gWL8VUkg8VRaCAUgG9WmhefMqHmMblxe2rVpMF86nZY/+ZysU+BkAp+3cz03AixWDSSz0ks5WX59yAhv/cDwFA==",
"dev": true
},
"node_modules/@nuxt/types/node_modules/@types/webpack": {
"version": "4.41.27",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.27.tgz",
"integrity": "sha512-wK/oi5gcHi72VMTbOaQ70VcDxSQ1uX8S2tukBK9ARuGXrYM/+u4ou73roc7trXDNmCxCoerE8zruQqX/wuHszA==",
"dev": true,
"dependencies": {
"@types/anymatch": "*",
"@types/node": "*",
"@types/tapable": "^1",
"@types/uglify-js": "*",
"@types/webpack-sources": "*",
"source-map": "^0.6.0"
}
},
"node_modules/@nuxt/types/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@nuxt/utils": {
"version": "2.14.7",
"resolved": "https://registry.npmjs.org/@nuxt/utils/-/utils-2.14.7.tgz",
@ -2504,11 +2559,170 @@
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA=="
},
"node_modules/@types/autoprefixer": {
"version": "9.7.2",
"resolved": "https://registry.npmjs.org/@types/autoprefixer/-/autoprefixer-9.7.2.tgz",
"integrity": "sha512-QX7U7YW3zX3ex6MECtWO9folTGsXeP4b8bSjTq3I1ODM+H+sFHwGKuof+T+qBcDClGlCGtDb3SVfiTVfmcxw4g==",
"dev": true,
"dependencies": {
"@types/browserslist": "*",
"postcss": "7.x.x"
}
},
"node_modules/@types/babel__core": {
"version": "7.1.14",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz",
"integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0",
"@types/babel__generator": "*",
"@types/babel__template": "*",
"@types/babel__traverse": "*"
}
},
"node_modules/@types/babel__generator": {
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz",
"integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==",
"dev": true,
"dependencies": {
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__template": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz",
"integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__traverse": {
"version": "7.11.1",
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz",
"integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==",
"dev": true,
"dependencies": {
"@babel/types": "^7.3.0"
}
},
"node_modules/@types/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
"dev": true,
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
}
},
"node_modules/@types/browserslist": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@types/browserslist/-/browserslist-4.15.0.tgz",
"integrity": "sha512-h9LyKErRGZqMsHh9bd+FE8yCIal4S0DxKTOeui56VgVXqa66TKiuaIUxCAI7c1O0LjaUzOTcsMyOpO9GetozRA==",
"dev": true,
"dependencies": {
"browserslist": "*"
}
},
"node_modules/@types/clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.3.tgz",
"integrity": "sha512-ET0ldU/vpXecy5vO8JRIhtJWSrk1vzXdJcp3Bjf8bARZynl6vfkhEKY/A7njfNIRlmyTGuVFuqnD6I3tOGdXpQ==",
"dev": true,
"dependencies": {
"@types/node": "*",
"source-map": "^0.6.0"
}
},
"node_modules/@types/clean-css/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@types/compression": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.0.tgz",
"integrity": "sha512-3LzWUM+3k3XdWOUk/RO+uSjv7YWOatYq2QADJntK1pjkk4DfVP0KrIEPDnXRJxAAGKe0VpIPRmlINLDuCedZWw==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/connect": {
"version": "3.4.34",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz",
"integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/cookie": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
"integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
},
"node_modules/@types/etag": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@types/etag/-/etag-1.8.0.tgz",
"integrity": "sha512-EdSN0x+Y0/lBv7YAb8IU4Jgm6DWM+Bqtz7o5qozl96fzaqdqbdfHS5qjdpFeIv7xQ8jSLyjMMNShgYtMajEHyQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/express": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz",
"integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==",
"dev": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.18",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"node_modules/@types/express-serve-static-core": {
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz",
"integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*"
}
},
"node_modules/@types/file-loader": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@types/file-loader/-/file-loader-4.2.1.tgz",
"integrity": "sha512-ImtIwnIEEMgyE7DK1JduhiDv+8WzfRWb3BPuf6RiBD1ySz05vyDRhGiKvIcuUPxUzMNBRZHN0pB+bWXSX3+t1w==",
"dev": true,
"dependencies": {
"@types/webpack": "^4"
}
},
"node_modules/@types/html-minifier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-eFnGhrKmjWBlnSGNtunetE3UU2Tc/LUl92htFslSSTmpp9EKHQVcYQadCyYfnzUEFB5G/3wLWo/USQS/mEPKrA==",
"dev": true,
"dependencies": {
"@types/clean-css": "*",
"@types/relateurl": "*",
"@types/uglify-js": "*"
}
},
"node_modules/@types/html-minifier-terser": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
@ -2533,22 +2747,85 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"node_modules/@types/less": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/less/-/less-3.0.2.tgz",
"integrity": "sha512-62vfe65cMSzYaWmpmhqCMMNl0khen89w57mByPi1OseGfcV/LV03fO8YVrNj7rFQsRWNJo650WWyh6m7p8vZmA==",
"dev": true
},
"node_modules/@types/memory-fs": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@types/memory-fs/-/memory-fs-0.3.3.tgz",
"integrity": "sha512-rLEYzl1xODshz+Lm+YX8NYws8Xw7/qcYbQInMkotl96VpLZmUvoCfYYGxfajMSiugANV02QO5Fc+R98KKeE4gQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
"dev": true
},
"node_modules/@types/node": {
"version": "14.6.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.2.tgz",
"integrity": "sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A=="
},
"node_modules/@types/node-sass": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@types/node-sass/-/node-sass-4.11.1.tgz",
"integrity": "sha512-wPOmOEEtbwQiPTIgzUuRSQZ3H5YHinsxRGeZzPSDefAm4ylXWnZG9C0adses8ymyplKK0gwv3JkDNO8GGxnWfg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"node_modules/@types/optimize-css-assets-webpack-plugin": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@types/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz",
"integrity": "sha512-PJgbI4KplJfyxKWVrBbEL+rePEBqeozJRMT0mBL3ynhvngASBV/XJ+BneLuJN74RjjMzO0gA5ns80mgubQdZAA==",
"dev": true,
"dependencies": {
"@types/webpack": "^4"
}
},
"node_modules/@types/pug": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz",
"integrity": "sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=",
"dev": true
},
"node_modules/@types/q": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
},
"node_modules/@types/qs": {
"version": "6.9.6",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz",
"integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==",
"dev": true
},
"node_modules/@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
"dev": true
},
"node_modules/@types/relateurl": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.28.tgz",
"integrity": "sha1-a9p9uGU/piZD9e5p6facEaOS46Y=",
"dev": true
},
"node_modules/@types/sax": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.1.tgz",
@ -2557,6 +2834,16 @@
"@types/node": "*"
}
},
"node_modules/@types/serve-static": {
"version": "1.13.9",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz",
"integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==",
"dev": true,
"dependencies": {
"@types/mime": "^1",
"@types/node": "*"
}
},
"node_modules/@types/source-list-map": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
@ -2567,6 +2854,16 @@
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz",
"integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA=="
},
"node_modules/@types/terser-webpack-plugin": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@types/terser-webpack-plugin/-/terser-webpack-plugin-2.2.2.tgz",
"integrity": "sha512-cesfeuYsaBJwGVvhJC0b8bgKnsxeNT1MkEpy1BhLrEgi1U1SATenWew2J8vr9aZLRcpf6D3T+ReVtoq0vGswuQ==",
"dev": true,
"dependencies": {
"@types/webpack": "^4",
"terser": "^4.3.9"
}
},
"node_modules/@types/uglify-js": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.11.0.tgz",
@ -2596,6 +2893,37 @@
"source-map": "^0.6.0"
}
},
"node_modules/@types/webpack-bundle-analyzer": {
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.2.tgz",
"integrity": "sha512-+LirhEpWEPRMyOW0HCy/lTTGzfdEWL26ximknO+/oaAQkigJJktxMr/QE7EWVNMRv+hpwW20Mv3YfLs42q2kjA==",
"dev": true,
"dependencies": {
"@types/webpack": "^4"
}
},
"node_modules/@types/webpack-dev-middleware": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/webpack-dev-middleware/-/webpack-dev-middleware-3.7.4.tgz",
"integrity": "sha512-NV7qwBHYYOxgUo3PB12bBngpW7I4JY2AZTVLszmeNkmGH8DCgBTXKyc9t9KLaaATE07A9bXeJmyHFaE5OiV6mA==",
"dev": true,
"dependencies": {
"@types/connect": "*",
"@types/memory-fs": "*",
"@types/webpack": "^4",
"loglevel": "^1.6.2"
}
},
"node_modules/@types/webpack-hot-middleware": {
"version": "2.25.4",
"resolved": "https://registry.npmjs.org/@types/webpack-hot-middleware/-/webpack-hot-middleware-2.25.4.tgz",
"integrity": "sha512-6tQb9EBKIANZYUVLQYWiWfDFVe7FhXSj4bB2EF5QB7VtYWL3HDR+y/zqjZPAnCorv0spLqVMRqjRK8AmhfocMw==",
"dev": true,
"dependencies": {
"@types/connect": "*",
"@types/webpack": "^4"
}
},
"node_modules/@types/webpack-sources": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.0.0.tgz",
@ -9375,6 +9703,15 @@
"integrity": "sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=",
"dev": true
},
"node_modules/loglevel": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz",
"integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==",
"dev": true,
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/loud-rejection": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
@ -18446,6 +18783,60 @@
}
}
},
"@nuxt/types": {
"version": "2.14.12",
"resolved": "https://registry.npmjs.org/@nuxt/types/-/types-2.14.12.tgz",
"integrity": "sha512-x58uEVygHual/kHDTrLAwXjKNYn+5udR4HJOmHd2gXgYonZu8E2UpsShIkyMRZ0nRoEAZ72i4OfcHKqGsVSI6w==",
"dev": true,
"requires": {
"@types/autoprefixer": "^9.7.2",
"@types/babel__core": "^7.1.12",
"@types/compression": "^1.7.0",
"@types/connect": "^3.4.33",
"@types/etag": "^1.8.0",
"@types/file-loader": "^4.2.0",
"@types/html-minifier": "^4.0.0",
"@types/less": "^3.0.1",
"@types/node": "^12.19.8",
"@types/node-sass": "^4.11.1",
"@types/optimize-css-assets-webpack-plugin": "^5.0.1",
"@types/pug": "^2.0.4",
"@types/serve-static": "^1.13.8",
"@types/terser-webpack-plugin": "^2.2.0",
"@types/webpack": "^4.41.25",
"@types/webpack-bundle-analyzer": "^3.9.0",
"@types/webpack-dev-middleware": "^3.7.2",
"@types/webpack-hot-middleware": "^2.25.3"
},
"dependencies": {
"@types/node": {
"version": "12.20.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.7.tgz",
"integrity": "sha512-gWL8VUkg8VRaCAUgG9WmhefMqHmMblxe2rVpMF86nZY/+ZysU+BkAp+3cz03AixWDSSz0ks5WX59yAhv/cDwFA==",
"dev": true
},
"@types/webpack": {
"version": "4.41.27",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.27.tgz",
"integrity": "sha512-wK/oi5gcHi72VMTbOaQ70VcDxSQ1uX8S2tukBK9ARuGXrYM/+u4ou73roc7trXDNmCxCoerE8zruQqX/wuHszA==",
"dev": true,
"requires": {
"@types/anymatch": "*",
"@types/node": "*",
"@types/tapable": "^1",
"@types/uglify-js": "*",
"@types/webpack-sources": "*",
"source-map": "^0.6.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"@nuxt/utils": {
"version": "2.14.7",
"resolved": "https://registry.npmjs.org/@nuxt/utils/-/utils-2.14.7.tgz",
@ -18810,11 +19201,169 @@
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA=="
},
"@types/autoprefixer": {
"version": "9.7.2",
"resolved": "https://registry.npmjs.org/@types/autoprefixer/-/autoprefixer-9.7.2.tgz",
"integrity": "sha512-QX7U7YW3zX3ex6MECtWO9folTGsXeP4b8bSjTq3I1ODM+H+sFHwGKuof+T+qBcDClGlCGtDb3SVfiTVfmcxw4g==",
"dev": true,
"requires": {
"@types/browserslist": "*",
"postcss": "7.x.x"
}
},
"@types/babel__core": {
"version": "7.1.14",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz",
"integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==",
"dev": true,
"requires": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0",
"@types/babel__generator": "*",
"@types/babel__template": "*",
"@types/babel__traverse": "*"
}
},
"@types/babel__generator": {
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz",
"integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==",
"dev": true,
"requires": {
"@babel/types": "^7.0.0"
}
},
"@types/babel__template": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz",
"integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==",
"dev": true,
"requires": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0"
}
},
"@types/babel__traverse": {
"version": "7.11.1",
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz",
"integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==",
"dev": true,
"requires": {
"@babel/types": "^7.3.0"
}
},
"@types/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/browserslist": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@types/browserslist/-/browserslist-4.15.0.tgz",
"integrity": "sha512-h9LyKErRGZqMsHh9bd+FE8yCIal4S0DxKTOeui56VgVXqa66TKiuaIUxCAI7c1O0LjaUzOTcsMyOpO9GetozRA==",
"dev": true,
"requires": {
"browserslist": "*"
}
},
"@types/clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.3.tgz",
"integrity": "sha512-ET0ldU/vpXecy5vO8JRIhtJWSrk1vzXdJcp3Bjf8bARZynl6vfkhEKY/A7njfNIRlmyTGuVFuqnD6I3tOGdXpQ==",
"dev": true,
"requires": {
"@types/node": "*",
"source-map": "^0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"@types/compression": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.0.tgz",
"integrity": "sha512-3LzWUM+3k3XdWOUk/RO+uSjv7YWOatYq2QADJntK1pjkk4DfVP0KrIEPDnXRJxAAGKe0VpIPRmlINLDuCedZWw==",
"dev": true,
"requires": {
"@types/express": "*"
}
},
"@types/connect": {
"version": "3.4.34",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz",
"integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/cookie": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
"integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
},
"@types/etag": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@types/etag/-/etag-1.8.0.tgz",
"integrity": "sha512-EdSN0x+Y0/lBv7YAb8IU4Jgm6DWM+Bqtz7o5qozl96fzaqdqbdfHS5qjdpFeIv7xQ8jSLyjMMNShgYtMajEHyQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/express": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz",
"integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==",
"dev": true,
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.18",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz",
"integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*"
}
},
"@types/file-loader": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@types/file-loader/-/file-loader-4.2.1.tgz",
"integrity": "sha512-ImtIwnIEEMgyE7DK1JduhiDv+8WzfRWb3BPuf6RiBD1ySz05vyDRhGiKvIcuUPxUzMNBRZHN0pB+bWXSX3+t1w==",
"dev": true,
"requires": {
"@types/webpack": "^4"
}
},
"@types/html-minifier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-eFnGhrKmjWBlnSGNtunetE3UU2Tc/LUl92htFslSSTmpp9EKHQVcYQadCyYfnzUEFB5G/3wLWo/USQS/mEPKrA==",
"dev": true,
"requires": {
"@types/clean-css": "*",
"@types/relateurl": "*",
"@types/uglify-js": "*"
}
},
"@types/html-minifier-terser": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
@ -18839,22 +19388,85 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"@types/less": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/less/-/less-3.0.2.tgz",
"integrity": "sha512-62vfe65cMSzYaWmpmhqCMMNl0khen89w57mByPi1OseGfcV/LV03fO8YVrNj7rFQsRWNJo650WWyh6m7p8vZmA==",
"dev": true
},
"@types/memory-fs": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@types/memory-fs/-/memory-fs-0.3.3.tgz",
"integrity": "sha512-rLEYzl1xODshz+Lm+YX8NYws8Xw7/qcYbQInMkotl96VpLZmUvoCfYYGxfajMSiugANV02QO5Fc+R98KKeE4gQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
"dev": true
},
"@types/node": {
"version": "14.6.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.2.tgz",
"integrity": "sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A=="
},
"@types/node-sass": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@types/node-sass/-/node-sass-4.11.1.tgz",
"integrity": "sha512-wPOmOEEtbwQiPTIgzUuRSQZ3H5YHinsxRGeZzPSDefAm4ylXWnZG9C0adses8ymyplKK0gwv3JkDNO8GGxnWfg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"@types/optimize-css-assets-webpack-plugin": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@types/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz",
"integrity": "sha512-PJgbI4KplJfyxKWVrBbEL+rePEBqeozJRMT0mBL3ynhvngASBV/XJ+BneLuJN74RjjMzO0gA5ns80mgubQdZAA==",
"dev": true,
"requires": {
"@types/webpack": "^4"
}
},
"@types/pug": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz",
"integrity": "sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=",
"dev": true
},
"@types/q": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
},
"@types/qs": {
"version": "6.9.6",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz",
"integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==",
"dev": true
},
"@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
"dev": true
},
"@types/relateurl": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.28.tgz",
"integrity": "sha1-a9p9uGU/piZD9e5p6facEaOS46Y=",
"dev": true
},
"@types/sax": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.1.tgz",
@ -18863,6 +19475,16 @@
"@types/node": "*"
}
},
"@types/serve-static": {
"version": "1.13.9",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz",
"integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==",
"dev": true,
"requires": {
"@types/mime": "^1",
"@types/node": "*"
}
},
"@types/source-list-map": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
@ -18873,6 +19495,16 @@
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz",
"integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA=="
},
"@types/terser-webpack-plugin": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@types/terser-webpack-plugin/-/terser-webpack-plugin-2.2.2.tgz",
"integrity": "sha512-cesfeuYsaBJwGVvhJC0b8bgKnsxeNT1MkEpy1BhLrEgi1U1SATenWew2J8vr9aZLRcpf6D3T+ReVtoq0vGswuQ==",
"dev": true,
"requires": {
"@types/webpack": "^4",
"terser": "^4.3.9"
}
},
"@types/uglify-js": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.11.0.tgz",
@ -18908,6 +19540,37 @@
}
}
},
"@types/webpack-bundle-analyzer": {
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.2.tgz",
"integrity": "sha512-+LirhEpWEPRMyOW0HCy/lTTGzfdEWL26ximknO+/oaAQkigJJktxMr/QE7EWVNMRv+hpwW20Mv3YfLs42q2kjA==",
"dev": true,
"requires": {
"@types/webpack": "^4"
}
},
"@types/webpack-dev-middleware": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/webpack-dev-middleware/-/webpack-dev-middleware-3.7.4.tgz",
"integrity": "sha512-NV7qwBHYYOxgUo3PB12bBngpW7I4JY2AZTVLszmeNkmGH8DCgBTXKyc9t9KLaaATE07A9bXeJmyHFaE5OiV6mA==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/memory-fs": "*",
"@types/webpack": "^4",
"loglevel": "^1.6.2"
}
},
"@types/webpack-hot-middleware": {
"version": "2.25.4",
"resolved": "https://registry.npmjs.org/@types/webpack-hot-middleware/-/webpack-hot-middleware-2.25.4.tgz",
"integrity": "sha512-6tQb9EBKIANZYUVLQYWiWfDFVe7FhXSj4bB2EF5QB7VtYWL3HDR+y/zqjZPAnCorv0spLqVMRqjRK8AmhfocMw==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/webpack": "^4"
}
},
"@types/webpack-sources": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.0.0.tgz",
@ -24555,6 +25218,12 @@
"integrity": "sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=",
"dev": true
},
"loglevel": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz",
"integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==",
"dev": true
},
"loud-rejection": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",

View File

@ -8,7 +8,7 @@
"start": "nuxt start",
"export": "nuxt export",
"serve": "nuxt serve",
"lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .",
"lint:js": "eslint --ext .js,.vue --ignore-path .eslintignore .",
"lint": "npm run lint:js"
},
"dependencies": {
@ -30,6 +30,7 @@
"xss": "^1.0.8"
},
"devDependencies": {
"@nuxt/types": "^2.14.12",
"@nuxtjs/color-mode": "^1.1.1",
"@nuxtjs/eslint-config": "^3.1.0",
"@nuxtjs/eslint-module": "^2.0.0",

View File

@ -2,7 +2,7 @@
<div class="page-container">
<div class="page-contents">
<div class="sidebar-l">
<div class="card page-nav">
<div v-if="$auth.user != null" class="card page-nav">
<nuxt-link :to="'/dashboard/projects'" class="tab last">
<ModIcon />
My mods
@ -30,6 +30,16 @@
Settings
</nuxt-link>
</div>
<div v-else class="card page-nav">
<a :href="authUrl" class="tab last">
<UserIcon />
Log in
</a>
<nuxt-link :to="'/dashboard/privacy'" class="tab last">
<SettingsIcon />
Privacy Settings
</nuxt-link>
</div>
<m-footer class="footer" />
</div>
<div class="content">
@ -44,6 +54,7 @@ import ModerationIcon from '~/assets/images/sidebar/admin.svg?inline'
import SettingsIcon from '~/assets/images/sidebar/settings.svg?inline'
import NotificationsIcon from '~/assets/images/sidebar/notifications.svg?inline'
import FollowIcon from '~/assets/images/utils/heart.svg?inline'
import UserIcon from '~/assets/images/utils/user.svg?inline'
export default {
name: 'DashboardPage',
@ -53,6 +64,12 @@ export default {
SettingsIcon,
NotificationsIcon,
FollowIcon,
UserIcon,
},
computed: {
authUrl() {
return `https://api.modrinth.com/api/v1/auth/init?url=https://modrinth.com${this.$route.fullPath}`
},
},
}
</script>

161
pages/dashboard/privacy.vue Normal file
View File

@ -0,0 +1,161 @@
<!--suppress HtmlFormInputWithoutLabel -->
<template>
<div class="popup card">
<div class="consent-container">
<div class="h1">Tweak your privacy settings</div>
<div>
Modrinth relies on different providers and in-house tools to allow us to
provide custom-tailored experiences, and personalized advertising. You
can at any moment change your privacy settings by going to the setting
page, or at the footer of any page.
</div>
<br class="divider" />
<div class="toggles">
<div v-for="(scope, id) in scopes" :key="id" class="toggle">
<div class="toggle-text">
<div class="title">{{ scope.title }}</div>
<div class="contents">
{{ scope.description }}
</div>
</div>
<div class="spacer"></div>
<div class="toggle-action">
<label :for="id"></label>
<input
:id="id"
ref="toggles"
v-model="scopes[id].value"
type="checkbox"
class="switch stylized-toggle"
/>
</div>
</div>
</div>
</div>
<div class="actions">
<button class="btn button" @click="toggleOff">Refuse All</button>
<button class="btn button" @click="toggleOn">Accept All</button>
<button class="btn brand-button" @click="confirm">
Confirm my choices
</button>
</div>
</div>
</template>
<script>
/* eslint-disable require-await */
import scopes from '@/privacy-toggles'
export default {
name: 'Privacy',
data: () => {
const settings = scopes.settings
return {
scopes: settings,
}
},
mounted() {
this.$store.dispatch('consent/loadFromCookies', this.$cookies)
// Load the allowed scopes from the store
this.$store.state.consent.scopes_allowed.forEach((scope) => {
if (this.scopes[scope] != null)
this.$set(this.scopes[scope], 'value', true)
})
},
options: {
auth: false,
},
methods: {
toggleOff() {
for (const elem in this.scopes) {
this.$set(this.scopes[elem], 'value', false)
}
},
toggleOn() {
for (const elem in this.scopes) {
this.$set(this.scopes[elem], 'value', true)
}
},
confirm() {
this.$store.commit('consent/set_consent', true)
for (const elem in this.scopes) {
if (this.scopes[elem].value === true) {
this.$store.commit('consent/add_scope', elem)
} else {
this.$store.commit('consent/remove_scope', elem)
}
}
this.$store.dispatch('consent/save', this.$cookies)
},
},
}
</script>
<style scoped lang="scss">
.card {
@extend %card;
padding: var(--spacing-card-lg);
}
.popup {
display: flex;
flex-direction: column;
}
.spacer {
margin-top: 1rem;
}
.actions {
margin-top: 1.5rem;
margin-right: -0.5rem;
display: flex;
flex-direction: row;
justify-content: flex-end;
.btn {
margin-right: 0.5rem;
}
}
.consent-container {
overflow-x: auto;
max-height: 90vh;
@media screen and (min-width: 900px) {
max-height: 50vh;
}
.h1 {
font-size: 2rem;
font-weight: bold;
margin-bottom: 0.6rem;
}
.divider {
margin-top: 1rem;
}
.toggles {
display: flex;
flex-direction: column;
width: 100%;
.toggle {
display: flex;
flex-direction: row;
margin-bottom: 1rem;
.toggle-text {
.title {
color: var(--color-text-dark);
font-weight: bold;
margin-bottom: 0.5rem;
}
.contents {
color: var(--color-text);
}
}
.spacer {
flex-grow: 1;
}
.toggle-action {
margin-left: 1rem;
display: flex;
flex-direction: column;
justify-content: center;
margin-right: 1rem;
}
}
}
}
</style>

View File

@ -63,7 +63,7 @@
></pagination>
</section>
<div class="results column-grow-4">
<Advertisement />
<Advertisement ad-unit="banner" size="728x90,468x60" />
<div v-if="results === null" class="no-results">
<p>Loading...</p>
</div>
@ -279,7 +279,7 @@
@input="toggleLicense"
/>
</div>
<Advertisement format="rectangle" />
<Advertisement ad-unit="square" size="250x250,200x200" />
<m-footer class="footer" />
</section>
</div>
@ -313,7 +313,7 @@ import FabricLoader from '~/assets/images/categories/fabric.svg?inline'
import SearchIcon from '~/assets/images/utils/search.svg?inline'
import ExitIcon from '~/assets/images/utils/exit.svg?inline'
import Advertisement from '~/components/Advertisement'
import Advertisement from '~/components/ads/Advertisement'
export default {
auth: false,

View File

@ -55,11 +55,11 @@
</div>
</div>
</div>
<Advertisement format="rectangle" />
<Advertisement ad-unit="square" size="250x250,200x200" />
<m-footer class="footer" />
</div>
<div class="content">
<Advertisement />
<Advertisement ad-unit="banner" size="728x90,468x60" />
<div class="mods">
<SearchResult
v-for="result in mods"
@ -89,7 +89,7 @@ import MFooter from '~/components/layout/MFooter'
import ReportIcon from '~/assets/images/utils/report.svg?inline'
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
import Advertisement from '~/components/Advertisement'
import Advertisement from '~/components/ads/Advertisement'
export default {
auth: false,

18
privacy-toggles.js Normal file
View File

@ -0,0 +1,18 @@
export default {
settings: {
ads: {
title: 'Allow personalized ads',
description: `Marketing/target cookies are usually used to show you advertisements that meet your interests.
When you visit another website, your browser's cookie is recognized and selected ads are displayed to you
based on the information stored in this cookie.`,
default: false,
},
analytics: {
title: 'Analytics',
description: `Modrinth uses in-house tools that allows us to get insights on how
each user is using the platform, to improve the experience for
everyone.`,
default: true,
},
},
}

59
store/consent.js Normal file
View File

@ -0,0 +1,59 @@
const VERSION = 1
const parameters = {
maxAge: 60 * 60 * 24 * 365 * 10, // Ten years
sameSite: 'Strict',
secure: true,
httpOnly: false,
path: '/',
}
export const state = () => ({
is_consent_given: false,
scopes_allowed: [],
loaded: false,
})
export const mutations = {
loaded(state) {
state.loaded = true
},
set_consent(state, val) {
state.is_consent_given = val
},
add_scope(state, val) {
// Check if the scope is not already provided
if (state.scopes_allowed.includes(val)) return
state.scopes_allowed.push(val)
},
remove_scope(state, val) {
const pos = state.scopes_allowed.findIndex((el) => el === val)
if (pos >= 0) state.scopes_allowed.splice(pos, 1)
},
}
export const actions = {
loadFromCookies(state, $cookies) {
if (state.state.loaded) {
return
}
state.commit('set_consent', $cookies.get('modrinth-consent') === true)
const scopes = $cookies.get('modrinth-scopes')
if (scopes == null) return
scopes.split(',').forEach((elem) => {
state.commit('add_scope', elem)
})
state.commit('loaded')
},
save(state, $cookies) {
$cookies.set('modrinth-consent', state.state.is_consent_given, parameters)
$cookies.set('modrinth-version', VERSION, parameters)
$cookies.set(
'modrinth-scopes',
state.state.scopes_allowed.join(','),
parameters
)
},
}
export const getters = {
is_scope_allowed: (state) => (id) => {
return state.scopes_allowed.contains(id)
},
}