Skip to content

Commit d64d68c

Browse files
committed
feat: enhance connection handling and validation in app and form components
1 parent 03da332 commit d64d68c

4 files changed

Lines changed: 90 additions & 23 deletions

File tree

app.go

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ import (
1010
"os"
1111
"sync"
1212
"time"
13+
14+
"github.com/wailsapp/wails/v2/pkg/runtime"
15+
)
16+
17+
// Connection timeouts according to best practices
18+
const (
19+
connectionTimeout = 10 * time.Second
20+
writeTimeout = 30 * time.Second
21+
readTimeout = 30 * time.Second
1322
)
1423

1524
// App struct
@@ -35,11 +44,23 @@ func (a *App) domReady(ctx context.Context) {}
3544

3645
// beforeClose is called when the application is about to quit.
3746
func (a *App) beforeClose(ctx context.Context) (prevent bool) {
47+
// Clean up any active connections
48+
a.Disconnect()
3849
return false
3950
}
4051

4152
// shutdown is called at application termination
42-
func (a *App) shutdown(ctx context.Context) {}
53+
func (a *App) shutdown(ctx context.Context) {
54+
// Ensure all connections are closed
55+
a.Disconnect()
56+
}
57+
58+
// IsConnected returns the current connection state
59+
func (a *App) IsConnected() bool {
60+
a.connMu.Lock()
61+
defer a.connMu.Unlock()
62+
return a.isConnected
63+
}
4364

