feat: webhooker 1.0 MVP — entity rename, core engine, delivery, management UI #16
14
internal/database/model_entrypoint.go
Normal file
14
internal/database/model_entrypoint.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package database
|
||||
|
||||
// Entrypoint represents an inbound URL endpoint that feeds into a webhook
|
||||
type Entrypoint struct {
|
||||
BaseModel
|
||||
|
||||
WebhookID string `gorm:"type:uuid;not null" json:"webhook_id"`
|
||||
Path string `gorm:"uniqueIndex;not null" json:"path"` // URL path for this entrypoint
|
||||
Description string `json:"description"`
|
||||
Active bool `gorm:"default:true" json:"active"`
|
||||
|
||||
// Relations
|
||||
Webhook Webhook `json:"webhook,omitempty"`
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package database
|
||||
|
||||
// Event represents a webhook event
|
||||
// Event represents a captured webhook event
|
||||
type Event struct {
|
||||
BaseModel
|
||||
|
||||
ProcessorID string `gorm:"type:uuid;not null" json:"processor_id"`
|
||||
WebhookID string `gorm:"type:uuid;not null" json:"webhook_id"`
|
||||
WebhookID string `gorm:"type:uuid;not null" json:"webhook_id"`
|
||||
EntrypointID string `gorm:"type:uuid;not null" json:"entrypoint_id"`
|
||||
|
||||
// Request data
|
||||
Method string `gorm:"not null" json:"method"`
|
||||
@@ -14,7 +14,7 @@ type Event struct {
|
||||
ContentType string `json:"content_type"`
|
||||
|
||||
// Relations
|
||||
Processor Processor `json:"processor,omitempty"`
|
||||
Webhook Webhook `json:"webhook,omitempty"`
|
||||
Entrypoint Entrypoint `json:"entrypoint,omitempty"`
|
||||
Deliveries []Delivery `json:"deliveries,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package database
|
||||
|
||||
// Processor represents an event processor
|
||||
type Processor struct {
|
||||
BaseModel
|
||||
|
||||
UserID string `gorm:"type:uuid;not null" json:"user_id"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Description string `json:"description"`
|
||||
RetentionDays int `gorm:"default:30" json:"retention_days"` // Days to retain events
|
||||
|
||||
// Relations
|
||||
User User `json:"user,omitempty"`
|
||||
Webhooks []Webhook `json:"webhooks,omitempty"`
|
||||
Targets []Target `json:"targets,omitempty"`
|
||||
}
|
||||
@@ -10,14 +10,14 @@ const (
|
||||
TargetTypeLog TargetType = "log"
|
||||
)
|
||||
|
||||
// Target represents a delivery target for a processor
|
||||
// Target represents a delivery target for a webhook
|
||||
type Target struct {
|
||||
BaseModel
|
||||
|
||||
ProcessorID string `gorm:"type:uuid;not null" json:"processor_id"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Type TargetType `gorm:"not null" json:"type"`
|
||||
Active bool `gorm:"default:true" json:"active"`
|
||||
WebhookID string `gorm:"type:uuid;not null" json:"webhook_id"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Type TargetType `gorm:"not null" json:"type"`
|
||||
Active bool `gorm:"default:true" json:"active"`
|
||||
|
||||
// Configuration fields (JSON stored based on type)
|
||||
Config string `gorm:"type:text" json:"config"` // JSON configuration
|
||||
@@ -27,6 +27,6 @@ type Target struct {
|
||||
MaxQueueSize int `json:"max_queue_size,omitempty"`
|
||||
|
||||
// Relations
|
||||
Processor Processor `json:"processor,omitempty"`
|
||||
Webhook Webhook `json:"webhook,omitempty"`
|
||||
Deliveries []Delivery `json:"deliveries,omitempty"`
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ type User struct {
|
||||
Password string `gorm:"not null" json:"-"` // Argon2 hashed
|
||||
|
||||
// Relations
|
||||
Processors []Processor `json:"processors,omitempty"`
|
||||
APIKeys []APIKey `json:"api_keys,omitempty"`
|
||||
Webhooks []Webhook `json:"webhooks,omitempty"`
|
||||
APIKeys []APIKey `json:"api_keys,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package database
|
||||
|
||||
// Webhook represents a webhook endpoint that feeds into a processor
|
||||
// Webhook represents a webhook processing unit that groups entrypoints and targets
|
||||
type Webhook struct {
|
||||
BaseModel
|
||||
|
||||
ProcessorID string `gorm:"type:uuid;not null" json:"processor_id"`
|
||||
Path string `gorm:"uniqueIndex;not null" json:"path"` // URL path for this webhook
|
||||
Description string `json:"description"`
|
||||
Active bool `gorm:"default:true" json:"active"`
|
||||
UserID string `gorm:"type:uuid;not null" json:"user_id"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Description string `json:"description"`
|
||||
RetentionDays int `gorm:"default:30" json:"retention_days"` // Days to retain events
|
||||
|
||||
// Relations
|
||||
Processor Processor `json:"processor,omitempty"`
|
||||
User User `json:"user,omitempty"`
|
||||
Entrypoints []Entrypoint `json:"entrypoints,omitempty"`
|
||||
Targets []Target `json:"targets,omitempty"`
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ func (d *Database) Migrate() error {
|
||||
return d.db.AutoMigrate(
|
||||
&User{},
|
||||
&APIKey{},
|
||||
&Processor{},
|
||||
&Webhook{},
|
||||
&Entrypoint{},
|
||||
&Target{},
|
||||
&Event{},
|
||||
&Delivery{},
|
||||
|
||||
@@ -4,66 +4,66 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HandleSourceList shows a list of user's webhook sources
|
||||
// HandleSourceList shows a list of user's webhooks
|
||||
func (h *Handlers) HandleSourceList() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Implement source list page
|
||||
// TODO: Implement webhook list page
|
||||
http.Error(w, "Not implemented", http.StatusNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSourceCreate shows the form to create a new webhook source
|
||||
// HandleSourceCreate shows the form to create a new webhook
|
||||
func (h *Handlers) HandleSourceCreate() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Implement source creation form
|
||||
// TODO: Implement webhook creation form
|
||||
http.Error(w, "Not implemented", http.StatusNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSourceCreateSubmit handles the source creation form submission
|
||||
// HandleSourceCreateSubmit handles the webhook creation form submission
|
||||
func (h *Handlers) HandleSourceCreateSubmit() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Implement source creation logic
|
||||
// TODO: Implement webhook creation logic
|
||||
http.Error(w, "Not implemented", http.StatusNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSourceDetail shows details for a specific webhook source
|
||||
// HandleSourceDetail shows details for a specific webhook
|
||||
func (h *Handlers) HandleSourceDetail() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Implement source detail page
|
||||
// TODO: Implement webhook detail page
|
||||
http.Error(w, "Not implemented", http.StatusNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSourceEdit shows the form to edit a webhook source
|
||||
// HandleSourceEdit shows the form to edit a webhook
|
||||
func (h *Handlers) HandleSourceEdit() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Implement source edit form
|
||||
// TODO: Implement webhook edit form
|
||||
http.Error(w, "Not implemented", http.StatusNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSourceEditSubmit handles the source edit form submission
|
||||
// HandleSourceEditSubmit handles the webhook edit form submission
|
||||
func (h *Handlers) HandleSourceEditSubmit() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Implement source update logic
|
||||
// TODO: Implement webhook update logic
|
||||
http.Error(w, "Not implemented", http.StatusNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSourceDelete handles webhook source deletion
|
||||
// HandleSourceDelete handles webhook deletion
|
||||
func (h *Handlers) HandleSourceDelete() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Implement source deletion logic
|
||||
// TODO: Implement webhook deletion logic
|
||||
http.Error(w, "Not implemented", http.StatusNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSourceLogs shows the request/response logs for a webhook source
|
||||
// HandleSourceLogs shows the request/response logs for a webhook
|
||||
func (h *Handlers) HandleSourceLogs() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Implement source logs page
|
||||
// TODO: Implement webhook logs page
|
||||
http.Error(w, "Not implemented", http.StatusNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,19 @@ import (
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// HandleWebhook handles incoming webhook requests
|
||||
// HandleWebhook handles incoming webhook requests at entrypoint URLs
|
||||
func (h *Handlers) HandleWebhook() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Get webhook UUID from URL
|
||||
webhookUUID := chi.URLParam(r, "uuid")
|
||||
if webhookUUID == "" {
|
||||
// Get entrypoint UUID from URL
|
||||
entrypointUUID := chi.URLParam(r, "uuid")
|
||||
if entrypointUUID == "" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Log the incoming webhook request
|
||||
h.log.Info("webhook request received",
|
||||
"uuid", webhookUUID,
|
||||
"entrypoint_uuid", entrypointUUID,
|
||||
"method", r.Method,
|
||||
"remote_addr", r.RemoteAddr,
|
||||
"user_agent", r.UserAgent(),
|
||||
@@ -32,7 +32,7 @@ func (h *Handlers) HandleWebhook() http.HandlerFunc {
|
||||
}
|
||||
|
||||
// TODO: Implement webhook handling logic
|
||||
// For now, return "unimplemented" for all webhook POST requests
|
||||
// Look up entrypoint by UUID, find parent webhook, fan out to targets
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, err := w.Write([]byte("unimplemented"))
|
||||
if err != nil {
|
||||
|
||||
@@ -90,23 +90,23 @@ func (s *Server) SetupRoutes() {
|
||||
r.Get("/", s.h.HandleProfile())
|
||||
})
|
||||
|
||||
// Webhook source management routes (require authentication)
|
||||
// Webhook management routes (require authentication)
|
||||
s.router.Route("/sources", func(r chi.Router) {
|
||||
// TODO: Add authentication middleware here
|
||||
r.Get("/", s.h.HandleSourceList()) // List all sources
|
||||
r.Get("/", s.h.HandleSourceList()) // List all webhooks
|
||||
r.Get("/new", s.h.HandleSourceCreate()) // Show create form
|
||||
r.Post("/new", s.h.HandleSourceCreateSubmit()) // Handle create submission
|
||||
})
|
||||
|
||||
s.router.Route("/source/{sourceID}", func(r chi.Router) {
|
||||
// TODO: Add authentication middleware here
|
||||
r.Get("/", s.h.HandleSourceDetail()) // View source details
|
||||
r.Get("/", s.h.HandleSourceDetail()) // View webhook details
|
||||
r.Get("/edit", s.h.HandleSourceEdit()) // Show edit form
|
||||
r.Post("/edit", s.h.HandleSourceEditSubmit()) // Handle edit submission
|
||||
r.Post("/delete", s.h.HandleSourceDelete()) // Delete source
|
||||
r.Get("/logs", s.h.HandleSourceLogs()) // View source logs
|
||||
r.Post("/delete", s.h.HandleSourceDelete()) // Delete webhook
|
||||
r.Get("/logs", s.h.HandleSourceLogs()) // View webhook logs
|
||||
})
|
||||
|
||||
// Webhook endpoint - accepts all HTTP methods
|
||||
// Entrypoint endpoint - accepts incoming webhook POST requests
|
||||
s.router.HandleFunc("/webhook/{uuid}", s.h.HandleWebhook())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user