1
0
forked from baron/baron-sso

slog 통합

This commit is contained in:
2026-01-21 12:51:00 +09:00
parent 03c8c14aa1
commit c39857c66c
5 changed files with 127 additions and 121 deletions

View File

@@ -1,27 +1,19 @@
package main
import (
"baron-sso-backend/internal/handler"
"baron-sso-backend/internal/logger"
"baron-sso-backend/internal/repository"
"baron-sso-backend/internal/service"
"fmt"
"log/slog"
"os"
"strings"
"strconv"
"strings"
"time"
"github.com/bwmarrin/snowflake"
"baron-sso-backend/internal/handler"
"baron-sso-backend/internal/logger"
"baron-sso-backend/internal/repository"
"baron-sso-backend/internal/service"
"github.com/gofiber/fiber/v2"
"github.com/bwmarrin/snowflake"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/encryptcookie"
"github.com/gofiber/fiber/v2/middleware/recover"
@@ -41,7 +33,6 @@ func main() {
ServiceName: "baron-sso",
Environment: getEnv("GO_ENV", "dev"),
})
// Initialize Snowflake Node (Node 2 for Baron)
node, err := snowflake.NewNode(2)
if err != nil {
@@ -50,10 +41,22 @@ func main() {
}
// 1. Log Config on Startup
slog.Info("Starting Baron SSO Backend",
fmt.Println("============================================================")
fmt.Println(`
|\__/,| (\
_.|o o |_ ) )
-(((---(((--------
`)
fmt.Println("🚀 Baron SSO Backend Starting...")
slog.Info("Service starting",
"service", "baron-sso",
"app_env", getEnv("APP_ENV", "dev"),
"db_port", getEnv("DB_PORT", "5532"),
"backend_port", getEnv("BACKEND_PORT", "3000"),
"frontend_port", getEnv("FRONTEND_PORT", "5000"),
"frontend_url", getEnv("FRONTEND_URL", "http://ssologin.hmac.kr"),
"redis_addr", getEnv("REDIS_ADDR", "redis:6379"),
"descope_id", getEnv("DESCOPE_PROJECT_ID", "not-set"),
)
// 2. Initialize DB Connections
@@ -80,7 +83,7 @@ func main() {
// 3. Initialize Fiber
app := fiber.New(fiber.Config{
AppName: "Baron SSO Backend",
AppName: "Baron SSO Backend",
DisableStartupMessage: true, // Clean logs
})
@@ -94,10 +97,10 @@ func main() {
// [Standardized] HTTP Request Logger Middleware using slog
app.Use(func(c *fiber.Ctx) error {
start := time.Now()
// Handle request
err := c.Next()
// Log after request
latency := time.Since(start)
status := c.Response().StatusCode()
@@ -107,7 +110,7 @@ func main() {
if status < 400 {
return err
}
msg := "http_request"
if err != nil {
msg = "http_request_error"
@@ -129,7 +132,7 @@ func main() {
AllowOrigins: "*", // Adjust in production
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
AllowMethods: "GET, POST, HEAD, PUT, DELETE, PATCH, OPTIONS",
}))
}))
app.Use(encryptcookie.New(encryptcookie.Config{
Key: getEnv("COOKIE_SECRET", "secret-key-must-be-32-bytes-long!"),
}))
@@ -185,7 +188,7 @@ func main() {
// API Group
api := app.Group("/api/v1")
api.Post("/audit", auditHandler.CreateLog)
// Auth Proxy Routes
auth := api.Group("/auth")
auth.Post("/enchanted-link/init", authHandler.InitEnchantedLink)
@@ -196,7 +199,7 @@ func main() {
auth.Post("/qr/init", authHandler.InitQRLogin)
auth.Post("/qr/poll", authHandler.PollQRLogin)
auth.Post("/qr/approve", authHandler.ScanQRLogin)
// Admin Routes
admin := api.Group("/admin")
admin.Post("/users", adminHandler.CreateUser)
@@ -205,10 +208,10 @@ func main() {
admin.Patch("/users/:loginId", adminHandler.UpdateUser)
admin.Delete("/users/:loginId", adminHandler.DeleteUser)
admin.Patch("/users/:loginId/status", adminHandler.UpdateUserStatus)
// Webhook for Descope Generic SMS Gateway
auth.Post("/webhooks/descope-sms", authHandler.HandleDescopeSmsRelay)
// Webhook for Descope Generic Email Gateway (Fake Email Strategy)
auth.Post("/webhooks/descope-email", authHandler.HandleDescopeEmailRelay)
@@ -229,7 +232,7 @@ func main() {
slog.String("source", "client"),
}
for k, v := range req.Data {
// Skip svc if it's already set by the global logger to avoid confusion,
// Skip svc if it's already set by the global logger to avoid confusion,
// or keep it as client_svc
if k == "svc" {
attrs = append(attrs, slog.Any("client_svc", v))
@@ -252,9 +255,10 @@ func main() {
// Filter out noisy client navigation logs
if level == slog.LevelInfo {
msg := strings.ToLower(req.Message)
if strings.Contains(msg, "navigating to") ||
strings.Contains(msg, "going to") ||
strings.Contains(msg, "redirecting to") {
if strings.Contains(msg, "navigating to") ||
strings.Contains(msg, "going to") ||
strings.Contains(msg, "redirecting to") ||
strings.Contains(msg, "full paths for routes") {
return c.SendStatus(fiber.StatusOK)
}
}
@@ -264,8 +268,9 @@ func main() {
})
// Start Server
port := getEnv("PORT", "3000")
port := getEnv("BACKEND_PORT", "3000")
slog.Info("Server listening", "port", port)
fmt.Println("============================================================")
if err := app.Listen(":" + port); err != nil {
slog.Error("Server failed to start", "error", err)
os.Exit(1)

View File

@@ -2,7 +2,7 @@ package handler
import (
"context"
"log"
"log/slog"
"os"
"strings"
"net/url"
@@ -29,10 +29,10 @@ func NewAdminHandler() *AdminHandler {
ManagementKey: managementKey,
})
if err != nil {
log.Printf("Warning: Failed to initialize Descope Client for Admin: %v", err)
slog.Warn("Failed to initialize Descope Client for Admin", "error", err)
}
} else {
log.Println("Warning: DESCOPE_PROJECT_ID or DESCOPE_MANAGEMENT_KEY missing. Admin functions will fail.")
slog.Warn("DESCOPE_PROJECT_ID or DESCOPE_MANAGEMENT_KEY missing. Admin functions will fail.")
}
return &AdminHandler{
@@ -95,7 +95,7 @@ func (h *AdminHandler) ListUsers(c *fiber.Ctx) error {
}
if err != nil {
log.Printf("[Admin] ListUsers failed: %v", err)
slog.Error("[Admin] ListUsers failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
@@ -112,9 +112,9 @@ func (h *AdminHandler) DeleteUser(c *fiber.Ctx) error {
loginID = decoded
}
log.Printf("[Admin] Deleting user: %s", loginID)
slog.Info("[Admin] Deleting user", "loginID", loginID)
if err := h.DescopeClient.Management.User().Delete(context.Background(), loginID); err != nil {
log.Printf("[Admin] DeleteUser failed: %v", err)
slog.Error("[Admin] DeleteUser failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
@@ -140,7 +140,7 @@ func (h *AdminHandler) UpdateUserStatus(c *fiber.Ctx) error {
var user *descope.UserResponse
var err error
log.Printf("[Admin] Updating status for %s to %s", loginID, req.Status)
slog.Info("[Admin] Updating status", "loginID", loginID, "status", req.Status)
if req.Status == "enabled" || req.Status == "active" {
user, err = h.DescopeClient.Management.User().Activate(context.Background(), loginID)
@@ -149,7 +149,7 @@ func (h *AdminHandler) UpdateUserStatus(c *fiber.Ctx) error {
}
if err != nil {
log.Printf("[Admin] Status update failed: %v", err)
slog.Error("[Admin] Status update failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
@@ -266,11 +266,11 @@ func (h *AdminHandler) CreateUser(c *fiber.Ctx) error {
userObj.Tenants = userTenants
}
log.Printf("[Admin] Creating user: %s (Email: %s, Phone: %s)", req.LoginID, req.Email, normalizedPhone)
slog.Info("[Admin] Creating user", "loginID", req.LoginID, "email", req.Email, "phone", normalizedPhone)
res, err := h.DescopeClient.Management.User().Create(context.Background(), req.LoginID, userObj)
if err != nil {
log.Printf("[Admin] Failed to create user: %v", err)
slog.Error("[Admin] Failed to create user", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}

View File

@@ -8,7 +8,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"log"
"log/slog"
"math/rand"
"os"
"strings"
@@ -55,35 +55,34 @@ func NewAuthHandler(redisService *service.RedisService) *AuthHandler {
var descopeClient *client.DescopeClient
var err error
if projectID != "" {
descopeClient, err = client.NewWithConfig(&client.Config{
ProjectID: projectID,
ManagementKey: managementKey,
})
if err != nil {
log.Printf("Warning: Failed to initialize Descope Client: %v", err)
if projectID != "" {
descopeClient, err = client.NewWithConfig(&client.Config{
ProjectID: projectID,
ManagementKey: managementKey,
})
if err != nil {
slog.Warn("Failed to initialize Descope Client", "error", err)
}
}
return &AuthHandler{
ProjectID: projectID,
SmsService: service.NewSmsService(),
EmailService: service.NewEmailService(),
RedisService: redisService,
DescopeClient: descopeClient,
}
}
return &AuthHandler{
ProjectID: projectID,
SmsService: service.NewSmsService(),
EmailService: service.NewEmailService(),
RedisService: redisService,
DescopeClient: descopeClient,
}
}
// SendSms sends a verification code via SMS. (Restored for completeness)
func (h *AuthHandler) SendSms(c *fiber.Ctx) error {
var req domain.SmsRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
}
log.Printf("[SMS] Sending code to: %s", req.PhoneNumber)
sanitizedPhone := strings.ReplaceAll(req.PhoneNumber, "-", "")
// SendSms sends a verification code via SMS. (Restored for completeness)
func (h *AuthHandler) SendSms(c *fiber.Ctx) error {
var req domain.SmsRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
}
slog.Info("[SMS] Sending code", "phoneNumber", req.PhoneNumber)
sanitizedPhone := strings.ReplaceAll(req.PhoneNumber, "-", "")
rand.Seed(time.Now().UnixNano())
code := fmt.Sprintf("%06d", rand.Intn(1000000))
content := fmt.Sprintf("[Baron SSO] 인증번호: %s", code)
@@ -124,7 +123,7 @@ func (h *AuthHandler) VerifySms(c *fiber.Ctx) error {
func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
var req domain.EnchantedLinkInitRequest
if err := c.BodyParser(&req); err != nil {
log.Printf("[Enchanted] Body parse error: %v", err)
slog.Error("[Enchanted] Body parse error", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
}
@@ -135,7 +134,7 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
token := GenerateSecureToken(3)
pendingRef := GenerateSecureToken(3)
log.Printf("[Enchanted] Initiating for %s. Token: %s, PendingRef: %s", loginID, token, pendingRef)
slog.Info("[Enchanted] Initiating enchanted link", "loginID", loginID, "token", token, "pendingRef", pendingRef)
// Store in Redis
h.RedisService.Set(prefixSession+pendingRef, fmt.Sprintf(`{"status":"%s"}`, statusPending), defaultExpiration)
@@ -152,7 +151,7 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
if strings.Contains(loginID, "@") {
// Send Email
if h.EmailService == nil {
log.Printf("[Enchanted] Email Service not configured")
slog.Error("[Enchanted] Email Service not configured")
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Email service not configured"})
}
@@ -169,18 +168,18 @@ func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
</div>
`, link)
log.Printf("[Enchanted] Sending Email to %s via AWS SES", loginID)
slog.Info("[Enchanted] Sending Email via AWS SES", "loginID", loginID)
if err := h.EmailService.SendEmail(loginID, subject, body); err != nil {
log.Printf("[Enchanted] Email Failed: %v", err)
slog.Error("[Enchanted] Email Failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send Email"})
}
} else {
// Send SMS
content := fmt.Sprintf("[Baron SSO] 로그인 링크: %s", link)
log.Printf("[Enchanted] Sending SMS to %s via Naver Cloud", loginID)
slog.Info("[Enchanted] Sending SMS via Naver Cloud", "loginID", loginID)
if err := h.SmsService.SendSms(loginID, content); err != nil {
log.Printf("[Enchanted] SMS Failed: %v", err)
slog.Error("[Enchanted] SMS Failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send SMS"})
}
}
@@ -208,7 +207,7 @@ func (h *AuthHandler) PollEnchantedLink(c *fiber.Ctx) error {
json.Unmarshal([]byte(val), &data)
if data["status"] == statusSuccess {
log.Printf("[Poll] Success for ref: %s", req.PendingRef)
slog.Info("[Poll] Success", "pendingRef", req.PendingRef)
return c.JSON(fiber.Map{
"sessionJwt": data["jwt"],
"status": "ok",
@@ -222,16 +221,16 @@ func (h *AuthHandler) PollEnchantedLink(c *fiber.Ctx) error {
func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
var req domain.MagicLinkVerifyRequest
if err := c.BodyParser(&req); err != nil {
log.Printf("[Verify] Body parse error: %v", err)
slog.Error("[Verify] Body parse error", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
}
log.Printf("[Verify] Attempting to verify token: %s", req.Token)
slog.Info("[Verify] Attempting to verify token", "token", req.Token)
tokenKey := prefixToken + req.Token
val, err := h.RedisService.Get(tokenKey)
if err != nil || val == "" {
log.Printf("[Verify] Token not found or expired in Redis: %s", req.Token)
slog.Warn("[Verify] Token not found or expired in Redis", "token", req.Token)
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid or expired token"})
}
@@ -240,11 +239,11 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
pendingRef := tokenData["pendingRef"]
loginID := tokenData["loginId"]
log.Printf("[Verify] Token valid. LoginID: %s, PendingRef: %s", loginID, pendingRef)
slog.Info("[Verify] Token valid", "loginID", loginID, "pendingRef", pendingRef)
// 1. Generate Descope Session Directly (Management SDK)
if h.DescopeClient == nil {
log.Printf("[Verify] Descope Client is nil!")
slog.Error("[Verify] Descope Client is nil!")
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Descope Client not configured"})
}
@@ -260,7 +259,7 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
}
}
log.Printf("[Verify] Searching for user with phone: %s", searchPhone)
slog.Info("[Verify] Searching for user", "phone", searchPhone)
searchOptions := &descope.UserSearchOptions{
Phones: []string{searchPhone},
Limit: 1,
@@ -272,24 +271,24 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
if errSearch == nil && len(users) > 0 {
if len(users[0].LoginIDs) > 0 {
targetLoginID = users[0].LoginIDs[0]
log.Printf("[Verify] User found! Existing LoginID: %s", targetLoginID)
slog.Info("[Verify] User found", "existingLoginID", targetLoginID)
} else {
// Should not happen for a valid user, but fallback to UserID or searchPhone
log.Printf("[Verify] User found but no LoginIDs. Using UserID.")
slog.Warn("[Verify] User found but no LoginIDs, using UserID")
targetLoginID = users[0].UserID
}
} else {
// Not found, or search error. Fallback to using the phone as LoginID.
// Use the normalized phone number to ensure consistency (+82...)
targetLoginID = searchPhone
log.Printf("[Verify] User not found by phone. Will use/create: %s", targetLoginID)
slog.Info("[Verify] User not found by phone, will use/create", "loginID", targetLoginID)
}
log.Printf("[Verify] Generating embedded link for %s", targetLoginID)
slog.Info("[Verify] Generating embedded link", "loginID", targetLoginID)
embeddedToken, err := h.DescopeClient.Management.User().GenerateEmbeddedLink(context.Background(), targetLoginID, nil, 0)
if err != nil {
if strings.Contains(err.Error(), "User not found") || strings.Contains(err.Error(), "E062108") {
log.Printf("[Verify] User %s not found. Creating...", targetLoginID)
slog.Info("[Verify] User not found, creating...", "loginID", targetLoginID)
// Create User with Explicit Phone Attribute
userObj := &descope.UserRequest{}
@@ -301,30 +300,30 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
_, errCreate := h.DescopeClient.Management.User().Create(context.Background(), targetLoginID, userObj)
if errCreate != nil {
log.Printf("[Verify] Failed to create user: %v", errCreate)
slog.Error("[Verify] Failed to create user", "error", errCreate)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to create new user"})
}
embeddedToken, err = h.DescopeClient.Management.User().GenerateEmbeddedLink(context.Background(), targetLoginID, nil, 0)
if err != nil {
log.Printf("[Verify] Failed to generate token after creation: %v", err)
slog.Error("[Verify] Failed to generate token after creation", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to generate upstream token"})
}
} else {
log.Printf("[Verify] Descope Error: %v", err)
slog.Error("[Verify] Descope Error", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to generate upstream token"})
}
}
log.Printf("[Verify] Exchanging embedded token for session JWT")
slog.Info("[Verify] Exchanging embedded token for session JWT")
authInfo, err := h.DescopeClient.Auth.MagicLink().Verify(context.Background(), embeddedToken, nil)
if err != nil {
log.Printf("[Verify] Final verification failed: %v", err)
slog.Error("[Verify] Final verification failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to verify upstream token"})
}
sessionToken := authInfo.SessionToken.JWT
log.Printf("[Verify] Success! Updating Redis session: %s", pendingRef)
slog.Info("[Verify] Success! Updating Redis session", "pendingRef", pendingRef)
sessionData, _ := json.Marshal(map[string]string{
"status": statusSuccess,
"jwt": sessionToken,
@@ -348,7 +347,7 @@ func (h *AuthHandler) InitQRLogin(c *fiber.Ctx) error {
}
qrPayload := fmt.Sprintf("%s/approve?ref=%s", frontendURL, pendingRef)
log.Printf("[QR] Init: PendingRef=%s, URL=%s", pendingRef, qrPayload)
slog.Info("[QR] Init", "pendingRef", pendingRef, "url", qrPayload)
// Redis에 초기 상태 저장 (5분 만료)
h.RedisService.Set(prefixSession+pendingRef, fmt.Sprintf(`{"status":"%s"}`, statusPending), 5*time.Minute)
@@ -378,6 +377,7 @@ func (h *AuthHandler) PollQRLogin(c *fiber.Ctx) error {
json.Unmarshal([]byte(val), &data)
if data["status"] == statusSuccess {
slog.Info("[QR] Poll Success", "pendingRef", req.PendingRef)
return c.JSON(fiber.Map{
"status": "ok",
"sessionJwt": data["jwt"],
@@ -395,10 +395,11 @@ func (h *AuthHandler) ScanQRLogin(c *fiber.Ctx) error {
Token string `json:"token"` // 모바일 사용자의 세션 토큰 (검증용)
}
if err := c.BodyParser(&req); err != nil {
slog.Error("[QR] Scan body parse error", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid body"})
}
log.Printf("[QR] Scan & Approve: PendingRef=%s", req.PendingRef)
slog.Info("[QR] Scan & Approve", "pendingRef", req.PendingRef)
// 1. Redis에서 세션 확인
val, err := h.RedisService.Get(prefixSession + req.PendingRef)
@@ -430,16 +431,16 @@ func (h *AuthHandler) HandleDescopeSmsRelay(c *fiber.Ctx) error {
}
if err := c.BodyParser(&req); err != nil {
log.Printf("[Webhook] Body parsing failed: %v", err)
slog.Error("Webhook Body parsing failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
}
if req.Recipient == "" || req.Body == "" {
log.Printf("[Webhook] Missing recipient or body")
slog.Warn("Webhook missing recipient or body")
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Missing recipient or body"})
}
log.Printf("[Webhook] Received SMS request for %s", req.Recipient)
slog.Info("Received SMS request", "recipient", req.Recipient)
phone := req.Recipient
if strings.HasPrefix(phone, "+82") {
@@ -449,11 +450,11 @@ func (h *AuthHandler) HandleDescopeSmsRelay(c *fiber.Ctx) error {
phone = strings.ReplaceAll(phone, " ", "")
if err := h.SmsService.SendSms(phone, req.Body); err != nil {
log.Printf("[Webhook] Failed to forward SMS to Naver: %v", err)
slog.Error("Failed to forward SMS to Naver", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send SMS via Naver"})
}
log.Printf("[Webhook] Successfully forwarded SMS to %s", phone)
slog.Info("Successfully forwarded SMS", "phone", phone)
return c.JSON(fiber.Map{"status": "ok"})
}
@@ -467,11 +468,11 @@ func (h *AuthHandler) HandleDescopeEmailRelay(c *fiber.Ctx) error {
}
if err := c.BodyParser(&req); err != nil {
log.Printf("[Email Webhook] Body parsing failed: %v", err)
slog.Error("[Email Webhook] Body parsing failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
}
log.Printf("[Email Webhook] Received email request for %s", req.To)
slog.Info("[Email Webhook] Received email request", "to", req.To)
// Check if it's a Fake Email for SMS
if strings.HasSuffix(req.To, "@sms.baron") {
@@ -484,16 +485,16 @@ func (h *AuthHandler) HandleDescopeEmailRelay(c *fiber.Ctx) error {
// Send SMS with the text body (Descope template should be optimized for SMS)
if err := h.SmsService.SendSms(phone, req.Text); err != nil {
log.Printf("[Email Webhook] Failed to forward Email-as-SMS: %v", err)
slog.Error("[Email Webhook] Failed to forward Email-as-SMS", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send SMS"})
}
log.Printf("[Email Webhook] Successfully converted Email to SMS for %s", phone)
slog.Info("[Email Webhook] Successfully converted Email to SMS", "phone", phone)
return c.JSON(fiber.Map{"status": "ok"})
}
// Real Email Handling (Not implemented in this Relay)
// You would need an SMTP service here if you route ALL emails through this relay.
log.Printf("[Email Webhook] Real email skipped (Not implemented): %s", req.To)
slog.Warn("[Email Webhook] Real email skipped (Not implemented)", "to", req.To)
return c.Status(501).JSON(fiber.Map{"error": "Real email sending not implemented"})
}

View File

@@ -3,7 +3,7 @@ package service
import (
"context"
"fmt"
"log"
"log/slog"
"os"
"baron-sso-backend/internal/domain"
@@ -26,16 +26,15 @@ func NewEmailService() domain.EmailService {
sender := os.Getenv("AWS_SES_SENDER")
if region == "" || accessKey == "" || secretKey == "" {
log.Println("[EmailService] AWS configuration missing, email service will not work")
slog.Warn("[EmailService] AWS configuration missing, email service will not work")
return nil
}
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion(region),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
)
if err != nil {
log.Printf("[EmailService] Failed to load AWS config: %v", err)
slog.Error("Failed to load AWS config", "error", err)
return nil
}
@@ -71,9 +70,9 @@ func (s *SesServiceImpl) SendEmail(to, subject, body string) error {
_, err := s.client.SendEmail(context.TODO(), input)
if err != nil {
log.Printf("[EmailService] Failed to send email to %s: %v", to, err)
slog.Error("[EmailService] Failed to send email", "to", to, "error", err)
} else {
log.Printf("[EmailService] Email sent successfully to %s", to)
slog.Info("[EmailService] Email sent successfully", "to", to)
}
return err
}

View File

@@ -8,7 +8,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"log/slog"
"net/http"
"os"
"strconv"
@@ -17,6 +17,7 @@ import (
"baron-sso-backend/internal/domain"
)
type SmsServiceImpl struct {
accessKey string
secretKey string
@@ -28,7 +29,7 @@ func NewSmsService() domain.SmsService {
// Sanitize sender phone number right after reading from env
rawSenderPhone := os.Getenv("NAVER_SENDER_PHONE_NUMBER")
sanitizedSenderPhone := strings.ReplaceAll(rawSenderPhone, "-", "")
log.Printf("[서비스 초기화] 발신자 번호 처리: 원본='%s', 정제 후='%s'", rawSenderPhone, sanitizedSenderPhone)
slog.Info("[서비스 초기화] 발신자 번호 처리", "원본", rawSenderPhone, "정제후", sanitizedSenderPhone)
return &SmsServiceImpl{
accessKey: os.Getenv("NAVER_CLOUD_ACCESS_KEY"),
@@ -41,7 +42,7 @@ func NewSmsService() domain.SmsService {
func (s *SmsServiceImpl) SendSms(to, content string) error {
timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
apiURL := fmt.Sprintf("https://sens.apigw.ntruss.com/sms/v2/services/%s/messages", s.serviceID)
log.Printf("Requesting SENS API URL: %s", apiURL)
slog.Info("[SmsService] Requesting SENS API URL", "url", apiURL)
// Naver SENS API requires phone number without '+'
sanitizedTo := strings.Replace(to, "+", "", 1)
@@ -92,11 +93,11 @@ func (s *SmsServiceImpl) SendSms(to, content string) error {
}
if resp.StatusCode >= 300 {
log.Printf("error response from naver cloud sms api: %s", string(respBody))
slog.Error("[SmsService] error response from naver cloud sms api", "body", string(respBody))
return fmt.Errorf("error sending sms: status code %d", resp.StatusCode)
}
log.Printf("sms sent successfully: %s", string(respBody))
slog.Info("[SmsService] sms sent successfully", "body", string(respBody))
return nil
}
@@ -112,4 +113,4 @@ func (s *SmsServiceImpl) makeSignature(method, url, timestamp string) (string, e
}
return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
}
}