refactor(packages): migrate to yarn workspaces

This commit is contained in:
wanhose 2022-05-05 17:19:35 +02:00
parent 619c53e190
commit 0b9193e3e4
56 changed files with 6089 additions and 237 deletions

24
.commitlintrc Normal file
View File

@ -0,0 +1,24 @@
{
"extends": [
"@commitlint/config-conventional"
],
"rules": {
"scope-case": [
0
],
"header-max-length": [
0,
"always",
110
],
"scope-enum": [
2,
"always",
[
"browser-extension",
"packages",
"report-service"
]
]
}
}

6
.gitignore vendored
View File

@ -1,2 +1,6 @@
.DS_Store .DS_Store
release .pnp.*
.yarn/*
!.yarn/releases
!.yarn/plugins
build

4
.husky/commit-msg Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn commitlint --edit "$1"

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn lint-staged

6
.lintstagedrc Normal file
View File

@ -0,0 +1,6 @@
{
"packages/**/*.{js,ts}": [
"prettier --loglevel=silent --write",
"yarn lint"
]
}

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"files.associations": {
".commitlintrc": "json",
".lintstagedrc": "json",
},
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

785
.yarn/releases/yarn-3.2.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

7
.yarnrc.yml Normal file
View File

@ -0,0 +1,7 @@
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-outdated.cjs
spec: "https://mskelton.dev/yarn-outdated/v2"
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
yarnPath: .yarn/releases/yarn-3.2.0.cjs

View File

