working build - with ui

This commit is contained in:
2025-08-25 18:03:20 -07:00
parent b370173873
commit 9377db0164
4 changed files with 52 additions and 45 deletions

View File

@@ -24,8 +24,8 @@ RUN CGO_ENABLED=1 GOOS=linux go build -o garminsync .
# Runtime stage
FROM alpine:3.18
# Install runtime dependencies
RUN apk add --no-cache ca-certificates tzdata
# Install runtime dependencies (wget needed for healthcheck)
RUN apk add --no-cache ca-certificates tzdata wget sqlite
# Create app directory
WORKDIR /app
@@ -33,14 +33,13 @@ WORKDIR /app
# Copy binary from builder
COPY --from=builder /app/garminsync /app/garminsync
# Copy templates
COPY internal/web/templates ./internal/web/templates
# Copy web directory (frontend assets)
COPY web ./web
# Set timezone and environment
ENV TZ=UTC \
DATA_DIR=/data \
DB_PATH=/data/garmin.db \
TEMPLATE_DIR=/app/internal/web/templates
DB_PATH=/data/garmin.db
# Create data volume and set permissions
RUN mkdir /data && chown nobody:nobody /data

View File

@@ -6,12 +6,8 @@ services:
container_name: garminsync
ports:
- "8888:8888"
environment:
- GARMIN_EMAIL=${GARMIN_EMAIL}
- GARMIN_PASSWORD=${GARMIN_PASSWORD}
- DATA_DIR=/data
- DB_PATH=/data/garmin.db
- TEMPLATE_DIR=/app/internal/web/templates
env_file:
- .env # Use the root .env file
volumes:
- ./data:/data
- ./internal/web/templates:/app/internal/web/templates

View File

@@ -2,6 +2,7 @@ package web
import (
"context"
"log"
"net/http"
"strconv"
@@ -15,7 +16,6 @@ type WebHandler struct {
db *database.SQLiteDB
syncer *sync.SyncService
garmin *garmin.Client
templates map[string]interface{} // Placeholder for template handling
}
func NewWebHandler(db *database.SQLiteDB, syncer *sync.SyncService, garmin *garmin.Client) *WebHandler {
@@ -23,30 +23,22 @@ func NewWebHandler(db *database.SQLiteDB, syncer *sync.SyncService, garmin *garm
db: db,
syncer: syncer,
garmin: garmin,
templates: make(map[string]interface{}),
}
}
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)
func (h *WebHandler) RegisterRoutes(router *gin.RouterGroup) {
router.GET("/stats", h.GetStats)
router.GET("/activities", h.ActivityList)
router.GET("/activities/:id", h.ActivityDetail)
router.POST("/sync", h.Sync)
}
func (h *WebHandler) Index(c *gin.Context) {
func (h *WebHandler) GetStats(c *gin.Context) {
stats, err := h.db.GetStats()
if err != nil {
c.AbortWithStatus(http.StatusInternalServerError)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get stats"})
return
}
// Placeholder for template rendering
c.JSON(http.StatusOK, stats)
}
@@ -60,7 +52,7 @@ func (h *WebHandler) ActivityList(c *gin.Context) {
activities, err := h.db.GetActivities(limit, offset)
if err != nil {
c.AbortWithStatus(http.StatusInternalServerError)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get activities"})
return
}
@@ -70,13 +62,13 @@ func (h *WebHandler) ActivityList(c *gin.Context) {
func (h *WebHandler) ActivityDetail(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.AbortWithStatus(http.StatusBadRequest)
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid activity ID"})
return
}
activity, err := h.db.GetActivity(id)
if err != nil {
c.AbortWithStatus(http.StatusNotFound)
c.JSON(http.StatusNotFound, gin.H{"error": "Activity not found"})
return
}
@@ -84,11 +76,12 @@ func (h *WebHandler) ActivityDetail(c *gin.Context) {
}
func (h *WebHandler) Sync(c *gin.Context) {
err := h.syncer.Sync(context.Background())
if err != nil {
c.AbortWithStatus(http.StatusInternalServerError)
return
}
go func() {
err := h.syncer.Sync(context.Background())
if err != nil {
log.Printf("Sync error: %v", err)
}
}()
c.Status(http.StatusOK)
c.JSON(http.StatusOK, gin.H{"status": "sync_started", "message": "Sync started in background"})
}

41
main.go
View File

@@ -84,14 +84,7 @@ func (app *App) init() error {
// Setup HTTP server
webHandler := web.NewWebHandler(app.db, app.syncService, app.garmin)
templateDir := os.Getenv("TEMPLATE_DIR")
if templateDir == "" {
templateDir = "./internal/web/templates"
}
if err := webHandler.LoadTemplates(templateDir); err != nil {
return fmt.Errorf("failed to load templates: %w", err)
}
// We've removed template loading since we're using static frontend
app.server = &http.Server{
Addr: ":8888",
Handler: app.setupRoutes(webHandler),
@@ -183,13 +176,39 @@ func initDatabase() (*database.SQLiteDB, error) {
func (app *App) setupRoutes(webHandler *web.WebHandler) http.Handler {
router := gin.Default()
// Add middleware
router.Use(gin.Logger()) // Log all requests
router.Use(gin.Recovery()) // Recover from any panics
// Enable CORS for development
router.Use(func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
// Serve static files
router.Static("/static", "./web/static")
router.LoadHTMLFiles("web/index.html")
// API routes
api := router.Group("/api")
webHandler.RegisterRoutes(api)
// Serve main page
router.GET("/", func(c *gin.Context) {
c.HTML(200, "index.html", nil)
})
// Health check
router.GET("/health", func(c *gin.Context) {
c.String(http.StatusOK, "OK")
})
// Register web routes
webHandler.RegisterRoutes(router)
return router
}