monfront - add basic interface to create some entities

This commit is contained in:
Gibheer 2022-12-15 11:16:56 +01:00
parent 49dac92034
commit e29f38937c
6 changed files with 255 additions and 0 deletions

148
cmd/monfront/create.go Normal file
View 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
}

View File

@ -170,6 +170,7 @@ func main() {
s := newServer(l, db, tmpl, auth, autho)
s.Handle("/", showChecks)
s.Handle("/create", showCreate)
s.Handle("/check", showCheck)
s.Handle("/checks", showChecks)
s.Handle("/groups", showGroups)

View File

@ -46,6 +46,8 @@ type (
CheckDetails *checkDetails `json:"check_details,omitempty"`
Groups []group `json:"groups,omitempty"`
Unhandled bool `json:"-"` // set this flag when unhandled was called
Content map[string]any `json:"-"` // used for the configuration dashboard
}
)

View 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" . }}

View File

@ -92,6 +92,7 @@
<li><a href="/">home</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="/create">create</a></li>
</ul>
</nav>
{{ if .Error }}<div class="error">{{ .Error }}</div>{{ end }}

View 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>