4465
// dialTLS establece una conexión TLS al servidor remoto
4566
// Si tlsVerify es false, acepta cualquier certificado (útil para certificados autofirmados)
@@ -62,7 +83,7 @@ func dialTLS(address, port string, tlsVerify bool) (net.Conn, error) {
6283

6384
// Establecer conexión TLS con timeout
6485
dialer := &net.Dialer{
65-
Timeout: 10 * time.Second,
86+
Timeout: connectionTimeout,
6687
}
6788

6889
tlsConn, err := tls.DialWithDialer(dialer, "tcp", fullAddress, tlsConfig)
@@ -120,7 +141,7 @@ func dialConnection(address, port, protocol string, useTLS, tlsVerify bool) (net
120141
// net.JoinHostPort maneja correctamente IPv4 e IPv6
121142
fullAddress := net.JoinHostPort(address, port)
122143
dialer := &net.Dialer{
123-
Timeout: 10 * time.Second,
144+
Timeout: connectionTimeout,
124145
}
125146

126147
conn, err := dialer.Dial(protocol, fullAddress)
@@ -225,6 +246,11 @@ func (a *App) SendSyslogMessages(config SyslogConfig) SyslogResponse {
225246
return response
226247
}
227248

249+
// Emit start event
250+
runtime.EventsEmit(a.ctx, "syslog:sending", map[string]interface{}{
251+
"total": len(config.Messages),
252+
})
253+
228254
// net.JoinHostPort maneja correctamente IPv4 e IPv6
229255
fullAddress := net.JoinHostPort(config.Address, config.Port)
230256

@@ -245,14 +271,14 @@ func (a *App) SendSyslogMessages(config SyslogConfig) SyslogResponse {
245271

246272
// Enviar mensajes según el protocolo
247273
if config.Protocol == "tcp" {
248-
return sendTCPMessages(conn, config)
274+
return a.sendTCPMessages(conn, config)
249275
}
250-
return sendUDPMessages(conn, config)
276+
return a.sendUDPMessages(conn, config)
251277
}
252278

253279
// sendTCPMessages envía mensajes TCP con el framing adecuado según RFC 6587
254280
// Utiliza el módulo Framer para aplicar el método de framing configurado
255-
func sendTCPMessages(conn net.Conn, config SyslogConfig) SyslogResponse {
281+
func (a *App) sendTCPMessages(conn net.Conn, config SyslogConfig) SyslogResponse {
256282
response := SyslogResponse{
257283
SentMessages: []string{},
258284
Errors: []string{},
@@ -265,7 +291,8 @@ func sendTCPMessages(conn net.Conn, config SyslogConfig) SyslogResponse {
265291
MaxMessageLength: 0, // Sin límite (el servidor puede imponer límites)
266292
})
267293

268-
for _, message := range config.Messages {
294+
totalMessages := len(config.Messages)
295+
for i, message := range config.Messages {
269296
// Construir mensaje syslog según RFC 5424 o RFC 3164
270297
syslogMsg, err := buildSyslogMessage(config, message)
271298
if err != nil {
@@ -280,6 +307,11 @@ func sendTCPMessages(conn net.Conn, config SyslogConfig) SyslogResponse {
280307
continue
281308
}
282309

310+
// Set write deadline to prevent hanging
311+
if err := conn.SetWriteDeadline(time.Now().Add(writeTimeout)); err != nil {
312+
log.Printf("Warning: failed to set write deadline: %v", err)
313+
}
314+
283315
// Enviar con manejo robusto de escrituras parciales
284316
if err := writeAll(conn, framedMsg); err != nil {
285317
log.Printf("Error sending message: %v", err)
@@ -288,25 +320,44 @@ func sendTCPMessages(conn net.Conn, config SyslogConfig) SyslogResponse {
288320
log.Printf("Sent message: %s", syslogMsg)
289321
response.SentMessages = append(response.SentMessages, syslogMsg)
290322
}
323+
324+
// Emit progress event
325+
runtime.EventsEmit(a.ctx, "syslog:progress", map[string]interface{}{
326+
"current": i + 1,
327+
"total": totalMessages,
328+
"percent": float64(i+1) / float64(totalMessages) * 100,
329+
})
291330
}
292331

332+
// Emit completion event
333+
runtime.EventsEmit(a.ctx, "syslog:complete", map[string]interface{}{
334+
"sent": len(response.SentMessages),
335+
"errors": len(response.Errors),
336+
})
337+
293338
return response
294339
}
295340

296341
// sendUDPMessages envía mensajes UDP (un mensaje por paquete, sin framing)
297-
func sendUDPMessages(conn net.Conn, config SyslogConfig) SyslogResponse {
342+
func (a *App) sendUDPMessages(conn net.Conn, config SyslogConfig) SyslogResponse {
298343
response := SyslogResponse{
299344
SentMessages: []string{},
300345
Errors: []string{},
301346
}
302347

303-
for _, message := range config.Messages {
348+
totalMessages := len(config.Messages)
349+
for i, message := range config.Messages {
304350
syslogMsg, err := buildSyslogMessage(config, message)
305351
if err != nil {
306352
response.Errors = append(response.Errors, fmt.Sprintf("Error building message: %v", err))
307353
continue
308354
}
309355

356+
// Set write deadline to prevent hanging
357+
if err := conn.SetWriteDeadline(time.Now().Add(writeTimeout)); err != nil {
358+
log.Printf("Warning: failed to set write deadline: %v", err)
359+
}
360+
310361
// UDP no requiere framing, un mensaje por paquete
311362
if err := writeAll(conn, []byte(syslogMsg)); err != nil {
312363
log.Printf("Error sending message: %v", err)
@@ -315,8 +366,21 @@ func sendUDPMessages(conn net.Conn, config SyslogConfig) SyslogResponse {
315366
log.Printf("Sent message: %s", syslogMsg)
316367
response.SentMessages = append(response.SentMessages, syslogMsg)
317368
}
369+
370+
// Emit progress event
371+
runtime.EventsEmit(a.ctx, "syslog:progress", map[string]interface{}{
372+
"current": i + 1,
373+
"total": totalMessages,
374+
"percent": float64(i+1) / float64(totalMessages) * 100,
375+
})
318376
}
319377

378+
// Emit completion event
379+
runtime.EventsEmit(a.ctx, "syslog:complete", map[string]interface{}{
380+
"sent": len(response.SentMessages),
381+
"errors": len(response.Errors),
382+
})
383+
320384
return response
321385
}
322386

frontend/src/components/form.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,14 @@ import {
4141
} from "lucide-react";
4242

4343
const FormSchema = z.object({
44-
Address: z.string().ip({ version: "v4", message: "Invalid IP address" }),
45-
Port: z.string({ message: "Port is required" }),
44+
Address: z.string().ip({ message: "Invalid IP address (IPv4 or IPv6)" }),
45+
Port: z.string({ message: "Port is required" })
46+
.refine((val: string) => {
47+
const num = parseInt(val, 10);
48+
return !isNaN(num) && num >= 1 && num <= 65535;
49+
}, { message: "Port must be between 1-65535" }),
4650
Protocol: z.string({ message: "Please select a protocol" }),
47-
Messages: z.array(z.string({ message: "Messages cannot be empty" })),
51+
Messages: z.string().min(1, { message: "At least one message is required" }),
4852
FramingMethod: z.enum(["octet-counting", "non-transparent"], {
4953
message: "Please select a framing method",
5054
}),
@@ -64,7 +68,7 @@ export function InputForm() {
6468
Address: "",
6569
Port: "",
6670
Protocol: "",
67-
Messages: [],
71+
Messages: "",
6872
FramingMethod: "octet-counting",
6973
Facility: 16, // local0
7074
Severity: 6, // info
@@ -125,7 +129,7 @@ export function InputForm() {
125129
Address: data.Address,
126130
Port: data.Port,
127131
Protocol: data.Protocol,
128-
Messages: data.Messages[0].split("\n").filter((msg) => msg.trim() !== ""),
132+
Messages: data.Messages.split("\n").filter((msg: string) => msg.trim() !== ""),
129133
FramingMethod: data.FramingMethod,
130134
Facility: data.Facility,
131135
Severity: data.Severity,
@@ -358,7 +362,7 @@ export function InputForm() {
358362
<input
359363
type="checkbox"
360364
checked={field.value}
361-
onChange={(e) => {
365+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
362366
field.onChange(e.target.checked);
363367
// Auto-sugerir puerto 6514 cuando se activa TLS
364368
if (e.target.checked && form.getValues("Port") === "514") {
@@ -457,7 +461,7 @@ export function InputForm() {
457461
<FormItem>
458462
<FormLabel className="text-xs">Facility</FormLabel>
459463
<Select
460-
onValueChange={(value) => field.onChange(parseInt(value))}
464+
onValueChange={(value: string) => field.onChange(parseInt(value))}
461465
defaultValue={field.value?.toString()}
462466
>
463467
<FormControl>
@@ -489,7 +493,7 @@ export function InputForm() {
489493
<FormItem>
490494
<FormLabel className="text-xs">Severity</FormLabel>
491495
<Select
492-
onValueChange={(value) => field.onChange(parseInt(value))}
496+
onValueChange={(value: string) => field.onChange(parseInt(value))}
493497
defaultValue={field.value?.toString()}
494498
>
495499
<FormControl>
@@ -576,7 +580,6 @@ export function InputForm() {
576580
placeholder="Type your messages here, one per line..."
577581
className="resize-none h-[70px] text-sm"
578582
{...field}
579-
onChange={(e) => field.onChange([e.target.value])}
580583
/>
581584
</FormControl>
582585
<FormDescription className="text-[10px]">

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ require (
2828
github.com/tkrajina/go-reflector v0.5.8 // indirect
2929
github.com/valyala/bytebufferpool v1.0.0 // indirect
3030
github.com/valyala/fasttemplate v1.2.2 // indirect
31-
github.com/wailsapp/go-webview2 v1.0.19 // indirect
31+
github.com/wailsapp/go-webview2 v1.0.22 // indirect
3232
github.com/wailsapp/mimetype v1.4.1 // indirect
3333
golang.org/x/crypto v0.33.0 // indirect
3434
golang.org/x/net v0.35.0 // indirect

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
5353
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
5454
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
5555
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
56-
github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU=
57-
github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
56+
github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6NZijQ58=
57+
github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
5858
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
5959
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
60-
github.com/wailsapp/wails/v2 v2.10.2 h1:29U+c5PI4K4hbx8yFbFvwpCuvqK9VgNv8WGobIlKlXk=
61-
github.com/wailsapp/wails/v2 v2.10.2/go.mod h1:XuN4IUOPpzBrHUkEd7sCU5ln4T/p1wQedfxP7fKik+4=
60+
github.com/wailsapp/wails/v2 v2.11.0 h1:seLacV8pqupq32IjS4Y7V8ucab0WZwtK6VvUVxSBtqQ=
61+
github.com/wailsapp/wails/v2 v2.11.0/go.mod h1:jrf0ZaM6+GBc1wRmXsM8cIvzlg0karYin3erahI4+0k=
6262
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
6363
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
6464
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=

0 commit comments

Comments
 (0)