Skip to content

Commit 545c4dd

Browse files
committed
Began implementing SSE for cloning status/progress
1 parent a0ff9e9 commit 545c4dd

3 files changed

Lines changed: 57 additions & 1 deletion

File tree

internal/api/handlers/cloning_handler.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/cpp-cyber/proclone/internal/ldap"
1212
"github.com/cpp-cyber/proclone/internal/proxmox"
1313
"github.com/cpp-cyber/proclone/internal/tools"
14+
"github.com/cpp-cyber/proclone/internal/tools/sse"
1415
"github.com/gin-contrib/sessions"
1516
"github.com/gin-gonic/gin"
1617
)
@@ -88,6 +89,16 @@ func (ch *CloningHandler) CloneTemplateHandler(c *gin.Context) {
8889
return
8990
}
9091

92+
// Create new sse object for streaming
93+
sseWriter, err := sse.NewWriter(c.Writer)
94+
if err != nil {
95+
c.JSON(http.StatusInternalServerError, gin.H{
96+
"error": "Failed to initialize SSE",
97+
"details": err.Error(),
98+
})
99+
return
100+
}
101+
91102
// Create the cloning request using the new format
92103
cloneReq := cloning.CloneRequest{
93104
Template: req.Template,
@@ -98,6 +109,7 @@ func (ch *CloningHandler) CloneTemplateHandler(c *gin.Context) {
98109
IsGroup: false,
99110
},
100111
},
112+
SSE: sseWriter,
101113
}
102114

103115
if err := ch.Service.CloneTemplate(cloneReq); err != nil {
@@ -144,16 +156,27 @@ func (ch *CloningHandler) AdminCloneTemplateHandler(c *gin.Context) {
144156
})
145157
}
146158

159+
// Create new sse object for streaming
160+
sseWriter, err := sse.NewWriter(c.Writer)
161+
if err != nil {
162+
c.JSON(http.StatusInternalServerError, gin.H{
163+
"error": "Failed to initialize SSE",
164+
"details": err.Error(),
165+
})
166+
return
167+
}
168+
147169
// Create clone request
148170
cloneReq := cloning.CloneRequest{
149171
Template: req.Template,
150172
Targets: targets,
151173
CheckExistingDeployments: false,
152174
StartingVMID: req.StartingVMID,
175+
SSE: sseWriter,
153176
}
154177

155178
// Perform clone operation
156-
err := ch.Service.CloneTemplate(cloneReq)
179+
err = ch.Service.CloneTemplate(cloneReq)
157180
if err != nil {
158181
log.Printf("Admin %s encountered error while bulk cloning template: %v", username, err)
159182
c.JSON(http.StatusInternalServerError, gin.H{

internal/cloning/types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/cpp-cyber/proclone/internal/ldap"
99
"github.com/cpp-cyber/proclone/internal/proxmox"
10+
"github.com/cpp-cyber/proclone/internal/tools/sse"
1011
"github.com/gin-gonic/gin"
1112
)
1213

@@ -116,6 +117,7 @@ type CloneRequest struct {
116117
Targets []CloneTarget
117118
CheckExistingDeployments bool // Whether to check if templates are already deployed
118119
StartingVMID int // Optional starting VMID for admin clones
120+
SSE *sse.Writer
119121
}
120122

121123
type RouterInfo struct {
@@ -124,3 +126,8 @@ type RouterInfo struct {
124126
Node string
125127
VMID int
126128
}
129+
130+
type ProgressMessage struct {
131+
Message string `json:"message"`
132+
Progress int `json:"progress"`
133+
}

internal/tools/sse/sse.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package sse
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
)
8+
9+
type Writer struct {
10+
w http.ResponseWriter
11+
f http.Flusher
12+
}
13+
14+
func NewWriter(w http.ResponseWriter) (*Writer, error) {
15+
f, ok := w.(http.Flusher)
16+
if !ok {
17+
return nil, fmt.Errorf("streaming unsupported")
18+
}
19+
return &Writer{w: w, f: f}, nil
20+
}
21+
22+
func (s *Writer) Send(message any) {
23+
b, _ := json.Marshal(message)
24+
fmt.Fprintf(s.w, "data: %s\n", b)
25+
s.f.Flush()
26+
}

0 commit comments

Comments
 (0)