diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-26 08:08:17 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-26 08:08:17 -1000 |
| commit | aff60af8ba24c8d5330c706ddf26927d81436d79 (patch) | |
| tree | 60d13f58663473a3db204d8a11f2cc8f6e65445f | |
| parent | 8c2b8c352f8c980c79bb4bb4772e8cbc02d14164 (diff) | |
Phase 4: Extract magic numbers to constants
Create config/constants.go with centralized configuration values:
- Concurrency limits (MaxConcurrentTrelloRequests)
- Timeouts (HTTP, Google Calendar, graceful shutdown, request)
- Meal times (breakfast, lunch, dinner hours)
- Database pool settings (connections, lifetime)
- Session and rate limiting settings
Update all files to use these constants instead of hardcoded values.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| -rw-r--r-- | cmd/dashboard/main.go | 12 | ||||
| -rw-r--r-- | internal/api/http.go | 5 | ||||
| -rw-r--r-- | internal/api/trello.go | 3 | ||||
| -rw-r--r-- | internal/config/constants.go | 51 | ||||
| -rw-r--r-- | internal/handlers/timeline_logic.go | 9 | ||||
| -rw-r--r-- | internal/store/sqlite.go | 7 |
6 files changed, 71 insertions, 16 deletions
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go index 6b895d1..b3eab14 100644 --- a/cmd/dashboard/main.go +++ b/cmd/dashboard/main.go @@ -45,7 +45,7 @@ func main() { // Initialize session manager sessionManager := scs.New() sessionManager.Store = sqlite3store.New(db.DB()) - sessionManager.Lifetime = 24 * time.Hour + sessionManager.Lifetime = config.SessionLifetime sessionManager.Cookie.Persist = true sessionManager.Cookie.Secure = !cfg.Debug sessionManager.Cookie.SameSite = http.SameSiteLaxMode @@ -91,7 +91,7 @@ func main() { var googleCalendarClient api.GoogleCalendarAPI if cfg.HasGoogleCalendar() { // Use timeout context to prevent startup hangs if credentials file is unreachable - initCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + initCtx, cancel := context.WithTimeout(context.Background(), config.GoogleCalendarInitTimeout) var err error googleCalendarClient, err = api.NewGoogleCalendarClient(initCtx, cfg.GoogleCredentialsFile, cfg.GoogleCalendarID) cancel() @@ -111,13 +111,13 @@ func main() { // Global middleware r.Use(middleware.Logger) r.Use(middleware.Recoverer) - r.Use(middleware.Timeout(60 * time.Second)) + r.Use(middleware.Timeout(config.RequestTimeout)) r.Use(appmiddleware.SecurityHeaders(cfg.Debug)) // Security headers r.Use(sessionManager.LoadAndSave) // Session middleware must be applied globally r.Use(authHandlers.Middleware().CSRFProtect) // CSRF protection - // Rate limiter for auth endpoints (5 requests per 15 minutes per IP) - authRateLimiter := appmiddleware.NewRateLimiter(5, 15*time.Minute) + // Rate limiter for auth endpoints + authRateLimiter := appmiddleware.NewRateLimiter(config.AuthRateLimitRequests, config.AuthRateLimitWindow) // Public routes (no auth required) r.Get("/login", authHandlers.HandleLoginPage) @@ -206,7 +206,7 @@ func main() { log.Println("Shutting down server...") // Graceful shutdown with timeout - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), config.GracefulShutdownTimeout) defer cancel() if err := srv.Shutdown(ctx); err != nil { diff --git a/internal/api/http.go b/internal/api/http.go index df28c65..d9ddf0e 100644 --- a/internal/api/http.go +++ b/internal/api/http.go @@ -7,7 +7,8 @@ import ( "fmt" "io" "net/http" - "time" + + "task-dashboard/internal/config" ) // BaseClient provides common HTTP functionality for API clients @@ -19,7 +20,7 @@ type BaseClient struct { // NewBaseClient creates a new BaseClient with default settings func NewBaseClient(baseURL string) BaseClient { return BaseClient{ - HTTPClient: &http.Client{Timeout: 15 * time.Second}, + HTTPClient: &http.Client{Timeout: config.HTTPClientTimeout}, BaseURL: baseURL, } } diff --git a/internal/api/trello.go b/internal/api/trello.go index 67ffad0..a276726 100644 --- a/internal/api/trello.go +++ b/internal/api/trello.go @@ -9,6 +9,7 @@ import ( "sync" "time" + "task-dashboard/internal/config" "task-dashboard/internal/models" ) @@ -170,7 +171,7 @@ func (c *TrelloClient) GetBoardsWithCards(ctx context.Context) ([]models.Board, var wg sync.WaitGroup var mu sync.Mutex // Protects writes to boards slice elements - sem := make(chan struct{}, 5) // Limit to 5 concurrent requests + sem := make(chan struct{}, config.MaxConcurrentTrelloRequests) for i := range boards { wg.Add(1) diff --git a/internal/config/constants.go b/internal/config/constants.go new file mode 100644 index 0000000..a199404 --- /dev/null +++ b/internal/config/constants.go @@ -0,0 +1,51 @@ +package config + +import "time" + +// Concurrency limits +const ( + // MaxConcurrentTrelloRequests limits parallel Trello API calls + MaxConcurrentTrelloRequests = 5 +) + +// Timeouts +const ( + // HTTPClientTimeout is the default timeout for HTTP clients + HTTPClientTimeout = 15 * time.Second + + // GoogleCalendarInitTimeout is the timeout for Google Calendar initialization + GoogleCalendarInitTimeout = 30 * time.Second + + // GracefulShutdownTimeout is the timeout for server graceful shutdown + GracefulShutdownTimeout = 10 * time.Second + + // RequestTimeout is the timeout for individual HTTP requests + RequestTimeout = 60 * time.Second +) + +// Default meal times (24-hour format) +const ( + BreakfastHour = 8 + LunchHour = 12 + DinnerHour = 19 +) + +// Database connection pool settings +const ( + SQLiteMaxOpenConns = 5 + SQLiteMaxIdleConns = 2 + SQLiteConnMaxLifetime = time.Hour +) + +// Session settings +const ( + SessionLifetime = 24 * time.Hour +) + +// Rate limiting +const ( + // AuthRateLimitRequests is max login attempts per window + AuthRateLimitRequests = 5 + // AuthRateLimitWindow is the time window for rate limiting + AuthRateLimitWindow = 15 * time.Minute +) diff --git a/internal/handlers/timeline_logic.go b/internal/handlers/timeline_logic.go index c51262a..0d4595f 100644 --- a/internal/handlers/timeline_logic.go +++ b/internal/handlers/timeline_logic.go @@ -6,6 +6,7 @@ import ( "time" "task-dashboard/internal/api" + "task-dashboard/internal/config" "task-dashboard/internal/models" "task-dashboard/internal/store" ) @@ -49,13 +50,13 @@ func BuildTimeline(ctx context.Context, s *store.Store, calendarClient api.Googl // Apply Meal Defaults switch meal.MealType { case "breakfast": - mealTime = time.Date(mealTime.Year(), mealTime.Month(), mealTime.Day(), 8, 0, 0, 0, mealTime.Location()) + mealTime = time.Date(mealTime.Year(), mealTime.Month(), mealTime.Day(), config.BreakfastHour, 0, 0, 0, mealTime.Location()) case "lunch": - mealTime = time.Date(mealTime.Year(), mealTime.Month(), mealTime.Day(), 12, 0, 0, 0, mealTime.Location()) + mealTime = time.Date(mealTime.Year(), mealTime.Month(), mealTime.Day(), config.LunchHour, 0, 0, 0, mealTime.Location()) case "dinner": - mealTime = time.Date(mealTime.Year(), mealTime.Month(), mealTime.Day(), 19, 0, 0, 0, mealTime.Location()) + mealTime = time.Date(mealTime.Year(), mealTime.Month(), mealTime.Day(), config.DinnerHour, 0, 0, 0, mealTime.Location()) default: - mealTime = time.Date(mealTime.Year(), mealTime.Month(), mealTime.Day(), 12, 0, 0, 0, mealTime.Location()) + mealTime = time.Date(mealTime.Year(), mealTime.Month(), mealTime.Day(), config.LunchHour, 0, 0, 0, mealTime.Location()) } item := models.TimelineItem{ diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go index 12aa1ce..c2f6e98 100644 --- a/internal/store/sqlite.go +++ b/internal/store/sqlite.go @@ -13,6 +13,7 @@ import ( _ "github.com/mattn/go-sqlite3" + "task-dashboard/internal/config" "task-dashboard/internal/models" ) @@ -47,9 +48,9 @@ func New(dbPath, migrationDir string) (*Store, error) { // Configure connection pool for SQLite with WAL mode // WAL allows concurrent reads, but writes still need serialization - db.SetMaxOpenConns(5) - db.SetMaxIdleConns(2) - db.SetConnMaxLifetime(time.Hour) + db.SetMaxOpenConns(config.SQLiteMaxOpenConns) + db.SetMaxIdleConns(config.SQLiteMaxIdleConns) + db.SetConnMaxLifetime(config.SQLiteConnMaxLifetime) store := &Store{db: db, migrationDir: migrationDir} |
