feat: sesión 1 año + validación sintaxis plantilla en tiempo real
This commit is contained in:
+3
-3
@@ -48,14 +48,14 @@ mail:
|
||||
token:
|
||||
app_jwt_secret: SECRET_APP
|
||||
api_jwt_secret: SECRET_API
|
||||
expires_in: 3600
|
||||
expires_in: 31536000
|
||||
jwt:
|
||||
app:
|
||||
secret: SECRET_APP
|
||||
expire: 3600
|
||||
expire: 31536000
|
||||
api:
|
||||
secret: SECRET_API
|
||||
expire: 3600
|
||||
expire: 31536000
|
||||
|
||||
template:
|
||||
path: "resources/view"
|
||||
|
||||
+3
-3
@@ -53,14 +53,14 @@ mail:
|
||||
token:
|
||||
app_jwt_secret: SECRET_APP
|
||||
api_jwt_secret: SECRET_API
|
||||
expires_in: 3600
|
||||
expires_in: 31536000
|
||||
jwt:
|
||||
app:
|
||||
secret: SECRET_APP
|
||||
expire: 3600
|
||||
expire: 31536000
|
||||
api:
|
||||
secret: SECRET_API
|
||||
expire: 3600
|
||||
expire: 31536000
|
||||
|
||||
template:
|
||||
path: "resources/view"
|
||||
|
||||
+3
-1
@@ -28,7 +28,9 @@ func (s *SessionConfig) Setup() error {
|
||||
}
|
||||
|
||||
// Configurar el proveedor de sesiones en memoria
|
||||
store := session.New() // Usar el proveedor de sesiones en memoria
|
||||
store := session.New(session.Config{
|
||||
Expiration: 365 * 24 * time.Hour, // 1 año
|
||||
}) // Usar el proveedor de sesiones en memoria
|
||||
|
||||
// Crear una nueva sesión
|
||||
s.Session = store
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ func (t *Token) CreateToken(c *fiber.Ctx, userID uint, secret string, expire ...
|
||||
if len(expire) > 0 {
|
||||
t.Expire = expire[0]
|
||||
} else {
|
||||
t.Expire = 3600
|
||||
t.Expire = 31536000 // 1 año por defecto si no se configura
|
||||
}
|
||||
expiresIn := time.Now().Add(time.Duration(t.Expire) * time.Second).Unix()
|
||||
claims["exp"] = expiresIn
|
||||
|
||||
@@ -110,7 +110,9 @@
|
||||
<label class="text-xs font-medium text-gray-600 block mb-1">HTML del correo *</label>
|
||||
<textarea x-model="form.cuerpo_html" rows="16"
|
||||
@input.debounce.600ms="previewInline()"
|
||||
:class="templateError ? 'border-red-400' : ''"
|
||||
class="w-full border rounded px-3 py-2 text-xs font-mono" required></textarea>
|
||||
<p x-show="templateError" x-text="templateError" class="text-red-500 text-xs mt-1"></p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs font-medium text-gray-600 block mb-1">Vista previa</label>
|
||||
@@ -177,7 +179,7 @@ document.addEventListener('alpine:init', () => {
|
||||
loading: false,
|
||||
datos: [], total: 0, totalPages: 1, page: 1, limit: 10, search: '',
|
||||
addModal: false, editModal: false, deleteModal: false, previewModal: false, testModal: false,
|
||||
selectedId: null, testEmail: '',
|
||||
selectedId: null, testEmail: '', templateError: '',
|
||||
form: { nombre:'', asunto:'', cuerpo_html:'', tipo:'renovacion' },
|
||||
toast: { show:false, msg:'', type:'ok' },
|
||||
|
||||
@@ -211,18 +213,25 @@ document.addEventListener('alpine:init', () => {
|
||||
});
|
||||
},
|
||||
|
||||
previewInline() {
|
||||
async previewInline() {
|
||||
const frame = this.$refs.previewFrame;
|
||||
if (frame) frame.srcdoc = this.form.cuerpo_html || '<p style="color:#999;padding:1rem">Sin contenido</p>';
|
||||
if (!this.form.cuerpo_html) { this.templateError = ''; return; }
|
||||
try {
|
||||
const { data } = await axios.post('/app/api/plantillas-correo/validate', { html: this.form.cuerpo_html });
|
||||
this.templateError = data.ok ? '' : (data.error || 'Sintaxis inválida');
|
||||
} catch { this.templateError = ''; }
|
||||
},
|
||||
|
||||
closeModals() {
|
||||
this.addModal = this.editModal = this.deleteModal = this.previewModal = this.testModal = false;
|
||||
this.selectedId = null;
|
||||
this.templateError = '';
|
||||
this.form = { nombre:'', asunto:'', cuerpo_html:'', tipo:'renovacion' };
|
||||
},
|
||||
|
||||
async save() {
|
||||
if (this.templateError) { this.showToast('Corrige los errores de sintaxis antes de guardar', 'error'); return; }
|
||||
this.loading = true;
|
||||
try {
|
||||
if (this.editModal) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"html/template"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
@@ -101,6 +102,24 @@ func PreviewPlantilla(c *fiber.Ctx) error {
|
||||
return c.JSON(fiber.Map{"html": html, "ok": true})
|
||||
}
|
||||
|
||||
// ValidarPlantilla comprueba si el CuerpoHTML es un Go template válido
|
||||
func ValidarPlantilla(c *fiber.Ctx) error {
|
||||
var body struct {
|
||||
HTML string `json:"html"`
|
||||
}
|
||||
if err := c.BodyParser(&body); err != nil {
|
||||
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
html := strings.TrimSpace(body.HTML)
|
||||
if html == "" {
|
||||
return c.JSON(fiber.Map{"ok": true})
|
||||
}
|
||||
if _, err := template.New("validate").Parse(html); err != nil {
|
||||
return c.Status(400).JSON(fiber.Map{"ok": false, "error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"ok": true})
|
||||
}
|
||||
|
||||
func TestEnvioPlantilla(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||
if err != nil {
|
||||
|
||||
@@ -42,6 +42,7 @@ func RenovacionesRoutes(protected fiber.Router) {
|
||||
protected.Delete("/api/plantillas-correo/:id", controllers.DeletePlantilla)
|
||||
protected.Get("/api/plantillas-correo/:id/preview", controllers.PreviewPlantilla)
|
||||
protected.Post("/api/plantillas-correo/:id/test", controllers.TestEnvioPlantilla)
|
||||
protected.Post("/api/plantillas-correo/validate", controllers.ValidarPlantilla)
|
||||
|
||||
// ─── Reglas de notificación ───────────────────────────────────────
|
||||
protected.Get("/reglas-notificacion", middlewares.MenuMiddleware, controllers.ReglasView)
|
||||
|
||||
Reference in New Issue
Block a user