initial commit
This commit is contained in:
commit
af40e0890f
97
App.go
Normal file
97
App.go
Normal file
@ -0,0 +1,97 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
cfg AppConfig
|
||||
|
||||
rxTaskInfo *regexp.Regexp
|
||||
|
||||
tasksMtx sync.RWMutex
|
||||
tasks map[string]Task
|
||||
}
|
||||
|
||||
func NewApp(configPath string) (*App, error) {
|
||||
|
||||
confBytes, err := ioutil.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't open configuration file '%s': %s", configPath, err.Error())
|
||||
}
|
||||
|
||||
cfg := AppConfig{}
|
||||
err = json.Unmarshal(confBytes, &cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid configuration file: %s", err.Error())
|
||||
}
|
||||
|
||||
return NewAppFromConfig(cfg), nil
|
||||
}
|
||||
|
||||
func NewAppFromConfig(cfg AppConfig) *App {
|
||||
return &App{
|
||||
cfg: cfg,
|
||||
rxTaskInfo: regexp.MustCompile(`^/task/([A-Z0-9]+)$`),
|
||||
tasks: make(map[string]Task),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Server", "webcmd/1.0")
|
||||
|
||||
if r.Method == "GET" {
|
||||
if r.URL.Path == "/" {
|
||||
this.Serve_Homepage(w)
|
||||
|
||||
} else if r.URL.Path == "/style.css" {
|
||||
this.Serve_StyleCSS(w)
|
||||
|
||||
} else if r.URL.Path == "/tasks" {
|
||||
this.Serve_Tasks(w)
|
||||
|
||||
} else if matches := this.rxTaskInfo.FindStringSubmatch(r.URL.Path); len(matches) == 2 {
|
||||
this.Serve_TaskInfo(w, matches[1])
|
||||
|
||||
} else {
|
||||
http.Error(w, "No matching route for request", 404)
|
||||
|
||||
}
|
||||
|
||||
} else if r.Method == "POST" {
|
||||
if r.URL.Path == "/x-new-task" {
|
||||
this.Action_NewTask(w, r)
|
||||
|
||||
} else if r.URL.Path == "/x-abandon-task" {
|
||||
this.Action_AbandonTask(w, r)
|
||||
|
||||
} else if r.URL.Path == "/x-clear-completed-tasks" {
|
||||
this.Action_ClearCompleted(w, r)
|
||||
|
||||
} else {
|
||||
http.Error(w, "No matching route for request", 404)
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
http.Error(w, "Invalid method", 400)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (this *App) Run() {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", this)
|
||||
|
||||
log.Printf("Listening on '%s'...", this.cfg.ListenAddress)
|
||||
err := http.ListenAndServe(this.cfg.ListenAddress, mux)
|
||||
if err != nil {
|
||||
log.Fatalf("Network error: %s", err.Error())
|
||||
}
|
||||
}
|
24
Makefile
Normal file
24
Makefile
Normal file
@ -0,0 +1,24 @@
|
||||
.PHONY: all clean
|
||||
|
||||
OBJS := $(addprefix webcmd.,linux64 linux32 win64 win32)
|
||||
|
||||
all: $(OBJS)
|
||||
|
||||
define compile
|
||||
/bin/bash -c 'GOARCH=$(1) GOOS=$(2) go build -o ./webcmd.$(3) -ldflags "-s -w" ./cmd/webcmd'
|
||||
endef
|
||||
|
||||
webcmd.linux64:
|
||||
$(call compile,amd64,linux,linux64)
|
||||
|
||||
webcmd.linux32:
|
||||
$(call compile,386,linux,linux32)
|
||||
|
||||
webcmd.win64:
|
||||
$(call compile,amd64,windows,win64)
|
||||
|
||||
webcmd.win32:
|
||||
$(call compile,386,windows,win32)
|
||||
|
||||
clean:
|
||||
rm $(OBJS)
|
121
Task.go
Normal file
121
Task.go
Normal file
@ -0,0 +1,121 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base32"
|
||||
"errors"
|
||||
"io"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OutputLine struct {
|
||||
isError bool
|
||||
text string
|
||||
}
|
||||
|
||||
type Task struct {
|
||||
cmd *exec.Cmd
|
||||
output []OutputLine
|
||||
started int64
|
||||
stopped int64
|
||||
exitCode int
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (t Task) Finished() bool {
|
||||
return t.stopped != 0
|
||||
}
|
||||
|
||||
func uuid() string {
|
||||
buff := make([]byte, 15) // multiples of five are best for base32
|
||||
_, err := rand.Read(buff)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return base32.StdEncoding.EncodeToString(buff)
|
||||
}
|
||||
|
||||
// LaunchTask creates a new task from the given command parameters.
|
||||
func (this *App) LaunchTask(params []string) (taskRef string, err error) {
|
||||
if len(params) == 0 {
|
||||
return "", errors.New("No parameters for task")
|
||||
}
|
||||
|
||||
ref := uuid()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cmd := exec.CommandContext(ctx, params[0], params[1:]...)
|
||||
|
||||
errPipe, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
outPipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
this.tasksMtx.Lock()
|
||||
this.tasks[ref] = Task{
|
||||
cmd: cmd,
|
||||
output: make([]OutputLine, 0),
|
||||
started: time.Now().Unix(),
|
||||
stopped: 0,
|
||||
exitCode: 0,
|
||||
cancel: cancel,
|
||||
}
|
||||
this.tasksMtx.Unlock()
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
writeline := func(text string, isError bool) {
|
||||
this.tasksMtx.Lock()
|
||||
defer this.tasksMtx.Unlock()
|
||||
|
||||
task := this.tasks[ref]
|
||||
task.output = append(task.output, OutputLine{isError: isError, text: text})
|
||||
this.tasks[ref] = task
|
||||
}
|
||||
|
||||
pipe2line := func(rc io.ReadCloser, isError bool) {
|
||||
defer wg.Done()
|
||||
sc := bufio.NewScanner(rc)
|
||||
for sc.Scan() {
|
||||
writeline(sc.Text(), isError)
|
||||
}
|
||||
rc.Close()
|
||||
}
|
||||
go pipe2line(errPipe, true)
|
||||
go pipe2line(outPipe, false)
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
err := cmd.Wait()
|
||||
stopTime := time.Now().Unix()
|
||||
exitCode := 0
|
||||
if err != nil {
|
||||
writeline(err.Error(), true)
|
||||
exitCode = 1
|
||||
}
|
||||
|
||||
this.tasksMtx.Lock()
|
||||
defer this.tasksMtx.Unlock()
|
||||
task := this.tasks[ref]
|
||||
task.stopped = stopTime
|
||||
task.exitCode = exitCode
|
||||
this.tasks[ref] = task
|
||||
}()
|
||||
|
||||
return ref, nil
|
||||
}
|
20
cmd/webcmd/main.go
Normal file
20
cmd/webcmd/main.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"code.ivysaur.me/webcmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
confPath := flag.String("config", "webcmd.conf", "Path to configuration file")
|
||||
flag.Parse()
|
||||
|
||||
app, err := webcmd.NewApp(*confPath)
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
app.Run()
|
||||
}
|
20
cmd/webcmd/webcmd.conf
Normal file
20
cmd/webcmd/webcmd.conf
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"AppTitle": "Looking Glass",
|
||||
|
||||
"ListenAddress": ":8192",
|
||||
|
||||
"Commands": [
|
||||
|
||||
{
|
||||
"Title": "Ping",
|
||||
"Execution": [
|
||||
{"ParamType": 0, "Value": "/bin/ping"},
|
||||
{"ParamType": 0, "Value": "-c"},
|
||||
{"ParamType": 0, "Value": "4"},
|
||||
{"ParamType": 0, "Value": "--"},
|
||||
{"ParamType": 1, "Value": "example.com", "Description": "Target host"}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
33
config.go
Normal file
33
config.go
Normal file
@ -0,0 +1,33 @@
|
||||
package webcmd
|
||||
|
||||
type ParamType int
|
||||
|
||||
const (
|
||||
PARAMTYPE_CONST ParamType = 0
|
||||
PARAMTYPE_STRING ParamType = 1
|
||||
PARAMTYPE_OPTIONAL ParamType = 2
|
||||
// bool 1/0
|
||||
// list
|
||||
// k/v list
|
||||
// file upload (temporary path passed to binary)
|
||||
// nested parse subgroup (e.g. ffmpeg filters)
|
||||
// one optional to control a whole subgroup (e.g. --timeout 4)
|
||||
// String validations (regexp, min-length, ...)
|
||||
)
|
||||
|
||||
type InputParam struct {
|
||||
Description string `json:",omitempty"` // only use for editable parameters
|
||||
ParamType ParamType
|
||||
Value string
|
||||
}
|
||||
|
||||
type CommandConfig struct {
|
||||
Title string
|
||||
Execution []InputParam // TODO allow plain strings as a shorthand for PARAMTYPE_CONST
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
ListenAddress string
|
||||
AppTitle string
|
||||
Commands []CommandConfig
|
||||
}
|
16
html.go
Normal file
16
html.go
Normal file
@ -0,0 +1,16 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"html"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func hesc(in string) string {
|
||||
return html.EscapeString(in)
|
||||
}
|
||||
|
||||
func fail(w http.ResponseWriter, r *http.Request, s string) {
|
||||
log.Printf("[%s] %s", r.RemoteAddr, s)
|
||||
http.Error(w, "Malformed request", 400)
|
||||
}
|
42
tpl_Home.go
Normal file
42
tpl_Home.go
Normal file
@ -0,0 +1,42 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (this *App) Serve_Homepage(w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", "text/html;charset=UTF-8")
|
||||
w.WriteHeader(200)
|
||||
|
||||
this.ServePartial_Header(w, "/")
|
||||
|
||||
for i, t := range this.cfg.Commands {
|
||||
fmt.Fprint(w,
|
||||
`<h3>`+hesc(t.Title)+`</h3>
|
||||
<form method="POST" action="/x-new-task">
|
||||
<input type="hidden" name="task_id" value="`+fmt.Sprintf("%d", i)+`">
|
||||
`)
|
||||
for i, param := range t.Execution {
|
||||
switch param.ParamType {
|
||||
case PARAMTYPE_CONST:
|
||||
// not configurable parameter
|
||||
case PARAMTYPE_STRING:
|
||||
fmt.Fprintf(w, `<input type="text" name="param[%d]" placeholder="%s" title="%s" value="%s"><br>`,
|
||||
i, hesc(param.Description), hesc(param.Description), hesc(param.Value))
|
||||
case PARAMTYPE_OPTIONAL:
|
||||
fmt.Fprintf(w, `<input type="hidden" name="param[%d]" value="off"><label><input type="checkbox" name="param[%d]" value="on">%s</label><br>`,
|
||||
i, i, hesc(param.Description))
|
||||
default:
|
||||
log.Fatalf("Unknown PARAMTYPE(%d)", param.ParamType)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, `
|
||||
<input type="submit" value="Run »">
|
||||
</form>
|
||||
`)
|
||||
}
|
||||
|
||||
this.ServePartial_Footer(w)
|
||||
}
|
50
tpl_Style.go
Normal file
50
tpl_Style.go
Normal file
@ -0,0 +1,50 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (this *App) Serve_StyleCSS(w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", "text/css")
|
||||
w.WriteHeader(200)
|
||||
fmt.Fprint(w, `
|
||||
/* Global styles */
|
||||
html,body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
a {
|
||||
color:green;
|
||||
}
|
||||
th {
|
||||
text-align:left;
|
||||
}
|
||||
table {
|
||||
width:100%;
|
||||
border-collapse: collapse;
|
||||
margin-top:1em;
|
||||
}
|
||||
td {
|
||||
padding:2px;
|
||||
}
|
||||
tr:hover td {
|
||||
background:lightyellow;
|
||||
}
|
||||
|
||||
/* Specific elements */
|
||||
.task-state-finished {
|
||||
font-weight:bold;
|
||||
}
|
||||
.task-state-running {
|
||||
color:darkgreen;
|
||||
}
|
||||
#task-output tbody {
|
||||
font-family: monospace;
|
||||
}
|
||||
#task-output .stdout {
|
||||
}
|
||||
#task-output .stderr {
|
||||
color:red;
|
||||
}
|
||||
`)
|
||||
}
|
81
tpl_Taskinfo.go
Normal file
81
tpl_Taskinfo.go
Normal file
@ -0,0 +1,81 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (this *App) Serve_TaskInfo(w http.ResponseWriter, task_ref string) {
|
||||
|
||||
this.tasksMtx.RLock()
|
||||
taskinfo, ok := this.tasks[task_ref]
|
||||
this.tasksMtx.RUnlock()
|
||||
|
||||
if !ok {
|
||||
http.Error(w, "Unknown task.", 404)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html;charset=UTF-8")
|
||||
w.WriteHeader(200)
|
||||
|
||||
this.ServePartial_Header(w, "/taskinfo/...")
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
<ul>
|
||||
<li>Type: %s</li>
|
||||
<li>Start time: %s</li>
|
||||
`,
|
||||
"???",
|
||||
hesc(time.Unix(taskinfo.started, 0).Format(time.RFC822Z)),
|
||||
)
|
||||
|
||||
if taskinfo.Finished() {
|
||||
fmt.Fprintf(w, `
|
||||
<li>Completed at: %s</li>
|
||||
<li>Process exit code: %d</li>
|
||||
`,
|
||||
hesc(time.Unix(taskinfo.stopped, 0).Format(time.RFC822Z)),
|
||||
taskinfo.exitCode,
|
||||
)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, `
|
||||
</ul>
|
||||
|
||||
<table id="task-output">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`)
|
||||
|
||||
for _, line := range taskinfo.output {
|
||||
messageClass := "stdout"
|
||||
if line.isError {
|
||||
messageClass = "stderr"
|
||||
}
|
||||
fmt.Fprintf(w,
|
||||
`<tr class="%s">
|
||||
<td>%s</td>
|
||||
</tr>
|
||||
`, messageClass, hesc(line.text),
|
||||
)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
</tbody>
|
||||
</table>
|
||||
<form method="POST" action="/x-abandon-task">
|
||||
<input type="hidden" name="task_ref" value="%s">
|
||||
<input type="submit" value="Cancel »">
|
||||
</form>
|
||||
`,
|
||||
hesc(task_ref),
|
||||
)
|
||||
|
||||
this.ServePartial_Footer(w)
|
||||
}
|
61
tpl_Tasks.go
Normal file
61
tpl_Tasks.go
Normal file
@ -0,0 +1,61 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (this *App) Serve_Tasks(w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", "text/html;charset=UTF-8")
|
||||
|
||||
w.WriteHeader(200)
|
||||
this.ServePartial_Header(w, "/tasks")
|
||||
|
||||
fmt.Fprint(w, `
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Task</th>
|
||||
<th>Started</th>
|
||||
<th>State</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`)
|
||||
|
||||
this.tasksMtx.RLock()
|
||||
defer this.tasksMtx.RUnlock()
|
||||
for ref, t := range this.tasks {
|
||||
fmt.Fprintf(w,
|
||||
`<tr>
|
||||
<td><a href="/task/%s">%s</td>
|
||||
<td>%s</td>
|
||||
<td>
|
||||
`,
|
||||
hesc(ref), hesc(ref),
|
||||
hesc(time.Unix(t.started, 0).Format(time.RFC822Z)),
|
||||
)
|
||||
|
||||
if t.Finished() {
|
||||
fmt.Fprint(w, `<span class="task-state-finished">Finished</span>`)
|
||||
} else {
|
||||
fmt.Fprint(w, `<span class="task-state-running">Running</span>`)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, `
|
||||
</td>
|
||||
</tr>
|
||||
`)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, `
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<form method="POST" action="/x-clear-completed-tasks">
|
||||
<input type="submit" value="Clear completed tasks »">
|
||||
</form>
|
||||
`)
|
||||
this.ServePartial_Footer(w)
|
||||
}
|
41
tpt_Header.go
Normal file
41
tpt_Header.go
Normal file
@ -0,0 +1,41 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (this *App) ServePartial_Header(w http.ResponseWriter, slug string) {
|
||||
|
||||
fmt.Fprint(w, `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>`+hesc(this.cfg.AppTitle)+`</title>
|
||||
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h2>`+hesc(this.cfg.AppTitle)+`</h2>
|
||||
`)
|
||||
|
||||
if slug == "/" {
|
||||
fmt.Fprint(w, "New task")
|
||||
} else {
|
||||
fmt.Fprint(w, `<a href="/">New task</a>`)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, ` | `)
|
||||
|
||||
if slug == "/tasks" {
|
||||
fmt.Fprint(w, "Current tasks")
|
||||
} else {
|
||||
fmt.Fprint(w, `<a href="/tasks">Current tasks</a>`)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *App) ServePartial_Footer(w http.ResponseWriter) {
|
||||
fmt.Fprint(w, `
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
}
|
27
twa_AbandonTask.go
Normal file
27
twa_AbandonTask.go
Normal file
@ -0,0 +1,27 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (this *App) Action_AbandonTask(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
fail(w, r, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
taskRef := r.Form.Get("task_ref")
|
||||
|
||||
this.tasksMtx.RLock()
|
||||
task, ok := this.tasks[taskRef]
|
||||
this.tasksMtx.RUnlock()
|
||||
|
||||
if !ok {
|
||||
http.Error(w, "Invalid task specified", 404)
|
||||
return
|
||||
}
|
||||
|
||||
task.cancel()
|
||||
http.Redirect(w, r, "/task/"+taskRef, 302)
|
||||
}
|
19
twa_ClearCompleted.go
Normal file
19
twa_ClearCompleted.go
Normal file
@ -0,0 +1,19 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (this *App) Action_ClearCompleted(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
this.tasksMtx.Lock()
|
||||
defer this.tasksMtx.Unlock()
|
||||
|
||||
for k, v := range this.tasks {
|
||||
if v.Finished() {
|
||||
delete(this.tasks, k)
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/tasks", 302)
|
||||
}
|
68
twa_NewTask.go
Normal file
68
twa_NewTask.go
Normal file
@ -0,0 +1,68 @@
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (this *App) Action_NewTask(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
fail(w, r, fmt.Sprintf("Bad form data: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
taskIdStr := r.Form.Get("task_id")
|
||||
if len(taskIdStr) == 0 {
|
||||
fail(w, r, "Missing task ID in request")
|
||||
return
|
||||
}
|
||||
|
||||
taskId, err := strconv.Atoi(taskIdStr)
|
||||
if err != nil {
|
||||
fail(w, r, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if taskId < 0 || taskId >= len(this.cfg.Commands) {
|
||||
fail(w, r, fmt.Sprintf("Invalid task ID %d", taskId))
|
||||
return
|
||||
}
|
||||
|
||||
taskInfo := this.cfg.Commands[taskId]
|
||||
|
||||
params := make([]string, 0, len(taskInfo.Execution))
|
||||
for i, prop := range taskInfo.Execution {
|
||||
switch prop.ParamType {
|
||||
case PARAMTYPE_CONST:
|
||||
params = append(params, prop.Value)
|
||||
|
||||
case PARAMTYPE_STRING:
|
||||
val := r.Form.Get(fmt.Sprintf("param[%d]", i))
|
||||
params = append(params, val)
|
||||
|
||||
case PARAMTYPE_OPTIONAL:
|
||||
val := r.Form.Get(fmt.Sprintf("param[%d]", i))
|
||||
if val == "on" {
|
||||
params = append(params, prop.Value)
|
||||
} else if val == "off" {
|
||||
// nothing
|
||||
} else {
|
||||
fail(w, r, "Unexpected value for parameter")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Create new command from supplied values
|
||||
taskRef, err := this.LaunchTask(params)
|
||||
if err != nil {
|
||||
fail(w, r, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/task/"+taskRef, 302)
|
||||
}
|
Loading…
Reference in New Issue
Block a user