@ -2,46 +2,7 @@
A browser extension that eats cookie consent dialogs. A browser extension that eats cookie consent dialogs.
## Downloads ## Repositories
- [Chrome Web Store](https://chrome.google.com/webstore/detail/djcbfpkdhdkaflcigibkbpboflaplabg) - [Browser extension](/packages/browser-extension/)
- [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/cookie-dialog-monster/) - [Report service](/packages/report-service/)
## Compatibility
- Google Chrome 51+.
- Microsoft Edge 15+.
- Mozilla Firefox 54+.
- Opera 38+.
## Installation (on Google Chrome)
1. Clone this repository.
2. Go to [chrome://extensions](chrome://extensions).
3. Enable **Developer mode** by clicking the toggle switch next to **Developer Mode**.
4. Then, click the **LOAD UNPACKED** button and select the cloned folder.
5. That's all, you have a development build of this extension.
## Installation (on Mozilla Firefox)
1. Clone this repository.
2. Go to [about:debugging](about:debugging).
3. Enter **This Firefox** section.
4. Then, click the **LOAD TEMPORARY ADD-ON** button and select any file inside the cloned folder.
5. That's all, you have a development build of this extension.
## Installation (on Microsoft Edge)
1. Clone this repository.
2. Go to [edge://extensions](edge://extensions).
3. Enable **Developer mode** by clicking the toggle switch next to **Developer Mode**.
4. Then, click the **LOAD UNPACKED** button and select the cloned folder.
5. That's all, you have a development build of this extension.
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
## License
[MIT](https://choosealicense.com/licenses/mit/)

21
package.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "cookie-dialog-monster",
"private": true,
"version": "1.0.0",
"scripts": {
"build": "yarn workspaces foreach -p run build",
"lint": "yarn workspaces foreach -p run lint",
"prepare": "husky install"
},
"devDependencies": {
"@commitlint/cli": "^16.2.4",
"@commitlint/config-conventional": "^16.2.4",
"husky": "^7.0.4",
"lint-staged": "^12.4.1",
"prettier": "^2.6.2"
},
"workspaces": [
"packages/*"
],
"packageManager": "yarn@3.2.0"
}

View File

@ -0,0 +1,12 @@
{
"env": {
"es6": true
},
"extends": [
"plugin:prettier/recommended"
],
"parserOptions": {
"ecmaVersion": "latest"
}
}

View File

@ -0,0 +1,45 @@
# Cookie Monster Dialog Browser Extension
## Downloads
- [Chrome Web Store](https://chrome.google.com/webstore/detail/djcbfpkdhdkaflcigibkbpboflaplabg)
- [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/cookie-dialog-monster/)
## Compatibility
- Google Chrome 51+.
- Microsoft Edge 15+.
- Mozilla Firefox 54+.
- Opera 38+.
## Installation (on Google Chrome)
1. Clone this repository.
2. Go to [chrome://extensions](chrome://extensions).
3. Enable **Developer mode** by clicking the toggle switch next to **Developer Mode**.
4. Then, click the **LOAD UNPACKED** button and select the cloned folder.
5. That's all, you have a development build of this extension.
## Installation (on Mozilla Firefox)
1. Clone this repository.
2. Go to [about:debugging](about:debugging).
3. Enter **This Firefox** section.
4. Then, click the **LOAD TEMPORARY ADD-ON** button and select any file inside the cloned folder.
5. That's all, you have a development build of this extension.
## Installation (on Microsoft Edge)
1. Clone this repository.
2. Go to [edge://extensions](edge://extensions).
3. Enable **Developer mode** by clicking the toggle switch next to **Developer Mode**.
4. Then, click the **LOAD UNPACKED** button and select the cloned folder.
5. That's all, you have a development build of this extension.
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
## License
[MIT](https://choosealicense.com/licenses/mit/)

View File

@ -0,0 +1,18 @@
{
"name": "browser-extension",
"version": "1.0.0",
"scripts": {
"build": "exit 1",
"lint": "eslint src/**/*.js --fix"
},
"devDependencies": {
"eslint": "^8.8.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0"
},
"engines": {
"node": "16.x"
},
"packageManager": "yarn@3.2.0",
"license": "MIT"
}

View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1016 B

After

Width:  |  Height:  |  Size: 1016 B

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -2,7 +2,7 @@
* @description Context menu identifier * @description Context menu identifier
*/ */
const contextMenuId = "CDM_MENU"; const contextMenuId = 'CDM_MENU';
/** /**
* @description Cache initial state * @description Cache initial state
@ -18,7 +18,7 @@ const initial = {
* @param {object} [cache] * @param {object} [cache]
*/ */
const check = (cache) => typeof cache.enabled === "boolean"; const check = (cache) => typeof cache.enabled === 'boolean';
/** /**
* @description Disables icon * @description Disables icon
@ -27,7 +27,7 @@ const check = (cache) => typeof cache.enabled === "boolean";
const disableIcon = (tabId) => { const disableIcon = (tabId) => {
chrome.browserAction.setIcon({ chrome.browserAction.setIcon({
path: "assets/icons/disabled.png", path: 'assets/icons/disabled.png',
tabId, tabId,
}); });
}; };
@ -39,7 +39,7 @@ const disableIcon = (tabId) => {
const disablePopup = (tabId) => { const disablePopup = (tabId) => {
chrome.browserAction.setPopup({ chrome.browserAction.setPopup({
popup: "", popup: '',
tabId, tabId,
}); });
}; };
@ -51,7 +51,7 @@ const disablePopup = (tabId) => {
const enableIcon = (tabId) => { const enableIcon = (tabId) => {
chrome.browserAction.setIcon({ chrome.browserAction.setIcon({
path: "assets/icons/enabled.png", path: 'assets/icons/enabled.png',
tabId, tabId,
}); });
}; };
@ -63,7 +63,7 @@ const enableIcon = (tabId) => {
const enablePopup = (tabId) => { const enablePopup = (tabId) => {
chrome.browserAction.setPopup({ chrome.browserAction.setPopup({
popup: "popup.html", popup: 'popup.html',
tabId, tabId,
}); });
}; };
@ -100,13 +100,13 @@ const getCache = (hostname, callback) => {
const getClasses = async (callback) => { const getClasses = async (callback) => {
try { try {
const url = const url =
"https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/master/data/classes.txt"; 'https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/master/data/classes.txt';
const response = await fetch(url); const response = await fetch(url);
const data = await response.text(); const data = await response.text();
if (response.status !== 200) throw new Error(); if (response.status !== 200) throw new Error();
callback({ classes: data.split("\n") }); callback({ classes: data.split('\n') });
} catch { } catch {
callback({ classes: [] }); callback({ classes: [] });
} }
@ -122,13 +122,13 @@ const getClasses = async (callback) => {
const getFixes = async (callback) => { const getFixes = async (callback) => {
try { try {
const url = const url =
"https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/master/data/fixes.txt"; 'https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/master/data/fixes.txt';
const response = await fetch(url); const response = await fetch(url);
const data = await response.text(); const data = await response.text();
if (response.status !== 200) throw new Error(); if (response.status !== 200) throw new Error();
callback({ fixes: data.split("\n") }); callback({ fixes: data.split('\n') });
} catch { } catch {
callback({ fixes: [] }); callback({ fixes: [] });
} }
@ -144,13 +144,13 @@ const getFixes = async (callback) => {
const getSelectors = async (callback) => { const getSelectors = async (callback) => {
try { try {
const url = const url =
"https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/master/data/elements.txt"; 'https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/master/data/elements.txt';
const response = await fetch(url); const response = await fetch(url);
const data = await response.text(); const data = await response.text();
if (response.status !== 200) throw new Error(); if (response.status !== 200) throw new Error();
callback({ selectors: data.split("\n") }); callback({ selectors: data.split('\n') });
} catch { } catch {
callback({ selectors: [] }); callback({ selectors: [] });
} }
@ -182,16 +182,16 @@ const report = () => {
const version = chrome.runtime.getManifest().version; const version = chrome.runtime.getManifest().version;
if (tab) { if (tab) {
fetch("https://cdm-report-service.herokuapp.com/rest/v1/report/", { fetch('https://cdm-report-service.herokuapp.com/rest/v1/report/', {
body: JSON.stringify({ body: JSON.stringify({
text: `There's a problem with ${tab.url} using ${userAgent} in CDM ${version}`, text: `There's a problem with ${tab.url} using ${userAgent} in CDM ${version}`,
to: "wanhose.development@gmail.com", to: 'wanhose.development@gmail.com',
subject: "Cookie Dialog Monster Report", subject: 'Cookie Dialog Monster Report',
}), }),
headers: { headers: {
"Content-type": "application/json", 'Content-type': 'application/json',
}, },
method: "POST", method: 'POST',
}); });
} }
}); });
@ -210,7 +210,7 @@ const updateCache = (hostname, state) => {
chrome.storage.local.set({ chrome.storage.local.set({
[hostname]: { [hostname]: {
enabled: enabled:
typeof state.enabled === "undefined" typeof state.enabled === 'undefined'
? current.enabled ? current.enabled
: state.enabled, : state.enabled,
}, },
@ -227,34 +227,34 @@ chrome.runtime.onMessage.addListener((request, sender, callback) => {
let tabId = sender.tab ? sender.tab.id : undefined; let tabId = sender.tab ? sender.tab.id : undefined;
switch (request.type) { switch (request.type) {
case "DISABLE_ICON": case 'DISABLE_ICON':
if (hasPermission && tabId) disableIcon(tabId); if (hasPermission && tabId) disableIcon(tabId);
break; break;
case "DISABLE_POPUP": case 'DISABLE_POPUP':
if (hasPermission && tabId) disablePopup(tabId); if (hasPermission && tabId) disablePopup(tabId);
break; break;
case "ENABLE_ICON": case 'ENABLE_ICON':
if (hasPermission && tabId) enableIcon(tabId); if (hasPermission && tabId) enableIcon(tabId);
break; break;
case "ENABLE_POPUP": case 'ENABLE_POPUP':
if (hasPermission && tabId) enablePopup(tabId); if (hasPermission && tabId) enablePopup(tabId);
break; break;
case "GET_CACHE": case 'GET_CACHE':
getCache(request.hostname, callback); getCache(request.hostname, callback);
break; break;
case "GET_CLASSES": case 'GET_CLASSES':
getClasses(callback); getClasses(callback);
break; break;
case "GET_FIXES": case 'GET_FIXES':
getFixes(callback); getFixes(callback);
break; break;
case "GET_SELECTORS": case 'GET_SELECTORS':
getSelectors(callback); getSelectors(callback);
break; break;
case "GET_TAB": case 'GET_TAB':
getTab(callback); getTab(callback);
break; break;
case "UPDATE_CACHE": case 'UPDATE_CACHE':
updateCache(request.hostname, request.state); updateCache(request.hostname, request.state);
break; break;
default: default:
@ -269,9 +269,9 @@ chrome.runtime.onMessage.addListener((request, sender, callback) => {
*/ */
chrome.contextMenus.create({ chrome.contextMenus.create({
contexts: ["all"], contexts: ['all'],
id: contextMenuId, id: contextMenuId,
title: chrome.i18n.getMessage("contextMenuText"), title: chrome.i18n.getMessage('contextMenuText'),
}); });
/** /**

View File

@ -30,7 +30,7 @@ const hostname = document.location.hostname;
*/ */
const isPreview = const isPreview =
hostname.startsWith("consent.") || hostname.startsWith("myprivacy."); hostname.startsWith('consent.') || hostname.startsWith('myprivacy.');
/** /**
* @description Options provided to observer * @description Options provided to observer
@ -59,8 +59,8 @@ const target = document.body || document.documentElement;
const check = (node) => const check = (node) =>
node instanceof HTMLElement && node instanceof HTMLElement &&
node.parentElement && node.parentElement &&
!["BODY", "HTML"].includes(node.tagName) && !['BODY', 'HTML'].includes(node.tagName) &&
!(node.id && ["APP", "ROOT"].includes(node.id.toUpperCase?.())); !(node.id && ['APP', 'ROOT'].includes(node.id.toUpperCase?.()));
/** /**
* @description Cleans DOM * @description Cleans DOM
@ -69,7 +69,7 @@ const check = (node) =>
const clean = () => { const clean = () => {
if (selectors.length) { if (selectors.length) {
const nodes = Array.from(document.querySelectorAll(selectors)); const nodes = Array.from(document.querySelectorAll(selectors));
nodes.filter(check).forEach((node) => (node.outerHTML = "")); nodes.filter(check).forEach((node) => (node.outerHTML = ''));
} }
}; };
@ -82,30 +82,30 @@ const fix = () => {
const html = document.documentElement; const html = document.documentElement;
body?.classList.remove(...classes); body?.classList.remove(...classes);
body?.style.setProperty("position", "initial", "important"); body?.style.setProperty('position', 'initial', 'important');
body?.style.setProperty("overflow-y", "initial", "important"); body?.style.setProperty('overflow-y', 'initial', 'important');
html?.classList.remove(...classes); html?.classList.remove(...classes);
html?.style.setProperty("position", "initial", "important"); html?.style.setProperty('position', 'initial', 'important');
html?.style.setProperty("overflow-y", "initial", "important"); html?.style.setProperty('overflow-y', 'initial', 'important');
fixes.forEach((fix) => { fixes.forEach((fix) => {
const [match, selector, action, property] = fix.split("##"); const [match, selector, action, property] = fix.split('##');
if (hostname.includes(match)) { if (hostname.includes(match)) {
switch (action) { switch (action) {
case "click": { case 'click': {
const node = document.querySelector(selector); const node = document.querySelector(selector);
node?.click(); node?.click();
} }
case "remove": { case 'remove': {
const node = document.querySelector(selector); const node = document.querySelector(selector);
node?.style?.removeProperty(property); node?.style?.removeProperty(property);
} }
case "reset": { case 'reset': {
const node = document.querySelector(selector); const node = document.querySelector(selector);
node?.style?.setProperty(property, "initial", "important"); node?.style?.setProperty(property, 'initial', 'important');
} }
case "resetAll": { case 'resetAll': {
const nodes = document.querySelectorAll(selector); const nodes = document.querySelectorAll(selector);
// prettier-ignore // prettier-ignore
nodes.forEach((node) => node?.style?.setProperty(property, "initial", "important")); nodes.forEach((node) => node?.style?.setProperty(property, "initial", "important"));
@ -126,7 +126,7 @@ const observer = new MutationObserver((mutations, instance) => {
for (const node of mutation.addedNodes) { for (const node of mutation.addedNodes) {
const valid = check(node); const valid = check(node);
if (valid && node.matches(selectors)) node.outerHTML = ""; if (valid && node.matches(selectors)) node.outerHTML = '';
} }
} }
} }
@ -141,7 +141,7 @@ const observer = new MutationObserver((mutations, instance) => {
const queryClasses = () => const queryClasses = () =>
new Promise((resolve) => { new Promise((resolve) => {
dispatch({ type: "GET_CLASSES" }, null, resolve); dispatch({ type: 'GET_CLASSES' }, null, resolve);
}); });
/** /**
@ -151,7 +151,7 @@ const queryClasses = () =>
const queryFixes = () => const queryFixes = () =>
new Promise((resolve) => { new Promise((resolve) => {
dispatch({ type: "GET_FIXES" }, null, resolve); dispatch({ type: 'GET_FIXES' }, null, resolve);
}); });
/** /**
@ -161,7 +161,7 @@ const queryFixes = () =>
const querySelectors = () => const querySelectors = () =>
new Promise((resolve) => { new Promise((resolve) => {
dispatch({ type: "GET_SELECTORS" }, null, resolve); dispatch({ type: 'GET_SELECTORS' }, null, resolve);
}); });
/** /**
@ -169,9 +169,9 @@ const querySelectors = () =>
* @listens document#readystatechange * @listens document#readystatechange
*/ */
document.addEventListener("readystatechange", () => { document.addEventListener('readystatechange', () => {
dispatch({ hostname, type: "GET_CACHE" }, null, async ({ enabled }) => { dispatch({ hostname, type: 'GET_CACHE' }, null, async ({ enabled }) => {
if (document.readyState === "complete" && enabled && !isPreview) { if (document.readyState === 'complete' && enabled && !isPreview) {
fix(); fix();
clean(); clean();
setTimeout(clean, 2000); setTimeout(clean, 2000);
@ -184,14 +184,14 @@ document.addEventListener("readystatechange", () => {
* @listens window#unload * @listens window#unload
*/ */
window.addEventListener("unload", () => {}); window.addEventListener('unload', () => {});
/** /**
* @description Setups everything and starts to observe if enabled * @description Setups everything and starts to observe if enabled
*/ */
dispatch({ hostname, type: "GET_CACHE" }, null, async ({ enabled }) => { dispatch({ hostname, type: 'GET_CACHE' }, null, async ({ enabled }) => {
dispatch({ type: "ENABLE_POPUP" }); dispatch({ type: 'ENABLE_POPUP' });
if (enabled) { if (enabled) {
const promises = [queryClasses(), queryFixes(), querySelectors()]; const promises = [queryClasses(), queryFixes(), querySelectors()];
@ -201,6 +201,6 @@ dispatch({ hostname, type: "GET_CACHE" }, null, async ({ enabled }) => {
fixes = results[1]?.fixes ?? []; fixes = results[1]?.fixes ?? [];
selectors = results[2]?.selectors ?? []; selectors = results[2]?.selectors ?? [];
observer.observe(target, options); observer.observe(target, options);
dispatch({ type: "ENABLE_ICON" }); dispatch({ type: 'ENABLE_ICON' });
} }
}); });

View File

@ -0,0 +1,139 @@
/**
* @constant chromeUrl
* @description Chrome Web Store link
* @type {string}
*/
const chromeUrl =
'https://chrome.google.com/webstore/detail/djcbfpkdhdkaflcigibkbpboflaplabg';
/**
* @constant dispatch
* @description Shortcut to send messages to background script
* @type {void}
*/
const dispatch = chrome.runtime.sendMessage;
/**
* @constant firefoxUrl
* @description Firefox Add-ons link
* @type {string}
*/
const firefoxUrl =
'https://addons.mozilla.org/es/firefox/addon/cookie-dialog-monster/';
/**
* @constant isChromium
* @description Is current browser an instance of Chromium?
* @type {boolean}
*/
const isChromium = chrome.runtime.getURL('').startsWith('chrome-extension://');
/**
* @description Disables or enables extension on current page
*/
const handlePowerChange = () => {
dispatch({ type: 'GET_TAB' }, null, ({ hostname, id }) => {
dispatch({ hostname, type: 'GET_CACHE' }, null, ({ enabled }) => {
const power = document.getElementById('power');
dispatch({
hostname,
state: { enabled: !enabled },
type: 'UPDATE_CACHE',
});
dispatch({
type: !enabled === true ? 'ENABLE_ICON' : 'DISABLE_ICON',
});
if (!enabled === false) power.removeAttribute('checked');
if (!enabled === true) power.setAttribute('checked', 'checked');
chrome.tabs.reload(id, { bypassCache: true });
});
});
};
/**
* @description Reload current page
*/
const handleReload = () => {
dispatch({ type: 'GET_TAB' }, null, ({ id }) => {
chrome.tabs.reload(id, { bypassCache: true });
});
};
/**
* @description Shows negative or positive messages
* @param {MouseEvent} event
*/
const handleRate = (event) => {
const negative = document.getElementById('negative');
const positive = document.getElementById('positive');
switch (event.currentTarget.id) {
case 'unlike':
positive.setAttribute('hidden', 'true');
negative.removeAttribute('hidden');
break;
case 'like':
negative.setAttribute('hidden', 'true');
positive.removeAttribute('hidden');
break;
default:
break;
}
};
/**
* @description Setup stars handlers and result message links
*/
const handleContentLoaded = () => {
dispatch({ type: 'GET_TAB' }, null, ({ hostname }) => {
dispatch({ hostname, type: 'GET_CACHE' }, null, ({ enabled }) => {
translate();
const host = document.getElementById('host');
const like = document.getElementById('like');
const power = document.getElementById('power');
const reload = document.getElementById('reload');
const store = document.getElementById('store');
const unlike = document.getElementById('unlike');
like.addEventListener('click', handleRate);
power.addEventListener('change', handlePowerChange);
reload.addEventListener('click', handleReload);
store.setAttribute('href', isChromium ? chromeUrl : firefoxUrl);
unlike.addEventListener('click', handleRate);
if (location) host.innerText = hostname.replace('www.', '');
if (!enabled) power.removeAttribute('checked');
});
});
};
/**
* @description Applies translations to tags with i18n data attribute
*/
const translate = () => {
const nodes = document.querySelectorAll('[data-i18n]');
for (let i = nodes.length; i--; ) {
const node = nodes[i];
const { i18n } = node.dataset;
node.innerHTML = chrome.i18n.getMessage(i18n);
}
};
/**
* @description Listen to document ready
* @listens document#ready
*/
document.addEventListener('DOMContentLoaded', handleContentLoaded);

View File

@ -0,0 +1,3 @@
CHROME_EXTENSION_ID=?
MAIL_PASS=?
MAIL_USER=?

View File

@ -0,0 +1,19 @@
{
"env": {
"es6": true,
"jest": true,
"node": true
},
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest"
}
}

View File

@ -0,0 +1,21 @@
MIT License Copyright (c) 2020 Juan José Vílchez Naranjo
Permission is hereby
granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice
(including the next paragraph) shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,9 @@
# Cookie Monster Dialog Report Service
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
## License
[MIT](https://choosealicense.com/licenses/mit/)

View File

@ -0,0 +1,6 @@
{
"exec": "ts-node -r tsconfig-paths/register ./src/index.ts",
"ext": ".ts",
"ignore": [],
"watch": ["src"]
}

View File

@ -0,0 +1,38 @@
{
"name": "report-service",
"version": "1.0.0",
"scripts": {
"build": "rimraf build && tsc",
"dev": "nodemon",
"lint": "eslint src/**/*.ts --fix",
"setup": "rimraf .husky && husky install && husky add .husky/pre-commit \"yarn lint-staged\" && husky add .husky/commit-msg 'npx --no -- commitlint --edit \"$1\"'",
"start": "NODE_PATH=build node build/index.js"
},
"dependencies": {
"dotenv": "^16.0.0",
"fastify": "^3.27.1",
"fastify-cors": "^6.0.2",
"fastify-rate-limit": "^5.7.0",
"nodemailer": "^6.7.2"
},
"devDependencies": {
"@tsconfig/node16": "^1.0.2",
"@types/node": "^17.0.16",
"@types/nodemailer": "^6.4.4",
"@typescript-eslint/eslint-plugin": "^5.11.0",
"@typescript-eslint/parser": "^5.11.0",
"eslint": "^8.8.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"nodemon": "^2.0.15",
"rimraf": "^3.0.2",
"ts-node": "^10.5.0",
"tsconfig-paths": "^3.12.0",
"typescript": "^4.5.5"
},
"engines": {
"node": "16.x"
},
"packageManager": "yarn@3.2.0",
"license": "MIT"
}

View File

@ -0,0 +1,37 @@
import fastify from 'fastify';
import cors from 'fastify-cors';
import rateLimit from 'fastify-rate-limit';
import v1ReportRoutes from 'routes/v1/report';
import environment from 'services/environment';
const server = fastify({ logger: true });
server.register(cors, {
origin: (origin, callback) => {
const moz =
/moz-extension:\/\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/g;
switch (true) {
case environment.extension.chrome === origin:
case moz.test(origin):
callback(null, true);
break;
default:
callback(new Error('Not allowed'), false);
break;
}
},
});
server.register(rateLimit, { max: 1, timeWindow: 30000 });
server.register(v1ReportRoutes, { prefix: '/rest/v1' });
server.listen(environment.port, '0.0.0.0', (error, address) => {
if (error) {
console.error(error);
process.exit(1);
}
console.log(`Server listening at ${address}`);
});

View File

@ -0,0 +1,45 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import { sendMail } from 'services/mailing';
type PostReportBody = {
subject: string;
text: string;
to: string;
};
export default (
server: FastifyInstance,
options: RouteShorthandOptions,
done: () => void
) => {
server.post<{ Body: PostReportBody }>(
'/report/',
{
schema: {
body: {
properties: {
subject: {
type: 'string',
},
text: {
type: 'string',
},
to: {
type: 'string',
},
},
required: ['subject', 'text', 'to'],
type: 'object',
},
},
},
async (request, reply) => {
const { subject, text, to } = request.body;
sendMail({ text, to, subject });
reply.send({ success: true });
}
);
done();
};

View File

@ -0,0 +1,14 @@
import dotenv from 'dotenv';
dotenv.config();
export default {
extension: {
chrome: process.env.CHROME_EXTENSION_ID ?? '',
},
mail: {
pass: process.env.MAIL_PASS ?? '',
user: process.env.MAIL_USER ?? '',
},
port: process.env.PORT ?? 8080,
};

View File

@ -0,0 +1,12 @@
import nodemailer, { SendMailOptions } from 'nodemailer';
import environment from './environment';
const mailing = nodemailer.createTransport({
auth: { pass: environment.mail.pass, user: environment.mail.user },
host: 'smtp.zoho.eu',
port: 465,
secure: true,
});
export const sendMail = (options: SendMailOptions) =>
mailing.sendMail({ ...options, from: environment.mail.user }, () => null);

View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"baseUrl": "src",
"outDir": "build",
"rootDir": "src"
},
"extends": "@tsconfig/node16/tsconfig.json",
"include": ["src/**/*.ts"],
"ts-node": {
"files": true
}
}

View File

@ -1,139 +0,0 @@
/**
* @constant chromeUrl
* @description Chrome Web Store link
* @type {string}
*/
const chromeUrl =
"https://chrome.google.com/webstore/detail/djcbfpkdhdkaflcigibkbpboflaplabg";
/**
* @constant dispatch
* @description Shortcut to send messages to background script
* @type {void}
*/
const dispatch = chrome.runtime.sendMessage;
/**
* @constant firefoxUrl
* @description Firefox Add-ons link
* @type {string}
*/
const firefoxUrl =
"https://addons.mozilla.org/es/firefox/addon/cookie-dialog-monster/";
/**
* @constant isChromium
* @description Is current browser an instance of Chromium?
* @type {boolean}
*/
const isChromium = chrome.runtime.getURL("").startsWith("chrome-extension://");
/**
* @description Disables or enables extension on current page
*/
const handlePowerChange = () => {
dispatch({ type: "GET_TAB" }, null, ({ hostname, id }) => {
dispatch({ hostname, type: "GET_CACHE" }, null, ({ enabled }) => {
const power = document.getElementById("power");
dispatch({
hostname,
state: { enabled: !enabled },
type: "UPDATE_CACHE",
});
dispatch({
type: !enabled === true ? "ENABLE_ICON" : "DISABLE_ICON",
});
if (!enabled === false) power.removeAttribute("checked");
if (!enabled === true) power.setAttribute("checked", "checked");
chrome.tabs.reload(id, { bypassCache: true });
});
});
};
/**
* @description Reload current page
*/
const handleReload = () => {
dispatch({ type: "GET_TAB" }, null, ({ id }) => {
chrome.tabs.reload(id, { bypassCache: true });
});
};
/**
* @description Shows negative or positive messages
* @param {MouseEvent} event
*/
const handleRate = (event) => {
const negative = document.getElementById("negative");
const positive = document.getElementById("positive");
switch (event.currentTarget.id) {
case "unlike":
positive.setAttribute("hidden", "true");
negative.removeAttribute("hidden");
break;
case "like":
negative.setAttribute("hidden", "true");
positive.removeAttribute("hidden");
break;
default:
break;
}
};
/**
* @description Setup stars handlers and result message links
*/
const handleContentLoaded = () => {
dispatch({ type: "GET_TAB" }, null, ({ hostname }) => {
dispatch({ hostname, type: "GET_CACHE" }, null, ({ enabled }) => {
translate();
const host = document.getElementById("host");
const like = document.getElementById("like");
const power = document.getElementById("power");
const reload = document.getElementById("reload");
const store = document.getElementById("store");
const unlike = document.getElementById("unlike");
like.addEventListener("click", handleRate);
power.addEventListener("change", handlePowerChange);
reload.addEventListener("click", handleReload);
store.setAttribute("href", isChromium ? chromeUrl : firefoxUrl);
unlike.addEventListener("click", handleRate);
if (location) host.innerText = hostname.replace("www.", "");
if (!enabled) power.removeAttribute("checked");
});
});
};
/**
* @description Applies translations to tags with i18n data attribute
*/
const translate = () => {
const nodes = document.querySelectorAll("[data-i18n]");
for (let i = nodes.length; i--; ) {
const node = nodes[i];
const { i18n } = node.dataset;
node.innerHTML = chrome.i18n.getMessage(i18n);
}
};
/**
* @description Listen to document ready
* @listens document#ready
*/
document.addEventListener("DOMContentLoaded", handleContentLoaded);

4672
yarn.lock Normal file

File diff suppressed because it is too large Load Diff