added insomnia collection and implemented rules API

pull/1/head
Adam Veldhousen 3 years ago
parent 9cb717cf5c
commit 36ad084621
Signed by: adam
GPG Key ID: 6DB29003C6DD1E4B

File diff suppressed because one or more lines are too long

@ -34,7 +34,7 @@ func NewAdminHandler(c Cache, s Storage, re *RuleEngine) http.Handler {
handler.Use(middleware.RequestID)
handler.Use(middleware.RealIP)
handler.Use(middleware.Logger)
handler.Use(middleware.AllowContentType("application/json; utf-8"))
handler.Use(middleware.AllowContentType("application/json; utf-8", "application/json"))
handler.Use(middleware.Timeout(time.Second * 10))
handler.Use(cors.Handler(cors.Options{
@ -50,16 +50,17 @@ func NewAdminHandler(c Cache, s Storage, re *RuleEngine) http.Handler {
handler.Route("/api/v1", func(r chi.Router) {
r.Get("/metrics/log", RestHandler(a.getLog).ToHF())
r.Get("/metrics/stats", RestHandler(a.getStats).ToHF())
// r.Delete("/cache/purgeall", RestHandler(a.purgeAll).ToHF())
// r.Delete("/cache/purge", a.purgeKey)
// r.Get("/cache", a.getCacheContents)
// r.Put("/rules", a.createRule)
// r.Get("/rules", a.getRules)
// r.Delete("/rules/{id}", a.deleteRole)
r.Get("/rules", RestHandler(a.getRules).ToHF())
r.Put("/rules", RestHandler(a.createRule).ToHF())
r.Delete("/rules/{id:[0-9]+}", RestHandler(a.deleteRule).ToHF())
// r.Put("/rules/lists", a.addRulelist)
// r.Get("/rules/lists", a.getRuleLists)
// r.Delete("/rules/lists/{id}", a.deleteRuleList)
// r.Post("/rules/lists/reload", a.reloadRuleLists)
// r.Post("/rules/lists/reload/{id}", a.reloadRuleLists)
// r.Delete("/cache/purgeall", RestHandler(a.purgeAll).ToHF())
// r.Delete("/cache/purge", a.purgeKey)
// r.Get("/cache", a.getCacheContents)
})
return a
@ -70,6 +71,102 @@ func (a *adminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.h.ServeHTTP(w, r)
}
func (a *adminHandler) deleteRule(r *http.Request) (*RestResponse, error) {
ruleIdParam := chi.URLParam(r, "id")
ruleId, err := strconv.Atoi(ruleIdParam)
if err != nil {
return &RestResponse{
Payload: struct {
Success bool "json:\"success\""
Payload interface{} "json:\"payload\""
}{
Success: false,
Payload: "Invalid rule ID",
},
}, nil
}
if err := a.Storage.DeleteRule(ruleId); err != nil {
return &RestResponse{
Payload: struct {
Success bool "json:\"success\""
Payload interface{} "json:\"payload\""
}{
Success: false,
Payload: err.Error(),
},
}, nil
}
return &RestResponse{
Payload: struct {
Success bool "json:\"success\""
Payload interface{} "json:\"payload\""
}{
Success: true,
Payload: ruleId,
},
}, nil
}
func (a *adminHandler) createRule(r *http.Request) (*RestResponse, error) {
var rr RuleRow
if err := json.NewDecoder(r.Body).Decode(&rr); err != nil {
return &RestResponse{
Status: http.StatusUnprocessableEntity,
Payload: struct {
Success bool "json:\"success\""
Payload interface{} "json:\"payload\""
}{
Success: false,
Payload: err.Error(),
},
}, nil
}
if err := a.Storage.AddRule(rr); err != nil {
return &RestResponse{
Payload: struct {
Success bool "json:\"success\""
Payload interface{} "json:\"payload\""
}{
Success: false,
Payload: err.Error(),
},
}, nil
}
return &RestResponse{
Payload: struct {
Success bool "json:\"success\""
Payload interface{} "json:\"payload\""
}{
Success: true,
},
}, nil
}
func (a *adminHandler) getRules(r *http.Request) (*RestResponse, error) {
results, err := a.Storage.GetRules()
if err != nil {
return nil, err
}
if len(results) <= 0 {
results = []RuleRow{}
}
return &RestResponse{
Payload: struct {
Success bool "json:\"success\""
Payload interface{} "json:\"payload\""
}{
Success: true,
Payload: results,
},
}, nil
}
func (a *adminHandler) getStats(r *http.Request) (*RestResponse, error) {
q := r.URL.Query()
startFilter := q.Get("start")

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"net"
"time"
_ "github.com/mattn/go-sqlite3"
@ -15,12 +16,14 @@ const ISO8601 = "2006-01-02 15:04:05.999"
type Storage interface {
io.Closer
Open() error
// AddRecursor() error
// DeleteRecursors(id) error
// GetRecursors(net.IP) ([]string,error)
// AddRule(Rule) error
// DeleteRule(int) error
// GetRules() ([]Rule,error)
AddRecursors(net.IP, int, int, int) error
DeleteRecursors(int) error
GetRecursors() ([]RecursorRow, error)
// UpdateRule(RuleRow) error
AddRule(RuleRow) error
GetRule(int) (RuleRow, error)
GetRules() ([]RuleRow, error)
DeleteRule(int) error
Log(QueryLog) error
GetLog(GetLogInput) ([]QueryLog, error)
GetLogAggregate(LogAggregateInput) ([]LogAggregateDataPoint, error)
@ -31,6 +34,58 @@ type Sqlite struct {
*sql.DB
}
func (ss *Sqlite) GetRecursors() ([]RecursorRow, error) {
sql := `
SELECT id, ipAddress, timeoutMs, weight FROM recursors ORDER BY weight ASC;
`
rows, err := ss.Query(sql)
if err != nil {
return nil, fmt.Errorf("could not execute select for recursors: %w", err)
}
defer rows.Close()
var results []RecursorRow
for rows.Next() {
var row RecursorRow
if err := rows.Scan(&row.ID, &row.IpAddress, &row.TimeoutMs, &row.Weight); err != nil {
return nil, fmt.Errorf("could not read row: %w", err)
}
results = append(results, row)
}
return results, nil
}
func (ss *Sqlite) DeleteRecursors(id int) error {
sql := `DELETE FROM recursors WHERE id = ?;`
if _, err := ss.Exec(sql, id); err != nil {
return fmt.Errorf("Could not delete recursor: %w", err)
}
return nil
}
type RecursorRow struct {
ID int `json:"id"`
IpAddress string `json:"ipAddress"`
TimeoutMs int `json:"timeoutMs"`
Weight int `json:"weight"`
}
func (ss *Sqlite) AddRecursors(ip net.IP, port, timeout, weight int) error {
sql := `INSERT INTO recursors (ipAddress, timeoutMs, weight) VALUES (?, ?, ?);`
if _, err := ss.Exec(sql, fmt.Sprintf("%s:%d", ip.String(), port), timeout, weight); err != nil {
return fmt.Errorf("could not insert recursor: %w", err)
}
return nil
}
type GetLogInput struct {
Start time.Time
End time.Time
@ -39,6 +94,92 @@ type GetLogInput struct {
Page int
}
type RuleRow struct {
ID int `json:"id"`
Weight int `json:"weight"`
Enabled bool `json:"enabled"`
Created time.Time `json:"created"`
Rule
}
func (ss *Sqlite) AddRule(rr RuleRow) error {
sql := `INSERT INTO rules (name, expression, answerType, answerValue, ttl, weight, enabled, created)
VALUES (?, ? , ?, ?, ?, ?, 1, ?);`
if _, err := ss.Exec(sql, rr.Name, rr.Value, rr.Answer.Type, rr.Answer.Value, rr.TTL, rr.Weight, time.Now().UTC().Format(ISO8601)); err != nil {
return fmt.Errorf("could not delete rule: %w", err)
}
return nil
}
func (ss *Sqlite) GetRule(ruleId int) (RuleRow, error) {
sql := `SELECT id, name, expression, answerType, answerValue, ttl, weight, enabled, created FROM rules WHERE id = ?;`
var rr RuleRow
row := ss.QueryRow(sql, ruleId)
var createdTime string
if err := row.Scan(&rr.ID, &rr.Name, &rr.Value, &rr.Answer.Type, &rr.Answer.Value, &rr.TTL, &rr.Weight, &rr.Enabled, &createdTime); err != nil {
return rr, err
}
var err error
rr.Created, err = time.Parse(ISO8601, createdTime)
if err != nil {
return rr, fmt.Errorf("could not parse time: %w", err)
}
return rr, nil
}
func (ss *Sqlite) DeleteRule(ruleId int) error {
if _, err := ss.Exec(`DELETE FROM rules WHERE id = ?;`, ruleId); err != nil {
return fmt.Errorf("could not delete rule: %w", err)
}
return nil
}
func (ss *Sqlite) GetRules() ([]RuleRow, error) {
sql := `SELECT id, name, expression, answerType, answerValue, ttl, weight, enabled, created FROM rules ORDER BY weight ASC;`
rows, err := ss.Query(sql)
if err != nil {
return nil, err
}
defer rows.Close()
var results []RuleRow
for rows.Next() {
var rule RuleRow
var createdTime string
if err := rows.Scan(
&rule.ID,
&rule.Name,
&rule.Value,
&rule.Answer.Type,
&rule.Answer.Value,
&rule.TTL,
&rule.Weight,
&rule.Enabled,
&createdTime,
); err != nil {
return nil, fmt.Errorf("could not read from db: %w", err)
}
rule.Created, err = time.Parse(ISO8601, createdTime)
if err != nil {
return nil, err
}
results = append(results, rule)
}
return results, nil
}
func (ss *Sqlite) GetLog(in GetLogInput) ([]QueryLog, error) {
if in.Limit <= 0 {
in.Limit = 100
@ -67,7 +208,6 @@ func (ss *Sqlite) GetLog(in GetLogInput) ([]QueryLog, error) {
if err != nil {
return nil, err
}
defer rows.Close()
var ql []QueryLog
@ -159,16 +299,17 @@ func (ss *Sqlite) GetLogAggregate(la LogAggregateInput) ([]LogAggregateDataPoint
sql = fmt.Sprintf(sql, column, timeWindow, column, timeWindow)
result, err := ss.Query(sql)
rows, err := ss.Query(sql)
if err != nil {
return nil, err
}
defer rows.Close()
var results []LogAggregateDataPoint
for result.Next() {
for rows.Next() {
var ladp LogAggregateDataPoint
var timeInterval int64
if err := result.Scan(
if err := rows.Scan(
&ladp.Header,
&ladp.AverageTotalTime,
&ladp.Count,
@ -244,6 +385,28 @@ func initTable(db *sql.DB) error {
recurseUpStreamIP TEXT,
status TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS rules (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
expression TEXT NOT NULL,
answerType TEXT NOT NULL,
answerValue TEXT NOT NULL,
ttl INT NOT NULL,
weight INT NOT NULL,
enabled INT NOT NULL,
created TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_rules_name ON rules (name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_rules_expression ON rules (expression);
CREATE TABLE IF NOT EXISTS recursors (
id INTEGER PRIMARY KEY,
ipAddress TEXT NOT NULL,
timeoutMs INT NOT NULL,
weight INT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_recursors_ipAddress ON recursors (ipAddress);
`
if _, err := db.Exec(sql); err != nil {

Loading…
Cancel
Save