parent
943da8807b
commit
1c43e4479a
@ -0,0 +1,31 @@
|
||||
FROM node:lts-alpine as build-client
|
||||
|
||||
COPY . /build/
|
||||
|
||||
RUN apk add --no-cache make
|
||||
|
||||
RUN make build-client
|
||||
|
||||
FROM golang:alpine as build-server
|
||||
|
||||
COPY --from=build-client /build /build
|
||||
|
||||
RUN apk add --no-cache make
|
||||
|
||||
RUN make .bin/gopherhole
|
||||
|
||||
|
||||
FROM alpine
|
||||
|
||||
RUN apl add --no-cache ca-certificates
|
||||
|
||||
RUN addgroup -g 1000 gopherhole \
|
||||
&& adduser -H -D -u 1000 gopherhole gopherhole
|
||||
|
||||
COPY --chown=gopherhole:gopherhole --from=build-server /build/.bin/gopherhole /opt/gopherhole
|
||||
|
||||
USER gopherhole
|
||||
|
||||
VOLUME "/data"
|
||||
|
||||
ENTRYPOINT /opt/gopherhole
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -0,0 +1,26 @@
|
||||
package internal
|
||||
|
||||
import "context"
|
||||
|
||||
var (
|
||||
LogUpdated = EventType("LOG_UPDATE")
|
||||
DNSLog = EventSource("DNS_LOG")
|
||||
)
|
||||
|
||||
type EventType string
|
||||
|
||||
type EventSource string
|
||||
|
||||
type Event struct {
|
||||
Type EventType `json:"type"`
|
||||
Source EventSource `json:"source"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
type EventPublisher interface {
|
||||
Publish(Event)
|
||||
}
|
||||
|
||||
type EventSubscriber interface {
|
||||
Subscribe(context.Context) <-chan Event
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (a *adminHandler) addRecursor(r *http.Request) (*RestResponse, error) {
|
||||
var recursorHttpInput RecursorRow
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&recursorHttpInput); err != nil {
|
||||
return ErrorResponse(http.StatusUnprocessableEntity, err), nil
|
||||
}
|
||||
|
||||
ipAddr, port, ok := recursorHttpInput.ValidIp()
|
||||
if !ok {
|
||||
return BasicResponse(false, "The ip address provided is invalid."), nil
|
||||
}
|
||||
|
||||
if err := a.Storage.AddRecursors(ipAddr, port, recursorHttpInput.TimeoutMs, recursorHttpInput.Weight); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return BasicResponse(true, recursorHttpInput), nil
|
||||
}
|
||||
|
||||
func (a *adminHandler) getRecursors(r *http.Request) (*RestResponse, error) {
|
||||
recursors, err := a.Storage.GetRecursors()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return BasicResponse(true, recursors), nil
|
||||
}
|
||||
|
||||
func (a *adminHandler) getRecursor(r *http.Request) (*RestResponse, error) {
|
||||
return BasicResponse(false, "Nothing here yet"), nil
|
||||
}
|
||||
|
||||
func (a *adminHandler) deleteRecursor(r *http.Request) (*RestResponse, error) {
|
||||
return BasicResponse(false, "Nothing here yet"), nil
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
func (a *adminHandler) createRule(r *http.Request) (*RestResponse, error) {
|
||||
var rr RuleRow
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&rr); err != nil {
|
||||
return ErrorResponse(http.StatusUnprocessableEntity, err), nil
|
||||
}
|
||||
|
||||
if err := a.Storage.AddRule(rr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return BasicResponse(true, nil), nil
|
||||
}
|
||||
|
||||
func (a *adminHandler) getRule(r *http.Request) (*RestResponse, error) {
|
||||
ruleIdParam := chi.URLParam(r, "id")
|
||||
ruleId, err := strconv.Atoi(ruleIdParam)
|
||||
if err != nil {
|
||||
return BasicResponse(false, "`id` must be a valid integer."), nil
|
||||
}
|
||||
|
||||
results, err := a.Storage.GetRule(ruleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return BasicResponse(true, results), 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 BasicResponse(true, results), nil
|
||||
}
|
||||
|
||||
func (a *adminHandler) deleteRule(r *http.Request) (*RestResponse, error) {
|
||||
ruleIdParam := chi.URLParam(r, "id")
|
||||
ruleId, err := strconv.Atoi(ruleIdParam)
|
||||
if err != nil {
|
||||
return BasicResponse(false, "`id` must be a valid integer"), nil
|
||||
}
|
||||
|
||||
if err := a.Storage.DeleteRule(ruleId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rule, err := a.Storage.GetRule(ruleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return BasicResponse(true, rule), nil
|
||||
}
|
||||
|
||||
func (a *adminHandler) updateRule(r *http.Request) (*RestResponse, error) {
|
||||
var rr RuleRow
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&rr); err != nil {
|
||||
return ErrorResponse(http.StatusUnprocessableEntity, err), nil
|
||||
}
|
||||
|
||||
if err := a.UpdateRule(rr.ID, rr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rule, err := a.GetRule(rr.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return BasicResponse(true, rule), nil
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (a *adminHandler) getStats(r *http.Request) (*RestResponse, error) {
|
||||
q := r.URL.Query()
|
||||
startFilter := q.Get("start")
|
||||
endFilter := q.Get("end")
|
||||
key := q.Get("key")
|
||||
intervalSecondsStr := q.Get("interval")
|
||||
|
||||
var err error
|
||||
start := time.Now().UTC().Add(time.Hour * -86400)
|
||||
end := time.Now().UTC()
|
||||
|
||||
if startFilter != "" {
|
||||
if start, err = time.Parse(ISO8601, startFilter); err != nil {
|
||||
return BasicResponse(false, fmt.Sprintf("start: could not understand value `%v`", err.Error())), nil
|
||||
}
|
||||
}
|
||||
|
||||
if endFilter != "" {
|
||||
if end, err = time.Parse(ISO8601, endFilter); err != nil {
|
||||
return BasicResponse(false, fmt.Sprintf("end: could not understand value `%v`", err.Error())), nil
|
||||
}
|
||||
}
|
||||
|
||||
lai := LogAggregateInput{
|
||||
Start: start,
|
||||
End: end,
|
||||
Column: key,
|
||||
}
|
||||
|
||||
if intervalSecondsStr != "" {
|
||||
if lai.IntervalSeconds, err = strconv.Atoi(intervalSecondsStr); err != nil {
|
||||
return BasicResponse(false, "interval query param must be a valid whole number"), nil
|
||||
}
|
||||
}
|
||||
|
||||
la, err := a.Storage.GetLogAggregate(lai)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return BasicResponse(true, la), nil
|
||||
|
||||
}
|
||||
|
||||
type LogFilter struct {
|
||||
Expression string
|
||||
}
|
||||
|
||||
func (a *adminHandler) getLog(r *http.Request) (*RestResponse, error) {
|
||||
q := r.URL.Query()
|
||||
startFilter := q.Get("start")
|
||||
endFilter := q.Get("end")
|
||||
// filter := LogFilter{Expression: q.Get("filter")}
|
||||
pageStr := q.Get("page")
|
||||
|
||||
var err error
|
||||
var page int
|
||||
start := time.Now().UTC().Add(time.Hour * -86400)
|
||||
end := time.Now().UTC()
|
||||
|
||||
if startFilter != "" {
|
||||
if start, err = time.Parse(ISO8601, startFilter); err != nil {
|
||||
return BasicResponse(false, "start: not a valid start time"), nil
|
||||
}
|
||||
}
|
||||
|
||||
if endFilter != "" {
|
||||
if end, err = time.Parse(ISO8601, endFilter); err != nil {
|
||||
return BasicResponse(false, "end: not a valid end time"), nil
|
||||
}
|
||||
}
|
||||
|
||||
if pageStr != "" {
|
||||
if page, err = strconv.Atoi(pageStr); err != nil {
|
||||
return BasicResponse(false, "page: must be a valid integer"), nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
gli := GetLogInput{
|
||||
// Filter: filter,
|
||||
Start: start,
|
||||
End: end,
|
||||
Limit: 250,
|
||||
Page: page,
|
||||
}
|
||||
|
||||
// if err := gli.Validate(); err != nil {
|
||||
// return BasicResponse(false, err.Error()), nil
|
||||
// }
|
||||
|
||||
ql, err := a.Storage.GetLog(gli)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return BasicResponse(true, ql), nil
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/middleware"
|
||||
)
|
||||
|
||||
type RestResponse struct {
|
||||
Status int
|
||||
Headers http.Header
|
||||
Payload struct {
|
||||
Success bool `json:"success"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
}
|
||||
|
||||
func BasicResponse(success bool, model interface{}) *RestResponse {
|
||||
return &RestResponse{
|
||||
Status: http.StatusOK,
|
||||
Payload: struct {
|
||||
Success bool "json:\"success\""
|
||||
Payload interface{} "json:\"payload\""
|
||||
}{Success: success, Payload: model},
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorResponse(status int, err error) *RestResponse {
|
||||
return &RestResponse{
|
||||
Status: status,
|
||||
Payload: struct {
|
||||
Success bool "json:\"success\""
|
||||
Payload interface{} "json:\"payload\""
|
||||
}{
|
||||
Success: false,
|
||||
Payload: err.Error(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (rr *RestResponse) Write(w http.ResponseWriter) error {
|
||||
if rr.Status != 0 && rr.Status != 200 {
|
||||
w.WriteHeader(rr.Status)
|
||||
}
|
||||
|
||||
for k, v := range rr.Headers {
|
||||
for _, ve := range v {
|
||||
w.Header().Add(k, ve)
|
||||
}
|
||||
}
|
||||
|
||||
e := json.NewEncoder(w)
|
||||
e.SetIndent("\n", "\t")
|
||||
|
||||
if err := e.Encode(rr.Payload); err != nil {
|
||||
return fmt.Errorf("could not serialize struct for http response: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type RestHandler func(request *http.Request) (*RestResponse, error)
|
||||
|
||||
func (rh RestHandler) ToHF() http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
rid := r.Context().Value(middleware.RequestIDKey)
|
||||
rw.Header().Set(middleware.RequestIDHeader, rid.(string))
|
||||
rh.ServeHTTP(rw, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (rh RestHandler) Error(e error) *RestResponse {
|
||||
return &RestResponse{
|
||||
Status: http.StatusInternalServerError,
|
||||
Payload: struct {
|
||||
Success bool `json:"success"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}{
|
||||
Success: false,
|
||||
Payload: e.Error(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r RestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
response, err := r(req)
|
||||
if err != nil {
|
||||
response = r.Error(err)
|
||||
}
|
||||
|
||||
if err := response.Write(w); err != nil {
|
||||
log.Printf("Error occurred handling rest response: %v", err)
|
||||
}
|
||||
}
|
Loading…
Reference in new issue