Decompose handle-event.go into DDD domain services (v0.36.15)
Some checks failed
Go / build-and-release (push) Has been cancelled
Some checks failed
Go / build-and-release (push) Has been cancelled
Major refactoring of event handling into clean, testable domain services: - Add pkg/event/validation: JSON hex validation, signature verification, timestamp bounds, NIP-70 protected tag validation - Add pkg/event/authorization: Policy and ACL authorization decisions, auth challenge handling, access level determination - Add pkg/event/routing: Event router registry with ephemeral and delete handlers, kind-based dispatch - Add pkg/event/processing: Event persistence, delivery to subscribers, and post-save hooks (ACL reconfig, sync, relay groups) - Reduce handle-event.go from 783 to 296 lines (62% reduction) - Add comprehensive unit tests for all new domain services - Refactor database tests to use shared TestMain setup - Fix blossom URL test expectations (missing "/" separator) - Add go-memory-optimization skill and analysis documentation - Update DDD_ANALYSIS.md to reflect completed decomposition Files modified: - app/handle-event.go: Slim orchestrator using domain services - app/server.go: Service initialization and interface wrappers - app/handle-event-types.go: Shared types (OkHelper, result types) - pkg/event/validation/*: New validation service package - pkg/event/authorization/*: New authorization service package - pkg/event/routing/*: New routing service package - pkg/event/processing/*: New processing service package - pkg/database/*_test.go: Refactored to shared TestMain - pkg/blossom/http_test.go: Fixed URL format expectations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
124
pkg/event/validation/validation.go
Normal file
124
pkg/event/validation/validation.go
Normal file
@@ -0,0 +1,124 @@
|
||||
// Package validation provides event validation services for the ORLY relay.
|
||||
// It handles structural validation (hex case, JSON format), cryptographic
|
||||
// validation (signature, ID), and protocol validation (timestamp, NIP-70).
|
||||
package validation
|
||||
|
||||
import (
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
)
|
||||
|
||||
// ReasonCode identifies the type of validation failure for response formatting.
|
||||
type ReasonCode int
|
||||
|
||||
const (
|
||||
ReasonNone ReasonCode = iota
|
||||
ReasonBlocked
|
||||
ReasonInvalid
|
||||
ReasonError
|
||||
)
|
||||
|
||||
// Result contains the outcome of a validation check.
|
||||
type Result struct {
|
||||
Valid bool
|
||||
Code ReasonCode // For response formatting
|
||||
Msg string // Human-readable error message
|
||||
}
|
||||
|
||||
// OK returns a successful validation result.
|
||||
func OK() Result {
|
||||
return Result{Valid: true}
|
||||
}
|
||||
|
||||
// Blocked returns a blocked validation result.
|
||||
func Blocked(msg string) Result {
|
||||
return Result{Valid: false, Code: ReasonBlocked, Msg: msg}
|
||||
}
|
||||
|
||||
// Invalid returns an invalid validation result.
|
||||
func Invalid(msg string) Result {
|
||||
return Result{Valid: false, Code: ReasonInvalid, Msg: msg}
|
||||
}
|
||||
|
||||
// Error returns an error validation result.
|
||||
func Error(msg string) Result {
|
||||
return Result{Valid: false, Code: ReasonError, Msg: msg}
|
||||
}
|
||||
|
||||
// Validator validates events before processing.
|
||||
type Validator interface {
|
||||
// ValidateRawJSON validates raw message before unmarshaling.
|
||||
// This catches issues like uppercase hex that are lost after unmarshal.
|
||||
ValidateRawJSON(msg []byte) Result
|
||||
|
||||
// ValidateEvent validates an unmarshaled event.
|
||||
// Checks ID computation, signature, and timestamp.
|
||||
ValidateEvent(ev *event.E) Result
|
||||
|
||||
// ValidateProtectedTag checks NIP-70 protected tag requirements.
|
||||
// The authedPubkey is the authenticated pubkey of the connection.
|
||||
ValidateProtectedTag(ev *event.E, authedPubkey []byte) Result
|
||||
}
|
||||
|
||||
// Config holds configuration for the validation service.
|
||||
type Config struct {
|
||||
// MaxFutureSeconds is how far in the future a timestamp can be (default: 3600 = 1 hour)
|
||||
MaxFutureSeconds int64
|
||||
}
|
||||
|
||||
// DefaultConfig returns the default validation configuration.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
MaxFutureSeconds: 3600,
|
||||
}
|
||||
}
|
||||
|
||||
// Service implements the Validator interface.
|
||||
type Service struct {
|
||||
cfg *Config
|
||||
}
|
||||
|
||||
// New creates a new validation service with default configuration.
|
||||
func New() *Service {
|
||||
return &Service{cfg: DefaultConfig()}
|
||||
}
|
||||
|
||||
// NewWithConfig creates a new validation service with the given configuration.
|
||||
func NewWithConfig(cfg *Config) *Service {
|
||||
if cfg == nil {
|
||||
cfg = DefaultConfig()
|
||||
}
|
||||
return &Service{cfg: cfg}
|
||||
}
|
||||
|
||||
// ValidateRawJSON validates raw message before unmarshaling.
|
||||
func (s *Service) ValidateRawJSON(msg []byte) Result {
|
||||
if errMsg := ValidateLowercaseHexInJSON(msg); errMsg != "" {
|
||||
return Blocked(errMsg)
|
||||
}
|
||||
return OK()
|
||||
}
|
||||
|
||||
// ValidateEvent validates an unmarshaled event.
|
||||
func (s *Service) ValidateEvent(ev *event.E) Result {
|
||||
// Validate event ID
|
||||
if result := ValidateEventID(ev); !result.Valid {
|
||||
return result
|
||||
}
|
||||
|
||||
// Validate timestamp
|
||||
if result := ValidateTimestamp(ev, s.cfg.MaxFutureSeconds); !result.Valid {
|
||||
return result
|
||||
}
|
||||
|
||||
// Validate signature
|
||||
if result := ValidateSignature(ev); !result.Valid {
|
||||
return result
|
||||
}
|
||||
|
||||
return OK()
|
||||
}
|
||||
|
||||
// ValidateProtectedTag checks NIP-70 protected tag requirements.
|
||||
func (s *Service) ValidateProtectedTag(ev *event.E, authedPubkey []byte) Result {
|
||||
return ValidateProtectedTagMatch(ev, authedPubkey)
|
||||
}
|
||||
Reference in New Issue
Block a user