feat(vcard): agregar login con email+password para obtener token automáticamente

- Nuevo endpoint POST /app/vcard-api/login que llama a /api/login del sistema VCard externo
- Modal de configuración con campos email+password y botón 'Obtener Token'
- El token se auto-rellena en el campo Bearer Token al conectar exitosamente
- Se mantiene la opción de pegar el token manualmente como fallback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Lizandro Guarnizo
2026-05-19 20:44:40 -05:00
co-authored by Copilot
parent 31259cbb71
commit 45032dae3f
3 changed files with 92 additions and 1 deletions
+39
View File
@@ -23,6 +23,45 @@ func VcardApiIndex(c *fiber.Ctx) error {
return c.Render("vcard_api", data, "layouts/main")
}
// VcardApiLogin llama a /api/login del sistema VCard externo y devuelve el access_token.
func VcardApiLogin(c *fiber.Ctx) error {
type Req struct {
BaseURL string `json:"base_url"`
Email string `json:"email"`
Password string `json:"password"`
}
var req Req
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
}
req.BaseURL = strings.TrimRight(strings.TrimSpace(req.BaseURL), "/")
if req.BaseURL == "" || req.Email == "" || req.Password == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "base_url, email y password son requeridos"})
}
payload, _ := json.Marshal(map[string]string{"email": req.Email, "password": req.Password})
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Post(req.BaseURL+"/api/login", "application/json", strings.NewReader(string(payload)))
if err != nil {
return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": "No se pudo conectar: " + err.Error()})
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": "Respuesta inválida del servidor"})
}
if token, ok := result["access_token"].(string); ok && token != "" {
return c.JSON(fiber.Map{"ok": true, "token": token})
}
msg := "Credenciales incorrectas"
if m, ok := result["message"].(string); ok && m != "" {
msg = m
}
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": msg})
}
// VcardApiSaveConfig guarda / actualiza la configuración (base URL + token).
func VcardApiSaveConfig(c *fiber.Ctx) error {
type Req struct {