diff --git a/README.md b/README.md index 1ea2a4c..a6dbe8f 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ This part is unchanged from the original project. Follow @masterking32's guide o 1. Open while signed into your Google account. 2. **New project**, delete the default code. -3. Copy the contents of [`Code.gs` from the original repo](https://github.com/masterking32/MasterHttpRelayVPN/blob/python_testing/Code.gs) ([raw](https://raw.githubusercontent.com/masterking32/MasterHttpRelayVPN/refs/heads/python_testing/Code.gs)) into the editor. +3. Copy the contents of [`Code.gs` from the original repo](https://github.com/masterking32/MasterHttpRelayVPN/blob/python_testing/apps_script/Code.gs) ([raw](https://raw.githubusercontent.com/masterking32/MasterHttpRelayVPN/refs/heads/python_testing/apps_script/Code.gs)) into the editor. If that URL is unreachable from your network, there's a mirrored copy in this repo at [`assets/apps_script/Code.gs`](assets/apps_script/Code.gs) — same file, pulled from upstream. 4. Change `const AUTH_KEY = "..."` to a strong secret only you know. 5. **Deploy → New deployment → Web app**. - Execute as: **Me** @@ -385,7 +385,7 @@ Original project: by [@mast ۱. به بروید و با حساب گوگل خودتان وارد شوید ۲. روی **`New project`** کلیک کنید و کد پیش‌فرض را پاک کنید -۳. محتوای فایل [`Code.gs`](https://github.com/masterking32/MasterHttpRelayVPN/blob/python_testing/Code.gs) را از ریپوی اصلی کپی کنید و داخل ویرایشگر بچسبانید +۳. محتوای فایل [`Code.gs`](https://github.com/masterking32/MasterHttpRelayVPN/blob/python_testing/apps_script/Code.gs) را از ریپوی اصلی کپی کنید و داخل ویرایشگر بچسبانید. اگر به آدرس بالا دسترسی ندارید، یک کپی از همین فایل داخل این ریپو هم هست: [`assets/apps_script/Code.gs`](assets/apps_script/Code.gs) ۴. بالای کد، خط `const AUTH_KEY = "..."` را پیدا کنید و مقدار آن را به یک رمز قوی و خاص خودتان تغییر دهید (یک رشتهٔ تصادفی حداقل ۱۶ کاراکتری کافی است، مثلاً `aK8f3xM9pQ2nL5vR`) ۵. روی دکمهٔ آبی **`Deploy`** در بالا سمت راست کلیک کنید و **`New deployment`** را بزنید ۶. **`Type`** را روی **`Web app`** بگذارید و این تنظیمات را اعمال کنید: diff --git a/assets/apps_script/Code.gs b/assets/apps_script/Code.gs new file mode 100644 index 0000000..8c2acec --- /dev/null +++ b/assets/apps_script/Code.gs @@ -0,0 +1,141 @@ +/** + * DomainFront Relay — Google Apps Script + * + * TWO modes: + * 1. Single: POST { k, m, u, h, b, ct, r } → { s, h, b } + * 2. Batch: POST { k, q: [{m,u,h,b,ct,r}, ...] } → { q: [{s,h,b}, ...] } + * Uses UrlFetchApp.fetchAll() — all URLs fetched IN PARALLEL. + * + * DEPLOYMENT: + * 1. Go to https://script.google.com → New project + * 2. Delete the default code, paste THIS entire file + * 3. Click Deploy → New deployment + * 4. Type: Web app | Execute as: Me | Who has access: Anyone + * 5. Copy the Deployment ID into config.json as "script_id" + * + * CHANGE THE AUTH KEY BELOW TO YOUR OWN SECRET! + */ + +const AUTH_KEY = "CHANGE_ME_TO_A_STRONG_SECRET"; + +// Keep browser capability headers (sec-ch-ua*, sec-fetch-*) intact. +// Some modern apps, notably Google Meet, use them for browser gating. +const SKIP_HEADERS = { + host: 1, connection: 1, "content-length": 1, + "transfer-encoding": 1, "proxy-connection": 1, "proxy-authorization": 1, + "priority": 1, te: 1, +}; + +function doPost(e) { + try { + var req = JSON.parse(e.postData.contents); + if (req.k !== AUTH_KEY) return _json({ e: "unauthorized" }); + + // Batch mode: { k, q: [...] } + if (Array.isArray(req.q)) return _doBatch(req.q); + + // Single mode + return _doSingle(req); + } catch (err) { + return _json({ e: String(err) }); + } +} + +function _doSingle(req) { + if (!req.u || typeof req.u !== "string" || !req.u.match(/^https?:\/\//i)) { + return _json({ e: "bad url" }); + } + var opts = _buildOpts(req); + var resp = UrlFetchApp.fetch(req.u, opts); + return _json({ + s: resp.getResponseCode(), + h: _respHeaders(resp), + b: Utilities.base64Encode(resp.getContent()), + }); +} + +function _doBatch(items) { + var fetchArgs = []; + var errorMap = {}; + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (!item.u || typeof item.u !== "string" || !item.u.match(/^https?:\/\//i)) { + errorMap[i] = "bad url"; + continue; + } + var opts = _buildOpts(item); + opts.url = item.u; + fetchArgs.push({ _i: i, _o: opts }); + } + + // fetchAll() processes all requests in parallel inside Google + var responses = []; + if (fetchArgs.length > 0) { + responses = UrlFetchApp.fetchAll(fetchArgs.map(function(x) { return x._o; })); + } + + var results = []; + var rIdx = 0; + for (var i = 0; i < items.length; i++) { + if (errorMap.hasOwnProperty(i)) { + results.push({ e: errorMap[i] }); + } else { + var resp = responses[rIdx++]; + results.push({ + s: resp.getResponseCode(), + h: _respHeaders(resp), + b: Utilities.base64Encode(resp.getContent()), + }); + } + } + return _json({ q: results }); +} + +function _buildOpts(req) { + var opts = { + method: (req.m || "GET").toLowerCase(), + muteHttpExceptions: true, + followRedirects: req.r !== false, + validateHttpsCertificates: true, + escaping: false, + }; + if (req.h && typeof req.h === "object") { + var headers = {}; + for (var k in req.h) { + if (req.h.hasOwnProperty(k) && !SKIP_HEADERS[k.toLowerCase()]) { + headers[k] = req.h[k]; + } + } + opts.headers = headers; + } + if (req.b) { + opts.payload = Utilities.base64Decode(req.b); + if (req.ct) opts.contentType = req.ct; + } + return opts; +} + +function _respHeaders(resp) { + try { + if (typeof resp.getAllHeaders === "function") { + return resp.getAllHeaders(); + } + } catch (err) {} + return resp.getHeaders(); +} + +function doGet(e) { + return HtmlService.createHtmlOutput( + "My App" + + '' + + "

Welcome

This application is running normally.

" + + "" + ); +} + +function _json(obj) { + return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType( + ContentService.MimeType.JSON + ); +} diff --git a/assets/apps_script/README.md b/assets/apps_script/README.md new file mode 100644 index 0000000..1cf339a --- /dev/null +++ b/assets/apps_script/README.md @@ -0,0 +1,13 @@ +# Apps Script source (mirrored) + +The file `Code.gs` next to this README is a verbatim snapshot of the upstream script you deploy in your own Google Apps Script project: + +- Upstream: +- Raw link: + +This copy lives in our repo for two reasons: + +1. **Survives upstream outages**: if the user is on a network where raw.githubusercontent.com is temporarily unreachable but they can clone or ZIP this repo, they still have the deploy-ready file. +2. **Pins what we tested against**: the relay protocol between `mhrv-rs` and the script is informal; upstream changes can silently break us. Keeping a snapshot here lets us diff and see if a spec drift is responsible for any reported breakage. + +All credit for `Code.gs` goes to [@masterking32](https://github.com/masterking32) — we do not modify it. If you're using mhrv-rs, follow the upstream deploy instructions in the script's header comment. The only edit **you** must make is the `AUTH_KEY` constant — set it to a strong secret and reuse that exact string in your `mhrv-rs` config.