diff --git a/data/cv-en.json b/data/cv-en.json
index b81f988..31ba804 100644
--- a/data/cv-en.json
+++ b/data/cv-en.json
@@ -736,6 +736,23 @@
],
"courseID": "codecademy-certifications"
},
+ {
+ "title": "Udemy Certifications",
+ "institution": "Udemy",
+ "courseLogo": "udemy.png",
+ "location": "Online",
+ "date": "2024-2025",
+ "duration": "Various",
+ "shortDescription": "Professional development courses in Go programming and modern web technologies through Udemy's comprehensive learning platform.",
+ "responsibilities": [
+ "
Go - The Complete Guide 2024: Comprehensive Go programming course covering fundamentals, concurrency, testing, and building production-ready applications
",
+ "Building a Module in Go 2024: Deep dive into Go modules, dependency management, versioning, and creating reusable packages
",
+ "",
+ "",
+ "HTMX - The Practical Guide 2024: Modern web development with HTMX, building dynamic web applications with minimal JavaScript using hypermedia patterns
"
+ ],
+ "courseID": "udemy-certifications"
+ },
{
"title": "LinkedIn Learning Certifications",
"institution": "LinkedIn Learning",
diff --git a/data/cv-es.json b/data/cv-es.json
index 6555c1a..00d6e70 100644
--- a/data/cv-es.json
+++ b/data/cv-es.json
@@ -741,6 +741,23 @@
],
"courseID": "certificaciones-codecademy"
},
+ {
+ "title": "Certificaciones Udemy",
+ "institution": "Udemy",
+ "courseLogo": "udemy.png",
+ "location": "Online",
+ "date": "2024-2025",
+ "duration": "Varios",
+ "shortDescription": "Cursos de desarrollo profesional en programación Go y tecnologías web modernas a través de la plataforma de aprendizaje integral de Udemy.",
+ "responsibilities": [
+ "Go - The Complete Guide 2024: Curso completo de programación Go cubriendo fundamentos, concurrencia, testing y construcción de aplicaciones listas para producción
",
+ "Building a Module in Go 2024: Profundización en módulos Go, gestión de dependencias, versionado y creación de paquetes reutilizables
",
+ "",
+ "",
+ "HTMX - The Practical Guide 2024: Desarrollo web moderno con HTMX, construyendo aplicaciones web dinámicas con JavaScript mínimo usando patrones hypermedia
"
+ ],
+ "courseID": "certificaciones-udemy"
+ },
{
"title": "Certificaciones LinkedIn Learning",
"institution": "LinkedIn Learning",
diff --git a/data/ui-en.json b/data/ui-en.json
index d8e4c02..230dd71 100644
--- a/data/ui-en.json
+++ b/data/ui-en.json
@@ -36,7 +36,12 @@
},
"footer": {
"viewOnGithub": "View this project on GitHub",
- "lastUpdated": "Last updated"
+ "lastUpdated": "Last updated",
+ "linkedin": "linkedin_",
+ "github": "github_",
+ "domestika": "domestika_",
+ "email": "email@",
+ "phone": "phone#"
},
"portfolio": {
"seeAllProjects": "See all projects on my",
diff --git a/data/ui-es.json b/data/ui-es.json
index 17fda97..8625a3e 100644
--- a/data/ui-es.json
+++ b/data/ui-es.json
@@ -36,7 +36,12 @@
},
"footer": {
"viewOnGithub": "Ver este proyecto en GitHub",
- "lastUpdated": "Última actualización"
+ "lastUpdated": "Última actualización",
+ "linkedin": "linkedin_",
+ "github": "github_",
+ "domestika": "domestika_",
+ "email": "email@",
+ "phone": "teléfono#"
},
"portfolio": {
"seeAllProjects": "Ver todos los proyectos en mi",
diff --git a/internal/handlers/cv_text.go b/internal/handlers/cv_text.go
index 9cb46f5..f38b0bf 100644
--- a/internal/handlers/cv_text.go
+++ b/internal/handlers/cv_text.go
@@ -44,11 +44,7 @@ func isTextBrowser(r *http.Request) bool {
// Check Accept header - if client prefers text/plain
accept := r.Header.Get("Accept")
- if strings.HasPrefix(accept, "text/plain") {
- return true
- }
-
- return false
+ return strings.HasPrefix(accept, "text/plain")
}
// ==============================================================================
@@ -72,10 +68,7 @@ func (h *CVHandler) PlainText(w http.ResponseWriter, r *http.Request) {
}
// Check icons parameter (default: true)
- showIcons := true
- if r.URL.Query().Get("icons") == "false" {
- showIcons = false
- }
+ showIcons := r.URL.Query().Get("icons") != "false"
// Prepare template data using shared helper (loads CV data)
data, err := h.prepareTemplateData(langCode)
diff --git a/internal/middleware/security_logger.go b/internal/middleware/security_logger.go
index ccab2fd..67b6053 100644
--- a/internal/middleware/security_logger.go
+++ b/internal/middleware/security_logger.go
@@ -132,7 +132,11 @@ func logToSecurityFile(eventJSON []byte) {
log.Printf("WARNING: Failed to open security log file: %v", err)
return
}
- defer f.Close()
+ defer func() {
+ if err := f.Close(); err != nil {
+ log.Printf("WARNING: Failed to close security log file: %v", err)
+ }
+ }()
// Write event with newline
if _, err := f.Write(eventJSON); err != nil {
diff --git a/internal/models/ui/ui.go b/internal/models/ui/ui.go
index 223e09e..0b646b4 100644
--- a/internal/models/ui/ui.go
+++ b/internal/models/ui/ui.go
@@ -102,6 +102,11 @@ type Sections struct {
type Footer struct {
ViewOnGithub string `json:"viewOnGithub"`
LastUpdated string `json:"lastUpdated"`
+ Linkedin string `json:"linkedin"`
+ Github string `json:"github"`
+ Domestika string `json:"domestika"`
+ Email string `json:"email"`
+ Phone string `json:"phone"`
}
// Portfolio labels
diff --git a/internal/services/email.go b/internal/services/email.go
index 9397b4a..f8d3a0e 100644
--- a/internal/services/email.go
+++ b/internal/services/email.go
@@ -224,74 +224,7 @@ func (e *EmailService) sendMultipartEmail(subject, htmlBody, textBody, replyTo s
if err != nil {
return fmt.Errorf("failed to connect to SMTP server: %w", err)
}
- defer client.Close()
-
- // Authenticate
- if err = client.Auth(auth); err != nil {
- return fmt.Errorf("SMTP authentication failed: %w", err)
- }
-
- // Set sender and recipient
- if err = client.Mail(from); err != nil {
- return fmt.Errorf("failed to set sender: %w", err)
- }
- if err = client.Rcpt(to); err != nil {
- return fmt.Errorf("failed to set recipient: %w", err)
- }
-
- // Send message
- w, err := client.Data()
- if err != nil {
- return fmt.Errorf("failed to get data writer: %w", err)
- }
-
- _, err = w.Write([]byte(message))
- if err != nil {
- return fmt.Errorf("failed to write message: %w", err)
- }
-
- err = w.Close()
- if err != nil {
- return fmt.Errorf("failed to close writer: %w", err)
- }
-
- return client.Quit()
-}
-
-// sendEmail sends an email using SMTP (plain text only - legacy)
-func (e *EmailService) sendEmail(subject, body string) error {
- // Validate config
- if e.config.SMTPHost == "" || e.config.SMTPPort == "" {
- return fmt.Errorf("SMTP configuration incomplete")
- }
- if e.config.SMTPUser == "" || e.config.SMTPPassword == "" {
- return fmt.Errorf("SMTP credentials missing")
- }
- if e.config.ToEmail == "" {
- return fmt.Errorf("recipient email not configured")
- }
-
- // Build email message
- from := e.config.FromEmail
- if from == "" {
- from = e.config.SMTPUser
- }
-
- to := e.config.ToEmail
- message := e.formatMessage(from, to, subject, body)
-
- // SMTP server address
- addr := fmt.Sprintf("%s:%s", e.config.SMTPHost, e.config.SMTPPort)
-
- // Setup authentication
- auth := smtp.PlainAuth("", e.config.SMTPUser, e.config.SMTPPassword, e.config.SMTPHost)
-
- // Connect to SMTP server with TLS
- client, err := e.connectSMTP(addr)
- if err != nil {
- return fmt.Errorf("failed to connect to SMTP server: %w", err)
- }
- defer client.Close()
+ defer func() { _ = client.Close() }()
// Authenticate
if err = client.Auth(auth); err != nil {
@@ -342,7 +275,7 @@ func (e *EmailService) connectSMTP(addr string) (*smtp.Client, error) {
}
client, err := smtp.NewClient(conn, e.config.SMTPHost)
if err != nil {
- conn.Close()
+ _ = conn.Close()
return nil, fmt.Errorf("SMTP client creation failed: %w", err)
}
return client, nil
@@ -411,23 +344,3 @@ func (e *EmailService) formatMultipartMessage(from, to, replyTo, subject, htmlBo
return message.String()
}
-
-// formatMessage formats an email message with proper headers (plain text only)
-func (e *EmailService) formatMessage(from, to, subject, body string) string {
- headers := make(map[string]string)
- headers["From"] = from
- headers["To"] = to
- headers["Subject"] = subject
- headers["MIME-Version"] = "1.0"
- headers["Content-Type"] = "text/plain; charset=\"utf-8\""
- headers["Date"] = time.Now().Format(time.RFC1123Z)
-
- var message strings.Builder
- for k, v := range headers {
- message.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
- }
- message.WriteString("\r\n")
- message.WriteString(body)
-
- return message.String()
-}
diff --git a/static/pdf/udemy/Building GUI Applications with Fyne and Go.pdf b/static/pdf/udemy/Building GUI Applications with Fyne and Go.pdf
new file mode 100644
index 0000000..c5eed05
Binary files /dev/null and b/static/pdf/udemy/Building GUI Applications with Fyne and Go.pdf differ
diff --git a/static/pdf/udemy/Building a module in Go.pdf b/static/pdf/udemy/Building a module in Go.pdf
new file mode 100644
index 0000000..f20d94b
Binary files /dev/null and b/static/pdf/udemy/Building a module in Go.pdf differ
diff --git a/static/pdf/udemy/Go - The Complete Guide.pdf b/static/pdf/udemy/Go - The Complete Guide.pdf
new file mode 100644
index 0000000..1dc41ee
Binary files /dev/null and b/static/pdf/udemy/Go - The Complete Guide.pdf differ
diff --git a/static/pdf/udemy/HTMX - The Practical Guide.pdf b/static/pdf/udemy/HTMX - The Practical Guide.pdf
new file mode 100644
index 0000000..83e199d
Binary files /dev/null and b/static/pdf/udemy/HTMX - The Practical Guide.pdf differ
diff --git a/static/pdf/udemy/Up and Running with Concurrency in Go.pdf b/static/pdf/udemy/Up and Running with Concurrency in Go.pdf
new file mode 100644
index 0000000..5549aca
Binary files /dev/null and b/static/pdf/udemy/Up and Running with Concurrency in Go.pdf differ
diff --git a/templates/partials/cv/cv-footer.html b/templates/partials/cv/cv-footer.html
index e63d4c4..50646ae 100644
--- a/templates/partials/cv/cv-footer.html
+++ b/templates/partials/cv/cv-footer.html
@@ -5,38 +5,38 @@
diff --git a/tests/integration/email_test.go b/tests/integration/email_test.go
index 7dca28b..2d424c7 100644
--- a/tests/integration/email_test.go
+++ b/tests/integration/email_test.go
@@ -39,7 +39,7 @@ func TestSMTPConnection(t *testing.T) {
if err != nil {
t.Fatalf("TLS dial failed: %v", err)
}
- defer conn.Close()
+ defer func() { _ = conn.Close() }()
t.Log("TLS connection established (port 465 - implicit SSL)")
} else {
// STARTTLS
@@ -47,7 +47,7 @@ func TestSMTPConnection(t *testing.T) {
if err != nil {
t.Fatalf("SMTP dial failed: %v", err)
}
- defer client.Close()
+ defer func() { _ = client.Close() }()
if err := client.StartTLS(tlsConfig); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
@@ -71,7 +71,7 @@ func TestSMTPConnection(t *testing.T) {
if err != nil {
t.Fatalf("TLS dial failed: %v", err)
}
- defer conn.Close()
+ defer func() { _ = conn.Close() }()
client, err = smtp.NewClient(conn, host)
if err != nil {
@@ -88,7 +88,7 @@ func TestSMTPConnection(t *testing.T) {
t.Fatalf("STARTTLS failed: %v", err)
}
}
- defer client.Close()
+ defer func() { _ = client.Close() }()
auth := smtp.PlainAuth("", user, pass, host)
if err := client.Auth(auth); err != nil {