mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-10-13 01:28:17 +02:00
refactor: job scheduling and execution components
- Refactored existing scheduler logic to integrate new components and improve job management.
This commit is contained in:
1 parent
68dd9fc173
commit
ae18cd1a12
10 files changed
+2593
-2991
No files matched your search
@@ -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}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
Reference in new issue
Block a user