blob: b25f5b19d9d4d531786c997168edd05fed43bd6e [file] [log] [blame]
gio778577f2024-04-29 09:44:38 +04001{{ define "task" }}
2{{ range . }}
3<li aria-busy="{{ eq .Status 1 }}">
giof9f0bee2024-06-11 20:10:05 +04004 {{ 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 }}
gio778577f2024-04-29 09:44:38 +04005 {{ if .Subtasks }}
6 <ul>
7 {{ template "task" .Subtasks }}
8 </ul>
9 {{ end }}
10</li>
11{{ end }}
12{{ end }}
13
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040014{{ define "schema-form" }}
15 {{ $readonly := .ReadOnly }}
16 {{ $networks := .AvailableNetworks }}
17 {{ $data := .Data }}
gio44f621b2024-04-29 09:44:38 +040018 {{ range $f := .Schema.Fields }}
19 {{ $name := $f.Name }}
20 {{ $schema := $f.Schema }}
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040021 {{ if eq $schema.Kind 0 }}
gio44f621b2024-04-29 09:44:38 +040022 <label {{ if $schema.Advanced }}hidden{{ end }}>
23 <input type="checkbox" role="swtich" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.checked)" {{ if $readonly }}disabled{{ end }} {{ if index $data $name }}checked{{ end }} />
24 {{ $schema.Name }}
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040025 </label>
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +040026 {{ else if eq $schema.Kind 7 }}
gio44f621b2024-04-29 09:44:38 +040027 <label {{ if $schema.Advanced }}hidden{{ end }}>
28 {{ $schema.Name }}
29 <input type="text" name="{{ $name }}" oninput="valueChanged({{ $name }}, parseInt(this.value))" {{ if $readonly }}disabled{{ end }} value="{{ index $data $name }}" />
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +040030 </label>
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040031 {{ else if eq $schema.Kind 1 }}
gio44f621b2024-04-29 09:44:38 +040032 <label {{ if $schema.Advanced }}hidden{{ end }}>
33 {{ $schema.Name }}
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040034 <input type="text" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} value="{{ index $data $name }}" />
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040035 {{ else if eq $schema.Kind 4 }}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040036 </label>
gio44f621b2024-04-29 09:44:38 +040037 <label {{ if $schema.Advanced }}hidden{{ end }}>
38 {{ $schema.Name }}
39 <input type="text" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} value="{{ index $data $name }}" />
40 </label>
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040041 {{ else if eq $schema.Kind 3 }}
gio44f621b2024-04-29 09:44:38 +040042 <label {{ if $schema.Advanced }}hidden{{ end }}>
43 {{ $schema.Name }}
giobb338e22024-07-18 18:09:22 +040044 <details class="dropdown">
45 {{ $selectedNetwork := index $data $name }}
46 <summary id="{{ $name }}">{{ $selectedNetwork }}</summary>
47 <ul>
48 {{ range $networks }}
49 {{ $selected := eq $selectedNetwork .Name }}
50 <li>
51 <label>
52 <input type="radio" name="{{ $name }}" oninput="networkSelected('{{ $name }}', '{{ .Name }}', this.checked)" {{ if $selected }}checked{{ end }} />
53 {{ .Name }}
54 </label>
55 </li>
56 {{ end }}
57 </ul>
58 </details>
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040059 </label>
gio4ece99c2024-07-18 11:05:50 +040060 {{ else if eq $schema.Kind 10 }}
61 <label {{ if $schema.Advanced }}hidden{{ end }}>
62 {{ $schema.Name }}
63 <details class="dropdown">
64 {{ $selectedNetworks := index $data $name }}
65 <summary id="{{ $name }}">{{ $selectedNetworks | join "," }}</summary>
66 <ul>
67 {{ range $networks }}
68 {{ $networkName := .Name }}
69 {{ $selected := false }}
70 {{ range $selectedNetworks }}
71 {{ if eq . $networkName }}
72 {{ $selected = true }}
73 {{ end }}
74 {{ end }}
75 <li>
76 <label>
77 <input type="checkbox" name="{{ $networkName }}" oninput="multiNetworkSelected('{{ $name }}', '{{ $networkName }}', this.checked)" {{ if $selected }}checked{{ end }} />
78 {{ .Name }}
79 </label>
80 </li>
81 {{ end }}
82 </ul>
83 </details>
84 </label>
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040085 {{ else if eq $schema.Kind 5 }}
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040086 {{ $auth := index $data $name }}
87 {{ $authEnabled := false }}
88 {{ $authGroups := "" }}
89 {{ if and $auth (index $auth "enabled") }}{{ $authEnabled = true }}{{ end }}
90 {{ if and $auth (index $auth "groups") }}{{ $authGroups = index $auth "groups" }}{{ end }}
gio44f621b2024-04-29 09:44:38 +040091 <label {{ if $schema.Advanced }}hidden{{ end }}>
92 <input type="checkbox" role="swtich" name="authEnabled" oninput="valueChanged('{{- $name -}}.enabled', this.checked)" {{ if $readonly }}disabled{{ end }} {{ if $authEnabled }}checked{{ end }} />
93 <span>Require authentication</span>
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040094 </label>
gio44f621b2024-04-29 09:44:38 +040095 <label for="authGroups">
96 <span>Authentication groups</span>
97 <input type="text" name="authGroups" oninput="valueChanged('{{- $name -}}.groups', this.value)" {{ if $readonly }}disabled{{ end }} value="{{ $authGroups }}" />
98 </label>
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +040099 {{ else if eq $schema.Kind 6 }}
100 {{ $sshKey := index $data $name }}
101 {{ $public := "" }}
102 {{ $private := "" }}
103 {{ if $sshKey }}{{ $public = index $sshKey "public" }}{{ end }}
104 {{ if $sshKey }}{{ $private = index $sshKey "private" }}{{ end }}
gio44f621b2024-04-29 09:44:38 +0400105 <label {{ if $schema.Advanced }}hidden{{ end }}>
106 <span>Public Key</span>
107 <textarea name="{{ $name }}-public" disabled>{{ $public }}</textarea>
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +0400108 </label>
gio44f621b2024-04-29 09:44:38 +0400109 <label {{ if $schema.Advanced }}hidden{{ end }}>
110 <span>Private Key</span>
111 <textarea name="{{ $name }}-private" disabled>{{ $private }}</textarea>
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +0400112 </label>
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400113 {{ end }}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400114 {{ end }}
115{{ end }}
116
Davit Tabidze3ec24cf2024-05-22 14:06:02 +0400117{{ define "header" }}
Davit Tabidzed1b742e2024-07-15 16:01:52 +0400118 {{ .App.Icon }}
119 <h1>{{ .App.Name }}</h1>
Davit Tabidze3ec24cf2024-05-22 14:06:02 +0400120{{ end }}
gio18d5c682024-05-02 10:30:57 +0400121
Davit Tabidze3ec24cf2024-05-22 14:06:02 +0400122{{ define "extra_menu" }}
gio1cd65152024-08-16 08:18:49 +0400123 <li><a href="/app/{{ .App.Slug }}" {{ if eq $.CurrentPage .App.Name }}class="primary"{{ end }}>{{ .App.Name }}</a></li>
124 {{ if (and (not $.Instance) $.Task) }}
125 <li><a href="/instance/{{ $.CurrentPage }}" class="primary">{{ $.CurrentPage }}</a></li>
126 {{ end }}
Davit Tabidze3ec24cf2024-05-22 14:06:02 +0400127 {{ range .Instances }}
gio1cd65152024-08-16 08:18:49 +0400128 <li><a href="/instance/{{ .Id }}" {{ if eq $.CurrentPage .Id }}class="primary"{{ end }}>{{ .Id }}</a></li>
gio778577f2024-04-29 09:44:38 +0400129 {{ end }}
130{{ end }}
131
Davit Tabidze3ec24cf2024-05-22 14:06:02 +0400132{{ define "content"}}
133 {{ $schema := .App.Schema }}
134 {{ $networks := .AvailableNetworks }}
135 {{ $instance := .Instance }}
136 {{ $renderForm := true }}
137
138 {{ if .Task }}
139 {{if or (eq .Task.Status 0) (eq .Task.Status 1) }}
140 {{ $renderForm = false }}
gio1cd65152024-08-16 08:18:49 +0400141 Installation in progress (feel free to navigate away from this page):
Davit Tabidze3ec24cf2024-05-22 14:06:02 +0400142 <ul class="progress">
143 {{ template "task" .Task.Subtasks }}
144 </ul>
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400145 {{ end }}
Davit Tabidze3ec24cf2024-05-22 14:06:02 +0400146 {{ end }}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400147
Davit Tabidze3ec24cf2024-05-22 14:06:02 +0400148 {{ if $renderForm }}
149 <form id="config-form">
150 {{ if $instance }}
151 {{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" false "Data" ($instance.InputToValues $schema)) }}
152 {{ else }}
153 {{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" false "Data" (dict)) }}
154 {{ end }}
155 {{ if $instance }}
156 <div class="grid">
157 <button type="submit" id="submit" name="update">Update</button>
158 <button type="submit" id="uninstall" name="remove">Uninstall</button>
159 </div>
160 {{ else }}
161 <button type="submit" id="submit">{{ if $instance }}Update{{ else }}Install{{ end }}</button>
162 {{ end }}
163 </form>
gio778577f2024-04-29 09:44:38 +0400164 {{ end }}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400165
166<div id="toast-failure" class="toast hidden">
167 <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 }}
168</div>
169
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400170<div id="toast-uninstall-failure" class="toast hidden">
171 <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
172</div>
173
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400174<script>
gio3cdee592024-04-17 10:15:56 +0400175 let config = {{ if $instance }}JSON.parse({{ toJson ($instance.InputToValues $schema) }}){{ else }}{}{{ end }};
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400176
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400177 function setValue(name, value, config) {
178 let items = name.split(".")
179 for (let i = 0; i < items.length - 1; i++) {
180 if (!(items[i] in config)) {
181 config[items[i]] = {}
182 }
183 config = config[items[i]];
184 }
185 config[items[items.length - 1]] = value;
gio4ece99c2024-07-18 11:05:50 +0400186 }
187
188 function getValue(name, value) {
189 let items = name.split(".")
190 for (let i = 0; i < items.length - 1; i++) {
191 if (!(items[i] in config)) {
192 config[items[i]] = {}
193 }
194 config = config[items[i]];
195 }
196 return config[items[items.length - 1]];
197 }
198
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400199 function valueChanged(name, value) {
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400200 setValue(name, value, config);
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400201 }
202
giobb338e22024-07-18 18:09:22 +0400203 function networkSelected(name, network, selected) {
204 console.log(selected);
205 setValue(name, network, config);
206 let summary = document.getElementById(name);
207 summary.innerHTML = network;
208 summary.parentNode.removeAttribute("open");
209 }
210
gio4ece99c2024-07-18 11:05:50 +0400211 function multiNetworkSelected(name, network, selected) {
212 let v = getValue(name, config);
213 if (v === undefined) {
214 v = [];
215 }
216 if (selected) {
217 v.push(network);
218 } else {
219 v = v.filter((n) => n != network);
220 }
221 setValue(name, v, config);
222 document.getElementById(name).innerHTML = v.join(",");
223 }
224
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400225 function disableForm() {
226 document.querySelectorAll("#config-form input").forEach((i) => i.setAttribute("disabled", ""));
227 document.querySelectorAll("#config-form select").forEach((i) => i.setAttribute("disabled", ""));
228 document.querySelectorAll("#config-form button").forEach((i) => i.setAttribute("disabled", ""));
229 }
230
231 function enableForm() {
232 document.querySelectorAll("[aria-busy]").forEach((i) => i.removeAttribute("aria-busy"));
233 document.querySelectorAll("#config-form input").forEach((i) => i.removeAttribute("disabled"));
234 document.querySelectorAll("#config-form select").forEach((i) => i.removeAttribute("disabled"));
235 document.querySelectorAll("#config-form button").forEach((i) => i.removeAttribute("disabled"));
236 }
237
238 function installStarted() {
239 const submit = document.getElementById("submit");
240 submit.setAttribute("aria-busy", true);
241 submit.innerHTML = {{ if $instance }}"Updating ..."{{ else }}"Installing ..."{{ end }};
242 disableForm();
243 }
244
245 function uninstallStarted() {
246 const submit = document.getElementById("uninstall");
247 submit.setAttribute("aria-busy", true);
248 submit.innerHTML = "Uninstalling ...";
249 disableForm();
250 }
251
252 function actionFinished(toast) {
253 enableForm();
254 toast.classList.remove("hidden");
255 setTimeout(
256 () => toast.classList.add("hidden"),
257 2000,
258 );
259 }
260
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400261 function installFailed() {
262 actionFinished(document.getElementById("toast-failure"));
263 }
264
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400265 function uninstallFailed() {
266 actionFinished(document.getElementById("toast-uninstall-failure"));
267 }
268
gio44f621b2024-04-29 09:44:38 +0400269 const submitAddr = {{ if $instance }}"/api/instance/{{ $instance.Id }}/update"{{ else }}"/api/app/{{ .App.Slug }}/install"{{ end }};
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400270
271 async function install() {
272 installStarted();
273 const resp = await fetch(submitAddr, {
gio778577f2024-04-29 09:44:38 +0400274 method: "POST",
275 headers: {
276 "Content-Type": "application/json",
277 "Accept": "application/json",
278 },
279 body: JSON.stringify(config),
280 });
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400281 if (resp.status === 200) {
gio778577f2024-04-29 09:44:38 +0400282 window.location = await resp.text();
283 } else {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400284 installFailed();
285 }
286 }
287
288 async function uninstall() {
289 {{ if $instance }}
290 uninstallStarted();
291 const resp = await fetch("/api/instance/{{ $instance.Id }}/remove", {
292 method: "POST",
293 });
294 if (resp.status === 200) {
gio778577f2024-04-29 09:44:38 +0400295 window.location = await resp.text();
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400296 } else {
297 uninstallFailed();
298 }
299 {{ end }}
300 }
301
giof9f0bee2024-06-11 20:10:05 +0400302 const configForm = document.getElementById("config-form");
303 if (configForm) {
304 configForm.addEventListener("submit", (event) => {
305 event.preventDefault();
306 if (event.submitter.id === "submit") {
307 install();
308 } if (event.submitter.id === "uninstall") {
309 uninstall();
310 }
311 });
312 }
313
314 {{ if .Task }}
315 async function refresh() {
316 try {
317 const resp = await fetch(window.location.href);
318 if (resp.ok) {
319 var tmp = document.createElement("html");
320 tmp.innerHTML = await resp.text();
321 const progress = tmp.getElementsByClassName("progress")[0];
322 if (progress) {
323 document.getElementsByClassName("progress")[0].innerHTML = progress.innerHTML;
324 } else {
325 location.reload();
326 }
327 }
328 } catch (error) {
329 console.log(error);
330 } finally {
331 setTimeout(refresh, 3000);
332 }
333 }
334 setTimeout(refresh, 3000);
335 {{ end }}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400336</script>
Davit Tabidze3ec24cf2024-05-22 14:06:02 +0400337
338{{end}}