mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 23:50:48 +02:00
- Implement FileMetadata model to track processed files - Create file metadata handlers for listing, searching, and viewing files - Add file metadata routes and UI components - Support advanced file search with multiple filters - Enhance job execution to capture file metadata during transfers - Implement file hash and duplicate detection logic
379 lines
10 KiB
Go
379 lines
10 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/starfleetcptn/gomft/components"
|
|
"github.com/starfleetcptn/gomft/internal/db"
|
|
)
|
|
|
|
// FileMetadataHandler handles displaying and searching file metadata
|
|
type FileMetadataHandler struct {
|
|
DB *db.DB
|
|
}
|
|
|
|
// Register registers the file metadata routes
|
|
func (h *FileMetadataHandler) Register(router *gin.RouterGroup) {
|
|
fileGroup := router.Group("/files")
|
|
|
|
fileGroup.GET("", h.ListFileMetadata)
|
|
fileGroup.GET("/:id", h.GetFileMetadataDetails)
|
|
fileGroup.GET("/job/:job_id", h.GetFileMetadataForJob)
|
|
fileGroup.GET("/search", h.SearchFileMetadata)
|
|
fileGroup.DELETE("/:id", h.DeleteFileMetadata)
|
|
}
|
|
|
|
// ListFileMetadata displays a list of file metadata with pagination and filtering options
|
|
func (h *FileMetadataHandler) ListFileMetadata(c *gin.Context) {
|
|
userID := c.GetUint("userID")
|
|
|
|
// Query parameters for pagination and filtering
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
|
if limit < 1 || limit > 100 {
|
|
limit = 50
|
|
}
|
|
|
|
status := c.Query("status")
|
|
jobIDStr := c.Query("job_id")
|
|
fileName := c.Query("filename")
|
|
|
|
// Base query
|
|
query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id")
|
|
|
|
// Apply filters
|
|
if jobIDStr != "" {
|
|
jobID, _ := strconv.ParseUint(jobIDStr, 10, 64)
|
|
query = query.Where("file_metadata.job_id = ?", jobID)
|
|
} else {
|
|
// Only show files from jobs created by the current user
|
|
query = query.Where("jobs.created_by = ?", userID)
|
|
}
|
|
|
|
if status != "" {
|
|
query = query.Where("file_metadata.status = ?", status)
|
|
}
|
|
|
|
if fileName != "" {
|
|
query = query.Where("file_metadata.file_name LIKE ?", "%"+fileName+"%")
|
|
}
|
|
|
|
// Count total records for pagination
|
|
var totalCount int64
|
|
query.Count(&totalCount)
|
|
|
|
// Retrieve file metadata with pagination
|
|
var fileMetadata []db.FileMetadata
|
|
offset := (page - 1) * limit
|
|
err := query.Preload("Job").Preload("Job.Config").
|
|
Order("file_metadata.processed_time DESC").
|
|
Offset(offset).Limit(limit).
|
|
Find(&fileMetadata).Error
|
|
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve file metadata"})
|
|
return
|
|
}
|
|
|
|
// Create context for template
|
|
ctx := components.CreateTemplateContext(c)
|
|
|
|
// Render the file metadata list template
|
|
data := components.FileMetadataListData{
|
|
Files: fileMetadata,
|
|
TotalCount: totalCount,
|
|
Page: page,
|
|
Limit: limit,
|
|
TotalPages: int(totalCount) / limit,
|
|
Filter: components.FileMetadataFilter{
|
|
Status: status,
|
|
JobID: jobIDStr,
|
|
FileName: fileName,
|
|
},
|
|
}
|
|
|
|
// If total count is not exactly divisible by limit, add one more page
|
|
if int(totalCount)%limit > 0 {
|
|
data.TotalPages++
|
|
}
|
|
|
|
c.Header("Content-Type", "text/html")
|
|
components.FileMetadataList(ctx, data).Render(ctx, c.Writer)
|
|
}
|
|
|
|
// GetFileMetadataDetails displays detailed information about a specific file
|
|
func (h *FileMetadataHandler) GetFileMetadataDetails(c *gin.Context) {
|
|
userID := c.GetUint("userID")
|
|
|
|
// Get file ID from URL parameter
|
|
fileID, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file ID"})
|
|
return
|
|
}
|
|
|
|
// Retrieve file metadata
|
|
var fileMetadata db.FileMetadata
|
|
err = h.DB.DB.Preload("Job").Preload("Job.Config").First(&fileMetadata, fileID).Error
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "File not found"})
|
|
return
|
|
}
|
|
|
|
// Check if the user has access to this file (file must belong to a job created by the user)
|
|
var jobCreator uint
|
|
err = h.DB.DB.Model(&db.Job{}).Where("id = ?", fileMetadata.JobID).Pluck("created_by", &jobCreator).Error
|
|
if err != nil || jobCreator != userID {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "You don't have permission to view this file"})
|
|
return
|
|
}
|
|
|
|
// Create context for template
|
|
ctx := components.CreateTemplateContext(c)
|
|
|
|
// Render the file metadata details template
|
|
data := components.FileMetadataDetailsData{
|
|
File: fileMetadata,
|
|
}
|
|
|
|
c.Header("Content-Type", "text/html")
|
|
components.FileMetadataDetails(ctx, data).Render(ctx, c.Writer)
|
|
}
|
|
|
|
// GetFileMetadataForJob displays file metadata for a specific job
|
|
func (h *FileMetadataHandler) GetFileMetadataForJob(c *gin.Context) {
|
|
userID := c.GetUint("userID")
|
|
|
|
// Get job ID from URL parameter
|
|
jobID, err := strconv.ParseUint(c.Param("job_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid job ID"})
|
|
return
|
|
}
|
|
|
|
// Check if the user has access to this job
|
|
var job db.Job
|
|
err = h.DB.DB.Where("id = ?", jobID).First(&job).Error
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Job not found"})
|
|
return
|
|
}
|
|
|
|
if job.CreatedBy != userID {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "You don't have permission to view this job's files"})
|
|
return
|
|
}
|
|
|
|
// Query parameters for pagination
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
|
if limit < 1 || limit > 100 {
|
|
limit = 50
|
|
}
|
|
|
|
status := c.Query("status")
|
|
fileName := c.Query("filename")
|
|
|
|
// Base query
|
|
query := h.DB.DB.Model(&db.FileMetadata{}).Where("job_id = ?", jobID)
|
|
|
|
// Apply filters
|
|
if status != "" {
|
|
query = query.Where("status = ?", status)
|
|
}
|
|
|
|
if fileName != "" {
|
|
query = query.Where("file_name LIKE ?", "%"+fileName+"%")
|
|
}
|
|
|
|
// Count total records for pagination
|
|
var totalCount int64
|
|
query.Count(&totalCount)
|
|
|
|
// Retrieve file metadata with pagination
|
|
var fileMetadata []db.FileMetadata
|
|
offset := (page - 1) * limit
|
|
err = query.Preload("Job").Preload("Job.Config").
|
|
Order("processed_time DESC").
|
|
Offset(offset).Limit(limit).
|
|
Find(&fileMetadata).Error
|
|
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve file metadata"})
|
|
return
|
|
}
|
|
|
|
// Create context for template
|
|
ctx := components.CreateTemplateContext(c)
|
|
|
|
// Render the file metadata list template
|
|
data := components.FileMetadataListData{
|
|
Files: fileMetadata,
|
|
TotalCount: totalCount,
|
|
Page: page,
|
|
Limit: limit,
|
|
TotalPages: int(totalCount) / limit,
|
|
Job: &job,
|
|
Filter: components.FileMetadataFilter{
|
|
Status: status,
|
|
JobID: strconv.FormatUint(uint64(job.ID), 10),
|
|
FileName: fileName,
|
|
},
|
|
}
|
|
|
|
// If total count is not exactly divisible by limit, add one more page
|
|
if int(totalCount)%limit > 0 {
|
|
data.TotalPages++
|
|
}
|
|
|
|
c.Header("Content-Type", "text/html")
|
|
components.FileMetadataList(ctx, data).Render(ctx, c.Writer)
|
|
}
|
|
|
|
// SearchFileMetadata searches file metadata based on various criteria
|
|
func (h *FileMetadataHandler) SearchFileMetadata(c *gin.Context) {
|
|
userID := c.GetUint("userID")
|
|
|
|
// Query parameters for search and pagination
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
|
if limit < 1 || limit > 100 {
|
|
limit = 50
|
|
}
|
|
|
|
status := c.Query("status")
|
|
jobIDStr := c.Query("job_id")
|
|
fileName := c.Query("filename")
|
|
hash := c.Query("hash")
|
|
startDate := c.Query("start_date")
|
|
endDate := c.Query("end_date")
|
|
|
|
// Base query
|
|
query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id")
|
|
|
|
// Apply filters
|
|
if jobIDStr != "" {
|
|
jobID, _ := strconv.ParseUint(jobIDStr, 10, 64)
|
|
query = query.Where("file_metadata.job_id = ?", jobID)
|
|
} else {
|
|
// Only show files from jobs created by the current user
|
|
query = query.Where("jobs.created_by = ?", userID)
|
|
}
|
|
|
|
if status != "" {
|
|
query = query.Where("file_metadata.status = ?", status)
|
|
}
|
|
|
|
if fileName != "" {
|
|
query = query.Where("file_metadata.file_name LIKE ?", "%"+fileName+"%")
|
|
}
|
|
|
|
if hash != "" {
|
|
query = query.Where("file_metadata.file_hash = ?", hash)
|
|
}
|
|
|
|
if startDate != "" {
|
|
query = query.Where("file_metadata.processed_time >= ?", startDate)
|
|
}
|
|
|
|
if endDate != "" {
|
|
query = query.Where("file_metadata.processed_time <= ?", endDate+" 23:59:59")
|
|
}
|
|
|
|
// Count total records for pagination
|
|
var totalCount int64
|
|
query.Count(&totalCount)
|
|
|
|
// Retrieve file metadata with pagination
|
|
var fileMetadata []db.FileMetadata
|
|
offset := (page - 1) * limit
|
|
err := query.Preload("Job").Preload("Job.Config").
|
|
Order("file_metadata.processed_time DESC").
|
|
Offset(offset).Limit(limit).
|
|
Find(&fileMetadata).Error
|
|
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve file metadata"})
|
|
return
|
|
}
|
|
|
|
// Create context for template
|
|
ctx := components.CreateTemplateContext(c)
|
|
|
|
// Render the file metadata search template
|
|
data := components.FileMetadataSearchData{
|
|
Files: fileMetadata,
|
|
TotalCount: totalCount,
|
|
Page: page,
|
|
Limit: limit,
|
|
TotalPages: int(totalCount) / limit,
|
|
Filter: components.FileMetadataFilter{
|
|
Status: status,
|
|
JobID: jobIDStr,
|
|
FileName: fileName,
|
|
Hash: hash,
|
|
StartDate: startDate,
|
|
EndDate: endDate,
|
|
},
|
|
}
|
|
|
|
// If total count is not exactly divisible by limit, add one more page
|
|
if int(totalCount)%limit > 0 {
|
|
data.TotalPages++
|
|
}
|
|
|
|
c.Header("Content-Type", "text/html")
|
|
components.FileMetadataSearch(ctx, data).Render(ctx, c.Writer)
|
|
}
|
|
|
|
// DeleteFileMetadata deletes a file metadata record
|
|
func (h *FileMetadataHandler) DeleteFileMetadata(c *gin.Context) {
|
|
userID := c.GetUint("userID")
|
|
|
|
// Get file ID from URL parameter
|
|
fileID, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file ID"})
|
|
return
|
|
}
|
|
|
|
// Check if the user has access to this file
|
|
var fileMetadata db.FileMetadata
|
|
err = h.DB.DB.Preload("Job").First(&fileMetadata, fileID).Error
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "File not found"})
|
|
return
|
|
}
|
|
|
|
var jobCreator uint
|
|
err = h.DB.DB.Model(&db.Job{}).Where("id = ?", fileMetadata.JobID).Pluck("created_by", &jobCreator).Error
|
|
if err != nil || jobCreator != userID {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "You don't have permission to delete this file"})
|
|
return
|
|
}
|
|
|
|
// Delete the file metadata
|
|
err = h.DB.DeleteFileMetadata(uint(fileID))
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete file metadata"})
|
|
return
|
|
}
|
|
|
|
// Redirect to the file list
|
|
c.Redirect(http.StatusFound, "/files")
|
|
}
|