env: status page
Updates page asynchronously every 5 seconds.
Introduces beforeStart and afterStart trigger points to update setup status information.
Change-Id: Ic2f6a9bb7a0fefeefc4d6a1a7338d506a4f99e80
diff --git a/core/installer/welcome/env-manager-tmpl/status.html b/core/installer/welcome/env-manager-tmpl/status.html
index 3a54edf..65e1e15 100644
--- a/core/installer/welcome/env-manager-tmpl/status.html
+++ b/core/installer/welcome/env-manager-tmpl/status.html
@@ -1,6 +1,6 @@
{{ define "task" }}
{{ range . }}
-<li aria-busy="{{ eq .Status 0 }}">
+<li aria-busy="{{ eq .Status 1 }}">
{{ .Title }}{{ if .Err }} - {{ .Err.Error }} {{ end }}
{{ if .Subtasks }}
<ul>
@@ -12,33 +12,58 @@
{{ end }}
{{ define "main" }}
-<article>
- <ul>
- {{ template "task" .Root.Subtasks }}
- </ul>
-</article>
-{{ if .DNSRecords }}
-<div>
- <form action="" method="POST">
- You will have to publish following DNS records via your domain registrar.
- <textarea rows="7">{{ .DNSRecords }}</textarea>
- <label for="domain-registrar">Domain Registrar</label>
- <select id="domain-registrar" required tabindex="1">
- <option value="" selected>Select registrar</option>
- <option value="gandi">Gandi</option>
- <option value="namecheap">Namecheap</option>
- </select>
- <label for="api-token">API Token</label>
- <input
- type="text"
- id="api-token"
- name="api-token"
- required
- autofocus
- tabindex="2"
- />
- <button type="submit" tabindex="3">Update</button>
- </form>
+<div class="grid contents-header">
+ <div style="border-width: 1px; border-right-style: solid;">
+ setup
+ </div>
+ <div>
+ information
+ </div>
</div>
-{{ end }}
+<div id="contents" class="grid env-status">
+ <div>
+ <ul class="progress">
+ {{ template "task" .Root.Subtasks }}
+ </ul>
+ </div>
+ <div id="create-instance-form">
+ {{ if .DNSRecords }}
+ <form action="" method="POST">
+ <p>You will have to publish following DNS records via your domain registrar.</p>
+ <textarea rows="7" disabled>{{ .DNSRecords }}</textarea>
+ <label for="domain-registrar">Domain Registrar</label>
+ <select id="domain-registrar" required tabindex="1">
+ <option value="" selected>Select registrar</option>
+ <option value="gandi">Gandi</option>
+ <option value="namecheap">Namecheap</option>
+ </select>
+ <label for="api-token">API Token</label>
+ <input
+ type="text"
+ id="api-token"
+ name="api-token"
+ required
+ autofocus
+ tabindex="2"
+ />
+ <button type="submit" tabindex="3">Update</button>
+ </form>
+ {{ else if .EnvInfo }}
+ <p>{{ .EnvInfo }}</p>
+ {{ end }}
+ </div>
+</div>
+<script type="text/javascript">
+ async function refresh() {
+ const resp = await fetch(window.location.href);
+ if (resp.ok) {
+ var tmp = document.createElement("html");
+ tmp.innerHTML = await resp.text();
+ document.getElementById("contents").innerHTML = tmp.getElementsByClassName("env-status")[0].innerHTML;
+ }
+ setTimeout(refresh, 5000);
+ }
+
+ setTimeout(refresh, 5000);
+</script>
{{ end }}
diff --git a/core/installer/welcome/env.go b/core/installer/welcome/env.go
index f8ee70d..e0d5a3f 100644
--- a/core/installer/welcome/env.go
+++ b/core/installer/welcome/env.go
@@ -14,6 +14,7 @@
"net/netip"
"strings"
+ "github.com/gomarkdown/markdown"
"github.com/gorilla/mux"
"github.com/giolekva/pcloud/core/installer"
@@ -83,7 +84,9 @@
dnsFetcher installer.ZoneStatusFetcher
nameGenerator installer.NameGenerator
tasks map[string]tasks.Task
+ envInfo map[string]template.HTML
dns map[string]tasks.DNSZoneRef
+ dnsPublished map[string]struct{}
}
func NewEnvServer(
@@ -102,7 +105,9 @@
dnsFetcher,
nameGenerator,
make(map[string]tasks.Task),
+ make(map[string]template.HTML),
make(map[string]tasks.DNSZoneRef),
+ make(map[string]struct{}),
}
}
@@ -130,23 +135,29 @@
http.Error(w, "Task not found", http.StatusBadRequest)
return
}
- dnsRef, ok := s.dns[key]
- if !ok {
- http.Error(w, "Task dns configuration not found", http.StatusInternalServerError)
- return
+ dnsRecords := ""
+ if _, ok := s.dnsPublished[key]; !ok {
+ dnsRef, ok := s.dns[key]
+ if !ok {
+ http.Error(w, "Task dns configuration not found", http.StatusInternalServerError)
+ return
+ }
+ err, ready, info := s.dnsFetcher.Fetch(dnsRef.Namespace, dnsRef.Name)
+ // TODO(gio): check error type
+ if err != nil && (ready || len(info.Records) > 0) {
+ panic("!! SHOULD NOT REACH !!")
+ }
+ if !ready && len(info.Records) > 0 {
+ panic("!! SHOULD NOT REACH !!")
+ }
+ dnsRecords = info.Records
}
- err, ready, info := s.dnsFetcher.Fetch(dnsRef.Namespace, dnsRef.Name)
- // TODO(gio): check error type
- if err != nil && (ready || len(info.Records) > 0) {
- panic("!! SHOULD NOT REACH !!")
- }
- if !ready && len(info.Records) > 0 {
- panic("!! SHOULD NOT REACH !!")
- }
- if err := tmplsParsed.status.Execute(w, map[string]any{
+ data := map[string]any{
"Root": t,
- "DNSRecords": info.Records,
- }); err != nil {
+ "EnvInfo": s.envInfo[key],
+ "DNSRecords": dnsRecords,
+ }
+ if err := tmplsParsed.status.Execute(w, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -184,6 +195,8 @@
return
}
}
+ s.envInfo[key] = "Successfully published DNS records, waiting to propagate."
+ s.dnsPublished[key] = struct{}{}
http.Redirect(w, r, fmt.Sprintf("/env/%s", key), http.StatusSeeOther)
}
@@ -359,6 +372,17 @@
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
+ key := func() string {
+ for {
+ key, err := s.nameGenerator.Generate()
+ if err == nil {
+ return key
+ }
+ }
+ }()
+ infoUpdater := func(info string) {
+ s.envInfo[key] = template.HTML(markdown.ToHTML([]byte(info), nil, nil))
+ }
t, dns := tasks.NewCreateEnvTask(
tasks.Env{
PCloudEnvName: env.Name,
@@ -374,15 +398,8 @@
startIP,
s.nsCreator,
s.repo,
+ infoUpdater,
)
- key := func() string {
- for {
- key, err := s.nameGenerator.Generate()
- if err == nil {
- return key
- }
- }
- }()
s.tasks[key] = t
s.dns[key] = dns
go t.Start()
diff --git a/core/installer/welcome/static/main.css b/core/installer/welcome/static/main.css
index 2a01ae8..76485a1 100644
--- a/core/installer/welcome/static/main.css
+++ b/core/installer/welcome/static/main.css
@@ -90,11 +90,15 @@
color: #ffffff;
}
-#create-instance-form input, textarea, button {
+#create-instance-form input, textarea, button, select {
border-width: 0.5px !important;
background-color: #3a3a3a;
}
+#create-instance-form p {
+ color: #ffffff;
+}
+
#create-instance-form input {
height: 3rem;
color: #ffffff;
@@ -109,3 +113,19 @@
font-weight: bold;
color: #3a3a3a;
}
+
+/* .progress { */
+/* padding-top: 10px; */
+/* } */
+
+.progress {
+ padding-left: 0;
+}
+
+.progress ul {
+ padding-left: 15px;
+}
+
+.progress li {
+ list-style-type: none;
+}