Skip to content

Commit

Permalink
prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
sandstone991 committed Aug 31, 2023
1 parent e114cfd commit ecbaf85
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 27 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"dependencies": {
"@babel/runtime": "^7.14.0",
"advanced-css-reset": "^1.2.2",
"clsx": "^2.0.0",
"emoji-log": "^1.0.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down
95 changes: 71 additions & 24 deletions source/Background/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import 'emoji-log';
import {browser} from 'webextension-polyfill-ts';

const providers = {
jsdelivr: 'https://cdn.jsdelivr.net/npm/',
unpkg: 'https://unpkg.com/',
cdnjs: 'https://cdnjs.cloudflare.com/ajax/libs/',
github: 'https://raw.githubusercontent.com/',
};
import {providers} from '../Constants';
browser.runtime.onInstalled.addListener((): void => {
console.emoji('🦄', 'extension installed');
});
let blockedProvidersSynced:string[] = [];

// sync object on startup and install
browser.storage.local.get("blockedProivders").then((result)=>{
blockedProvidersSynced = result["blockedProivders"] || [];
}
)

browser.webRequest.onErrorOccurred.addListener(
(details) => {
if (details.error === 'net::ERR_BLOCKED_BY_CLIENT') return;
Object.keys(providers).forEach((provider) => {
if (details.url.search(provider) !== -1) {
browser.storage.local.get([provider]).then((result) => {
browser.storage.local.get(provider).then((result) => {
if (result[provider] === undefined) {
result[provider] = 0;
}
Expand All @@ -31,21 +32,67 @@ browser.webRequest.onErrorOccurred.addListener(
);

browser.storage.onChanged.addListener((changes) => {
for (const [key, {newValue}] of Object.entries(changes)) {
if (key in providers && newValue >= 3) {
// user has a problem with this provider
// notify the user to change the provider
browser.notifications.create({
type: 'basic',
iconUrl: browser.runtime.getURL('assets/icons/favicon-128.png'),
title: 'Provider Error',
message: `You have a problem with ${key} provider, please change it from the options page`,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
});
}
}
browser.storage.local.get('lastNotification').then((result) => {
const lastNotification = result.lastNotification;
const now = Date.now();
if (lastNotification && now - lastNotification <= 1000 * 60 * 60) return;
for (const [key, {newValue}] of Object.entries(changes)) {
if (key in providers && newValue >= 3) {
browser.notifications.clear('provider-error');
browser.notifications.create('provider-error', {
type: 'basic',
iconUrl: browser.runtime.getURL('assets/icons/favicon-128.png'),
title: 'Provider Error',
message: `You have a problem with ${key} provider, please change it from the options page`,
});
browser.storage.local.set({lastNotification: now});
}
if(key === "blockedProivders"){
//sync object
browser.storage.local.get("blockedProivders").then((result)=>{
blockedProvidersSynced = result["blockedProivders"];
}
)
}
}
})
});

browser.notifications.onClicked.addListener(() => {
browser.runtime.openOptionsPage();
browser.notifications.onClicked.addListener((id) => {
if(id==="provider-error")browser.runtime.openOptionsPage();
});


browser.webRequest.onBeforeRequest.addListener(
(details) => {
// check if the url is blocked
console.log(blockedProvidersSynced)

if (blockedProvidersSynced.length === 0) return;
console.log("1")
for (const provider of blockedProvidersSynced) {
if (details.url.search(provider) === -1) continue;
const replacement: (keyof typeof providers | undefined)= Object.keys(providers).find((provider) => {
return blockedProvidersSynced.indexOf(provider) === -1;}
) as keyof typeof providers | undefined;
if (replacement === undefined) return;
// ex: found https://cdn.jsdelivr.net/npm/package@version/file
// extract package, version and file
const url = new URL(details.url);
let newUrl = "";
if(url.hostname.search("jsdelivr") !== -1){
let path = url.pathname.split("/");
let rest = path[2];
newUrl = `${providers[replacement]}${rest}`
};
//find a replacement that is not blocked
//replace the url

console.log(newUrl);
return {redirectUrl: newUrl};
}
return;
},

{urls: ['<all_urls>'], types: ['script']},
['blocking']
);
10 changes: 10 additions & 0 deletions source/Constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const providers = {
jsdelivr: 'https://cdn.jsdelivr.net/npm/',
unpkg: 'https://unpkg.com/',
cdnjs: 'https://cdnjs.cloudflare.com/ajax/libs/',

};

export {
providers
}
196 changes: 193 additions & 3 deletions source/Options/Options.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,200 @@
import * as React from 'react';
import '../styles/index.css';
import { providers } from '../Constants';
import { browser } from 'webextension-polyfill-ts';
import clsx from 'clsx';
const Options: React.FC = () => {
const [notWorkingProviders, setNotWorkingProviders] =
React.useState<{ provider: string; times: number }[] | null>(null);
const [blockedProivders, setBlockedProviders] =
React.useState<string[] | null>(null);
const [showSuccessMessage, setShowSuccessMessage] = React.useState(false);
const errorMessageTimeout = React.useRef<number | null>(null);
React.useEffect(() => {
async function getNotWorkingProviders() {
let blockedProivders = (
await Promise.all(
Object.keys(providers).map(async (provider) => {
let res = await browser.storage.local.get(provider);
if (res[provider]) {
return {
provider,
times: res[provider],
};
} else {
return null;
}
}),
)
).filter((provider) => provider !== null);
setNotWorkingProviders(
blockedProivders as { provider: string; times: number }[],
);
}
async function getBlockedProviders() {
let blockedProivders = await browser.storage.local.get(
'blockedProivders',
);
setBlockedProviders(blockedProivders['blockedProivders'] || []);
}
getNotWorkingProviders();
getBlockedProviders();
}, []);

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
let formData = new FormData(e.currentTarget);
let blockedProivders = Object.keys(providers).filter(
(provider) => !formData.getAll('provider').includes(provider),
);
browser.storage.local.set({ blockedProivders });
setShowSuccessMessage(true);
//clear the message after 5 seconds
if (errorMessageTimeout.current)
clearTimeout(errorMessageTimeout.current);
errorMessageTimeout.current = window.setTimeout(() => {
setShowSuccessMessage(false);
}, 5000);
};

const SuccessMessage = () => {
if (!showSuccessMessage) return null;
return (
<div
className="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative"
role="alert"
>
<strong className="font-bold">Success!</strong>
<span className="block sm:inline">
{' '}
Your changes have been saved.
</span>
<span
onClick={() => setShowSuccessMessage(false)}
className="absolute top-0 bottom-0 right-0 px-4 py-3"
>
<svg
className="fill-current h-6 w-6 text-green-500"
role="button"
xmlns="http:https://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<title>Close</title>
<path d="M14.348 14.849a1 1 0 01-1.414 0L10 11.414l-2.93 2.93a1 1 0 01-1.414 0l-.707-.707a1 1 0 010-1.414l2.93-2.93-2.93-2.93a1 1 0 010-1.414l.707-.707a1 1 0 011.414 0l2.93 2.93 2.93-2.93a1 1 0 011.414 0l.707.707a1 1 0 010 1.414l-2.93 2.93 2.93 2.93a1 1 0 010 1.414l-.707.707z" />
</svg>
</span>
</div>
);
};
const FormBody = () => {
if (notWorkingProviders === null || blockedProivders === null)
return null;
return (
<div className="flex flex-col w-full gap-4">
<div>
{Object.keys(providers).map((provider) => {
const notWorking = notWorkingProviders.find((p) => {
return p.provider === provider;
});
return (
<div
key={provider}
className={clsx(
'flex flex-row justify-between items-center p-4 border-b',
{
'bg-red-200': notWorking,
},
)}
>
<div className="flex flex-col justify-center items-start">
<div className="text-xl font-semibold flex flex-row items-center gap-4">
{provider}
{notWorking && (
<span className="text-red-800 text-sm">
This provider has not been
working for you for{' '}
<b className="font-extrabold">
{notWorking.times}
</b>{' '}
times, consider unselecting it.
</span>
)}
</div>
</div>
<div className="flex flex-row justify-center items-center">
<div className="flex flex-col justify-center items-center">
<input
type="checkbox"
checked={
!blockedProivders.includes(
provider,
)
}
onChange={() => {
if (
blockedProivders.includes(
provider,
)
) {
setBlockedProviders(
blockedProivders.filter(
(blockedProvider) =>
blockedProvider !==
provider,
),
);
} else {
setBlockedProviders([
...blockedProivders,
provider,
]);
}
}}
name="provider"
value={provider}
className="form-checkbox h-4 w-4 text-indigo-600 transition duration-150 ease-in-out"
/>
</div>
</div>
</div>
);
})}
</div>
<button
type="submit"
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mx-auto"
>
Save
</button>
</div>
);
};

return (
<div className="w-full h-full flex flex-col justify-center items-center">
<form className="shadow-md m-auto w-3/4 h-3/4">
<input type="checkbox" name="checkbox" id="checkbox" />
<div className="w-full h-full flex flex-col justify-center items-center text-gray-700 bg-gray-200">
<h2 className="text-4xl font-bold">Options</h2>
<form
className="shadow-md m-auto w-3/4 bg-white"
onSubmit={handleSubmit}
>
<div className="p-4">
<h2 className="text-2xl text-center font-bold">
Allowed CDNs
</h2>
<div className="text-gray-500 text-sm">
<p>
Allowed CDNs won't be rerouted even if they're
blocked or don't work for you
</p>
<p>
You'll have to deselect undesired providers manually
</p>
<div className="flex flex-col gap-4">
<FormBody />
<SuccessMessage />
</div>
</div>
</div>
</form>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2530,6 +2530,11 @@ clone-deep@^4.0.1:
kind-of "^6.0.2"
shallow-clone "^3.0.0"

clsx@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==

coa@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz"
Expand Down

0 comments on commit ecbaf85

Please sign in to comment.