frontend: Make Control Server nicer
This commit is contained in:
parent
c258b46443
commit
36891d7e7c
16 changed files with 231 additions and 170 deletions
18
cmd/cron.go
18
cmd/cron.go
|
|
@ -6,7 +6,6 @@ import (
|
||||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
|
||||||
"github.com/tkw1536/goprogram/status"
|
"github.com/tkw1536/goprogram/status"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
)
|
)
|
||||||
|
|
@ -32,11 +31,6 @@ func (cron) Description() wisski_distillery.Description {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var errCronFailed = exit.Error{
|
|
||||||
Message: "Failed to run cron script for instance %q: exited with code %s",
|
|
||||||
ExitCode: exit.ExitGeneric,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cr cron) Run(context wisski_distillery.Context) error {
|
func (cr cron) Run(context wisski_distillery.Context) error {
|
||||||
// find all the instances!
|
// find all the instances!
|
||||||
wissKIs, err := context.Environment.Instances().Load(cr.Positionals.Slug...)
|
wissKIs, err := context.Environment.Instances().Load(cr.Positionals.Slug...)
|
||||||
|
|
@ -46,17 +40,7 @@ func (cr cron) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// and do the actual blind_update!
|
// and do the actual blind_update!
|
||||||
return status.StreamGroup(context.IOStream, cr.Parallel, func(instance instances.WissKI, io stream.IOStream) error {
|
return status.StreamGroup(context.IOStream, cr.Parallel, func(instance instances.WissKI, io stream.IOStream) error {
|
||||||
code, err := instance.Shell(io, "/runtime/cron.sh")
|
return instance.Cron(io)
|
||||||
if err != nil {
|
|
||||||
io.EPrintln(err)
|
|
||||||
}
|
|
||||||
if code != 0 {
|
|
||||||
// keep going, because we want to run as many crons as possible
|
|
||||||
err = errBlindUpdateFailed.WithMessageF(instance.Slug, code)
|
|
||||||
io.EPrintln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, wissKIs, status.SmartMessage(func(item instances.WissKI) string {
|
}, wissKIs, status.SmartMessage(func(item instances.WissKI) string {
|
||||||
return fmt.Sprintf("cron %q", item.Slug)
|
return fmt.Sprintf("cron %q", item.Slug)
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ func (i info) Run(context wisski_distillery.Context) error {
|
||||||
context.Printf("Locked: %v\n", info.Locked)
|
context.Printf("Locked: %v\n", info.Locked)
|
||||||
context.Printf("Last Rebuild: %v\n", info.LastRebuild.String())
|
context.Printf("Last Rebuild: %v\n", info.LastRebuild.String())
|
||||||
context.Printf("Last Update: %v\n", info.LastUpdate.String())
|
context.Printf("Last Update: %v\n", info.LastUpdate.String())
|
||||||
|
context.Printf("Last Cron: %v\n", info.LastCron.String())
|
||||||
|
|
||||||
context.Printf("Skip Prefixes: %v\n", info.NoPrefixes)
|
context.Printf("Skip Prefixes: %v\n", info.NoPrefixes)
|
||||||
context.Printf("Prefixes: (count %d)\n", len(info.Prefixes))
|
context.Printf("Prefixes: (count %d)\n", len(info.Prefixes))
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@
|
||||||
<main>
|
<main>
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1-1">
|
<div class="pure-u-1-1">
|
||||||
<h2 id="overview">Instance Overview</h2>
|
<h2 id="overview">Info & Status</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-xl-1-3">
|
<div class="pure-u-1 pure-u-xl-2-5">
|
||||||
<div class="padding">
|
<div class="padding">
|
||||||
<div class="overflow">
|
<div class="overflow">
|
||||||
<table class="pure-table pure-table-bordered">
|
<table class="pure-table pure-table-bordered">
|
||||||
|
|
@ -70,14 +70,72 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-xl-1-3">
|
<div class="pure-u-1 pure-u-xl-2-5">
|
||||||
<div class="padding">
|
<div class="padding">
|
||||||
<div class="overflow">
|
<div class="overflow">
|
||||||
<table class="pure-table pure-table-bordered">
|
<table class="pure-table pure-table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2">
|
<th colspan="2">
|
||||||
Build
|
Component Settings
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Directory
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<code style="overflow: auto;">{{ .Instance.FilesystemBase }}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
SQL DB
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<code>{{ .Instance.SqlDatabase }}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
SQL User
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<code>{{ .Instance.SqlUsername }}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
TS Repo
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<code>{{ .Instance.GraphDBRepository }}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
TS User
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<code>{{ .Instance.GraphDBUsername }}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</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">
|
||||||
|
Build Status
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -85,7 +143,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
Created
|
Created
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code class="date">{{ .Instance.Created.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
<code class="date">{{ .Instance.Created.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -101,10 +159,11 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
Automatic Updates
|
Last Cron<br>
|
||||||
|
<button class="remote-action pure-button pure-button-action" data-action="cron" data-param="{{ .Instance.Slug }}" data-buffer="1000" data-force-reload="true">Cron</button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code>{{ .Instance.AutoBlindUpdateEnabled }}</code>
|
<code class="date">{{ .Info.LastRebuild.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -113,7 +172,8 @@
|
||||||
<button class="remote-action pure-button pure-button-action" data-action="update" data-param="{{ .Instance.Slug }}" data-buffer="1000" data-force-reload="true">Update</button>
|
<button class="remote-action pure-button pure-button-action" data-action="update" data-param="{{ .Instance.Slug }}" data-buffer="1000" data-force-reload="true">Update</button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code class="date">{{ .Info.LastUpdate.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
<code class="date">{{ .Info.LastUpdate.Format "2006-01-02T15:04:05Z07:00" }}</code><br>
|
||||||
|
(Automatic: <code>{{ .Instance.AutoBlindUpdateEnabled }}</code>)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
@ -122,138 +182,89 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-u-1 pure-u-xl-1-3">
|
<div class="pure-u-1 pure-u-xl-2-5">
|
||||||
|
<!--
|
||||||
<div class="padding">
|
<div class="padding">
|
||||||
<div class="overflow">
|
<div class="overflow">
|
||||||
<table class="pure-table pure-table-bordered">
|
<table class="pure-table pure-table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2">
|
<th colspan="2">
|
||||||
Resolver
|
Composer Status
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
Excluded
|
???
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code>{{ .Info.NoPrefixes }}</code>
|
???
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
URI Prefixes
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ range .Info.Prefixes }}
|
|
||||||
<code>{{ . }}</code><br />
|
|
||||||
{{ end}}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-xl-1-3">
|
|
||||||
<div class="padding">
|
|
||||||
<div class="overflow">
|
|
||||||
<table class="pure-table pure-table-bordered">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th colspan="2">
|
|
||||||
Database Settings
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
SQL Database
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<code>{{ .Instance.SqlDatabase }}</code>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
SQL Username
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<code>{{ .Instance.SqlUsername }}</code>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
GraphDB Repository
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<code>{{ .Instance.GraphDBRepository }}</code>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
GraphDB Username
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<code>{{ .Instance.GraphDBUsername }}</code>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="pure-u-1 pure-u-xl-1-3">
|
|
||||||
<div class="padding">
|
|
||||||
<div class="overflow">
|
|
||||||
<table class="pure-table pure-table-bordered">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th colspan="2">
|
|
||||||
Whitebox Data
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
<div class="pure-u-1-1">
|
||||||
|
<h2 id="wisski">WissKI Data</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pure-u-1 pure-u-xl-1-2">
|
||||||
|
<div class="padding">
|
||||||
|
<div class="overflow">
|
||||||
|
<table class="pure-table pure-table-bordered">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<th colspan="2">
|
||||||
Pathbuilders
|
Pathbuilders
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<code class="pathbuilders">{{ .Info.Pathbuilders }}</code>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="pure-u-1 pure-u-xl-1-3">
|
|
||||||
<div class="padding">
|
|
||||||
<div class="overflow">
|
|
||||||
<table class="pure-table pure-table-bordered">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th colspan="2">
|
|
||||||
Misc Settings
|
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{{ range $name, $xml := .Info.Pathbuilders }}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
Filesystem Base
|
<code>{{ $name }}</code>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code>{{ .Instance.FilesystemBase }}</code>
|
<code class="pathbuilder" data-name="{{ $name }}">{{ $xml }}</code>
|
||||||
<script>window.pathbuilders = {{ .Info.Pathbuilders }};</script>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-1 pure-u-xl-1-2">
|
||||||
|
<div class="padding">
|
||||||
|
<div class="overflow">
|
||||||
|
<table class="pure-table pure-table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
URI Prefixes
|
||||||
|
|
||||||
|
{{ if .Info.NoPrefixes }}
|
||||||
|
(excluded from resolver)
|
||||||
|
{{ end }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range $index, $prefix := .Info.Prefixes }}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>{{ $prefix }}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ var socketInstanceActions = map[string]instanceActionFunc{
|
||||||
"update": func(_ *Info, instance instances.WissKI, str stream.IOStream) error {
|
"update": func(_ *Info, instance instances.WissKI, str stream.IOStream) error {
|
||||||
return instance.BlindUpdate(str)
|
return instance.BlindUpdate(str)
|
||||||
},
|
},
|
||||||
|
"cron": func(_ *Info, instance instances.WissKI, str stream.IOStream) error {
|
||||||
|
return instance.Cron(str)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (info *Info) serveSocket(conn httpx.WebSocketConnection) {
|
func (info *Info) serveSocket(conn httpx.WebSocketConnection) {
|
||||||
|
|
|
||||||
36
internal/component/instances/wisski_cron.go
Normal file
36
internal/component/instances/wisski_cron.go
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
package instances
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tkw1536/goprogram/exit"
|
||||||
|
"github.com/tkw1536/goprogram/stream"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errCronFailed = exit.Error{
|
||||||
|
Message: "Failed to run cron script for instance %q: exited with code %s",
|
||||||
|
ExitCode: exit.ExitGeneric,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) Cron(io stream.IOStream) error {
|
||||||
|
code, err := wisski.Shell(io, "/runtime/cron.sh")
|
||||||
|
if err != nil {
|
||||||
|
io.EPrintln(err)
|
||||||
|
}
|
||||||
|
if code != 0 {
|
||||||
|
// keep going, because we want to run as many crons as possible
|
||||||
|
err = errBlindUpdateFailed.WithMessageF(wisski.Slug, code)
|
||||||
|
io.EPrintln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) LastCron(server *PHPServer) (t time.Time, err error) {
|
||||||
|
var timestamp int64
|
||||||
|
err = wisski.EvalPHPCode(server, ×tamp, `$val = \Drupal::state()->get('system.cron_last'); return $val; `)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return time.Unix(timestamp, 0), nil
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package instances
|
package instances
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
|
@ -22,6 +23,7 @@ type WissKIInfo struct {
|
||||||
Running bool
|
Running bool
|
||||||
LastRebuild time.Time
|
LastRebuild time.Time
|
||||||
LastUpdate time.Time
|
LastUpdate time.Time
|
||||||
|
LastCron time.Time
|
||||||
|
|
||||||
// List of backups made
|
// List of backups made
|
||||||
Snapshots []models.Export
|
Snapshots []models.Export
|
||||||
|
|
@ -40,9 +42,9 @@ func (wisski *WissKI) Info(quick bool) (info WissKIInfo, err error) {
|
||||||
if !quick {
|
if !quick {
|
||||||
server, err := wisski.NewPHPServer()
|
server, err := wisski.NewPHPServer()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
wisski.infoSlow(&info, server, &group)
|
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
}
|
}
|
||||||
|
wisski.infoSlow(&info, server, &group)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = group.Wait()
|
err = group.Wait()
|
||||||
|
|
@ -87,19 +89,28 @@ func (wisski *WissKI) infoQuick(info *WissKIInfo, group *errgroup.Group) {
|
||||||
|
|
||||||
func (wisski *WissKI) infoSlow(info *WissKIInfo, server *PHPServer, group *errgroup.Group) {
|
func (wisski *WissKI) infoSlow(info *WissKIInfo, server *PHPServer, group *errgroup.Group) {
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
info.Prefixes, _ = wisski.Prefixes(server)
|
info.Prefixes, err = wisski.Prefixes(server)
|
||||||
|
log.Println("error prefixes: ", err)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
group.Go(func() error {
|
group.Go(func() (err error) {
|
||||||
info.Snapshots, _ = wisski.Snapshots()
|
info.Snapshots, err = wisski.Snapshots()
|
||||||
|
log.Println("error snapshots: ", err)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
group.Go(func() error {
|
group.Go(func() (err error) {
|
||||||
info.Pathbuilders, _ = wisski.AllPathbuilders(server)
|
info.Pathbuilders, err = wisski.AllPathbuilders(server)
|
||||||
|
log.Println("error pathbuilders: ", err)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
group.Go(func() (err error) {
|
||||||
|
info.LastCron, err = wisski.LastCron(server)
|
||||||
|
log.Println("error cron: ", err)
|
||||||
|
return
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Running checks if this WissKI is currently running.
|
// Running checks if this WissKI is currently running.
|
||||||
|
|
|
||||||
|
|
@ -174,9 +174,13 @@ func (server *PHPServer) MarshalCall(value any, function string, args ...any) er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PHPServerError{Message: errPHPMarshal, Err: err}
|
return PHPServerError{Message: errPHPMarshal, Err: err}
|
||||||
}
|
}
|
||||||
userFunctionArgs, err := marshalPHP(args)
|
|
||||||
if err != nil {
|
userFunctionArgs := "[]"
|
||||||
return PHPServerError{Message: errPHPMarshal, Err: err}
|
if len(args) > 0 {
|
||||||
|
userFunctionArgs, err = marshalPHP(args)
|
||||||
|
if err != nil {
|
||||||
|
return PHPServerError{Message: errPHPMarshal, Err: err}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
code := "return call_user_func_array(" + userFunction + "," + userFunctionArgs + ");"
|
code := "return call_user_func_array(" + userFunction + "," + userFunctionArgs + ");"
|
||||||
|
|
||||||
|
|
@ -246,6 +250,7 @@ func (server *PHPServer) Close() error {
|
||||||
|
|
||||||
// ExecPHPScript executes the PHP code as a script on the given server.
|
// ExecPHPScript executes the PHP code as a script on the given server.
|
||||||
// When server is nil, creates a new server and automatically closes it after execution.
|
// When server is nil, creates a new server and automatically closes it after execution.
|
||||||
|
// Calling this function repeatedly with server = nil is inefficient.
|
||||||
//
|
//
|
||||||
// The script should define a function called entrypoint, and may define additional functions.
|
// The script should define a function called entrypoint, and may define additional functions.
|
||||||
//
|
//
|
||||||
|
|
@ -257,8 +262,6 @@ func (server *PHPServer) Close() error {
|
||||||
// It's arguments are encoded as json using [json.Marshal] and decoded within php.
|
// It's arguments are encoded as json using [json.Marshal] and decoded within php.
|
||||||
//
|
//
|
||||||
// The return value of the function is again marshaled with json and returned to the caller.
|
// The return value of the function is again marshaled with json and returned to the caller.
|
||||||
//
|
|
||||||
// Calling this function is inefficient, and a [NewPHPServer] call should be prefered instead.
|
|
||||||
func (wisski *WissKI) ExecPHPScript(server *PHPServer, value any, code string, entrypoint string, args ...any) (err error) {
|
func (wisski *WissKI) ExecPHPScript(server *PHPServer, value any, code string, entrypoint string, args ...any) (err error) {
|
||||||
if server == nil {
|
if server == nil {
|
||||||
server, err = wisski.NewPHPServer()
|
server, err = wisski.NewPHPServer()
|
||||||
|
|
@ -268,13 +271,27 @@ func (wisski *WissKI) ExecPHPScript(server *PHPServer, value any, code string, e
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := server.MarshalEval(nil, strings.TrimPrefix(code, "<?php")); err != nil {
|
if code != "" {
|
||||||
return err
|
if err := server.MarshalEval(nil, strings.TrimPrefix(code, "<?php")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return server.MarshalCall(value, entrypoint, args...)
|
return server.MarshalCall(value, entrypoint, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) EvalPHPCode(server *PHPServer, value any, code string) (err error) {
|
||||||
|
if server == nil {
|
||||||
|
server, err = wisski.NewPHPServer()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.MarshalEval(value, code)
|
||||||
|
}
|
||||||
|
|
||||||
//go:embed php/server.php
|
//go:embed php/server.php
|
||||||
var serverPHP string
|
var serverPHP string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package static
|
package static
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"html/template"
|
"html/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -30,11 +31,17 @@ func (assets *Assets) MustParse(value string) *template.Template {
|
||||||
return template.Must(assets.RegisterFuncs(template.New("")).Parse(value))
|
return template.Must(assets.RegisterFuncs(template.New("")).Parse(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterFuncs registers two new template functions called "JS" and "CSS".
|
// RegisterFuncs registers three new template functions called "JS", "CSS" and "json".
|
||||||
// Both take no arguments, and return a html-safe version of the Scripts and Style tags to be included.
|
//
|
||||||
|
// "JS" and "CSS" take no arguments, and return appropriate tags to be inserted into html.
|
||||||
|
// json takes a single argument of any type, and returns it's encoding as a string to be inserted into the page.
|
||||||
func (assets *Assets) RegisterFuncs(t *template.Template) *template.Template {
|
func (assets *Assets) RegisterFuncs(t *template.Template) *template.Template {
|
||||||
return t.Funcs(template.FuncMap{
|
return t.Funcs(template.FuncMap{
|
||||||
"JS": func() template.HTML { return template.HTML(assets.Scripts) },
|
"JS": func() template.HTML { return template.HTML(assets.Scripts) },
|
||||||
"CSS": func() template.HTML { return template.HTML(assets.Styles) },
|
"CSS": func() template.HTML { return template.HTML(assets.Styles) },
|
||||||
|
"json": func(data any) (string, error) {
|
||||||
|
bytes, err := json.Marshal(data)
|
||||||
|
return string(bytes), err
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,17 @@ package static
|
||||||
// AssetsHomeHome contains assets for the 'HomeHome' entrypoint.
|
// AssetsHomeHome contains assets for the 'HomeHome' entrypoint.
|
||||||
var AssetsHomeHome = Assets{
|
var AssetsHomeHome = Assets{
|
||||||
Scripts: `<script type="module" src="/static/HomeHome.38d394c2.js"></script><script src="/static/HomeHome.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/HomeHome.38d394c2.js"></script><script src="/static/HomeHome.38d394c2.js" nomodule="" defer></script>`,
|
Scripts: `<script type="module" src="/static/HomeHome.38d394c2.js"></script><script src="/static/HomeHome.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/HomeHome.38d394c2.js"></script><script src="/static/HomeHome.38d394c2.js" nomodule="" defer></script>`,
|
||||||
Styles: `<link rel="stylesheet" href="/static/HomeHome.518b2dbe.css"><link rel="stylesheet" href="/static/HomeHome.38d394c2.css">`,
|
Styles: `<link rel="stylesheet" href="/static/HomeHome.a75f04fa.css"><link rel="stylesheet" href="/static/HomeHome.38d394c2.css">`,
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetsControlIndex contains assets for the 'ControlIndex' entrypoint.
|
// AssetsControlIndex contains assets for the 'ControlIndex' entrypoint.
|
||||||
var AssetsControlIndex = Assets{
|
var AssetsControlIndex = Assets{
|
||||||
Scripts: `<script type="module" src="/static/HomeHome.38d394c2.js"></script><script src="/static/HomeHome.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/ControlIndex.43f953d2.js"></script><script src="/static/ControlIndex.c70a89e1.js" nomodule="" defer></script>`,
|
Scripts: `<script type="module" src="/static/HomeHome.38d394c2.js"></script><script src="/static/HomeHome.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/ControlIndex.cfbf936d.js"></script><script src="/static/ControlIndex.613b02c2.js" nomodule="" defer></script>`,
|
||||||
Styles: `<link rel="stylesheet" href="/static/HomeHome.518b2dbe.css"><link rel="stylesheet" href="/static/ControlIndex.6d59e220.css"><link rel="stylesheet" href="/static/ControlIndex.6d2ae968.css">`,
|
Styles: `<link rel="stylesheet" href="/static/HomeHome.a75f04fa.css"><link rel="stylesheet" href="/static/ControlIndex.6d59e220.css"><link rel="stylesheet" href="/static/ControlIndex.6d2ae968.css">`,
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetsControlInstance contains assets for the 'ControlInstance' entrypoint.
|
// AssetsControlInstance contains assets for the 'ControlInstance' entrypoint.
|
||||||
var AssetsControlInstance = Assets{
|
var AssetsControlInstance = Assets{
|
||||||
Scripts: `<script nomodule="" defer src="/static/ControlIndex.c70a89e1.js"></script><script type="module" src="/static/ControlIndex.43f953d2.js"></script><script type="module" src="/static/HomeHome.38d394c2.js"></script><script src="/static/HomeHome.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/ControlInstance.66b95713.js"></script><script src="/static/ControlInstance.9cc7166d.js" nomodule="" defer></script>`,
|
Scripts: `<script nomodule="" defer src="/static/ControlIndex.613b02c2.js"></script><script type="module" src="/static/ControlIndex.cfbf936d.js"></script><script type="module" src="/static/HomeHome.38d394c2.js"></script><script src="/static/HomeHome.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/ControlInstance.66b95713.js"></script><script src="/static/ControlInstance.9cc7166d.js" nomodule="" defer></script>`,
|
||||||
Styles: `<link rel="stylesheet" href="/static/HomeHome.518b2dbe.css"><link rel="stylesheet" href="/static/ControlIndex.6d59e220.css"><link rel="stylesheet" href="/static/ControlIndex.6d2ae968.css"><link rel="stylesheet" href="/static/ControlInstance.38d394c2.css">`,
|
Styles: `<link rel="stylesheet" href="/static/HomeHome.a75f04fa.css"><link rel="stylesheet" href="/static/ControlIndex.6d59e220.css"><link rel="stylesheet" href="/static/ControlIndex.6d2ae968.css"><link rel="stylesheet" href="/static/ControlInstance.38d394c2.css">`,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
1
internal/component/static/dist/ControlIndex.613b02c2.js
vendored
Normal file
1
internal/component/static/dist/ControlIndex.613b02c2.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
internal/component/static/dist/ControlIndex.cfbf936d.js
vendored
Normal file
1
internal/component/static/dist/ControlIndex.cfbf936d.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -20,6 +20,11 @@ footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overflow table td,
|
||||||
|
.overflow table th{
|
||||||
|
padding: .5em .5em;
|
||||||
|
}
|
||||||
|
|
||||||
.overflow table td:not(:last-child),
|
.overflow table td:not(:last-child),
|
||||||
.overflow table th:not(:last-child) {
|
.overflow table th:not(:last-child) {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
|
|
@ -34,7 +39,7 @@ footer {
|
||||||
|
|
||||||
.hspace {
|
.hspace {
|
||||||
display: block;
|
display: block;
|
||||||
height: 2em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pure-button-action {
|
.pure-button-action {
|
||||||
|
|
|
||||||
|
|
@ -7,32 +7,19 @@ const types: Record<string, (element: HTMLElement) => HTMLElement | string> = {
|
||||||
const text = element.innerText.split("/");
|
const text = element.innerText.split("/");
|
||||||
return text[text.length - 1];
|
return text[text.length - 1];
|
||||||
},
|
},
|
||||||
"pathbuilders": () => {
|
"pathbuilder": (element) => {
|
||||||
const pathbuilders: {[name: string]: string} = (window as any).pathbuilders; // must be declared globally on page!
|
// create a link and get the blob
|
||||||
const wrapper = document.createElement("span");
|
const filename = (element.getAttribute('data-name') ?? 'pathbuilder') + ".xml"
|
||||||
|
const [link, blob] = make_download_link(filename, element.innerText, "application/xml")
|
||||||
|
|
||||||
let found_one = false
|
link.className = "pure-button"
|
||||||
Object.keys(pathbuilders).forEach(name => {
|
const title = filename + ' (' + blob.size + ' Bytes)';
|
||||||
found_one = true
|
link.append(title)
|
||||||
|
return link
|
||||||
const filename = name + ".xml"
|
|
||||||
const data = pathbuilders[name]
|
|
||||||
const mime = "application/xml"
|
|
||||||
wrapper.append(make_download_link(filename, name, data, mime))
|
|
||||||
wrapper.append(document.createTextNode(" "))
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!found_one) return '(none)';
|
|
||||||
|
|
||||||
const small = document.createElement('small')
|
|
||||||
small.append(document.createTextNode("(click to download)"))
|
|
||||||
wrapper.append(small)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const make_download_link = (filename: string, title: string, content: string, type: string) => {
|
const make_download_link = (filename: string, content: string, type: string): [HTMLAnchorElement, Blob] => {
|
||||||
const blob = new Blob(
|
const blob = new Blob(
|
||||||
[content],
|
[content],
|
||||||
{
|
{
|
||||||
|
|
@ -44,9 +31,8 @@ const make_download_link = (filename: string, title: string, content: string, ty
|
||||||
link.target = "_blank"
|
link.target = "_blank"
|
||||||
link.download = filename
|
link.download = filename
|
||||||
link.href = URL.createObjectURL(blob)
|
link.href = URL.createObjectURL(blob)
|
||||||
link.append(document.createTextNode(title))
|
|
||||||
|
|
||||||
return link
|
return [link, blob]
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(types).forEach(key => {
|
Object.keys(types).forEach(key => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue