refactor: job scheduling and execution components

- Refactored existing scheduler logic to integrate new components and improve job management.
This commit is contained in:
StarFleetCPTN committed 2025-03-29 19:27:19 -07:00
1 parent 68dd9fc173
commit ae18cd1a12
10 files changed
+2593 -2991

No files matched your search

-92
View File
@@ -1,92 +0,0 @@
package handlers
import (
"fmt"
"testing"
"github.com/gin-gonic/gin"
"github.com/glebarez/sqlite"
"github.com/starfleetcptn/gomft/internal/db"
"github.com/starfleetcptn/gomft/internal/email"
"github.com/starfleetcptn/gomft/internal/scheduler"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// Static counter to ensure unique emails for each test
var testEmailCounter int = 0
func setupTestHandlers(t *testing.T) (*Handlers, *gin.Engine) {
// Set Gin to test mode
gin.SetMode(gin.TestMode)
// Create a test DB
testDB := setupTestDB(t)
// Create a mock scheduler
mockScheduler := &scheduler.Scheduler{}
// Create a mock email service
mockEmailService := &email.Service{}
// Create test handlers
handlers := NewHandlers(
testDB,
mockScheduler,
"test-jwt-secret",
"test-db-path",
"test-backup-dir",
"test-logs-dir",
mockEmailService,
)
// Create a test router
router := gin.New()
return handlers, router
}
// setupTestDB creates a test database for handler tests
func setupTestDB(t *testing.T) *db.DB {
// Set up an in-memory SQLite DB
gormDB, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open in-memory database: %v", err)
}
// Run migrations
err = gormDB.AutoMigrate(
&db.User{},
&db.PasswordHistory{},
&db.PasswordResetToken{},
&db.TransferConfig{},
&db.Job{},
&db.JobHistory{},
&db.FileMetadata{},
)
if err != nil {
t.Fatalf("Failed to migrate database: %v", err)
}
// Create a test admin user with a unique email
testEmailCounter++
testEmail := fmt.Sprintf("test%d@example.com", testEmailCounter)
// Generate a hashed password for "admin"
hashedPassword, err := bcrypt.GenerateFromPassword([]byte("admin"), bcrypt.DefaultCost)
if err != nil {
t.Fatalf("Failed to hash password: %v", err)
}
admin := db.User{
Email: testEmail,
PasswordHash: string(hashedPassword),
}
admin.SetIsAdmin(true)
if err := gormDB.Create(&admin).Error; err != nil {
t.Fatalf("Failed to create test admin user: %v", err)
}
return &db.DB{DB: gormDB}
}
-430
View File
@@ -15,102 +15,6 @@ import (
"golang.org/x/crypto/bcrypt"
)
// HandleUsers handles the GET /admin/users route
// func (h *Handlers) HandleUsers(c *gin.Context) {
// var users []db.User
// if err := h.DB.Find(&users).Error; err != nil {
// c.String(http.StatusInternalServerError, "Failed to retrieve users")
// return
// }
// // Create the data for the Users component, not UserManagement
// data := components.UsersData{
// Users: users,
// }
// // Use the Users component from users.templ and ensure context flows through consistently
// ctx := h.CreateTemplateContext(c)
// components.Users(ctx, data).Render(ctx, c.Writer)
// }
// HandleNewUser handles the GET /admin/users/new route
// func (h *Handlers) HandleNewUser(c *gin.Context) {
// // Create the data for the UserForm component
// data := components.UserFormData{
// IsNew: true,
// ErrorMessage: "",
// }
// // Use consistent context handling
// ctx := h.CreateTemplateContext(c)
// err := components.UserForm(ctx, data).Render(ctx, c.Writer)
// if err != nil {
// log.Printf("ERROR rendering UserForm: %v", err)
// c.String(http.StatusInternalServerError, "Error rendering form: %v", err)
// return
// }
// }
// HandleCreateUser handles the POST /admin/users/new route
// func (h *Handlers) HandleCreateUser(c *gin.Context) {
// email := c.PostForm("email")
// password := c.PostForm("password")
// isAdmin := c.PostForm("is_admin") == "on"
// // Check if email already exists
// var existingUser db.User
// if err := h.DB.Where("email = ?", email).First(&existingUser).Error; err == nil {
// c.String(http.StatusBadRequest, "Email already exists")
// return
// }
// // Hash the password
// hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
// if err != nil {
// c.String(http.StatusInternalServerError, "Failed to hash password")
// return
// }
// // Create the user
// user := db.User{
// Email: email,
// PasswordHash: string(hashedPassword),
// LastPasswordChange: time.Now(),
// }
// user.SetIsAdmin(isAdmin)
// if err := h.DB.Create(&user).Error; err != nil {
// c.String(http.StatusInternalServerError, "Failed to create user")
// return
// }
// c.Redirect(http.StatusSeeOther, "/admin/users")
// }
// HandleDeleteUser handles the POST /admin/users/delete route
// func (h *Handlers) HandleDeleteUser(c *gin.Context) {
// userID, err := strconv.ParseUint(c.Param("id"), 10, 32)
// if err != nil {
// c.String(http.StatusBadRequest, "Invalid user ID")
// return
// }
// // Don't allow deleting the current user
// currentUserID := c.GetUint("userID")
// if uint(userID) == currentUserID {
// c.String(http.StatusBadRequest, "Cannot delete your own account")
// return
// }
// // Delete the user
// if err := h.DB.Delete(&db.User{}, userID).Error; err != nil {
// c.String(http.StatusInternalServerError, "Failed to delete user")
// return
// }
// c.Redirect(http.StatusSeeOther, "/admin/users")
// }
// HandleRegisterPage handles the GET /register route
func (h *Handlers) HandleRegisterPage(c *gin.Context) {
// Check if any users exist
@@ -303,39 +207,6 @@ func (h *Handlers) AdminCreateUser(c *gin.Context) {
c.Redirect(http.StatusFound, "/admin/users")
}
// HandleEditUser handles the GET /admin/users/:id/edit route
// func (h *Handlers) HandleEditUser(c *gin.Context) {
// id := c.Param("id")
// var user db.User
// if err := h.DB.First(&user, id).Error; err != nil {
// c.Redirect(http.StatusFound, "/admin/users")
// return
// }
// // Get user's roles
// userRoles, err := h.DB.GetUserRoles(user.ID)
// if err != nil {
// log.Printf("Error fetching user roles: %v", err)
// }
// // Get all available roles
// var allRoles []db.Role
// if err := h.DB.Find(&allRoles).Error; err != nil {
// log.Printf("Error fetching roles: %v", err)
// }
// // Use UserEdit component to match the form that's being submitted
// data := components.UserEditData{
// User: &user,
// Roles: allRoles,
// UserRoles: userRoles,
// IsNew: false,
// }
// ctx := h.CreateTemplateContext(c)
// components.UserEdit(ctx, data).Render(ctx, c.Writer)
// }
// AdminUpdateUser handles the PUT /admin/users/:id route
func (h *Handlers) AdminUpdateUser(c *gin.Context) {
id := c.Param("id")
@@ -569,307 +440,6 @@ func (h *Handlers) AdminDeleteUser(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "User deleted successfully"})
}
// AdminRoles handles the GET /admin/roles route
// func (h *Handlers) AdminRoles(c *gin.Context) {
// var dbRoles []db.Role
// if err := h.DB.Find(&dbRoles).Error; err != nil {
// c.String(http.StatusInternalServerError, "Error fetching roles")
// return
// }
// // Convert db.Role to components.Role
// var roles []components.Role
// for _, dbRole := range dbRoles {
// role := components.Role{
// ID: dbRole.ID,
// Name: dbRole.Name,
// Description: dbRole.Description,
// Permissions: dbRole.Permissions,
// }
// roles = append(roles, role)
// }
// // Use components instead of HTML templates
// data := components.RolesData{
// Roles: roles,
// }
// ctx := h.CreateTemplateContext(c)
// components.AdminRoles(ctx, data).Render(ctx, c.Writer)
// }
// AdminNewRolePage handles the GET /admin/roles/new route
// func (h *Handlers) AdminNewRolePage(c *gin.Context) {
// // Create an empty role for the form
// role := &components.Role{
// ID: 0,
// Name: "",
// Description: "",
// Permissions: []string{},
// }
// // All available permissions
// allPermissions := []string{
// "users.view", "users.create", "users.edit", "users.delete",
// "roles.view", "roles.create", "roles.edit", "roles.delete",
// "transfers.view", "transfers.create", "transfers.edit", "transfers.delete",
// "audit.view",
// }
// // Use components instead of HTML templates
// data := components.RoleFormData{
// Role: role,
// IsNew: true,
// AllPermissions: allPermissions,
// }
// ctx := h.CreateTemplateContext(c)
// components.AdminRoleForm(ctx, data).Render(ctx, c.Writer)
// }
// AdminCreateRole handles the POST /admin/roles route
// func (h *Handlers) AdminCreateRole(c *gin.Context) {
// name := c.PostForm("name")
// description := c.PostForm("description")
// permissions := c.PostFormArray("permissions[]")
// // Create role
// role := &db.Role{
// Name: name,
// Description: description,
// }
// role.SetPermissions(permissions)
// // Validate role
// if err := role.Validate(); err != nil {
// ctx := components.CreateTemplateContext(c)
// data := components.RoleFormData{
// Role: &components.Role{Name: name, Description: description, Permissions: permissions},
// IsNew: true,
// ErrorMessage: err.Error(),
// AllPermissions: GetAllPermissions(),
// }
// _ = components.AdminRoleForm(ctx, data).Render(ctx, c.Writer)
// return
// }
// // Start transaction
// tx := h.DB.Begin()
// if err := tx.Error; err != nil {
// handleRoleError(c, role, true, "Failed to begin transaction: "+err.Error())
// return
// }
// // Create role in database
// if err := tx.Create(role).Error; err != nil {
// tx.Rollback()
// handleRoleError(c, role, true, "Failed to create role: "+err.Error())
// return
// }
// // Create audit log
// userID := getUserID(c) // Implement this helper to get current user ID
// if err := role.AuditLog(tx, "create", userID); err != nil {
// tx.Rollback()
// handleRoleError(c, role, true, "Failed to create audit log: "+err.Error())
// return
// }
// // Commit transaction
// if err := tx.Commit().Error; err != nil {
// handleRoleError(c, role, true, "Failed to commit transaction: "+err.Error())
// return
// }
// c.Redirect(http.StatusFound, "/admin/roles")
// }
// AdminEditRolePage handles the GET /admin/roles/:id/edit route
// func (h *Handlers) AdminEditRolePage(c *gin.Context) {
// id := c.Param("id")
// var dbRole db.Role
// if err := h.DB.First(&dbRole, id).Error; err != nil {
// c.Redirect(http.StatusFound, "/admin/roles")
// return
// }
// // Convert db.Role to components.Role
// role := &components.Role{
// ID: dbRole.ID,
// Name: dbRole.Name,
// Description: dbRole.Description,
// Permissions: dbRole.Permissions,
// }
// // All available permissions
// allPermissions := []string{
// "users.view", "users.create", "users.edit", "users.delete",
// "roles.view", "roles.create", "roles.edit", "roles.delete",
// "transfers.view", "transfers.create", "transfers.edit", "transfers.delete",
// "audit.view",
// }
// // Use components instead of HTML templates
// data := components.RoleFormData{
// Role: role,
// IsNew: false,
// AllPermissions: allPermissions,
// }
// ctx := h.CreateTemplateContext(c)
// components.AdminRoleForm(ctx, data).Render(ctx, c.Writer)
// }
// AdminUpdateRole handles the PUT /admin/roles/:id route
// func (h *Handlers) AdminUpdateRole(c *gin.Context) {
// id := c.Param("id")
// var role db.Role
// if err := h.DB.First(&role, id).Error; err != nil {
// c.String(http.StatusNotFound, "Role not found")
// return
// }
// // Store original role state for audit log
// oldRole := role
// // Update role with form data
// name := c.PostForm("name")
// if name != "" {
// role.Name = name
// }
// description := c.PostForm("description")
// if description != "" {
// role.Description = description
// }
// // Process permissions
// permissionsStr := c.PostForm("permissions")
// if permissionsStr != "" {
// permissions := strings.Split(permissionsStr, ",")
// for i, p := range permissions {
// permissions[i] = strings.TrimSpace(p)
// }
// role.Permissions = db.Permissions(permissions)
// }
// // Start a transaction
// tx := h.DB.Begin()
// if tx.Error != nil {
// c.String(http.StatusInternalServerError, "Failed to begin transaction")
// return
// }
// // Save the role
// if err := tx.Save(&role).Error; err != nil {
// tx.Rollback()
// c.String(http.StatusInternalServerError, fmt.Sprintf("Failed to update role: %v", err))
// return
// }
// // Create audit log entry
// adminID := c.GetUint("userID")
// auditDetails := map[string]interface{}{
// "name": role.Name,
// "description": role.Description,
// "permissions": role.Permissions,
// "previous_state": map[string]interface{}{
// "name": oldRole.Name,
// "description": oldRole.Description,
// "permissions": oldRole.Permissions,
// },
// }
// auditLog := db.AuditLog{
// Action: "update",
// EntityType: "role",
// EntityID: role.ID,
// UserID: adminID,
// Details: auditDetails,
// Timestamp: time.Now(),
// }
// if err := tx.Create(&auditLog).Error; err != nil {
// tx.Rollback()
// c.String(http.StatusInternalServerError, "Failed to create audit log")
// return
// }
// // Commit the transaction
// if err := tx.Commit().Error; err != nil {
// c.String(http.StatusInternalServerError, "Failed to commit transaction")
// return
// }
// c.Redirect(http.StatusFound, "/admin/roles")
// }
// AdminDeleteRole handles the DELETE /admin/roles/:id route
// func (h *Handlers) AdminDeleteRole(c *gin.Context) {
// id := c.Param("id")
// adminID := c.GetUint("userID")
// var role db.Role
// if err := h.DB.First(&role, id).Error; err != nil {
// c.JSON(http.StatusNotFound, gin.H{"error": "Role not found"})
// return
// }
// // Check if role is in use
// var count int64
// h.DB.Table("user_roles").Where("role_id = ?", role.ID).Count(&count)
// if count > 0 {
// c.JSON(http.StatusBadRequest, gin.H{"error": "Role is assigned to users and cannot be deleted"})
// return
// }
// // Start a transaction
// tx := h.DB.Begin()
// if tx.Error != nil {
// c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to begin transaction"})
// return
// }
// // Create audit log before deletion
// auditDetails := map[string]interface{}{
// "name": role.Name,
// "description": role.Description,
// "permissions": role.Permissions,
// }
// auditLog := db.AuditLog{
// Action: "delete",
// EntityType: "role",
// EntityID: role.ID,
// UserID: adminID,
// Details: auditDetails,
// Timestamp: time.Now(),
// }
// if err := tx.Create(&auditLog).Error; err != nil {
// tx.Rollback()
// c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create audit log"})
// return
// }
// // Delete the role
// if err := tx.Delete(&role).Error; err != nil {
// tx.Rollback()
// c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to delete role: %v", err)})
// return
// }
// // Commit the transaction
// if err := tx.Commit().Error; err != nil {
// c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to commit transaction"})
// return
// }
// c.JSON(http.StatusOK, gin.H{"message": "Role deleted successfully"})
// }
// AdminUserRoles handles the GET /admin/users/:id/roles route
func (h *Handlers) AdminUserRoles(c *gin.Context) {
id := c.Param("id")