feature/epic1-core-infrastructure #2
@@ -13,21 +13,11 @@ linters:
|
||||
- errcheck
|
||||
- govet
|
||||
- staticcheck
|
||||
- revive
|
||||
- gosec
|
||||
disable:
|
||||
- gocritic # Can be enabled later for stricter checks
|
||||
|
||||
linters-settings:
|
||||
revive:
|
||||
rules:
|
||||
- name: exported
|
||||
severity: warning
|
||||
arguments:
|
||||
- checkPrivateReceivers
|
||||
# Disable stuttering check - interface names like ConfigProvider are acceptable
|
||||
- name: package-comments
|
||||
severity: warning
|
||||
gosec:
|
||||
severity: medium
|
||||
errcheck:
|
||||
@@ -43,10 +33,6 @@ issues:
|
||||
linters:
|
||||
- errcheck
|
||||
- gosec
|
||||
# ConfigProvider stuttering is acceptable - it's a common pattern for interfaces
|
||||
- path: pkg/config/config\.go
|
||||
linters:
|
||||
- revive
|
||||
|
||||
output:
|
||||
print-issued-lines: true
|
||||
|
||||
@@ -184,6 +184,7 @@ When working on this project, follow this workflow:
|
||||
- Meet the acceptance criteria
|
||||
- Use the implementation notes as guidance
|
||||
- Follow the patterns established in `playbook.md`
|
||||
- Implement tests
|
||||
|
||||
### 6. Verify Alignment
|
||||
- Ensure code follows Clean/Hexagonal Architecture principles
|
||||
@@ -196,6 +197,8 @@ When working on this project, follow this workflow:
|
||||
- **ALWAYS commit** after successful implementation
|
||||
- Ensure the code builds (`go build`)
|
||||
- Ensure all tests pass (`go test`)
|
||||
- Ensure there are no linter issues (`make lint`)
|
||||
- Ensure there are no fmt issues (`make fmt-check`)
|
||||
- Verify all acceptance criteria are met
|
||||
- Write a clear, descriptive commit message
|
||||
|
||||
@@ -301,6 +304,7 @@ If you make architectural decisions or significant changes:
|
||||
2. Update architecture documents if structure changes
|
||||
3. Update stories if implementation details change
|
||||
4. Keep documentation in sync with code
|
||||
5. Do not use any emojis
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ func main() {
|
||||
fx.Invoke(di.RegisterLifecycleHooks),
|
||||
// Force HTTP server to be created (which triggers all dependencies)
|
||||
// This ensures database, health, metrics, etc. are all created
|
||||
fx.Invoke(func(srv *server.Server, dbClient *database.Client) {
|
||||
fx.Invoke(func(_ *server.Server, _ *database.Client) {
|
||||
// Both server and database are created, hooks are registered
|
||||
// This ensures all providers execute
|
||||
}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package ent provides code generation for Ent schema definitions.
|
||||
package ent
|
||||
|
||||
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package schema defines the Ent schema for audit log entities.
|
||||
package schema
|
||||
|
||||
import "entgo.io/ent"
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"git.dcentral.systems/toolz/goplt/pkg/errorbus"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/logger"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
@@ -130,7 +131,7 @@ func ProvideDatabase() fx.Option {
|
||||
log.Info("Database migrations completed successfully")
|
||||
return nil
|
||||
},
|
||||
OnStop: func(ctx context.Context) error {
|
||||
OnStop: func(_ context.Context) error {
|
||||
return dbClient.Close()
|
||||
},
|
||||
})
|
||||
@@ -147,7 +148,7 @@ func ProvideErrorBus() fx.Option {
|
||||
|
||||
// Register lifecycle hook to close the bus on shutdown
|
||||
lc.Append(fx.Hook{
|
||||
OnStop: func(ctx context.Context) error {
|
||||
OnStop: func(_ context.Context) error {
|
||||
return bus.Close()
|
||||
},
|
||||
})
|
||||
@@ -181,7 +182,7 @@ func ProvideTracer() fx.Option {
|
||||
enabled := cfg.GetBool("tracing.enabled")
|
||||
if !enabled {
|
||||
// Return no-op tracer
|
||||
return trace.NewNoopTracerProvider(), nil
|
||||
return noop.NewTracerProvider(), nil
|
||||
}
|
||||
|
||||
serviceName := cfg.GetString("tracing.service_name")
|
||||
@@ -248,8 +249,7 @@ func ProvideHTTPServer() fx.Option {
|
||||
|
||||
// Register lifecycle hooks
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(ctx context.Context) error {
|
||||
|
||||
OnStart: func(_ context.Context) error {
|
||||
// Get server address from config
|
||||
port := cfg.GetInt("server.port")
|
||||
if port == 0 {
|
||||
@@ -300,7 +300,7 @@ func ProvideHTTPServer() fx.Option {
|
||||
)
|
||||
// Continue anyway - server might still be starting
|
||||
} else {
|
||||
resp.Body.Close()
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
|
||||
log.Info("HTTP server started successfully",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package schema defines the Ent schema for domain entities.
|
||||
package schema
|
||||
|
||||
import (
|
||||
@@ -46,4 +47,3 @@ func (AuditLog) Indexes() []ent.Index {
|
||||
index.Fields("action"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,4 +30,3 @@ func (Permission) Edges() []ent.Edge {
|
||||
edge.To("role_permissions", RolePermission.Type),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,4 +37,3 @@ func (Role) Edges() []ent.Edge {
|
||||
edge.To("user_roles", UserRole.Type),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,4 +32,3 @@ func (RolePermission) Edges() []ent.Edge {
|
||||
Field("permission_id"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,4 +41,3 @@ func (User) Edges() []ent.Edge {
|
||||
edge.To("user_roles", UserRole.Type),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,4 +32,3 @@ func (UserRole) Edges() []ent.Edge {
|
||||
Field("role_id"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package errorbus provides a channel-based error bus implementation.
|
||||
package errorbus
|
||||
|
||||
import (
|
||||
@@ -11,17 +12,17 @@ import (
|
||||
|
||||
// ChannelBus implements a channel-based error bus.
|
||||
type ChannelBus struct {
|
||||
errors chan errorWithContext
|
||||
logger logger.Logger
|
||||
done chan struct{}
|
||||
wg sync.WaitGroup
|
||||
once sync.Once
|
||||
errors chan errorWithContext
|
||||
logger logger.Logger
|
||||
done chan struct{}
|
||||
wg sync.WaitGroup
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
type errorWithContext struct {
|
||||
err error
|
||||
ctx context.Context
|
||||
stack []byte
|
||||
err error
|
||||
ctx context.Context
|
||||
stack []byte
|
||||
}
|
||||
|
||||
// NewChannelBus creates a new channel-based error bus.
|
||||
@@ -162,4 +163,3 @@ func (b *ChannelBus) Close() error {
|
||||
|
||||
// Ensure ChannelBus implements ErrorPublisher
|
||||
var _ errorbus.ErrorPublisher = (*ChannelBus)(nil)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package health provides health check implementations for various components.
|
||||
package health
|
||||
|
||||
import (
|
||||
@@ -23,4 +24,3 @@ func NewDatabaseChecker(client *database.Client) health.HealthChecker {
|
||||
func (d *DatabaseChecker) Check(ctx context.Context) error {
|
||||
return d.client.Ping(ctx)
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ func (r *Registry) Check(ctx context.Context) health.HealthStatus {
|
||||
}
|
||||
|
||||
// LivenessCheck performs a basic liveness check (no dependencies).
|
||||
func (r *Registry) LivenessCheck(ctx context.Context) health.HealthStatus {
|
||||
func (r *Registry) LivenessCheck(_ context.Context) health.HealthStatus {
|
||||
// Liveness is always healthy if the service is running
|
||||
return health.HealthStatus{
|
||||
Status: health.StatusHealthy,
|
||||
@@ -71,4 +71,3 @@ func (r *Registry) LivenessCheck(ctx context.Context) health.HealthStatus {
|
||||
func (r *Registry) ReadinessCheck(ctx context.Context) health.HealthStatus {
|
||||
return r.Check(ctx)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package database provides database client and connection management.
|
||||
package database
|
||||
|
||||
import (
|
||||
@@ -20,11 +21,11 @@ type Client struct {
|
||||
|
||||
// Config holds database configuration.
|
||||
type Config struct {
|
||||
DSN string
|
||||
MaxConnections int
|
||||
MaxIdleConns int
|
||||
ConnMaxLifetime time.Duration
|
||||
ConnMaxIdleTime time.Duration
|
||||
DSN string
|
||||
MaxConnections int
|
||||
MaxIdleConns int
|
||||
ConnMaxLifetime time.Duration
|
||||
ConnMaxIdleTime time.Duration
|
||||
}
|
||||
|
||||
// NewClient creates a new Ent client with connection pooling.
|
||||
@@ -46,7 +47,7 @@ func NewClient(cfg Config) (*Client, error) {
|
||||
defer cancel()
|
||||
|
||||
if err := db.PingContext(ctx); err != nil {
|
||||
db.Close()
|
||||
_ = db.Close()
|
||||
return nil, fmt.Errorf("failed to ping database: %w", err)
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ func (c *Client) Close() error {
|
||||
|
||||
// Migrate runs database migrations.
|
||||
func (c *Client) Migrate(ctx context.Context) error {
|
||||
return c.Client.Schema.Create(ctx)
|
||||
return c.Schema.Create(ctx)
|
||||
}
|
||||
|
||||
// Ping checks database connectivity.
|
||||
@@ -84,4 +85,3 @@ func (c *Client) Ping(ctx context.Context) error {
|
||||
func (c *Client) DB() *sql.DB {
|
||||
return c.db
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package metrics provides Prometheus metrics collection and instrumentation.
|
||||
package metrics
|
||||
|
||||
import (
|
||||
@@ -12,9 +13,9 @@ import (
|
||||
// Metrics holds all Prometheus metrics.
|
||||
type Metrics struct {
|
||||
httpRequestDuration *prometheus.HistogramVec
|
||||
httpRequestTotal *prometheus.CounterVec
|
||||
httpErrorsTotal *prometheus.CounterVec
|
||||
registry *prometheus.Registry
|
||||
httpRequestTotal *prometheus.CounterVec
|
||||
httpErrorsTotal *prometheus.CounterVec
|
||||
registry *prometheus.Registry
|
||||
}
|
||||
|
||||
// NewMetrics creates a new metrics registry with all metrics.
|
||||
@@ -94,4 +95,3 @@ func (m *Metrics) Handler() http.Handler {
|
||||
func (m *Metrics) Registry() *prometheus.Registry {
|
||||
return m.registry
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package observability provides OpenTelemetry tracing setup and configuration.
|
||||
package observability
|
||||
|
||||
import (
|
||||
@@ -13,22 +14,23 @@ import (
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
// Config holds OpenTelemetry configuration.
|
||||
type Config struct {
|
||||
Enabled bool
|
||||
ServiceName string
|
||||
Enabled bool
|
||||
ServiceName string
|
||||
ServiceVersion string
|
||||
Environment string
|
||||
OTLPEndpoint string
|
||||
Environment string
|
||||
OTLPEndpoint string
|
||||
}
|
||||
|
||||
// InitTracer initializes OpenTelemetry tracing.
|
||||
func InitTracer(ctx context.Context, cfg Config) (trace.TracerProvider, error) {
|
||||
if !cfg.Enabled {
|
||||
// Return a no-op tracer provider
|
||||
return trace.NewNoopTracerProvider(), nil
|
||||
return noop.NewTracerProvider(), nil
|
||||
}
|
||||
|
||||
// Create resource with service information
|
||||
@@ -91,4 +93,3 @@ func ShutdownTracer(ctx context.Context, tp trace.TracerProvider) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package server provides HTTP middleware functions for request processing.
|
||||
package server
|
||||
|
||||
import (
|
||||
@@ -6,15 +7,17 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/errorbus"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/logger"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
const (
|
||||
requestIDKey = "request_id"
|
||||
userIDKey = "user_id"
|
||||
requestIDKey contextKey = "request_id"
|
||||
userIDKey contextKey = "user_id"
|
||||
)
|
||||
|
||||
// RequestIDMiddleware generates a unique request ID for each request.
|
||||
@@ -25,7 +28,7 @@ func RequestIDMiddleware() gin.HandlerFunc {
|
||||
requestID = uuid.New().String()
|
||||
}
|
||||
|
||||
c.Set(requestIDKey, requestID)
|
||||
c.Set(string(requestIDKey), requestID)
|
||||
c.Header("X-Request-ID", requestID)
|
||||
c.Next()
|
||||
}
|
||||
@@ -45,7 +48,7 @@ func LoggingMiddleware(log logger.Logger) gin.HandlerFunc {
|
||||
duration := time.Since(start)
|
||||
|
||||
// Get request ID from context
|
||||
requestID, _ := c.Get(requestIDKey)
|
||||
requestID, _ := c.Get(string(requestIDKey))
|
||||
requestIDStr := ""
|
||||
if id, ok := requestID.(string); ok {
|
||||
requestIDStr = id
|
||||
@@ -74,8 +77,8 @@ func PanicRecoveryMiddleware(errorBus errorbus.ErrorPublisher) gin.HandlerFunc {
|
||||
stack = stack[:n]
|
||||
|
||||
// Get request ID from context
|
||||
requestID, _ := c.Get(requestIDKey)
|
||||
ctx := context.WithValue(context.Background(), "request_id", requestID)
|
||||
requestID, _ := c.Get(string(requestIDKey))
|
||||
ctx := context.WithValue(context.Background(), requestIDKey, requestID)
|
||||
|
||||
// Create error
|
||||
var panicErr error
|
||||
@@ -138,4 +141,3 @@ func TimeoutMiddleware(timeout time.Duration) gin.HandlerFunc {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package errorbus provides interfaces for error publishing and handling.
|
||||
package errorbus
|
||||
|
||||
import (
|
||||
@@ -18,4 +19,3 @@ type ErrorContext struct {
|
||||
Component string
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package health provides interfaces and types for health checking.
|
||||
package health
|
||||
|
||||
import "context"
|
||||
@@ -31,4 +32,3 @@ type HealthStatus struct {
|
||||
Status Status `json:"status"`
|
||||
Components []ComponentStatus `json:"components,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user