kinda working watch view page

This commit is contained in:
BroodjeAap 2022-10-07 19:54:18 +00:00
parent b2644e429d
commit 9a5c14bf65
5 changed files with 324 additions and 207 deletions

46
main.go
View file

@ -65,7 +65,8 @@ func (web *Web) initRouter() {
web.router.GET("/", web.index)
web.router.GET("/watch/:id", web.watchView)
web.router.GET("/watch/view/:id", web.watchView)
web.router.GET("/watch/edit/:id", web.watchEdit)
web.router.GET("/watch/new", web.watchCreate)
web.router.POST("/watch/create", web.watchCreatePost)
web.router.POST("/watch/update", web.watchUpdate)
@ -80,6 +81,7 @@ func (web *Web) initTemplates() {
web.templates.AddFromFiles("index", "templates/base.html", "templates/index.html")
web.templates.AddFromFiles("watchCreate", "templates/base.html", "templates/watch/create.html")
web.templates.AddFromFiles("watchView", "templates/base.html", "templates/watch/view.html")
web.templates.AddFromFiles("watchEdit", "templates/base.html", "templates/watch/edit.html")
web.templates.AddFromFiles("cacheView", "templates/base.html", "templates/cache/view.html")
@ -107,11 +109,22 @@ func (web *Web) run() {
web.router.Run("0.0.0.0:8080")
}
type WatchEntry struct {
Watch *Watch
Entry *cron.Entry
}
func (web *Web) index(c *gin.Context) {
//msg := tgbotapi.NewMessage(viper.GetInt64("telegram.chat"), message)
//web.Bot.Send(msg)
watches := []Watch{}
web.db.Find(&watches)
for i := 0; i < len(watches); i++ {
entry := web.cronWatch[watches[i].ID]
watches[i].CronEntry = &entry
}
c.HTML(http.StatusOK, "index", watches)
}
@ -149,6 +162,33 @@ func (web *Web) deleteWatch(c *gin.Context) {
func (web *Web) watchView(c *gin.Context) {
id := c.Param("id")
var watch Watch
web.db.Model(&Watch{}).First(&watch, id)
entry, exists := web.cronWatch[watch.ID]
if !exists {
log.Println("Could not find entry for Watch", watch.ID)
c.HTML(http.StatusNotFound, "watchView", gin.H{"error": "Entry not found"})
return
}
watch.CronEntry = &entry
var values []FilterOutput
web.db.Model(&FilterOutput{}).Where("watch_id = ?", watch.ID).Find(&values)
valueMap := make(map[string][]FilterOutput, len(values))
for _, value := range values {
valueMap[value.Name] = append(valueMap[value.Name], value)
}
c.HTML(http.StatusOK, "watchView", gin.H{
"Watch": watch,
"ValueMap": valueMap,
})
}
func (web *Web) watchEdit(c *gin.Context) {
id := c.Param("id")
var watch Watch
web.db.Model(&Watch{}).First(&watch, id)
@ -164,7 +204,7 @@ func (web *Web) watchView(c *gin.Context) {
buildFilterTree(filters, connections)
processFilters(filters, web, &watch, true, true)
c.HTML(http.StatusOK, "watchView", gin.H{
c.HTML(http.StatusOK, "watchEdit", gin.H{
"Watch": watch,
"Filters": filters,
"Connections": connections,
@ -216,7 +256,7 @@ func (web *Web) watchUpdate(c *gin.Context) {
web.db.Create(&newConnections)
}
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/%d", watch.ID))
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/edit/%d", watch.ID))
}
func (web *Web) cacheView(c *gin.Context) {

View file

@ -4,12 +4,15 @@ import (
"fmt"
"html"
"time"
"github.com/robfig/cron/v3"
)
type Watch struct {
ID uint `form:"watch_id" yaml:"watch_id"`
Name string `form:"watch_name" yaml:"watch_name" binding:"required" validate:"min=1"`
Interval int `form:"interval" yaml:"interval" binding:"required"`
ID uint `form:"watch_id" yaml:"watch_id"`
Name string `form:"watch_name" yaml:"watch_name" binding:"required" validate:"min=1"`
Interval int `form:"interval" yaml:"interval" binding:"required"`
CronEntry *cron.Entry `gorm:"-:all"`
}
type Filter struct {

View file

@ -3,15 +3,21 @@
<thead class="table-dark">
<tr>
<th>Name</th>
<th>Interval</th>
<th></th>
<th>Last Run</th>
<th>Next Run</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{{ range . }}
<tr class="pointer" onclick="window.location='/watch/{{ .ID }}'">
<tr class="pointer" onclick="window.location='/watch/view/{{ .ID }}'">
<td class="h3">{{ .Name }}</td>
<td class="h3">{{ .Interval }}</td>
<td class="h3">{{ .CronEntry.Prev.Format "2006-01-02 15:04:05" }}</td>
<td class="h3">{{ .CronEntry.Next.Format "2006-01-02 15:04:05" }}</td>
<td>
<a href="/watch/edit/{{ .ID }}" class="btn btn-success">Edit</a>
</td>
<td>
<form action="/watch/delete" method="post">
<input type="hidden" name="watch_id" value="{{ .ID }}">

213
templates/watch/edit.html Normal file
View file

@ -0,0 +1,213 @@
{{define "head"}}
<script src="/static/diagram.js"></script>
<script src="/static/edit.js"></script>
{{ end }}
{{define "content"}}
<div class="canvas_parent">
<canvas id="canvas">
</canvas>
</div>
{{ end }}
{{ define "left" }}
<table>
<tr>
<td>
<button type="button" id="filterButton" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#FilterModal">
Add Filter
</button>
</td>
<td>
<button type="button" id="filterButton" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#WatchModal">
Edit Watch
</button>
</td>
<td>
<button type="button" id="saveButtonMain" class="btn btn-primary btn-sm">
Save Watch
</button>
</td>
<td id="OpenLogTD" hidden>
<button type="button" id="logButton" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#LogModal">
Open Log
</button>
</td>
</tr>
</table>
<div class="modal fade" id="FilterModal" tabindex="-1" aria-labelledby="FilterModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="FilterModalLabel">Add Filter</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div>
<form action="/filter/create" method="post">
<div class="mb-3 m-3 row">
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10 p-2">
<input type="text" class="form-control" name="filter_name" id="nameInput" placeholder="Name">
</div>
<label for="typeInput" class="col-sm-2 col-form-label">Type:</label>
<div class="col-sm-10 p-2">
<select id="typeInput" class="form-control" name="type">
<option value="xpath" selected="true">XPath</option>
<option value="gurl">Get URL</option>
<option value="gurls">Get URLs</option>
<option value="json">JSON</option>
<option value="css">CSS</option>
<option value="replace">Replace</option>
<option value="match">Match</option>
<option value="substring">Substring</option>
<option value="contains">Contains</option>
<option value="math">Math</option>
<option value="store">Store</option>
<option value="condition">Condition</option>
<option value="notify">Notify</option>`
<option value="cron">Schedule</option>
</select>
</div>
<label for="var1" id="var1Label" class="col-sm-2 col-form-label">XPath:</label>
<div class="col-sm-10 p-2" id="var1Div">
<input type="text" class="form-control" name="var1" id="var1Input" placeholder="//a[@class='price']">
</div>
<label for="var2" id="var2Label" class="col-sm-2 col-form-label">-</label>
<div class="col-sm-10 p-2" id="var2Div">
<input type="text" class="form-control" name="var2" id="var2Input" placeholder="" disabled>
</div>
<label for="var3" id="var3Label" class="col-sm-2 col-form-label">-</label>
<div class="col-sm-10 p-2" id="var3Div">
<input type="text" class="form-control" name="var3" id="var3Input" placeholder="" disabled>
</div>
</div>
</form>
<div >
<button class="btn btn-primary mt-4" data-bs-dismiss="modal" id="submitFilterButton">Add Filter</button>
</div>
<div id="filterResultsDiv">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="WatchModal" tabindex="-1" aria-labelledby="WatchModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="WatchModalLabel">Watch - {{ .Watch.Name }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div>
<form action="/watch/update" id="saveWatchForm" method="post">
<div class="mb-3 m-3 row">
<input type="hidden" id="watch_id" name="watch_id" value="{{ .Watch.ID }}">
<label for="watchNameInput" class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10 p-2">
<input type="text" class="form-control" id="watchNameInput" name="watch_name" value="{{ .Watch.Name }}" placeholder="Name">
</div>
<label for="scheduleInput" class="col-sm-2 col-form-label">Schedule:</label>
<div class="col-sm-10 p-2">
<select id="scheduleInput" class="form-control" name="schedule_id">
<option value=".ID">.Name</option>
</select>
</div>
<label for="proxyInput" class="col-sm-2 col-form-label">Proxy Pool:</label>
<div class="col-sm-10 p-2">
<select id="proxyInput" class="form-control" name="proxy_pool_id">
<option value=".ID">.Name</option>
</select>
</div>
<input type="hidden" id="filtersInput" name="filters" value="">
<input type="hidden" id="connectionsInput" name="connections" value="">
</div>
</form>
<button class="btn btn-primary mt-4 float-start" id="saveButtonModal" data-bs-dismiss="modal" id="submitWatchButton">Save</button>
<form action="/watch/delete" method="post">
<input type="hidden" name="watch_id" value="{{ .Watch.ID }}">
<button class="btn btn-danger mt-4 float-end" data-bs-dismiss="modal" id="deleteFilterButton">Delete Watch</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="LogModal" tabindex="-1" aria-labelledby="LogModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="logModalLabel">-</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div>
<table class="table table-hover table-sm" id="logTable">
<thead>
<tr>
<th scope="col">Message</th>
</tr>
</thead>
<tbody id="logTableBody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{{ end }}
{{define "scripts"}}
<script>
var diagrams;
function canvasInit() {
diagrams = new Diagrams("canvas", editNode, deleteNode, logNode);
{{ range .Filters }}
diagrams.addNode(
{{ .ID }},
{{ .X }},
{{ .Y }},
"{{ .Name }}",
{
type: "{{ .Type }}",
var1: "{{ .Var1 }}",
var2: "{{ .Var2 }}",
var3: "{{ .Var3 }}",
},
[
{{ range .Results }}
'{{ . }}',
{{ end }}
],
[
{{ range.Logs }}
'{{ . }}',
{{ end }}
]
);
{{ end }}
{{ range .Connections }}
diagrams.addConnectionById({{ .OutputID }}, {{ .InputID }});
{{ end }}
diagrams.fillParent();
}
document.addEventListener('DOMContentLoaded', canvasInit, false);
</script>
{{ end }}

View file

@ -1,213 +1,68 @@
{{define "head"}}
<script src="/static/diagram.js"></script>
<script src="/static/edit.js"></script>
{{ end }}
{{define "content"}}
<div class="canvas_parent">
<canvas id="canvas">
</canvas>
</div>
{{ if .error }}
Could not find entry
{{ end }}
{{ define "left" }}
<table>
<tr>
<td>
<button type="button" id="filterButton" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#FilterModal">
Add Filter
</button>
</td>
<td>
<button type="button" id="filterButton" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#WatchModal">
Edit Watch
</button>
</td>
<td>
<button type="button" id="saveButtonMain" class="btn btn-primary btn-sm">
Save Watch
</button>
</td>
<td id="OpenLogTD" hidden>
<button type="button" id="logButton" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#LogModal">
Open Log
</button>
</td>
</tr>
</table>
<div class="modal fade" id="FilterModal" tabindex="-1" aria-labelledby="FilterModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="FilterModalLabel">Add Filter</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div>
<form action="/filter/create" method="post">
<div class="mb-3 m-3 row">
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10 p-2">
<input type="text" class="form-control" name="filter_name" id="nameInput" placeholder="Name">
</div>
<label for="typeInput" class="col-sm-2 col-form-label">Type:</label>
<div class="col-sm-10 p-2">
<select id="typeInput" class="form-control" name="type">
<option value="xpath" selected="true">XPath</option>
<option value="gurl">Get URL</option>
<option value="gurls">Get URLs</option>
<option value="json">JSON</option>
<option value="css">CSS</option>
<option value="replace">Replace</option>
<option value="match">Match</option>
<option value="substring">Substring</option>
<option value="contains">Contains</option>
<option value="math">Math</option>
<option value="store">Store</option>
<option value="condition">Condition</option>
<option value="notify">Notify</option>`
<option value="cron">Schedule</option>
</select>
</div>
<label for="var1" id="var1Label" class="col-sm-2 col-form-label">XPath:</label>
<div class="col-sm-10 p-2" id="var1Div">
<input type="text" class="form-control" name="var1" id="var1Input" placeholder="//a[@class='price']">
</div>
<label for="var2" id="var2Label" class="col-sm-2 col-form-label">-</label>
<div class="col-sm-10 p-2" id="var2Div">
<input type="text" class="form-control" name="var2" id="var2Input" placeholder="" disabled>
</div>
<label for="var3" id="var3Label" class="col-sm-2 col-form-label">-</label>
<div class="col-sm-10 p-2" id="var3Div">
<input type="text" class="form-control" name="var3" id="var3Input" placeholder="" disabled>
</div>
</div>
</form>
<div >
<button class="btn btn-primary mt-4" data-bs-dismiss="modal" id="submitFilterButton">Add Filter</button>
</div>
<div id="filterResultsDiv">
</div>
<div class="row">
<div class="d-flex justify-content-around">
<div class="card d-flex justify-content-around">
<div class="card-body">
<h4 class="card-title text-center">{{ .Watch.Name }}</h4>
<div class="row">
<div class="col-4">Previous</div>
<div class="col-8">{{ .Watch.CronEntry.Prev.Format "2006-01-02 15:04:05" }}</div>
</div>
<div class="row">
<div class="col-4">Next</div>
<div class="col-8">{{ .Watch.CronEntry.Next.Format "2006-01-02 15:04:05" }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="WatchModal" tabindex="-1" aria-labelledby="WatchModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="WatchModalLabel">Watch - {{ .Watch.Name }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div>
<form action="/watch/update" id="saveWatchForm" method="post">
<div class="mb-3 m-3 row">
<input type="hidden" id="watch_id" name="watch_id" value="{{ .Watch.ID }}">
<label for="watchNameInput" class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10 p-2">
<input type="text" class="form-control" id="watchNameInput" name="watch_name" value="{{ .Watch.Name }}" placeholder="Name">
</div>
<canvas id="chartCanvas">
<label for="scheduleInput" class="col-sm-2 col-form-label">Schedule:</label>
<div class="col-sm-10 p-2">
<select id="scheduleInput" class="form-control" name="schedule_id">
<option value=".ID">.Name</option>
</select>
</div>
<label for="proxyInput" class="col-sm-2 col-form-label">Proxy Pool:</label>
<div class="col-sm-10 p-2">
<select id="proxyInput" class="form-control" name="proxy_pool_id">
<option value=".ID">.Name</option>
</select>
</div>
<input type="hidden" id="filtersInput" name="filters" value="">
<input type="hidden" id="connectionsInput" name="connections" value="">
</div>
</form>
<button class="btn btn-primary mt-4 float-start" id="saveButtonModal" data-bs-dismiss="modal" id="submitWatchButton">Save</button>
<form action="/watch/delete" method="post">
<input type="hidden" name="watch_id" value="{{ .Watch.ID }}">
<button class="btn btn-danger mt-4 float-end" data-bs-dismiss="modal" id="deleteFilterButton">Delete Watch</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="LogModal" tabindex="-1" aria-labelledby="LogModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="logModalLabel">-</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div>
<table class="table table-hover table-sm" id="logTable">
<thead>
<tr>
<th scope="col">Message</th>
</tr>
</thead>
<tbody id="logTableBody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</canvas>
{{end}}
{{ define "scripts"}}
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@^2"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@^1"></script>
{{ end }}
{{define "scripts"}}
<script>
var diagrams;
function canvasInit() {
diagrams = new Diagrams("canvas", editNode, deleteNode, logNode);
{{ range .Filters }}
diagrams.addNode(
{{ .ID }},
{{ .X }},
{{ .Y }},
"{{ .Name }}",
{
type: "{{ .Type }}",
var1: "{{ .Var1 }}",
var2: "{{ .Var2 }}",
var3: "{{ .Var3 }}",
function canvasInit() {
const ctx = document.getElementById("chartCanvas").getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
datasets:[{
{{ range $name, $values := .ValueMap }}
label: {{ $name }},
data: [
{{ range $value := $values }}
{x: luxon.DateTime.fromISO('{{ $value.Time.Format "2006-01-02T15:04:05Z07:00" }}'), y: '{{ $value.Value }}'},
{{ end }}
],
{{ end }}
}]
},
[
{{ range .Results }}
'{{ . }}',
{{ end }}
],
[
{{ range.Logs }}
'{{ . }}',
{{ end }}
]
);
{{ end }}
{{ range .Connections }}
diagrams.addConnectionById({{ .OutputID }}, {{ .InputID }});
{{ end }}
diagrams.fillParent();
}
document.addEventListener('DOMContentLoaded', canvasInit, false);
options: {
scales: {
x: {
type: 'time',
distribution: 'series',
},
y: {
beginAtZero: true,
},
}
}
});
console.log(chart);
}
document.addEventListener('DOMContentLoaded', canvasInit, false);
</script>
{{ end }}