refactor: Enhance container handling and logging in HTTP server
- Update router comments for clarity on endpoint functionality. - Add debug logging in CreateContainerPOST handler to track ShortID and image ID conversion. - Modify ActionSuccess function to pass a success message to the template. - Refactor Fragment function to support data rendering in HTML fragments. - Include jQuery script in header template for improved functionality. - Enhance custom.js with debug logging for admin dashboard initialization and HTMX events.
- commit
- efe619a1feffd6858f57a9a3493cfd7e3ac0b9ef
- author
- bamen · · 2025-03-17 17:15 UTC
- parent
- dc2c50b
- signature
- unsigned
6 files · +74 -6
M internal/httpserve/handlers/container_crud.go +7 -1
| @@ -126,18 +126,24 @@ func CreateContainerFullGET(c echo.Context, a *server.App) error { | |||
| 126 | 126 | ||
| 127 | 127 | // CreateContainerPOST handles the create container form submission | |
| 128 | 128 | func CreateContainerPOST(c echo.Context, a *server.App) error { | |
| 129 | - | // Retreive the ShortID of the image from the URL | |
| 129 | + | log.Debug("CreateContainerPOST handler called") | |
| 130 | + | ||
| 131 | + | // Retrieve the ShortID of the image from the URL | |
| 130 | 132 | ShortID := c.Param("ID") | |
| 133 | + | log.Debug("Received ShortID for container creation:", "ShortID", ShortID) | |
| 131 | 134 | ||
| 132 | 135 | // Convert the ShortID to a full image ID | |
| 133 | 136 | imageID, err := FromShortIDToImageID(ShortID) | |
| 134 | 137 | if err != nil { | |
| 138 | + | log.Error("Error converting ShortID to full image ID:", "error", err) | |
| 135 | 139 | return sendError(c, err) | |
| 136 | 140 | } | |
| 141 | + | log.Debug("Converted ShortID to full image ID:", "imageID", imageID) | |
| 137 | 142 | ||
| 138 | 143 | // Get the image info | |
| 139 | 144 | _, err = docker.GetImageInfo(imageID) | |
| 140 | 145 | if err != nil { | |
| 146 | + | log.Error("Error getting image info:", "error", err) | |
| 141 | 147 | return sendError(c, err) | |
| 142 | 148 | } | |
| 143 | 149 | ||
| @@ -126,18 +126,24 @@ func CreateContainerFullGET(c echo.Context, a *server.App) error { | |||||
| 126 | 126 | ||||
| 127 | // CreateContainerPOST handles the create container form submission | 127 | // CreateContainerPOST handles the create container form submission | ||
| 128 | func CreateContainerPOST(c echo.Context, a *server.App) error { | 128 | func CreateContainerPOST(c echo.Context, a *server.App) error { | ||
| 129 | - | // Retreive the ShortID of the image from the URL | 129 | + | log.Debug("CreateContainerPOST handler called") |
| 130 | + | ||||
| 131 | + | // Retrieve the ShortID of the image from the URL | |||
| 130 | ShortID := c.Param("ID") | 132 | ShortID := c.Param("ID") | ||
| 133 | + | log.Debug("Received ShortID for container creation:", "ShortID", ShortID) | |||
| 131 | 134 | ||||
| 132 | // Convert the ShortID to a full image ID | 135 | // Convert the ShortID to a full image ID | ||
| 133 | imageID, err := FromShortIDToImageID(ShortID) | 136 | imageID, err := FromShortIDToImageID(ShortID) | ||
| 134 | if err != nil { | 137 | if err != nil { | ||
| 138 | + | log.Error("Error converting ShortID to full image ID:", "error", err) | |||
| 135 | return sendError(c, err) | 139 | return sendError(c, err) | ||
| 136 | } | 140 | } | ||
| 141 | + | log.Debug("Converted ShortID to full image ID:", "imageID", imageID) | |||
| 137 | 142 | ||||
| 138 | // Get the image info | 143 | // Get the image info | ||
| 139 | _, err = docker.GetImageInfo(imageID) | 144 | _, err = docker.GetImageInfo(imageID) | ||
| 140 | if err != nil { | 145 | if err != nil { | ||
| 146 | + | log.Error("Error getting image info:", "error", err) | |||
| 141 | return sendError(c, err) | 147 | return sendError(c, err) | ||
| 142 | } | 148 | } | ||
| 143 | 149 | ||||
M internal/httpserve/handlers/container_management.go +6 -1
| @@ -98,7 +98,12 @@ func renderHTML(c echo.Context, a *server.App, path, templateName string, data m | |||
| 98 | 98 | ||
| 99 | 99 | // ActionSuccess returns the success HTML fragment | |
| 100 | 100 | func ActionSuccess(a *server.App) string { | |
| 101 | - | successFragment, err := load.Fragment(a, "success") | |
| 101 | + | // Create a data structure to pass to the template | |
| 102 | + | data := map[string]interface{}{ | |
| 103 | + | "Message": "Container deleted successfully!", | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | successFragment, err := load.Fragment(a, "success", data) | |
| 102 | 107 | if err != nil { | |
| 103 | 108 | fmt.Println(err) | |
| 104 | 109 | return "" | |
| @@ -98,7 +98,12 @@ func renderHTML(c echo.Context, a *server.App, path, templateName string, data m | |||||
| 98 | 98 | ||||
| 99 | // ActionSuccess returns the success HTML fragment | 99 | // ActionSuccess returns the success HTML fragment | ||
| 100 | func ActionSuccess(a *server.App) string { | 100 | func ActionSuccess(a *server.App) string { | ||
| 101 | - | successFragment, err := load.Fragment(a, "success") | 101 | + | // Create a data structure to pass to the template |
| 102 | + | data := map[string]interface{}{ | |||
| 103 | + | "Message": "Container deleted successfully!", | |||
| 104 | + | } | |||
| 105 | + | ||||
| 106 | + | successFragment, err := load.Fragment(a, "success", data) | |||
| 102 | if err != nil { | 107 | if err != nil { | ||
| 103 | fmt.Println(err) | 108 | fmt.Println(err) | ||
| 104 | return "" | 109 | return "" | ||
M internal/httpserve/router.go +1 -1
| @@ -198,7 +198,7 @@ func bindHTMXEndpoints(e *echo.Echo, a *server.App) { | |||
| 198 | 198 | htmxGroup.GET("/create-container/:ID", func(c echo.Context) error { | |
| 199 | 199 | return handlers.CreateContainerGET(c, a) | |
| 200 | 200 | }) | |
| 201 | - | // Create container | |
| 201 | + | // Handle create-container form submission | |
| 202 | 202 | htmxGroup.POST("/create-container/:ID", func(c echo.Context) error { | |
| 203 | 203 | return handlers.CreateContainerPOST(c, a) | |
| 204 | 204 | }) | |
| @@ -198,7 +198,7 @@ func bindHTMXEndpoints(e *echo.Echo, a *server.App) { | |||||
| 198 | htmxGroup.GET("/create-container/:ID", func(c echo.Context) error { | 198 | htmxGroup.GET("/create-container/:ID", func(c echo.Context) error { | ||
| 199 | return handlers.CreateContainerGET(c, a) | 199 | return handlers.CreateContainerGET(c, a) | ||
| 200 | }) | 200 | }) | ||
| 201 | - | // Create container | 201 | + | // Handle create-container form submission |
| 202 | htmxGroup.POST("/create-container/:ID", func(c echo.Context) error { | 202 | htmxGroup.POST("/create-container/:ID", func(c echo.Context) error { | ||
| 203 | return handlers.CreateContainerPOST(c, a) | 203 | return handlers.CreateContainerPOST(c, a) | ||
| 204 | }) | 204 | }) | ||
M internal/templating/load/fragment.go +23 -3
| @@ -1,14 +1,16 @@ | |||
| 1 | 1 | package load | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "bytes" | |
| 4 | 5 | "fmt" | |
| 6 | + | "html/template" | |
| 5 | 7 | "io" | |
| 6 | 8 | ||
| 7 | 9 | "github.com/bnema/gordon/internal/server" | |
| 8 | 10 | ) | |
| 9 | 11 | ||
| 10 | - | // LoadFragment loads a specific HTML fragment from the app's TemplateFS | |
| 11 | - | func Fragment(a *server.App, fragmentName string) (string, error) { | |
| 12 | + | // Fragment loads and renders a specific HTML fragment from the app's TemplateFS | |
| 13 | + | func Fragment(a *server.App, fragmentName string, data ...interface{}) (string, error) { | |
| 12 | 14 | filePath := fmt.Sprintf("html/fragments/%s.gohtml", fragmentName) | |
| 13 | 15 | ||
| 14 | 16 | // Open the file | |
| @@ -24,5 +26,23 @@ func Fragment(a *server.App, fragmentName string) (string, error) { | |||
| 24 | 26 | return "", err | |
| 25 | 27 | } | |
| 26 | 28 | ||
| 27 | - | return string(content), nil | |
| 29 | + | // If no data is provided, just return the raw content | |
| 30 | + | if len(data) == 0 { | |
| 31 | + | return string(content), nil | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | // Parse the template | |
| 35 | + | tmpl, err := template.New(fragmentName).Parse(string(content)) | |
| 36 | + | if err != nil { | |
| 37 | + | return "", err | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | // Execute the template with the provided data | |
| 41 | + | var buf bytes.Buffer | |
| 42 | + | err = tmpl.Execute(&buf, data[0]) | |
| 43 | + | if err != nil { | |
| 44 | + | return "", err | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | return buf.String(), nil | |
| 28 | 48 | } | |
| @@ -1,14 +1,16 @@ | |||||
| 1 | package load | 1 | package load | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "bytes" | |||
| 4 | "fmt" | 5 | "fmt" | ||
| 6 | + | "html/template" | |||
| 5 | "io" | 7 | "io" | ||
| 6 | 8 | ||||
| 7 | "github.com/bnema/gordon/internal/server" | 9 | "github.com/bnema/gordon/internal/server" | ||
| 8 | ) | 10 | ) | ||
| 9 | 11 | ||||
| 10 | - | // LoadFragment loads a specific HTML fragment from the app's TemplateFS | 12 | + | // Fragment loads and renders a specific HTML fragment from the app's TemplateFS |
| 11 | - | func Fragment(a *server.App, fragmentName string) (string, error) { | 13 | + | func Fragment(a *server.App, fragmentName string, data ...interface{}) (string, error) { |
| 12 | filePath := fmt.Sprintf("html/fragments/%s.gohtml", fragmentName) | 14 | filePath := fmt.Sprintf("html/fragments/%s.gohtml", fragmentName) | ||
| 13 | 15 | ||||
| 14 | // Open the file | 16 | // Open the file | ||
| @@ -24,5 +26,23 @@ func Fragment(a *server.App, fragmentName string) (string, error) { | |||||
| 24 | return "", err | 26 | return "", err | ||
| 25 | } | 27 | } | ||
| 26 | 28 | ||||
| 27 | - | return string(content), nil | 29 | + | // If no data is provided, just return the raw content |
| 30 | + | if len(data) == 0 { | |||
| 31 | + | return string(content), nil | |||
| 32 | + | } | |||
| 33 | + | ||||
| 34 | + | // Parse the template | |||
| 35 | + | tmpl, err := template.New(fragmentName).Parse(string(content)) | |||
| 36 | + | if err != nil { | |||
| 37 | + | return "", err | |||
| 38 | + | } | |||
| 39 | + | ||||
| 40 | + | // Execute the template with the provided data | |||
| 41 | + | var buf bytes.Buffer | |||
| 42 | + | err = tmpl.Execute(&buf, data[0]) | |||
| 43 | + | if err != nil { | |||
| 44 | + | return "", err | |||
| 45 | + | } | |||
| 46 | + | ||||
| 47 | + | return buf.String(), nil | |||
| 28 | } | 48 | } | ||
M internal/templating/models/html/fragments/header.gohtml +1 -0
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | <title>{{ .header.meta.title }}</title> | |
| 5 | 5 | <link rel="stylesheet" href="/assets/css/tailwind.css" /> | |
| 6 | 6 | ||
| 7 | + | <script src="/libs/jquery.min.js"></script> | |
| 7 | 8 | <script src="/libs/htmx.min.js"></script> | |
| 8 | 9 | <script src="/libs/htmx-response-targets.js"></script> | |
| 9 | 10 | <script src="/libs/htmx-debug.js"></script> | |
| @@ -4,6 +4,7 @@ | |||||
| 4 | <title>{{ .header.meta.title }}</title> | 4 | <title>{{ .header.meta.title }}</title> | ||
| 5 | <link rel="stylesheet" href="/assets/css/tailwind.css" /> | 5 | <link rel="stylesheet" href="/assets/css/tailwind.css" /> | ||
| 6 | 6 | ||||
| 7 | + | <script src="/libs/jquery.min.js"></script> | |||
| 7 | <script src="/libs/htmx.min.js"></script> | 8 | <script src="/libs/htmx.min.js"></script> | ||
| 8 | <script src="/libs/htmx-response-targets.js"></script> | 9 | <script src="/libs/htmx-response-targets.js"></script> | ||
| 9 | <script src="/libs/htmx-debug.js"></script> | 10 | <script src="/libs/htmx-debug.js"></script> | ||
M internal/webui/public/assets/js/custom.js +36 -0
| @@ -52,6 +52,14 @@ function initializeVersionCheck() { | |||
| 52 | 52 | ||
| 53 | 53 | // Initialize jQuery functionality for the admin dashboard | |
| 54 | 54 | function initializeAdminDashboard() { | |
| 55 | + | console.debug('Initializing admin dashboard with jQuery'); | |
| 56 | + | ||
| 57 | + | // Check if jQuery is properly loaded | |
| 58 | + | if (typeof $ === 'undefined') { | |
| 59 | + | console.error('jQuery is not defined! Make sure jQuery is loaded before this script.'); | |
| 60 | + | return; | |
| 61 | + | } | |
| 62 | + | ||
| 55 | 63 | // Handle edit button clicks to toggle action response visibility | |
| 56 | 64 | $(document).on('click', '[id^="edit-button-"]', function() { | |
| 57 | 65 | const id = $(this).attr('id').replace('edit-button-', ''); | |
| @@ -78,6 +86,12 @@ function initializeAdminDashboard() { | |||
| 78 | 86 | console.debug(`Add button clicked for ${id}`); | |
| 79 | 87 | $(`#create-container-${id}`).toggleClass('visible hidden'); | |
| 80 | 88 | $('#upload-image').addClass('hidden'); | |
| 89 | + | ||
| 90 | + | // Add event listener for form submission | |
| 91 | + | $(document).on('submit', `#create-container-form-${id}`, function(e) { | |
| 92 | + | console.debug(`Form submission for container ${id}`); | |
| 93 | + | // Don't prevent default as HTMX will handle the submission | |
| 94 | + | }); | |
| 81 | 95 | }); | |
| 82 | 96 | ||
| 83 | 97 | // Handle upload submit button | |
| @@ -320,9 +334,31 @@ function setupHtmxEventListeners() { | |||
| 320 | 334 | console.error('HTMX response error:', event.detail.error); | |
| 321 | 335 | showNotification('Error: ' + (event.detail.error || 'Unknown error occurred'), 'error'); | |
| 322 | 336 | }); | |
| 337 | + | ||
| 338 | + | // Add HTMX event listeners for debugging | |
| 339 | + | document.addEventListener('htmx:beforeRequest', function(event) { | |
| 340 | + | console.debug('HTMX before request:', event.detail); | |
| 341 | + | }); | |
| 342 | + | ||
| 343 | + | document.addEventListener('htmx:afterRequest', function(event) { | |
| 344 | + | console.debug('HTMX after request:', event.detail); | |
| 345 | + | }); | |
| 346 | + | ||
| 347 | + | document.addEventListener('htmx:responseError', function(event) { | |
| 348 | + | console.error('HTMX response error:', event.detail); | |
| 349 | + | }); | |
| 323 | 350 | } | |
| 324 | 351 | ||
| 325 | 352 | document.addEventListener("DOMContentLoaded", function() { | |
| 353 | + | console.debug('DOM fully loaded'); | |
| 354 | + | ||
| 355 | + | // Check if jQuery is available | |
| 356 | + | if (typeof $ === 'undefined') { | |
| 357 | + | console.error('jQuery is not loaded! Some features may not work properly.'); | |
| 358 | + | } else { | |
| 359 | + | console.debug('jQuery is loaded successfully'); | |
| 360 | + | } | |
| 361 | + | ||
| 326 | 362 | initializeVersionCheck(); | |
| 327 | 363 | initializeAdminDashboard(); | |
| 328 | 364 | }); | |
| @@ -52,6 +52,14 @@ function initializeVersionCheck() { | |||||
| 52 | 52 | ||||
| 53 | // Initialize jQuery functionality for the admin dashboard | 53 | // Initialize jQuery functionality for the admin dashboard | ||
| 54 | function initializeAdminDashboard() { | 54 | function initializeAdminDashboard() { | ||
| 55 | + | console.debug('Initializing admin dashboard with jQuery'); | |||
| 56 | + | ||||
| 57 | + | // Check if jQuery is properly loaded | |||
| 58 | + | if (typeof $ === 'undefined') { | |||
| 59 | + | console.error('jQuery is not defined! Make sure jQuery is loaded before this script.'); | |||
| 60 | + | return; | |||
| 61 | + | } | |||
| 62 | + | ||||
| 55 | // Handle edit button clicks to toggle action response visibility | 63 | // Handle edit button clicks to toggle action response visibility | ||
| 56 | $(document).on('click', '[id^="edit-button-"]', function() { | 64 | $(document).on('click', '[id^="edit-button-"]', function() { | ||
| 57 | const id = $(this).attr('id').replace('edit-button-', ''); | 65 | const id = $(this).attr('id').replace('edit-button-', ''); | ||
| @@ -78,6 +86,12 @@ function initializeAdminDashboard() { | |||||
| 78 | console.debug(`Add button clicked for ${id}`); | 86 | console.debug(`Add button clicked for ${id}`); | ||
| 79 | $(`#create-container-${id}`).toggleClass('visible hidden'); | 87 | $(`#create-container-${id}`).toggleClass('visible hidden'); | ||
| 80 | $('#upload-image').addClass('hidden'); | 88 | $('#upload-image').addClass('hidden'); | ||
| 89 | + | ||||
| 90 | + | // Add event listener for form submission | |||
| 91 | + | $(document).on('submit', `#create-container-form-${id}`, function(e) { | |||
| 92 | + | console.debug(`Form submission for container ${id}`); | |||
| 93 | + | // Don't prevent default as HTMX will handle the submission | |||
| 94 | + | }); | |||
| 81 | }); | 95 | }); | ||
| 82 | 96 | ||||
| 83 | // Handle upload submit button | 97 | // Handle upload submit button | ||
| @@ -320,9 +334,31 @@ function setupHtmxEventListeners() { | |||||
| 320 | console.error('HTMX response error:', event.detail.error); | 334 | console.error('HTMX response error:', event.detail.error); | ||
| 321 | showNotification('Error: ' + (event.detail.error || 'Unknown error occurred'), 'error'); | 335 | showNotification('Error: ' + (event.detail.error || 'Unknown error occurred'), 'error'); | ||
| 322 | }); | 336 | }); | ||
| 337 | + | ||||
| 338 | + | // Add HTMX event listeners for debugging | |||
| 339 | + | document.addEventListener('htmx:beforeRequest', function(event) { | |||
| 340 | + | console.debug('HTMX before request:', event.detail); | |||
| 341 | + | }); | |||
| 342 | + | ||||
| 343 | + | document.addEventListener('htmx:afterRequest', function(event) { | |||
| 344 | + | console.debug('HTMX after request:', event.detail); | |||
| 345 | + | }); | |||
| 346 | + | ||||
| 347 | + | document.addEventListener('htmx:responseError', function(event) { | |||
| 348 | + | console.error('HTMX response error:', event.detail); | |||
| 349 | + | }); | |||
| 323 | } | 350 | } | ||
| 324 | 351 | ||||
| 325 | document.addEventListener("DOMContentLoaded", function() { | 352 | document.addEventListener("DOMContentLoaded", function() { | ||
| 353 | + | console.debug('DOM fully loaded'); | |||
| 354 | + | ||||
| 355 | + | // Check if jQuery is available | |||
| 356 | + | if (typeof $ === 'undefined') { | |||
| 357 | + | console.error('jQuery is not loaded! Some features may not work properly.'); | |||
| 358 | + | } else { | |||
| 359 | + | console.debug('jQuery is loaded successfully'); | |||
| 360 | + | } | |||
| 361 | + | ||||
| 326 | initializeVersionCheck(); | 362 | initializeVersionCheck(); | ||
| 327 | initializeAdminDashboard(); | 363 | initializeAdminDashboard(); | ||
| 328 | }); | 364 | }); | ||