[skip ci] Dev improvement (#1324)

* added ServerSettings for proxy server

* fix: duplicated credentials headers

* simplify settings request

* feat: use user agent

* clean up

* fix: missing status code

* update plugin template
This commit is contained in:
nyagami
2024-11-17 00:20:47 +07:00
committed by GitHub
parent c678b4d12d
commit 87a9f3c2cb
3 changed files with 108 additions and 106 deletions
+8 -1
View File
@@ -1,10 +1,14 @@
import { fetchApi } from '@libs/fetch'; import { fetchApi, fetchProto, fetchText } from '@libs/fetch';
import { Plugin } from '@typings/plugin'; import { Plugin } from '@typings/plugin';
import { Filters } from '@libs/filterInputs'; import { Filters } from '@libs/filterInputs';
import { load as loadCheerio } from 'cheerio'; import { load as loadCheerio } from 'cheerio';
import { defaultCover } from '@libs/defaultCover'; import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus'; import { NovelStatus } from '@libs/novelStatus';
// import { isUrlAbsolute } from '@libs/isAbsoluteUrl'; // import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
// import { storage, localStorage, sessionStorage } from '@libs/storage';
// import { encode, decode } from 'urlencode';
// import dayjs from 'dayjs';
// import { Parser } from 'htmlparser2';
class TemplatePlugin implements Plugin.PluginBase { class TemplatePlugin implements Plugin.PluginBase {
id = ''; id = '';
@@ -15,6 +19,9 @@ class TemplatePlugin implements Plugin.PluginBase {
filters: Filters | undefined = undefined; filters: Filters | undefined = undefined;
imageRequestInit?: Plugin.ImageRequestInit | undefined = undefined; imageRequestInit?: Plugin.ImageRequestInit | undefined = undefined;
//flag indicates whether access to LocalStorage, SesesionStorage is required.
webStorageUtilized?: boolean;
async popularNovels( async popularNovels(
pageNo: number, pageNo: number,
{ {
+65 -76
View File
@@ -2,22 +2,24 @@ import http from 'http';
import httpProxy from 'http-proxy'; import httpProxy from 'http-proxy';
import { exec } from 'child_process'; import { exec } from 'child_process';
const CLIENT_HOST = 'http://localhost:3000';
const proxy = httpProxy.createProxyServer({}); const proxy = httpProxy.createProxyServer({});
let request_mode = 'proxy'; const ServerSettings = {
CLIENT_HOST: 'http://localhost:3000',
const disAllowedRequestHeaders = [ fetchMode: 'proxy',
'sec-ch-ua', cookies: null,
'sec-ch-ua-mobile', disAllowedRequestHeaders: [
'sec-ch-ua-platform', 'sec-ch-ua',
'sec-fetch-site', 'sec-ch-ua-mobile',
'origin', 'sec-ch-ua-platform',
'sec-fetch-site', 'sec-fetch-site',
'sec-fetch-dest', 'origin',
'pragma', 'sec-fetch-site',
]; 'sec-fetch-dest',
'pragma',
const disAllowResponseHeaders = ['link', 'set-cookie', 'set-cookie2']; ],
disAllowResponseHeaders: ['link', 'set-cookie', 'set-cookie2'],
useUserAgent: true,
};
const proxyRequest = (req, res) => { const proxyRequest = (req, res) => {
const _url = new URL(req.url); const _url = new URL(req.url);
@@ -31,10 +33,14 @@ headers:`,
console.log('\t', '\x1b[32m', name + ':', '\x1b[37m', value); console.log('\t', '\x1b[32m', name + ':', '\x1b[37m', value);
}); });
console.log('\x1b[36m', '----------------'); console.log('\x1b[36m', '----------------');
if (request_mode === 'curl') { if (ServerSettings.fetchMode === 'curl') {
//i mean if it works it works i guess, better than nothing //i mean if it works it works i guess, better than nothing
let curl = `curl '${_url.href}' -H 'User-Agent: ${req.headers['user-agent']}'`; let curl = `curl '${_url.href}'`;
if (temp_cookies) curl += ` -H 'Cookie: ${temp_cookies}'`; if (ServerSettings.useUserAgent) {
curl += ` -H 'User-Agent: ${req.headers['user-agent']}'`;
}
if (ServerSettings.cookies)
curl += ` -H 'Cookie: ${ServerSettings.cookies}'`;
if (req.headers.origin2) curl += ` -H 'Origin: ${req.headers.origin2}'`; if (req.headers.origin2) curl += ` -H 'Origin: ${req.headers.origin2}'`;
console.log('Running curl command:', curl); console.log('Running curl command:', curl);
@@ -51,10 +57,7 @@ headers:`,
exec(curl, options, (error, stdout, stderr) => { exec(curl, options, (error, stdout, stderr) => {
if (error) { if (error) {
console.error(`exec error: ${error}`); console.error(`exec error: ${error}`);
res.writeHead(500, { res.statusCode = 500;
'Access-Control-Allow-Origin': CLIENT_HOST,
'Access-Control-Allow-Credentials': true,
});
res.write(`exec error: ${error}`); res.write(`exec error: ${error}`);
res.end(); res.end();
return; return;
@@ -62,27 +65,23 @@ headers:`,
if (stderr) { if (stderr) {
console.error(`stderr: ${stderr}`); console.error(`stderr: ${stderr}`);
} }
res.writeHead(200, { res.statusCode = 200;
'Access-Control-Allow-Origin': CLIENT_HOST,
'Access-Control-Allow-Credentials': true,
});
res.write(stdout); res.write(stdout);
res.end(); res.end();
}); });
} else if (request_mode === 'node-fetch') { } else if (ServerSettings.fetchMode === 'node-fetch') {
fetch(_url.href, { fetch(_url.href, {
'headers': { 'headers': {
'cookie': temp_cookies, 'cookie': ServerSettings.cookies,
'origin': req.headers.origin2, 'origin': req.headers.origin2,
'user-agent': req.headers['user-agent'], 'user-agent': ServerSettings.useUserAgent
? req.headers['user-agent']
: undefined,
}, },
}) })
.then(res2 => res2.text()) .then(res2 => res2.text())
.then(res2 => { .then(res2 => {
res.writeHead(200, { res.statusCode = 200;
'Access-Control-Allow-Origin': CLIENT_HOST,
'Access-Control-Allow-Credentials': true,
});
res.write(res2); res.write(res2);
res.end(); res.end();
}) })
@@ -91,7 +90,7 @@ headers:`,
res.statusCode = 500; res.statusCode = 500;
res.end(); res.end();
}); });
} else if (request_mode === 'proxy') { } else if (ServerSettings.fetchMode === 'proxy') {
proxy.web(req, res, { proxy.web(req, res, {
target: _url.origin, target: _url.origin,
selfHandleResponse: true, selfHandleResponse: true,
@@ -128,15 +127,13 @@ proxy.on('proxyRes', function (proxyRes, req, res) {
return false; return false;
} }
for (const _header in proxyRes.headers) { for (const _header in proxyRes.headers) {
if (!disAllowResponseHeaders.includes(_header)) { if (!ServerSettings.disAllowResponseHeaders.includes(_header)) {
res.setHeader(_header, proxyRes.headers[_header]); res.setHeader(_header, proxyRes.headers[_header]);
} }
} }
for (const _header in disAllowedRequestHeaders) { for (const _header in ServerSettings.disAllowedRequestHeaders) {
delete proxyRes.headers[_header]; delete proxyRes.headers[_header];
} }
res.setHeader('Access-Control-Allow-Origin', CLIENT_HOST);
res.setHeader('Access-Control-Allow-Credentials', true);
proxyRes.on('data', function (chunk) { proxyRes.on('data', function (chunk) {
res.write(chunk); res.write(chunk);
}); });
@@ -145,31 +142,25 @@ proxy.on('proxyRes', function (proxyRes, req, res) {
}); });
}); });
var temp_cookies = null; const settingsHandler = (req, res) => {
let str = '';
const cookiesHandler = (req, res) => {
let cookies = '';
res.setHeader('Access-Control-Allow-Origin', CLIENT_HOST);
res.setHeader('Access-Control-Allow-Credentials', true);
req.on('data', chunk => { req.on('data', chunk => {
cookies += chunk; str += chunk;
}); });
req.on('end', () => { req.on('end', () => {
temp_cookies = cookies; try {
res.end(); const settings = JSON.parse(str);
}); for (let setting in settings) {
}; ServerSettings[setting] = settings[setting];
}
const fetchModeHandler = (req, res) => { res.statusCode = 200;
let fetchMode = ''; res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', CLIENT_HOST); res.write(JSON.stringify(ServerSettings));
res.setHeader('Access-Control-Allow-Credentials', true); } catch {
req.on('data', chunk => { res.statusCode = 400;
fetchMode += chunk; } finally {
}); res.end();
req.on('end', () => { }
request_mode = fetchMode;
res.end();
}); });
}; };
@@ -190,37 +181,35 @@ http
); );
delete req.headers['access-control-request-headers']; delete req.headers['access-control-request-headers'];
} }
if (path === 'cookies') { res.setHeader('Access-Control-Allow-Origin', ServerSettings.CLIENT_HOST);
cookiesHandler(req, res); res.setHeader('Access-Control-Allow-Credentials', true);
} else if (path === 'fetchMode') { if (req.method === 'OPTIONS') {
fetchModeHandler(req, res); res.statusCode = 200;
res.end();
} else if (path === 'settings') {
settingsHandler(req, res);
} else { } else {
try { try {
const _url = new URL(path); const _url = new URL(path);
for (const _header in req.headers) { for (const _header in req.headers) {
if ( if (
req.headers[_header]?.includes('localhost') || req.headers[_header]?.includes('localhost') ||
disAllowedRequestHeaders.includes(_header) ServerSettings.disAllowedRequestHeaders.includes(_header)
) { ) {
delete req.headers[_header]; delete req.headers[_header];
} }
} }
req.headers['sec-fetch-mode'] = 'cors'; req.headers['sec-fetch-mode'] = 'cors';
if (temp_cookies) { if (ServerSettings.cookies) {
req.headers['cookie'] = temp_cookies; req.headers['cookie'] = ServerSettings.cookies;
}
if (!ServerSettings.useUserAgent) {
delete req.headers['user-agent'];
} }
req.headers.host = _url.host; req.headers.host = _url.host;
req.url = _url.toString(); req.url = _url.toString();
res.statusCode = 200; res.statusCode = 200;
if (req.method === 'OPTIONS') { proxyRequest(req, res);
res.writeHead(200, {
'Access-Control-Allow-Origin': CLIENT_HOST,
'Access-Control-Allow-Credentials': true,
});
res.end();
} else {
proxyRequest(req, res);
}
} catch (err) { } catch (err) {
console.log('\x1b[31m', '----------ERRROR----------'); console.log('\x1b[31m', '----------ERRROR----------');
console.error(err); console.error(err);
+35 -29
View File
@@ -8,6 +8,7 @@ import {
Select, Select,
TextField, TextField,
} from '@mui/material'; } from '@mui/material';
import Checkbox from '@mui/material/Checkbox';
import useDebounce from '@hooks/useDebounce'; import useDebounce from '@hooks/useDebounce';
export default function HeadersSection() { export default function HeadersSection() {
@@ -15,36 +16,34 @@ export default function HeadersSection() {
const [cookies, setCookies] = useState(''); const [cookies, setCookies] = useState('');
const debounceCookies = useDebounce(cookies, 500); const debounceCookies = useDebounce(cookies, 500);
const [fetchMode, setFetchMode] = useState('proxy'); const [fetchMode, setFetchMode] = useState('proxy');
const [alertVisible, setAlertVisble] = useState(''); const [alertVisible, setAlertVisble] = useState(false);
const [useUserAgent, setUseUserAgent] = useState(true);
useEffect(() => { useEffect(() => {
if (alertVisible) { if (alertVisible) {
const id = setTimeout(() => setAlertVisble(''), 1000); const id = setTimeout(() => setAlertVisble(false), 1000);
return () => clearTimeout(id); return () => clearTimeout(id);
} }
}, [alertVisible]); }, [alertVisible]);
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
fetch('cookies', { fetch('settings', {
method: 'POST', method: 'POST',
body: debounceCookies, headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
cookies: debounceCookies,
fetchMode: fetchMode,
useUserAgent,
}),
}) })
.then(() => setAlertVisble('Cookies updated')) .then(() => setAlertVisble(true))
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}, [debounceCookies]); }, [debounceCookies, fetchMode, useUserAgent]);
useEffect(() => {
setLoading(true);
fetch('fetchMode', {
method: 'POST',
body: fetchMode,
})
.then(() => setAlertVisble('Fetch mode updated'))
.finally(() => setLoading(false));
}, [fetchMode]);
return ( return (
<AccordionContainer title="Headers" loading={loading}> <AccordionContainer title="Settings" loading={loading}>
{alertVisible ? ( {alertVisible ? (
<Alert <Alert
sx={{ sx={{
@@ -54,20 +53,27 @@ export default function HeadersSection() {
right: 0, right: 0,
}} }}
> >
{alertVisible} Settings updated
</Alert> </Alert>
) : null} ) : null}
<TextField <Box sx={{ display: 'flex', alignItems: 'center' }}>
size="small" <TextField
InputProps={{ size="small"
startAdornment: ( InputProps={{
<InputAdornment position="start">Headers</InputAdornment> startAdornment: (
), <InputAdornment position="start">User Agent</InputAdornment>
}} ),
disabled }}
value={navigator.userAgent} disabled
sx={{ width: '100%' }} value={navigator.userAgent}
/> sx={{ width: '100%' }}
/>
<Checkbox
size="large"
checked={useUserAgent}
onChange={e => setUseUserAgent(e.target.checked)}
/>
</Box>
<Box sx={{ height: 10 }} /> <Box sx={{ height: 10 }} />
<TextField <TextField
size="small" size="small"