mirror of
https://github.com/lus/pasty.git
synced 2023-08-10 21:13:09 +03:00
Implement report webhook support
This commit is contained in:
parent
71477f71f6
commit
ebc7b20617
10
README.md
10
README.md
@ -90,6 +90,16 @@ Pasty provides an intuitive system to automatically delete pastes after a specif
|
|||||||
| `PASTY_AUTODELETE_LIFETIME` | `720h` | `string` | Defines the duration a paste should live until it gets deleted |
|
| `PASTY_AUTODELETE_LIFETIME` | `720h` | `string` | Defines the duration a paste should live until it gets deleted |
|
||||||
| `PASTY_AUTODELETE_TASK_INTERVAL` | `5m` | `string` | Defines the interval in which the AutoDelete task should clean up the database |
|
| `PASTY_AUTODELETE_TASK_INTERVAL` | `5m` | `string` | Defines the interval in which the AutoDelete task should clean up the database |
|
||||||
|
|
||||||
|
## Reports
|
||||||
|
Pasty aims at being lightweight by default. This is why no fully-featured admin interface with an overview over all pastes and reports is included.
|
||||||
|
However, pasty does include a way of abstract reports to allow frontends work with this information.
|
||||||
|
If enabled, pasty makes a standardized request to the configured webhook URL if a paste is reported.
|
||||||
|
| Environment Variable | Default Value | Type | Description |
|
||||||
|
|------------------------------|---------------|----------|-----------------------------------------------------------------------------------------------------|
|
||||||
|
| `PASTY_REPORTS` | `false` | `bool` | Defines whether or not the report system should be enabled |
|
||||||
|
| `PASTY_REPORT_WEBHOOK` | `<empty>` | `string` | Defines the webhook URL that is called whenever a paste is reported |
|
||||||
|
| `PASTY_REPORT_WEBHOOK_TOKEN` | `<empty>` | `string` | Defines the token that is sent in the `Authorization` header on every request to the report webhook |
|
||||||
|
|
||||||
## Storage types
|
## Storage types
|
||||||
Pasty supports multiple storage types, defined using the `PASTY_STORAGE_TYPE` environment variable (use the value behind the corresponding title in this README).
|
Pasty supports multiple storage types, defined using the `PASTY_STORAGE_TYPE` environment variable (use the value behind the corresponding title in this README).
|
||||||
Every single one of them has its own configuration variables:
|
Every single one of them has its own configuration variables:
|
||||||
|
@ -20,6 +20,7 @@ type Config struct {
|
|||||||
RateLimit string
|
RateLimit string
|
||||||
LengthCap int
|
LengthCap int
|
||||||
AutoDelete *AutoDeleteConfig
|
AutoDelete *AutoDeleteConfig
|
||||||
|
Reports *ReportConfig
|
||||||
File *FileConfig
|
File *FileConfig
|
||||||
Postgres *PostgresConfig
|
Postgres *PostgresConfig
|
||||||
MongoDB *MongoDBConfig
|
MongoDB *MongoDBConfig
|
||||||
@ -61,6 +62,13 @@ type S3Config struct {
|
|||||||
Bucket string
|
Bucket string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReportConfig represents the configuration specific for the report system
|
||||||
|
type ReportConfig struct {
|
||||||
|
Reports bool
|
||||||
|
ReportWebhook string
|
||||||
|
ReportWebhookToken string
|
||||||
|
}
|
||||||
|
|
||||||
// Current holds the currently loaded config
|
// Current holds the currently loaded config
|
||||||
var Current *Config
|
var Current *Config
|
||||||
|
|
||||||
@ -83,6 +91,11 @@ func Load() {
|
|||||||
Lifetime: env.MustDuration("AUTODELETE_LIFETIME", 720*time.Hour),
|
Lifetime: env.MustDuration("AUTODELETE_LIFETIME", 720*time.Hour),
|
||||||
TaskInterval: env.MustDuration("AUTODELETE_TASK_INTERVAL", 5*time.Minute),
|
TaskInterval: env.MustDuration("AUTODELETE_TASK_INTERVAL", 5*time.Minute),
|
||||||
},
|
},
|
||||||
|
Reports: &ReportConfig{
|
||||||
|
Reports: env.MustBool("REPORTS", false),
|
||||||
|
ReportWebhook: env.MustString("REPORT_WEBHOOK", ""),
|
||||||
|
ReportWebhookToken: env.MustString("REPORT_WEBHOOK_TOKEN", ""),
|
||||||
|
},
|
||||||
File: &FileConfig{
|
File: &FileConfig{
|
||||||
Path: env.MustString("STORAGE_FILE_PATH", "./data"),
|
Path: env.MustString("STORAGE_FILE_PATH", "./data"),
|
||||||
},
|
},
|
||||||
|
57
internal/report/report.go
Normal file
57
internal/report/report.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package report
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/lus/pasty/internal/config"
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReportRequest represents a report request sent to the report webhook
|
||||||
|
type ReportRequest struct {
|
||||||
|
Paste string `json:"paste"`
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportResponse represents a report response received from the report webhook
|
||||||
|
type ReportResponse struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendReport sends a report request to the report webhook
|
||||||
|
func SendReport(reportRequest *ReportRequest) (*ReportResponse, error) {
|
||||||
|
request := fasthttp.AcquireRequest()
|
||||||
|
defer fasthttp.ReleaseRequest(request)
|
||||||
|
|
||||||
|
response := fasthttp.AcquireResponse()
|
||||||
|
defer fasthttp.ReleaseResponse(response)
|
||||||
|
|
||||||
|
request.Header.SetMethod(fasthttp.MethodPost)
|
||||||
|
request.SetRequestURI(config.Current.Reports.ReportWebhook)
|
||||||
|
if config.Current.Reports.ReportWebhookToken != "" {
|
||||||
|
request.Header.Set("Authorization", "Bearer "+config.Current.Reports.ReportWebhookToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(reportRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
request.SetBody(data)
|
||||||
|
|
||||||
|
if err := fasthttp.Do(request, response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
status := response.StatusCode()
|
||||||
|
if status < 200 || status > 299 {
|
||||||
|
return nil, fmt.Errorf("the report webhook responded with an unexpected error: %d (%s)", status, string(response.Body()))
|
||||||
|
}
|
||||||
|
|
||||||
|
reportResponse := new(ReportResponse)
|
||||||
|
if err := json.Unmarshal(response.Body(), reportResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return reportResponse, nil
|
||||||
|
}
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fasthttp/router"
|
"github.com/fasthttp/router"
|
||||||
"github.com/lus/pasty/internal/config"
|
"github.com/lus/pasty/internal/config"
|
||||||
|
"github.com/lus/pasty/internal/report"
|
||||||
"github.com/lus/pasty/internal/shared"
|
"github.com/lus/pasty/internal/shared"
|
||||||
"github.com/lus/pasty/internal/storage"
|
"github.com/lus/pasty/internal/storage"
|
||||||
"github.com/lus/pasty/internal/utils"
|
"github.com/lus/pasty/internal/utils"
|
||||||
@ -21,6 +22,10 @@ func InitializePastesController(group *router.Group, rateLimiterMiddleware *limi
|
|||||||
group.POST("/", rateLimiterMiddleware.Handle(endpointCreatePaste))
|
group.POST("/", rateLimiterMiddleware.Handle(endpointCreatePaste))
|
||||||
group.PATCH("/{id}", rateLimiterMiddleware.Handle(middlewareInjectPaste(middlewareValidateModificationToken(endpointModifyPaste))))
|
group.PATCH("/{id}", rateLimiterMiddleware.Handle(middlewareInjectPaste(middlewareValidateModificationToken(endpointModifyPaste))))
|
||||||
group.DELETE("/{id}", rateLimiterMiddleware.Handle(middlewareInjectPaste(middlewareValidateModificationToken(endpointDeletePaste))))
|
group.DELETE("/{id}", rateLimiterMiddleware.Handle(middlewareInjectPaste(middlewareValidateModificationToken(endpointDeletePaste))))
|
||||||
|
|
||||||
|
if config.Current.Reports.Reports {
|
||||||
|
group.POST("/{id}/report", rateLimiterMiddleware.Handle(middlewareInjectPaste(endpointReportPaste)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// middlewareInjectPaste retrieves and injects the paste with the specified ID
|
// middlewareInjectPaste retrieves and injects the paste with the specified ID
|
||||||
@ -230,3 +235,42 @@ func endpointDeletePaste(ctx *fasthttp.RequestCtx) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type endpointReportPastePayload struct {
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func endpointReportPaste(ctx *fasthttp.RequestCtx) {
|
||||||
|
// Read, parse and validate the request payload
|
||||||
|
payload := new(endpointReportPastePayload)
|
||||||
|
if err := json.Unmarshal(ctx.PostBody(), payload); err != nil {
|
||||||
|
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||||
|
ctx.SetBodyString(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if payload.Reason == "" {
|
||||||
|
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
||||||
|
ctx.SetBodyString("missing report reason")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
request := &report.ReportRequest{
|
||||||
|
Paste: ctx.UserValue("_paste").(*shared.Paste).ID,
|
||||||
|
Reason: payload.Reason,
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
response, err := report.SendReport(request)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||||
|
ctx.SetBodyString(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||||
|
ctx.SetBodyString(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.SetBody(jsonData)
|
||||||
|
}
|
||||||
|
@ -75,6 +75,7 @@ func Serve() error {
|
|||||||
jsonData, _ := json.Marshal(map[string]interface{}{
|
jsonData, _ := json.Marshal(map[string]interface{}{
|
||||||
"version": static.Version,
|
"version": static.Version,
|
||||||
"modificationTokens": config.Current.ModificationTokens,
|
"modificationTokens": config.Current.ModificationTokens,
|
||||||
|
"reports": config.Current.Reports,
|
||||||
})
|
})
|
||||||
ctx.SetBody(jsonData)
|
ctx.SetBody(jsonData)
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user