blob: 5ce3534738683239e09cac4ffc3bdcb28ee7706d [file] [log] [blame]
{{ define "task" }}
{{ range . }}
<li aria-busy="{{ eq .Status 1 }}">
{{ if eq .Status 3 }}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="black" d="M21 7L9 19l-5.5-5.5l1.41-1.41L9 16.17L19.59 5.59z"/></svg>{{ end }}{{ .Title }}{{ if .Err }} - {{ .Err.Error }} {{ end }}
{{ if .Subtasks }}
<ul>
{{ template "task" .Subtasks }}
</ul>
{{ end }}
</li>
{{ end }}
{{ end }}
{{ define "schema-form" }}
{{ $readonly := .ReadOnly }}
{{ $networks := .AvailableNetworks }}
{{ $data := .Data }}
{{ range $f := .Schema.Fields }}
{{ $name := $f.Name }}
{{ $schema := $f.Schema }}
{{ if eq $schema.Kind 0 }}
<label {{ if $schema.Advanced }}hidden{{ end }}>
<input type="checkbox" role="swtich" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.checked)" {{ if $readonly }}disabled{{ end }} {{ if index $data $name }}checked{{ end }} />
{{ $schema.Name }}
</label>
{{ else if eq $schema.Kind 7 }}
<label {{ if $schema.Advanced }}hidden{{ end }}>
{{ $schema.Name }}
<input type="text" name="{{ $name }}" oninput="valueChanged({{ $name }}, parseInt(this.value))" {{ if $readonly }}disabled{{ end }} value="{{ index $data $name }}" />
</label>
{{ else if eq $schema.Kind 1 }}
<label {{ if $schema.Advanced }}hidden{{ end }}>
{{ $schema.Name }}
<input type="text" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} value="{{ index $data $name }}" />
{{ else if eq $schema.Kind 4 }}
</label>
<label {{ if $schema.Advanced }}hidden{{ end }}>
{{ $schema.Name }}
<input type="text" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} value="{{ index $data $name }}" />
</label>
{{ else if eq $schema.Kind 3 }}
<label {{ if $schema.Advanced }}hidden{{ end }}>
{{ $schema.Name }}
<select name="{{ $name }}" oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} >
{{ if not $readonly }}<option disabled selected value>Available networks</option>{{ end }}
{{ range $networks }}
<option {{if eq .Name (index $data $name) }}selected{{ end }}>{{ .Name }}</option>
{{ end }}
</select>
</label>
{{ else if eq $schema.Kind 5 }}
{{ $auth := index $data $name }}
{{ $authEnabled := false }}
{{ $authGroups := "" }}
{{ if and $auth (index $auth "enabled") }}{{ $authEnabled = true }}{{ end }}
{{ if and $auth (index $auth "groups") }}{{ $authGroups = index $auth "groups" }}{{ end }}
<label {{ if $schema.Advanced }}hidden{{ end }}>
<input type="checkbox" role="swtich" name="authEnabled" oninput="valueChanged('{{- $name -}}.enabled', this.checked)" {{ if $readonly }}disabled{{ end }} {{ if $authEnabled }}checked{{ end }} />
<span>Require authentication</span>
</label>
<label for="authGroups">
<span>Authentication groups</span>
<input type="text" name="authGroups" oninput="valueChanged('{{- $name -}}.groups', this.value)" {{ if $readonly }}disabled{{ end }} value="{{ $authGroups }}" />
</label>
{{ else if eq $schema.Kind 6 }}
{{ $sshKey := index $data $name }}
{{ $public := "" }}
{{ $private := "" }}
{{ if $sshKey }}{{ $public = index $sshKey "public" }}{{ end }}
{{ if $sshKey }}{{ $private = index $sshKey "private" }}{{ end }}
<label {{ if $schema.Advanced }}hidden{{ end }}>
<span>Public Key</span>
<textarea name="{{ $name }}-public" disabled>{{ $public }}</textarea>
</label>
<label {{ if $schema.Advanced }}hidden{{ end }}>
<span>Private Key</span>
<textarea name="{{ $name }}-private" disabled>{{ $private }}</textarea>
</label>
{{ end }}
{{ end }}
{{ end }}
{{ define "header" }}
<h1>{{ .App.Icon }}{{ .App.Name }}</h1>
{{ end }}
{{ define "extra_menu" }}
<li><a href="/app/{{ .App.Slug }}" class="{{ if eq $.CurrentPage .App.Name }}outline{{ else }}secondary{{ end }}">{{ .App.Name }}</a></li>
{{ range .Instances }}
<li><a href="/instance/{{ .Id }}" class="{{ if eq $.CurrentPage .Id }}outline{{ else }}secondary{{ end }}">{{ .Id }}</a></li>
{{ end }}
{{ end }}
{{ define "content"}}
{{ $schema := .App.Schema }}
{{ $networks := .AvailableNetworks }}
{{ $instance := .Instance }}
{{ $renderForm := true }}
{{ if .Task }}
{{if or (eq .Task.Status 0) (eq .Task.Status 1) }}
{{ $renderForm = false }}
Waiting for resources:
<ul class="progress">
{{ template "task" .Task.Subtasks }}
</ul>
{{ end }}
{{ end }}
{{ if $renderForm }}
<form id="config-form">
{{ if $instance }}
{{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" false "Data" ($instance.InputToValues $schema)) }}
{{ else }}
{{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" false "Data" (dict)) }}
{{ end }}
{{ if $instance }}
<div class="grid">
<button type="submit" id="submit" name="update">Update</button>
<button type="submit" id="uninstall" name="remove">Uninstall</button>
</div>
{{ else }}
<button type="submit" id="submit">{{ if $instance }}Update{{ else }}Install{{ end }}</button>
{{ end }}
</form>
{{ end }}
<div id="toast-failure" class="toast hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10Zm3-6L9 8m0 8l6-8"/></svg> {{ if $instance }}Update failed{{ else}}Install failed{{ end }}
</div>
<div id="toast-uninstall-failure" class="toast hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10Zm3-6L9 8m0 8l6-8"/></svg> Failed to uninstall application
</div>
<script>
let config = {{ if $instance }}JSON.parse({{ toJson ($instance.InputToValues $schema) }}){{ else }}{}{{ end }};
function setValue(name, value, config) {
let items = name.split(".")
for (let i = 0; i < items.length - 1; i++) {
if (!(items[i] in config)) {
config[items[i]] = {}
}
config = config[items[i]];
}
config[items[items.length - 1]] = value;
}
function valueChanged(name, value) {
setValue(name, value, config);
}
function disableForm() {
document.querySelectorAll("#config-form input").forEach((i) => i.setAttribute("disabled", ""));
document.querySelectorAll("#config-form select").forEach((i) => i.setAttribute("disabled", ""));
document.querySelectorAll("#config-form button").forEach((i) => i.setAttribute("disabled", ""));
}
function enableForm() {
document.querySelectorAll("[aria-busy]").forEach((i) => i.removeAttribute("aria-busy"));
document.querySelectorAll("#config-form input").forEach((i) => i.removeAttribute("disabled"));
document.querySelectorAll("#config-form select").forEach((i) => i.removeAttribute("disabled"));
document.querySelectorAll("#config-form button").forEach((i) => i.removeAttribute("disabled"));
}
function installStarted() {
const submit = document.getElementById("submit");
submit.setAttribute("aria-busy", true);
submit.innerHTML = {{ if $instance }}"Updating ..."{{ else }}"Installing ..."{{ end }};
disableForm();
}
function uninstallStarted() {
const submit = document.getElementById("uninstall");
submit.setAttribute("aria-busy", true);
submit.innerHTML = "Uninstalling ...";
disableForm();
}
function actionFinished(toast) {
enableForm();
toast.classList.remove("hidden");
setTimeout(
() => toast.classList.add("hidden"),
2000,
);
}
function installFailed() {
actionFinished(document.getElementById("toast-failure"));
}
function uninstallFailed() {
actionFinished(document.getElementById("toast-uninstall-failure"));
}
const submitAddr = {{ if $instance }}"/api/instance/{{ $instance.Id }}/update"{{ else }}"/api/app/{{ .App.Slug }}/install"{{ end }};
async function install() {
installStarted();
const resp = await fetch(submitAddr, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify(config),
});
if (resp.status === 200) {
window.location = await resp.text();
} else {
installFailed();
}
}
async function uninstall() {
{{ if $instance }}
uninstallStarted();
const resp = await fetch("/api/instance/{{ $instance.Id }}/remove", {
method: "POST",
});
if (resp.status === 200) {
window.location = await resp.text();
} else {
uninstallFailed();
}
{{ end }}
}
const configForm = document.getElementById("config-form");
if (configForm) {
configForm.addEventListener("submit", (event) => {
event.preventDefault();
if (event.submitter.id === "submit") {
install();
} if (event.submitter.id === "uninstall") {
uninstall();
}
});
}
{{ if .Task }}
async function refresh() {
try {
const resp = await fetch(window.location.href);
if (resp.ok) {
var tmp = document.createElement("html");
tmp.innerHTML = await resp.text();
const progress = tmp.getElementsByClassName("progress")[0];
if (progress) {
document.getElementsByClassName("progress")[0].innerHTML = progress.innerHTML;
} else {
location.reload();
}
}
} catch (error) {
console.log(error);
} finally {
setTimeout(refresh, 3000);
}
}
setTimeout(refresh, 3000);
{{ end }}
</script>
{{end}}