diff --git a/zefie_wtvp_minisrv/includes/ServiceDeps/https/zefienetCA.pem b/zefie_wtvp_minisrv/includes/ServiceDeps/https/zefienetCA.pem
new file mode 100644
index 00000000..9d1aae8f
--- /dev/null
+++ b/zefie_wtvp_minisrv/includes/ServiceDeps/https/zefienetCA.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGJzCCBA+gAwIBAgIUTKPx4tUyiX93N/egPKsjfZQNlCUwDQYJKoZIhvcNAQEL
+BQAwgaExCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwG
+SHVkc29uMRcwFQYDVQQKDA5aZWZpZSBOZXR3b3JrczEZMBcGA1UECwwQTG9jYWwg
+TmV0d29yayBDQTEaMBgGA1UEAwwRWmVmaWUgTmV0d29ya3MgQ0ExHjAcBgkqhkiG
+9w0BCQEWD3plZmllQHplZmllLm5ldDAgFw0yNDEwMTkxNDIzNTdaGA8yMDg0MTAx
+OTE0MjM1N1owgaExCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazEPMA0G
+A1UEBwwGSHVkc29uMRcwFQYDVQQKDA5aZWZpZSBOZXR3b3JrczEZMBcGA1UECwwQ
+TG9jYWwgTmV0d29yayBDQTEaMBgGA1UEAwwRWmVmaWUgTmV0d29ya3MgQ0ExHjAc
+BgkqhkiG9w0BCQEWD3plZmllQHplZmllLm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAJxwTr8iA/6kHZ3LL+PUQcAPqk9iRMH5YV4e1Wkq33cefFHN
+8s/yqac1is48NJq2G3oEXnQo6e1Bo7Y3NfR4hAHzW7gz/lRxukKkI1XH6QyIVigd
+sdRbq13WeDba2YEbJDP1XbbJMFupeZzEhOeG5EMRaqEgl0TE/AjOt2fD1iybA6Dr
+z+wMA4LOFbfth0tfOpeioiDAVqkvhp9hl3UF5ThMBplQJ7k3KbX82jwc3Ihl4cla
+bT7F+ypKieQx7YRl1JcqZ361TgbIzE/rEG7zK3bH9SJTjtqmphn9/3yZ8ualguqY
+VkJz0fdOWPQaqLmR+hQaVlyEfUEEw89MZ/TPHZY8pW79fp7OrM/KEL6mEz8E1zmJ
+T6n5+rILA3PM036qn1WEVvmh3Rtv5sINkrg7nNw6vZlDq8lzzu0cae+IqiikkTT+
+oFZ40jFEdd1Cr0m1irU5rLaP/SpDK6uz1T+JWgDSS8vdIARzxMTrNnL0XnEmjVmn
+kMab43iQ8UOcOmf/AJAdfzFS6C3SXLsz4aeeMKoEoFlut/8hWD3x6x9CTYUna18U
+e5y3Lhe17yASxKCvGHjQ1WvV/FG5MYPosLwTq5Baq57DTrz1rR0oGqjTP5u9ehWg
+8+3BbyU3kKEslsNlYsMAzRbqLe430WOBsgcJVMdUKmH7h3FfxmR+jsLrnJCXAgMB
+AAGjUzBRMB0GA1UdDgQWBBSWZoNRxAe1AyZeCSu6xOsIIZmiyTAfBgNVHSMEGDAW
+gBSWZoNRxAe1AyZeCSu6xOsIIZmiyTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4ICAQB7ek8RNQjcf9YlPPrMXsDHomSilDcEcjFZf6614SRjU+5WUgR0
+ABtCBp2X4hZzFq6/Tp/B/Y5Lwcv0jn8EhvZ0eTPQ0hALAvVKUvAh1ZTiQKCNPqTn
+xIJmeBKyWSfZUTOGiXoJnyi32wyIiKOLyqNWSFkR8GYIIhqQMTuGnMaXJ+XH2aJH
+cNH0iXtl1I/BrVtYPGC5LEEiPVL3tgKPL9EXZik+SkP33IxPFX3DefjfDcsFPEAR
+3T4026CTapMNKO3b6oQI8eeh91rLd2gzczdxjjod1W66xiNEdyFkHwxou236ePxV
+wUTB6tLYFzT1y2S+ANTbDyJwXnJqm90cO5iwanyofThvStIOSS2WQFuFshpSE7DS
+yCyc/z5T2/cM3DTTPJRQvOZHhJQdsS4Za4x/z9y3qi33Ko/L4YKCse49NqGG1BVm
+SXajYj62koVmD5ZwvEISRWQaRCRHLAAU3kBZdC4edYmULf4R9ORg2Qe6oAsjoBJg
+Sl4rmcM+0/ijviB8lARHNDwsxTUp6uhbe9pPKIH0dFN0G2JIe9tNHX8vpxIL+o9Q
+iQRaJWl7eMMfewr7IDfQaajNRjK4XyBsV8NEZn0C/jaq92XNwRjjCDaJul40qbom
+bv14SzRZP4arnRlCbfxxGlKfVoMf6rwlxmn5Jf5ArG+wm/+FGzdzcVlDQw==
+-----END CERTIFICATE-----
diff --git a/zefie_wtvp_minisrv/includes/ServiceVault/wtv-proxy/img/catchall.js b/zefie_wtvp_minisrv/includes/ServiceVault/wtv-proxy/img/catchall.js
new file mode 100644
index 00000000..1b17ab9a
--- /dev/null
+++ b/zefie_wtvp_minisrv/includes/ServiceVault/wtv-proxy/img/catchall.js
@@ -0,0 +1,33 @@
+minisrv_service_file = true;
+request_is_async = true;
+
+const proxyUrl = minisrv_config.services['wtv-proxy'].wrp_url;
+if (!proxyUrl.endsWith('/')) {
+ proxyUrl += '/';
+}
+// Remove 'wtv-proxy:/' from the start of request_url
+let forwardPath = request_headers.request_url.replace(/^wtv-proxy:\//, '');
+
+// Build the full URL to forward to
+const targetUrl = proxyUrl + forwardPath;
+
+// Forward the request using http(s) module
+const urlObj = new URL(targetUrl);
+const lib = urlObj.protocol === 'https:' ? https : http;
+
+lib.get(targetUrl, (res) => {
+ let headers = `200 OK\n`;
+ // Copy content-type if present
+ if (res.headers['content-type']) {
+ headers += `Content-Type: ${res.headers['content-type']}\n`;
+ }
+ // Optionally copy other headers as needed
+
+ let data = [];
+ res.on('data', chunk => data.push(chunk));
+ res.on('end', () => {
+ sendToClient(socket, headers, Buffer.concat(data));
+ });
+}).on('error', err => {
+ sendToClient(socket, '200 OK\nContent-Type: text/plain', `Error fetching image: ${err.message}`);
+});
\ No newline at end of file
diff --git a/zefie_wtvp_minisrv/includes/ServiceVault/wtv-proxy/map/catchall.js b/zefie_wtvp_minisrv/includes/ServiceVault/wtv-proxy/map/catchall.js
new file mode 100644
index 00000000..59396b71
--- /dev/null
+++ b/zefie_wtvp_minisrv/includes/ServiceVault/wtv-proxy/map/catchall.js
@@ -0,0 +1,68 @@
+minisrv_service_file = true;
+request_is_async = true;
+
+const proxyUrl = minisrv_config.services['wtv-proxy'].wrp_url;
+if (!proxyUrl.endsWith('/')) {
+ proxyUrl += '/';
+}
+// Remove 'wtv-proxy:/' from the start of request_url
+let forwardPath = request_headers.request_url.replace(/^wtv-proxy:\//, '');
+
+// Build the full URL to forward to
+var targetUrl = proxyUrl + forwardPath;
+
+// Forward the request using http(s) module
+const urlObj = new URL(targetUrl);
+const lib = urlObj.protocol === 'https:' ? https : http;
+
+coords = request_headers.request_url.split("?")[1];
+if (!coords) {
+ coords = '0,0'
+}
+
+console.log(`Forwarding request to ${targetUrl} with coordinates ${coords}`);
+targetUrl += `?${coords}`; // Append coordinates to the target URL
+
+lib.get(targetUrl, (res) => {
+ let headers = `200 OK\n`;
+ // Copy content-type if present
+ if (res.headers['content-type']) {
+ headers += `Content-Type: ${res.headers['content-type']}\n`;
+ }
+ // Optionally copy other headers as needed
+
+ let data = '';
+ res.on('data', chunk => data += chunk);
+ res.on('end', () => {
+ if (data.startsWith(' Welcome to the Web Rendering Proxy. ${request_headers.query.err} and extract the URLs
+ const aHrefMatch = data.match(/]+type=["']text["'][^>]+name=["']url["'][^>]+value=["']([^"']+)["']/i);
+ let pageUrl = '';
+ if (urlInputMatch) {
+ pageUrl = urlInputMatch[1];
+ }
+ var redirectUrl = `wtv-proxy:/proxy?id=${proxy_id}&t=${imgExt}&url=${encodeURIComponent(pageUrl)}`;
+ sendToClient(socket, {'Status': 302, 'Location': redirectUrl}, '');
+ } else {
+ var idx = data.indexOf('
');
+ data = data.substring(0, idx);
+ var redirectUrl = `wtv-proxy:/proxy?err=${escape(data)}`;
+ sendToClient(socket, {'Status': 302, 'Location': redirectUrl}, '');
+ }
+ });
+}).on('error', err => {
+ sendToClient(socket, '200 OK\nContent-Type: text/plain', `Error fetching image: ${err.message}`);
+});
\ No newline at end of file
diff --git a/zefie_wtvp_minisrv/includes/ServiceVault/wtv-proxy/proxy.js b/zefie_wtvp_minisrv/includes/ServiceVault/wtv-proxy/proxy.js
new file mode 100644
index 00000000..b06bb8eb
--- /dev/null
+++ b/zefie_wtvp_minisrv/includes/ServiceVault/wtv-proxy/proxy.js
@@ -0,0 +1,118 @@
+minisrv_service_file = true;
+request_is_async = true;
+
+proxyUrl = minisrv_config.services[service_name].wrp_url;
+if (!proxyUrl.endsWith('/')) {
+ proxyUrl += '/';
+}
+
+if (!request_headers.query.url) {
+ headers = `200 OK
+Content-Type: text/html`;
+ data = `
+
+
+ Web Rendering Proxy
+
Please provide a valid URL to render.Error
');
+ } else {
+ const params = new URLSearchParams({
+ url: request_headers.query.url,
+ z: request_headers.query.z || '1.0',
+ t: request_headers.query.t || 'jpg',
+ c: request_headers.query.c || '256',
+ m: request_headers.query.m || 'ismap'
+ });
+ const fullUrl = proxyUrl + '?' + params.toString();
+ const urlObj = new URL(fullUrl);
+ const lib = urlObj.protocol === 'https:' ? https : http;
+ if (request_headers.query.id) {
+ finishPage(``);
+ } else {
+ function fetch(url) {
+ return new Promise((resolve, reject) => {
+ lib.get(url, (res) => {
+ let data = '';
+ res.on('data', chunk => data += chunk);
+ res.on('end', () => resolve({ text: () => Promise.resolve(data) }));
+ }).on('error', reject);
+ });
+ }
+ fetch(fullUrl)
+ .then(response => response.text())
+ .then(text => { process(text); })
+ .catch(err => { finishPage(`Error fetching page: ${err.message}`); });
+ }
+ }
+}
+
+
+function process(content) {
+ if (content.startsWith('
and extract the URLs
+ const aHrefMatch = content.match(/ `
`).join('
'));
+ // You can now use the `links` array as needed
+
+ } else {
+ var idx = content.indexOf('
');
+ content = content.substring(0, idx);
+ finishPage(content);
+ }
+}
+
+function finishPage(content) {
+ headers = `200 OK
+Content-Type: text/html`;
+ data = `
+
+
Please provide a search query.
+ + +`; + sendToClient(socket, headers, data); +} else { + const params = new URLSearchParams(); + for (const key in request_headers.query) { + if (request_headers.query.hasOwnProperty(key)) { + params.append(key, request_headers.query[key]); + } + } + params.append('format', 'json'); + const urlObj = new URL(searchUrl); + const lib = urlObj.protocol === 'https:' ? https : http; + var post_data = params.toString(); + const options = { + protocol: urlObj.protocol, + hostname: urlObj.hostname, + port: parseInt(urlObj.port) || (urlObj.protocol === 'https:' ? 443 : 80), + path: urlObj.pathname + (urlObj.search || ''), + method: 'POST', + headers: { + 'User-Agent': request_headers['User-Agent'] || 'Mozilla/4.0 WebTV/2.6 (compatible; MSIE 4.0)', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(post_data), + 'Accept': 'application/json, text/plain, */*', + } + }; + if (urlObj.protocol === 'https:' && urlObj.hostname.includes("lan.zef")) { + options.ca = wtvshared.getServiceDep('https/zefienetCA.pem'); + } + + fetch(lib, options, post_data) + .then(response => response.text()) + .then(text => { process(text); }) + .catch(err => { finishPage(`Error fetching page: ${err.message}`); }); +} + +function fetch(lib, options, post_data) { + return new Promise((resolve, reject) => { + var req = lib.request(options, (res) => { + let data = ''; + res.on('data', chunk => data += chunk); + res.on('end', () => resolve({ text: () => Promise.resolve(data) })); + }).on('error', reject); + req.write(post_data); // 🔁 Send body + req.end(); + }); +} + +function process(data) { + if (data === "Too Many Requests") { + sendToClient(socket, '400 SearXNG reported${result.title}
${result.content || `` || ''}