@@ -539,7 +545,8 @@ async function init() {
SD.init({
events: {
statusChange: setServerStatus,
- idle: onIdle
+ idle: onIdle,
+ ping: tunnelUpdate
}
})
diff --git a/ui/media/css/auto-save.css b/ui/media/css/auto-save.css
index 80aa48d8..119a7e10 100644
--- a/ui/media/css/auto-save.css
+++ b/ui/media/css/auto-save.css
@@ -69,13 +69,15 @@
}
.parameters-table > div:first-child {
- border-radius: 12px 12px 0px 0px;
+ border-top-left-radius: 12px;
+ border-top-right-radius: 12px;
}
.parameters-table > div:last-child {
- border-radius: 0px 0px 12px 12px;
+ border-bottom-left-radius: 12px;
+ border-bottom-right-radius: 12px;
}
.parameters-table .fa-fire {
color: #F7630C;
-}
\ No newline at end of file
+}
diff --git a/ui/media/css/main.css b/ui/media/css/main.css
index e2d1f79a..bd3cdbe6 100644
--- a/ui/media/css/main.css
+++ b/ui/media/css/main.css
@@ -1309,6 +1309,21 @@ body.wait-pause {
padding-left: 5pt;
}
+#cloudflare-address {
+ background-color: var(--background-color3);
+ padding: 6px;
+ border-radius: var(--input-border-radius);
+ border: var(--input-border-size) solid var(--input-border-color);
+ margin-top: 0.2em;
+ margin-bottom: 0.2em;
+ display: inline-block;
+}
+
+#copy-cloudflare-address {
+ padding: 4px 8px;
+ margin-left: 0.5em;
+}
+
/* TOAST NOTIFICATIONS */
.toast-notification {
position: fixed;
diff --git a/ui/media/js/engine.js b/ui/media/js/engine.js
index e60409f1..f3ce0551 100644
--- a/ui/media/js/engine.js
+++ b/ui/media/js/engine.js
@@ -186,6 +186,7 @@
const EVENT_TASK_START = "taskStart"
const EVENT_TASK_END = "taskEnd"
const EVENT_TASK_ERROR = "task_error"
+ const EVENT_PING = "ping"
const EVENT_UNEXPECTED_RESPONSE = "unexpectedResponse"
const EVENTS_TYPES = [
EVENT_IDLE,
@@ -196,6 +197,7 @@
EVENT_TASK_START,
EVENT_TASK_END,
EVENT_TASK_ERROR,
+ EVENT_PING,
EVENT_UNEXPECTED_RESPONSE,
]
@@ -240,6 +242,7 @@
setServerStatus("error", "offline")
return false
}
+
// Set status
switch (serverState.status) {
case ServerStates.init:
@@ -261,6 +264,7 @@
break
}
serverState.time = Date.now()
+ await eventSource.fireEvent(EVENT_PING, serverState)
return true
} catch (e) {
console.error(e)
diff --git a/ui/media/js/main.js b/ui/media/js/main.js
index e4043fa0..1684046f 100644
--- a/ui/media/js/main.js
+++ b/ui/media/js/main.js
@@ -1979,6 +1979,38 @@ resumeBtn.addEventListener("click", function() {
document.body.classList.remove("wait-pause")
})
+function tunnelUpdate(event) {
+ if ("cloudflare" in event) {
+ document.getElementById('cloudflare-off').classList.add("displayNone")
+ document.getElementById('cloudflare-on').classList.remove("displayNone")
+ cloudflareAddressField.innerHTML = event.cloudflare
+ document.getElementById('toggle-cloudflare-tunnel').innerHTML = "Stop"
+ } else {
+ document.getElementById('cloudflare-on').classList.add("displayNone")
+ document.getElementById('cloudflare-off').classList.remove("displayNone")
+ document.getElementById('toggle-cloudflare-tunnel').innerHTML = "Start"
+ }
+}
+
+document.getElementById('toggle-cloudflare-tunnel').addEventListener("click", async function() {
+ let command = "stop"
+ if (document.getElementById('toggle-cloudflare-tunnel').innerHTML == "Start") {
+ command = "start"
+ }
+ showToast(`Cloudflare tunnel ${command} initiated. Please wait.`)
+
+ let res = await fetch("/tunnel/cloudflare/"+command, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({}),
+ })
+ res = await res.json()
+
+ console.log(`Cloudflare tunnel ${command} result:`, res)
+})
+
/* Pause function */
document.querySelectorAll(".tab").forEach(linkTabContents)
diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js
index 373de58d..2f915eeb 100644
--- a/ui/media/js/parameters.js
+++ b/ui/media/js/parameters.js
@@ -11,6 +11,12 @@ var ParameterType = {
custom: "custom",
}
+/**
+ * Element shortcuts
+ */
+let parametersTable = document.querySelector("#system-settings-table")
+let networkParametersTable = document.querySelector("#system-settings-network-table")
+
/**
* JSDoc style
* @typedef {object} Parameter
@@ -186,6 +192,7 @@ var PARAMETERS = [
icon: "fa-network-wired",
default: true,
saveInAppConfig: true,
+ table: networkParametersTable,
},
{
id: "listen_port",
@@ -198,6 +205,7 @@ var PARAMETERS = [
return `
`
},
saveInAppConfig: true,
+ table: networkParametersTable,
},
{
id: "use_beta_channel",
@@ -218,6 +226,21 @@ var PARAMETERS = [
default: false,
saveInAppConfig: true,
},
+ {
+ id: "cloudflare",
+ type: ParameterType.custom,
+ label: "Cloudflare tunnel",
+ note: `
Create a VPN tunnel to share your Easy Diffusion instance with your friends. This will
+ generate a web server address on the public Internet for your Easy Diffusion instance.
+
This Easy Diffusion server is available on the Internet using the
+ address:
+
Anyone knowing this address can access your server. The address of your server will change each time
+ you share a session.
+ Uses
Cloudflare services.`,
+ icon: ["fa-brands", "fa-cloudflare"],
+ render: () => '
',
+ table: networkParametersTable,
+ }
]
function getParameterSettingsEntry(id) {
@@ -266,7 +289,6 @@ function getParameterElement(parameter) {
}
}
-let parametersTable = document.querySelector("#system-settings .parameters-table")
/**
* fill in the system settings popup table
* @param {Array
| undefined} parameters
@@ -293,7 +315,10 @@ function initParameters(parameters) {
noteElements.push(noteElement)
}
- const icon = parameter.icon ? [createElement("i", undefined, ["fa", parameter.icon])] : []
+ if (typeof(parameter.icon) == "string") {
+ parameter.icon = [parameter.icon]
+ }
+ const icon = parameter.icon ? [createElement("i", undefined, ["fa", ...parameter.icon])] : []
const label = typeof parameter.label === "function" ? parameter.label(parameter) : parameter.label
const labelElement = createElement("label", { for: parameter.id })
@@ -313,7 +338,13 @@ function initParameters(parameters) {
elementWrapper,
]
)
- parametersTable.appendChild(newrow)
+
+ let p = parametersTable
+ if (parameter.table) {
+ p = parameter.table
+ }
+ p.appendChild(newrow)
+
parameter.settingsEntry = newrow
})
}
@@ -667,8 +698,25 @@ saveSettingsBtn.addEventListener("click", function() {
})
const savePromise = changeAppConfig(updateAppConfigRequest)
+ showToast("Settings saved")
saveSettingsBtn.classList.add("active")
Promise.all([savePromise, asyncDelay(300)]).then(() => saveSettingsBtn.classList.remove("active"))
})
+listenToNetworkField.addEventListener("change", debounce( ()=>{
+ saveSettingsBtn.click()
+}, 1000))
+
+listenPortField.addEventListener("change", debounce( ()=>{
+ saveSettingsBtn.click()
+}, 1000))
+
+let copyCloudflareAddressBtn = document.querySelector("#copy-cloudflare-address")
+let cloudflareAddressField = document.getElementById("cloudflare-address")
+
+copyCloudflareAddressBtn.addEventListener("click", (e) => {
+ navigator.clipboard.writeText(cloudflareAddressField.innerHTML)
+ showToast("Copied server address to clipboard")
+})
+
document.addEventListener("system_info_update", (e) => setDeviceInfo(e.detail))