mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-06-03 20:13:01 +00:00
initiall commit
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HealthService provides health check functionality
|
||||
type HealthService struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewHealthService creates a new health service
|
||||
func NewHealthService(db *sql.DB) *HealthService {
|
||||
return &HealthService{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// HealthStatus represents the health status of the application
|
||||
type HealthStatus struct {
|
||||
Status string `json:"status"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Version string `json:"version"`
|
||||
Checks map[string]CheckResult `json:"checks"`
|
||||
}
|
||||
|
||||
// CheckResult represents the result of a health check
|
||||
type CheckResult struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Latency time.Duration `json:"latency_ms"`
|
||||
}
|
||||
|
||||
// Check performs all health checks
|
||||
func (hs *HealthService) Check(ctx context.Context, version string) HealthStatus {
|
||||
status := HealthStatus{
|
||||
Status: "healthy",
|
||||
Timestamp: time.Now(),
|
||||
Version: version,
|
||||
Checks: make(map[string]CheckResult),
|
||||
}
|
||||
|
||||
// Database check
|
||||
dbCheck := hs.checkDatabase(ctx)
|
||||
status.Checks["database"] = dbCheck
|
||||
if dbCheck.Status != "healthy" {
|
||||
status.Status = "unhealthy"
|
||||
}
|
||||
|
||||
// Add more checks as needed
|
||||
status.Checks["api"] = CheckResult{
|
||||
Status: "healthy",
|
||||
Message: "API is responding",
|
||||
Latency: 0,
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
// checkDatabase checks database connectivity
|
||||
func (hs *HealthService) checkDatabase(ctx context.Context) CheckResult {
|
||||
start := time.Now()
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := hs.db.PingContext(ctx)
|
||||
latency := time.Since(start)
|
||||
|
||||
if err != nil {
|
||||
return CheckResult{
|
||||
Status: "unhealthy",
|
||||
Message: err.Error(),
|
||||
Latency: latency,
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{
|
||||
Status: "healthy",
|
||||
Message: "Database connection successful",
|
||||
Latency: latency,
|
||||
}
|
||||
}
|
||||
|
||||
// Readiness checks if the service is ready to accept traffic
|
||||
func (hs *HealthService) Readiness(ctx context.Context) bool {
|
||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := hs.db.PingContext(ctx)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Liveness checks if the service is alive
|
||||
func (hs *HealthService) Liveness(ctx context.Context) bool {
|
||||
// Simple check - if we can execute this, we're alive
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
"github.com/resend/resend-go/v2"
|
||||
|
||||
"github.com/tdvorak/primora/apps/backend/internal/config"
|
||||
)
|
||||
|
||||
type Mailer struct {
|
||||
cfg config.Config
|
||||
resend *resend.Client
|
||||
}
|
||||
|
||||
func NewMailer(cfg config.Config) *Mailer {
|
||||
var resendClient *resend.Client
|
||||
if cfg.ResendAPIKey != "" {
|
||||
resendClient = resend.NewClient(cfg.ResendAPIKey)
|
||||
}
|
||||
return &Mailer{cfg: cfg, resend: resendClient}
|
||||
}
|
||||
|
||||
func (m *Mailer) SendInvitation(ctx context.Context, toEmail, organizationName, inviteURL string) error {
|
||||
subject := fmt.Sprintf("You were invited to %s on Primora", organizationName)
|
||||
text := "You have been invited to Primora.\n\nOpen this link to accept the invitation:\n" + inviteURL + "\n"
|
||||
if m.resend != nil {
|
||||
_, err := m.resend.Emails.SendWithContext(ctx, &resend.SendEmailRequest{
|
||||
From: m.cfg.MailFrom,
|
||||
To: []string{toEmail},
|
||||
Subject: subject,
|
||||
Text: text,
|
||||
})
|
||||
return err
|
||||
}
|
||||
address := fmt.Sprintf("%s:%d", m.cfg.SMTPHost, m.cfg.SMTPPort)
|
||||
message := strings.Join([]string{
|
||||
"From: " + m.cfg.MailFrom,
|
||||
"To: " + toEmail,
|
||||
"Subject: " + subject,
|
||||
"MIME-Version: 1.0",
|
||||
"Content-Type: text/plain; charset=utf-8",
|
||||
"",
|
||||
text,
|
||||
}, "\r\n")
|
||||
var auth smtp.Auth
|
||||
if m.cfg.SMTPUser != "" {
|
||||
auth = smtp.PlainAuth("", m.cfg.SMTPUser, m.cfg.SMTPPassword, m.cfg.SMTPHost)
|
||||
}
|
||||
return smtp.SendMail(address, auth, extractEmail(m.cfg.MailFrom), []string{toEmail}, []byte(message))
|
||||
}
|
||||
|
||||
func extractEmail(input string) string {
|
||||
if start := strings.Index(input, "<"); start >= 0 {
|
||||
if end := strings.Index(input, ">"); end > start {
|
||||
return input[start+1 : end]
|
||||
}
|
||||
}
|
||||
return input
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user