up
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
// AiConfigIndex renderiza la vista del panel de configuraciones de IA.
|
||||
func AiConfigIndex(c *fiber.Ctx) error {
|
||||
data := fiber.Map{
|
||||
"user": c.Locals("user").(map[string]interface{}),
|
||||
"modules": c.Locals("userModules"),
|
||||
}
|
||||
return c.Render("ai_config", data, "layouts/main")
|
||||
}
|
||||
|
||||
// GetAiConfigs devuelve la lista paginada en JSON.
|
||||
func GetAiConfigs(c *fiber.Ctx) error {
|
||||
page, _ := strconv.Atoi(c.Query("page", "1"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
limit := 20
|
||||
offset := (page - 1) * limit
|
||||
search := c.Query("search", "")
|
||||
|
||||
items, total, err := models.GetAllAiConfigs(limit, offset, search)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
// Ocultar la API key en el listado (mostrar solo últimos 4 chars)
|
||||
type safe struct {
|
||||
ID uint `json:"ID"`
|
||||
Nombre string `json:"nombre"`
|
||||
Provider string `json:"provider"`
|
||||
ApiKeyHint string `json:"api_key_hint"`
|
||||
BaseURL string `json:"base_url"`
|
||||
ModelName string `json:"model_name"`
|
||||
IsActive bool `json:"is_active"`
|
||||
Notes string `json:"notes"`
|
||||
}
|
||||
safeItems := make([]safe, len(items))
|
||||
for i, it := range items {
|
||||
hint := "••••"
|
||||
if len(it.ApiKey) > 4 {
|
||||
hint = "••••" + it.ApiKey[len(it.ApiKey)-4:]
|
||||
}
|
||||
safeItems[i] = safe{
|
||||
ID: it.ID,
|
||||
Nombre: it.Nombre,
|
||||
Provider: it.Provider,
|
||||
ApiKeyHint: hint,
|
||||
BaseURL: it.BaseURL,
|
||||
ModelName: it.ModelName,
|
||||
IsActive: it.IsActive,
|
||||
Notes: it.Notes,
|
||||
}
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"items": safeItems,
|
||||
"total": total,
|
||||
"totalPages": int(math.Ceil(float64(total) / float64(limit))),
|
||||
"page": page,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateAiConfigHandler crea una nueva configuración de IA.
|
||||
func CreateAiConfigHandler(c *fiber.Ctx) error {
|
||||
type Req struct {
|
||||
Nombre string `json:"nombre"`
|
||||
Provider string `json:"provider"`
|
||||
ApiKey string `json:"api_key"`
|
||||
BaseURL string `json:"base_url"`
|
||||
ModelName string `json:"model_name"`
|
||||
IsActive bool `json:"is_active"`
|
||||
Notes string `json:"notes"`
|
||||
}
|
||||
var req Req
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
|
||||
}
|
||||
if strings.TrimSpace(req.Nombre) == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "nombre es requerido"})
|
||||
}
|
||||
if strings.TrimSpace(req.Provider) == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "provider es requerido"})
|
||||
}
|
||||
if strings.TrimSpace(req.ApiKey) == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "api_key es requerido"})
|
||||
}
|
||||
|
||||
item := models.AiConfig{
|
||||
Nombre: strings.TrimSpace(req.Nombre),
|
||||
Provider: strings.ToLower(strings.TrimSpace(req.Provider)),
|
||||
ApiKey: strings.TrimSpace(req.ApiKey),
|
||||
BaseURL: strings.TrimSpace(req.BaseURL),
|
||||
ModelName: strings.TrimSpace(req.ModelName),
|
||||
IsActive: req.IsActive,
|
||||
Notes: req.Notes,
|
||||
}
|
||||
if err := models.CreateAiConfig(&item); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"ok": true, "id": item.ID})
|
||||
}
|
||||
|
||||
// UpdateAiConfigHandler actualiza una configuración de IA existente.
|
||||
func UpdateAiConfigHandler(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
|
||||
}
|
||||
|
||||
type Req struct {
|
||||
Nombre string `json:"nombre"`
|
||||
Provider string `json:"provider"`
|
||||
ApiKey string `json:"api_key"` // vacío = no cambiar
|
||||
BaseURL string `json:"base_url"`
|
||||
ModelName string `json:"model_name"`
|
||||
IsActive bool `json:"is_active"`
|
||||
Notes string `json:"notes"`
|
||||
}
|
||||
var req Req
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
|
||||
}
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"nombre": strings.TrimSpace(req.Nombre),
|
||||
"provider": strings.ToLower(strings.TrimSpace(req.Provider)),
|
||||
"base_url": strings.TrimSpace(req.BaseURL),
|
||||
"model_name": strings.TrimSpace(req.ModelName),
|
||||
"is_active": req.IsActive,
|
||||
"notes": req.Notes,
|
||||
}
|
||||
if strings.TrimSpace(req.ApiKey) != "" {
|
||||
updates["api_key"] = strings.TrimSpace(req.ApiKey)
|
||||
}
|
||||
|
||||
if err := models.UpdateAiConfig(uint(id), updates); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"ok": true})
|
||||
}
|
||||
|
||||
// DeleteAiConfigHandler elimina una configuración de IA.
|
||||
func DeleteAiConfigHandler(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
|
||||
}
|
||||
if err := models.DeleteAiConfig(uint(id)); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"ok": true})
|
||||
}
|
||||
@@ -357,3 +357,27 @@ func LandingAdminList(c *fiber.Ctx) error {
|
||||
"page": page,
|
||||
})
|
||||
}
|
||||
|
||||
// ─── GET /landing/ai-config ───────────────────────────────────────────────────
|
||||
|
||||
// LandingGetAiConfig devuelve la configuración de IA activa para el Landing Generator.
|
||||
// Protegido por X-Landing-Secret.
|
||||
func LandingGetAiConfig(c *fiber.Ctx) error {
|
||||
if !landingSecret(c) {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "no autorizado"})
|
||||
}
|
||||
config, err := models.GetActiveAiConfig("qwen")
|
||||
if err != nil {
|
||||
// Intentar cualquier provider activo como fallback
|
||||
config, err = models.GetActiveAiConfig("")
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "no hay configuración de IA activa"})
|
||||
}
|
||||
}
|
||||
return c.JSON(fiber.Map{
|
||||
"provider": config.Provider,
|
||||
"api_key": config.ApiKey,
|
||||
"base_url": config.BaseURL,
|
||||
"model_name": config.ModelName,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -74,4 +74,6 @@ func RutasPublicas(web fiber.Router) {
|
||||
web.Post("/landing/payment", apiControllers.LandingCreatePayment)
|
||||
web.Get("/landing/answers/:token", apiControllers.LandingGetAnswers)
|
||||
web.Get("/landing/download/:token", apiControllers.LandingDownload)
|
||||
// Config de IA activa (para Landing Generator)
|
||||
web.Get("/landing/ai-config", apiControllers.LandingGetAiConfig)
|
||||
}
|
||||
|
||||
@@ -277,6 +277,13 @@ func UserRoutes(app fiber.Router) {
|
||||
protected.Get("/saas-api/logs", middlewares.MenuMiddleware, controllers.SaasDispatchLogIndex)
|
||||
protected.Get("/loadsaasdispatchlogs", controllers.GetSaasDispatchLogs)
|
||||
|
||||
// ─── Configuraciones de IA (Qwen, OpenAI, etc.) ─────────────────────────
|
||||
protected.Get("/ai-config", middlewares.MenuMiddleware, controllers.AiConfigIndex)
|
||||
protected.Get("/ai-config/list", controllers.GetAiConfigs)
|
||||
protected.Post("/ai-config", controllers.CreateAiConfigHandler)
|
||||
protected.Put("/ai-config/:id", controllers.UpdateAiConfigHandler)
|
||||
protected.Delete("/ai-config/:id", controllers.DeleteAiConfigHandler)
|
||||
|
||||
// ─── Alibaba Cloud OSS API ────────────────────────────────────────────────
|
||||
protected.Get("/oss-api", middlewares.MenuMiddleware, controllers.OssApiIndex)
|
||||
protected.Get("/loadossapi", controllers.GetOssApiConfigs)
|
||||
|
||||
Reference in New Issue
Block a user