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:
|
token:
|
||||||
app_jwt_secret: SECRET_APP
|
app_jwt_secret: SECRET_APP
|
||||||
api_jwt_secret: SECRET_API
|
api_jwt_secret: SECRET_API
|
||||||
expires_in: 3600
|
expires_in: 31536000
|
||||||
jwt:
|
jwt:
|
||||||
app:
|
app:
|
||||||
secret: SECRET_APP
|
secret: SECRET_APP
|
||||||
expire: 3600
|
expire: 31536000
|
||||||
api:
|
api:
|
||||||
secret: SECRET_API
|
secret: SECRET_API
|
||||||
expire: 3600
|
expire: 31536000
|
||||||
|
|
||||||
template:
|
template:
|
||||||
path: "resources/view"
|
path: "resources/view"
|
||||||
|
|||||||
+3
-3
@@ -53,14 +53,14 @@ mail:
|
|||||||
token:
|
token:
|
||||||
app_jwt_secret: SECRET_APP
|
app_jwt_secret: SECRET_APP
|
||||||
api_jwt_secret: SECRET_API
|
api_jwt_secret: SECRET_API
|
||||||
expires_in: 3600
|
expires_in: 31536000
|
||||||
jwt:
|
jwt:
|
||||||
app:
|
app:
|
||||||
secret: SECRET_APP
|
secret: SECRET_APP
|
||||||
expire: 3600
|
expire: 31536000
|
||||||
api:
|
api:
|
||||||
secret: SECRET_API
|
secret: SECRET_API
|
||||||
expire: 3600
|
expire: 31536000
|
||||||
|
|
||||||
template:
|
template:
|
||||||
path: "resources/view"
|
path: "resources/view"
|
||||||
|
|||||||
+3
-1
@@ -28,7 +28,9 @@ func (s *SessionConfig) Setup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configurar el proveedor de sesiones en memoria
|
// 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
|
// Crear una nueva sesión
|
||||||
s.Session = store
|
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 {
|
if len(expire) > 0 {
|
||||||
t.Expire = expire[0]
|
t.Expire = expire[0]
|
||||||
} else {
|
} 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()
|
expiresIn := time.Now().Add(time.Duration(t.Expire) * time.Second).Unix()
|
||||||
claims["exp"] = expiresIn
|
claims["exp"] = expiresIn
|
||||||
|
|||||||
@@ -110,7 +110,9 @@
|
|||||||
<label class="text-xs font-medium text-gray-600 block mb-1">HTML del correo *</label>
|
<label class="text-xs font-medium text-gray-600 block mb-1">HTML del correo *</label>
|
||||||
<textarea x-model="form.cuerpo_html" rows="16"
|
<textarea x-model="form.cuerpo_html" rows="16"
|
||||||
@input.debounce.600ms="previewInline()"
|
@input.debounce.600ms="previewInline()"
|
||||||
|
:class="templateError ? 'border-red-400' : ''"
|
||||||
class="w-full border rounded px-3 py-2 text-xs font-mono" required></textarea>
|
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>
|
||||||
<div>
|
<div>
|
||||||
<label class="text-xs font-medium text-gray-600 block mb-1">Vista previa</label>
|
<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,
|
loading: false,
|
||||||
datos: [], total: 0, totalPages: 1, page: 1, limit: 10, search: '',
|
datos: [], total: 0, totalPages: 1, page: 1, limit: 10, search: '',
|
||||||
addModal: false, editModal: false, deleteModal: false, previewModal: false, testModal: false,
|
addModal: false, editModal: false, deleteModal: false, previewModal: false, testModal: false,
|
||||||
selectedId: null, testEmail: '',
|
selectedId: null, testEmail: '', templateError: '',
|
||||||
form: { nombre:'', asunto:'', cuerpo_html:'', tipo:'renovacion' },
|
form: { nombre:'', asunto:'', cuerpo_html:'', tipo:'renovacion' },
|
||||||
toast: { show:false, msg:'', type:'ok' },
|
toast: { show:false, msg:'', type:'ok' },
|
||||||
|
|
||||||
@@ -211,18 +213,25 @@ document.addEventListener('alpine:init', () => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
previewInline() {
|
async previewInline() {
|
||||||
const frame = this.$refs.previewFrame;
|
const frame = this.$refs.previewFrame;
|
||||||
if (frame) frame.srcdoc = this.form.cuerpo_html || '<p style="color:#999;padding:1rem">Sin contenido</p>';
|
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() {
|
closeModals() {
|
||||||
this.addModal = this.editModal = this.deleteModal = this.previewModal = this.testModal = false;
|
this.addModal = this.editModal = this.deleteModal = this.previewModal = this.testModal = false;
|
||||||
this.selectedId = null;
|
this.selectedId = null;
|
||||||
|
this.templateError = '';
|
||||||
this.form = { nombre:'', asunto:'', cuerpo_html:'', tipo:'renovacion' };
|
this.form = { nombre:'', asunto:'', cuerpo_html:'', tipo:'renovacion' };
|
||||||
},
|
},
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
if (this.templateError) { this.showToast('Corrige los errores de sintaxis antes de guardar', 'error'); return; }
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
if (this.editModal) {
|
if (this.editModal) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
"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})
|
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 {
|
func TestEnvioPlantilla(c *fiber.Ctx) error {
|
||||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ func RenovacionesRoutes(protected fiber.Router) {
|
|||||||
protected.Delete("/api/plantillas-correo/:id", controllers.DeletePlantilla)
|
protected.Delete("/api/plantillas-correo/:id", controllers.DeletePlantilla)
|
||||||
protected.Get("/api/plantillas-correo/:id/preview", controllers.PreviewPlantilla)
|
protected.Get("/api/plantillas-correo/:id/preview", controllers.PreviewPlantilla)
|
||||||
protected.Post("/api/plantillas-correo/:id/test", controllers.TestEnvioPlantilla)
|
protected.Post("/api/plantillas-correo/:id/test", controllers.TestEnvioPlantilla)
|
||||||
|
protected.Post("/api/plantillas-correo/validate", controllers.ValidarPlantilla)
|
||||||
|
|
||||||
// ─── Reglas de notificación ───────────────────────────────────────
|
// ─── Reglas de notificación ───────────────────────────────────────
|
||||||
protected.Get("/reglas-notificacion", middlewares.MenuMiddleware, controllers.ReglasView)
|
protected.Get("/reglas-notificacion", middlewares.MenuMiddleware, controllers.ReglasView)
|
||||||
|
|||||||
Reference in New Issue
Block a user