converted to FilterGroups with filters per URL
This commit is contained in:
parent
71afae411c
commit
242b10e21b
7 changed files with 183 additions and 123 deletions
9
forms.go
9
forms.go
|
@ -1,8 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
type QueryUpdate struct {
|
type FilterGroupUpdate struct {
|
||||||
ID uint `form:"query_id" binding:"required"`
|
ID uint `form:"group_id" binding:"required"`
|
||||||
Name string `form:"query_name" binding:"required" validate:"min=1"`
|
Name string `form:"group_name" binding:"required" validate:"min=1"`
|
||||||
Type string `form:"query_type" binding:"required" validate:"oneof=css xpath regex json"`
|
Type string `form:"group_type" binding:"required" validate:"oneof=diff enum number"`
|
||||||
Query string `form:"query" binding:"required"`
|
|
||||||
}
|
}
|
||||||
|
|
55
main.go
55
main.go
|
@ -63,7 +63,7 @@ func (web Web) viewWatch(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
var watch Watch
|
var watch Watch
|
||||||
web.db.Model(&Watch{}).Preload("URLs.Queries.Filters").First(&watch, id)
|
web.db.Model(&Watch{}).Preload("URLs.GroupFilters.Filters").First(&watch, id)
|
||||||
c.HTML(http.StatusOK, "viewWatch", watch)
|
c.HTML(http.StatusOK, "viewWatch", watch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,20 +79,20 @@ func (web Web) createURL(c *gin.Context) {
|
||||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", url.WatchID))
|
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", url.WatchID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (web Web) createQuery(c *gin.Context) {
|
func (web Web) createFilterGroup(c *gin.Context) {
|
||||||
watch_id, err := strconv.ParseUint(c.PostForm("w_id"), 10, 64)
|
watch_id, err := strconv.ParseUint(c.PostForm("w_id"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
c.HTML(http.StatusInternalServerError, "500", gin.H{})
|
c.HTML(http.StatusInternalServerError, "500", gin.H{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var query Query
|
var group FilterGroup
|
||||||
errMap, err := bindAndValidateQuery(&query, c)
|
errMap, err := bindAndValidateGroup(&group, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.HTML(http.StatusBadRequest, "500", errMap)
|
c.HTML(http.StatusBadRequest, "500", errMap)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
web.db.Create(&query)
|
web.db.Create(&group)
|
||||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", watch_id))
|
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", watch_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,39 +105,38 @@ func (web Web) createFilter(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
web.db.Create(&filter)
|
web.db.Create(&filter)
|
||||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/query/edit/%d", filter.QueryID))
|
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/group/edit/%d", filter.FilterGroupID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (web Web) editQuery(c *gin.Context) {
|
func (web Web) editGroup(c *gin.Context) {
|
||||||
query_id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
group_id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Redirect(http.StatusSeeOther, "/watch/new")
|
c.Redirect(http.StatusSeeOther, "/watch/new")
|
||||||
return // TODO response
|
return // TODO response
|
||||||
}
|
}
|
||||||
var query Query
|
var group FilterGroup
|
||||||
web.db.Preload("URL.Watch").Preload("Filters").Preload("URL").First(&query, query_id)
|
web.db.Preload("URL.Watch").Preload("Filters").Preload("URL").First(&group, group_id)
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "editQuery", gin.H{
|
c.HTML(http.StatusOK, "editGroup", gin.H{
|
||||||
"Query": query,
|
"Group": group,
|
||||||
"currentResult": getQueryResult(&query),
|
"currentResult": getGroupResult(&group),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (web Web) updateQuery(c *gin.Context) {
|
func (web Web) updateGroup(c *gin.Context) {
|
||||||
var queryUpdate QueryUpdate
|
var groupUpdate FilterGroupUpdate
|
||||||
errMap, err := bindAndValidateQueryUpdate(&queryUpdate, c)
|
errMap, err := bindAndValidateGroupUpdate(&groupUpdate, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
c.HTML(http.StatusBadRequest, "500", errMap)
|
c.HTML(http.StatusBadRequest, "500", errMap)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var query Query
|
var group FilterGroup
|
||||||
web.db.First(&query, queryUpdate.ID)
|
web.db.First(&group, groupUpdate.ID)
|
||||||
query.Name = queryUpdate.Name
|
group.Name = groupUpdate.Name
|
||||||
query.Type = queryUpdate.Type
|
group.Type = groupUpdate.Type
|
||||||
query.Query = queryUpdate.Query
|
web.db.Save(&group)
|
||||||
web.db.Save(&query)
|
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/group/edit/%d", +group.ID))
|
||||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/query/edit/%d", +query.ID))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func passiveBot(bot *tgbotapi.BotAPI) {
|
func passiveBot(bot *tgbotapi.BotAPI) {
|
||||||
|
@ -183,7 +182,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
db, _ := gorm.Open(sqlite.Open(viper.GetString("database.dsn")))
|
db, _ := gorm.Open(sqlite.Open(viper.GetString("database.dsn")))
|
||||||
db.AutoMigrate(&Watch{}, &URL{}, &Query{}, &Filter{})
|
db.AutoMigrate(&Watch{}, &URL{}, &FilterGroup{}, &Filter{})
|
||||||
|
|
||||||
//bot, _ := tgbotapi.NewBotAPI(viper.GetString("telegram.token"))
|
//bot, _ := tgbotapi.NewBotAPI(viper.GetString("telegram.token"))
|
||||||
|
|
||||||
|
@ -205,7 +204,7 @@ func main() {
|
||||||
templates.AddFromFiles("index", "templates/base.html", "templates/index.html")
|
templates.AddFromFiles("index", "templates/base.html", "templates/index.html")
|
||||||
templates.AddFromFiles("newWatch", "templates/base.html", "templates/newWatch.html")
|
templates.AddFromFiles("newWatch", "templates/base.html", "templates/newWatch.html")
|
||||||
templates.AddFromFiles("viewWatch", "templates/base.html", "templates/viewWatch.html")
|
templates.AddFromFiles("viewWatch", "templates/base.html", "templates/viewWatch.html")
|
||||||
templates.AddFromFiles("editQuery", "templates/base.html", "templates/editQuery.html")
|
templates.AddFromFiles("editGroup", "templates/base.html", "templates/editGroup.html")
|
||||||
|
|
||||||
templates.AddFromFiles("500", "templates/base.html", "templates/500.html")
|
templates.AddFromFiles("500", "templates/base.html", "templates/500.html")
|
||||||
router.HTMLRender = templates
|
router.HTMLRender = templates
|
||||||
|
@ -216,9 +215,9 @@ func main() {
|
||||||
router.POST("/watch/delete", web.deleteWatch)
|
router.POST("/watch/delete", web.deleteWatch)
|
||||||
router.GET("/watch/view/:id/", web.viewWatch)
|
router.GET("/watch/view/:id/", web.viewWatch)
|
||||||
router.POST("/url/create/", web.createURL)
|
router.POST("/url/create/", web.createURL)
|
||||||
router.POST("/query/create/", web.createQuery)
|
router.POST("/group/create/", web.createFilterGroup)
|
||||||
router.GET("/query/edit/:id", web.editQuery)
|
router.GET("/group/edit/:id", web.editGroup)
|
||||||
router.POST("/query/update", web.updateQuery)
|
router.POST("/group/update", web.updateGroup)
|
||||||
router.POST("/filter/create/", web.createFilter)
|
router.POST("/filter/create/", web.createFilter)
|
||||||
|
|
||||||
router.Run("0.0.0.0:8080")
|
router.Run("0.0.0.0:8080")
|
||||||
|
|
31
models.go
31
models.go
|
@ -13,29 +13,28 @@ type Watch struct {
|
||||||
|
|
||||||
type URL struct {
|
type URL struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
WatchID uint `form:"url_watch_id" yaml:"url_watch_id" binding:"required"`
|
WatchID uint `form:"url_watch_id" yaml:"url_watch_id" binding:"required"`
|
||||||
Watch *Watch `form:"watch" yaml:"watch" validate:"omitempty"`
|
Watch *Watch `form:"watch" yaml:"watch" validate:"omitempty"`
|
||||||
Name string `form:"url_name" yaml:"url_name" binding:"required" validate:"min=1"`
|
Name string `form:"url_name" yaml:"url_name" binding:"required" validate:"min=1"`
|
||||||
URL string `form:"url" yaml:"url" binding:"required,url" validate:"min=1"`
|
URL string `form:"url" yaml:"url" binding:"required,url" validate:"min=1"`
|
||||||
Queries []Query
|
GroupFilters []FilterGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query struct {
|
type FilterGroup struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
URLID uint `form:"query_url_id" yaml:"query_url_id" binding:"required"`
|
URLID uint `form:"group_url_id" yaml:"group_url_id" binding:"required"`
|
||||||
URL *URL
|
URL *URL
|
||||||
Name string `form:"query_name" yaml:"query_name" binding:"required" validate:"min=1"`
|
Name string `form:"group_name" yaml:"group_name" binding:"required" validate:"min=1"`
|
||||||
Type string `form:"query_type" yaml:"query_type" binding:"required" validate:"oneof=css xpath regex json"`
|
Type string `form:"group_type" yaml:"group_type" binding:"required" validate:"oneof=diff enum number bool"`
|
||||||
Query string `form:"query" yaml:"query" binding:"required"`
|
|
||||||
Filters []Filter
|
Filters []Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
QueryID uint `form:"filter_query_id" yaml:"filter_query_id" binding:"required"`
|
FilterGroupID uint `form:"filter_group_id" yaml:"filter_group_id" binding:"required"`
|
||||||
Query *Query
|
FilterGroup *FilterGroup
|
||||||
Name string `form:"filter_name" yaml:"filter_name" binding:"required" validate:"min=1"`
|
Name string `form:"filter_name" yaml:"filter_name" binding:"required" validate:"min=1"`
|
||||||
Type string `form:"filter_type" yaml:"filter_type" binding:"required" validate:"oneof=replace regex substring"`
|
Type string `form:"filter_type" yaml:"filter_type" binding:"required" validate:"oneof=replace regex substring"`
|
||||||
From string `form:"from" yaml:"from" binding:"required"`
|
From string `form:"from" yaml:"from" binding:"required"`
|
||||||
To string `form:"to" yaml:"to" binding:"required"`
|
To string `form:"to" yaml:"to" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
63
scraping.go
63
scraping.go
|
@ -2,7 +2,9 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -11,43 +13,68 @@ import (
|
||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getQueryResult(query *Query) []string {
|
func getGroupResult(group *FilterGroup) []string {
|
||||||
doc, err := htmlquery.LoadURL(query.URL.URL)
|
resp, err := http.Get(group.URL.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("Something went wrong loading loading", query.URL.URL)
|
log.Print("Something went wrong loading", group.URL.URL)
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
nodes, _ := htmlquery.QueryAll(doc, query.Query)
|
defer resp.Body.Close()
|
||||||
nodeStrings := make([]string, len(nodes))
|
html, err := ioutil.ReadAll(resp.Body)
|
||||||
for i, node := range nodes {
|
if err != nil {
|
||||||
var b bytes.Buffer
|
log.Print("Something went wrong loading ", group.URL.URL)
|
||||||
html.Render(&b, node)
|
return []string{}
|
||||||
nodeStrings[i] = html.UnescapeString(b.String())
|
|
||||||
}
|
}
|
||||||
for _, filter := range query.Filters {
|
resultStrings := []string{string(html)}
|
||||||
for i, nodeString := range nodeStrings {
|
newStrings := []string{}
|
||||||
nodeStrings[i] = getFilterResult(nodeString, &filter)
|
for _, filter := range group.Filters {
|
||||||
|
for _, resultString := range resultStrings {
|
||||||
|
getFilterResult(resultString, &filter, &newStrings)
|
||||||
}
|
}
|
||||||
|
resultStrings = newStrings
|
||||||
|
log.Println(resultStrings)
|
||||||
}
|
}
|
||||||
return nodeStrings
|
return resultStrings
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFilterResult(s string, filter *Filter) string {
|
func getFilterResult(s string, filter *Filter, newStrings *[]string) {
|
||||||
switch {
|
switch {
|
||||||
|
case filter.Type == "css":
|
||||||
|
{
|
||||||
|
//getFilterResultReplace(s, filter, newStrings)
|
||||||
|
}
|
||||||
|
case filter.Type == "xpath":
|
||||||
|
{
|
||||||
|
getFilterResultXPath(s, filter, newStrings)
|
||||||
|
}
|
||||||
case filter.Type == "replace":
|
case filter.Type == "replace":
|
||||||
{
|
{
|
||||||
return getFilterResultReplace(s, filter)
|
//getFilterResultReplace(s, filter, newStrings)
|
||||||
}
|
}
|
||||||
case filter.Type == "regex":
|
case filter.Type == "regex":
|
||||||
{
|
{
|
||||||
return getFilterResultRegex(s, filter)
|
//getFilterResultRegex(s, filter, newStrings)
|
||||||
}
|
}
|
||||||
case filter.Type == "substring":
|
case filter.Type == "substring":
|
||||||
{
|
{
|
||||||
return getFilterResultSubstring(s, filter)
|
//getFilterResultSubstring(s, filter, newStrings)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return s
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFilterResultXPath(s string, filter *Filter, newStrings *[]string) {
|
||||||
|
doc, err := htmlquery.Parse(strings.NewReader(s))
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodes, _ := htmlquery.QueryAll(doc, filter.From)
|
||||||
|
for _, node := range nodes {
|
||||||
|
var b bytes.Buffer
|
||||||
|
html.Render(&b, node)
|
||||||
|
*newStrings = append(*newStrings, html.UnescapeString(b.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="card mb-5">
|
<div class="card mb-5">
|
||||||
<div class="card-header text-center text-white bg-dark ">
|
<div class="card-header text-center text-white bg-dark ">
|
||||||
<div class="h2">{{ .Query.URL.Watch.Name }} - {{ .Query.Name }}</div>
|
<div class="h2">{{ .Group.URL.Watch.Name }} - {{ .Group.Name }}</div>
|
||||||
<div class="h6">{{ .Query.URL.URL }}</div>
|
<div class="h6">{{ .Group.URL.URL }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-header text-white bg-secondary">
|
<div class="card-header text-white bg-secondary">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
{{ .Query.Type }}
|
{{ .Group.Type }}
|
||||||
</div>
|
|
||||||
<div class="col-8">
|
|
||||||
{{ .Query.Query }}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#updateQueryModal">
|
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#updateGroupModal">
|
||||||
Update
|
Update
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,17 +19,22 @@
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="card mb-2">
|
<div class="card mb-2">
|
||||||
<form action="/filter/create" method="post">
|
<form action="/filter/create" method="post">
|
||||||
<input type="hidden" name="query_id" value="{{ .ID }}" >
|
<input type="hidden" name="group_id" value="{{ .ID }}" >
|
||||||
<input type="hidden" name="watch_id" value="{{ $.ID }}" >
|
<input type="hidden" name="watch_id" value="{{ $.ID }}" >
|
||||||
<table class="table table-hover caption-top">
|
<table class="table table-hover caption-top">
|
||||||
<tbody>
|
<tbody>
|
||||||
{{ range .Query.Filters }}
|
{{ range .Group.Filters }}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ .Name }}</td>
|
<td>{{ .Name }}</td>
|
||||||
<td>{{ .Type }}</td>
|
<td>{{ .Type }}</td>
|
||||||
<td>{{ .From }}</td>
|
<td>{{ .From }}</td>
|
||||||
<td>{{ .To }}</td>
|
<td>{{ .To }}</td>
|
||||||
<td></td>
|
<td>
|
||||||
|
<form action="/filter/delete" method="post">
|
||||||
|
<input type="hidden" name="filter_id" value="{{ .ID }}">
|
||||||
|
<input type="submit" class="btn btn-sm btn-danger" value="X">
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -68,31 +70,28 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
<div class="modal fade" id="updateQueryModal" tabindex="-1" aria-labelledby="updateQueryModal" aria-hidden="true">
|
<div class="modal fade" id="updateGroupModal" tabindex="-1" aria-labelledby="updateGroupModal" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="updateQueryModal">Update Query</h5>
|
<h5 class="modal-title" id="updateGroupModal">Update Group</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form action="/query/update" method="post">
|
<form action="/group/update" method="post">
|
||||||
<input type="hidden" name="query_id" value="{{ .Query.ID }}" >
|
<input type="hidden" name="group_id" value="{{ .Group.ID }}" >
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="mb-3 m-3 row">
|
<div class="mb-3 m-3 row">
|
||||||
<input type="text" name="query_name" value="{{ .Query.Name }}" class="form-control">
|
<input type="text" name="group_name" value="{{ .Group.Name }}" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 m-3 row">
|
<div class="mb-3 m-3 row">
|
||||||
<select name="query_type" class="form-control" >
|
<select name="group_type" class="form-control" >
|
||||||
<option value="css" {{ if eq .Query.Type "css" }} selected="true" {{ end }}>CSS</option>
|
<option value="diff" {{ if eq .Group.Type "diff" }} selected="true" {{ end }}>Diff</option>
|
||||||
<option value="xpath" {{ if eq .Query.Type "xpath" }} selected="true" {{ end }}>XPath</option>
|
<option value="enum" {{ if eq .Group.Type "enum" }} selected="true" {{ end }}>Enum</option>
|
||||||
<option value="regex" {{ if eq .Query.Type "regex" }} selected="true" {{ end }}>Regex</option>
|
<option value="number" {{ if eq .Group.Type "number" }} selected="true" {{ end }}>Number</option>
|
||||||
<option value="json" {{ if eq .Query.Type "json" }} selected="true" {{ end }}>JSON</option>
|
<option value="bool" {{ if eq .Group.Type "bool" }} selected="true" {{ end }}>Boolean</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 m-3 row">
|
|
||||||
<input type="text" name="query" class="form-control" value="{{ .Query.Query }}">
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-primary">Update</button>
|
<button class="btn btn-primary">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -113,21 +112,65 @@
|
||||||
|
|
||||||
<ul class="nav nav-tabs" id="newFilterTabs" role="tablist">
|
<ul class="nav nav-tabs" id="newFilterTabs" role="tablist">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link active" id="replace-tab" data-bs-toggle="tab" data-bs-target="#replace-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">Replace</button>
|
<button class="nav-link active" id="xpath-tab" data-bs-toggle="tab" data-bs-target="#xpath-tab-pane" aria-controls="xpath-tab-pane" type="button" role="tab" aria-selected="true">XPath</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link" id="regex-tab" data-bs-toggle="tab" data-bs-target="#regex-tab-pane" type="button" role="tab" aria-controls="profile-tab-pane" aria-selected="false">Regex</button>
|
<button class="nav-link" id="css-tab" data-bs-toggle="tab" data-bs-target="#css-tab-pane" aria-controls="css-tab-pane" type="button" role="tab" aria-selected="false">CSS</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link" id="substring-tab" data-bs-toggle="tab" data-bs-target="#substring-tab-pane" type="button" role="tab" aria-controls="contact-tab-pane" aria-selected="false">Substring</button>
|
<button class="nav-link" id="replace-tab" data-bs-toggle="tab" data-bs-target="#replace-tab-pane" aria-controls="replace-tab-pane" type="button" role="tab" aria-selected="false">Replace</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="regex-tab" data-bs-toggle="tab" data-bs-target="#regex-tab-pane" aria-controls="regex-tab-pane" type="button" role="tab" aria-selected="false">Regex</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="substring-tab" data-bs-toggle="tab" data-bs-target="#substring-tab-pane" aria-controls="substring-tab-pane" type="button" role="tab" aria-selected="false">Substring</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content" id="newFilterTabsContent">
|
<div class="tab-content" id="newFilterTabsContent">
|
||||||
<div class="tab-pane fade show active" id="replace-tab-pane" role="tabpanel" aria-labelledby="replace-tab" tabindex="0">
|
<div class="tab-pane fade show active" id="xpath-tab-pane" role="tabpanel" aria-labelledby="xpath-tab" tabindex="0">
|
||||||
|
<form action="/filter/create" method="post">
|
||||||
|
<div class="mb-3 m-3 row">
|
||||||
|
<input type="hidden" name="filter_group_id" value="{{ .Group.ID }}" >
|
||||||
|
<input type="hidden" name="filter_type" value="xpath" >
|
||||||
|
<input type="hidden" name="to" value="-" >
|
||||||
|
|
||||||
|
<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="fromInput" class="col-sm-2 col-form-label">XPath:</label>
|
||||||
|
<div class="col-sm-10 p-2">
|
||||||
|
<input type="text" class="form-control" name="from" id="fromInput" placeholder="//a[@class='price']">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary mt-4">Add Filter</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="css-tab-pane" role="tabpanel" aria-labelledby="css-tab" tabindex="0">
|
||||||
|
<form action="/filter/create" method="post">
|
||||||
|
<div class="mb-3 m-3 row">
|
||||||
|
<input type="hidden" name="filter_group_id" value="{{ .Group.ID }}" >
|
||||||
|
<input type="hidden" name="filter_type" value="css" >
|
||||||
|
<input type="hidden" name="to" value="-" >
|
||||||
|
|
||||||
|
<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="fromInput" class="col-sm-2 col-form-label">Selector:</label>
|
||||||
|
<div class="col-sm-10 p-2">
|
||||||
|
<input type="text" class="form-control" name="from" id="fromInput" placeholder=".price">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary mt-4">Add Filter</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="replace-tab-pane" role="tabpanel" aria-labelledby="replace-tab" tabindex="0">
|
||||||
|
|
||||||
<form action="/filter/create" method="post">
|
<form action="/filter/create" method="post">
|
||||||
<div class="mb-3 m-3 row">
|
<div class="mb-3 m-3 row">
|
||||||
<input type="hidden" name="filter_query_id" value="{{ .Query.ID }}" >
|
<input type="hidden" name="filter_group_id" value="{{ .Group.ID }}" >
|
||||||
<input type="hidden" name="filter_type" value="replace" >
|
<input type="hidden" name="filter_type" value="replace" >
|
||||||
|
|
||||||
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
|
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
|
||||||
|
@ -149,7 +192,7 @@
|
||||||
<div class="tab-pane fade" id="regex-tab-pane" role="tabpanel" aria-labelledby="regex-tab" tabindex="0">
|
<div class="tab-pane fade" id="regex-tab-pane" role="tabpanel" aria-labelledby="regex-tab" tabindex="0">
|
||||||
<form action="/filter/create" method="post">
|
<form action="/filter/create" method="post">
|
||||||
<div class="mb-3 m-3 row">
|
<div class="mb-3 m-3 row">
|
||||||
<input type="hidden" name="filter_query_id" value="{{ .Query.ID }}" >
|
<input type="hidden" name="filter_group_id" value="{{ .Group.ID }}" >
|
||||||
<input type="hidden" name="filter_type" value="regex" >
|
<input type="hidden" name="filter_type" value="regex" >
|
||||||
|
|
||||||
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
|
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
|
||||||
|
@ -171,7 +214,7 @@
|
||||||
<div class="tab-pane fade" id="substring-tab-pane" role="tabpanel" aria-labelledby="substring-tab" tabindex="0">
|
<div class="tab-pane fade" id="substring-tab-pane" role="tabpanel" aria-labelledby="substring-tab" tabindex="0">
|
||||||
<form action="/filter/create" method="post">
|
<form action="/filter/create" method="post">
|
||||||
<div class="mb-3 m-3 row">
|
<div class="mb-3 m-3 row">
|
||||||
<input type="hidden" name="filter_query_id" value="{{ .Query.ID }}" >
|
<input type="hidden" name="filter_group_id" value="{{ .Group.ID }}" >
|
||||||
<input type="hidden" name="filter_type" value="substring" >
|
<input type="hidden" name="filter_type" value="substring" >
|
||||||
<input type="hidden" name="to" value="-" >
|
<input type="hidden" name="to" value="-" >
|
||||||
|
|
|
@ -18,20 +18,19 @@
|
||||||
<div>{{ .URL }}</div>
|
<div>{{ .URL }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{{ range .Queries }}
|
{{ range .GroupFilters }}
|
||||||
<div class="card mb-2">
|
<div class="card mb-2">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col h5 text-start">{{ .Type }}</div>
|
<div class="col h4 text-start" >{{ .Name }}</div>
|
||||||
<div class="col h4 text-center" >{{ .Name }}</div>
|
<div class="col h5 text-end"><a class="btn btn-success btn-sm" href="/group/edit/{{ .ID }}">Edit</a></div>
|
||||||
<div class="col h5 text-end"><a class="btn btn-success btn-sm" href="/query/edit/{{ .ID }}">Edit</a></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center text-muted">
|
<div class="text-center text-muted">
|
||||||
{{ .Query }}
|
{{ .Type }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<input type="hidden" name="query_id" value="{{ .ID }}" >
|
<input type="hidden" name="group_id" value="{{ .ID }}" >
|
||||||
<input type="hidden" name="w_id" value="{{ $.ID }}" >
|
<input type="hidden" name="w_id" value="{{ $.ID }}" >
|
||||||
<table class="table table-hover caption-top">
|
<table class="table table-hover caption-top">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -53,34 +52,28 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<form action="/query/create" method="post">
|
<form action="/group/create" method="post">
|
||||||
<input type="hidden" name="query_url_id" value="{{ .ID }}" >
|
<input type="hidden" name="group_url_id" value="{{ .ID }}" >
|
||||||
<input type="hidden" name="w_id" value="{{ $.ID }}" >
|
<input type="hidden" name="w_id" value="{{ $.ID }}" >
|
||||||
<table class="table table-hover caption-top">
|
<table class="table table-hover caption-top">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control" name="query_name" placeholder="Query Name">
|
<input type="text" class="form-control" name="group_name" placeholder="Group Name">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select class="form-control" id="query_type" name="query_type">
|
<select class="form-control" id="group_type" name="group_type">
|
||||||
<option value="css">CSS Selector</option>
|
<option value="diff">Difference</option>
|
||||||
<option value="xpath">XPath</option>
|
<option value="enum">Enum</option>
|
||||||
<option value="regex">Regex</option>
|
<option value="number">Number</option>
|
||||||
<option value="json">JSON</option>
|
<option value="bool">Boolean</option>
|
||||||
<!-- additions/changes should also be added to Query.Type oneof validator -->
|
<!-- additions/changes should also be added to GroupFilter.Type oneof validator -->
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control" name="query" placeholder="Query">
|
<button class="btn btn-primary">Add Group</button>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<button class="btn btn-primary">Add Query</button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
8
util.go
8
util.go
|
@ -17,8 +17,8 @@ func bindAndValidateURL(url *URL, c *gin.Context) (map[string]string, error) {
|
||||||
return validate(err), err
|
return validate(err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindAndValidateQuery(query *Query, c *gin.Context) (map[string]string, error) {
|
func bindAndValidateGroup(group *FilterGroup, c *gin.Context) (map[string]string, error) {
|
||||||
err := c.ShouldBind(query)
|
err := c.ShouldBind(group)
|
||||||
return validate(err), err
|
return validate(err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ func bindAndValidateFilter(filter *Filter, c *gin.Context) (map[string]string, e
|
||||||
return validate(err), err
|
return validate(err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindAndValidateQueryUpdate(query *QueryUpdate, c *gin.Context) (map[string]string, error) {
|
func bindAndValidateGroupUpdate(group *FilterGroupUpdate, c *gin.Context) (map[string]string, error) {
|
||||||
err := c.ShouldBind(query)
|
err := c.ShouldBind(group)
|
||||||
return validate(err), err
|
return validate(err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue