mirror of
https://github.com/movie-web/extension.git
synced 2025-09-13 14:33:26 +00:00
Compare commits
38 Commits
perms
...
b5be448511
Author | SHA1 | Date | |
---|---|---|---|
|
b5be448511 | ||
|
c9bf3286ae | ||
|
3f33d9bc4a | ||
|
86a659f478 | ||
|
dbdbf10c5d | ||
|
6fca55a6c3 | ||
|
50eadd3a14 | ||
|
b2ba74bd97 | ||
|
bb61fef30a | ||
|
8e179e4455 | ||
|
327f175241 | ||
|
c72ec41228 | ||
|
0bd1a39b53 | ||
|
de0a1b3849 | ||
|
8ba0544468 | ||
|
a884c785f0 | ||
|
0801f64e1b | ||
|
8735f7b4b2 | ||
|
90f941431c | ||
|
df05547581 | ||
|
a8e3de1451 | ||
|
1801ee238d | ||
|
434b2475cb | ||
|
50166457b8 | ||
|
e2ea8a3c7e | ||
|
213828e818 | ||
|
755bba3e1e | ||
|
e7ca90b75f | ||
|
d74f0abbf6 | ||
|
cade0b50ab | ||
|
1ac8147cdd | ||
|
6d1fa16553 | ||
|
a8417c75a3 | ||
|
abf26103e1 | ||
|
03e7649bc1 | ||
|
ec4e3b7392 | ||
|
ce31771e5f | ||
|
a84eb7b0e5 |
10
.github/SECURITY.md
vendored
10
.github/SECURITY.md
vendored
@@ -2,13 +2,9 @@
|
||||
|
||||
## Supported Versions
|
||||
|
||||
The movie-web maintainers only support the latest version of movie-web published at https://movie-web.app.
|
||||
This published version is equivalent to the master branch.
|
||||
|
||||
Support is not provided for any forks or mirrors of movie-web.
|
||||
The latest version of movie-web is the only version that is supported, as it is the only version that is being actively developed.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
There are two ways you can contact the movie-web maintainers to report a vulnerability:
|
||||
- Email [security@movie-web.app](mailto:security@movie-web.app)
|
||||
- Report the vulnerability in the [movie-web Discord server](https://discord.movie-web.app)
|
||||
You can contact the movie-web maintainers to report a vulnerability:
|
||||
- Report the vulnerability in the [movie-web Discord server](https://movie-web.github.io/links/discord)
|
||||
|
4
.github/workflows/deploying.yml
vendored
4
.github/workflows/deploying.yml
vendored
@@ -94,8 +94,8 @@ jobs:
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./chrome/chrome-mv3-prod.zip
|
||||
asset_name: extension-mw.chrome.crx
|
||||
asset_content_type: application/x-chrome-extension
|
||||
asset_name: extension-mw.chrome.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload Firefox release
|
||||
uses: actions/upload-release-asset@v1
|
||||
|
BIN
assets/active.png
Normal file
BIN
assets/active.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/inactive.png
Normal file
BIN
assets/inactive.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
16
package.json
16
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@movie-web/extension",
|
||||
"displayName": "movie-web extension",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.4",
|
||||
"description": "Enhance your movie-web experience with just one click",
|
||||
"author": "movie-web",
|
||||
"scripts": {
|
||||
@@ -53,7 +53,21 @@
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "{3fd86354-c73f-4395-9e26-2c5c984579bf}"
|
||||
},
|
||||
"gecko_android": {
|
||||
"id": "{3fd86354-c73f-4395-9e26-2c5c984579bf}"
|
||||
}
|
||||
},
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": [
|
||||
"assets/active.png",
|
||||
"assets/inactive.png"
|
||||
],
|
||||
"matches": [
|
||||
"<all_urls>"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,8 @@ type Response = BaseResponse<{
|
||||
|
||||
const handler: PlasmoMessaging.MessageHandler<BaseRequest, Response> = async (req, res) => {
|
||||
try {
|
||||
if (!req.sender?.tab?.url) throw new Error('No tab URL found in the request.');
|
||||
|
||||
const version = getVersion();
|
||||
res.send({
|
||||
success: true,
|
||||
@@ -24,7 +26,7 @@ const handler: PlasmoMessaging.MessageHandler<BaseRequest, Response> = async (re
|
||||
} catch (err) {
|
||||
res.send({
|
||||
success: false,
|
||||
error: err.message,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@@ -3,6 +3,7 @@ import type { PlasmoMessaging } from '@plasmohq/messaging';
|
||||
import type { BaseRequest } from '~types/request';
|
||||
import type { BaseResponse } from '~types/response';
|
||||
import { removeDynamicRules, setDynamicRules } from '~utils/declarativeNetRequest';
|
||||
import { isFirefox } from '~utils/extension';
|
||||
import { makeFullUrl } from '~utils/fetcher';
|
||||
import { assertDomainWhitelist } from '~utils/storage';
|
||||
|
||||
@@ -31,7 +32,7 @@ type Response<T> = BaseResponse<{
|
||||
const mapBodyToFetchBody = (body: Request['body'], bodyType: Request['bodyType']): BodyInit => {
|
||||
if (bodyType === 'FormData') {
|
||||
const formData = new FormData();
|
||||
body.forEach(([key, value]) => {
|
||||
body.forEach(([key, value]: [any, any]) => {
|
||||
formData.append(key, value.toString());
|
||||
});
|
||||
}
|
||||
@@ -49,16 +50,17 @@ const mapBodyToFetchBody = (body: Request['body'], bodyType: Request['bodyType']
|
||||
|
||||
const handler: PlasmoMessaging.MessageHandler<Request, Response<any>> = async (req, res) => {
|
||||
try {
|
||||
if (!req.sender?.tab?.url) throw new Error('No tab URL found in the request.');
|
||||
if (!req.body) throw new Error('No request body found in the request.');
|
||||
|
||||
const url = makeFullUrl(req.body.url, req.body);
|
||||
await assertDomainWhitelist(req.sender.tab.url);
|
||||
|
||||
if (Object.keys(req.body.headers).length > 0) {
|
||||
await setDynamicRules({
|
||||
ruleId: MAKE_REQUEST_DYNAMIC_RULE,
|
||||
targetDomains: [new URL(url).hostname],
|
||||
requestHeaders: req.body.headers,
|
||||
});
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: req.body.method,
|
||||
@@ -71,6 +73,9 @@ const handler: PlasmoMessaging.MessageHandler<Request, Response<any>> = async (r
|
||||
|
||||
const cookies = await (chrome || browser).cookies.getAll({
|
||||
url: response.url,
|
||||
...(isFirefox() && {
|
||||
firstPartyDomain: new URL(response.url).hostname,
|
||||
}),
|
||||
});
|
||||
|
||||
res.send({
|
||||
@@ -89,7 +94,7 @@ const handler: PlasmoMessaging.MessageHandler<Request, Response<any>> = async (r
|
||||
console.error('failed request', err);
|
||||
res.send({
|
||||
success: false,
|
||||
error: err.message,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@@ -11,6 +11,9 @@ type Request = BaseRequest & {
|
||||
|
||||
const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (req, res) => {
|
||||
try {
|
||||
if (!req.sender?.tab?.id) throw new Error('No tab ID found in the request.');
|
||||
if (!req.body) throw new Error('No body found in the request.');
|
||||
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set('redirectUrl', req.body.redirectUrl);
|
||||
const url = (chrome || browser).runtime.getURL(`/tabs/${req.body.page}.html?${searchParams.toString()}`);
|
||||
@@ -29,7 +32,7 @@ const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (re
|
||||
} catch (err) {
|
||||
res.send({
|
||||
success: false,
|
||||
error: err.message,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@@ -15,6 +15,9 @@ interface Request extends BaseRequest {
|
||||
|
||||
const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (req, res) => {
|
||||
try {
|
||||
if (!req.sender?.tab?.url) throw new Error('No tab URL found in the request.');
|
||||
if (!req.body) throw new Error('No request body found in the request.');
|
||||
|
||||
await assertDomainWhitelist(req.sender.tab.url);
|
||||
await setDynamicRules(req.body);
|
||||
res.send({
|
||||
@@ -23,7 +26,7 @@ const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (re
|
||||
} catch (err) {
|
||||
res.send({
|
||||
success: false,
|
||||
error: err.message,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@@ -6,6 +6,7 @@ const icons = {
|
||||
windows: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M432 64H208c-8.8 0-16 7.2-16 16V96H128V80c0-44.2 35.8-80 80-80H432c44.2 0 80 35.8 80 80V304c0 44.2-35.8 80-80 80H416V320h16c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16zM0 192c0-35.3 28.7-64 64-64H320c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V192zm64 32c0 17.7 14.3 32 32 32H288c17.7 0 32-14.3 32-32s-14.3-32-32-32H96c-17.7 0-32 14.3-32 32z"/></svg>`,
|
||||
shield: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M269.4 2.9C265.2 1 260.7 0 256 0s-9.2 1-13.4 2.9L54.3 82.8c-22 9.3-38.4 31-38.3 57.2c.5 99.2 41.3 280.7 213.6 363.2c16.7 8 36.1 8 52.8 0C454.7 420.7 495.5 239.2 496 140c.1-26.2-16.3-47.9-38.3-57.2L269.4 2.9zM160 154.4c0-5.8 4.7-10.4 10.4-10.4h.2c3.4 0 6.5 1.6 8.5 4.3l40 53.3c3 4 7.8 6.4 12.8 6.4h48c5 0 9.8-2.4 12.8-6.4l40-53.3c2-2.7 5.2-4.3 8.5-4.3h.2c5.8 0 10.4 4.7 10.4 10.4V272c0 53-43 96-96 96s-96-43-96-96V154.4zM216 288a16 16 0 1 0 0-32 16 16 0 1 0 0 32zm96-16a16 16 0 1 0 -32 0 16 16 0 1 0 32 0z"/></svg>`,
|
||||
logo: `<svg width="1em" height="1em" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.2254 4.95486L10.5285 5.65174L9.83162 4.95486L10.5285 4.25799L7.7414 1.4709L7.04453 2.16777L6.34766 1.4709L7.04453 0.774022L6.34766 0.0771484L0.0761918 6.34861L0.773066 7.04549L1.46994 6.34861L2.16681 7.04549L1.46994 7.74236L4.25743 10.5299L4.95431 9.83298L5.65118 10.5299L4.95431 11.2267L5.65118 11.9236L11.9226 5.65214L11.2254 4.95486ZM2.86529 6.35021L2.16681 5.65174L2.86369 4.95487L3.56056 5.65174L2.86529 6.35021ZM4.25904 4.95647L3.56056 4.25799L4.25703 3.56152L4.95391 4.25839L4.25904 4.95647ZM5.65278 3.56272L4.95431 2.86424L5.65078 2.16777L6.34766 2.86464L5.65278 3.56272ZM6.34766 9.83258L5.65078 9.13571L6.34766 8.43883L7.04453 9.13571L6.34766 9.83258ZM7.7414 8.43883L7.04453 7.74196L7.741 7.04549L8.43788 7.74236L7.7414 8.43883ZM9.13515 7.04509L8.43828 6.34821L9.13475 5.65174L9.83162 6.34861L9.13515 7.04509Z" fill="currentColor"/></svg>`,
|
||||
network: `<svg width="1em" height="1em" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 64H384v64H256V64zM240 0c-26.5 0-48 21.5-48 48v96c0 26.5 21.5 48 48 48h48v32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h96v32H80c-26.5 0-48 21.5-48 48v96c0 26.5 21.5 48 48 48H240c26.5 0 48-21.5 48-48V368c0-26.5-21.5-48-48-48H192V288H448v32H400c-26.5 0-48 21.5-48 48v96c0 26.5 21.5 48 48 48H560c26.5 0 48-21.5 48-48V368c0-26.5-21.5-48-48-48H512V288h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H352V192h48c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48H240zM96 448V384H224v64H96zm320-64H544v64H416V384z"/></svg>`,
|
||||
};
|
||||
|
||||
export type Icons = keyof typeof icons;
|
||||
|
@@ -9,6 +9,7 @@ export function SetupScreen() {
|
||||
const open = useCallback(() => {
|
||||
const url = (chrome || browser).runtime.getURL(`/tabs/PermissionRequest.html`);
|
||||
(chrome || browser).tabs.create({ url });
|
||||
window.close();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -17,7 +18,7 @@ export function SetupScreen() {
|
||||
<div className="icon">
|
||||
<Icon name="logo" />
|
||||
</div>
|
||||
<h1 className="title">Le's get this set up!</h1>
|
||||
<h1 className="title">Let's get this set up!</h1>
|
||||
<p className="paragraph" style={{ paddingBottom: 25, paddingTop: 10 }}>
|
||||
To get started, we need to setup some things first. Click the button below to continue.
|
||||
</p>
|
||||
|
@@ -36,7 +36,7 @@ export function ToggleButton(props: ToggleButtonProps) {
|
||||
</button>
|
||||
</div>
|
||||
<p>
|
||||
Extension {props.active ? 'enabled' : 'disabled'} <br /> on <strong>{props.domain}</strong>
|
||||
Extension <strong>{props.active ? 'enabled' : 'disabled'}</strong> <br /> on <strong>{props.domain}</strong>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
@@ -15,5 +15,5 @@ export function useDomain(): null | string {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return makeUrlIntoDomain(domain);
|
||||
return domain ? makeUrlIntoDomain(domain) : null;
|
||||
}
|
||||
|
@@ -8,12 +8,12 @@ export function useDomainWhitelist() {
|
||||
|
||||
const removeDomain = useCallback((domain: string | null) => {
|
||||
if (!domain) return;
|
||||
setDomainWhitelist((s) => [...s.filter((v) => v !== domain)]);
|
||||
setDomainWhitelist((s) => [...(s ?? []).filter((v) => v !== domain)]);
|
||||
}, []);
|
||||
|
||||
const addDomain = useCallback((domain: string | null) => {
|
||||
if (!domain) return;
|
||||
setDomainWhitelist((s) => [...s.filter((v) => v !== domain), domain]);
|
||||
setDomainWhitelist((s) => [...(s ?? []).filter((v) => v !== domain), domain]);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
@@ -23,10 +23,15 @@ export function useDomainWhitelist() {
|
||||
};
|
||||
}
|
||||
|
||||
export function useToggleWhitelistDomain(domain: string) {
|
||||
export function useToggleWhitelistDomain(domain: string | null) {
|
||||
const { domainWhitelist, addDomain, removeDomain } = useDomainWhitelist();
|
||||
const isWhitelisted = domainWhitelist.includes(domain);
|
||||
const isWhitelisted = domainWhitelist.includes(domain ?? '');
|
||||
const { grantPermission } = usePermission();
|
||||
const iconPath = (chrome || browser).runtime.getURL(isWhitelisted ? 'assets/active.png' : 'assets/inactive.png');
|
||||
|
||||
(chrome || browser).action.setIcon({
|
||||
path: iconPath,
|
||||
});
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
if (!isWhitelisted) {
|
||||
|
@@ -28,7 +28,7 @@ function IndexPopup() {
|
||||
) : (
|
||||
<Frame>
|
||||
<div className="popup">
|
||||
{page === 'toggle' ? <ToggleButton active={isWhitelisted} onClick={toggle} domain={domain} /> : null}
|
||||
{page === 'toggle' && domain ? <ToggleButton active={isWhitelisted} onClick={toggle} domain={domain} /> : null}
|
||||
{page === 'disabled' ? <DisabledScreen /> : null}
|
||||
<BottomLabel />
|
||||
</div>
|
||||
|
@@ -11,6 +11,8 @@ body {
|
||||
}
|
||||
|
||||
#__plasmo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: #0A0A10;
|
||||
}
|
||||
|
@@ -8,11 +8,27 @@ export default function PermissionGrant() {
|
||||
const { grantPermission } = usePermission();
|
||||
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const redirectUrl = queryParams.get('redirectUrl') ?? 'https://movie-web.app';
|
||||
const domain = makeUrlIntoDomain(redirectUrl);
|
||||
const redirectUrl = queryParams.get('redirectUrl') ?? undefined;
|
||||
const domain = redirectUrl ? makeUrlIntoDomain(redirectUrl) : undefined;
|
||||
|
||||
if (!domain) {
|
||||
return (
|
||||
<div className="permission-grant container">
|
||||
<div className="inner-container">
|
||||
<div className="permission-card">
|
||||
<h1 className="color-white">Permission</h1>
|
||||
<p className="text-color" style={{ textAlign: 'center' }}>
|
||||
No domain found to grant permission to.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const redirectBack = () => {
|
||||
chrome.tabs.getCurrent((tab) => {
|
||||
if (!tab?.id) return;
|
||||
chrome.tabs.update(tab.id, { url: redirectUrl });
|
||||
});
|
||||
};
|
||||
|
@@ -10,12 +10,21 @@ body {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
#__plasmo {
|
||||
height: unset;
|
||||
}
|
||||
|
||||
.permission-request.container {
|
||||
width: 90%;
|
||||
margin: 100px auto;
|
||||
max-width: 628px;
|
||||
}
|
||||
|
||||
.permission-request .inner-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.permission-request h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
@@ -56,6 +65,12 @@ body {
|
||||
border-radius: 11px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.permission-request .card {
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: auto auto auto;
|
||||
}
|
||||
}
|
||||
|
||||
.permission-request .card .icon-circle {
|
||||
width: 2rem;
|
||||
|
@@ -48,33 +48,48 @@ export default function PermissionRequest() {
|
||||
>
|
||||
<h3>Read the source code on GitHub</h3>
|
||||
<p className="text-color paragraph">
|
||||
Don't trust us? Read the code and choose for yourself if its safe!
|
||||
Don't trust us? Read the code and decide for yourself if it's safe!
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<h2>Permission list</h2>
|
||||
<div className="card-list" style={{ paddingBottom: '10rem' }}>
|
||||
<div className="card-list">
|
||||
<Card icon={<Icon name="windows" />}>
|
||||
<h3>Read & change data from all sites</h3>
|
||||
<p className="text-color paragraph">
|
||||
To be able to gather content from the sources. We need to be able to reach those sources. Unfortunately
|
||||
that requires us to request the permissions from all sites.
|
||||
This is so the extension can gather content from the sources. We need to be able to reach those sources.
|
||||
Unfortunately that requires us to request the permissions from all sites.
|
||||
</p>
|
||||
</Card>
|
||||
<Card icon={<Icon name="network" />}>
|
||||
<h3>Network Requests</h3>
|
||||
<p className="text-color paragraph">
|
||||
This permission allows the extension to instruct the browser how to request data from sites. In more
|
||||
technical terms, this allows movie-web to modify HTTP headers that it wouldn't normally be allowed
|
||||
to.
|
||||
</p>
|
||||
<p className="text-color paragraph">
|
||||
You won't be prompted for this permission, it's included in “Read & change data from all sites”.
|
||||
</p>
|
||||
</Card>
|
||||
<Card icon={<Icon name="cookie" />}>
|
||||
<h3>Read and write cookies</h3>
|
||||
<p className="text-color paragraph">
|
||||
Some sources use cookies for authentication. We need to be able to read and set those cookies. This
|
||||
won't be prompted to you, it's included in “Read & change data from all sites”.
|
||||
Some sources use cookies for authentication. We need to be able to read and set those cookies.
|
||||
</p>
|
||||
<p className="text-color paragraph">
|
||||
You won't be prompted for this permission, it's included in “Read & change data from all sites”.
|
||||
</p>
|
||||
</Card>
|
||||
<Card icon={<Icon name="shield" />}>
|
||||
<h3>Active tab</h3>
|
||||
<p className="text-color paragraph">
|
||||
To determine which site has access to the extension or not, we need to know what tab you're currently
|
||||
using. This permission is given to all extensions by default, so your browser won't prompt you for
|
||||
it.
|
||||
using.
|
||||
</p>
|
||||
<p className="text-color paragraph">
|
||||
This permission is given to all extensions by default, so your browser won't prompt you for it.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
@@ -123,5 +123,6 @@ export const removeDynamicRules = async (ruleIds: number[]) => {
|
||||
await (chrome || browser).declarativeNetRequest.updateDynamicRules({
|
||||
removeRuleIds: ruleIds,
|
||||
});
|
||||
if ((chrome || browser).runtime.lastError?.message) throw new Error((chrome || browser).runtime.lastError.message);
|
||||
if ((chrome || browser).runtime.lastError?.message)
|
||||
throw new Error((chrome || browser).runtime.lastError?.message ?? 'Unknown error');
|
||||
};
|
||||
|
@@ -1,3 +1,11 @@
|
||||
export const isChrome = () => {
|
||||
return chrome.runtime.getURL('').startsWith('chrome-extension://');
|
||||
};
|
||||
|
||||
export const isFirefox = () => {
|
||||
try {
|
||||
return browser.runtime.getURL('').startsWith('moz-extension://');
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@@ -3,7 +3,15 @@ import { useStorage } from '@plasmohq/storage/hook';
|
||||
|
||||
import { makeUrlIntoDomain } from '~utils/domains';
|
||||
|
||||
export const DEFAULT_DOMAIN_WHITELIST = ['movie-web.app', 'dev.movie-web.app'];
|
||||
export const DEFAULT_DOMAIN_WHITELIST = [
|
||||
'mw.lonelil.ru',
|
||||
'watch.qtchaos.de',
|
||||
'bmov.app',
|
||||
'bmov.vercel.app',
|
||||
'stream.thehairy.me',
|
||||
'scootydooter.vercel.app',
|
||||
'movie-web-me.vercel.app',
|
||||
];
|
||||
|
||||
export const storage = new Storage();
|
||||
|
||||
@@ -31,5 +39,9 @@ export const isDomainWhitelisted = async (url: string | undefined) => {
|
||||
|
||||
export const assertDomainWhitelist = async (url: string) => {
|
||||
const isWhiteListed = await isDomainWhitelisted(url);
|
||||
if (!isWhiteListed) throw new Error('Domain is not whitelisted');
|
||||
const currentDomain = makeUrlIntoDomain(url);
|
||||
if (!isWhiteListed)
|
||||
throw new Error(
|
||||
`${currentDomain} is not whitelisted. Open the extension and click on the power button to whitelist the site.`,
|
||||
);
|
||||
};
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { isChrome } from './extension';
|
||||
|
||||
export function queryCurrentDomain(cb: (domain: string | null) => void) {
|
||||
const handle = (tabUrl: string | null) => {
|
||||
const handle = (tabUrl: string | undefined) => {
|
||||
if (!tabUrl) cb(null);
|
||||
else cb(tabUrl);
|
||||
};
|
||||
|
@@ -1,18 +1,13 @@
|
||||
{
|
||||
"extends": "plasmo/templates/tsconfig.base",
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"include": [
|
||||
".plasmo/index.d.ts",
|
||||
"./**/*.ts",
|
||||
"./**/*.tsx"
|
||||
],
|
||||
"exclude": ["node_modules"],
|
||||
"include": [".plasmo/index.d.ts", "./**/*.ts", "./**/*.tsx"],
|
||||
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"~*": [
|
||||
"./src/*"
|
||||
]
|
||||
"~*": ["./src/*"]
|
||||
},
|
||||
"baseUrl": "."
|
||||
}
|
||||
|
Reference in New Issue
Block a user