Skip to content

Commit

Permalink
document webhooks (#24)
Browse files Browse the repository at this point in the history
implement webhooks to receive updates about specific documents
  • Loading branch information
topi314 authored Dec 8, 2023
1 parent 4046c48 commit b3d9cea
Show file tree
Hide file tree
Showing 22 changed files with 1,030 additions and 363 deletions.
184 changes: 181 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ gobin is a simple lightweight haste-server alternative written in Go, HTML, JS a
- [Update a document](#update-a-document)
- [Delete a document](#delete-a-document)
- [Delete a document version](#delete-a-document-version)
- [Document webhooks](#document-webhooks)
- [Create a document webhook](#create-a-document-webhook)
- [Update a document webhook](#update-a-document-webhook)
- [Delete a document webhook](#delete-a-document-webhook)
- [Other endpoints](#other-endpoints)
- [Errors](#errors)
- [License](#license)
Expand All @@ -46,6 +50,7 @@ gobin is a simple lightweight haste-server alternative written in Go, HTML, JS a
- Easy to deploy and use
- Built-in rate-limiting
- Create, update and delete documents
- Document update/delete webhooks
- Syntax highlighting
- Social Media PNG previews
- Document expiration
Expand All @@ -65,7 +70,7 @@ The easiest way to deploy gobin is using docker with [Docker Compose](https://do

Create a new `docker-compose.yml` file with the following content:

> **Note**
> [!Note]
> You should change the password in the `docker-compose.yml` and `gobin.json` file.
```yaml
Expand Down Expand Up @@ -139,7 +144,7 @@ The database schema is automatically created when you start gobin and there is n

Create a new `gobin.json` file with the following content:

> **Note**
> [!Note]
> Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".

```json5
Expand Down Expand Up @@ -223,6 +228,19 @@ Create a new `gobin.json` file with the following content:
"listen_addr": ":9100"
}
},
// settings for webhooks, omit to disable
"webhook": {
// webhook reqauest timeout
"timeout": "10s",
// max number of tries to send a webhook
"max_tries": 3,
// how long to wait before retrying a webhook
"backoff": "1s",
// how much the backoff should be increased after each retry
"backoff_factor": 2,
// max backoff time
"max_backoff": "5m"
},
// load custom chroma xml or base16 yaml themes from this directory, omit to disable
"custom_styles": "custom_styles",
"default_style": "snazzy"
Expand Down Expand Up @@ -269,6 +287,12 @@ GOBIN_PREVIEW_DPI=96
GOBIN_PREVIEW_CACHE_SIZE=1024
GOBIN_PREVIEW_CACHE_TTL=1h
GOBIN_WEBHOOK_TIMEOUT=10s
GOBIN_WEBHOOK_MAX_TRIES=3
GOBIN_WEBHOOK_BACKOFF=1s
GOBIN_WEBHOOK_BACKOFF_FACTOR=2
GOBIN_WEBHOOK_MAX_BACKOFF=5m
GOBIN_CUSTOM_STYLES=custom_styles
GOBIN_DEFAULT_STYLE=snazzy
```
Expand Down Expand Up @@ -509,7 +533,7 @@ func main() {

A successful request will return a `200 OK` response with a JSON body containing the document key and token to update the document.

> **Note**
> [!Note]
> The update token will not change after updating the document. You can use the same token to update the document again.
```json5
Expand Down Expand Up @@ -566,6 +590,160 @@ A successful request will return a `204 No Content` response with an empty body.

---

### Document webhooks

You can listen for document changes using webhooks. The webhook will send a `POST` request to the specified url with the following JSON body:

```json5
{
// the id of the webhook
"webhook_id": "hocwr6i6",
// the event which triggered the webhook (update or delete)
"event": "update",
// when the event was created
"created_at": "2021-08-01T12:00:00Z",
// the updated or deleted document
"document": {
// the key of the document
"key": "hocwr6i6",
// the version of the document
"version": 2,
// the language of the document
"language": "go",
// the content of the document
"data": "package main\n\nfunc main() {\n println(\"Hello World Updated!\")\n}"
}
}
```

Gobin will include the webhook secret in the `Authorization` header in the following format: `Secret {secret}`.

When sending an event to a webhook fails gobin will retry it up to x times with an exponential backoff. The retry settings can be configured in the config file.
When an event fails to be sent after x retries, the webhook will be dropped.

> [!Important]
> Authorizing for the following webhook endpoints is done using the `Authorization` header in the following format: `Secret {secret}`.
#### Create a document webhook

To create a webhook you have to send a `POST` request to `/documents/{key}/webhooks` with the following JSON body:

```json5
{
// the url to send a request to
"url": "https://example.com/webhook",
// the secret to include in the request
"secret": "secret",
// the events you want to receive
"events": [
// update event is sent when a document is updated. This includes content and language changes
"update",
// delete event is sent when a document is deleted
"delete"
]
}
```

A successful request will return a `200 OK` response with a JSON body containing the webhook.

```json5
{
// the id of the webhook
"id": 1,
// the url to send a request to
"url": "https://example.com/webhook",
// the secret to include in the request
"secret": "secret",
// the events you want to receive
"events": [
// update event is sent when a document is updated. This includes content and language changes
"update",
// delete event is sent when a document is deleted
"delete"
]
}
```

---

#### Get a document webhook

To get a webhook you have to send a `GET` request to `/documents/{key}/webhooks/{id}` with the `Authorization` header.

A successful request will return a `200 OK` response with a JSON body containing the webhook.

```json5
{
// the id of the webhook
"id": 1,
// the url to send a request to
"url": "https://example.com/webhook",
// the secret to include in the request
"secret": "secret",
// the events you want to receive
"events": [
// update event is sent when a document is updated. This includes content and language changes
"update",
// delete event is sent when a document is deleted
"delete"
]
}
```

---

#### Update a document webhook

To update a webhook you have to send a `PATCH` request to `/documents/{key}/webhooks/{id}` with the `Authorization` header and the following JSON body:

> [!Note]
> All fields are optional, but at least one field is required.
```json5
{
// the url to send a request to
"url": "https://example.com/webhook",
// the secret to include in the request
"secret": "secret",
// the events you want to receive
"events": [
// update event is sent when a document is updated. This includes content and language changes
"update",
// delete event is sent when a document is deleted
"delete"
]
}
```

A successful request will return a `200 OK` response with a JSON body containing the webhook.

```json5
{
// the id of the webhook
"id": 1,
// the url to send a request to
"url": "https://example.com/webhook",
// the secret to include in the request
"secret": "secret",
// the events you want to receive
"events": [
// update event is sent when a document is updated. This includes content and language changes
"update",
// delete event is sent when a document is deleted
"delete"
]
}
```

---

#### Delete a document webhook

To delete a webhook you have to send a `DELETE` request to `/documents/{key}/webhooks/{id}` with the `Authorization` header.

A successful request will return a `204 No Content` response with an empty body.

---

### Other endpoints

- `GET`/`HEAD` `/{key}/preview` - Get the preview of a document, query parameters are the same as for `GET /documents/{key}`
Expand Down
21 changes: 21 additions & 0 deletions example.gobin.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,40 @@
},
"max_document_size": 0,
"max_highlight_size": 0,
// omit or set values to 0 or "0" to disable rate limit
"rate_limit": {
"requests": 10,
"duration": "1m",
"whitelist": ["127.0.0.1"],
"blacklist": ["123.456.789.0"]
},
// settings for social media previews, omit to disable
"preview": {
"inkscape_path": "inkscape.exe",
"max_lines": 0,
"dpi": 120,
"cache_size": 1024,
"cache_ttl": "1h"
},
// open telemetry settings, omit to disable
"otel": {
"instance_id": "1",
"trace": {
"endpoint": "tempo:4318",
"insecure": true
},
"metrics": {
"listen_addr": ":9100"
}
},
// settings for webhooks, omit to disable
"webhook": {
"timeout": "10s",
"max_tries": 3,
"backoff": "1s",
"backoff_factor": 2,
"max_backoff": "5m"
},
// load custom chroma xml or base16 yaml themes from this directory, omit to disable
"custom_styles": "custom_styles",
"default_style": "snazzy"
Expand Down
42 changes: 22 additions & 20 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ replace (

require (
github.com/XSAM/otelsql v0.26.0
github.com/a-h/templ v0.2.432
github.com/alecthomas/chroma/v2 v2.10.0
github.com/a-h/templ v0.2.476
github.com/alecthomas/chroma/v2 v2.11.1
github.com/dustin/go-humanize v1.0.1
github.com/go-chi/chi/v5 v5.0.10
github.com/go-chi/httprate v0.7.4
github.com/go-chi/stampede v0.5.1
github.com/go-jose/go-jose/v3 v3.0.0
github.com/go-jose/go-jose/v3 v3.0.1
github.com/jackc/pgx/v5 v5.5.0
github.com/jmoiron/sqlx v1.3.5
github.com/mattn/go-colorable v0.1.13
Expand All @@ -27,13 +27,15 @@ require (
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.17.0
github.com/topi314/tint v0.0.0-20231106205902-77268b701ca6
go.opentelemetry.io/otel v1.19.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0
go.opentelemetry.io/otel/exporters/prometheus v0.42.0
go.opentelemetry.io/otel/metric v1.19.0
go.opentelemetry.io/otel/sdk v1.19.0
go.opentelemetry.io/otel/sdk/metric v1.19.0
go.opentelemetry.io/otel/trace v1.19.0
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
go.opentelemetry.io/otel v1.21.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0
go.opentelemetry.io/otel/exporters/prometheus v0.44.0
go.opentelemetry.io/otel/metric v1.21.0
go.opentelemetry.io/otel/sdk v1.21.0
go.opentelemetry.io/otel/sdk/metric v1.21.0
go.opentelemetry.io/otel/trace v1.21.0
modernc.org/sqlite v1.27.0
)

Expand All @@ -48,7 +50,7 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand All @@ -72,28 +74,28 @@ require (
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.opentelemetry.io/contrib v1.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
go.opentelemetry.io/contrib v1.21.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.14.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
golang.org/x/tools v0.15.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.3.0 // indirect
modernc.org/cc/v3 v3.41.0 // indirect
modernc.org/ccgo/v3 v3.16.15 // indirect
modernc.org/libc v1.30.0 // indirect
modernc.org/libc v1.34.4 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/opt v0.1.3 // indirect
Expand Down
Loading

0 comments on commit b3d9cea

Please sign in to comment.