large parts of the usage is now dynamic.
Still need to generate instructions for authorized keys. A lot of troubleshooting for the form to cookie persistence.
This commit is contained in:
parent
62b51a6d09
commit
49db7578a7
@ -3,11 +3,10 @@ package main
|
|||||||
import (
|
import (
|
||||||
templates2 "converge/pkg/server/templates"
|
templates2 "converge/pkg/server/templates"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func pageHandler(w http.ResponseWriter, r *http.Request) {
|
func pageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
username, _ := os.LookupEnv("CONVERGE_USERNAME")
|
username := getAgentSshUser()
|
||||||
access := getConvergeAccess(r, username)
|
access := getConvergeAccess(r, username)
|
||||||
|
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"converge/pkg/server/templates"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"os"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateCLIExammple(w http.ResponseWriter, r *http.Request) {
|
func generateCLIExammple(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -14,11 +17,31 @@ func generateCLIExammple(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
remote_shells := r.Form["remote-shell"]
|
ids := r.Form["rendez-vous-id"]
|
||||||
local_shells := r.Form["local-shhell"]
|
id := ""
|
||||||
|
if len(ids) > 0 {
|
||||||
|
id = ids[0]
|
||||||
|
}
|
||||||
|
if id == "" {
|
||||||
|
id = strconv.Itoa(rand.Int() % 1000000)
|
||||||
|
}
|
||||||
|
remoteShells := r.Form["remote-shell"]
|
||||||
|
localShells := r.Form["local-shell"]
|
||||||
keys := r.FormValue("ssh-keys")
|
keys := r.FormValue("ssh-keys")
|
||||||
log.Printf("remote_shells %v", remote_shells)
|
log.Printf("remote_shells %v", remoteShells)
|
||||||
log.Printf("local_shells %v", local_shells)
|
log.Printf("local_shells %v", localShells)
|
||||||
log.Printf("ssh-keys %v", keys)
|
log.Printf("ssh-keys %v", keys)
|
||||||
w.Write([]byte(time.Now().Format(time.DateTime)))
|
|
||||||
|
access := getConvergeAccess(r, getAgentSshUser())
|
||||||
|
|
||||||
|
usageInputs := templates.NewUsageInputs(id, remoteShells, localShells)
|
||||||
|
err = templates.ShellUsage(access, usageInputs).Render(r.Context(), w)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAgentSshUser() string {
|
||||||
|
username, _ := os.LookupEnv("CONVERGE_USERNAME")
|
||||||
|
return username
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,49 @@ templ About() {
|
|||||||
When the timeout of a session is near the user is informed about this with messages
|
When the timeout of a session is near the user is informed about this with messages
|
||||||
in the shell.
|
in the shell.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>end-to-end encryoption</p>
|
||||||
|
<p> ssh keys</p>
|
||||||
|
<p>agent options </p>
|
||||||
|
<p>client access </p>
|
||||||
|
|
||||||
|
<h2>Local clients: using ssh with a proxy command </h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<code>wsproxy</code> is a command that can be used as a proxy command for SSH which performs the connection to the
|
||||||
|
remote server. This command needs to be downloaded only once (see <a href="downloads.html">downloads</a>). It does not depend on
|
||||||
|
the converge implementation but only on the websocket standards. Other tools that
|
||||||
|
provide a mapping of stdio to a websocket can also be used instead of wsproxy.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Local clients: using SSH with a local TCP forwarding proxy</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This option is less convenient than the proxy command because it requires two separate
|
||||||
|
commands to execute.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Local clients can connect using regular ssh and sftp commands through a tunnel that
|
||||||
|
translates a local TCP port to a websocket connection in converge. See
|
||||||
|
the <a href="downloads.html">downloads</a> section.
|
||||||
|
This runs a local client that allows SSH to port 10000 and connects to converge using
|
||||||
|
a websocket connection.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Remote shell usage</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The agent supports a --shells command-line option by which a comma-separated
|
||||||
|
list of shells can be prepended to the default search path for shells, e.g.
|
||||||
|
<code>--shells zsh,csh,sh</code> (linux) or <code>cmd,powershell</code> for
|
||||||
|
windows.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The agent sets a <coder>agentdir</coder> environment variable that points to
|
||||||
|
the directory where the agent is running.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1,5 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
|
const BASH = "*.sh"
|
||||||
|
const CMD = "cmd"
|
||||||
|
const POWERSHELL = "powershell"
|
||||||
|
@ -2,10 +2,118 @@ package templates
|
|||||||
|
|
||||||
import "converge/pkg/models"
|
import "converge/pkg/models"
|
||||||
|
|
||||||
|
|
||||||
|
templ AgentUsage(access models.ConvergeAccess, shells map[string]bool, usageInputs UsageInputs) {
|
||||||
|
<div>
|
||||||
|
<h2>Downloading and running the agent</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This is what you run on a remote server, typically in your continuous integration job.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
if shells[BASH] {
|
||||||
|
<pre>{`
|
||||||
|
`}curl http{access.Secure}://{access.HostPort}/static/agent > agent{`
|
||||||
|
chmod 755 agent
|
||||||
|
`}./agent --id {usageInputs.Id} ws{access.Secure}://{access.HostPort}{`
|
||||||
|
rm -f agent
|
||||||
|
`}</pre>
|
||||||
|
}
|
||||||
|
if shells[CMD] || shells[POWERSHELL] {
|
||||||
|
<pre>{`
|
||||||
|
`}curl http{access.Secure}://{access.HostPort}/static/agent.exe > agent.exe{`
|
||||||
|
`}agent --id {usageInputs.Id} ws{access.Secure}://{access.HostPort}{`
|
||||||
|
del agent.exe
|
||||||
|
`}</pre>
|
||||||
|
}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The agent has more command-line options than shown here.
|
||||||
|
Download the agent and run it without arguments to
|
||||||
|
see all options.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Tip</b>: Run the above command locally in a similar shell (for instance in a
|
||||||
|
docker container) then try to connect to the shell using the commands below. If that
|
||||||
|
works, then you are all set.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Connecting to the agent</h2>
|
||||||
|
|
||||||
|
<pre>{`
|
||||||
|
`}ssh -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.HostPort}/client/{usageInputs.Id}" { access.Username }{"@localhost"} {`
|
||||||
|
`}sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.HostPort}/client/{usageInputs.Id}" { access.Username }{"@localhost"} {`
|
||||||
|
`}</pre>
|
||||||
|
|
||||||
|
<h2>Working with the agent</h2>
|
||||||
|
|
||||||
|
if shells[BASH] {
|
||||||
|
<pre>{`
|
||||||
|
# cd back to the agent directory
|
||||||
|
cd $agentdir
|
||||||
|
|
||||||
|
# prevent logout when last user exits
|
||||||
|
touch $agentdir/.hold
|
||||||
|
`}</pre>
|
||||||
|
|
||||||
|
}
|
||||||
|
if shells[CMD] {
|
||||||
|
<pre>{`
|
||||||
|
# cd back to the agent directory
|
||||||
|
cd %agentdir%
|
||||||
|
|
||||||
|
# prevent logout when last user exits
|
||||||
|
echo > %agentdir%\.hold
|
||||||
|
`}</pre>
|
||||||
|
}
|
||||||
|
if shells[POWERSHELL] {
|
||||||
|
<pre>{`
|
||||||
|
# cd back to the agent directory
|
||||||
|
cd $env:agentdir
|
||||||
|
|
||||||
|
# prevent logout when last user exits
|
||||||
|
$null > $env:agentdir\.hold
|
||||||
|
`}</pre>
|
||||||
|
}
|
||||||
|
if shells[CMD] || shells[POWERSHELL] {
|
||||||
|
<p>windows</p>
|
||||||
|
<p>
|
||||||
|
NOTE: When running the agent on windows, an exit of the remote session using
|
||||||
|
exit in powershell or command prompt does not terminate the shell completely.
|
||||||
|
To terminate the client ssh session must be killed by closing the terminal.
|
||||||
|
Cleanup of remote processes on the agent appears to work properly despite this
|
||||||
|
problem. It is just that exit of the windows shell (powershell or command prompt)
|
||||||
|
is not detected properly.
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ LocalShellUsage(access models.ConvergeAccess, shells map[string]bool, usageInput UsageInputs) {
|
||||||
|
<div>
|
||||||
|
if shells[BASH] {
|
||||||
|
<p>bash</p>
|
||||||
|
}
|
||||||
|
if shells[CMD] {
|
||||||
|
<p>cmd</p>
|
||||||
|
}
|
||||||
|
if shells[POWERSHELL] {
|
||||||
|
<p>powershell</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ ShellUsage(access models.ConvergeAccess, usageInputs UsageInputs) {
|
||||||
|
<div>
|
||||||
|
@AgentUsage(access, usageInputs.RemoteShells, usageInputs)
|
||||||
|
</div>
|
||||||
|
@LocalShellUsage(access, usageInputs.LocalShells, usageInputs)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
templ Usage(access models.ConvergeAccess) {
|
templ Usage(access models.ConvergeAccess) {
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
|
||||||
<h1>Usage</h1>
|
<h1>Usage</h1>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -18,7 +126,7 @@ templ Usage(access models.ConvergeAccess) {
|
|||||||
<form id="inputs" novalidate
|
<form id="inputs" novalidate
|
||||||
hx-post="/usage"
|
hx-post="/usage"
|
||||||
method="post"
|
method="post"
|
||||||
hx-trigger="load,input delay:500ms,change"
|
hx-trigger="load,input delay:500ms,change,formdataloaded"
|
||||||
hx-target="#example-cli"
|
hx-target="#example-cli"
|
||||||
hx-on::after-request="saveFormToCookie()">
|
hx-on::after-request="saveFormToCookie()">
|
||||||
<table class="table table-responsive">
|
<table class="table table-responsive">
|
||||||
@ -35,19 +143,19 @@ templ Usage(access models.ConvergeAccess) {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="minimal-width"><label for="remote-shell">remote shells</label></td>
|
<td class="minimal-width"><label for="remote-shell">agent environment</label></td>
|
||||||
<td>
|
<td>
|
||||||
<input checked id="remote-shell-0" name="remote-shell" type="checkbox" value="*sh"> <label for="remote-shell-0">*.sh</label>
|
<input checked id="remote-shell-0" name="remote-shell" type="radio" value={BASH}> <label for="remote-shell-0">*.sh</label>
|
||||||
<input id="remote-shell-1" name="remote-shell" type="checkbox" value="cmd"> <label for="remote-shell-1">cmd.exe</label>
|
<input id="remote-shell-1" name="remote-shell" type="radio" value={CMD}> <label for="remote-shell-1">command prompt</label>
|
||||||
<input id="remote-shell-2" name="remote-shell" type="checkbox" value="cmd"> <label for="remote-shell-2">powerpoint.exe</label>
|
<input id="remote-shell-2" name="remote-shell" type="radio" value={POWERSHELL}> <label for="remote-shell-2">power shell</label>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="minimal-width"><label for="local-shell">local shell</label></td>
|
<td class="minimal-width"><label for="local-shell">local environment</label></td>
|
||||||
<td>
|
<td>
|
||||||
<input id="checked local-shell-0" name="local-shell" type="checkbox" value="*sh"> <label for="local-shell-0">*.sh</label>
|
<input id="checked local-shell-0" name="local-shell" type="radio" value={BASH}> <label for="local-shell-0">*.sh</label>
|
||||||
<input id="local-shell-1" name="local-shell" type="checkbox" value="cmd"> <label for="local-shell-1">cmd.exe</label>
|
<input id="local-shell-1" name="local-shell" type="radio" value={CMD}> <label for="local-shell-1">command prompt</label>
|
||||||
<input id="local-shell-2" name="local-shell" type="checkbox" value="cmd"> <label for="local-shell-2">powerpoint.exe</label>
|
<input id="local-shell-2" name="local-shell" type="radio" value={POWERSHELL}> <label for="local-shell-2">powershell</label>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@ -56,159 +164,10 @@ templ Usage(access models.ConvergeAccess) {
|
|||||||
<div id="example-cli">
|
<div id="example-cli">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1>usage old</h1>
|
|
||||||
|
|
||||||
<h2>Continuous integration jobs</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
In a
|
|
||||||
continuous integration job, download the agent, chmod it and run it.
|
|
||||||
</p>
|
|
||||||
<pre>{`
|
|
||||||
# linux
|
|
||||||
`}curl http{access.Secure}://{access.HostPort}/static/agent > agent{`
|
|
||||||
chmod 755 agent
|
|
||||||
`}./agent --id ID ws{access.Secure}://{access.HostPort}{`
|
|
||||||
rm -f agent
|
|
||||||
|
|
||||||
# windows
|
|
||||||
`}curl http{access.Secure}://{access.HostPort}/static/agent.exe > agent.exe{`
|
|
||||||
`}agent --id ID ws{access.Secure}://{access.HostPort}{`
|
|
||||||
del agent.exe
|
|
||||||
`}</pre>
|
|
||||||
<p>
|
|
||||||
Above, ID is a unique id for the job, the so-called rendez-cous ID. This should not conflict with IDs
|
|
||||||
used by other agents. The ID is used for a rendez-vous between the end-user on a local system and
|
|
||||||
the continuous integration job running on a build agent. If you don't specify an id, a random
|
|
||||||
id will be generated. If you specify a duplicate ID, the server will generate a new one andd the
|
|
||||||
agent will tell you what id to use.
|
|
||||||
Clients can now connect to the Converge server with the ID to establish a connection to
|
|
||||||
the CI job through Converge.
|
|
||||||
|
|
||||||
Communication between
|
|
||||||
end-user and agent is encrypted using SSH and the rendez-vous server is unable to
|
|
||||||
read the contents. The rendez-vous server is nothing more then a glorified bit pipe,
|
|
||||||
simply transferring data between end-user SSH client and the agent which runs an
|
|
||||||
embedded SSH server.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
NOTE: When running the agent on windows, an exit of the remote session using
|
|
||||||
exit in powershell or command prompt does not terminate the shell completely.
|
|
||||||
To terminate the client ssh session must be killed by closing the terminal.
|
|
||||||
Cleanup of remote processes on the agent appears to work properly despite this
|
|
||||||
problem. It is just that exit of the windows shell (powershell or command prompt)
|
|
||||||
is not detected properly.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The agent has more command-line options than shown here.
|
|
||||||
Download the agent and run it without arguments to
|
|
||||||
see all options.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Local clients: using ssh with a proxy command </h2>
|
|
||||||
|
|
||||||
<p><code>wsproxy</code> is a command that can be used as a proxy command for SSH which performs the connection to the
|
|
||||||
remote server. This command needs to be downloaded only once (see <a href="downloads.html">downloads</a>). It does not depend on
|
|
||||||
the converge implementation but only on the websocket standards. Other tools that
|
|
||||||
provide a mapping of stdio to a websocket can also be used instead of wsproxy.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Next step is to run a local SSH or SFTP client:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
{`
|
|
||||||
`}ssh -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.HostPort}/client/ID" { access.Username }{"@localhost"} {`
|
|
||||||
`}sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.HostPort}/client/ID" { access.Username }{"@localhost"} {`
|
|
||||||
`}</pre>
|
|
||||||
|
|
||||||
<h2>Local clients: using SSH with a local TCP forwarding proxy</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
This option is less convenient than the proxy command because it requires two separate
|
|
||||||
commands to execute.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Local clients can connect using regular ssh and sftp commands through a tunnel that
|
|
||||||
translates a local TCP port to a websocket connection in converge. See
|
|
||||||
the <a href="downloads.html">downloads</a> section.
|
|
||||||
This runs a local client that allows SSH to port 10000 and connects to converge using
|
|
||||||
a websocket connection.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Next step is to run a local SSH of SFTP client:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<pre> {`
|
|
||||||
`}ssh -oServerAliveInterval=10 -p 10000 { access.Username }{"@localhost"} {`
|
|
||||||
`}sftp -oServerAliveInterval=10 -oPort=10000 { access.Username }{"@localhost"} {`
|
|
||||||
`}</pre>
|
|
||||||
|
|
||||||
<h2>Remote shell usage</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The agent supports a --shells command-line option by which a comma-separated
|
|
||||||
list of shells can be prepended to the default search path for shells, e.g.
|
|
||||||
<code>--shells zsh,csh,sh</code> (linux) or <code>cmd,powershell</code> for
|
|
||||||
windows.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The agent sets a <coder>agentdir</coder> environment variable that points to
|
|
||||||
the directory where the agent is running.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>Linux</h3>
|
|
||||||
|
|
||||||
<pre>{`
|
|
||||||
# cd back to the agent directory
|
|
||||||
cd $agentdir
|
|
||||||
|
|
||||||
# prevent logout when last user exits
|
|
||||||
touch $agentdir/.hold
|
|
||||||
`}</pre>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h3>Windows Command Prompt</h3>
|
|
||||||
|
|
||||||
<pre>{`
|
|
||||||
# cd back to the agent directory
|
|
||||||
cd %agentdir%
|
|
||||||
|
|
||||||
# prevent logout when last user exits
|
|
||||||
echo > %agentdir%\.hold
|
|
||||||
`}</pre>
|
|
||||||
|
|
||||||
<h3>Windows Powershell</h3>
|
|
||||||
|
|
||||||
<pre>{`
|
|
||||||
# cd back to the agent directory
|
|
||||||
cd $env:agentdir
|
|
||||||
|
|
||||||
# prevent logout when last user exits
|
|
||||||
$null > $env:agentdir\.hold
|
|
||||||
`}</pre>
|
|
||||||
|
|
||||||
<h2>Authentication</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The <code>{ access.Username }</code> user above is configured in the
|
|
||||||
Converge server and is communicated to the agent when the agent is
|
|
||||||
started as well as the password.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Another way to authenticate is through an .authorized_keys file in the
|
|
||||||
same directory as where the agent is started.
|
|
||||||
|
|
||||||
This can be setup as follows before starting the agent:
|
|
||||||
</p>
|
|
||||||
<pre> {`
|
<pre> {`
|
||||||
`}# linux {`
|
`}# linux {`
|
||||||
`}echo "ssh-rsa dkddkdkkk a@b.c" > .authorized_keys {`
|
`}echo "ssh-rsa dkddkdkkk a@b.c" > .authorized_keys {`
|
||||||
@ -250,13 +209,17 @@ templ Usage(access models.ConvergeAccess) {
|
|||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
for (let element of form.elements) {
|
for (let element of form.elements) {
|
||||||
if (element.id) {
|
if (element.id) {
|
||||||
console.log("Saving " + element.id)
|
//console.log("Saving " + element.id)
|
||||||
if (element.type === 'checkbox') {
|
if (element.type === 'checkbox') {
|
||||||
console.log("Checkbox " + element.checked)
|
//console.log("Checkbox " + element.checked)
|
||||||
setCookie(element.id, element.checked ? 'true' : 'false', 7);
|
setCookie(element.id, element.checked ? 'true' : 'false', 7);
|
||||||
} else if (element.type === 'radio') {
|
} else if (element.type === 'radio') {
|
||||||
if (element.checked) {
|
if (element.checked) {
|
||||||
|
//console.log("Set cookie " + element.id + " " + element.value)
|
||||||
setCookie(element.id, element.value, 7);
|
setCookie(element.id, element.value, 7);
|
||||||
|
} else {
|
||||||
|
//console.log("Set cookie " + element.id + " EMPTY")
|
||||||
|
setCookie(element.id, "", 7)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setCookie(element.id, element.value, 7);
|
setCookie(element.id, element.value, 7);
|
||||||
@ -266,32 +229,57 @@ templ Usage(access models.ConvergeAccess) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadFormFromCookie() {
|
function loadFormFromCookie() {
|
||||||
|
console.log("Load form from cookie")
|
||||||
const form = document.getElementById('inputs');
|
const form = document.getElementById('inputs');
|
||||||
|
if (!form) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updated = false
|
||||||
for (let element of form.elements) {
|
for (let element of form.elements) {
|
||||||
if (element.id) {
|
if (element.id) {
|
||||||
const value = getCookie(element.id);
|
const value = getCookie(element.id);
|
||||||
console.log("Loading " + element.id + " value: " + value)
|
//console.log("Loading " + element.id + " value: " + value)
|
||||||
if (value !== null) {
|
if (value !== null) {
|
||||||
if (element.type === 'checkbox') {
|
if (element.type === 'checkbox') {
|
||||||
element.checked = value === 'true';
|
newvalue = value === "true"
|
||||||
|
if (element.checked != newvalue) {
|
||||||
|
console.log("Setting " + element.id + " checked " + newvalue)
|
||||||
|
element.checked = newvalue
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
} else if (element.type === 'radio') {
|
} else if (element.type === 'radio') {
|
||||||
element.checked = (element.value === value);
|
newvalue = element.value === value
|
||||||
|
if (element.checked != newvalue) {
|
||||||
|
console.log("Setting " + element.id + " selected " + newvalue)
|
||||||
|
element.checked = newvalue;
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
element.value = value;
|
if (element.value != value) {
|
||||||
|
console.log("Setting " + element.id + " " + element.value + " -> " + value)
|
||||||
|
element.value = value;
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (updated) {
|
||||||
|
console.log("Sending htmx event to trigger form")
|
||||||
|
htmx.trigger(form, 'formdataloaded')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save form data to cookie on change
|
// Save form data to cookie on change
|
||||||
document.getElementById('inputs').addEventListener('change', saveFormToCookie);
|
document.getElementById('inputs').addEventListener('change', saveFormToCookie);
|
||||||
|
|
||||||
document.body.addEventListener('htmx:load', function(event) {
|
document.body.addEventListener('htmx:load', function(event) {
|
||||||
loadFormFromCookie();
|
//console.log("htmx:load")
|
||||||
|
//loadFormFromCookie();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.body.addEventListener('htmx:afterSettle', function(event) {
|
document.body.addEventListener('htmx:afterSettle', function(event) {
|
||||||
|
console.log("htmx:afterSettle")
|
||||||
loadFormFromCookie();
|
loadFormFromCookie();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
23
pkg/server/templates/usageinputs.go
Normal file
23
pkg/server/templates/usageinputs.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
type UsageInputs struct {
|
||||||
|
Id string
|
||||||
|
SshKeys []string
|
||||||
|
RemoteShells map[string]bool
|
||||||
|
LocalShells map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUsageInputs(id string, remoteShells []string, localShells []string) UsageInputs {
|
||||||
|
inputs := UsageInputs{
|
||||||
|
Id: id,
|
||||||
|
RemoteShells: make(map[string]bool),
|
||||||
|
LocalShells: make(map[string]bool),
|
||||||
|
}
|
||||||
|
for _, remoteShell := range remoteShells {
|
||||||
|
inputs.RemoteShells[remoteShell] = true
|
||||||
|
}
|
||||||
|
for _, localShell := range localShells {
|
||||||
|
inputs.LocalShells[localShell] = true
|
||||||
|
}
|
||||||
|
return inputs
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user