monfront - add basic interface to create some entities
This commit is contained in:
parent
49dac92034
commit
e29f38937c
148
cmd/monfront/create.go
Normal file
148
cmd/monfront/create.go
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func showCreate(con *Context) {
|
||||||
|
if con.r.Method == "POST" {
|
||||||
|
addCreate(con)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if con.r.Method != "GET" {
|
||||||
|
con.w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
con.w.Write([]byte("method is not supported"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !con.CanEdit {
|
||||||
|
con.w.WriteHeader(http.StatusForbidden)
|
||||||
|
con.w.Write([]byte("no permission to change data"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
con.Content = map[string]any{}
|
||||||
|
|
||||||
|
primitives := []struct {
|
||||||
|
name string
|
||||||
|
query string
|
||||||
|
}{
|
||||||
|
{"commands", "select id, name, updated, command, message from commands order by name"},
|
||||||
|
{"checkers", "select id, name, description from checkers order by name"},
|
||||||
|
{"notifier", "select id, name, settings from notifier order by name"},
|
||||||
|
{"nodes", "select id, name, updated, message from nodes order by name"},
|
||||||
|
}
|
||||||
|
for _, prim := range primitives {
|
||||||
|
rows, err := DB.Query(prim.query)
|
||||||
|
defer rows.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("could not get commands: %s", err)
|
||||||
|
con.Error = "could not get commands"
|
||||||
|
returnError(http.StatusInternalServerError, con, con.w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result, err := rowsToResult(rows)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("could not get %s: %s", prim.name, err)
|
||||||
|
con.Error = "could not get " + prim.name
|
||||||
|
returnError(http.StatusInternalServerError, con, con.w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
con.Content[prim.name] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
con.w.Header()["Content-Type"] = []string{"text/html"}
|
||||||
|
con.Render("create_index")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
sqlResult struct {
|
||||||
|
Columns []string
|
||||||
|
Rows [][]sql.NullString
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func rowsToResult(rows *sql.Rows) (*sqlResult, error) {
|
||||||
|
res := &sqlResult{}
|
||||||
|
cols, err := rows.Columns()
|
||||||
|
if err != nil {
|
||||||
|
return res, fmt.Errorf("could not get columns: %w", err)
|
||||||
|
}
|
||||||
|
res.Columns = cols
|
||||||
|
res.Rows = [][]sql.NullString{}
|
||||||
|
colNum := len(cols)
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
line := make([]sql.NullString, colNum)
|
||||||
|
lineMap := make([]any, colNum)
|
||||||
|
for i := 0; i < colNum; i++ {
|
||||||
|
lineMap[i] = &(line[i])
|
||||||
|
}
|
||||||
|
if err := rows.Scan(lineMap...); err != nil {
|
||||||
|
return res, fmt.Errorf("could not scan values: %w", err)
|
||||||
|
}
|
||||||
|
res.Rows = append(res.Rows, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addCreate(con *Context) {
|
||||||
|
if !con.CanEdit {
|
||||||
|
con.w.WriteHeader(http.StatusForbidden)
|
||||||
|
con.w.Write([]byte("no permission to change data"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := con.r.ParseForm(); err != nil {
|
||||||
|
con.w.WriteHeader(http.StatusBadRequest)
|
||||||
|
fmt.Fprintf(con.w, "could not parse parameters: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
con.w.Header()["Location"] = []string{"/create"}
|
||||||
|
|
||||||
|
addType := con.r.PostForm.Get("type")
|
||||||
|
types := map[string]struct {
|
||||||
|
Fields []string
|
||||||
|
Table string
|
||||||
|
}{
|
||||||
|
"command": {[]string{"name", "command", "message"}, "commands"},
|
||||||
|
"node": {[]string{"name", "message"}, "nodes"},
|
||||||
|
"checker": {[]string{"name", "description"}, "checkers"},
|
||||||
|
"notifier": {[]string{"name", "settings"}, "notifier"},
|
||||||
|
"check": {[]string{"name", "message", "options", "intval", "node_id", "command_id", "checker_id"}, "checks"},
|
||||||
|
}
|
||||||
|
t, found := types[addType]
|
||||||
|
if !found {
|
||||||
|
con.Error = "undefined type '" + addType + "'"
|
||||||
|
returnError(http.StatusBadRequest, con, con.w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := make([]any, len(t.Fields))
|
||||||
|
vals := make([]string, len(t.Fields))
|
||||||
|
for i := 0; i < len(fields); i++ {
|
||||||
|
vals[i] = fmt.Sprintf(`$%d`, i+1)
|
||||||
|
fields[i] = con.r.PostForm.Get(t.Fields[i])
|
||||||
|
if fields[i] == "" {
|
||||||
|
con.Error = "field " + t.Fields[i] + " must not be empty"
|
||||||
|
returnError(http.StatusBadRequest, con, con.w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stmt := `insert into ` + t.Table + `(` + strings.Join(t.Fields, ",") + `) values (` + strings.Join(vals, ",") + `)`
|
||||||
|
_, err := DB.Exec(stmt, fields...)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("could not insert new %s: %s", addType, err)
|
||||||
|
con.Error = "could not insert new " + addType
|
||||||
|
returnError(http.StatusInternalServerError, con, con.w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
con.w.WriteHeader(http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
@ -170,6 +170,7 @@ func main() {
|
|||||||
|
|
||||||
s := newServer(l, db, tmpl, auth, autho)
|
s := newServer(l, db, tmpl, auth, autho)
|
||||||
s.Handle("/", showChecks)
|
s.Handle("/", showChecks)
|
||||||
|
s.Handle("/create", showCreate)
|
||||||
s.Handle("/check", showCheck)
|
s.Handle("/check", showCheck)
|
||||||
s.Handle("/checks", showChecks)
|
s.Handle("/checks", showChecks)
|
||||||
s.Handle("/groups", showGroups)
|
s.Handle("/groups", showGroups)
|
||||||
|
@ -46,6 +46,8 @@ type (
|
|||||||
CheckDetails *checkDetails `json:"check_details,omitempty"`
|
CheckDetails *checkDetails `json:"check_details,omitempty"`
|
||||||
Groups []group `json:"groups,omitempty"`
|
Groups []group `json:"groups,omitempty"`
|
||||||
Unhandled bool `json:"-"` // set this flag when unhandled was called
|
Unhandled bool `json:"-"` // set this flag when unhandled was called
|
||||||
|
|
||||||
|
Content map[string]any `json:"-"` // used for the configuration dashboard
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
84
cmd/monfront/templates/create_index.html
Normal file
84
cmd/monfront/templates/create_index.html
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
{{ template "header" . }}
|
||||||
|
<section id="content">
|
||||||
|
<h1>create entries</h1>
|
||||||
|
<details>
|
||||||
|
<summary>checks</summary>
|
||||||
|
<form action="/create" method="POST">
|
||||||
|
<p><label>name</label><input name="name" /></p>
|
||||||
|
<p><label>node</label>
|
||||||
|
<select name="node_id">
|
||||||
|
{{ range $node := .Content.nodes.Rows }}
|
||||||
|
<option value="{{ (index . 0).String }}">{{ (index . 1).String }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p><label>command</label><select name="command_id">
|
||||||
|
{{ range $node := .Content.commands.Rows }}
|
||||||
|
<option value="{{ (index . 0).String }}">{{ (index . 1).String }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select></p>
|
||||||
|
<p><label>checker</label><select name="checker_id">
|
||||||
|
{{ range $node := .Content.checkers.Rows }}
|
||||||
|
<option value="{{ (index . 0).String }}">{{ (index . 1).String }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select></p>
|
||||||
|
<p><label>interval</label><input type="integer" name="interval" value="300" /></p>
|
||||||
|
<fieldset>
|
||||||
|
<legend>options</legend>
|
||||||
|
<!-- TODO implement setting options -->
|
||||||
|
</fieldset>
|
||||||
|
<p><label>message</label><textarea name="message"></textarea></p>
|
||||||
|
<p><button type="submit" name="type" value="check">create</button></p>
|
||||||
|
</form>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>nodes</summary>
|
||||||
|
<details>
|
||||||
|
<summary>create new node</summary>
|
||||||
|
<form action="/create" method="POST">
|
||||||
|
<p><label>name</label><input name="name" /></p>
|
||||||
|
<p><label>message</label><textarea name="message"></textarea></p>
|
||||||
|
<p><button type="submit" name="type" value="node">create</button></p>
|
||||||
|
</form>
|
||||||
|
</details>
|
||||||
|
{{ template "rows_to_table" .Content.nodes }}
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>commands</summary>
|
||||||
|
<details>
|
||||||
|
<summary>create new command</summary>
|
||||||
|
<form action="/create" method="POST">
|
||||||
|
<p><label>name</label><input name="name" /></p>
|
||||||
|
<p><label>command</label><textarea name="command"></textarea></p>
|
||||||
|
<p><label>message</label><textarea name="message"></textarea></p>
|
||||||
|
<p><button type="submit" name="type" value="command">create</button></p>
|
||||||
|
</form>
|
||||||
|
</details>
|
||||||
|
{{ template "rows_to_table" .Content.commands }}
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>checkers</summary>
|
||||||
|
<details>
|
||||||
|
<summary>create new checker instance</summary>
|
||||||
|
<form action="/create" method="POST">
|
||||||
|
<p><label>name</label><input name="name" /></p>
|
||||||
|
<p><label>description</label><textarea name="description"></textarea></p>
|
||||||
|
<p><button type="submit" name="type" value="checker">create</button></p>
|
||||||
|
</form>
|
||||||
|
</details>
|
||||||
|
{{ template "rows_to_table" .Content.checkers }}
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>notifier</summary>
|
||||||
|
<details>
|
||||||
|
<summary>create new notifier</summary>
|
||||||
|
<form action="/create" method="POST">
|
||||||
|
<p><label>name</label><input name="name" /></p>
|
||||||
|
<p><label>settings</label><textarea name="settings">{}</textarea></p>
|
||||||
|
<p><button type="submit" name="type" value="notifier">create</button></p>
|
||||||
|
</form>
|
||||||
|
</details>
|
||||||
|
{{ template "rows_to_table" .Content.notifier }}
|
||||||
|
</details>
|
||||||
|
</section>
|
||||||
|
{{ template "footer" . }}
|
@ -92,6 +92,7 @@
|
|||||||
<li><a href="/">home</a></li>
|
<li><a href="/">home</a></li>
|
||||||
<li><a href="/checks?filter-state=1&filter-ack=false">checks</a></li>
|
<li><a href="/checks?filter-state=1&filter-ack=false">checks</a></li>
|
||||||
<li><a href="/groups">groups</a></li>
|
<li><a href="/groups">groups</a></li>
|
||||||
|
<li><a href="/create">create</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
{{ if .Error }}<div class="error">{{ .Error }}</div>{{ end }}
|
{{ if .Error }}<div class="error">{{ .Error }}</div>{{ end }}
|
||||||
|
19
cmd/monfront/templates/rows_to_table.html
Normal file
19
cmd/monfront/templates/rows_to_table.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
{{ range $col := .Columns }}
|
||||||
|
<th>{{ $col }}</th>
|
||||||
|
{{ end }}
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
{{ range $row := .Rows }}
|
||||||
|
<tr>
|
||||||
|
{{ range $col := $row }}
|
||||||
|
<td>{{ if $col.Valid }}{{ $col.String }}{{ end }}</td>
|
||||||
|
{{ end }}
|
||||||
|
<td>
|
||||||
|
<button name="update" value="{{ (index $row 0).String }}">update</button>
|
||||||
|
<button name="delete" value="{{ (index $row 0).String }}">delete</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</table>
|
Loading…
Reference in New Issue
Block a user