mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-08 16:44:20 +01:00
Rewrite Tracks application in Golang
This commit introduces a complete rewrite of the Tracks GTD application in Go (Golang), providing a modern, performant alternative to the Ruby on Rails implementation. ## Architecture & Technology Stack - Language: Go 1.21+ - Web Framework: Gin - ORM: GORM with SQLite/MySQL/PostgreSQL support - Authentication: JWT with bcrypt password hashing - Clean Architecture: Separated models, services, handlers, and middleware ## Implemented Features ### Core Models - User: Authentication and user management - Context: GTD contexts (@home, @work, etc.) - Project: Project grouping and tracking - Todo: Task management with state machine (active, completed, deferred, pending) - Tag: Flexible tagging system with polymorphic associations - Dependency: Todo dependencies with circular dependency detection - Preference: User preferences and settings - Note: Project notes - Attachment: File attachment support (model only) - RecurringTodo: Recurring task template (model only) ### API Endpoints **Authentication:** - POST /api/auth/login - User login - POST /api/auth/register - User registration - POST /api/auth/logout - User logout - GET /api/me - Get current user **Todos:** - GET /api/todos - List todos with filtering - POST /api/todos - Create todo - GET /api/todos/:id - Get todo details - PUT /api/todos/:id - Update todo - DELETE /api/todos/:id - Delete todo - POST /api/todos/:id/complete - Mark as completed - POST /api/todos/:id/activate - Mark as active - POST /api/todos/:id/defer - Defer to future date - POST /api/todos/:id/dependencies - Add dependency - DELETE /api/todos/:id/dependencies/:successor_id - Remove dependency **Projects:** - GET /api/projects - List projects - POST /api/projects - Create project - GET /api/projects/:id - Get project details - PUT /api/projects/:id - Update project - DELETE /api/projects/:id - Delete project - POST /api/projects/:id/complete - Complete project - POST /api/projects/:id/activate - Activate project - POST /api/projects/:id/hide - Hide project - POST /api/projects/:id/review - Mark as reviewed - GET /api/projects/:id/stats - Get project statistics **Contexts:** - GET /api/contexts - List contexts - POST /api/contexts - Create context - GET /api/contexts/:id - Get context details - PUT /api/contexts/:id - Update context - DELETE /api/contexts/:id - Delete context - POST /api/contexts/:id/hide - Hide context - POST /api/contexts/:id/activate - Activate context - POST /api/contexts/:id/close - Close context - GET /api/contexts/:id/stats - Get context statistics ### Business Logic **Todo State Management:** - Active: Ready to work on - Completed: Finished tasks - Deferred: Future actions (show_from date) - Pending: Blocked by dependencies **Dependency Management:** - Create blocking relationships between todos - Automatic state transitions when blocking todos complete - Circular dependency detection - Automatic unblocking when prerequisites complete **Tag System:** - Polymorphic tagging for todos and recurring todos - Automatic tag creation on first use - Tag cloud support **Project & Context Tracking:** - State management (active, hidden, closed/completed) - Statistics and health indicators - Review tracking for projects ### Infrastructure **Configuration:** - Environment-based configuration - Support for SQLite, MySQL, and PostgreSQL - Configurable JWT secrets and token expiry - Flexible server settings **Database:** - GORM for ORM - Automatic migrations - Connection pooling - Multi-database support **Authentication & Security:** - JWT-based authentication - Bcrypt password hashing - Secure cookie support - Token refresh mechanism **Docker Support:** - Multi-stage Dockerfile for optimized builds - Docker Compose with PostgreSQL - Volume mounting for data persistence - Production-ready configuration ## Project Structure ``` cmd/tracks/ # Application entry point internal/ config/ # Configuration management database/ # Database setup and migrations handlers/ # HTTP request handlers middleware/ # Authentication middleware models/ # Database models services/ # Business logic layer ``` ## Documentation - README_GOLANG.md: Comprehensive documentation - .env.example: Configuration template - API documentation included in README - Code comments for complex logic ## Future Work The following features from the original Rails app are not yet implemented: - Recurring todo instantiation logic - Email integration (Mailgun/CloudMailin) - Advanced statistics and analytics - Import/Export functionality (CSV, YAML, XML) - File upload handling for attachments - Mobile views - RSS/Atom feeds - iCalendar export ## Benefits Over Rails Version - Performance: Compiled binary, lower resource usage - Deployment: Single binary, no runtime dependencies - Type Safety: Compile-time type checking - Concurrency: Better handling of concurrent requests - Memory: Lower memory footprint - Portability: Easy cross-platform compilation ## Testing The code structure supports testing, though tests are not yet implemented. Future work includes adding unit and integration tests.
This commit is contained in:
parent
6613d33f10
commit
f0eb4bdef5
29 changed files with 4100 additions and 104 deletions
143
internal/config/config.go
Normal file
143
internal/config/config.go
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
// Config holds the application configuration
|
||||
type Config struct {
|
||||
Server ServerConfig
|
||||
Database DatabaseConfig
|
||||
Auth AuthConfig
|
||||
App AppConfig
|
||||
}
|
||||
|
||||
// ServerConfig holds server-related configuration
|
||||
type ServerConfig struct {
|
||||
Host string
|
||||
Port int
|
||||
Mode string // debug, release, test
|
||||
}
|
||||
|
||||
// DatabaseConfig holds database-related configuration
|
||||
type DatabaseConfig struct {
|
||||
Driver string // sqlite, mysql, postgres
|
||||
Host string
|
||||
Port int
|
||||
Name string
|
||||
User string
|
||||
Password string
|
||||
SSLMode string
|
||||
}
|
||||
|
||||
// AuthConfig holds authentication-related configuration
|
||||
type AuthConfig struct {
|
||||
JWTSecret string
|
||||
TokenExpiry int // in hours
|
||||
SecureCookies bool
|
||||
}
|
||||
|
||||
// AppConfig holds application-specific configuration
|
||||
type AppConfig struct {
|
||||
Name string
|
||||
TimeZone string
|
||||
OpenSignups bool
|
||||
AdminEmail string
|
||||
SecretToken string
|
||||
ForceSSL bool
|
||||
UploadPath string
|
||||
MaxUploadSizeMB int64
|
||||
}
|
||||
|
||||
// Load reads configuration from environment variables
|
||||
func Load() (*Config, error) {
|
||||
// Try to load .env file if it exists
|
||||
_ = godotenv.Load()
|
||||
|
||||
cfg := &Config{
|
||||
Server: ServerConfig{
|
||||
Host: getEnv("SERVER_HOST", "0.0.0.0"),
|
||||
Port: getEnvAsInt("SERVER_PORT", 3000),
|
||||
Mode: getEnv("GIN_MODE", "debug"),
|
||||
},
|
||||
Database: DatabaseConfig{
|
||||
Driver: getEnv("DB_DRIVER", "sqlite"),
|
||||
Host: getEnv("DB_HOST", "localhost"),
|
||||
Port: getEnvAsInt("DB_PORT", 5432),
|
||||
Name: getEnv("DB_NAME", "tracks.db"),
|
||||
User: getEnv("DB_USER", ""),
|
||||
Password: getEnv("DB_PASSWORD", ""),
|
||||
SSLMode: getEnv("DB_SSLMODE", "disable"),
|
||||
},
|
||||
Auth: AuthConfig{
|
||||
JWTSecret: getEnv("JWT_SECRET", "change-me-in-production"),
|
||||
TokenExpiry: getEnvAsInt("TOKEN_EXPIRY_HOURS", 24),
|
||||
SecureCookies: getEnvAsBool("SECURE_COOKIES", false),
|
||||
},
|
||||
App: AppConfig{
|
||||
Name: getEnv("APP_NAME", "Tracks"),
|
||||
TimeZone: getEnv("TZ", "UTC"),
|
||||
OpenSignups: getEnvAsBool("OPEN_SIGNUPS", false),
|
||||
AdminEmail: getEnv("ADMIN_EMAIL", ""),
|
||||
SecretToken: getEnv("SECRET_TOKEN", "change-me-in-production"),
|
||||
ForceSSL: getEnvAsBool("FORCE_SSL", false),
|
||||
UploadPath: getEnv("UPLOAD_PATH", "./uploads"),
|
||||
MaxUploadSizeMB: getEnvAsInt64("MAX_UPLOAD_SIZE_MB", 10),
|
||||
},
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// GetDSN returns the database connection string
|
||||
func (c *DatabaseConfig) GetDSN() string {
|
||||
switch c.Driver {
|
||||
case "sqlite":
|
||||
return c.Name
|
||||
case "mysql":
|
||||
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
c.User, c.Password, c.Host, c.Port, c.Name)
|
||||
case "postgres":
|
||||
return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
||||
c.Host, c.Port, c.User, c.Password, c.Name, c.SSLMode)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getEnvAsInt(key string, defaultValue int) int {
|
||||
valueStr := getEnv(key, "")
|
||||
if value, err := strconv.Atoi(valueStr); err == nil {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getEnvAsInt64(key string, defaultValue int64) int64 {
|
||||
valueStr := getEnv(key, "")
|
||||
if value, err := strconv.ParseInt(valueStr, 10, 64); err == nil {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getEnvAsBool(key string, defaultValue bool) bool {
|
||||
valueStr := getEnv(key, "")
|
||||
if value, err := strconv.ParseBool(valueStr); err == nil {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue