mirror of
https://github.com/adnanh/webhook.git
synced 2025-05-14 17:44:42 +00:00
commit
ec42679305
4 changed files with 72 additions and 11 deletions
|
@ -1,4 +1,4 @@
|
||||||
[](https://ghit.me/repo/adnanh/webhook)[](https://gitter.im/adnanh/webhook?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://flattr.com/submit/auto?user_id=adnanh&url=https%3A%2F%2Fwww.github.com%2Fadnanh%2Fwebhook)
|
[](https://ghit.me/repo/adnanh/webhook)[](https://gitter.im/adnanh/webhook?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://flattr.com/submit/auto?user_id=adnanh&url=https%3A%2F%2Fwww.github.com%2Fadnanh%2Fwebhook)
|
||||||
|
|
||||||
# What is webhook?
|
# What is webhook?
|
||||||
[webhook](https://github.com/adnanh/webhook/) is a lightweight configurable tool written in Go, that allows you to easily create HTTP endpoints (hooks) on your server, which you can use to execute configured commands. You can also pass data from the HTTP request (such as headers, payload or query variables) to your commands. [webhook](https://github.com/adnanh/webhook/) also allows you to specify rules which have to be satisfied in order for the hook to be triggered.
|
[webhook](https://github.com/adnanh/webhook/) is a lightweight configurable tool written in Go, that allows you to easily create HTTP endpoints (hooks) on your server, which you can use to execute configured commands. You can also pass data from the HTTP request (such as headers, payload or query variables) to your commands. [webhook](https://github.com/adnanh/webhook/) also allows you to specify rules which have to be satisfied in order for the hook to be triggered.
|
||||||
|
@ -59,6 +59,9 @@ However, hook defined like that could pose a security threat to your system, bec
|
||||||
# Using HTTPS
|
# Using HTTPS
|
||||||
[webhook](https://github.com/adnanh/webhook/) by default serves hooks using http. If you want [webhook](https://github.com/adnanh/webhook/) to serve secure content using https, you can use the `-secure` flag while starting [webhook](https://github.com/adnanh/webhook/). Files containing a certificate and matching private key for the server must be provided using the `-cert /path/to/cert.pem` and `-key /path/to/key.pem` flags. If the certificate is signed by a certificate authority, the cert file should be the concatenation of the server's certificate followed by the CA's certificate.
|
[webhook](https://github.com/adnanh/webhook/) by default serves hooks using http. If you want [webhook](https://github.com/adnanh/webhook/) to serve secure content using https, you can use the `-secure` flag while starting [webhook](https://github.com/adnanh/webhook/). Files containing a certificate and matching private key for the server must be provided using the `-cert /path/to/cert.pem` and `-key /path/to/key.pem` flags. If the certificate is signed by a certificate authority, the cert file should be the concatenation of the server's certificate followed by the CA's certificate.
|
||||||
|
|
||||||
|
# CORS Headers
|
||||||
|
If you want to set CORS headers, you can use the `-header name=value` flag while starting [webhook](https://github.com/adnanh/webhook/) to set the appropriate CORS headers that will be returned with each response.
|
||||||
|
|
||||||
# Interested in running webhook inside of a Docker container?
|
# Interested in running webhook inside of a Docker container?
|
||||||
You can use [almir/webhook](https://hub.docker.com/r/almir/webhook/) docker image, or create your own (please read [this discussion](https://github.com/adnanh/webhook/issues/63)).
|
You can use [almir/webhook](https://hub.docker.com/r/almir/webhook/) docker image, or create your own (please read [this discussion](https://github.com/adnanh/webhook/issues/63)).
|
||||||
|
|
||||||
|
|
56
hook/hook.go
56
hook/hook.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -246,17 +247,54 @@ func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Header is a structure containing header name and it's value
|
||||||
|
type Header struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseHeaders is a slice of Header objects
|
||||||
|
type ResponseHeaders []Header
|
||||||
|
|
||||||
|
func (h *ResponseHeaders) String() string {
|
||||||
|
// a 'hack' to display name=value in flag usage listing
|
||||||
|
if len(*h) == 0 {
|
||||||
|
return "name=value"
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]string, len(*h))
|
||||||
|
|
||||||
|
for idx, responseHeader := range *h {
|
||||||
|
result[idx] = fmt.Sprintf("%s=%s", responseHeader.Name, responseHeader.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprint(strings.Join(result, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set method appends new Header object from header=value notation
|
||||||
|
func (h *ResponseHeaders) Set(value string) error {
|
||||||
|
splitResult := strings.SplitN(value, "=", 2)
|
||||||
|
|
||||||
|
if len(splitResult) != 2 {
|
||||||
|
return errors.New("header flag must be in name=value format")
|
||||||
|
}
|
||||||
|
|
||||||
|
*h = append(*h, Header{Name: splitResult[0], Value: splitResult[1]})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Hook type is a structure containing details for a single hook
|
// Hook type is a structure containing details for a single hook
|
||||||
type Hook struct {
|
type Hook struct {
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
ExecuteCommand string `json:"execute-command,omitempty"`
|
ExecuteCommand string `json:"execute-command,omitempty"`
|
||||||
CommandWorkingDirectory string `json:"command-working-directory,omitempty"`
|
CommandWorkingDirectory string `json:"command-working-directory,omitempty"`
|
||||||
ResponseMessage string `json:"response-message,omitempty"`
|
ResponseMessage string `json:"response-message,omitempty"`
|
||||||
CaptureCommandOutput bool `json:"include-command-output-in-response,omitempty"`
|
ResponseHeaders ResponseHeaders `json:"response-headers,omitempty"`
|
||||||
PassEnvironmentToCommand []Argument `json:"pass-environment-to-command,omitempty"`
|
CaptureCommandOutput bool `json:"include-command-output-in-response,omitempty"`
|
||||||
PassArgumentsToCommand []Argument `json:"pass-arguments-to-command,omitempty"`
|
PassEnvironmentToCommand []Argument `json:"pass-environment-to-command,omitempty"`
|
||||||
JSONStringParameters []Argument `json:"parse-parameters-as-json,omitempty"`
|
PassArgumentsToCommand []Argument `json:"pass-arguments-to-command,omitempty"`
|
||||||
TriggerRule *Rules `json:"trigger-rule,omitempty"`
|
JSONStringParameters []Argument `json:"parse-parameters-as-json,omitempty"`
|
||||||
|
TriggerRule *Rules `json:"trigger-rule,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
|
// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
|
||||||
|
|
|
@ -4,6 +4,13 @@
|
||||||
"execute-command": "/home/adnan/redeploy-go-webhook.sh",
|
"execute-command": "/home/adnan/redeploy-go-webhook.sh",
|
||||||
"command-working-directory": "/home/adnan/go",
|
"command-working-directory": "/home/adnan/go",
|
||||||
"response-message": "I got the payload!",
|
"response-message": "I got the payload!",
|
||||||
|
"response-headers":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Access-Control-Allow-Origin",
|
||||||
|
"value": "*"
|
||||||
|
}
|
||||||
|
],
|
||||||
"pass-arguments-to-command":
|
"pass-arguments-to-command":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|
15
webhook.go
15
webhook.go
|
@ -21,7 +21,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
version = "2.3.7"
|
version = "2.3.8"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -36,6 +36,8 @@ var (
|
||||||
cert = flag.String("cert", "cert.pem", "path to the HTTPS certificate pem file")
|
cert = flag.String("cert", "cert.pem", "path to the HTTPS certificate pem file")
|
||||||
key = flag.String("key", "key.pem", "path to the HTTPS certificate private key pem file")
|
key = flag.String("key", "key.pem", "path to the HTTPS certificate private key pem file")
|
||||||
|
|
||||||
|
responseHeaders hook.ResponseHeaders
|
||||||
|
|
||||||
watcher *fsnotify.Watcher
|
watcher *fsnotify.Watcher
|
||||||
signals chan os.Signal
|
signals chan os.Signal
|
||||||
|
|
||||||
|
@ -45,6 +47,8 @@ var (
|
||||||
func main() {
|
func main() {
|
||||||
hooks = hook.Hooks{}
|
hooks = hook.Hooks{}
|
||||||
|
|
||||||
|
flag.Var(&responseHeaders, "header", "response header to return, specified in format name=value, use multiple times to set multiple headers")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
log.SetPrefix("[webhook] ")
|
log.SetPrefix("[webhook] ")
|
||||||
|
@ -137,6 +141,10 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func hookHandler(w http.ResponseWriter, r *http.Request) {
|
func hookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
for _, responseHeader := range responseHeaders {
|
||||||
|
w.Header().Set(responseHeader.Name, responseHeader.Value)
|
||||||
|
}
|
||||||
|
|
||||||
id := mux.Vars(r)["id"]
|
id := mux.Vars(r)["id"]
|
||||||
|
|
||||||
matchedHooks := hooks.MatchAll(id)
|
matchedHooks := hooks.MatchAll(id)
|
||||||
|
@ -180,6 +188,7 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// handle hook
|
// handle hook
|
||||||
for _, h := range matchedHooks {
|
for _, h := range matchedHooks {
|
||||||
|
|
||||||
err := h.ParseJSONParameters(&headers, &query, &payload)
|
err := h.ParseJSONParameters(&headers, &query, &payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("error parsing JSON: %s", err)
|
msg := fmt.Sprintf("error parsing JSON: %s", err)
|
||||||
|
@ -207,6 +216,10 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if ok {
|
if ok {
|
||||||
log.Printf("%s hook triggered successfully\n", h.ID)
|
log.Printf("%s hook triggered successfully\n", h.ID)
|
||||||
|
|
||||||
|
for _, responseHeader := range h.ResponseHeaders {
|
||||||
|
w.Header().Set(responseHeader.Name, responseHeader.Value)
|
||||||
|
}
|
||||||
|
|
||||||
if h.CaptureCommandOutput {
|
if h.CaptureCommandOutput {
|
||||||
response := handleHook(h, &headers, &query, &payload, &body)
|
response := handleHook(h, &headers, &query, &payload, &body)
|
||||||
fmt.Fprintf(w, response)
|
fmt.Fprintf(w, response)
|
||||||
|
|
Loading…
Add table
Reference in a new issue