detached c10a3cb

Refactor HTTP Handlers and Utility Functions

- Generalize admin page rendering into a single `renderAdminPage` function
- Rewrite the mutex-safe on idMap operations into one function
- Update status code usages to use `http` package constants
- Use more descriptive function names and comments for better readability
- Various comments and code style improvements
commit
c10a3cb1c6daefbd9e1c3da5e3a06acf35675217
author
bnema · · 2023-10-20 17:35 UTC
parent
3d52ec3
signature
unsigned
5 files · +91 -75
M internal/httpserve/handler/adminroutes.go +15 -22
@@ -3,6 +3,8 @@ package handler
33 import (
44 "fmt"
55
6+ "net/http"
7+
68 "github.com/bnema/gordon/internal/app"
79 "github.com/bnema/gordon/internal/httpserve/middleware"
810 "github.com/bnema/gordon/internal/templating/render"
@@ -14,6 +16,7 @@ var (
1416 fragmentsPath = "html/fragments"
1517 )
1618
19+// GetLocalizedData returns the data for the localization
1720 func GetLocalizedData(c echo.Context, a *app.App) (map[string]interface{}, error) {
1821 lang := c.Get(middleware.LangKey)
1922
@@ -30,14 +33,14 @@ func GetLocalizedData(c echo.Context, a *app.App) (map[string]interface{}, error
3033 }, nil
3134 }
3235
33-// Handle the admin route to display index.gohtml from the templateFS with the data from strings.yml
34-func AdminRoute(c echo.Context, a *app.App) error {
36+// renderAdminPage is a generalized function to render admin pages
37+func renderAdminPage(c echo.Context, a *app.App, templateName string) error {
3538 data, err := GetLocalizedData(c, a)
3639 if err != nil {
3740 return err
3841 }
39- // Navigate inside the fs.FS to get the template
40- rendererData, err := render.GetHTMLRenderer(mainPath, "index.gohtml", a.TemplateFS, a, fragmentsPath)
42+
43+ rendererData, err := render.GetHTMLRenderer(mainPath, templateName, a.TemplateFS, a, fragmentsPath)
4144 if err != nil {
4245 return err
4346 }
@@ -47,25 +50,15 @@ func AdminRoute(c echo.Context, a *app.App) error {
4750 return err
4851 }
4952
50- return c.HTML(200, renderedHTML)
53+ return c.HTML(http.StatusOK, renderedHTML)
5154 }
5255
53-func AdminManagerRoute(c echo.Context, a *app.App) error {
54- data, err := GetLocalizedData(c, a)
55- if err != nil {
56- return err
57- }
58-
59- // Navigate inside the fs.FS to get the template
60- rendererData, err := render.GetHTMLRenderer(mainPath, "manager.gohtml", a.TemplateFS, a, fragmentsPath)
61- if err != nil {
62- return err
63- }
64-
65- renderedHTML, err := rendererData.Render(data, a)
66- if err != nil {
67- return err
68- }
56+// AdminRoute is the route for the admin panel
57+func AdminRoute(c echo.Context, a *app.App) error {
58+ return renderAdminPage(c, a, "index.gohtml")
59+}
6960
70- return c.HTML(200, renderedHTML)
61+// AdminManagerRoute is the route of the manager page
62+func AdminManagerRoute(c echo.Context, a *app.App) error {
63+ return renderAdminPage(c, a, "manager.gohtml")
7164 }
@@ -3,6 +3,8 @@ package handler
3 import (3 import (
4 "fmt"4 "fmt"
5 5
6+ "net/http"
7+
6 "github.com/bnema/gordon/internal/app"8 "github.com/bnema/gordon/internal/app"
7 "github.com/bnema/gordon/internal/httpserve/middleware"9 "github.com/bnema/gordon/internal/httpserve/middleware"
8 "github.com/bnema/gordon/internal/templating/render"10 "github.com/bnema/gordon/internal/templating/render"
@@ -14,6 +16,7 @@ var (
14 fragmentsPath = "html/fragments"16 fragmentsPath = "html/fragments"
15 )17 )
16 18
19+// GetLocalizedData returns the data for the localization
17 func GetLocalizedData(c echo.Context, a *app.App) (map[string]interface{}, error) {20 func GetLocalizedData(c echo.Context, a *app.App) (map[string]interface{}, error) {
18 lang := c.Get(middleware.LangKey)21 lang := c.Get(middleware.LangKey)
19 22
@@ -30,14 +33,14 @@ func GetLocalizedData(c echo.Context, a *app.App) (map[string]interface{}, error
30 }, nil33 }, nil
31 }34 }
32 35
33-// Handle the admin route to display index.gohtml from the templateFS with the data from strings.yml36+// renderAdminPage is a generalized function to render admin pages
34-func AdminRoute(c echo.Context, a *app.App) error {37+func renderAdminPage(c echo.Context, a *app.App, templateName string) error {
35 data, err := GetLocalizedData(c, a)38 data, err := GetLocalizedData(c, a)
36 if err != nil {39 if err != nil {
37 return err40 return err
38 }41 }
39- // Navigate inside the fs.FS to get the template42+
40- rendererData, err := render.GetHTMLRenderer(mainPath, "index.gohtml", a.TemplateFS, a, fragmentsPath)43+ rendererData, err := render.GetHTMLRenderer(mainPath, templateName, a.TemplateFS, a, fragmentsPath)
41 if err != nil {44 if err != nil {
42 return err45 return err
43 }46 }
@@ -47,25 +50,15 @@ func AdminRoute(c echo.Context, a *app.App) error {
47 return err50 return err
48 }51 }
49 52
50- return c.HTML(200, renderedHTML)53+ return c.HTML(http.StatusOK, renderedHTML)
51 }54 }
52 55
53-func AdminManagerRoute(c echo.Context, a *app.App) error {56+// AdminRoute is the route for the admin panel
54- data, err := GetLocalizedData(c, a)57+func AdminRoute(c echo.Context, a *app.App) error {
55- if err != nil {58+ return renderAdminPage(c, a, "index.gohtml")
56- return err59+}
57- }
58-
59- // Navigate inside the fs.FS to get the template
60- rendererData, err := render.GetHTMLRenderer(mainPath, "manager.gohtml", a.TemplateFS, a, fragmentsPath)
61- if err != nil {
62- return err
63- }
64-
65- renderedHTML, err := rendererData.Render(data, a)
66- if err != nil {
67- return err
68- }
69 60
70- return c.HTML(200, renderedHTML)61+// AdminManagerRoute is the route of the manager page
62+func AdminManagerRoute(c echo.Context, a *app.App) error {
63+ return renderAdminPage(c, a, "manager.gohtml")
71 }64 }
M internal/httpserve/handler/containers.go +55 -37
@@ -15,11 +15,40 @@ import (
1515 "github.com/labstack/echo/v4"
1616 )
1717
18+type MapOperation string
19+
1820 var (
1921 idMap = make(map[string]string)
2022 idMapMutex sync.Mutex
2123 )
2224
25+const (
26+ Fetch MapOperation = "Fetch"
27+ Update MapOperation = "Update"
28+ Delete MapOperation = "Delete"
29+)
30+
31+func safelyInteractWithIDMap(op MapOperation, key string, value ...string) (string, bool) {
32+ idMapMutex.Lock()
33+ defer idMapMutex.Unlock()
34+
35+ switch op {
36+ case Fetch:
37+ val, exists := idMap[key]
38+ return val, exists
39+ case Update:
40+ if len(value) > 0 {
41+ idMap[key] = value[0]
42+ }
43+ return "", true
44+ case Delete:
45+ delete(idMap, key)
46+ return "", true
47+ default:
48+ return "", false
49+ }
50+}
51+
2352 type HumanReadableContainerImage struct {
2453 *types.ImageSummary
2554 Name string
@@ -39,6 +68,20 @@ type HumanReadableContainer struct {
3968 StateColor string
4069 }
4170
71+// renderHTML is a generalized function to render HTML
72+func renderHTML(c echo.Context, a *app.App, path, templateName string, data map[string]interface{}) error {
73+ rendererData, err := render.GetHTMLRenderer(path, templateName, a.TemplateFS, a)
74+ if err != nil {
75+ return err
76+ }
77+ renderedHTML, err := rendererData.Render(data, a)
78+ if err != nil {
79+ return err
80+ }
81+ return c.HTML(http.StatusOK, renderedHTML)
82+}
83+
84+// ActionSuccess returns the success HTML fragment
4285 func ActionSuccess(a *app.App) string {
4386 successFragment, err := load.Fragment(a, "success")
4487 if err != nil {
@@ -58,10 +101,10 @@ func ImageManagerComponent(c echo.Context, a *app.App) error {
58101 var humanReadableImages []HumanReadableContainerImage
59102
60103 for _, image := range images {
104+ // We need to shorten the ID because it breaks the HTML
61105 ShortID := image.ID[7:19]
62- idMapMutex.Lock()
63- idMap[ShortID] = image.ID
64- idMapMutex.Unlock()
106+ // And we store both the ShortID and the full ID in a map so we can retrieve the full ID later
107+ safelyInteractWithIDMap(Update, ShortID, image.ID)
65108 createdTime := time.Unix(image.Created, 0)
66109 createdStr := humanize.TimeAgo(createdTime)
67110 sizeStr := humanize.BytesToReadableSize(image.Size)
@@ -80,26 +123,15 @@ func ImageManagerComponent(c echo.Context, a *app.App) error {
80123 "Images": humanReadableImages,
81124 }
82125
83- rendererData, err := render.GetHTMLRenderer("html/fragments", "imagelist.gohtml", a.TemplateFS, a)
84- if err != nil {
85- return err
86- }
87- renderedHTML, err := rendererData.Render(data, a)
88- if err != nil {
89- return err
90- }
91- return c.HTML(200, renderedHTML)
126+ return renderHTML(c, a, "html/fragments", "imagelist.gohtml", data)
92127 }
93128
94129 // ImageManagerDelete handles the /image-manager/delete route
95130 func ImageManagerDelete(c echo.Context, a *app.App) error {
96- // Get the ShortImgID from the URL
131+ //
97132 ShortID := c.Param("ID")
98133
99- idMapMutex.Lock()
100- // Check if the ShortImgID exists in the idMap
101- imageID, exists := idMap[ShortID]
102- idMapMutex.Unlock()
134+ imageID, exists := safelyInteractWithIDMap(Fetch, ShortID)
103135
104136 if !exists {
105137 return c.String(http.StatusBadRequest, "Invalid ShortImgID")
@@ -111,10 +143,7 @@ func ImageManagerDelete(c echo.Context, a *app.App) error {
111143 return c.String(http.StatusInternalServerError, err.Error())
112144 }
113145
114- // Remove the entry from idMap
115- idMapMutex.Lock()
116- delete(idMap, ShortID)
117- idMapMutex.Unlock()
146+ safelyInteractWithIDMap(Delete, ShortID)
118147
119148 return c.String(http.StatusOK, ActionSuccess(a))
120149 }
@@ -163,21 +192,12 @@ func ContainerManagerComponent(c echo.Context, a *app.App) error {
163192 "containers": humanReadableContainers,
164193 }
165194
166- rendererData, err := render.GetHTMLRenderer("html/fragments", "containerlist.gohtml", a.TemplateFS, a)
167- if err != nil {
168- return err
169- }
170- renderedHTML, err := rendererData.Render(data, a)
171- if err != nil {
172- return err
173- }
174- return c.HTML(200, renderedHTML)
195+ return renderHTML(c, a, "html/fragments", "containerlist.gohtml", data)
175196 }
176197
177198 // ContainerManagerDelete handles the /container-manager/delete route
178199 func ContainerManagerDelete(c echo.Context, a *app.App) error {
179- ID := c.Param("ID")
180- err := docker.RemoveContainer(ID)
200+ err := docker.RemoveContainer(c.Param("ID"))
181201 if err != nil {
182202 return sendError(c, err)
183203 }
@@ -186,9 +206,8 @@ func ContainerManagerDelete(c echo.Context, a *app.App) error {
186206
187207 // ContainerManagerStop handles the /container-manager/stop route
188208 func ContainerManagerStop(c echo.Context, a *app.App) error {
189- ID := c.Param("ID")
190209 // Stop the container gracefully with a timeout
191- stopped, err := docker.StopContainerGracefully(ID, 3*time.Second)
210+ stopped, err := docker.StopContainerGracefully(c.Param("ID"), 3*time.Second)
192211 if err != nil {
193212 return sendError(c, err)
194213 }
@@ -197,7 +216,7 @@ func ContainerManagerStop(c echo.Context, a *app.App) error {
197216 return c.HTML(http.StatusOK, ActionSuccess(a))
198217 }
199218 // If stopped is false, the container was not stopped gracefully we force stop it
200- err = docker.StopContainerRagefully(ID)
219+ err = docker.StopContainerRagefully(c.Param("ID"))
201220 if err != nil {
202221 return sendError(c, err)
203222 }
@@ -207,8 +226,7 @@ func ContainerManagerStop(c echo.Context, a *app.App) error {
207226
208227 // ContainerManagerStart handles the /container-manager/start route
209228 func ContainerManagerStart(c echo.Context, a *app.App) error {
210- ID := c.Param("ID")
211- err := docker.StartContainer(ID)
229+ err := docker.StartContainer(c.Param("ID"))
212230 if err != nil {
213231 return sendError(c, err)
214232 }
@@ -15,11 +15,40 @@ import (
15 "github.com/labstack/echo/v4"15 "github.com/labstack/echo/v4"
16 )16 )
17 17
18+type MapOperation string
19+
18 var (20 var (
19 idMap = make(map[string]string)21 idMap = make(map[string]string)
20 idMapMutex sync.Mutex22 idMapMutex sync.Mutex
21 )23 )
22 24
25+const (
26+ Fetch MapOperation = "Fetch"
27+ Update MapOperation = "Update"
28+ Delete MapOperation = "Delete"
29+)
30+
31+func safelyInteractWithIDMap(op MapOperation, key string, value ...string) (string, bool) {
32+ idMapMutex.Lock()
33+ defer idMapMutex.Unlock()
34+
35+ switch op {
36+ case Fetch:
37+ val, exists := idMap[key]
38+ return val, exists
39+ case Update:
40+ if len(value) > 0 {
41+ idMap[key] = value[0]
42+ }
43+ return "", true
44+ case Delete:
45+ delete(idMap, key)
46+ return "", true
47+ default:
48+ return "", false
49+ }
50+}
51+
23 type HumanReadableContainerImage struct {52 type HumanReadableContainerImage struct {
24 *types.ImageSummary53 *types.ImageSummary
25 Name string54 Name string
@@ -39,6 +68,20 @@ type HumanReadableContainer struct {
39 StateColor string68 StateColor string
40 }69 }
41 70
71+// renderHTML is a generalized function to render HTML
72+func renderHTML(c echo.Context, a *app.App, path, templateName string, data map[string]interface{}) error {
73+ rendererData, err := render.GetHTMLRenderer(path, templateName, a.TemplateFS, a)
74+ if err != nil {
75+ return err
76+ }
77+ renderedHTML, err := rendererData.Render(data, a)
78+ if err != nil {
79+ return err
80+ }
81+ return c.HTML(http.StatusOK, renderedHTML)
82+}
83+
84+// ActionSuccess returns the success HTML fragment
42 func ActionSuccess(a *app.App) string {85 func ActionSuccess(a *app.App) string {
43 successFragment, err := load.Fragment(a, "success")86 successFragment, err := load.Fragment(a, "success")
44 if err != nil {87 if err != nil {
@@ -58,10 +101,10 @@ func ImageManagerComponent(c echo.Context, a *app.App) error {
58 var humanReadableImages []HumanReadableContainerImage101 var humanReadableImages []HumanReadableContainerImage
59 102
60 for _, image := range images {103 for _, image := range images {
104+ // We need to shorten the ID because it breaks the HTML
61 ShortID := image.ID[7:19]105 ShortID := image.ID[7:19]
62- idMapMutex.Lock()106+ // And we store both the ShortID and the full ID in a map so we can retrieve the full ID later
63- idMap[ShortID] = image.ID107+ safelyInteractWithIDMap(Update, ShortID, image.ID)
64- idMapMutex.Unlock()
65 createdTime := time.Unix(image.Created, 0)108 createdTime := time.Unix(image.Created, 0)
66 createdStr := humanize.TimeAgo(createdTime)109 createdStr := humanize.TimeAgo(createdTime)
67 sizeStr := humanize.BytesToReadableSize(image.Size)110 sizeStr := humanize.BytesToReadableSize(image.Size)
@@ -80,26 +123,15 @@ func ImageManagerComponent(c echo.Context, a *app.App) error {
80 "Images": humanReadableImages,123 "Images": humanReadableImages,
81 }124 }
82 125
83- rendererData, err := render.GetHTMLRenderer("html/fragments", "imagelist.gohtml", a.TemplateFS, a)126+ return renderHTML(c, a, "html/fragments", "imagelist.gohtml", data)
84- if err != nil {
85- return err
86- }
87- renderedHTML, err := rendererData.Render(data, a)
88- if err != nil {
89- return err
90- }
91- return c.HTML(200, renderedHTML)
92 }127 }
93 128
94 // ImageManagerDelete handles the /image-manager/delete route129 // ImageManagerDelete handles the /image-manager/delete route
95 func ImageManagerDelete(c echo.Context, a *app.App) error {130 func ImageManagerDelete(c echo.Context, a *app.App) error {
96- // Get the ShortImgID from the URL131+ //
97 ShortID := c.Param("ID")132 ShortID := c.Param("ID")
98 133
99- idMapMutex.Lock()134+ imageID, exists := safelyInteractWithIDMap(Fetch, ShortID)
100- // Check if the ShortImgID exists in the idMap
101- imageID, exists := idMap[ShortID]
102- idMapMutex.Unlock()
103 135
104 if !exists {136 if !exists {
105 return c.String(http.StatusBadRequest, "Invalid ShortImgID")137 return c.String(http.StatusBadRequest, "Invalid ShortImgID")
@@ -111,10 +143,7 @@ func ImageManagerDelete(c echo.Context, a *app.App) error {
111 return c.String(http.StatusInternalServerError, err.Error())143 return c.String(http.StatusInternalServerError, err.Error())
112 }144 }
113 145
114- // Remove the entry from idMap146+ safelyInteractWithIDMap(Delete, ShortID)
115- idMapMutex.Lock()
116- delete(idMap, ShortID)
117- idMapMutex.Unlock()
118 147
119 return c.String(http.StatusOK, ActionSuccess(a))148 return c.String(http.StatusOK, ActionSuccess(a))
120 }149 }
@@ -163,21 +192,12 @@ func ContainerManagerComponent(c echo.Context, a *app.App) error {
163 "containers": humanReadableContainers,192 "containers": humanReadableContainers,
164 }193 }
165 194
166- rendererData, err := render.GetHTMLRenderer("html/fragments", "containerlist.gohtml", a.TemplateFS, a)195+ return renderHTML(c, a, "html/fragments", "containerlist.gohtml", data)
167- if err != nil {
168- return err
169- }
170- renderedHTML, err := rendererData.Render(data, a)
171- if err != nil {
172- return err
173- }
174- return c.HTML(200, renderedHTML)
175 }196 }
176 197
177 // ContainerManagerDelete handles the /container-manager/delete route198 // ContainerManagerDelete handles the /container-manager/delete route
178 func ContainerManagerDelete(c echo.Context, a *app.App) error {199 func ContainerManagerDelete(c echo.Context, a *app.App) error {
179- ID := c.Param("ID")200+ err := docker.RemoveContainer(c.Param("ID"))
180- err := docker.RemoveContainer(ID)
181 if err != nil {201 if err != nil {
182 return sendError(c, err)202 return sendError(c, err)
183 }203 }
@@ -186,9 +206,8 @@ func ContainerManagerDelete(c echo.Context, a *app.App) error {
186 206
187 // ContainerManagerStop handles the /container-manager/stop route207 // ContainerManagerStop handles the /container-manager/stop route
188 func ContainerManagerStop(c echo.Context, a *app.App) error {208 func ContainerManagerStop(c echo.Context, a *app.App) error {
189- ID := c.Param("ID")
190 // Stop the container gracefully with a timeout209 // Stop the container gracefully with a timeout
191- stopped, err := docker.StopContainerGracefully(ID, 3*time.Second)210+ stopped, err := docker.StopContainerGracefully(c.Param("ID"), 3*time.Second)
192 if err != nil {211 if err != nil {
193 return sendError(c, err)212 return sendError(c, err)
194 }213 }
@@ -197,7 +216,7 @@ func ContainerManagerStop(c echo.Context, a *app.App) error {
197 return c.HTML(http.StatusOK, ActionSuccess(a))216 return c.HTML(http.StatusOK, ActionSuccess(a))
198 }217 }
199 // If stopped is false, the container was not stopped gracefully we force stop it218 // If stopped is false, the container was not stopped gracefully we force stop it
200- err = docker.StopContainerRagefully(ID)219+ err = docker.StopContainerRagefully(c.Param("ID"))
201 if err != nil {220 if err != nil {
202 return sendError(c, err)221 return sendError(c, err)
203 }222 }
@@ -207,8 +226,7 @@ func ContainerManagerStop(c echo.Context, a *app.App) error {
207 226
208 // ContainerManagerStart handles the /container-manager/start route227 // ContainerManagerStart handles the /container-manager/start route
209 func ContainerManagerStart(c echo.Context, a *app.App) error {228 func ContainerManagerStart(c echo.Context, a *app.App) error {
210- ID := c.Param("ID")229+ err := docker.StartContainer(c.Param("ID"))
211- err := docker.StartContainer(ID)
212 if err != nil {230 if err != nil {
213 return sendError(c, err)231 return sendError(c, err)
214 }232 }
M internal/httpserve/handler/createcontainer.go +8 -13
@@ -2,6 +2,7 @@ package handler
22
33 import (
44 "fmt"
5+ "net/http"
56
67 "github.com/bnema/gordon/internal/app"
78 "github.com/bnema/gordon/internal/templating/render"
@@ -12,16 +13,11 @@ import (
1213
1314 // FromShortIDToImageID converts a short image ID to a full image ID
1415 func FromShortIDToImageID(ShortID string) (string, error) {
15- idMapMutex.Lock()
16- // Check if the ShortImgID exists in the idMap
17- imageID, exists := idMap[ShortID]
18- idMapMutex.Unlock()
19-
20- if !exists {
21- return "", fmt.Errorf("image ID not found")
16+ imageID, exists := safelyInteractWithIDMap(Fetch, ShortID)
17+ if exists {
18+ return imageID, nil
2219 }
23-
24- return imageID, nil
20+ return "", fmt.Errorf("image ID not found")
2521 }
2622
2723 // CreateContainerRoute is the route for creating a new container
@@ -44,7 +40,7 @@ func CreateContainerGET(c echo.Context, a *app.App) error {
4440 // Extract image name
4541 var imageName string
4642 if len(imageInfo.RepoTags) > 0 {
47- imageName = imageInfo.RepoTags[0] // Using the first tag as the image name
43+ imageName = imageInfo.RepoTags[0]
4844 }
4945
5046 data := map[string]interface{}{
@@ -70,6 +66,7 @@ func CreateContainerGET(c echo.Context, a *app.App) error {
7066
7167 }
7268
69+// CreateContainerPOST handles the create container form submission
7370 func CreateContainerPOST(c echo.Context, a *app.App) error {
7471 // Retreive the ShortID of the image from the URL
7572 ShortID := c.Param("ID")
@@ -111,13 +108,11 @@ func CreateContainerPOST(c echo.Context, a *app.App) error {
111108 return sendError(c, err)
112109 }
113110
114- fmt.Println(cmdParams.PortMappings)
115-
116111 // Create the container
117112 _, err = docker.CreateContainer(cmdParams)
118113 if err != nil {
119114 return sendError(c, err)
120115 }
121116
122- return c.String(200, "Container created")
117+ return c.HTML(http.StatusOK, ActionSuccess(a))
123118 }
@@ -2,6 +2,7 @@ package handler
2 2
3 import (3 import (
4 "fmt"4 "fmt"
5+ "net/http"
5 6
6 "github.com/bnema/gordon/internal/app"7 "github.com/bnema/gordon/internal/app"
7 "github.com/bnema/gordon/internal/templating/render"8 "github.com/bnema/gordon/internal/templating/render"
@@ -12,16 +13,11 @@ import (
12 13
13 // FromShortIDToImageID converts a short image ID to a full image ID14 // FromShortIDToImageID converts a short image ID to a full image ID
14 func FromShortIDToImageID(ShortID string) (string, error) {15 func FromShortIDToImageID(ShortID string) (string, error) {
15- idMapMutex.Lock()16+ imageID, exists := safelyInteractWithIDMap(Fetch, ShortID)
16- // Check if the ShortImgID exists in the idMap17+ if exists {
17- imageID, exists := idMap[ShortID]18+ return imageID, nil
18- idMapMutex.Unlock()
19-
20- if !exists {
21- return "", fmt.Errorf("image ID not found")
22 }19 }
23-20+ return "", fmt.Errorf("image ID not found")
24- return imageID, nil
25 }21 }
26 22
27 // CreateContainerRoute is the route for creating a new container23 // CreateContainerRoute is the route for creating a new container
@@ -44,7 +40,7 @@ func CreateContainerGET(c echo.Context, a *app.App) error {
44 // Extract image name40 // Extract image name
45 var imageName string41 var imageName string
46 if len(imageInfo.RepoTags) > 0 {42 if len(imageInfo.RepoTags) > 0 {
47- imageName = imageInfo.RepoTags[0] // Using the first tag as the image name43+ imageName = imageInfo.RepoTags[0]
48 }44 }
49 45
50 data := map[string]interface{}{46 data := map[string]interface{}{
@@ -70,6 +66,7 @@ func CreateContainerGET(c echo.Context, a *app.App) error {
70 66
71 }67 }
72 68
69+// CreateContainerPOST handles the create container form submission
73 func CreateContainerPOST(c echo.Context, a *app.App) error {70 func CreateContainerPOST(c echo.Context, a *app.App) error {
74 // Retreive the ShortID of the image from the URL71 // Retreive the ShortID of the image from the URL
75 ShortID := c.Param("ID")72 ShortID := c.Param("ID")
@@ -111,13 +108,11 @@ func CreateContainerPOST(c echo.Context, a *app.App) error {
111 return sendError(c, err)108 return sendError(c, err)
112 }109 }
113 110
114- fmt.Println(cmdParams.PortMappings)
115-
116 // Create the container111 // Create the container
117 _, err = docker.CreateContainer(cmdParams)112 _, err = docker.CreateContainer(cmdParams)
118 if err != nil {113 if err != nil {
119 return sendError(c, err)114 return sendError(c, err)
120 }115 }
121 116
122- return c.String(200, "Container created")117+ return c.HTML(http.StatusOK, ActionSuccess(a))
123 }118 }
M internal/httpserve/handler/editcontainer.go +3 -3
@@ -45,7 +45,7 @@ func ContainerManagerEditGET(c echo.Context, a *app.App) error {
4545
4646 // Trim the slash from the container name
4747 containerInfo.Name = strings.TrimPrefix(containerInfo.Name, "/")
48- // Get just the network names
48+ // Get the network names
4949 networkNames := make([]string, 0, len(containerInfo.NetworkSettings.Networks))
5050 for networkName := range containerInfo.NetworkSettings.Networks {
5151 networkNames = append(networkNames, networkName)
@@ -65,7 +65,7 @@ func ContainerManagerEditGET(c echo.Context, a *app.App) error {
6565 volumeMappings = append(volumeMappings, fmt.Sprintf("%s:%s", mount.Source, mount.Destination))
6666 }
6767
68- // Initialize a map to store the information you want to display
68+ // Initialize a map to store the information from the container
6969 infoMap := make(map[string]interface{})
7070
7171 // Populate the map with container details
@@ -198,7 +198,7 @@ func ContainerManagerEditPOST(c echo.Context, a *app.App) error {
198198 return sendError(c, fmt.Errorf("transaction failed: %w", err))
199199 }
200200
201- return c.HTML(200, fmt.Sprintf("Successfully edited container %s", tq.NewContainerName))
201+ return c.HTML(200, ActionSuccess(a))
202202 }
203203
204204 // CreateNewContainerStep creates a new container with the given parameters
@@ -45,7 +45,7 @@ func ContainerManagerEditGET(c echo.Context, a *app.App) error {
45 45
46 // Trim the slash from the container name46 // Trim the slash from the container name
47 containerInfo.Name = strings.TrimPrefix(containerInfo.Name, "/")47 containerInfo.Name = strings.TrimPrefix(containerInfo.Name, "/")
48- // Get just the network names48+ // Get the network names
49 networkNames := make([]string, 0, len(containerInfo.NetworkSettings.Networks))49 networkNames := make([]string, 0, len(containerInfo.NetworkSettings.Networks))
50 for networkName := range containerInfo.NetworkSettings.Networks {50 for networkName := range containerInfo.NetworkSettings.Networks {
51 networkNames = append(networkNames, networkName)51 networkNames = append(networkNames, networkName)
@@ -65,7 +65,7 @@ func ContainerManagerEditGET(c echo.Context, a *app.App) error {
65 volumeMappings = append(volumeMappings, fmt.Sprintf("%s:%s", mount.Source, mount.Destination))65 volumeMappings = append(volumeMappings, fmt.Sprintf("%s:%s", mount.Source, mount.Destination))
66 }66 }
67 67
68- // Initialize a map to store the information you want to display68+ // Initialize a map to store the information from the container
69 infoMap := make(map[string]interface{})69 infoMap := make(map[string]interface{})
70 70
71 // Populate the map with container details71 // Populate the map with container details
@@ -198,7 +198,7 @@ func ContainerManagerEditPOST(c echo.Context, a *app.App) error {
198 return sendError(c, fmt.Errorf("transaction failed: %w", err))198 return sendError(c, fmt.Errorf("transaction failed: %w", err))
199 }199 }
200 200
201- return c.HTML(200, fmt.Sprintf("Successfully edited container %s", tq.NewContainerName))201+ return c.HTML(200, ActionSuccess(a))
202 }202 }
203 203
204 // CreateNewContainerStep creates a new container with the given parameters204 // CreateNewContainerStep creates a new container with the given parameters
M internal/httpserve/handler/oauth.go +10 -0
@@ -52,6 +52,7 @@ func RenderLoginPage(c echo.Context, a *app.App) error {
5252 return c.HTML(200, renderedHTML)
5353 }
5454
55+// StartOAuthGithub starts the Github OAuth flow
5556 func StartOAuthGithub(c echo.Context, a *app.App) error {
5657 // Clear the session in case it is already set or corrupted
5758 err := ResetSession(c)
@@ -77,6 +78,7 @@ type Sessions struct {
7778 *db.Sessions
7879 }
7980
81+// OAuthCallback handles the callback response from Github OAuth
8082 func OAuthCallback(c echo.Context, a *app.App) error {
8183 redirectPath := a.Config.Admin.Path
8284
@@ -119,6 +121,7 @@ func OAuthCallback(c echo.Context, a *app.App) error {
119121 return c.Redirect(http.StatusFound, redirectPath)
120122 }
121123
124+// parseQueryParams parses the query parameters from the callback URL
122125 func parseQueryParams(c echo.Context) (string, string, error) {
123126 accessToken := c.QueryParam("access_token")
124127 encodedState := c.QueryParam("state")
@@ -131,6 +134,7 @@ func parseQueryParams(c echo.Context) (string, string, error) {
131134 return accessToken, encodedState, nil
132135 }
133136
137+// getSession gets the session from the context
134138 func getSession(c echo.Context) (*sessions.Session, error) {
135139 sess, err := session.Get("session", c)
136140 if err != nil {
@@ -139,6 +143,7 @@ func getSession(c echo.Context) (*sessions.Session, error) {
139143 return sess, nil
140144 }
141145
146+// handleUser handles the user creation or update
142147 func handleUser(c echo.Context, a *app.App, accessToken, browserInfo string, userInfo *queries.GithubUserInfo) (*db.User, error) {
143148 userExists, err := queries.CheckDBUserExists(a)
144149 if err != nil {
@@ -179,6 +184,7 @@ func handleUser(c echo.Context, a *app.App, accessToken, browserInfo string, use
179184 return nil, fmt.Errorf("could not handle user")
180185 }
181186
187+// createUser creates a new user in the database
182188 func createUser(a *app.App, accessToken, browserInfo string, userInfo *queries.GithubUserInfo) (*db.User, error) {
183189 err := queries.CreateUser(a, accessToken, browserInfo, userInfo)
184190 if err != nil {
@@ -187,6 +193,7 @@ func createUser(a *app.App, accessToken, browserInfo string, userInfo *queries.G
187193 return &a.DBTables.User, nil
188194 }
189195
196+// updateUser updates an existing user in the database
190197 func updateUser(a *app.App, accessToken, browserInfo string, userInfo *queries.GithubUserInfo) (*db.User, error) {
191198 user, err := queries.UpdateUser(a, accessToken, browserInfo, userInfo)
192199 if err != nil {
@@ -195,6 +202,7 @@ func updateUser(a *app.App, accessToken, browserInfo string, userInfo *queries.G
195202 return user, nil
196203 }
197204
205+// setSessionValues sets the session values for the user
198206 func setSessionValues(c echo.Context, sess *sessions.Session, user *db.User, accountID string, expires string, sessionID string) error {
199207 sess.Values["authenticated"] = true
200208 sess.Values["accountID"] = accountID
@@ -207,6 +215,7 @@ func setSessionValues(c echo.Context, sess *sessions.Session, user *db.User, acc
207215 return nil
208216 }
209217
218+// fetchGithubAPI fetches the Github API
210219 func fetchGithubAPI(url string, authHeader string, result interface{}) error {
211220 req, err := http.NewRequest("GET", url, nil)
212221 if err != nil {
@@ -238,6 +247,7 @@ func fetchGithubAPI(url string, authHeader string, result interface{}) error {
238247 return nil
239248 }
240249
250+// githubGetUserDetails gets the user details from the Github API using the access token
241251 func githubGetUserDetails(c echo.Context, a *app.App) (userInfo *queries.GithubUserInfo, err error) {
242252 accessToken := c.QueryParam("access_token")
243253
@@ -52,6 +52,7 @@ func RenderLoginPage(c echo.Context, a *app.App) error {
52 return c.HTML(200, renderedHTML)52 return c.HTML(200, renderedHTML)
53 }53 }
54 54
55+// StartOAuthGithub starts the Github OAuth flow
55 func StartOAuthGithub(c echo.Context, a *app.App) error {56 func StartOAuthGithub(c echo.Context, a *app.App) error {
56 // Clear the session in case it is already set or corrupted57 // Clear the session in case it is already set or corrupted
57 err := ResetSession(c)58 err := ResetSession(c)
@@ -77,6 +78,7 @@ type Sessions struct {
77 *db.Sessions78 *db.Sessions
78 }79 }
79 80
81+// OAuthCallback handles the callback response from Github OAuth
80 func OAuthCallback(c echo.Context, a *app.App) error {82 func OAuthCallback(c echo.Context, a *app.App) error {
81 redirectPath := a.Config.Admin.Path83 redirectPath := a.Config.Admin.Path
82 84
@@ -119,6 +121,7 @@ func OAuthCallback(c echo.Context, a *app.App) error {
119 return c.Redirect(http.StatusFound, redirectPath)121 return c.Redirect(http.StatusFound, redirectPath)
120 }122 }
121 123
124+// parseQueryParams parses the query parameters from the callback URL
122 func parseQueryParams(c echo.Context) (string, string, error) {125 func parseQueryParams(c echo.Context) (string, string, error) {
123 accessToken := c.QueryParam("access_token")126 accessToken := c.QueryParam("access_token")
124 encodedState := c.QueryParam("state")127 encodedState := c.QueryParam("state")
@@ -131,6 +134,7 @@ func parseQueryParams(c echo.Context) (string, string, error) {
131 return accessToken, encodedState, nil134 return accessToken, encodedState, nil
132 }135 }
133 136
137+// getSession gets the session from the context
134 func getSession(c echo.Context) (*sessions.Session, error) {138 func getSession(c echo.Context) (*sessions.Session, error) {
135 sess, err := session.Get("session", c)139 sess, err := session.Get("session", c)
136 if err != nil {140 if err != nil {
@@ -139,6 +143,7 @@ func getSession(c echo.Context) (*sessions.Session, error) {
139 return sess, nil143 return sess, nil
140 }144 }
141 145
146+// handleUser handles the user creation or update
142 func handleUser(c echo.Context, a *app.App, accessToken, browserInfo string, userInfo *queries.GithubUserInfo) (*db.User, error) {147 func handleUser(c echo.Context, a *app.App, accessToken, browserInfo string, userInfo *queries.GithubUserInfo) (*db.User, error) {
143 userExists, err := queries.CheckDBUserExists(a)148 userExists, err := queries.CheckDBUserExists(a)
144 if err != nil {149 if err != nil {
@@ -179,6 +184,7 @@ func handleUser(c echo.Context, a *app.App, accessToken, browserInfo string, use
179 return nil, fmt.Errorf("could not handle user")184 return nil, fmt.Errorf("could not handle user")
180 }185 }
181 186
187+// createUser creates a new user in the database
182 func createUser(a *app.App, accessToken, browserInfo string, userInfo *queries.GithubUserInfo) (*db.User, error) {188 func createUser(a *app.App, accessToken, browserInfo string, userInfo *queries.GithubUserInfo) (*db.User, error) {
183 err := queries.CreateUser(a, accessToken, browserInfo, userInfo)189 err := queries.CreateUser(a, accessToken, browserInfo, userInfo)
184 if err != nil {190 if err != nil {
@@ -187,6 +193,7 @@ func createUser(a *app.App, accessToken, browserInfo string, userInfo *queries.G
187 return &a.DBTables.User, nil193 return &a.DBTables.User, nil
188 }194 }
189 195
196+// updateUser updates an existing user in the database
190 func updateUser(a *app.App, accessToken, browserInfo string, userInfo *queries.GithubUserInfo) (*db.User, error) {197 func updateUser(a *app.App, accessToken, browserInfo string, userInfo *queries.GithubUserInfo) (*db.User, error) {
191 user, err := queries.UpdateUser(a, accessToken, browserInfo, userInfo)198 user, err := queries.UpdateUser(a, accessToken, browserInfo, userInfo)
192 if err != nil {199 if err != nil {
@@ -195,6 +202,7 @@ func updateUser(a *app.App, accessToken, browserInfo string, userInfo *queries.G
195 return user, nil202 return user, nil
196 }203 }
197 204
205+// setSessionValues sets the session values for the user
198 func setSessionValues(c echo.Context, sess *sessions.Session, user *db.User, accountID string, expires string, sessionID string) error {206 func setSessionValues(c echo.Context, sess *sessions.Session, user *db.User, accountID string, expires string, sessionID string) error {
199 sess.Values["authenticated"] = true207 sess.Values["authenticated"] = true
200 sess.Values["accountID"] = accountID208 sess.Values["accountID"] = accountID
@@ -207,6 +215,7 @@ func setSessionValues(c echo.Context, sess *sessions.Session, user *db.User, acc
207 return nil215 return nil
208 }216 }
209 217
218+// fetchGithubAPI fetches the Github API
210 func fetchGithubAPI(url string, authHeader string, result interface{}) error {219 func fetchGithubAPI(url string, authHeader string, result interface{}) error {
211 req, err := http.NewRequest("GET", url, nil)220 req, err := http.NewRequest("GET", url, nil)
212 if err != nil {221 if err != nil {
@@ -238,6 +247,7 @@ func fetchGithubAPI(url string, authHeader string, result interface{}) error {
238 return nil247 return nil
239 }248 }
240 249
250+// githubGetUserDetails gets the user details from the Github API using the access token
241 func githubGetUserDetails(c echo.Context, a *app.App) (userInfo *queries.GithubUserInfo, err error) {251 func githubGetUserDetails(c echo.Context, a *app.App) (userInfo *queries.GithubUserInfo, err error) {
242 accessToken := c.QueryParam("access_token")252 accessToken := c.QueryParam("access_token")
243 253

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