summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/dashboard/main.go12
-rw-r--r--internal/api/http.go5
-rw-r--r--internal/api/trello.go3
-rw-r--r--internal/config/constants.go51
-rw-r--r--internal/handlers/timeline_logic.go9
-rw-r--r--internal/store/sqlite.go7
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}