Add support for provisioning and rebuilding via interface
This commit is contained in:
parent
f5c5999f44
commit
ddb4bb3546
76 changed files with 1306 additions and 625 deletions
|
|
@ -75,6 +75,7 @@ var (
|
|||
|
||||
menuInstances = component.MenuItem{Title: "Instances", Path: "/admin/instances/"}
|
||||
menuInstance = component.DummyMenuItem()
|
||||
menuRebuild = component.DummyMenuItem()
|
||||
menuGrants = component.DummyMenuItem()
|
||||
menuIngredients = component.DummyMenuItem()
|
||||
)
|
||||
|
|
@ -146,6 +147,11 @@ func (admin *Admin) HandleRoute(ctx context.Context, route string) (handler http
|
|||
router.Handler(http.MethodGet, route+"instance/:slug", instance)
|
||||
}
|
||||
|
||||
{
|
||||
rebuild := admin.instanceRebuild(ctx)
|
||||
router.Handler(http.MethodGet, route+"rebuild/:slug", rebuild)
|
||||
}
|
||||
|
||||
{
|
||||
grants := admin.grants(ctx)
|
||||
router.Handler(http.MethodGet, route+"grants/:slug", grants)
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ func (gc *grantsContext) use(r *http.Request, slug string, admin *Admin) (funcs
|
|||
// replace the functions
|
||||
funcs = []templating.FlagFunc{
|
||||
templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)}),
|
||||
templating.ReplaceCrumb(menuGrants, component.MenuItem{Title: "Grants", Path: template.URL("/admin/instance/" + slug + "/grants/")}),
|
||||
templating.ReplaceCrumb(menuGrants, component.MenuItem{Title: "Grants", Path: template.URL("/admin/grants/" + slug)}),
|
||||
templating.Title(gc.Instance.Slug + " - Grants"),
|
||||
}
|
||||
return funcs, nil
|
||||
|
|
|
|||
|
|
@ -84,6 +84,48 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-1 pure-u-xl-2-5">
|
||||
<div class="padding">
|
||||
<div class="overflow">
|
||||
<table class="pure-table pure-table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
System
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
PHP Version
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ .Instance.System.PHP }}</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Docker Base Image
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ .Instance.System.GetDockerBaseImage }}</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
OPCache Development Config
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ .Instance.System.OpCacheDevelopment }}</code>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1 pure-u-xl-2-5">
|
||||
<div class="padding">
|
||||
<div class="overflow">
|
||||
|
|
@ -182,7 +224,7 @@
|
|||
<tr>
|
||||
<td>
|
||||
Last Rebuild <br>
|
||||
<button class="remote-action pure-button pure-button-action" data-action="rebuild" data-param="{{ .Instance.Slug }}" data-buffer="1000" data-force-reload>Rebuild</button>
|
||||
<a class="pure-button" href="/admin/rebuild/{{ .Info.Slug }}">Rebuild</button>
|
||||
</td>
|
||||
<td>
|
||||
<code class="date">{{ .Info.LastRebuild.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,38 @@
|
|||
<div class="pure-u-1-1">
|
||||
<form class="pure-form pure-form-aligned" id="provision">
|
||||
<fieldset disabled="disabled">
|
||||
<fieldset>
|
||||
<legend>Main Parameters</legend>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="slug">Slug</label>
|
||||
<input name="slug" id="slug" placeholder="" autocomplete="slug">
|
||||
</div>
|
||||
<input type="submit" value="Provision" class="pure-button">
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>System Parameters</legend>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="php">PHP Version</label>
|
||||
<select id="php">
|
||||
<option value="" selected>Default ({{ .DefaultPHPVersion }})</option>
|
||||
{{ range .PHPVersions }}
|
||||
<option value="{{ . }}">{{ . }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
<label for="opcacheDevelopment" class="pure-checkbox">
|
||||
<input type="checkbox" id="opcacheDevelopment" />
|
||||
Opache Development Configuration
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Profile</legend>
|
||||
|
||||
<div class="pure-control-group">
|
||||
Not yet available
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<input type="submit" value="Provision" class="pure-button">
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<div class="pure-u-1-1">
|
||||
<form class="pure-form pure-form-aligned" id="provision">
|
||||
<fieldset>
|
||||
<legend>Main Parameters</legend>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="slug">Slug</label>
|
||||
<input name="slug" id="slug" placeholder="" autocomplete="slug" readonly="readonly" value="{{ .Slug }}">
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>System Parameters</legend>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="php">PHP Version</label>
|
||||
<select id="php">
|
||||
{{ $PHP := .System.PHP }}
|
||||
<option {{ if eq $PHP "" }}selected{{ end }}>Default ({{ .DefaultPHPVersion }})</option>
|
||||
{{ range .PHPVersions }}
|
||||
<option {{ if eq $PHP . }}selected{{ end }} value="{{ . }}">{{ . }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
<label for="opcacheDevelopment" class="pure-checkbox">
|
||||
<input {{ if .System.OpCacheDevelopment }}checked{{end}} type="checkbox" id="opcacheDevelopment" check/>
|
||||
Opache Development Configuration
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Profile</legend>
|
||||
|
||||
<div class="pure-control-group">
|
||||
Not yet available
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<input type="submit" value="Rebuild" class="pure-button">
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
|
|
@ -22,7 +23,18 @@ var instanceProvisionTemplate = templating.Parse[instanceProvisionContext](
|
|||
type instanceProvisionContext struct {
|
||||
templating.RuntimeFlags
|
||||
|
||||
// nothing for the moment
|
||||
systemParams
|
||||
}
|
||||
|
||||
type systemParams struct {
|
||||
PHPVersions []string
|
||||
DefaultPHPVersion string
|
||||
}
|
||||
|
||||
func newSystemParams() (sp systemParams) {
|
||||
sp.PHPVersions = models.KnownPHPVersions()
|
||||
sp.DefaultPHPVersion = models.DefaultPHPVersion
|
||||
return sp
|
||||
}
|
||||
|
||||
func (admin *Admin) instanceProvision(ctx context.Context) http.Handler {
|
||||
|
|
@ -37,6 +49,7 @@ func (admin *Admin) instanceProvision(ctx context.Context) http.Handler {
|
|||
)
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (ipc instanceProvisionContext, err error) {
|
||||
ipc.systemParams = newSystemParams()
|
||||
return ipc, nil
|
||||
})
|
||||
}
|
||||
|
|
|
|||
74
internal/dis/component/server/admin/instance_rebuild.go
Normal file
74
internal/dis/component/server/admin/instance_rebuild.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed "html/instance_rebuild.html"
|
||||
var instanceRebuildHTML []byte
|
||||
var instanceRebuildTemplate = templating.Parse[instanceRebuildContext](
|
||||
"instance_rebuild.html", instanceRebuildHTML, nil,
|
||||
|
||||
templating.Title("Rebuild Instance"),
|
||||
templating.Assets(assets.AssetsAdminRebuild),
|
||||
)
|
||||
|
||||
type instanceRebuildContext struct {
|
||||
templating.RuntimeFlags
|
||||
|
||||
Slug string
|
||||
System models.System
|
||||
|
||||
systemParams
|
||||
}
|
||||
|
||||
func (admin *Admin) instanceRebuild(ctx context.Context) http.Handler {
|
||||
tpl := instanceRebuildTemplate.Prepare(
|
||||
admin.Dependencies.Templating,
|
||||
templating.Crumbs(
|
||||
menuAdmin,
|
||||
menuInstances,
|
||||
menuInstance,
|
||||
menuRebuild,
|
||||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ib instanceRebuildContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
var instance *wisski.WissKI
|
||||
instance, err = admin.Dependencies.Instances.WissKI(r.Context(), slug)
|
||||
if err == instances.ErrWissKINotFound {
|
||||
return ib, nil, httpx.ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return ib, nil, err
|
||||
}
|
||||
|
||||
ib.Slug = instance.Slug
|
||||
ib.System = instance.System
|
||||
|
||||
// replace the menu item
|
||||
funcs = []templating.FlagFunc{
|
||||
templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + instance.Slug)}),
|
||||
templating.ReplaceCrumb(menuRebuild, component.MenuItem{Title: "Rebuild", Path: template.URL("/admin/rebuild/" + instance.Slug)}),
|
||||
templating.Title(instance.Slug + " - Rebuild"),
|
||||
}
|
||||
|
||||
ib.systemParams = newSystemParams()
|
||||
return
|
||||
})
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/exporter"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/provision"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||
)
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ func (sockets *Sockets) Actions() ActionMap {
|
|||
}),
|
||||
"provision": sockets.Generic(1, func(ctx context.Context, sockets *Sockets, in io.Reader, out io.Writer, params ...string) error {
|
||||
// read the flags of the instance to be provisioned
|
||||
var flags provision.ProvisionFlags
|
||||
var flags provision.Flags
|
||||
if err := json.Unmarshal([]byte(params[0]), &flags); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -63,8 +64,13 @@ func (sockets *Sockets) Actions() ActionMap {
|
|||
},
|
||||
)
|
||||
}),
|
||||
"rebuild": sockets.Instance(0, func(ctx context.Context, _ *Sockets, instance *wisski.WissKI, in io.Reader, out io.Writer, params ...string) error {
|
||||
return instance.Barrel().Build(ctx, out, true)
|
||||
"rebuild": sockets.Instance(1, func(ctx context.Context, _ *Sockets, instance *wisski.WissKI, in io.Reader, out io.Writer, params ...string) error {
|
||||
// read the flags of the instance to be provisioned
|
||||
var system models.System
|
||||
if err := json.Unmarshal([]byte(params[0]), &system); err != nil {
|
||||
return err
|
||||
}
|
||||
return instance.SystemManager().Apply(ctx, out, system, true)
|
||||
}),
|
||||
"update": sockets.Instance(0, func(ctx context.Context, _ *Sockets, instance *wisski.WissKI, in io.Reader, out io.Writer, params ...string) error {
|
||||
return instance.Drush().Update(ctx, out)
|
||||
|
|
|
|||
|
|
@ -21,4 +21,4 @@ type Assets struct {
|
|||
Styles template.HTML // <link> tags inserted by the asset
|
||||
}
|
||||
|
||||
//go:generate node build.mjs Default User Admin AdminProvision
|
||||
//go:generate node build.mjs Default User Admin AdminProvision AdminRebuild
|
||||
|
|
|
|||
|
|
@ -24,12 +24,18 @@ var AssetsUser = Assets{
|
|||
|
||||
// AssetsAdmin contains assets for the 'Admin' entrypoint.
|
||||
var AssetsAdmin = Assets{
|
||||
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/Admin.205f0180.js"></script><script src="/⛰/Admin.59fb2e50.js" nomodule="" defer></script>`,
|
||||
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/Admin.ad1b495b.js"></script><script src="/⛰/Admin.6daf9fdd.js" nomodule="" defer></script>`,
|
||||
Styles: `<link rel="stylesheet" href="/⛰/Default.938b4407.css"><link rel="stylesheet" href="/⛰/Admin.a1e05c23.css"><link rel="stylesheet" href="/⛰/User.840de3b4.css"><link rel="stylesheet" href="/⛰/User.68febbf8.css"><link rel="stylesheet" href="/⛰/Admin.d79c7b30.css">`,
|
||||
}
|
||||
|
||||
// AssetsAdminProvision contains assets for the 'AdminProvision' entrypoint.
|
||||
var AssetsAdminProvision = Assets{
|
||||
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script nomodule="" defer src="/⛰/Admin.59fb2e50.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Admin.205f0180.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/AdminProvision.3cf9e19e.js"></script><script src="/⛰/AdminProvision.d195fd59.js" nomodule="" defer></script>`,
|
||||
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script nomodule="" defer src="/⛰/Admin.6daf9fdd.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Admin.ad1b495b.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/AdminProvision.b7679968.js"></script><script src="/⛰/AdminProvision.12a47f22.js" nomodule="" defer></script>`,
|
||||
Styles: `<link rel="stylesheet" href="/⛰/Default.938b4407.css"><link rel="stylesheet" href="/⛰/Admin.a1e05c23.css"><link rel="stylesheet" href="/⛰/User.840de3b4.css"><link rel="stylesheet" href="/⛰/User.68febbf8.css"><link rel="stylesheet" href="/⛰/Admin.d79c7b30.css"><link rel="stylesheet" href="/⛰/AdminProvision.38d394c2.css">`,
|
||||
}
|
||||
|
||||
// AssetsAdminRebuild contains assets for the 'AdminRebuild' entrypoint.
|
||||
var AssetsAdminRebuild = Assets{
|
||||
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script nomodule="" defer src="/⛰/Admin.6daf9fdd.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Admin.ad1b495b.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/AdminRebuild.330247d9.js"></script><script src="/⛰/AdminRebuild.527a9616.js" nomodule="" defer></script>`,
|
||||
Styles: `<link rel="stylesheet" href="/⛰/Default.938b4407.css"><link rel="stylesheet" href="/⛰/Admin.a1e05c23.css"><link rel="stylesheet" href="/⛰/User.840de3b4.css"><link rel="stylesheet" href="/⛰/User.68febbf8.css"><link rel="stylesheet" href="/⛰/Admin.d79c7b30.css"><link rel="stylesheet" href="/⛰/AdminRebuild.38d394c2.css">`,
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
internal/dis/component/server/assets/dist/Admin.6daf9fdd.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/Admin.6daf9fdd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
internal/dis/component/server/assets/dist/Admin.ad1b495b.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/Admin.ad1b495b.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
internal/dis/component/server/assets/dist/AdminProvision.12a47f22.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminProvision.12a47f22.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
!function(){var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},o={},t=e.parcelRequireafa4;null==t&&((t=function(e){if(e in n)return n[e].exports;if(e in o){var t=o[e];delete o[e];var r={id:e,exports:{}};return n[e]=r,t.call(r.exports,r,r.exports),r.exports}var l=new Error("Cannot find module '"+e+"'");throw l.code="MODULE_NOT_FOUND",l}).register=function(e,n){o[e]=n},e.parcelRequireafa4=t),t("dK5Bi");var r,l=t("8vh0V");async function i(e){return new Promise(((n,o)=>{(0,l.createModal)("provision",[JSON.stringify(e)],{bufferSize:0,onClose:(t,r)=>{t?n(e.Slug):o(new Error(null!=r?r:"unspecified error"))}})}))}const d=document.getElementById("provision"),a=document.getElementById("slug"),u=document.getElementById("php"),c=document.getElementById("opcacheDevelopment");d.addEventListener("submit",(e=>{e.preventDefault(),i({Slug:a.value,System:{PHP:u.value,OpCacheDevelopment:c.checked}}).then((e=>{location.href="/admin/instance/"+e})).catch((e=>{console.error(e),location.reload()}))})),null===(r=d.querySelector("fieldset"))||void 0===r||r.removeAttribute("disabled")}();
|
||||
|
|
@ -1 +0,0 @@
|
|||
var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},o={},t=e.parcelRequireafa4;null==t&&((t=function(e){if(e in n)return n[e].exports;if(e in o){var t=o[e];delete o[e];var r={id:e,exports:{}};return n[e]=r,t.call(r.exports,r,r.exports),r.exports}var i=new Error("Cannot find module '"+e+"'");throw i.code="MODULE_NOT_FOUND",i}).register=function(e,n){o[e]=n},e.parcelRequireafa4=t),t("8xGhL");var r=t("12vpF");const i=document.getElementById("provision"),l=document.getElementById("slug");i.addEventListener("submit",(e=>{e.preventDefault();const n={Slug:l.value};(0,r.createModal)("provision",[JSON.stringify(n)],{bufferSize:0,onClose:e=>{e?location.href="/admin/instance/"+n.Slug:location.reload()}})})),i.querySelector("fieldset")?.removeAttribute("disabled");
|
||||
1
internal/dis/component/server/assets/dist/AdminProvision.b7679968.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminProvision.b7679968.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},t={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in n)return n[e].exports;if(e in t){var o=t[e];delete t[e];var r={id:e,exports:{}};return n[e]=r,o.call(r.exports,r,r.exports),r.exports}var i=new Error("Cannot find module '"+e+"'");throw i.code="MODULE_NOT_FOUND",i}).register=function(e,n){t[e]=n},e.parcelRequireafa4=o),o("8xGhL");var r=o("12vpF");async function i(e){return new Promise(((n,t)=>{(0,r.createModal)("provision",[JSON.stringify(e)],{bufferSize:0,onClose:(o,r)=>{o?n(e.Slug):t(new Error(r??"unspecified error"))}})}))}const l=document.getElementById("provision"),a=document.getElementById("slug"),d=document.getElementById("php"),u=document.getElementById("opcacheDevelopment");l.addEventListener("submit",(e=>{e.preventDefault(),i({Slug:a.value,System:{PHP:d.value,OpCacheDevelopment:u.checked}}).then((e=>{location.href="/admin/instance/"+e})).catch((e=>{console.error(e),location.reload()}))})),l.querySelector("fieldset")?.removeAttribute("disabled");
|
||||
|
|
@ -1 +0,0 @@
|
|||
!function(){var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},o={},t=e.parcelRequireafa4;null==t&&((t=function(e){if(e in n)return n[e].exports;if(e in o){var t=o[e];delete o[e];var i={id:e,exports:{}};return n[e]=i,t.call(i.exports,i,i.exports),i.exports}var r=new Error("Cannot find module '"+e+"'");throw r.code="MODULE_NOT_FOUND",r}).register=function(e,n){o[e]=n},e.parcelRequireafa4=t),t("dK5Bi");var i,r=t("8vh0V");const l=document.getElementById("provision"),d=document.getElementById("slug");l.addEventListener("submit",(e=>{e.preventDefault();const n={Slug:d.value};(0,r.createModal)("provision",[JSON.stringify(n)],{bufferSize:0,onClose:e=>{e?location.href="/admin/instance/"+n.Slug:location.reload()}})})),null===(i=l.querySelector("fieldset"))||void 0===i||i.removeAttribute("disabled")}();
|
||||
1
internal/dis/component/server/assets/dist/AdminRebuild.330247d9.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminRebuild.330247d9.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},t={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in n)return n[e].exports;if(e in t){var o=t[e];delete t[e];var r={id:e,exports:{}};return n[e]=r,o.call(r.exports,r,r.exports),r.exports}var i=new Error("Cannot find module '"+e+"'");throw i.code="MODULE_NOT_FOUND",i}).register=function(e,n){t[e]=n},e.parcelRequireafa4=o),o("8xGhL");var r=o("12vpF");async function i(e,n){return new Promise(((t,o)=>{(0,r.createModal)("rebuild",[e,JSON.stringify(n)],{bufferSize:0,onClose:(n,r)=>{n?t(e):o(new Error(r??"unspecified error"))}})}))}const l=document.getElementById("slug"),d=document.getElementById("provision"),a=document.getElementById("php"),u=document.getElementById("opcacheDevelopment");d.addEventListener("submit",(e=>{e.preventDefault(),i(l.value,{PHP:a.value,OpCacheDevelopment:u.checked}).then((e=>{location.href="/admin/instance/"+e})).catch((e=>{console.error(e),location.reload()}))})),d.querySelector("fieldset")?.removeAttribute("disabled");
|
||||
0
internal/dis/component/server/assets/dist/AdminRebuild.38d394c2.css
vendored
Normal file
0
internal/dis/component/server/assets/dist/AdminRebuild.38d394c2.css
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminRebuild.527a9616.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminRebuild.527a9616.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
!function(){var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},t={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in n)return n[e].exports;if(e in t){var o=t[e];delete t[e];var r={id:e,exports:{}};return n[e]=r,o.call(r.exports,r,r.exports),r.exports}var l=new Error("Cannot find module '"+e+"'");throw l.code="MODULE_NOT_FOUND",l}).register=function(e,n){t[e]=n},e.parcelRequireafa4=o),o("dK5Bi");var r,l=o("8vh0V");async function i(e,n){return new Promise(((t,o)=>{(0,l.createModal)("rebuild",[e,JSON.stringify(n)],{bufferSize:0,onClose:(n,r)=>{n?t(e):o(new Error(null!=r?r:"unspecified error"))}})}))}const d=document.getElementById("slug"),a=document.getElementById("provision"),u=document.getElementById("php"),c=document.getElementById("opcacheDevelopment");a.addEventListener("submit",(e=>{e.preventDefault(),i(d.value,{PHP:u.value,OpCacheDevelopment:c.checked}).then((e=>{location.href="/admin/instance/"+e})).catch((e=>{console.error(e),location.reload()}))})),null===(r=a.querySelector("fieldset"))||void 0===r||r.removeAttribute("disabled")}();
|
||||
|
|
@ -1,30 +1,22 @@
|
|||
import "../Admin/index.ts"
|
||||
import "../Admin/index.css"
|
||||
|
||||
import { createModal } from "~/src/lib/remote"
|
||||
import { Provision } from "~/src/lib/remote/api"
|
||||
|
||||
const provision = document.getElementById("provision") as HTMLFormElement;
|
||||
const slug = document.getElementById("slug") as HTMLInputElement;
|
||||
const php = document.getElementById("php") as HTMLSelectElement;
|
||||
const opcacheDevelopment = document.getElementById("opcacheDevelopment") as HTMLInputElement;
|
||||
|
||||
// add an event handler to open the modal form!
|
||||
provision.addEventListener('submit', (evt) => {
|
||||
evt.preventDefault();
|
||||
|
||||
// flags used to create the server
|
||||
const flags = { Slug: slug.value };
|
||||
|
||||
// open a modal to provision a new instance
|
||||
createModal("provision", [JSON.stringify(flags)], {
|
||||
bufferSize: 0,
|
||||
onClose: (success: boolean) => {
|
||||
if (success) {
|
||||
location.href = "/admin/instance/" + flags.Slug
|
||||
} else {
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
Provision({ Slug: slug.value, System: { PHP: php.value, OpCacheDevelopment: opcacheDevelopment.checked } })
|
||||
.then(slug => {
|
||||
location.href = "/admin/instance/" + slug;
|
||||
})
|
||||
.catch((e) => {console.error(e); location.reload()});
|
||||
})
|
||||
|
||||
// enable the form!
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import "../Admin/index.ts"
|
||||
import "../Admin/index.css"
|
||||
|
||||
import { Rebuild } from "~/src/lib/remote/api"
|
||||
|
||||
const slug = document.getElementById("slug") as HTMLInputElement
|
||||
const provision = document.getElementById("provision") as HTMLFormElement;
|
||||
const php = document.getElementById("php") as HTMLSelectElement;
|
||||
const opcacheDevelopment = document.getElementById("opcacheDevelopment") as HTMLInputElement;
|
||||
|
||||
// add an event handler to open the modal form!
|
||||
provision.addEventListener('submit', (evt) => {
|
||||
evt.preventDefault();
|
||||
|
||||
Rebuild(slug.value, { PHP: php.value, OpCacheDevelopment: opcacheDevelopment.checked })
|
||||
.then(slug => {
|
||||
location.href = "/admin/instance/" + slug;
|
||||
})
|
||||
.catch((e) => {console.error(e); location.reload()});
|
||||
})
|
||||
|
||||
// enable the form!
|
||||
provision.querySelector('fieldset')?.removeAttribute('disabled');
|
||||
|
||||
50
internal/dis/component/server/assets/src/lib/remote/api.ts
Normal file
50
internal/dis/component/server/assets/src/lib/remote/api.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { createModal } from "~/src/lib/remote"
|
||||
|
||||
/**
|
||||
* Flags to provision a new system.
|
||||
* Should mirror "provision".Flags.
|
||||
*/
|
||||
interface ProvisionFlags {
|
||||
Slug: string
|
||||
System: System
|
||||
}
|
||||
|
||||
interface System {
|
||||
PHP: string;
|
||||
OpCacheDevelopment: boolean
|
||||
}
|
||||
|
||||
/** Rebuild the specified instance */
|
||||
export async function Rebuild(slug: string, system: System): Promise<string> {
|
||||
return new Promise((rs, rj) => {
|
||||
createModal("rebuild", [slug, JSON.stringify(system)], {
|
||||
bufferSize: 0,
|
||||
onClose: (success: boolean, message?: string) => {
|
||||
if (!success) {
|
||||
rj(new Error(message ?? "unspecified error"))
|
||||
return;
|
||||
}
|
||||
|
||||
rs(slug);
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/** Provision provisions a new instance */
|
||||
export async function Provision(flags: ProvisionFlags): Promise<string> {
|
||||
// open a modal to provision a new instance
|
||||
return new Promise((rs, rj) => {
|
||||
createModal("provision", [JSON.stringify(flags)], {
|
||||
bufferSize: 0,
|
||||
onClose: (success: boolean, message?: string) => {
|
||||
if (!success) {
|
||||
rj(new Error(message ?? "unspecified error"))
|
||||
return;
|
||||
}
|
||||
|
||||
rs(flags.Slug);
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ export default function setup() {
|
|||
|
||||
type ModalOptions = {
|
||||
bufferSize: number;
|
||||
onClose: (success: boolean) => void
|
||||
onClose: (success: boolean, message?: string) => void
|
||||
}
|
||||
export function createModal(action: string, params: string[], opts: Partial<ModalOptions>) {
|
||||
// create a modal dialog and append it to the body
|
||||
|
|
@ -123,14 +123,14 @@ export function createModal(action: string, params: string[], opts: Partial<Moda
|
|||
finishButton.className = "pure-button pure-button-success"
|
||||
finishButton.append(typeof opts?.onClose === 'function' ? "Close & Finish" : "Close")
|
||||
|
||||
let result = {success: false, error: "unknown error"};
|
||||
let result: ResultMessage = {success: false};
|
||||
finishButton.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (typeof opts?.onClose === 'function') {
|
||||
finishButton.setAttribute('disabled', 'disabled')
|
||||
target.innerHTML = 'Finishing up ...'
|
||||
opts.onClose(result.success)
|
||||
opts.onClose(result.success, result.message)
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +147,9 @@ export function createModal(action: string, params: string[], opts: Partial<Moda
|
|||
window.onbeforeunload = () => "A remote session is in progress. Are you sure you want to leave?";
|
||||
|
||||
// when closing, add a button to the modal!
|
||||
const close = (result: ResultMessage) => {
|
||||
const close = (message: ResultMessage) => {
|
||||
result = message
|
||||
|
||||
if (result.success) {
|
||||
println('Process completed successfully. ', true);
|
||||
} else {
|
||||
|
|
@ -168,6 +170,7 @@ export function createModal(action: string, params: string[], opts: Partial<Moda
|
|||
|
||||
// connect to the socket and send the action
|
||||
callServerAction(
|
||||
location.href.replace('http', 'ws'),
|
||||
{
|
||||
'name': action,
|
||||
'params': params,
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@ function isResultMessage(value: any): value is ResultMessage {
|
|||
|
||||
/**
|
||||
* Opens a WebSocket connection and calls a server action
|
||||
* @param endpoint Endpoint to call
|
||||
* @param call Function to call
|
||||
* @param onOpen callback for once the connection is opened. The send function can be used to send additional text to the server.
|
||||
* @param onText called when the connection receives some text
|
||||
* @returns a promise that is resolved once the conneciton is closed. Rejected if the connection errors.
|
||||
*/
|
||||
export default async function callServerAction(
|
||||
endpoint: string,
|
||||
call: CallMessage,
|
||||
onOpen: (send: (text: string) => void, cancel: () => void) => void,
|
||||
onText: (text: string) => void,
|
||||
|
|
@ -24,7 +26,7 @@ export default async function callServerAction(
|
|||
return new Promise((rs, rj) => {
|
||||
const mutex = new Mutex();
|
||||
|
||||
const socket = new WebSocket(location.href.replace('http', 'ws'));
|
||||
const socket = new WebSocket(endpoint);
|
||||
|
||||
let result: ResultMessage;
|
||||
socket.onmessage = (msg) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue