Share ED via Cloudflare's ArgoTunnel

Shares the Easy Diffusion instance via https://try.cloudflare.com/
This commit is contained in:
JeLuF 2023-05-28 00:50:23 +02:00
parent 3d7e16cfd9
commit 2080d6e27b
8 changed files with 157 additions and 8 deletions

View File

@ -23,6 +23,7 @@ modules_to_check = {
"rich": "12.6.0",
"uvicorn": "0.19.0",
"fastapi": "0.85.1",
"pycloudflared": "0.2.0",
# "xformers": "0.0.16",
}

View File

@ -15,6 +15,7 @@ from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Extra
from starlette.responses import FileResponse, JSONResponse, StreamingResponse
from pycloudflared import try_cloudflare
log.info(f"started in {app.SD_DIR}")
log.info(f"started at {datetime.datetime.now():%x %X}")
@ -113,6 +114,14 @@ def init():
def get_image(task_id: int, img_id: int):
return get_image_internal(task_id, img_id)
@server_api.post("/tunnel/cloudflare/start")
def start_cloudflare_tunnel(req: dict):
return start_cloudflare_tunnel_internal(req)
@server_api.post("/tunnel/cloudflare/stop")
def stop_cloudflare_tunnel(req: dict):
return stop_cloudflare_tunnel_internal(req)
@server_api.get("/")
def read_root():
return FileResponse(os.path.join(app.SD_UI_DIR, "index.html"), headers=NOCACHE_HEADERS)
@ -211,6 +220,8 @@ def ping_internal(session_id: str = None):
session = task_manager.get_cached_session(session_id, update_ttl=True)
response["tasks"] = {id(t): t.status for t in session.tasks}
response["devices"] = task_manager.get_devices()
if cloudflare.address != None:
response["cloudflare"] = cloudflare.address
return JSONResponse(response, headers=NOCACHE_HEADERS)
@ -322,3 +333,46 @@ def get_image_internal(task_id: int, img_id: int):
return StreamingResponse(img_data, media_type="image/jpeg")
except KeyError as e:
raise HTTPException(status_code=500, detail=str(e))
#---- Cloudflare Tunnel ----
class CloudflareTunnel:
def __init__(self):
config = app.getConfig()
self.Urls = None
self.port = config["net"]["listen_port"]
def start(self):
self.Urls = try_cloudflare(self.port)
def stop(self):
if self.Urls != None:
try_cloudflare.terminate(self.port)
self.Urls = None
@property
def address(self):
if self.Urls != None:
return self.Urls.tunnel
else:
return None
cloudflare = CloudflareTunnel()
def start_cloudflare_tunnel_internal(req: dict):
try:
cloudflare.start()
log.info(f"- Started cloudflare tunnel. Using address: {cloudflare.address}")
return JSONResponse({"address":cloudflare.address})
except Exception as e:
log.error(str(e))
log.error(traceback.format_exc())
return HTTPException(status_code=500, detail=str(e))
def stop_cloudflare_tunnel_internal(req: dict):
try:
cloudflare.stop()
except Exception as e:
log.error(str(e))
log.error(traceback.format_exc())
return HTTPException(status_code=500, detail=str(e))

View File

@ -347,10 +347,16 @@
<div id="tab-content-settings" class="tab-content">
<div id="system-settings" class="tab-content-inner">
<h1>System Settings</h1>
<div class="parameters-table"></div>
<div class="parameters-table" id="system-settings-table"></div>
<br/>
<button id="save-system-settings-btn" class="primaryButton">Save</button>
<br/><br/>
<div id="share-easy-diffusion">
<h3><i class="fa fa-user-group"></i> Share Easy Diffusion</h3>
<div class="parameters-table" id="system-settings-network-table">
</div>
</div>
<br/><br/>
<div>
<h3><i class="fa fa-microchip icon"></i> System Info</h3>
<div id="system-info">
@ -525,7 +531,8 @@ async function init() {
SD.init({
events: {
statusChange: setServerStatus,
idle: onIdle
idle: onIdle,
ping: tunnelUpdate
}
})

View File

@ -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;
}
}

View File

@ -1309,6 +1309,15 @@ 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;
}
/* TOAST NOTIFICATIONS */
.toast-notification {
position: fixed;

View File

@ -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)

View File

@ -1951,6 +1951,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")
document.getElementById('cloudflare-address').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)

View File

@ -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 `<input id="${parameter.id}" name="${parameter.id}" size="6" value="9000" onkeypress="preventNonNumericalInput(event)">`
},
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: `<span id="cloudflare-off">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. </span>
<div id="cloudflare-on" class="displayNone"><div>This Easy Diffusion server is available on the Internet using the
address:</div><div id="cloudflare-address"></div></div>
<b>Anyone knowing this address can access your server.</b> The address of your server will change each time
you share a session.<br>
Uses <a href="https://try.cloudflare.com/" target="_blank">Cloudflare services</a>.`,
icon: ["fa-brands", "fa-cloudflare"],
render: () => '<button id="toggle-cloudflare-tunnel" class="primaryButton">Start</button>',
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<Parameter> | 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
})
}
@ -665,8 +696,17 @@ 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))
document.addEventListener("system_info_update", (e) => setDeviceInfo(e.detail))