detached dbdbc7c

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
dbdbc7c298c1d2185ce31e3f46bf3991ed0a10f2
author
bamen · · 2025-03-17 17:15 UTC
parent
cd73f27
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 {
126126
127127 // CreateContainerPOST handles the create container form submission
128128 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
130132 ShortID := c.Param("ID")
133+ log.Debug("Received ShortID for container creation:", "ShortID", ShortID)
131134
132135 // Convert the ShortID to a full image ID
133136 imageID, err := FromShortIDToImageID(ShortID)
134137 if err != nil {
138+ log.Error("Error converting ShortID to full image ID:", "error", err)
135139 return sendError(c, err)
136140 }
141+ log.Debug("Converted ShortID to full image ID:", "imageID", imageID)
137142
138143 // Get the image info
139144 _, err = docker.GetImageInfo(imageID)
140145 if err != nil {
146+ log.Error("Error getting image info:", "error", err)
141147 return sendError(c, err)
142148 }
143149
@@ -126,18 +126,24 @@ func CreateContainerFullGET(c echo.Context, a *server.App) error {
126 126
127 // CreateContainerPOST handles the create container form submission127 // 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 URL129+ 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 ID135 // 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 info143 // 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
9898
9999 // ActionSuccess returns the success HTML fragment
100100 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)
102107 if err != nil {
103108 fmt.Println(err)
104109 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 fragment99 // 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) {
198198 htmxGroup.GET("/create-container/:ID", func(c echo.Context) error {
199199 return handlers.CreateContainerGET(c, a)
200200 })
201- // Create container
201+ // Handle create-container form submission
202202 htmxGroup.POST("/create-container/:ID", func(c echo.Context) error {
203203 return handlers.CreateContainerPOST(c, a)
204204 })
@@ -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 container201+ // 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 @@
11 package load
22
33 import (
4+ "bytes"
45 "fmt"
6+ "html/template"
57 "io"
68
79 "github.com/bnema/gordon/internal/server"
810 )
911
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) {
1214 filePath := fmt.Sprintf("html/fragments/%s.gohtml", fragmentName)
1315
1416 // Open the file
@@ -24,5 +26,23 @@ func Fragment(a *server.App, fragmentName string) (string, error) {
2426 return "", err
2527 }
2628
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
2848 }
@@ -1,14 +1,16 @@
1 package load1 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 TemplateFS12+// 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 file16 // Open the file
@@ -24,5 +26,23 @@ func Fragment(a *server.App, fragmentName string) (string, error) {
24 return "", err26 return "", err
25 }27 }
26 28
27- return string(content), nil29+ // 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 @@
44 <title>{{ .header.meta.title }}</title>
55 <link rel="stylesheet" href="/assets/css/tailwind.css" />
66
7+ <script src="/libs/jquery.min.js"></script>
78 <script src="/libs/htmx.min.js"></script>
89 <script src="/libs/htmx-response-targets.js"></script>
910 <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() {
5252
5353 // Initialize jQuery functionality for the admin dashboard
5454 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+
5563 // Handle edit button clicks to toggle action response visibility
5664 $(document).on('click', '[id^="edit-button-"]', function() {
5765 const id = $(this).attr('id').replace('edit-button-', '');
@@ -78,6 +86,12 @@ function initializeAdminDashboard() {
7886 console.debug(`Add button clicked for ${id}`);
7987 $(`#create-container-${id}`).toggleClass('visible hidden');
8088 $('#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+ });
8195 });
8296
8397 // Handle upload submit button
@@ -320,9 +334,31 @@ function setupHtmxEventListeners() {
320334 console.error('HTMX response error:', event.detail.error);
321335 showNotification('Error: ' + (event.detail.error || 'Unknown error occurred'), 'error');
322336 });
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+ });
323350 }
324351
325352 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+
326362 initializeVersionCheck();
327363 initializeAdminDashboard();
328364 });
@@ -52,6 +52,14 @@ function initializeVersionCheck() {
52 52
53 // Initialize jQuery functionality for the admin dashboard53 // 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 visibility63 // 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 button97 // 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 });

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close