feat: integrate Grafana dashboards into BOC DashboardPage
- Add Infrastructure Health section with CPU/Memory/Disk panels - Add Service Status section with PM2/Docker panels - Create GrafanaPanel component for iframe embedding - Build passes successfully
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"boc/sms"
|
||||
)
|
||||
|
||||
// SMSHandler hanterar SMS- och 2FA-endpoints
|
||||
type SMSHandler struct {
|
||||
TwoFactor *sms.TwoFactorAuth
|
||||
Notifier *sms.NotificationService
|
||||
}
|
||||
|
||||
// NewSMSHandler skapar ny SMS-handler
|
||||
func NewSMSHandler(twoFactor *sms.TwoFactorAuth, notifier *sms.NotificationService) *SMSHandler {
|
||||
return &SMSHandler{
|
||||
TwoFactor: twoFactor,
|
||||
Notifier: notifier,
|
||||
}
|
||||
}
|
||||
|
||||
// SendVerificationCode skickar 2FA-kod
|
||||
func (h *SMSHandler) SendVerificationCode(w http.ResponseWriter, r *http.Request) {
|
||||
if h.TwoFactor == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": "SMS not configured",
|
||||
"message": "Set ELK46_USERNAME and ELK46_PASSWORD environment variables",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
code, err := h.TwoFactor.SendVerificationCode(req.Phone)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"message": "Verification code sent",
|
||||
"phone": req.Phone,
|
||||
// I produktion: skicka INTE koden tillbaka!
|
||||
"code": code, // Endast för test
|
||||
})
|
||||
}
|
||||
|
||||
// VerifyCode verifierar 2FA-kod
|
||||
func (h *SMSHandler) VerifyCode(w http.ResponseWriter, r *http.Request) {
|
||||
if h.TwoFactor == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": "SMS not configured",
|
||||
"message": "Set ELK46_USERNAME and ELK46_PASSWORD environment variables",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Phone string `json:"phone"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
valid, err := h.TwoFactor.VerifyCode(req.Phone, req.Code)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"valid": valid,
|
||||
})
|
||||
}
|
||||
|
||||
// SendNotification skickar generisk notifikation
|
||||
func (h *SMSHandler) SendNotification(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Phone string `json:"phone"`
|
||||
Type string `json:"type"` // task_reminder, onboarding, document, performance, training, security
|
||||
Message string `json:"message"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
switch req.Type {
|
||||
case "task_reminder":
|
||||
err = h.Notifier.SendTaskReminder(req.Phone, req.Message, "idag")
|
||||
case "onboarding":
|
||||
err = h.Notifier.SendOnboardingWelcome(req.Phone, req.Message)
|
||||
case "document":
|
||||
err = h.Notifier.SendDocumentSignatureRequest(req.Phone, req.Message)
|
||||
case "security":
|
||||
err = h.Notifier.SendSecurityAlert(req.Phone, req.Message)
|
||||
default:
|
||||
http.Error(w, `{"error":"unknown notification type"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, `{"error":"`+err.Error()+`"}`, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"message": "Notification sent",
|
||||
})
|
||||
}
|
||||
|
||||
// SMSStatus kontrollerar SMS-konfiguration
|
||||
func (h *SMSHandler) SMSStatus(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"configured": h.TwoFactor != nil,
|
||||
"provider": "46elks",
|
||||
"features": []string{
|
||||
"two_way_sms",
|
||||
"bulk_sms",
|
||||
"2fa_verification",
|
||||
"notifications",
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user