Decompose handle-event.go into DDD domain services (v0.36.15)
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:
2025-12-25 05:30:07 +01:00
parent 3e0a94a053
commit 24383ef1f4
42 changed files with 4791 additions and 2118 deletions

View 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)
}