A lot of work in getting cut and paste from the UI to
work properly. Wrote two web components. One for cut and paste in general, and another for code samples.
This commit is contained in:
parent
02914ae40f
commit
7e60e23df1
@ -51,9 +51,21 @@ func main() {
|
||||
return templates2.UsageTab(access)
|
||||
}
|
||||
|
||||
usageInputs := templates2.UsageInputs{
|
||||
Id: "myid",
|
||||
SshKeys: []string{"a", "b"},
|
||||
ErrorMessages: []string{},
|
||||
RemoteShells: map[string]bool{templates2.POWERSHELL: true},
|
||||
LocalShells: nil,
|
||||
}
|
||||
shellUsage := func() templ.Component {
|
||||
return templates2.ShellUsageTab(access, usageInputs)
|
||||
}
|
||||
|
||||
render(dir, "fullindex.html", fullindex)
|
||||
render(dir, "index.html", templates2.AboutTab)
|
||||
render(dir, "usage.html", usage)
|
||||
render(dir, "shellusage.html", shellUsage)
|
||||
render(dir, "downloads.html", templates2.Downloads)
|
||||
|
||||
render(dir, "sessions-none.html", func() templ.Component {
|
||||
|
@ -16,10 +16,13 @@ templ BasePage(tab int) {
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="../static/css/bootstrap.min.css"
|
||||
crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="../static/icons/font/bootstrap-icons.css">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<script src="../static/js/htmx.1.9.12.js"></script>
|
||||
<script src="../static/js/htmx.ws.1.9.12.js"></script>
|
||||
<script type="module" src="../static/js/custom.js"></script>
|
||||
|
||||
<title>Converge</title>
|
||||
</head>
|
||||
|
@ -13,32 +13,31 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) {
|
||||
}
|
||||
|
||||
<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 usageInputs.RemoteShells[BASH] {
|
||||
<pre>{addSshKeys(BASH, usageInputs.SshKeys)}
|
||||
curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent > agent{`
|
||||
chmod 755 agent
|
||||
`}./agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}{`
|
||||
rm -f agent
|
||||
`}</pre>
|
||||
<code-sample id="run-agent-bash">@templ.Raw(addSshKeys(BASH, usageInputs.SshKeys)) <br/>
|
||||
curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent > agent <br/>
|
||||
chmod 755 agent <br/>
|
||||
./agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}<br/>
|
||||
rm -f agent <br/>
|
||||
</code-sample>
|
||||
}
|
||||
if usageInputs.RemoteShells[CMD] {
|
||||
<pre>{addSshKeys(CMD, usageInputs.SshKeys)}
|
||||
curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent.exe > agent.exe{`
|
||||
`}agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}{`
|
||||
del agent.exe
|
||||
`}</pre>
|
||||
<code-sample id="run-agent-cmd">@templ.Raw(addSshKeys(CMD, usageInputs.SshKeys)) <br/>
|
||||
curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent.exe > agent.exe <br/>
|
||||
agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl} <br/>
|
||||
del agent.exe <br/>
|
||||
</code-sample>
|
||||
}
|
||||
if usageInputs.RemoteShells[POWERSHELL] {
|
||||
<pre>{addSshKeys(POWERSHELL, usageInputs.SshKeys)}
|
||||
curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent.exe > agent.exe{`
|
||||
`}agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}{`
|
||||
del agent.exe
|
||||
`}</pre>
|
||||
<code-sample id="run-agent-powershell">@templ.Raw(addSshKeys(POWERSHELL, usageInputs.SshKeys)) <br/>
|
||||
curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent.exe > agent.exe <br/>
|
||||
agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl} <br/>
|
||||
del agent.exe <br/>
|
||||
</code-sample>
|
||||
}
|
||||
|
||||
<p>
|
||||
@ -61,10 +60,13 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) {
|
||||
by the same user that started the agent.
|
||||
</p>
|
||||
|
||||
<pre>{`
|
||||
`}ssh -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.BaseUrl}/client/{usageInputs.Id}" { access.Username }{"@localhost"} {`
|
||||
`}sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.BaseUrl}/client/{usageInputs.Id}" { access.Username }{"@localhost"} {`
|
||||
`}</pre>
|
||||
<code-sample id="ssh-connect">
|
||||
ssh -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.BaseUrl}/client/{usageInputs.Id}" { access.Username }{"@localhost"}
|
||||
</code-sample>
|
||||
<code-sample id="sftp-connect">
|
||||
sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.BaseUrl}/client/{usageInputs.Id}" { access.Username }{"@localhost"}
|
||||
</code-sample>
|
||||
|
||||
|
||||
<p>This requires the <code>wsproxy</code> utility which is available in the
|
||||
<a href="downloads.html">downloads</a> section. This utility must be downloaded
|
||||
@ -76,49 +78,44 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) {
|
||||
way to connect. In this method, a local port forwarder is started that forwards a local port
|
||||
to the webserver. Then you can start an ssh client that connects to the local tcp port.
|
||||
</p>
|
||||
<pre>{`
|
||||
`}ssh -oServerAliveInterval=10 -p 10000 { access.Username }{"@localhost"} {`
|
||||
`}sftp -oServerAliveInterval=10 -p 10000 { access.Username }{"@localhost"} {`
|
||||
`}</pre>
|
||||
<code-sample id="tcptows-ssh">ssh -oServerAliveInterval=10 -p 10000 { access.Username }{"@localhost"}</code-sample>
|
||||
<code-sample id="tcptows-sftp">sftp -oServerAliveInterval=10 -p 10000 { access.Username }{"@localhost"}</code-sample>
|
||||
|
||||
<p>This requires the <code>tcptows</code> utility which is available in the
|
||||
<a href="downloads.html">downloads</a> section. The utility must be started beforehand
|
||||
using:
|
||||
</p>
|
||||
<pre>{`
|
||||
`}tcptows ws{access.Secure}://{access.BaseUrl}/client/{usageInputs.Id} {`
|
||||
`}tcptows ws{access.Secure}://{access.BaseUrl}/client/{usageInputs.Id} {`
|
||||
`}</pre>
|
||||
<code-sample id="tcptows-ssh">tcptows 10000 ws{access.Secure}://{access.BaseUrl}/client/{usageInputs.Id}</code-sample>
|
||||
|
||||
<h2>Working with the agent</h2>
|
||||
|
||||
if usageInputs.RemoteShells[BASH] {
|
||||
<pre>{`
|
||||
# cd back to the agent directory
|
||||
cd $agentdir
|
||||
|
||||
# prevent logout when last user exits
|
||||
touch $agentdir/.hold
|
||||
`}</pre>
|
||||
<code-sammple id="session-bash">
|
||||
# cd back to the agent directory <br/>
|
||||
cd $agentdir <br/>
|
||||
<br/>
|
||||
# prevent logout when last user exits <br/>
|
||||
touch $agentdir/.hold<br/>
|
||||
</code-sammple>
|
||||
|
||||
}
|
||||
if usageInputs.RemoteShells[CMD] {
|
||||
<pre>{`
|
||||
# cd back to the agent directory
|
||||
cd %agentdir%
|
||||
|
||||
# prevent logout when last user exits
|
||||
echo > %agentdir%\.hold
|
||||
`}</pre>
|
||||
<code-sample id="session-cmd">
|
||||
# cd back to the agent directory <br/>
|
||||
cd %agentdir% <br/>
|
||||
<br/>
|
||||
# prevent logout when last user exits <br/>
|
||||
echo > %agentdir%\.hold <br/>
|
||||
</code-sample>
|
||||
}
|
||||
if usageInputs.RemoteShells[POWERSHELL] {
|
||||
<pre>{`
|
||||
# cd back to the agent directory
|
||||
cd $env:agentdir
|
||||
|
||||
# prevent logout when last user exits
|
||||
$null > $env:agentdir\.hold
|
||||
`}</pre>
|
||||
<code-sample id="session-powershell">
|
||||
# cd back to the agent directory <br/>
|
||||
cd $env:agentdir <br/>
|
||||
<br/>
|
||||
# prevent logout when last user exits <br/>
|
||||
$null > $env:agentdir\.hold <br/>
|
||||
</code-sample>
|
||||
}
|
||||
if usageInputs.RemoteShells[CMD] || usageInputs.RemoteShells[POWERSHELL] {
|
||||
<p>
|
||||
@ -145,6 +142,7 @@ templ Usage(access models.ConvergeAccess) {
|
||||
<div>
|
||||
<h1>Usage</h1>
|
||||
|
||||
<!-- TODO Investigate how to do this properly with bootstrap -->
|
||||
<style>
|
||||
.minimal-width {
|
||||
width: 1%;
|
||||
@ -308,3 +306,10 @@ templ UsageTab(access models.ConvergeAccess) {
|
||||
@Usage(access)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
templ ShellUsageTab(access models.ConvergeAccess, usageInputs UsageInputs) {
|
||||
@BasePage(2) {
|
||||
@ShellUsage(access, usageInputs)
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ func addSshKeys(shell string, keys []string) string {
|
||||
if index == 0 {
|
||||
operator = ">"
|
||||
}
|
||||
res += fmt.Sprintf(" echo %s%s%s %s .authorized_keys\n", quote, key, quote,
|
||||
res += fmt.Sprintf(" echo %s%s%s %s .authorized_keys<br/>", quote, key, quote,
|
||||
operator)
|
||||
}
|
||||
return res + " "
|
||||
|
@ -5,10 +5,11 @@ make
|
||||
cd bin
|
||||
|
||||
mkdir -p html
|
||||
mkdir -p html/static/css html/static/js html/docs
|
||||
mkdir -p html/static/css html/static/js html/static/icons html/docs
|
||||
|
||||
rsync -av ../static/css/ html/static/css/
|
||||
rsync -av ../static/js/ html/static/js/
|
||||
rsync -av ../static/icons/ html/static/icons/
|
||||
mkdir -p html/docs
|
||||
rsync -av agent* wsproxy* wstotcp* html/downloads
|
||||
./templaterender
|
||||
|
76
static/js/copy-to-clipboard.js
Normal file
76
static/js/copy-to-clipboard.js
Normal file
@ -0,0 +1,76 @@
|
||||
|
||||
|
||||
|
||||
class CopyToClipboardComponent extends HTMLElement {
|
||||
static STYLE = `
|
||||
<style>
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="../static/icons/font/bootstrap-icons.css">
|
||||
`;
|
||||
static IN_ACTIVE = CopyToClipboardComponent.STYLE + `<div class="clickable"><i class="bi bi-clipboard"></i> copy</div>`;
|
||||
static ACTIVE = CopyToClipboardComponent.STYLE + `<div class="clickable"><i class="bi bi-clipboard-check"></i> copied</div>`;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.render(CopyToClipboardComponent.IN_ACTIVE);
|
||||
this.addEventListeners();
|
||||
}
|
||||
|
||||
render(value) {
|
||||
this.shadowRoot.innerHTML = value;
|
||||
}
|
||||
|
||||
addEventListeners() {
|
||||
this.addEventListener('click', this.copyToClipboard.bind(this));
|
||||
}
|
||||
|
||||
copyToClipboard() {
|
||||
const fromElement = document.getElementById(this.getAttribute('from'));
|
||||
if (fromElement) {
|
||||
navigator.clipboard.writeText(fromElement.innerText)
|
||||
.then(() => {
|
||||
console.log('Content copied to clipboard');
|
||||
this.render(CopyToClipboardComponent.ACTIVE)
|
||||
setTimeout((target) => {
|
||||
this.render(CopyToClipboardComponent.IN_ACTIVE)
|
||||
}, 3000, this);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to copy: ', err);
|
||||
});
|
||||
} else {
|
||||
console.error('Element not found');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
customElements.define('copy-to-clipboard', CopyToClipboardComponent);
|
||||
|
||||
|
||||
|
||||
|
||||
class CodeSampleComponent extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
let id = this.getAttribute("id");
|
||||
let html = "<div class='small ps-5'><copy-to-clipboard from='" + id + "'></copy-to-clipboard></div>";
|
||||
html += "<p class='font-monospace small ps-5' id='" +id + "' >" + this.innerHTML + "</p>";
|
||||
console.log("html=" + html);
|
||||
this.innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('code-sample', CodeSampleComponent);
|
||||
|
2
static/js/custom.js
Normal file
2
static/js/custom.js
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
import './copy-to-clipboard.js';
|
Loading…
Reference in New Issue
Block a user