@doppelgangerdev/doppelganger 0.4.3 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +162 -162
- package/README.md +39 -37
- package/agent.js +342 -130
- package/dist/assets/index-BXRKDZ1_.css +1 -0
- package/dist/assets/index-Deb2QMGx.js +19 -0
- package/dist/captures/run_1769042882403_589_agent_1769042884446_initial.png +0 -0
- package/dist/captures/run_1769042882403_589_agent_1769042887058.png +0 -0
- package/dist/captures/run_1769042882403_589_agent_1769042888468.webm +0 -0
- package/dist/captures/run_1769043202318_943_agent_1769043206237.png +0 -0
- package/dist/captures/run_1769043202318_943_agent_1769043207415.webm +0 -0
- package/dist/captures/run_1769043449517_97_agent_1769043451350_initial.png +0 -0
- package/dist/captures/run_1769043449517_97_agent_1769043455038.png +0 -0
- package/dist/captures/run_1769043449517_97_agent_1769043456476.webm +0 -0
- package/dist/captures/run_1769043471164_239_agent_1769043472720_initial.png +0 -0
- package/dist/captures/run_1769043471164_239_agent_1769043474022.png +0 -0
- package/dist/captures/run_1769043471164_239_agent_1769043476419.png +0 -0
- package/dist/captures/run_1769043471164_239_agent_1769043477795.webm +0 -0
- package/dist/captures/run_1769080585290_151_agent_1769080595110.png +0 -0
- package/dist/captures/run_1769080585290_151_agent_1769080596335.webm +0 -0
- package/dist/index.html +2 -2
- package/dist/screenshots/agent_1769037343598.png +0 -0
- package/dist/screenshots/agent_1769037357541.png +0 -0
- package/dist/screenshots/scrape_1769037382254.png +0 -0
- package/dist/screenshots/scrape_1769037413189.png +0 -0
- package/dist/screenshots/scrape_1769037449707.png +0 -0
- package/dist/screenshots/scrape_1769037461756.png +0 -0
- package/dist/screenshots/scrape_1769037490581.png +0 -0
- package/dist/screenshots/scrape_1769038242368.png +0 -0
- package/headful.js +76 -21
- package/package.json +3 -1
- package/proxy-rotation.js +133 -90
- package/public/captures/run_1769042882403_589_agent_1769042884446_initial.png +0 -0
- package/public/captures/run_1769042882403_589_agent_1769042887058.png +0 -0
- package/public/captures/run_1769042882403_589_agent_1769042888468.webm +0 -0
- package/public/captures/run_1769043202318_943_agent_1769043206237.png +0 -0
- package/public/captures/run_1769043202318_943_agent_1769043207415.webm +0 -0
- package/public/captures/run_1769043449517_97_agent_1769043451350_initial.png +0 -0
- package/public/captures/run_1769043449517_97_agent_1769043455038.png +0 -0
- package/public/captures/run_1769043449517_97_agent_1769043456476.webm +0 -0
- package/public/captures/run_1769043471164_239_agent_1769043472720_initial.png +0 -0
- package/public/captures/run_1769043471164_239_agent_1769043474022.png +0 -0
- package/public/captures/run_1769043471164_239_agent_1769043476419.png +0 -0
- package/public/captures/run_1769043471164_239_agent_1769043477795.webm +0 -0
- package/public/captures/run_1769080585290_151_agent_1769080595110.png +0 -0
- package/public/captures/run_1769080585290_151_agent_1769080596335.webm +0 -0
- package/public/screenshots/agent_1769037343598.png +0 -0
- package/public/screenshots/agent_1769037357541.png +0 -0
- package/public/screenshots/scrape_1769037382254.png +0 -0
- package/public/screenshots/scrape_1769037413189.png +0 -0
- package/public/screenshots/scrape_1769037449707.png +0 -0
- package/public/screenshots/scrape_1769037461756.png +0 -0
- package/public/screenshots/scrape_1769037490581.png +0 -0
- package/public/screenshots/scrape_1769038242368.png +0 -0
- package/scrape.js +163 -66
- package/server.js +127 -72
- package/dist/assets/index-D68YZVOp.js +0 -19
- package/dist/assets/index-WbwoTnJa.css +0 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/index.html
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<link
|
|
12
12
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap"
|
|
13
13
|
rel="stylesheet">
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-Deb2QMGx.js"></script>
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BXRKDZ1_.css">
|
|
16
16
|
</head>
|
|
17
17
|
|
|
18
18
|
<body class="bg-[#020202] text-gray-100 font-sans h-full overflow-hidden selection:bg-white selection:text-black">
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/headful.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const { chromium } = require('playwright');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const { getProxySelection } = require('./proxy-rotation');
|
|
4
|
+
const { getProxySelection } = require('./proxy-rotation');
|
|
5
|
+
const { selectUserAgent } = require('./user-agent-settings');
|
|
5
6
|
|
|
6
7
|
const STORAGE_STATE_PATH = path.join(__dirname, 'storage_state.json');
|
|
7
8
|
const STORAGE_STATE_FILE = (() => {
|
|
@@ -16,12 +17,6 @@ const STORAGE_STATE_FILE = (() => {
|
|
|
16
17
|
return STORAGE_STATE_PATH;
|
|
17
18
|
})();
|
|
18
19
|
|
|
19
|
-
// Use a consistent User Agent or the same pool
|
|
20
|
-
const userAgents = [
|
|
21
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
|
|
22
|
-
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
|
|
23
|
-
];
|
|
24
|
-
|
|
25
20
|
let activeSession = null;
|
|
26
21
|
|
|
27
22
|
const teardownActiveSession = async () => {
|
|
@@ -53,8 +48,7 @@ async function handleHeadful(req, res) {
|
|
|
53
48
|
const rotateProxiesRaw = req.body.rotateProxies ?? req.query.rotateProxies;
|
|
54
49
|
const rotateProxies = String(rotateProxiesRaw).toLowerCase() === 'true' || rotateProxiesRaw === true;
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
const selectedUA = userAgents[0];
|
|
51
|
+
const selectedUA = selectUserAgent(false);
|
|
58
52
|
|
|
59
53
|
console.log(`Opening headful browser for: ${url}`);
|
|
60
54
|
|
|
@@ -89,18 +83,79 @@ async function handleHeadful(req, res) {
|
|
|
89
83
|
contextOptions.storageState = STORAGE_STATE_FILE;
|
|
90
84
|
}
|
|
91
85
|
|
|
92
|
-
const context = await browser.newContext(contextOptions);
|
|
93
|
-
await context.addInitScript(() => {
|
|
94
|
-
window.open = () => null;
|
|
95
|
-
document.addEventListener('click', (event) => {
|
|
96
|
-
const target = event.target;
|
|
97
|
-
const anchor = target && target.closest ? target.closest('a[target="_blank"]') : null;
|
|
98
|
-
if (anchor) {
|
|
99
|
-
event.preventDefault();
|
|
100
|
-
}
|
|
101
|
-
}, true);
|
|
102
|
-
});
|
|
103
|
-
|
|
86
|
+
const context = await browser.newContext(contextOptions);
|
|
87
|
+
await context.addInitScript(() => {
|
|
88
|
+
window.open = () => null;
|
|
89
|
+
document.addEventListener('click', (event) => {
|
|
90
|
+
const target = event.target;
|
|
91
|
+
const anchor = target && target.closest ? target.closest('a[target="_blank"]') : null;
|
|
92
|
+
if (anchor) {
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
}
|
|
95
|
+
}, true);
|
|
96
|
+
});
|
|
97
|
+
await context.addInitScript(() => {
|
|
98
|
+
const cursorId = 'dg-cursor-overlay';
|
|
99
|
+
const dotId = 'dg-click-dot';
|
|
100
|
+
if (document.getElementById(cursorId)) return;
|
|
101
|
+
const cursor = document.createElement('div');
|
|
102
|
+
cursor.id = cursorId;
|
|
103
|
+
cursor.style.cssText = [
|
|
104
|
+
'position:fixed',
|
|
105
|
+
'top:0',
|
|
106
|
+
'left:0',
|
|
107
|
+
'width:18px',
|
|
108
|
+
'height:18px',
|
|
109
|
+
'margin-left:-9px',
|
|
110
|
+
'margin-top:-9px',
|
|
111
|
+
'border:2px solid rgba(56,189,248,0.7)',
|
|
112
|
+
'background:rgba(56,189,248,0.25)',
|
|
113
|
+
'border-radius:50%',
|
|
114
|
+
'box-shadow:0 0 10px rgba(56,189,248,0.6)',
|
|
115
|
+
'pointer-events:none',
|
|
116
|
+
'z-index:2147483647',
|
|
117
|
+
'transform:translate3d(0,0,0)',
|
|
118
|
+
'transition:transform 60ms ease-out'
|
|
119
|
+
].join(';');
|
|
120
|
+
const dot = document.createElement('div');
|
|
121
|
+
dot.id = dotId;
|
|
122
|
+
dot.style.cssText = [
|
|
123
|
+
'position:fixed',
|
|
124
|
+
'top:0',
|
|
125
|
+
'left:0',
|
|
126
|
+
'width:10px',
|
|
127
|
+
'height:10px',
|
|
128
|
+
'margin-left:-5px',
|
|
129
|
+
'margin-top:-5px',
|
|
130
|
+
'background:rgba(239,68,68,0.9)',
|
|
131
|
+
'border-radius:50%',
|
|
132
|
+
'box-shadow:0 0 12px rgba(239,68,68,0.8)',
|
|
133
|
+
'pointer-events:none',
|
|
134
|
+
'z-index:2147483647',
|
|
135
|
+
'opacity:0',
|
|
136
|
+
'transform:translate3d(0,0,0) scale(0.6)',
|
|
137
|
+
'transition:opacity 120ms ease, transform 120ms ease'
|
|
138
|
+
].join(';');
|
|
139
|
+
document.documentElement.appendChild(cursor);
|
|
140
|
+
document.documentElement.appendChild(dot);
|
|
141
|
+
const move = (x, y) => {
|
|
142
|
+
cursor.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
|
143
|
+
};
|
|
144
|
+
window.addEventListener('mousemove', (e) => move(e.clientX, e.clientY), { passive: true });
|
|
145
|
+
window.addEventListener('click', (e) => {
|
|
146
|
+
dot.style.left = `${e.clientX}px`;
|
|
147
|
+
dot.style.top = `${e.clientY}px`;
|
|
148
|
+
dot.style.opacity = '1';
|
|
149
|
+
dot.style.transform = 'translate3d(0,0,0) scale(1)';
|
|
150
|
+
cursor.style.transform = `translate3d(${e.clientX}px, ${e.clientY}px, 0) scale(0.65)`;
|
|
151
|
+
setTimeout(() => {
|
|
152
|
+
dot.style.opacity = '0';
|
|
153
|
+
dot.style.transform = 'translate3d(0,0,0) scale(0.6)';
|
|
154
|
+
cursor.style.transform = `translate3d(${e.clientX}px, ${e.clientY}px, 0) scale(1)`;
|
|
155
|
+
}, 180);
|
|
156
|
+
}, true);
|
|
157
|
+
});
|
|
158
|
+
const page = await context.newPage();
|
|
104
159
|
|
|
105
160
|
const closeIfExtra = async (extraPage) => {
|
|
106
161
|
if (!extraPage || extraPage === page) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doppelgangerdev/doppelganger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"doppelganger": "bin/cli.js"
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"react": "^19.2.3",
|
|
47
47
|
"react-dom": "^19.2.3",
|
|
48
48
|
"react-router-dom": "^7.11.0",
|
|
49
|
+
"react-window": "^1.8.8",
|
|
49
50
|
"session-file-store": "^1.5.0"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
@@ -53,6 +54,7 @@
|
|
|
53
54
|
"@types/node": "^25.0.3",
|
|
54
55
|
"@types/react": "^19.2.7",
|
|
55
56
|
"@types/react-dom": "^19.2.3",
|
|
57
|
+
"@types/react-window": "^1.8.8",
|
|
56
58
|
"@vitejs/plugin-react": "^5.1.2",
|
|
57
59
|
"autoprefixer": "^10.4.23",
|
|
58
60
|
"cross-env": "^10.1.0",
|
package/proxy-rotation.js
CHANGED
|
@@ -8,12 +8,14 @@ const PROXY_FILES = [
|
|
|
8
8
|
path.join(__dirname, 'proxies.json')
|
|
9
9
|
];
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const ROTATION_MODES = new Set(['round-robin', 'random']);
|
|
12
|
+
|
|
13
|
+
let cached = {
|
|
14
|
+
file: null,
|
|
15
|
+
mtimeMs: 0,
|
|
16
|
+
config: { proxies: [], defaultProxyId: null, includeDefaultInRotation: false, rotationMode: 'round-robin' }
|
|
17
|
+
};
|
|
18
|
+
let rotationIndex = 0;
|
|
17
19
|
|
|
18
20
|
const normalizeServer = (raw) => {
|
|
19
21
|
if (!raw) return '';
|
|
@@ -71,21 +73,27 @@ const normalizeProxy = (entry) => {
|
|
|
71
73
|
return null;
|
|
72
74
|
};
|
|
73
75
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
76
|
+
const normalizeRotationMode = (mode) => {
|
|
77
|
+
if (ROTATION_MODES.has(mode)) return mode;
|
|
78
|
+
return 'round-robin';
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const loadProxyFile = (filePath) => {
|
|
82
|
+
try {
|
|
83
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
84
|
+
const parsed = JSON.parse(raw);
|
|
85
|
+
if (Array.isArray(parsed)) {
|
|
86
|
+
return { proxies: parsed, defaultProxyId: null, includeDefaultInRotation: false, rotationMode: 'round-robin' };
|
|
87
|
+
}
|
|
88
|
+
const proxies = Array.isArray(parsed.proxies) ? parsed.proxies : [];
|
|
89
|
+
const defaultProxyId = parsed.defaultProxyId || null;
|
|
90
|
+
const includeDefaultInRotation = !!parsed.includeDefaultInRotation;
|
|
91
|
+
const rotationMode = normalizeRotationMode(parsed.rotationMode);
|
|
92
|
+
return { proxies, defaultProxyId, includeDefaultInRotation, rotationMode };
|
|
93
|
+
} catch {
|
|
94
|
+
return { proxies: [], defaultProxyId: null, includeDefaultInRotation: false, rotationMode: 'round-robin' };
|
|
95
|
+
}
|
|
96
|
+
};
|
|
89
97
|
|
|
90
98
|
const loadProxyConfig = () => {
|
|
91
99
|
const filePath = PROXY_FILES.find((candidate) => {
|
|
@@ -97,9 +105,9 @@ const loadProxyConfig = () => {
|
|
|
97
105
|
});
|
|
98
106
|
|
|
99
107
|
if (!filePath) {
|
|
100
|
-
cached = { file: null, mtimeMs: 0, config: { proxies: [], defaultProxyId: null, includeDefaultInRotation: false } };
|
|
101
|
-
return cached.config;
|
|
102
|
-
}
|
|
108
|
+
cached = { file: null, mtimeMs: 0, config: { proxies: [], defaultProxyId: null, includeDefaultInRotation: false, rotationMode: 'round-robin' } };
|
|
109
|
+
return cached.config;
|
|
110
|
+
}
|
|
103
111
|
|
|
104
112
|
try {
|
|
105
113
|
const stat = fs.statSync(filePath);
|
|
@@ -112,26 +120,28 @@ const loadProxyConfig = () => {
|
|
|
112
120
|
const defaultProxyId = rawConfig.defaultProxyId && proxies.some((proxy) => proxy.id === rawConfig.defaultProxyId)
|
|
113
121
|
? rawConfig.defaultProxyId
|
|
114
122
|
: null;
|
|
115
|
-
const config = {
|
|
116
|
-
proxies,
|
|
117
|
-
defaultProxyId,
|
|
118
|
-
includeDefaultInRotation: !!rawConfig.includeDefaultInRotation
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
123
|
+
const config = {
|
|
124
|
+
proxies,
|
|
125
|
+
defaultProxyId,
|
|
126
|
+
includeDefaultInRotation: !!rawConfig.includeDefaultInRotation,
|
|
127
|
+
rotationMode: normalizeRotationMode(rawConfig.rotationMode)
|
|
128
|
+
};
|
|
129
|
+
cached = { file: filePath, mtimeMs, config };
|
|
130
|
+
return config;
|
|
131
|
+
} catch {
|
|
132
|
+
cached = { file: filePath, mtimeMs: 0, config: { proxies: [], defaultProxyId: null, includeDefaultInRotation: false, rotationMode: 'round-robin' } };
|
|
133
|
+
return cached.config;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const saveProxyConfig = (config) => {
|
|
138
|
+
const target = DATA_PROXY_FILE;
|
|
139
|
+
const payload = {
|
|
140
|
+
defaultProxyId: config.defaultProxyId || null,
|
|
141
|
+
proxies: Array.isArray(config.proxies) ? config.proxies : [],
|
|
142
|
+
includeDefaultInRotation: !!config.includeDefaultInRotation,
|
|
143
|
+
rotationMode: normalizeRotationMode(config.rotationMode)
|
|
144
|
+
};
|
|
135
145
|
fs.writeFileSync(target, JSON.stringify(payload, null, 2));
|
|
136
146
|
try {
|
|
137
147
|
const stat = fs.statSync(target);
|
|
@@ -149,12 +159,13 @@ const listProxies = () => {
|
|
|
149
159
|
server: 'host_ip',
|
|
150
160
|
label: 'Host IP (no proxy)'
|
|
151
161
|
};
|
|
152
|
-
return {
|
|
153
|
-
proxies: [hostEntry, ...(config.proxies || [])],
|
|
154
|
-
defaultProxyId: config.defaultProxyId || 'host',
|
|
155
|
-
includeDefaultInRotation: !!config.includeDefaultInRotation
|
|
156
|
-
|
|
157
|
-
};
|
|
162
|
+
return {
|
|
163
|
+
proxies: [hostEntry, ...(config.proxies || [])],
|
|
164
|
+
defaultProxyId: config.defaultProxyId || 'host',
|
|
165
|
+
includeDefaultInRotation: !!config.includeDefaultInRotation,
|
|
166
|
+
rotationMode: normalizeRotationMode(config.rotationMode)
|
|
167
|
+
};
|
|
168
|
+
};
|
|
158
169
|
|
|
159
170
|
const addProxy = (entry) => {
|
|
160
171
|
const normalized = normalizeProxy(entry);
|
|
@@ -170,11 +181,32 @@ const addProxies = (entries) => {
|
|
|
170
181
|
const normalizedEntries = entries.map(normalizeProxy).filter(Boolean);
|
|
171
182
|
if (normalizedEntries.length === 0) return null;
|
|
172
183
|
const config = loadProxyConfig();
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
184
|
+
const existingByServer = new Map(
|
|
185
|
+
config.proxies.map((proxy) => [String(proxy.server || '').toLowerCase(), proxy])
|
|
186
|
+
);
|
|
187
|
+
const seenServers = new Set();
|
|
188
|
+
const updates = [];
|
|
189
|
+
const additions = [];
|
|
190
|
+
|
|
191
|
+
normalizedEntries.forEach((proxy) => {
|
|
192
|
+
const serverKey = String(proxy.server || '').toLowerCase();
|
|
193
|
+
if (!serverKey || seenServers.has(serverKey)) return;
|
|
194
|
+
seenServers.add(serverKey);
|
|
195
|
+
const existing = existingByServer.get(serverKey);
|
|
196
|
+
if (existing) {
|
|
197
|
+
updates.push({ ...existing, ...proxy, id: existing.id });
|
|
198
|
+
} else {
|
|
199
|
+
additions.push({ ...proxy, id: `proxy_${crypto.randomBytes(6).toString('hex')}` });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const merged = config.proxies.map((proxy) => {
|
|
204
|
+
const serverKey = String(proxy.server || '').toLowerCase();
|
|
205
|
+
const replacement = updates.find((item) => String(item.server || '').toLowerCase() === serverKey);
|
|
206
|
+
return replacement || proxy;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const proxies = [...merged, ...additions];
|
|
178
210
|
const next = { ...config, proxies };
|
|
179
211
|
return saveProxyConfig(next);
|
|
180
212
|
};
|
|
@@ -192,13 +224,13 @@ const updateProxy = (id, entry) => {
|
|
|
192
224
|
return saveProxyConfig({ ...config, proxies });
|
|
193
225
|
};
|
|
194
226
|
|
|
195
|
-
const deleteProxy = (id) => {
|
|
196
|
-
if (!id) return null;
|
|
197
|
-
const config = loadProxyConfig();
|
|
198
|
-
const proxies = config.proxies.filter((proxy) => proxy.id !== id);
|
|
199
|
-
const defaultProxyId = config.defaultProxyId === id ? null : config.defaultProxyId;
|
|
200
|
-
return saveProxyConfig({ proxies, defaultProxyId });
|
|
201
|
-
};
|
|
227
|
+
const deleteProxy = (id) => {
|
|
228
|
+
if (!id) return null;
|
|
229
|
+
const config = loadProxyConfig();
|
|
230
|
+
const proxies = config.proxies.filter((proxy) => proxy.id !== id);
|
|
231
|
+
const defaultProxyId = config.defaultProxyId === id ? null : config.defaultProxyId;
|
|
232
|
+
return saveProxyConfig({ ...config, proxies, defaultProxyId });
|
|
233
|
+
};
|
|
202
234
|
|
|
203
235
|
const setDefaultProxy = (id) => {
|
|
204
236
|
const config = loadProxyConfig();
|
|
@@ -209,17 +241,26 @@ const setDefaultProxy = (id) => {
|
|
|
209
241
|
return saveProxyConfig({ ...config, defaultProxyId: id });
|
|
210
242
|
};
|
|
211
243
|
|
|
212
|
-
const setIncludeDefaultInRotation = (enabled) => {
|
|
213
|
-
const config = loadProxyConfig();
|
|
214
|
-
return saveProxyConfig({ ...config, includeDefaultInRotation: !!enabled });
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
244
|
+
const setIncludeDefaultInRotation = (enabled) => {
|
|
245
|
+
const config = loadProxyConfig();
|
|
246
|
+
return saveProxyConfig({ ...config, includeDefaultInRotation: !!enabled });
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const setRotationMode = (mode) => {
|
|
250
|
+
const config = loadProxyConfig();
|
|
251
|
+
return saveProxyConfig({ ...config, rotationMode: normalizeRotationMode(mode) });
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const getNextProxy = (proxies, mode) => {
|
|
255
|
+
if (!proxies.length) return null;
|
|
256
|
+
if (mode === 'random') {
|
|
257
|
+
const index = Math.floor(Math.random() * proxies.length);
|
|
258
|
+
return proxies[index];
|
|
259
|
+
}
|
|
260
|
+
const selected = proxies[rotationIndex % proxies.length];
|
|
261
|
+
rotationIndex += 1;
|
|
262
|
+
return selected;
|
|
263
|
+
};
|
|
223
264
|
|
|
224
265
|
const getProxySelection = (rotateProxies) => {
|
|
225
266
|
const config = loadProxyConfig();
|
|
@@ -229,24 +270,25 @@ const getProxySelection = (rotateProxies) => {
|
|
|
229
270
|
const defaultProxy = config.defaultProxyId
|
|
230
271
|
? proxies.find((proxy) => proxy.id === config.defaultProxyId) || null
|
|
231
272
|
: null;
|
|
232
|
-
const defaultIsHost = !config.defaultProxyId;
|
|
233
|
-
const includeDefaultInRotation = !!config.includeDefaultInRotation;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
273
|
+
const defaultIsHost = !config.defaultProxyId;
|
|
274
|
+
const includeDefaultInRotation = !!config.includeDefaultInRotation;
|
|
275
|
+
const rotationMode = normalizeRotationMode(config.rotationMode);
|
|
276
|
+
|
|
277
|
+
if (rotateProxies) {
|
|
278
|
+
let rotationPool = pool;
|
|
279
|
+
if (!includeDefaultInRotation) {
|
|
280
|
+
if (defaultIsHost) {
|
|
239
281
|
rotationPool = pool.filter((proxy) => proxy.id !== 'host');
|
|
240
282
|
} else {
|
|
241
|
-
rotationPool = pool.filter((proxy) => proxy.id !== config.defaultProxyId);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
if (rotationPool.length > 0) {
|
|
245
|
-
const picked = getNextProxy(rotationPool);
|
|
246
|
-
return { proxy: picked && picked.id !== 'host' ? picked : null, mode: 'rotate' };
|
|
247
|
-
}
|
|
248
|
-
if (defaultProxy) return { proxy: defaultProxy, mode: 'default' };
|
|
249
|
-
return { proxy: null, mode: 'host' };
|
|
283
|
+
rotationPool = pool.filter((proxy) => proxy.id !== config.defaultProxyId);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (rotationPool.length > 0) {
|
|
287
|
+
const picked = getNextProxy(rotationPool, rotationMode);
|
|
288
|
+
return { proxy: picked && picked.id !== 'host' ? picked : null, mode: 'rotate' };
|
|
289
|
+
}
|
|
290
|
+
if (defaultProxy) return { proxy: defaultProxy, mode: 'default' };
|
|
291
|
+
return { proxy: null, mode: 'host' };
|
|
250
292
|
}
|
|
251
293
|
|
|
252
294
|
if (defaultProxy) return { proxy: defaultProxy, mode: 'default' };
|
|
@@ -261,5 +303,6 @@ module.exports = {
|
|
|
261
303
|
updateProxy,
|
|
262
304
|
deleteProxy,
|
|
263
305
|
setDefaultProxy,
|
|
264
|
-
setIncludeDefaultInRotation
|
|
306
|
+
setIncludeDefaultInRotation,
|
|
307
|
+
setRotationMode
|
|
265
308
|
};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|