detached fe7fdda

Refactor image storage: centralize in data/images, use UUIDs for filename, improve SaveImageToStorage and RemoveFromStorage functions

commit
fe7fddac78a6dddd983999f8b91f52cbaafecb0a
author
bamen · · 2024-08-27 17:21 UTC
parent
746cfe6
signature
unsigned
6 files · +103 -105
A build-dev.sh +18 -0
@@ -0,0 +1,18 @@
1+#!/bin/bash
2+
3+set -e
4+
5+REPO="ghcr.io/bnema/gordon"
6+TAG="dev"
7+DIST_DIR="./dist"
8+
9+# Ensure dist directory exists
10+mkdir -p $DIST_DIR
11+
12+# Clean up previous builds
13+rm -f $DIST_DIR/*
14+
15+# Build Go binaries for multiple platforms with CGO_ENABLED=0
16+echo "Building Go binaries..."
17+CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $DIST_DIR/gordon-linux-amd64 ./cmd/cli
18+CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o $DIST_DIR/gordon-linux-arm64 ./cmd/cli
@@ -0,0 +1,18 @@
1+#!/bin/bash
2+
3+set -e
4+
5+REPO="ghcr.io/bnema/gordon"
6+TAG="dev"
7+DIST_DIR="./dist"
8+
9+# Ensure dist directory exists
10+mkdir -p $DIST_DIR
11+
12+# Clean up previous builds
13+rm -f $DIST_DIR/*
14+
15+# Build Go binaries for multiple platforms with CGO_ENABLED=0
16+echo "Building Go binaries..."
17+CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $DIST_DIR/gordon-linux-amd64 ./cmd/cli
18+CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o $DIST_DIR/gordon-linux-arm64 ./cmd/cli
M internal/cli/handler/http.go +11 -39
@@ -48,8 +48,8 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
4848 var req *http.Request
4949 var err error
5050
51- // Handle deploy type payload
52- if rp.Type == "deploy" {
51+ switch rp.Type {
52+ case "deploy":
5353 deployPayload, ok := rp.Payload.(common.DeployPayload)
5454 if !ok {
5555 return nil, fmt.Errorf("invalid payload type for deploy")
@@ -66,22 +66,7 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
6666 req.Header.Set("X-Target-Domain", deployPayload.TargetDomain)
6767 req.Header.Set("X-Image-Name", deployPayload.ImageName)
6868
69- client := &http.Client{}
70- resp, err := client.Do(req)
71- if err != nil {
72- return nil, fmt.Errorf("failed to send request: %w", err)
73- }
74- defer resp.Body.Close()
75-
76- body, err := io.ReadAll(resp.Body)
77- if err != nil {
78- return nil, fmt.Errorf("failed to read response body: %w", err)
79- }
80- return &Response{Body: body}, nil
81- }
82-
83- // Handle other types of payloads
84- if rp.Type != "push" {
69+ case "push":
8570 pushPayload, ok := rp.Payload.(common.PushPayload)
8671 if !ok {
8772 return nil, fmt.Errorf("invalid payload type for push")
@@ -96,35 +81,21 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
9681 req.Header.Set("Content-Type", "application/octet-stream")
9782 req.Header.Set("X-Image-Name", pushPayload.ImageName)
9883
99- client := &http.Client{}
100- resp, err := client.Do(req)
84+ default: // This will handle "ping" and any other types
85+ jsonPayload, err := json.Marshal(rp)
10186 if err != nil {
102- return nil, fmt.Errorf("failed to send request: %w", err)
87+ return nil, fmt.Errorf("failed to marshal payload: %w", err)
10388 }
10489
105- defer resp.Body.Close()
106-
107- body, err := io.ReadAll(resp.Body)
90+ req, err = http.NewRequest(method, apiUrl+endpoint, bytes.NewBuffer(jsonPayload))
10891 if err != nil {
109- return nil, fmt.Errorf("failed to read response body: %w", err)
92+ return nil, fmt.Errorf("failed to create new JSON request: %w", err)
11093 }
11194
112- return &Response{Body: body}, nil
113- }
114-
115- jsonPayload, err := json.Marshal(rp)
116- if err != nil {
117- return nil, fmt.Errorf("failed to marshal payload: %w", err)
118- }
119-
120- req, err = http.NewRequest(method, apiUrl+endpoint, bytes.NewBuffer(jsonPayload))
121- if err != nil {
122- return nil, fmt.Errorf("failed to create new JSON request: %w", err)
95+ setAuthRequestHeaders(req, token)
96+ req.Header.Set("Content-Type", "application/json")
12397 }
12498
125- setAuthRequestHeaders(req, token)
126- req.Header.Set("Content-Type", "application/json")
127-
12899 client := &http.Client{}
129100 resp, err := client.Do(req)
130101 if err != nil {
@@ -136,5 +107,6 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
136107 if err != nil {
137108 return nil, fmt.Errorf("failed to read response body: %w", err)
138109 }
110+
139111 return &Response{Http: resp, Body: body, StatusCode: resp.StatusCode}, nil
140112 }
@@ -48,8 +48,8 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
48 var req *http.Request48 var req *http.Request
49 var err error49 var err error
50 50
51- // Handle deploy type payload51+ switch rp.Type {
52- if rp.Type == "deploy" {52+ case "deploy":
53 deployPayload, ok := rp.Payload.(common.DeployPayload)53 deployPayload, ok := rp.Payload.(common.DeployPayload)
54 if !ok {54 if !ok {
55 return nil, fmt.Errorf("invalid payload type for deploy")55 return nil, fmt.Errorf("invalid payload type for deploy")
@@ -66,22 +66,7 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
66 req.Header.Set("X-Target-Domain", deployPayload.TargetDomain)66 req.Header.Set("X-Target-Domain", deployPayload.TargetDomain)
67 req.Header.Set("X-Image-Name", deployPayload.ImageName)67 req.Header.Set("X-Image-Name", deployPayload.ImageName)
68 68
69- client := &http.Client{}69+ case "push":
70- resp, err := client.Do(req)
71- if err != nil {
72- return nil, fmt.Errorf("failed to send request: %w", err)
73- }
74- defer resp.Body.Close()
75-
76- body, err := io.ReadAll(resp.Body)
77- if err != nil {
78- return nil, fmt.Errorf("failed to read response body: %w", err)
79- }
80- return &Response{Body: body}, nil
81- }
82-
83- // Handle other types of payloads
84- if rp.Type != "push" {
85 pushPayload, ok := rp.Payload.(common.PushPayload)70 pushPayload, ok := rp.Payload.(common.PushPayload)
86 if !ok {71 if !ok {
87 return nil, fmt.Errorf("invalid payload type for push")72 return nil, fmt.Errorf("invalid payload type for push")
@@ -96,35 +81,21 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
96 req.Header.Set("Content-Type", "application/octet-stream")81 req.Header.Set("Content-Type", "application/octet-stream")
97 req.Header.Set("X-Image-Name", pushPayload.ImageName)82 req.Header.Set("X-Image-Name", pushPayload.ImageName)
98 83
99- client := &http.Client{}84+ default: // This will handle "ping" and any other types
100- resp, err := client.Do(req)85+ jsonPayload, err := json.Marshal(rp)
101 if err != nil {86 if err != nil {
102- return nil, fmt.Errorf("failed to send request: %w", err)87+ return nil, fmt.Errorf("failed to marshal payload: %w", err)
103 }88 }
104 89
105- defer resp.Body.Close()90+ req, err = http.NewRequest(method, apiUrl+endpoint, bytes.NewBuffer(jsonPayload))
106-
107- body, err := io.ReadAll(resp.Body)
108 if err != nil {91 if err != nil {
109- return nil, fmt.Errorf("failed to read response body: %w", err)92+ return nil, fmt.Errorf("failed to create new JSON request: %w", err)
110 }93 }
111 94
112- return &Response{Body: body}, nil95+ setAuthRequestHeaders(req, token)
113- }96+ req.Header.Set("Content-Type", "application/json")
114-
115- jsonPayload, err := json.Marshal(rp)
116- if err != nil {
117- return nil, fmt.Errorf("failed to marshal payload: %w", err)
118- }
119-
120- req, err = http.NewRequest(method, apiUrl+endpoint, bytes.NewBuffer(jsonPayload))
121- if err != nil {
122- return nil, fmt.Errorf("failed to create new JSON request: %w", err)
123 }97 }
124 98
125- setAuthRequestHeaders(req, token)
126- req.Header.Set("Content-Type", "application/json")
127-
128 client := &http.Client{}99 client := &http.Client{}
129 resp, err := client.Do(req)100 resp, err := client.Do(req)
130 if err != nil {101 if err != nil {
@@ -136,5 +107,6 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
136 if err != nil {107 if err != nil {
137 return nil, fmt.Errorf("failed to read response body: %w", err)108 return nil, fmt.Errorf("failed to read response body: %w", err)
138 }109 }
110+
139 return &Response{Http: resp, Body: body, StatusCode: resp.StatusCode}, nil111 return &Response{Http: resp, Body: body, StatusCode: resp.StatusCode}, nil
140 }112 }
M internal/cli/handler/pingrequest.go +4 -13
@@ -18,30 +18,21 @@ type PingResponse struct {
1818 func PerformPingRequest(a *cli.App) (PingResponse, error) {
1919 var pingResp PingResponse
2020
21- data := map[string]interface{}{"message": "ping"}
22- payload, err := common.NewPingPayload(data)
23- if err != nil {
24- return pingResp, fmt.Errorf("failed to create new ping payload: %w", err)
25- }
26-
27- // Create a RequestPayload and populate it
21+ pingPayload := common.PingPayload{Message: "ping"}
2822 reqPayload := common.RequestPayload{
2923 Type: "ping",
30- Payload: payload,
24+ Payload: pingPayload,
3125 }
3226
3327 resp, err := SendHTTPRequest(a, &reqPayload, "GET", "/ping")
3428 if err != nil {
35- return pingResp, fmt.Errorf("failed to reach backend")
29+ return pingResp, fmt.Errorf("failed to reach server: %w", err)
3630 }
3731
3832 defer resp.Http.Body.Close()
3933
4034 if resp.StatusCode != http.StatusOK {
41- return PingResponse{
42- Uptime: "",
43- Version: "",
44- }, fmt.Errorf("expected status code 200, got %d", resp.StatusCode)
35+ return PingResponse{}, fmt.Errorf("expected status code 200, got %d", resp.StatusCode)
4536 }
4637
4738 // Unmarshal the JSON response into the PingResponse struct
@@ -18,30 +18,21 @@ type PingResponse struct {
18 func PerformPingRequest(a *cli.App) (PingResponse, error) {18 func PerformPingRequest(a *cli.App) (PingResponse, error) {
19 var pingResp PingResponse19 var pingResp PingResponse
20 20
21- data := map[string]interface{}{"message": "ping"}21+ pingPayload := common.PingPayload{Message: "ping"}
22- payload, err := common.NewPingPayload(data)
23- if err != nil {
24- return pingResp, fmt.Errorf("failed to create new ping payload: %w", err)
25- }
26-
27- // Create a RequestPayload and populate it
28 reqPayload := common.RequestPayload{22 reqPayload := common.RequestPayload{
29 Type: "ping",23 Type: "ping",
30- Payload: payload,24+ Payload: pingPayload,
31 }25 }
32 26
33 resp, err := SendHTTPRequest(a, &reqPayload, "GET", "/ping")27 resp, err := SendHTTPRequest(a, &reqPayload, "GET", "/ping")
34 if err != nil {28 if err != nil {
35- return pingResp, fmt.Errorf("failed to reach backend")29+ return pingResp, fmt.Errorf("failed to reach server: %w", err)
36 }30 }
37 31
38 defer resp.Http.Body.Close()32 defer resp.Http.Body.Close()
39 33
40 if resp.StatusCode != http.StatusOK {34 if resp.StatusCode != http.StatusOK {
41- return PingResponse{35+ return PingResponse{}, fmt.Errorf("expected status code 200, got %d", resp.StatusCode)
42- Uptime: "",
43- Version: "",
44- }, fmt.Errorf("expected status code 200, got %d", resp.StatusCode)
45 }36 }
46 37
47 // Unmarshal the JSON response into the PingResponse struct38 // Unmarshal the JSON response into the PingResponse struct
M internal/httpserve/handler/clientendpoins.go +18 -30
@@ -1,9 +1,7 @@
11 package handler
22
33 import (
4- "bytes"
54 "fmt"
6- "io"
75 "net/http"
86 "regexp"
97
@@ -27,13 +25,9 @@ func (info *InfoResponse) Populate(a *server.App) {
2725
2826 // Handle GET on /api/ping endpoint
2927 func GetInfos(c echo.Context, a *server.App) error {
30- body, _ := io.ReadAll(c.Request().Body)
31- c.Request().Body = io.NopCloser(bytes.NewBuffer(body)) // Reset the body
32-
3328 payload := new(common.RequestPayload)
3429 if err := c.Bind(payload); err != nil {
35- fmt.Println("Bind Error:", err)
36- return c.JSON(http.StatusBadRequest, err.Error())
30+ return c.JSON(http.StatusBadRequest, "Invalid payload: "+err.Error())
3731 }
3832
3933 if payload.Type != "ping" {
@@ -42,10 +36,11 @@ func GetInfos(c echo.Context, a *server.App) error {
4236
4337 pingPayload, ok := payload.Payload.(common.PingPayload)
4438 if !ok {
45- return c.JSON(http.StatusBadRequest, "Invalid payload type")
39+ return c.JSON(http.StatusBadRequest, "Invalid payload structure")
4640 }
41+
4742 if pingPayload.Message != "ping" {
48- return c.JSON(http.StatusBadRequest, "Invalid payload data")
43+ return c.JSON(http.StatusBadRequest, "Invalid ping message")
4944 }
5045
5146 // Prepare and populate the information
@@ -53,11 +48,9 @@ func GetInfos(c echo.Context, a *server.App) error {
5348 info.Populate(a)
5449
5550 return c.JSON(http.StatusOK, info)
56-
5751 }
5852
5953 func PostDeploy(c echo.Context, a *server.App) error {
60-
6154 // Initialize pushPayload object
6255 payload := &common.DeployPayload{
6356 Ports: c.Request().Header.Get("X-Ports"),
@@ -89,21 +82,24 @@ func PostDeploy(c echo.Context, a *server.App) error {
8982 }
9083
9184 // Save the image tar in the storage
92- imageFilePath, err := store.SaveImageToStorage(&a.Config, imageFileName, imageReader)
85+ imagePath, err := store.SaveImageToStorage(&a.Config, imageFileName, imageReader)
9386 if err != nil {
94- return sendJsonError(c, err)
87+ return sendJsonError(c, fmt.Errorf("failed to save image: %v", err))
9588 }
9689
90+ fmt.Printf("Image saved successfully to: %s\n", imagePath)
91+
9792 // Import the tar in docker
98- err = docker.ImportImageToEngine(imageFilePath)
93+ err = docker.ImportImageToEngine(imagePath)
9994 if err != nil {
100- return sendJsonError(c, err)
95+ store.RemoveFromStorage(imagePath) // Clean up the saved image if import fails
96+ return sendJsonError(c, fmt.Errorf("failed to import image: %v", err))
10197 }
10298
10399 // Remove the image from the storage
104- err = store.RemoveFromStorage()
100+ err = store.RemoveFromStorage(imagePath)
105101 if err != nil {
106- return sendJsonError(c, err)
102+ return sendJsonError(c, fmt.Errorf("failed to remove temporary image file: %v", err))
107103 }
108104
109105 // append localhost to the image name since it's a manually imported image and not from a registry
@@ -112,7 +108,7 @@ func PostDeploy(c echo.Context, a *server.App) error {
112108 // Get the image ID
113109 imageID, err := docker.GetImageIDByName(payload.ImageName)
114110 if err != nil {
115- return sendJsonError(c, err)
111+ return sendJsonError(c, fmt.Errorf("failed to get image ID: %v", err))
116112 }
117113
118114 // Update the payload with the image ID
@@ -121,30 +117,22 @@ func PostDeploy(c echo.Context, a *server.App) error {
121117 // Create the container using cmdparams.FromPayloadStructToCmdParams
122118 params, err := cmdparams.FromPayloadStructToCmdParams(payload, a)
123119 if err != nil {
124- return sendJsonError(c, err)
120+ return sendJsonError(c, fmt.Errorf("failed to create command parameters: %v", err))
125121 }
126122
127123 // Create the container
128124 containerID, err := docker.CreateContainer(params)
129125 if err != nil {
130- return sendJsonError(c, err)
126+ return sendJsonError(c, fmt.Errorf("failed to create container: %v", err))
131127 }
132128
133129 // Start the container
134130 err = docker.StartContainer(containerID)
135131 if err != nil {
136- return sendJsonError(c, err)
132+ docker.RemoveContainer(containerID) // Clean up if start fails
133+ return sendJsonError(c, fmt.Errorf("failed to start container: %v", err))
137134 }
138135
139- // Remove the container if it fails to start
140- defer func() {
141- if err != nil {
142- docker.RemoveContainer(containerID)
143- c.JSON(http.StatusInternalServerError, sendJsonError(c, err))
144- return
145- }
146- }()
147-
148136 // If we arrive here, send back payload.TargetDomain so the client can test it
149137 return c.JSON(http.StatusOK, payload.TargetDomain)
150138 }
@@ -1,9 +1,7 @@
1 package handler1 package handler
2 2
3 import (3 import (
4- "bytes"
5 "fmt"4 "fmt"
6- "io"
7 "net/http"5 "net/http"
8 "regexp"6 "regexp"
9 7
@@ -27,13 +25,9 @@ func (info *InfoResponse) Populate(a *server.App) {
27 25
28 // Handle GET on /api/ping endpoint26 // Handle GET on /api/ping endpoint
29 func GetInfos(c echo.Context, a *server.App) error {27 func GetInfos(c echo.Context, a *server.App) error {
30- body, _ := io.ReadAll(c.Request().Body)
31- c.Request().Body = io.NopCloser(bytes.NewBuffer(body)) // Reset the body
32-
33 payload := new(common.RequestPayload)28 payload := new(common.RequestPayload)
34 if err := c.Bind(payload); err != nil {29 if err := c.Bind(payload); err != nil {
35- fmt.Println("Bind Error:", err)30+ return c.JSON(http.StatusBadRequest, "Invalid payload: "+err.Error())
36- return c.JSON(http.StatusBadRequest, err.Error())
37 }31 }
38 32
39 if payload.Type != "ping" {33 if payload.Type != "ping" {
@@ -42,10 +36,11 @@ func GetInfos(c echo.Context, a *server.App) error {
42 36
43 pingPayload, ok := payload.Payload.(common.PingPayload)37 pingPayload, ok := payload.Payload.(common.PingPayload)
44 if !ok {38 if !ok {
45- return c.JSON(http.StatusBadRequest, "Invalid payload type")39+ return c.JSON(http.StatusBadRequest, "Invalid payload structure")
46 }40 }
41+
47 if pingPayload.Message != "ping" {42 if pingPayload.Message != "ping" {
48- return c.JSON(http.StatusBadRequest, "Invalid payload data")43+ return c.JSON(http.StatusBadRequest, "Invalid ping message")
49 }44 }
50 45
51 // Prepare and populate the information46 // Prepare and populate the information
@@ -53,11 +48,9 @@ func GetInfos(c echo.Context, a *server.App) error {
53 info.Populate(a)48 info.Populate(a)
54 49
55 return c.JSON(http.StatusOK, info)50 return c.JSON(http.StatusOK, info)
56-
57 }51 }
58 52
59 func PostDeploy(c echo.Context, a *server.App) error {53 func PostDeploy(c echo.Context, a *server.App) error {
60-
61 // Initialize pushPayload object54 // Initialize pushPayload object
62 payload := &common.DeployPayload{55 payload := &common.DeployPayload{
63 Ports: c.Request().Header.Get("X-Ports"),56 Ports: c.Request().Header.Get("X-Ports"),
@@ -89,21 +82,24 @@ func PostDeploy(c echo.Context, a *server.App) error {
89 }82 }
90 83
91 // Save the image tar in the storage84 // Save the image tar in the storage
92- imageFilePath, err := store.SaveImageToStorage(&a.Config, imageFileName, imageReader)85+ imagePath, err := store.SaveImageToStorage(&a.Config, imageFileName, imageReader)
93 if err != nil {86 if err != nil {
94- return sendJsonError(c, err)87+ return sendJsonError(c, fmt.Errorf("failed to save image: %v", err))
95 }88 }
96 89
90+ fmt.Printf("Image saved successfully to: %s\n", imagePath)
91+
97 // Import the tar in docker92 // Import the tar in docker
98- err = docker.ImportImageToEngine(imageFilePath)93+ err = docker.ImportImageToEngine(imagePath)
99 if err != nil {94 if err != nil {
100- return sendJsonError(c, err)95+ store.RemoveFromStorage(imagePath) // Clean up the saved image if import fails
96+ return sendJsonError(c, fmt.Errorf("failed to import image: %v", err))
101 }97 }
102 98
103 // Remove the image from the storage99 // Remove the image from the storage
104- err = store.RemoveFromStorage()100+ err = store.RemoveFromStorage(imagePath)
105 if err != nil {101 if err != nil {
106- return sendJsonError(c, err)102+ return sendJsonError(c, fmt.Errorf("failed to remove temporary image file: %v", err))
107 }103 }
108 104
109 // append localhost to the image name since it's a manually imported image and not from a registry105 // append localhost to the image name since it's a manually imported image and not from a registry
@@ -112,7 +108,7 @@ func PostDeploy(c echo.Context, a *server.App) error {
112 // Get the image ID108 // Get the image ID
113 imageID, err := docker.GetImageIDByName(payload.ImageName)109 imageID, err := docker.GetImageIDByName(payload.ImageName)
114 if err != nil {110 if err != nil {
115- return sendJsonError(c, err)111+ return sendJsonError(c, fmt.Errorf("failed to get image ID: %v", err))
116 }112 }
117 113
118 // Update the payload with the image ID114 // Update the payload with the image ID
@@ -121,30 +117,22 @@ func PostDeploy(c echo.Context, a *server.App) error {
121 // Create the container using cmdparams.FromPayloadStructToCmdParams117 // Create the container using cmdparams.FromPayloadStructToCmdParams
122 params, err := cmdparams.FromPayloadStructToCmdParams(payload, a)118 params, err := cmdparams.FromPayloadStructToCmdParams(payload, a)
123 if err != nil {119 if err != nil {
124- return sendJsonError(c, err)120+ return sendJsonError(c, fmt.Errorf("failed to create command parameters: %v", err))
125 }121 }
126 122
127 // Create the container123 // Create the container
128 containerID, err := docker.CreateContainer(params)124 containerID, err := docker.CreateContainer(params)
129 if err != nil {125 if err != nil {
130- return sendJsonError(c, err)126+ return sendJsonError(c, fmt.Errorf("failed to create container: %v", err))
131 }127 }
132 128
133 // Start the container129 // Start the container
134 err = docker.StartContainer(containerID)130 err = docker.StartContainer(containerID)
135 if err != nil {131 if err != nil {
136- return sendJsonError(c, err)132+ docker.RemoveContainer(containerID) // Clean up if start fails
133+ return sendJsonError(c, fmt.Errorf("failed to start container: %v", err))
137 }134 }
138 135
139- // Remove the container if it fails to start
140- defer func() {
141- if err != nil {
142- docker.RemoveContainer(containerID)
143- c.JSON(http.StatusInternalServerError, sendJsonError(c, err))
144- return
145- }
146- }()
147-
148 // If we arrive here, send back payload.TargetDomain so the client can test it136 // If we arrive here, send back payload.TargetDomain so the client can test it
149 return c.JSON(http.StatusOK, payload.TargetDomain)137 return c.JSON(http.StatusOK, payload.TargetDomain)
150 }138 }
M internal/httpserve/handler/uploadcontainerimage.go +1 -1
@@ -91,7 +91,7 @@ func UploadImagePOSTHandler(c echo.Context, a *server.App) error {
9191 }
9292
9393 // Remove the image from the storage directory
94- err = store.RemoveFromStorage()
94+ err = store.RemoveFromStorage(saveInPath)
9595 if err != nil {
9696 return sendError(c, err)
9797 }
@@ -91,7 +91,7 @@ func UploadImagePOSTHandler(c echo.Context, a *server.App) error {
91 }91 }
92 92
93 // Remove the image from the storage directory93 // Remove the image from the storage directory
94- err = store.RemoveFromStorage()94+ err = store.RemoveFromStorage(saveInPath)
95 if err != nil {95 if err != nil {
96 return sendError(c, err)96 return sendError(c, err)
97 }97 }
M pkg/store/storeimages.go +51 -22
@@ -5,53 +5,82 @@ import (
55 "io"
66 "os"
77 "path/filepath"
8+ "sync"
89
910 "github.com/bnema/gordon/internal/common"
11+ "github.com/google/uuid"
1012 )
1113
12-var imagePath string
14+var (
15+ imagePath string
16+ mu sync.Mutex
17+)
18+
19+// setImagePath sets the image path in a thread-safe manner
20+func setImagePath(path string) {
21+ mu.Lock()
22+ defer mu.Unlock()
23+ imagePath = path
24+}
1325
14-func SaveImageToStorage(config *common.Config, filename string, buf io.Reader) (string, error) {
15- // Check if the folder exist if not create it
16- if _, err := os.Stat(config.General.StorageDir); os.IsNotExist(err) {
17- err := os.MkdirAll(config.General.StorageDir, 0755)
18- if err != nil {
19- return "", fmt.Errorf("failed to create storage directory: %v", err)
20- }
26+// getImagePath gets the image path in a thread-safe manner
27+func getImagePath() string {
28+ mu.Lock()
29+ defer mu.Unlock()
30+ return imagePath
31+}
32+
33+func SaveImageToStorage(config *common.Config, originalFilename string, buf io.Reader) (string, error) {
34+ // Ensure the images directory exists
35+ imagesDir := filepath.Join(config.General.StorageDir, "images")
36+ if err := os.MkdirAll(imagesDir, 0755); err != nil {
37+ return "", fmt.Errorf("failed to create images directory: %v", err)
2138 }
2239
23- // Define the path where the image will be saved
24- imagePath = filepath.Join(config.General.StorageDir, filename)
40+ // Generate a unique ID for the image
41+ id := uuid.New().String()
42+
43+ // Create a filename with the ID and original extension
44+ ext := filepath.Ext(originalFilename)
45+ filename := fmt.Sprintf("%s%s", id, ext)
2546
26- // Create or open a file for appending.
47+ // Full path for the new image
48+ imagePath := filepath.Join(imagesDir, filename)
49+
50+ fmt.Printf("Saving image to: %s\n", imagePath)
51+
52+ // Create the file
2753 outFile, err := os.Create(imagePath)
2854 if err != nil {
29- return "", fmt.Errorf("failed to open file: %v", err)
55+ return "", fmt.Errorf("failed to create file: %v", err)
3056 }
57+ defer outFile.Close()
3158
32- // Write the uploaded file's content to the outFile
33- _, err = io.Copy(outFile, buf)
59+ // Write the content
60+ written, err := io.Copy(outFile, buf)
3461 if err != nil {
62+ os.Remove(imagePath) // Clean up in case of error
3563 return "", fmt.Errorf("failed to write file: %v", err)
3664 }
3765
38- // Check if file exists
39- if _, err := os.Stat(imagePath); os.IsNotExist(err) {
40- return "", fmt.Errorf("file does not exist: %v", err)
41- }
66+ fmt.Printf("Wrote %d bytes to %s\n", written, imagePath)
4267
4368 return imagePath, nil
4469 }
4570
46-func RemoveFromStorage() error {
47- // Delete the file
71+func RemoveFromStorage(imagePath string) error {
72+ if imagePath == "" {
73+ return fmt.Errorf("no image path provided to remove")
74+ }
75+
76+ fmt.Printf("Attempting to remove file: %s\n", imagePath)
77+
4878 err := os.Remove(imagePath)
4979 if err != nil {
5080 return fmt.Errorf("failed to remove file: %v", err)
5181 }
5282
53- // clean up the path
54- imagePath = ""
83+ fmt.Printf("Successfully removed file: %s\n", imagePath)
5584
5685 return nil
5786 }
@@ -5,53 +5,82 @@ import (
5 "io"5 "io"
6 "os"6 "os"
7 "path/filepath"7 "path/filepath"
8+ "sync"
8 9
9 "github.com/bnema/gordon/internal/common"10 "github.com/bnema/gordon/internal/common"
11+ "github.com/google/uuid"
10 )12 )
11 13
12-var imagePath string14+var (
15+ imagePath string
16+ mu sync.Mutex
17+)
18+
19+// setImagePath sets the image path in a thread-safe manner
20+func setImagePath(path string) {
21+ mu.Lock()
22+ defer mu.Unlock()
23+ imagePath = path
24+}
13 25
14-func SaveImageToStorage(config *common.Config, filename string, buf io.Reader) (string, error) {26+// getImagePath gets the image path in a thread-safe manner
15- // Check if the folder exist if not create it27+func getImagePath() string {
16- if _, err := os.Stat(config.General.StorageDir); os.IsNotExist(err) {28+ mu.Lock()
17- err := os.MkdirAll(config.General.StorageDir, 0755)29+ defer mu.Unlock()
18- if err != nil {30+ return imagePath
19- return "", fmt.Errorf("failed to create storage directory: %v", err)31+}
20- }32+
33+func SaveImageToStorage(config *common.Config, originalFilename string, buf io.Reader) (string, error) {
34+ // Ensure the images directory exists
35+ imagesDir := filepath.Join(config.General.StorageDir, "images")
36+ if err := os.MkdirAll(imagesDir, 0755); err != nil {
37+ return "", fmt.Errorf("failed to create images directory: %v", err)
21 }38 }
22 39
23- // Define the path where the image will be saved40+ // Generate a unique ID for the image
24- imagePath = filepath.Join(config.General.StorageDir, filename)41+ id := uuid.New().String()
42+
43+ // Create a filename with the ID and original extension
44+ ext := filepath.Ext(originalFilename)
45+ filename := fmt.Sprintf("%s%s", id, ext)
25 46
26- // Create or open a file for appending.47+ // Full path for the new image
48+ imagePath := filepath.Join(imagesDir, filename)
49+
50+ fmt.Printf("Saving image to: %s\n", imagePath)
51+
52+ // Create the file
27 outFile, err := os.Create(imagePath)53 outFile, err := os.Create(imagePath)
28 if err != nil {54 if err != nil {
29- return "", fmt.Errorf("failed to open file: %v", err)55+ return "", fmt.Errorf("failed to create file: %v", err)
30 }56 }
57+ defer outFile.Close()
31 58
32- // Write the uploaded file's content to the outFile59+ // Write the content
33- _, err = io.Copy(outFile, buf)60+ written, err := io.Copy(outFile, buf)
34 if err != nil {61 if err != nil {
62+ os.Remove(imagePath) // Clean up in case of error
35 return "", fmt.Errorf("failed to write file: %v", err)63 return "", fmt.Errorf("failed to write file: %v", err)
36 }64 }
37 65
38- // Check if file exists66+ fmt.Printf("Wrote %d bytes to %s\n", written, imagePath)
39- if _, err := os.Stat(imagePath); os.IsNotExist(err) {
40- return "", fmt.Errorf("file does not exist: %v", err)
41- }
42 67
43 return imagePath, nil68 return imagePath, nil
44 }69 }
45 70
46-func RemoveFromStorage() error {71+func RemoveFromStorage(imagePath string) error {
47- // Delete the file72+ if imagePath == "" {
73+ return fmt.Errorf("no image path provided to remove")
74+ }
75+
76+ fmt.Printf("Attempting to remove file: %s\n", imagePath)
77+
48 err := os.Remove(imagePath)78 err := os.Remove(imagePath)
49 if err != nil {79 if err != nil {
50 return fmt.Errorf("failed to remove file: %v", err)80 return fmt.Errorf("failed to remove file: %v", err)
51 }81 }
52 82
53- // clean up the path83+ fmt.Printf("Successfully removed file: %s\n", imagePath)
54- imagePath = ""
55 84
56 return nil85 return nil
57 }86 }

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