feat: extend equipment and workout models with service tracking
This commit is contained in:
195
cmd/server/Untitled-1.dockerfile
Normal file
195
cmd/server/Untitled-1.dockerfile
Normal file
@@ -0,0 +1,195 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Default values
|
||||
IMAGE_NAME="rideaware"
|
||||
IMAGE_TAG="latest"
|
||||
NO_CACHE=false
|
||||
RUN_CONTAINER=false
|
||||
CONTAINER_NAME="rideaware-api"
|
||||
HOST_PORT="5010"
|
||||
CONTAINER_PORT="5010"
|
||||
|
||||
# Help function
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-t, --tag TAG Image tag (default: latest)
|
||||
-n, --name NAME Image name (default: rideaware)
|
||||
-r, --run Run container after build
|
||||
-c, --container NAME Container name when running (default: rideaware-api)
|
||||
-p, --port PORT Host port mapping (default: 5010)
|
||||
Format: HOST:CONTAINER or just HOST (uses same for container)
|
||||
--no-cache Build without cache
|
||||
-h, --help Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
$0 # Build as rideaware:latest
|
||||
$0 -t v1.0 # Build as rideaware:v1.0
|
||||
$0 -t dev --run # Build and run on port 5010
|
||||
$0 -t dev --run -p 5010 # Build and run on port 5010
|
||||
$0 -t dev --run -p 5010:5010 # Map host 5010 to container 5000
|
||||
$0 --no-cache -t prod # Build without cache as rideaware:prod
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-t|--tag)
|
||||
IMAGE_TAG="$2"
|
||||
shift 2
|
||||
;;
|
||||
-n|--name)
|
||||
IMAGE_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
-r|--run)
|
||||
RUN_CONTAINER=true
|
||||
shift
|
||||
;;
|
||||
-c|--container)
|
||||
CONTAINER_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--port)
|
||||
PORT_MAPPING="$2"
|
||||
# Parse port mapping
|
||||
if [[ $PORT_MAPPING == *":"* ]]; then
|
||||
HOST_PORT="${PORT_MAPPING%%:*}"
|
||||
CONTAINER_PORT="${PORT_MAPPING##*:}"
|
||||
else
|
||||
HOST_PORT="$PORT_MAPPING"
|
||||
CONTAINER_PORT="$PORT_MAPPING"
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--no-cache)
|
||||
NO_CACHE=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unknown option: $1${NC}"
|
||||
show_help
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
FULL_IMAGE="$IMAGE_NAME:$IMAGE_TAG"
|
||||
BUILD_ARGS=""
|
||||
|
||||
if [ "$NO_CACHE" = true ]; then
|
||||
BUILD_ARGS="--no-cache"
|
||||
fi
|
||||
|
||||
# Function to stop and remove container
|
||||
cleanup_container() {
|
||||
local name=$1
|
||||
|
||||
if podman ps -a --format "{{.Names}}" | grep -q "^${name}\$"; then
|
||||
echo -e "${YELLOW}Removing existing container: $name${NC}"
|
||||
|
||||
# Stop if running
|
||||
if podman ps --format "{{.Names}}" | grep -q "^${name}\$"; then
|
||||
echo " Stopping container..."
|
||||
podman kill "$name" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Remove
|
||||
echo " Removing container..."
|
||||
if podman rm "$name" 2>/dev/null; then
|
||||
echo -e "${GREEN} ✓ Container removed${NC}"
|
||||
else
|
||||
echo -e "${RED} ✗ Failed to remove container${NC}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Building Podman Image ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
echo -e "${YELLOW}Image: $FULL_IMAGE${NC}"
|
||||
echo ""
|
||||
|
||||
if ! podman build $BUILD_ARGS -f docker/Dockerfile -t "$FULL_IMAGE" .; then
|
||||
echo -e "${RED}✗ Build failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Image built successfully${NC}"
|
||||
echo ""
|
||||
|
||||
# Show image info
|
||||
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Image Details ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
podman images "$IMAGE_NAME:$IMAGE_TAG" \
|
||||
--format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.Created}}"
|
||||
echo ""
|
||||
|
||||
if [ "$RUN_CONTAINER" = true ]; then
|
||||
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Starting Container ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
|
||||
# Cleanup existing container FIRST (before checking port)
|
||||
if ! cleanup_container "$CONTAINER_NAME"; then
|
||||
echo -e "${RED}✗ Failed to clean up existing container${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Starting new container: $CONTAINER_NAME"
|
||||
echo "Port mapping: $HOST_PORT:$CONTAINER_PORT"
|
||||
|
||||
if podman run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-e PORT="$CONTAINER_PORT" \
|
||||
-p "$HOST_PORT:$CONTAINER_PORT" \
|
||||
--env-file .env \
|
||||
"$FULL_IMAGE"; then
|
||||
echo -e "${GREEN}✓ Container running: $CONTAINER_NAME${NC}"
|
||||
echo ""
|
||||
|
||||
# Wait for startup
|
||||
sleep 2
|
||||
|
||||
echo -e "${YELLOW}Container logs:${NC}"
|
||||
podman logs "$CONTAINER_NAME"
|
||||
echo ""
|
||||
|
||||
echo -e "${GREEN}API available at: http://localhost:$HOST_PORT${NC}"
|
||||
echo -e "${YELLOW}To view logs: podman logs -f $CONTAINER_NAME${NC}"
|
||||
echo -e "${YELLOW}To stop: podman kill $CONTAINER_NAME${NC}"
|
||||
echo -e "${YELLOW}To remove: podman rm $CONTAINER_NAME${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Failed to start container${NC}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}To run the container:${NC}"
|
||||
echo " podman run -d --name $CONTAINER_NAME -e PORT=$CONTAINER_PORT -p $HOST_PORT:$CONTAINER_PORT --env-file .env $FULL_IMAGE"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Or use this script with --run:${NC}"
|
||||
echo " $0 -t $IMAGE_TAG --run -p $HOST_PORT"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Done!${NC}"
|
||||
@@ -10,10 +10,15 @@ import (
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
"rideaware/internal/activity"
|
||||
"rideaware/internal/auth"
|
||||
"rideaware/internal/config"
|
||||
"rideaware/internal/equipment"
|
||||
"rideaware/internal/export"
|
||||
"rideaware/internal/integration"
|
||||
"rideaware/internal/middleware"
|
||||
"rideaware/internal/stats"
|
||||
"rideaware/internal/templates"
|
||||
"rideaware/internal/user"
|
||||
"rideaware/internal/workout"
|
||||
"rideaware/pkg/database"
|
||||
@@ -34,6 +39,8 @@ func main() {
|
||||
&user.Session{},
|
||||
&equipment.Equipment{},
|
||||
&workout.Workout{},
|
||||
&integration.OAuthConnection{},
|
||||
&integration.OAuthState{},
|
||||
); err != nil {
|
||||
log.Fatalf("Failed to migrate database: %v", err)
|
||||
}
|
||||
@@ -41,6 +48,9 @@ func main() {
|
||||
// Initialize JWT config
|
||||
config.InitJWT()
|
||||
|
||||
// Initialize OAuth config
|
||||
config.InitOAuth()
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
// Logging middleware
|
||||
@@ -107,6 +117,12 @@ func setupRoutes(r *chi.Mux) {
|
||||
r.Post("/api/password-reset/confirm", authHandler.ConfirmPasswordReset)
|
||||
r.Post("/api/refresh-token", authHandler.RefreshToken)
|
||||
|
||||
// OAuth callbacks (public - called by provider redirects)
|
||||
garminHandler := integration.NewGarminHandler()
|
||||
wahooHandler := integration.NewWahooHandler()
|
||||
r.Get("/api/garmin/callback", garminHandler.Callback)
|
||||
r.Get("/api/wahoo/callback", wahooHandler.Callback)
|
||||
|
||||
// Protected routes
|
||||
authMiddleware := middleware.NewAuthMiddleware()
|
||||
r.Route("/api/protected", func(r chi.Router) {
|
||||
@@ -124,6 +140,10 @@ func setupRoutes(r *chi.Mux) {
|
||||
r.Put("/equipment", equipmentHandler.UpdateEquipment)
|
||||
r.Delete("/equipment", equipmentHandler.DeleteEquipment)
|
||||
|
||||
// Equipment service tracking
|
||||
r.Post("/equipment/service", equipmentHandler.RecordService)
|
||||
r.Get("/equipment/service-status", equipmentHandler.GetServiceStatus)
|
||||
|
||||
// Training zones
|
||||
r.Get("/zones", equipmentHandler.GetTrainingZones)
|
||||
|
||||
@@ -132,10 +152,45 @@ func setupRoutes(r *chi.Mux) {
|
||||
r.Post("/workouts", workoutHandler.CreateWorkout)
|
||||
r.Get("/workouts", workoutHandler.GetWorkouts)
|
||||
r.Get("/workouts/month", workoutHandler.GetWorkoutsByMonth)
|
||||
r.Get("/workouts/equipment-stats", workoutHandler.GetEquipmentStats)
|
||||
r.Put("/workouts", workoutHandler.UpdateWorkout)
|
||||
r.Delete("/workouts", workoutHandler.DeleteWorkout)
|
||||
r.Get("/workout-types", workoutHandler.GetWorkoutTypes)
|
||||
r.Post("/workouts/upload", workoutHandler.UploadWorkoutFile)
|
||||
|
||||
// Activity import (FIT/TCX/GPX)
|
||||
activityHandler := activity.NewHandler()
|
||||
r.Post("/workouts/import", activityHandler.ImportActivity)
|
||||
|
||||
// Workout export routes
|
||||
exportHandler := export.NewHandler()
|
||||
r.Get("/workouts/export/fit", exportHandler.ExportFIT)
|
||||
r.Get("/workouts/export/zwo", exportHandler.ExportZWO)
|
||||
|
||||
// Garmin integration routes
|
||||
r.Get("/garmin/auth", garminHandler.StartAuth)
|
||||
r.Post("/workouts/push/garmin", garminHandler.PushWorkout)
|
||||
r.Get("/garmin/status", garminHandler.ConnectionStatus)
|
||||
r.Delete("/garmin/disconnect", garminHandler.Disconnect)
|
||||
|
||||
// Wahoo integration routes
|
||||
r.Get("/wahoo/auth", wahooHandler.StartAuth)
|
||||
r.Post("/workouts/push/wahoo", wahooHandler.PushWorkout)
|
||||
r.Get("/wahoo/status", wahooHandler.ConnectionStatus)
|
||||
r.Delete("/wahoo/disconnect", wahooHandler.Disconnect)
|
||||
|
||||
// Stats routes
|
||||
statsHandler := stats.NewHandler()
|
||||
r.Get("/stats/summary", statsHandler.GetSummary)
|
||||
r.Get("/stats/weekly", statsHandler.GetWeeklyStats)
|
||||
r.Get("/stats/monthly", statsHandler.GetMonthlyStats)
|
||||
r.Get("/stats/personal-bests", statsHandler.GetPersonalBests)
|
||||
|
||||
// Workout template routes
|
||||
templateHandler := templates.NewHandler()
|
||||
r.Get("/workout-templates", templateHandler.ListTemplates)
|
||||
r.Get("/workout-templates/detail", templateHandler.GetTemplate)
|
||||
r.Post("/workouts/from-template", templateHandler.CreateFromTemplate)
|
||||
})
|
||||
|
||||
log.Println("✅ Routes registered successfully")
|
||||
|
||||
Reference in New Issue
Block a user