diff --git a/garminsync b/garminsync new file mode 100755 index 0000000..2d6d33e Binary files /dev/null and b/garminsync differ diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index 2d25aa5..e6e1620 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -20,15 +20,15 @@ func NewSQLiteDB(dbPath string) (*SQLiteDB, error) { sqlite := &SQLiteDB{db: db} - // Create tables - if err := sqlite.createTables(); err != nil { - return nil, err - } + // Create tables + if err := sqlite.CreateTables(); err != nil { + return nil, err + } return sqlite, nil } -func (s *SQLiteDB) createTables() error { +func (s *SQLiteDB) CreateTables() error { schema := ` CREATE TABLE IF NOT EXISTS activities ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -134,6 +134,10 @@ func (s *SQLiteDB) ActivityExists(activityID int) (bool, error) { return count > 0, nil } +func (s *SQLiteDB) DB() *sql.DB { + return s.db +} + func (s *SQLiteDB) GetActivity(activityID int) (*Activity, error) { query := ` SELECT id, activity_id, start_time, activity_type, duration, distance, diff --git a/internal/garmin/client.go b/internal/garmin/client.go index 41d7b9f..f33727e 100644 --- a/internal/garmin/client.go +++ b/internal/garmin/client.go @@ -156,15 +156,20 @@ func (c *Client) GetActivities(start, limit int) ([]GarminActivity, error) { return nil, fmt.Errorf("failed to get activities: status %d", resp.StatusCode) } - var activities []GarminActivity - if err := json.NewDecoder(resp.Body).Decode(&activities); err != nil { + // Garmin API returns an object containing the activity list + type responseStruct struct { + ActivityList []GarminActivity `json:"activityList"` + } + var response responseStruct + + if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { return nil, err } // Rate limiting time.Sleep(2 * time.Second) - return activities, nil + return response.ActivityList, nil } func (c *Client) DownloadActivity(activityID int, format string) ([]byte, error) { diff --git a/internal/sync/sync.go b/internal/sync/sync.go index 2c7e483..2705899 100644 --- a/internal/sync/sync.go +++ b/internal/sync/sync.go @@ -12,21 +12,21 @@ import ( "github.com/sstent/garminsync-go/internal/parser" ) -type Syncer struct { +type SyncService struct { garminClient *garmin.Client db *database.SQLiteDB dataDir string } -func NewSyncer(garminClient *garmin.Client, db *database.SQLiteDB, dataDir string) *Syncer { - return &Syncer{ +func NewSyncService(garminClient *garmin.Client, db *database.SQLiteDB, dataDir string) *SyncService { + return &SyncService{ garminClient: garminClient, db: db, dataDir: dataDir, } } -func (s *Syncer) FullSync(ctx context.Context) error { +func (s *SyncService) FullSync(ctx context.Context) error { fmt.Println("Starting full sync...") defer fmt.Println("Sync completed") @@ -53,7 +53,7 @@ func (s *Syncer) FullSync(ctx context.Context) error { return nil } -func (s *Syncer) syncActivity(activity *garmin.GarminActivity) error { +func (s *SyncService) syncActivity(activity *garmin.GarminActivity) error { // Skip if already downloaded if exists, _ := s.db.ActivityExists(activity.ActivityID); exists { return nil @@ -111,6 +111,11 @@ func (s *Syncer) syncActivity(activity *garmin.GarminActivity) error { return nil } +// Add missing Sync method +func (s *SyncService) Sync(ctx context.Context) error { + return s.FullSync(ctx) +} + func getActivityType(activity *garmin.GarminActivity) string { if activityType, ok := activity.ActivityType["typeKey"]; ok { return activityType.(string) diff --git a/internal/web/routes.go b/internal/web/routes.go index 9fe073f..a610ed9 100644 --- a/internal/web/routes.go +++ b/internal/web/routes.go @@ -13,12 +13,12 @@ import ( type WebHandler struct { db *database.SQLiteDB - syncer *sync.Syncer + syncer *sync.SyncService garmin *garmin.Client templates map[string]interface{} // Placeholder for template handling } -func NewWebHandler(db *database.SQLiteDB, syncer *sync.Syncer, garmin *garmin.Client) *WebHandler { +func NewWebHandler(db *database.SQLiteDB, syncer *sync.SyncService, garmin *garmin.Client) *WebHandler { return &WebHandler{ db: db, syncer: syncer, @@ -27,6 +27,11 @@ func NewWebHandler(db *database.SQLiteDB, syncer *sync.Syncer, garmin *garmin.Cl } } +func (h *WebHandler) LoadTemplates(templateDir string) error { + // For now, just return nil - templates will be handled later + return nil +} + func (h *WebHandler) RegisterRoutes(router *gin.Engine) { router.GET("/", h.Index) router.GET("/activities", h.ActivityList) @@ -79,7 +84,7 @@ func (h *WebHandler) ActivityDetail(c *gin.Context) { } func (h *WebHandler) Sync(c *gin.Context) { - err := h.syncer.FullSync(context.Background()) + err := h.syncer.Sync(context.Background()) if err != nil { c.AbortWithStatus(http.StatusInternalServerError) return diff --git a/main.go b/main.go index 9c2eadf..d999b60 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ import ( "github.com/sstent/garminsync-go/internal/web" _ "github.com/mattn/go-sqlite3" + "github.com/gin-gonic/gin" "github.com/robfig/cron/v3" ) @@ -30,7 +31,7 @@ type App struct { server *http.Server garmin *garmin.Client shutdown chan os.Signal - syncService *sync.SyncService + syncService *sync.SyncService // This should now work } func main() { @@ -63,11 +64,10 @@ func (app *App) init() error { var err error // Initialize database - dbConn, err := initDatabase() + app.db, err = initDatabase() if err != nil { return err } - app.db = database.NewSQLiteDBFromDB(dbConn) // Initialize Garmin client app.garmin = garmin.NewClient() @@ -77,13 +77,13 @@ func (app *App) init() error { if dataDir == "" { dataDir = "./data" } - app.syncService = sync.NewSyncService(app.garmin, database.NewSQLiteDBFromDB(app.db), dataDir) + app.syncService = sync.NewSyncService(app.garmin, app.db, dataDir) // Setup cron scheduler app.cron = cron.New() // Setup HTTP server - webHandler := web.NewWebHandler(app.db) + webHandler := web.NewWebHandler(app.db, app.syncService, app.garmin) templateDir := os.Getenv("TEMPLATE_DIR") if templateDir == "" { templateDir = "./internal/web/templates" @@ -142,7 +142,7 @@ func (app *App) stop() { } // Database initialization -func initDatabase() (*sql.DB, error) { +func initDatabase() (*database.SQLiteDB, error) { // Get database path from environment or use default dbPath := os.Getenv("DB_PATH") if dbPath == "" { @@ -172,28 +172,24 @@ func initDatabase() (*sql.DB, error) { } // Create tables if they don't exist - sqliteDB := &database.SQLiteDB{db: db} - if err := sqliteDB.createTables(); err != nil { + sqliteDB := database.NewSQLiteDBFromDB(db) + if err := sqliteDB.CreateTables(); err != nil { return nil, fmt.Errorf("failed to create tables: %v", err) } - return db, nil + return sqliteDB, nil } -// Application routes -func (app *App) setupRoutes(webHandler *web.WebHandler) *http.ServeMux { - mux := http.NewServeMux() +func (app *App) setupRoutes(webHandler *web.WebHandler) http.Handler { + router := gin.Default() // Health check - mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte("OK")) + router.GET("/health", func(c *gin.Context) { + c.String(http.StatusOK, "OK") }) - // Web UI routes - mux.HandleFunc("/", webHandler.Index) - mux.HandleFunc("/activities", webHandler.ActivityList) - mux.HandleFunc("/activity", webHandler.ActivityDetail) + // Register web routes + webHandler.RegisterRoutes(router) - return mux + return router }