Initial commit: Homelab API

This commit is contained in:
Cosmo
2026-02-06 11:19:55 +00:00
commit 5a40127edd
26 changed files with 2807 additions and 0 deletions

128
cmd/api/main.go Normal file
View File

@@ -0,0 +1,128 @@
package main
import (
"log"
"net/http"
"os"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/daniil/homelab-api/internal/config"
"github.com/daniil/homelab-api/internal/handler"
customMiddleware "github.com/daniil/homelab-api/internal/middleware"
"github.com/daniil/homelab-api/internal/repository"
"github.com/daniil/homelab-api/internal/service"
)
func main() {
cfg := config.Load()
// Initialize database
db, err := repository.NewDB(cfg.DatabaseURL)
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer db.Close()
// Run migrations
if err := repository.RunMigrations(db); err != nil {
log.Fatalf("Failed to run migrations: %v", err)
}
// Initialize repositories
userRepo := repository.NewUserRepository(db)
habitRepo := repository.NewHabitRepository(db)
taskRepo := repository.NewTaskRepository(db)
emailTokenRepo := repository.NewEmailTokenRepository(db)
// Initialize services
emailService := service.NewEmailService(cfg.ResendAPIKey, cfg.FromEmail, cfg.FromName, cfg.AppURL)
authService := service.NewAuthService(userRepo, emailTokenRepo, emailService, cfg.JWTSecret)
habitService := service.NewHabitService(habitRepo)
taskService := service.NewTaskService(taskRepo)
// Initialize handlers
authHandler := handler.NewAuthHandler(authService)
habitHandler := handler.NewHabitHandler(habitService)
taskHandler := handler.NewTaskHandler(taskService)
healthHandler := handler.NewHealthHandler()
// Initialize middleware
authMiddleware := customMiddleware.NewAuthMiddleware(cfg.JWTSecret)
// Setup router
r := chi.NewRouter()
// Global middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.RequestID)
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"https://*", "http://*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-Request-ID"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300,
}))
// Public routes
r.Get("/health", healthHandler.Health)
// Auth routes (public)
r.Post("/auth/register", authHandler.Register)
r.Post("/auth/login", authHandler.Login)
r.Post("/auth/refresh", authHandler.Refresh)
r.Post("/auth/verify-email", authHandler.VerifyEmail)
r.Post("/auth/resend-verification", authHandler.ResendVerification)
r.Post("/auth/forgot-password", authHandler.ForgotPassword)
r.Post("/auth/reset-password", authHandler.ResetPassword)
// Protected routes
r.Group(func(r chi.Router) {
r.Use(authMiddleware.Authenticate)
// User routes
r.Get("/auth/me", authHandler.Me)
r.Put("/auth/me", authHandler.UpdateProfile)
r.Put("/auth/password", authHandler.ChangePassword)
// Habits routes
r.Get("/habits", habitHandler.List)
r.Post("/habits", habitHandler.Create)
r.Get("/habits/{id}", habitHandler.Get)
r.Put("/habits/{id}", habitHandler.Update)
r.Delete("/habits/{id}", habitHandler.Delete)
// Habit logs
r.Post("/habits/{id}/log", habitHandler.Log)
r.Get("/habits/{id}/logs", habitHandler.GetLogs)
r.Delete("/habits/{id}/logs/{logId}", habitHandler.DeleteLog)
// Stats
r.Get("/habits/stats", habitHandler.Stats)
r.Get("/habits/{id}/stats", habitHandler.HabitStats)
// Tasks routes
r.Get("/tasks", taskHandler.List)
r.Get("/tasks/today", taskHandler.Today)
r.Post("/tasks", taskHandler.Create)
r.Get("/tasks/{id}", taskHandler.Get)
r.Put("/tasks/{id}", taskHandler.Update)
r.Delete("/tasks/{id}", taskHandler.Delete)
r.Post("/tasks/{id}/complete", taskHandler.Complete)
r.Post("/tasks/{id}/uncomplete", taskHandler.Uncomplete)
})
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("Server starting on :%s", port)
if err := http.ListenAndServe(":"+port, r); err != nil {
log.Fatalf("Server failed: %v", err)
}
}