Skip to content

Commit

Permalink
Enable Javascript alert. Use of local and session storage. Concept of…
Browse files Browse the repository at this point in the history
… sign-in/sign-out. Ready Form URL. (#7)
  • Loading branch information
SomajitDey authored Sep 19, 2024
1 parent 71387f7 commit 09f6e36
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 84 deletions.
39 changes: 39 additions & 0 deletions 404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>EasyForm</title>
<meta name="description" content="Free, self-hosted, open-source form backend solution. No installation required for hosting. Lightweight server runs in browser. Backend sends form data as a Telegram bot to your Telegram account.">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
<link rel="manifest" href="site.webmanifest">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
<h1>Hello there! Feel free to post your message/comment/query to me</h1>
<form method="POST" target="hidden_iframe">
<input type="hidden" name="From" value="EasyFormURL">
<div class="row mt-2">
<div class="col">
<input type="email" class="form-control" name="Email" placeholder="Your Email" autocomplete="on" required>
</div>
<div class="col">
<input type="text" class="form-control" name="Name" placeholder="Your Name" autocomplete="on" required>
</div>
<input type="text" class="form-control mt-2" name="Message" placeholder="Your Message" required>
</div>
<button type="submit" id="submit" class="btn btn-info mt-2" onclick="alert('Thanks for your message!');">Post</button>
<button type="reset" class="btn btn-warning mt-2">Reset</button>
</form>
<iframe name="hidden_iframe" src="about:blank" hidden></iframe>

<script>
document.getElementById("submit").setAttribute("formaction", atob(location.pathname.substr(10,).replace(/_/g,'+').replace(/-/g,'/')));
</script>
</body>
</html>
12 changes: 6 additions & 6 deletions app/bg-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function urlEncoded2Json(str){
for (let el of arr) {
let elArray = el.split('=');
let val = decodeURIComponent(elArray[1].replace( /\+/g, ' ' )).replace(/"/g,'\\"'); // Decoded and escaped
eval('obj.' + elArray[0] +'="' + val +'"');
eval(`obj.${elArray[0]}="${val}"`);
}

return JSON.stringify(obj);
Expand All @@ -29,15 +29,15 @@ async function pollApi(getFrom, postTo, TGchatID) {

if (! response.ok) {
errLvl = 1; // Set error to critical/fatal
throw "GET @ " + getFrom + " status: " + response.status;
throw `GET @ ${getFrom} status: ${response.status}`;
}

data = urlEncoded2Json(await response.text());
// Send URL decoded form data as JSON string to main for logging. Also pass an error level.
postMessage([data, 0]);

} catch (error) {
console.error(Date() + ': Error making GET request --', error);
console.error(`${Date()}: Error making GET request -- ${error}`);
// Send error to main for logging. Error level: 1 for high priority / fatal.
postMessage(['Failed to fetch form data.', errLvl]);
return;
Expand All @@ -57,17 +57,17 @@ async function pollApi(getFrom, postTo, TGchatID) {
})

if (! response.ok) {
throw "POST @ " + postTo + " status: " + response.status +". Is chat ID = " + TGchatID + " ok?";
throw `POST @ ${postTo} status: ${response.status}. Is chat ID = ${TGchatID} ok?`;
}

} catch (error) {
console.error(Date() + ': Error making POST request --', error);
console.error(`${Date()}: Error making POST request -- ${error}`);
// Send error to main for logging. Error level: 2 for low priority / non-fatal.
postMessage(['Failed to post form data to Telegram.', errLvl]);
return;
}

console.log(Date() + ": Relay complete.");
console.log(`${Date()}: Relay complete.`);
};

relay(); // Start the first relay
Expand Down
81 changes: 60 additions & 21 deletions app/server.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// Main entry point for the server. Deploys background worker in "bg-worker.js" for handling networking.

spaHide("jsAlert");

let myWorker = null;
let getFrom, postTo, TGchatID;
let numReadMsgs = 0;
let numTotalMsgs = 0;
const logs = document.getElementById("logs");
const toggleServer = document.getElementById("toggleServer");

function logThis(report) {
const row = document.createElement("p");
row.append(Date() + ": " + report);
row.append(`${Date()}: ${report}`);
logs.prepend(row);
}

Expand Down Expand Up @@ -37,7 +38,7 @@ function inbox(json){
const cell = document.createElement("td");

// Create a text entry:
entry = eval("data." + keysEnumArray[key]);
entry = eval(`data.${keysEnumArray[key]}`);

// Append entry to cell:
cell.append(entry);
Expand Down Expand Up @@ -70,37 +71,45 @@ function genUUID() {
}

const fetchChatID = async () => {
logThis("Fetching Telegram chat ID")
const apiEndpoint = 'https://api.telegram.org/bot' + document.getElementById("apiKey").value + '/getUpdates';
logThis("Fetching Telegram chat ID");
const apiEndpoint = 'https://api.telegram.org/bot' + document.getElementById("TGbotKey").value + '/getUpdates';
const response = await fetch(apiEndpoint); // Make request
if (! response.ok) {
logThis("Telegram API status code:" + response.status +". Is Bot API Token ok?");
logThis(`Telegram API status code: ${response.status}. Is Bot API Token ok?`);
alert("Failed to fetch chat ID. Check your Bot API Token!");
return;
}
const data = await response.json();
document.getElementById("chatID").value = data.result[0].message.chat.id;
try {
const TGchatID = data.result[0].message.chat.id;
document.getElementById("chatID").value = TGchatID;
localStorage.setItem("TGchatID", TGchatID);
} catch (e) {
alert("Failed to fetch chat ID. Send any text to the Telegram Bot then try again.");
}
}

function config() {
const relayList = ["https://ppng.io", "https://piping.glitch.me", "https://demo.httprelay.io/link"];
const uuid = document.getElementById("uuid").value;
// Choose a random index in [0, relayList.length]. Use first two nibbles of uuid as random number in range [0,256].
const randomIdx = Math.floor(parseInt(uuid.substr(0,2),16)*relayList.length/256);
getFrom = relayList[randomIdx] + '/' + uuid;
postTo = 'https://api.telegram.org/bot' + document.getElementById("apiKey").value + '/sendMessage';
TGchatID = document.getElementById("chatID").value;
document.getElementById("config").innerHTML = '<p class="alert alert-success">HTML Form Action URL: <u>' + getFrom + '</u></p>';
document.getElementById("testFormBtn").setAttribute("formaction", getFrom);
spaShowHide("testForm");
document.getElementById("config").scrollIntoView();
const getFrom = relayList[randomIdx] + '/' + uuid;
localStorage.setItem("getFrom", getFrom);
const postTo = 'https://api.telegram.org/bot' + document.getElementById("TGbotKey").value + '/sendMessage';
localStorage.setItem("postTo", postTo);
spaGoTo("server");
localStorage.setItem("loggedIn", "true");
}

function startWorker() {
if (getFrom === undefined) {
config();
if (myWorker || sessionStorage.getItem("server")) {
alert('Another server is already running. Only one server can run at a time.');
return;
} else {
sessionStorage.setItem("server", "live");
}

myWorker = new Worker("app/bg-worker.js");

// Register handler for messages from the background worker
Expand All @@ -109,29 +118,40 @@ function startWorker() {
const msg = e.data[0];
if (! errLvl) {
inbox(msg);
logThis('RECEIVED: ' + msg);
logThis(`RECEIVED: ${msg}`);
} else if (errLvl === 1) {
stopWorker();
logThis('FATAL ERROR: ' + msg + ' See console for details.');
logThis(`FATAL ERROR: ${msg}. See console for details.`);
alert('Server stopped due to some critical error');
} else {
logThis('ERROR: ' + msg + ' See console for details.');
logThis(`ERROR: ${msg}. See console for details.`);
}
}

const getFrom = localStorage.getItem("getFrom");

// Communicate key data to the background worker
myWorker.postMessage([getFrom, postTo, TGchatID]);
myWorker.postMessage([getFrom, localStorage.getItem("postTo"), localStorage.getItem("TGchatID")]);

toggleServer.value = "Kill Server";
toggleServer.disabled = false;

logThis("Server started");
document.getElementById("serverStatus").innerHTML = 'Live <span class="spinner-grow spinner-grow-sm"></span>';

document.getElementById("formActionURL").innerHTML = `<p class="alert alert-success">HTML Form Action URL: <u>${getFrom}</u></p>`;
document.getElementById("readyForm").href = `./${btoa(getFrom).replace(/\+/g,'_').replace(/\//g,'-')}`;
document.getElementById("testFormBtn").setAttribute("formaction", getFrom);
spaShow("testForm");
}

function stopWorker() {
if (! myWorker) {
return;
}
myWorker.terminate();
myWorker = null;
sessionStorage.removeItem("server");
console.log("Worker terminated");
toggleServer.value = "Launch Server"
logThis("Server stopped");
Expand All @@ -145,3 +165,22 @@ function toggleWorker() {
startWorker();
}
}

function signout() {
stopWorker();
localStorage.clear();
location.reload();
}

function main() {
// Enable config if no prior settings found in localStorage
if (localStorage.getItem("loggedIn")) {
startWorker();
spaGoTo("server");
} else {
spaGoTo("setup");
}

}

spaHide("jsAlert");
40 changes: 24 additions & 16 deletions app/spa.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,30 @@
const spaHomePageID = document.querySelector(".spa-page").id; // Assuming first spa-page class is the home / hero page
let spaCurrentPageID = spaHomePageID;

{
const pages = document.getElementsByClassName("spa-page");
function spaShow(id) {
document.getElementById(id).style.display = 'block';
}

for (let el of pages) {
el.style.display = 'none';
function spaHide(id) {
document.getElementById(id).style.display = 'none';
}

function spaToggle(id) {
let x = document.getElementById(id);
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}

function spaGoTo(id) {
document.getElementById(spaCurrentPageID).style.display = 'none';
spaShow(id);
spaCurrentPageID = id;
spaTop();
}

function spaTop(){
document.getElementById(spaCurrentPageID).scrollIntoView();
}
Expand All @@ -21,19 +37,11 @@ function spaBottom(){
document.getElementById(spaCurrentPageID).scrollIntoView(false);
}

function spaGoTo(id) {
document.getElementById(spaCurrentPageID).style.display = 'none';
document.getElementById(id).style.display = 'block';
spaCurrentPageID = id;
spaTop();
}
{
const pages = document.getElementsByClassName("spa-page");

function spaShowHide(id) {
let x = document.getElementById(id);
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
for (let el of pages) {
el.style.display = 'none';
}
}

Expand Down
Loading

0 comments on commit 09f6e36

Please sign in to comment.