mirror of
https://github.com/sstent/garminsync-go.git
synced 2025-12-06 08:01:52 +00:00
working build - with ui
This commit is contained in:
11
Dockerfile
11
Dockerfile
@@ -24,8 +24,8 @@ RUN CGO_ENABLED=1 GOOS=linux go build -o garminsync .
|
|||||||
# Runtime stage
|
# Runtime stage
|
||||||
FROM alpine:3.18
|
FROM alpine:3.18
|
||||||
|
|
||||||
# Install runtime dependencies
|
# Install runtime dependencies (wget needed for healthcheck)
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata wget sqlite
|
||||||
|
|
||||||
# Create app directory
|
# Create app directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -33,14 +33,13 @@ WORKDIR /app
|
|||||||
# Copy binary from builder
|
# Copy binary from builder
|
||||||
COPY --from=builder /app/garminsync /app/garminsync
|
COPY --from=builder /app/garminsync /app/garminsync
|
||||||
|
|
||||||
# Copy templates
|
# Copy web directory (frontend assets)
|
||||||
COPY internal/web/templates ./internal/web/templates
|
COPY web ./web
|
||||||
|
|
||||||
# Set timezone and environment
|
# Set timezone and environment
|
||||||
ENV TZ=UTC \
|
ENV TZ=UTC \
|
||||||
DATA_DIR=/data \
|
DATA_DIR=/data \
|
||||||
DB_PATH=/data/garmin.db \
|
DB_PATH=/data/garmin.db
|
||||||
TEMPLATE_DIR=/app/internal/web/templates
|
|
||||||
|
|
||||||
# Create data volume and set permissions
|
# Create data volume and set permissions
|
||||||
RUN mkdir /data && chown nobody:nobody /data
|
RUN mkdir /data && chown nobody:nobody /data
|
||||||
|
|||||||
@@ -6,12 +6,8 @@ services:
|
|||||||
container_name: garminsync
|
container_name: garminsync
|
||||||
ports:
|
ports:
|
||||||
- "8888:8888"
|
- "8888:8888"
|
||||||
environment:
|
env_file:
|
||||||
- GARMIN_EMAIL=${GARMIN_EMAIL}
|
- .env # Use the root .env file
|
||||||
- GARMIN_PASSWORD=${GARMIN_PASSWORD}
|
|
||||||
- DATA_DIR=/data
|
|
||||||
- DB_PATH=/data/garmin.db
|
|
||||||
- TEMPLATE_DIR=/app/internal/web/templates
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/data
|
- ./data:/data
|
||||||
- ./internal/web/templates:/app/internal/web/templates
|
- ./internal/web/templates:/app/internal/web/templates
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package web
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@@ -15,7 +16,6 @@ type WebHandler struct {
|
|||||||
db *database.SQLiteDB
|
db *database.SQLiteDB
|
||||||
syncer *sync.SyncService
|
syncer *sync.SyncService
|
||||||
garmin *garmin.Client
|
garmin *garmin.Client
|
||||||
templates map[string]interface{} // Placeholder for template handling
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWebHandler(db *database.SQLiteDB, syncer *sync.SyncService, garmin *garmin.Client) *WebHandler {
|
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,
|
db: db,
|
||||||
syncer: syncer,
|
syncer: syncer,
|
||||||
garmin: garmin,
|
garmin: garmin,
|
||||||
templates: make(map[string]interface{}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *WebHandler) LoadTemplates(templateDir string) error {
|
func (h *WebHandler) RegisterRoutes(router *gin.RouterGroup) {
|
||||||
// For now, just return nil - templates will be handled later
|
router.GET("/stats", h.GetStats)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *WebHandler) RegisterRoutes(router *gin.Engine) {
|
|
||||||
router.GET("/", h.Index)
|
|
||||||
router.GET("/activities", h.ActivityList)
|
router.GET("/activities", h.ActivityList)
|
||||||
router.GET("/activities/:id", h.ActivityDetail)
|
router.GET("/activities/:id", h.ActivityDetail)
|
||||||
router.POST("/sync", h.Sync)
|
router.POST("/sync", h.Sync)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *WebHandler) Index(c *gin.Context) {
|
func (h *WebHandler) GetStats(c *gin.Context) {
|
||||||
stats, err := h.db.GetStats()
|
stats, err := h.db.GetStats()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithStatus(http.StatusInternalServerError)
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get stats"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder for template rendering
|
|
||||||
c.JSON(http.StatusOK, stats)
|
c.JSON(http.StatusOK, stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +52,7 @@ func (h *WebHandler) ActivityList(c *gin.Context) {
|
|||||||
|
|
||||||
activities, err := h.db.GetActivities(limit, offset)
|
activities, err := h.db.GetActivities(limit, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithStatus(http.StatusInternalServerError)
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get activities"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,13 +62,13 @@ func (h *WebHandler) ActivityList(c *gin.Context) {
|
|||||||
func (h *WebHandler) ActivityDetail(c *gin.Context) {
|
func (h *WebHandler) ActivityDetail(c *gin.Context) {
|
||||||
id, err := strconv.Atoi(c.Param("id"))
|
id, err := strconv.Atoi(c.Param("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithStatus(http.StatusBadRequest)
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid activity ID"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
activity, err := h.db.GetActivity(id)
|
activity, err := h.db.GetActivity(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithStatus(http.StatusNotFound)
|
c.JSON(http.StatusNotFound, gin.H{"error": "Activity not found"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,11 +76,12 @@ func (h *WebHandler) ActivityDetail(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *WebHandler) Sync(c *gin.Context) {
|
func (h *WebHandler) Sync(c *gin.Context) {
|
||||||
err := h.syncer.Sync(context.Background())
|
go func() {
|
||||||
if err != nil {
|
err := h.syncer.Sync(context.Background())
|
||||||
c.AbortWithStatus(http.StatusInternalServerError)
|
if err != nil {
|
||||||
return
|
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
41
main.go
@@ -84,14 +84,7 @@ func (app *App) init() error {
|
|||||||
|
|
||||||
// Setup HTTP server
|
// Setup HTTP server
|
||||||
webHandler := web.NewWebHandler(app.db, app.syncService, app.garmin)
|
webHandler := web.NewWebHandler(app.db, app.syncService, app.garmin)
|
||||||
templateDir := os.Getenv("TEMPLATE_DIR")
|
// We've removed template loading since we're using static frontend
|
||||||
if templateDir == "" {
|
|
||||||
templateDir = "./internal/web/templates"
|
|
||||||
}
|
|
||||||
if err := webHandler.LoadTemplates(templateDir); err != nil {
|
|
||||||
return fmt.Errorf("failed to load templates: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.server = &http.Server{
|
app.server = &http.Server{
|
||||||
Addr: ":8888",
|
Addr: ":8888",
|
||||||
Handler: app.setupRoutes(webHandler),
|
Handler: app.setupRoutes(webHandler),
|
||||||
@@ -183,13 +176,39 @@ func initDatabase() (*database.SQLiteDB, error) {
|
|||||||
func (app *App) setupRoutes(webHandler *web.WebHandler) http.Handler {
|
func (app *App) setupRoutes(webHandler *web.WebHandler) http.Handler {
|
||||||
router := gin.Default()
|
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
|
// Health check
|
||||||
router.GET("/health", func(c *gin.Context) {
|
router.GET("/health", func(c *gin.Context) {
|
||||||
c.String(http.StatusOK, "OK")
|
c.String(http.StatusOK, "OK")
|
||||||
})
|
})
|
||||||
|
|
||||||
// Register web routes
|
|
||||||
webHandler.RegisterRoutes(router)
|
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user