Add PDF reports, file uploads, admin auth, and comprehensive tests

Features:
- PDF service for generating task reports with ReportLab-style formatting
- Storage service for file uploads (local and S3-compatible)
- Admin authentication middleware with JWT support
- Admin user model and repository

Infrastructure:
- Updated Docker configuration for admin panel builds
- Email service enhancements for task notifications
- Updated router with admin and file upload routes
- Environment configuration updates

Tests:
- Unit tests for handlers (auth, residence, task)
- Unit tests for models (user, residence, task)
- Unit tests for repositories (user, residence, task)
- Unit tests for services (residence, task)
- Integration test setup
- Test utilities for mocking database and services

Database:
- Admin user seed data
- Updated test data seeds

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-27 23:36:20 -06:00
parent 2817deee3c
commit 469f21a833
50 changed files with 6795 additions and 582 deletions

View File

@@ -1,81 +0,0 @@
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
_ "github.com/lib/pq" // PostgreSQL driver for GoAdmin
"github.com/treytartt/mycrib-api/internal/admin"
"github.com/treytartt/mycrib-api/internal/config"
"github.com/treytartt/mycrib-api/internal/database"
"github.com/treytartt/mycrib-api/pkg/utils"
)
func main() {
// Initialize logger
utils.InitLogger(true)
// Load configuration
cfg, err := config.Load()
if err != nil {
log.Fatal().Err(err).Msg("Failed to load configuration")
}
// Initialize database
db, err := database.Connect(&cfg.Database, cfg.Server.Debug)
if err != nil {
log.Fatal().Err(err).Msg("Failed to connect to database")
}
_ = db // Database handle managed by GoAdmin
// Get underlying *sql.DB for cleanup
sqlDB, _ := db.DB()
defer sqlDB.Close()
// Set Gin mode
if cfg.Server.Debug {
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
// Create Gin router
r := gin.New()
r.Use(gin.Recovery())
r.Use(gin.Logger())
// Setup GoAdmin
eng, err := admin.Setup(r, cfg)
if err != nil {
log.Fatal().Err(err).Msg("Failed to setup GoAdmin")
}
_ = eng // Engine is used internally
// Determine admin port (default: 9000, or PORT+1000)
adminPort := 9000
if cfg.Server.Port > 0 {
adminPort = cfg.Server.Port + 1000
}
// Start server
addr := fmt.Sprintf(":%d", adminPort)
log.Info().Str("addr", addr).Msg("Starting MyCrib Admin Panel")
// Handle graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
go func() {
if err := r.Run(addr); err != nil {
log.Fatal().Err(err).Msg("Failed to start admin server")
}
}()
<-quit
log.Info().Msg("Shutting down admin server...")
}

View File

@@ -9,11 +9,9 @@ import (
"syscall"
"time"
_ "github.com/lib/pq" // PostgreSQL driver for GoAdmin
"github.com/rs/zerolog/log"
"gorm.io/gorm"
"github.com/treytartt/mycrib-api/internal/admin"
"github.com/treytartt/mycrib-api/internal/config"
"github.com/treytartt/mycrib-api/internal/database"
"github.com/treytartt/mycrib-api/internal/router"
@@ -93,24 +91,36 @@ func main() {
Msg("Email service not configured - emails will not be sent")
}
// Setup router with dependencies
deps := &router.Dependencies{
DB: db,
Cache: cache,
Config: cfg,
EmailService: emailService,
}
r := router.SetupRouter(deps)
// Setup GoAdmin panel at /admin
if db != nil {
if _, err := admin.Setup(r, cfg); err != nil {
log.Warn().Err(err).Msg("Failed to setup admin panel - admin will be unavailable")
// Initialize storage service for file uploads
var storageService *services.StorageService
if cfg.Storage.UploadDir != "" {
storageService, err = services.NewStorageService(&cfg.Storage)
if err != nil {
log.Warn().Err(err).Msg("Failed to initialize storage service - uploads disabled")
} else {
log.Info().Msg("Admin panel available at /admin")
log.Info().
Str("upload_dir", cfg.Storage.UploadDir).
Str("base_url", cfg.Storage.BaseURL).
Int64("max_file_size", cfg.Storage.MaxFileSize).
Msg("Storage service initialized")
}
}
// Initialize PDF service for report generation
pdfService := services.NewPDFService()
log.Info().Msg("PDF service initialized")
// Setup router with dependencies (includes admin panel at /admin)
deps := &router.Dependencies{
DB: db,
Cache: cache,
Config: cfg,
EmailService: emailService,
PDFService: pdfService,
StorageService: storageService,
}
r := router.SetupRouter(deps)
// Create HTTP server
srv := &http.Server{
Addr: fmt.Sprintf(":%d", cfg.Server.Port),