2016-07-07 18:54:16 -07:00
|
|
|
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
|
|
|
//
|
|
|
|
// This software (Documize Community Edition) is licensed under
|
|
|
|
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
|
|
|
//
|
|
|
|
// You can operate outside the AGPL restrictions by purchasing
|
|
|
|
// Documize Enterprise Edition and obtaining a commercial license
|
|
|
|
// by contacting <sales@documize.com>.
|
|
|
|
//
|
|
|
|
// https://documize.com
|
|
|
|
|
|
|
|
package papertrail
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
|
2016-07-20 15:58:37 +01:00
|
|
|
"github.com/documize/community/core/log"
|
2016-10-17 14:07:05 -07:00
|
|
|
"github.com/documize/community/core/section/provider"
|
2016-07-07 18:54:16 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
const me = "papertrail"
|
|
|
|
|
|
|
|
// Provider represents Gemini
|
|
|
|
type Provider struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Meta describes us.
|
|
|
|
func (*Provider) Meta() provider.TypeMeta {
|
|
|
|
section := provider.TypeMeta{}
|
|
|
|
section.ID = "db0a3a0a-b5d4-4d00-bfac-ee28abba451d"
|
|
|
|
section.Title = "Papertrail"
|
|
|
|
section.Description = "Display log entries"
|
|
|
|
section.ContentType = "papertrail"
|
|
|
|
|
|
|
|
return section
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render converts Papertrail data into HTML suitable for browser rendering.
|
|
|
|
func (*Provider) Render(ctx *provider.Context, config, data string) string {
|
|
|
|
var search papertrailSearch
|
|
|
|
var events []papertrailEvent
|
|
|
|
var payload = papertrailRender{}
|
|
|
|
var c = papertrailConfig{}
|
|
|
|
|
|
|
|
json.Unmarshal([]byte(data), &search)
|
|
|
|
json.Unmarshal([]byte(config), &c)
|
|
|
|
|
|
|
|
c.APIToken = ctx.GetSecrets("APIToken")
|
|
|
|
|
|
|
|
max := len(search.Events)
|
|
|
|
if c.Max < max {
|
|
|
|
max = c.Max
|
|
|
|
}
|
|
|
|
|
|
|
|
events = search.Events[:max]
|
|
|
|
payload.Count = len(events)
|
|
|
|
payload.HasData = payload.Count > 0
|
|
|
|
|
|
|
|
payload.Events = events
|
|
|
|
payload.Config = c
|
|
|
|
payload.Authenticated = c.APIToken != ""
|
|
|
|
|
|
|
|
t := template.New("items")
|
|
|
|
t, _ = t.Parse(renderTemplate)
|
|
|
|
|
|
|
|
buffer := new(bytes.Buffer)
|
|
|
|
t.Execute(buffer, payload)
|
|
|
|
|
|
|
|
return buffer.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Command handles authentication, workspace listing and items retrieval.
|
|
|
|
func (p *Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
|
|
|
|
query := r.URL.Query()
|
|
|
|
method := query.Get("method")
|
|
|
|
|
|
|
|
if len(method) == 0 {
|
|
|
|
provider.WriteMessage(w, me, "missing method name")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
defer r.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(r.Body)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
provider.WriteMessage(w, me, "Bad payload")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var config = papertrailConfig{}
|
|
|
|
err = json.Unmarshal(body, &config)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
provider.WriteMessage(w, me, "Bad config")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
config.Clean()
|
|
|
|
|
2016-07-08 12:29:53 +01:00
|
|
|
if config.APIToken == provider.SecretReplacement || config.APIToken == "" {
|
2016-07-07 18:54:16 -07:00
|
|
|
config.APIToken = ctx.GetSecrets("APIToken")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(config.APIToken) == 0 {
|
|
|
|
provider.WriteMessage(w, me, "Missing API token")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch method {
|
|
|
|
case "auth":
|
|
|
|
auth(ctx, config, w, r)
|
|
|
|
case "options":
|
|
|
|
options(config, w, r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Refresh just sends back data as-is.
|
|
|
|
func (*Provider) Refresh(ctx *provider.Context, config, data string) (newData string) {
|
|
|
|
var c = papertrailConfig{}
|
|
|
|
err := json.Unmarshal([]byte(config), &c)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error("unable to read Papertrail config", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Clean()
|
|
|
|
|
|
|
|
c.APIToken = ctx.GetSecrets("APIToken")
|
|
|
|
|
|
|
|
if len(c.APIToken) == 0 {
|
|
|
|
log.Error("missing API token", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := fetchEvents(c)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Papertrail fetchEvents failed", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
j, err := json.Marshal(result)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error("unable to marshal Papaertrail events", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
newData = string(j)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func auth(ctx *provider.Context, config papertrailConfig, w http.ResponseWriter, r *http.Request) {
|
|
|
|
result, err := fetchEvents(config)
|
|
|
|
|
|
|
|
if result == nil {
|
|
|
|
err = errors.New("nil result of papertrail query")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
log.IfErr(ctx.SaveSecrets(`{"APIToken":""}`)) // invalid token, so reset it
|
|
|
|
|
|
|
|
if err.Error() == "forbidden" {
|
|
|
|
provider.WriteForbidden(w)
|
|
|
|
} else {
|
|
|
|
provider.WriteError(w, me, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
log.IfErr(ctx.SaveSecrets(`{"APIToken":"` + config.APIToken + `"}`))
|
|
|
|
|
|
|
|
provider.WriteJSON(w, result)
|
|
|
|
}
|
|
|
|
|
|
|
|
func options(config papertrailConfig, w http.ResponseWriter, r *http.Request) {
|
|
|
|
// get systems
|
|
|
|
req, err := http.NewRequest("GET", "https://papertrailapp.com/api/v1/systems.json", nil)
|
|
|
|
req.Header.Set("X-Papertrail-Token", config.APIToken)
|
|
|
|
|
|
|
|
client := &http.Client{}
|
|
|
|
res, err := client.Do(req)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
provider.WriteError(w, me, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
|
|
provider.WriteForbidden(w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
defer res.Body.Close()
|
|
|
|
var systems []papertrailOption
|
|
|
|
|
|
|
|
dec := json.NewDecoder(res.Body)
|
|
|
|
err = dec.Decode(&systems)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
provider.WriteError(w, me, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// get groups
|
|
|
|
req, err = http.NewRequest("GET", "https://papertrailapp.com/api/v1/groups.json", nil)
|
|
|
|
req.Header.Set("X-Papertrail-Token", config.APIToken)
|
|
|
|
|
|
|
|
client = &http.Client{}
|
|
|
|
res, err = client.Do(req)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
provider.WriteError(w, me, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
|
|
provider.WriteForbidden(w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
defer res.Body.Close()
|
|
|
|
var groups []papertrailOption
|
|
|
|
|
|
|
|
dec = json.NewDecoder(res.Body)
|
|
|
|
err = dec.Decode(&groups)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
provider.WriteError(w, me, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var options = papertrailOptions{}
|
|
|
|
options.Groups = groups
|
|
|
|
options.Systems = systems
|
|
|
|
|
|
|
|
provider.WriteJSON(w, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchEvents(config papertrailConfig) (result interface{}, err error) {
|
|
|
|
var filter string
|
|
|
|
if len(config.Query) > 0 {
|
|
|
|
filter = fmt.Sprintf("q=%s", url.QueryEscape(config.Query))
|
|
|
|
}
|
|
|
|
if config.Group.ID > 0 {
|
|
|
|
prefix := ""
|
|
|
|
if len(filter) > 0 {
|
|
|
|
prefix = "&"
|
|
|
|
}
|
|
|
|
filter = fmt.Sprintf("%s%sgroup_id=%d", filter, prefix, config.Group.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
var req *http.Request
|
|
|
|
req, err = http.NewRequest("GET", "https://papertrailapp.com/api/v1/events/search.json?"+filter, nil)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("new request", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
req.Header.Set("X-Papertrail-Token", config.APIToken)
|
|
|
|
|
|
|
|
client := &http.Client{}
|
|
|
|
var res *http.Response
|
|
|
|
res, err = client.Do(req)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error("message", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
|
|
log.Error("forbidden", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
defer res.Body.Close()
|
|
|
|
|
|
|
|
dec := json.NewDecoder(res.Body)
|
|
|
|
err = dec.Decode(&result)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error("unable to read result", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|