forked from baron/baron-sso
slog 통합
This commit is contained in:
@@ -1,27 +1,19 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"baron-sso-backend/internal/handler"
|
||||||
|
"baron-sso-backend/internal/logger"
|
||||||
|
"baron-sso-backend/internal/repository"
|
||||||
|
"baron-sso-backend/internal/service"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bwmarrin/snowflake"
|
"github.com/bwmarrin/snowflake"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
"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/gofiber/fiber/v2/middleware/cors"
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
"github.com/gofiber/fiber/v2/middleware/encryptcookie"
|
"github.com/gofiber/fiber/v2/middleware/encryptcookie"
|
||||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||||
@@ -41,7 +33,6 @@ func main() {
|
|||||||
ServiceName: "baron-sso",
|
ServiceName: "baron-sso",
|
||||||
Environment: getEnv("GO_ENV", "dev"),
|
Environment: getEnv("GO_ENV", "dev"),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Initialize Snowflake Node (Node 2 for Baron)
|
// Initialize Snowflake Node (Node 2 for Baron)
|
||||||
node, err := snowflake.NewNode(2)
|
node, err := snowflake.NewNode(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -50,10 +41,22 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. Log Config on Startup
|
// 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"),
|
"frontend_url", getEnv("FRONTEND_URL", "http://ssologin.hmac.kr"),
|
||||||
"redis_addr", getEnv("REDIS_ADDR", "redis:6379"),
|
"redis_addr", getEnv("REDIS_ADDR", "redis:6379"),
|
||||||
"descope_id", getEnv("DESCOPE_PROJECT_ID", "not-set"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 2. Initialize DB Connections
|
// 2. Initialize DB Connections
|
||||||
@@ -80,7 +83,7 @@ func main() {
|
|||||||
|
|
||||||
// 3. Initialize Fiber
|
// 3. Initialize Fiber
|
||||||
app := fiber.New(fiber.Config{
|
app := fiber.New(fiber.Config{
|
||||||
AppName: "Baron SSO Backend",
|
AppName: "Baron SSO Backend",
|
||||||
DisableStartupMessage: true, // Clean logs
|
DisableStartupMessage: true, // Clean logs
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,10 +97,10 @@ func main() {
|
|||||||
// [Standardized] HTTP Request Logger Middleware using slog
|
// [Standardized] HTTP Request Logger Middleware using slog
|
||||||
app.Use(func(c *fiber.Ctx) error {
|
app.Use(func(c *fiber.Ctx) error {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
// Handle request
|
// Handle request
|
||||||
err := c.Next()
|
err := c.Next()
|
||||||
|
|
||||||
// Log after request
|
// Log after request
|
||||||
latency := time.Since(start)
|
latency := time.Since(start)
|
||||||
status := c.Response().StatusCode()
|
status := c.Response().StatusCode()
|
||||||
@@ -107,7 +110,7 @@ func main() {
|
|||||||
if status < 400 {
|
if status < 400 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := "http_request"
|
msg := "http_request"
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg = "http_request_error"
|
msg = "http_request_error"
|
||||||
@@ -129,7 +132,7 @@ func main() {
|
|||||||
AllowOrigins: "*", // Adjust in production
|
AllowOrigins: "*", // Adjust in production
|
||||||
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
|
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
|
||||||
AllowMethods: "GET, POST, HEAD, PUT, DELETE, PATCH, OPTIONS",
|
AllowMethods: "GET, POST, HEAD, PUT, DELETE, PATCH, OPTIONS",
|
||||||
}))
|
}))
|
||||||
app.Use(encryptcookie.New(encryptcookie.Config{
|
app.Use(encryptcookie.New(encryptcookie.Config{
|
||||||
Key: getEnv("COOKIE_SECRET", "secret-key-must-be-32-bytes-long!"),
|
Key: getEnv("COOKIE_SECRET", "secret-key-must-be-32-bytes-long!"),
|
||||||
}))
|
}))
|
||||||
@@ -185,7 +188,7 @@ func main() {
|
|||||||
// API Group
|
// API Group
|
||||||
api := app.Group("/api/v1")
|
api := app.Group("/api/v1")
|
||||||
api.Post("/audit", auditHandler.CreateLog)
|
api.Post("/audit", auditHandler.CreateLog)
|
||||||
|
|
||||||
// Auth Proxy Routes
|
// Auth Proxy Routes
|
||||||
auth := api.Group("/auth")
|
auth := api.Group("/auth")
|
||||||
auth.Post("/enchanted-link/init", authHandler.InitEnchantedLink)
|
auth.Post("/enchanted-link/init", authHandler.InitEnchantedLink)
|
||||||
@@ -196,7 +199,7 @@ func main() {
|
|||||||
auth.Post("/qr/init", authHandler.InitQRLogin)
|
auth.Post("/qr/init", authHandler.InitQRLogin)
|
||||||
auth.Post("/qr/poll", authHandler.PollQRLogin)
|
auth.Post("/qr/poll", authHandler.PollQRLogin)
|
||||||
auth.Post("/qr/approve", authHandler.ScanQRLogin)
|
auth.Post("/qr/approve", authHandler.ScanQRLogin)
|
||||||
|
|
||||||
// Admin Routes
|
// Admin Routes
|
||||||
admin := api.Group("/admin")
|
admin := api.Group("/admin")
|
||||||
admin.Post("/users", adminHandler.CreateUser)
|
admin.Post("/users", adminHandler.CreateUser)
|
||||||
@@ -205,10 +208,10 @@ func main() {
|
|||||||
admin.Patch("/users/:loginId", adminHandler.UpdateUser)
|
admin.Patch("/users/:loginId", adminHandler.UpdateUser)
|
||||||
admin.Delete("/users/:loginId", adminHandler.DeleteUser)
|
admin.Delete("/users/:loginId", adminHandler.DeleteUser)
|
||||||
admin.Patch("/users/:loginId/status", adminHandler.UpdateUserStatus)
|
admin.Patch("/users/:loginId/status", adminHandler.UpdateUserStatus)
|
||||||
|
|
||||||
// Webhook for Descope Generic SMS Gateway
|
// Webhook for Descope Generic SMS Gateway
|
||||||
auth.Post("/webhooks/descope-sms", authHandler.HandleDescopeSmsRelay)
|
auth.Post("/webhooks/descope-sms", authHandler.HandleDescopeSmsRelay)
|
||||||
|
|
||||||
// Webhook for Descope Generic Email Gateway (Fake Email Strategy)
|
// Webhook for Descope Generic Email Gateway (Fake Email Strategy)
|
||||||
auth.Post("/webhooks/descope-email", authHandler.HandleDescopeEmailRelay)
|
auth.Post("/webhooks/descope-email", authHandler.HandleDescopeEmailRelay)
|
||||||
|
|
||||||
@@ -229,7 +232,7 @@ func main() {
|
|||||||
slog.String("source", "client"),
|
slog.String("source", "client"),
|
||||||
}
|
}
|
||||||
for k, v := range req.Data {
|
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
|
// or keep it as client_svc
|
||||||
if k == "svc" {
|
if k == "svc" {
|
||||||
attrs = append(attrs, slog.Any("client_svc", v))
|
attrs = append(attrs, slog.Any("client_svc", v))
|
||||||
@@ -252,9 +255,10 @@ func main() {
|
|||||||
// Filter out noisy client navigation logs
|
// Filter out noisy client navigation logs
|
||||||
if level == slog.LevelInfo {
|
if level == slog.LevelInfo {
|
||||||
msg := strings.ToLower(req.Message)
|
msg := strings.ToLower(req.Message)
|
||||||
if strings.Contains(msg, "navigating to") ||
|
if strings.Contains(msg, "navigating to") ||
|
||||||
strings.Contains(msg, "going to") ||
|
strings.Contains(msg, "going to") ||
|
||||||
strings.Contains(msg, "redirecting to") {
|
strings.Contains(msg, "redirecting to") ||
|
||||||
|
strings.Contains(msg, "full paths for routes") {
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,8 +268,9 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start Server
|
// Start Server
|
||||||
port := getEnv("PORT", "3000")
|
port := getEnv("BACKEND_PORT", "3000")
|
||||||
slog.Info("Server listening", "port", port)
|
slog.Info("Server listening", "port", port)
|
||||||
|
fmt.Println("============================================================")
|
||||||
if err := app.Listen(":" + port); err != nil {
|
if err := app.Listen(":" + port); err != nil {
|
||||||
slog.Error("Server failed to start", "error", err)
|
slog.Error("Server failed to start", "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -29,10 +29,10 @@ func NewAdminHandler() *AdminHandler {
|
|||||||
ManagementKey: managementKey,
|
ManagementKey: managementKey,
|
||||||
})
|
})
|
||||||
if err != nil {
|
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 {
|
} 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{
|
return &AdminHandler{
|
||||||
@@ -95,7 +95,7 @@ func (h *AdminHandler) ListUsers(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
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()})
|
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
|
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 {
|
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()})
|
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 user *descope.UserResponse
|
||||||
var err error
|
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" {
|
if req.Status == "enabled" || req.Status == "active" {
|
||||||
user, err = h.DescopeClient.Management.User().Activate(context.Background(), loginID)
|
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 {
|
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()})
|
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
|
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)
|
res, err := h.DescopeClient.Management.User().Create(context.Background(), req.LoginID, userObj)
|
||||||
if err != nil {
|
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()})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log/slog"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -55,35 +55,34 @@ func NewAuthHandler(redisService *service.RedisService) *AuthHandler {
|
|||||||
|
|
||||||
var descopeClient *client.DescopeClient
|
var descopeClient *client.DescopeClient
|
||||||
var err error
|
var err error
|
||||||
if projectID != "" {
|
if projectID != "" {
|
||||||
descopeClient, err = client.NewWithConfig(&client.Config{
|
descopeClient, err = client.NewWithConfig(&client.Config{
|
||||||
ProjectID: projectID,
|
ProjectID: projectID,
|
||||||
ManagementKey: managementKey,
|
ManagementKey: managementKey,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Warning: Failed to initialize Descope Client: %v", err)
|
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{
|
// SendSms sends a verification code via SMS. (Restored for completeness)
|
||||||
ProjectID: projectID,
|
func (h *AuthHandler) SendSms(c *fiber.Ctx) error {
|
||||||
SmsService: service.NewSmsService(),
|
var req domain.SmsRequest
|
||||||
EmailService: service.NewEmailService(),
|
if err := c.BodyParser(&req); err != nil {
|
||||||
RedisService: redisService,
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||||
DescopeClient: descopeClient,
|
}
|
||||||
}
|
|
||||||
}
|
slog.Info("[SMS] Sending code", "phoneNumber", 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"})
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("[SMS] Sending code to: %s", req.PhoneNumber)
|
|
||||||
sanitizedPhone := strings.ReplaceAll(req.PhoneNumber, "-", "")
|
|
||||||
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
code := fmt.Sprintf("%06d", rand.Intn(1000000))
|
code := fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||||
content := fmt.Sprintf("[Baron SSO] 인증번호: %s", code)
|
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 {
|
func (h *AuthHandler) InitEnchantedLink(c *fiber.Ctx) error {
|
||||||
var req domain.EnchantedLinkInitRequest
|
var req domain.EnchantedLinkInitRequest
|
||||||
if err := c.BodyParser(&req); err != nil {
|
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"})
|
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)
|
token := GenerateSecureToken(3)
|
||||||
pendingRef := 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
|
// Store in Redis
|
||||||
h.RedisService.Set(prefixSession+pendingRef, fmt.Sprintf(`{"status":"%s"}`, statusPending), defaultExpiration)
|
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, "@") {
|
if strings.Contains(loginID, "@") {
|
||||||
// Send Email
|
// Send Email
|
||||||
if h.EmailService == nil {
|
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"})
|
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>
|
</div>
|
||||||
`, link)
|
`, 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 {
|
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"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send Email"})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Send SMS
|
// Send SMS
|
||||||
content := fmt.Sprintf("[Baron SSO] 로그인 링크: %s", link)
|
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 {
|
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"})
|
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)
|
json.Unmarshal([]byte(val), &data)
|
||||||
|
|
||||||
if data["status"] == statusSuccess {
|
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{
|
return c.JSON(fiber.Map{
|
||||||
"sessionJwt": data["jwt"],
|
"sessionJwt": data["jwt"],
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
@@ -222,16 +221,16 @@ func (h *AuthHandler) PollEnchantedLink(c *fiber.Ctx) error {
|
|||||||
func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
||||||
var req domain.MagicLinkVerifyRequest
|
var req domain.MagicLinkVerifyRequest
|
||||||
if err := c.BodyParser(&req); err != nil {
|
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"})
|
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
|
tokenKey := prefixToken + req.Token
|
||||||
val, err := h.RedisService.Get(tokenKey)
|
val, err := h.RedisService.Get(tokenKey)
|
||||||
if err != nil || val == "" {
|
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"})
|
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"]
|
pendingRef := tokenData["pendingRef"]
|
||||||
loginID := tokenData["loginId"]
|
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)
|
// 1. Generate Descope Session Directly (Management SDK)
|
||||||
if h.DescopeClient == nil {
|
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"})
|
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{
|
searchOptions := &descope.UserSearchOptions{
|
||||||
Phones: []string{searchPhone},
|
Phones: []string{searchPhone},
|
||||||
Limit: 1,
|
Limit: 1,
|
||||||
@@ -272,24 +271,24 @@ func (h *AuthHandler) VerifyMagicLink(c *fiber.Ctx) error {
|
|||||||
if errSearch == nil && len(users) > 0 {
|
if errSearch == nil && len(users) > 0 {
|
||||||
if len(users[0].LoginIDs) > 0 {
|
if len(users[0].LoginIDs) > 0 {
|
||||||
targetLoginID = 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 {
|
} else {
|
||||||
// Should not happen for a valid user, but fallback to UserID or searchPhone
|
// 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
|
targetLoginID = users[0].UserID
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Not found, or search error. Fallback to using the phone as LoginID.
|
// Not found, or search error. Fallback to using the phone as LoginID.
|
||||||
// Use the normalized phone number to ensure consistency (+82...)
|
// Use the normalized phone number to ensure consistency (+82...)
|
||||||
targetLoginID = searchPhone
|
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)
|
embeddedToken, err := h.DescopeClient.Management.User().GenerateEmbeddedLink(context.Background(), targetLoginID, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "User not found") || strings.Contains(err.Error(), "E062108") {
|
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
|
// Create User with Explicit Phone Attribute
|
||||||
userObj := &descope.UserRequest{}
|
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)
|
_, errCreate := h.DescopeClient.Management.User().Create(context.Background(), targetLoginID, userObj)
|
||||||
if errCreate != nil {
|
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"})
|
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)
|
embeddedToken, err = h.DescopeClient.Management.User().GenerateEmbeddedLink(context.Background(), targetLoginID, nil, 0)
|
||||||
if err != nil {
|
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"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to generate upstream token"})
|
||||||
}
|
}
|
||||||
} else {
|
} 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"})
|
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)
|
authInfo, err := h.DescopeClient.Auth.MagicLink().Verify(context.Background(), embeddedToken, nil)
|
||||||
if err != 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"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to verify upstream token"})
|
||||||
}
|
}
|
||||||
sessionToken := authInfo.SessionToken.JWT
|
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{
|
sessionData, _ := json.Marshal(map[string]string{
|
||||||
"status": statusSuccess,
|
"status": statusSuccess,
|
||||||
"jwt": sessionToken,
|
"jwt": sessionToken,
|
||||||
@@ -348,7 +347,7 @@ func (h *AuthHandler) InitQRLogin(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
qrPayload := fmt.Sprintf("%s/approve?ref=%s", frontendURL, pendingRef)
|
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분 만료)
|
// Redis에 초기 상태 저장 (5분 만료)
|
||||||
h.RedisService.Set(prefixSession+pendingRef, fmt.Sprintf(`{"status":"%s"}`, statusPending), 5*time.Minute)
|
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)
|
json.Unmarshal([]byte(val), &data)
|
||||||
|
|
||||||
if data["status"] == statusSuccess {
|
if data["status"] == statusSuccess {
|
||||||
|
slog.Info("[QR] Poll Success", "pendingRef", req.PendingRef)
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"sessionJwt": data["jwt"],
|
"sessionJwt": data["jwt"],
|
||||||
@@ -395,10 +395,11 @@ func (h *AuthHandler) ScanQRLogin(c *fiber.Ctx) error {
|
|||||||
Token string `json:"token"` // 모바일 사용자의 세션 토큰 (검증용)
|
Token string `json:"token"` // 모바일 사용자의 세션 토큰 (검증용)
|
||||||
}
|
}
|
||||||
if err := c.BodyParser(&req); err != nil {
|
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"})
|
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에서 세션 확인
|
// 1. Redis에서 세션 확인
|
||||||
val, err := h.RedisService.Get(prefixSession + req.PendingRef)
|
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 {
|
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"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Recipient == "" || req.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"})
|
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
|
phone := req.Recipient
|
||||||
if strings.HasPrefix(phone, "+82") {
|
if strings.HasPrefix(phone, "+82") {
|
||||||
@@ -449,11 +450,11 @@ func (h *AuthHandler) HandleDescopeSmsRelay(c *fiber.Ctx) error {
|
|||||||
phone = strings.ReplaceAll(phone, " ", "")
|
phone = strings.ReplaceAll(phone, " ", "")
|
||||||
|
|
||||||
if err := h.SmsService.SendSms(phone, req.Body); err != nil {
|
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"})
|
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"})
|
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 {
|
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"})
|
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
|
// Check if it's a Fake Email for SMS
|
||||||
if strings.HasSuffix(req.To, "@sms.baron") {
|
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)
|
// Send SMS with the text body (Descope template should be optimized for SMS)
|
||||||
if err := h.SmsService.SendSms(phone, req.Text); err != nil {
|
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"})
|
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"})
|
return c.JSON(fiber.Map{"status": "ok"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Real Email Handling (Not implemented in this Relay)
|
// Real Email Handling (Not implemented in this Relay)
|
||||||
// You would need an SMTP service here if you route ALL emails through 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"})
|
return c.Status(501).JSON(fiber.Map{"error": "Real email sending not implemented"})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"baron-sso-backend/internal/domain"
|
"baron-sso-backend/internal/domain"
|
||||||
@@ -26,16 +26,15 @@ func NewEmailService() domain.EmailService {
|
|||||||
sender := os.Getenv("AWS_SES_SENDER")
|
sender := os.Getenv("AWS_SES_SENDER")
|
||||||
|
|
||||||
if region == "" || accessKey == "" || secretKey == "" {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.LoadDefaultConfig(context.TODO(),
|
cfg, err := config.LoadDefaultConfig(context.TODO(),
|
||||||
config.WithRegion(region),
|
config.WithRegion(region),
|
||||||
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
|
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[EmailService] Failed to load AWS config: %v", err)
|
slog.Error("Failed to load AWS config", "error", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,9 +70,9 @@ func (s *SesServiceImpl) SendEmail(to, subject, body string) error {
|
|||||||
|
|
||||||
_, err := s.client.SendEmail(context.TODO(), input)
|
_, err := s.client.SendEmail(context.TODO(), input)
|
||||||
if err != nil {
|
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 {
|
} else {
|
||||||
log.Printf("[EmailService] Email sent successfully to %s", to)
|
slog.Info("[EmailService] Email sent successfully", "to", to)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
|
|
||||||
"baron-sso-backend/internal/domain"
|
"baron-sso-backend/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SmsServiceImpl struct {
|
type SmsServiceImpl struct {
|
||||||
accessKey string
|
accessKey string
|
||||||
secretKey string
|
secretKey string
|
||||||
@@ -28,7 +29,7 @@ func NewSmsService() domain.SmsService {
|
|||||||
// Sanitize sender phone number right after reading from env
|
// Sanitize sender phone number right after reading from env
|
||||||
rawSenderPhone := os.Getenv("NAVER_SENDER_PHONE_NUMBER")
|
rawSenderPhone := os.Getenv("NAVER_SENDER_PHONE_NUMBER")
|
||||||
sanitizedSenderPhone := strings.ReplaceAll(rawSenderPhone, "-", "")
|
sanitizedSenderPhone := strings.ReplaceAll(rawSenderPhone, "-", "")
|
||||||
log.Printf("[서비스 초기화] 발신자 번호 처리: 원본='%s', 정제 후='%s'", rawSenderPhone, sanitizedSenderPhone)
|
slog.Info("[서비스 초기화] 발신자 번호 처리", "원본", rawSenderPhone, "정제후", sanitizedSenderPhone)
|
||||||
|
|
||||||
return &SmsServiceImpl{
|
return &SmsServiceImpl{
|
||||||
accessKey: os.Getenv("NAVER_CLOUD_ACCESS_KEY"),
|
accessKey: os.Getenv("NAVER_CLOUD_ACCESS_KEY"),
|
||||||
@@ -41,7 +42,7 @@ func NewSmsService() domain.SmsService {
|
|||||||
func (s *SmsServiceImpl) SendSms(to, content string) error {
|
func (s *SmsServiceImpl) SendSms(to, content string) error {
|
||||||
timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
|
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)
|
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 '+'
|
// Naver SENS API requires phone number without '+'
|
||||||
sanitizedTo := strings.Replace(to, "+", "", 1)
|
sanitizedTo := strings.Replace(to, "+", "", 1)
|
||||||
@@ -92,11 +93,11 @@ func (s *SmsServiceImpl) SendSms(to, content string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode >= 300 {
|
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)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,4 +113,4 @@ func (s *SmsServiceImpl) makeSignature(method, url, timestamp string) (string, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
|
return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user