diff --git a/amplify/data/resource.ts b/amplify/data/resource.ts
index f0037990..e37ef6c0 100644
--- a/amplify/data/resource.ts
+++ b/amplify/data/resource.ts
@@ -179,7 +179,7 @@ const schema = a
owner: a.string().required(),
products: a.hasMany('Product', 'collectionId'),
})
- .secondaryIndexes(index => [index('storeId')])
+ .secondaryIndexes(index => [index('storeId'), index('title')])
.authorization(allow => [
allow.ownerDefinedIn('owner').to(['update', 'delete', 'read', 'create']),
allow.guest().to(['read']),
diff --git a/app/[store]/page.tsx b/app/[store]/page.tsx
index 7a196f44..a07e366f 100644
--- a/app/[store]/page.tsx
+++ b/app/[store]/page.tsx
@@ -1,10 +1,46 @@
import { Metadata } from 'next'
import { notFound } from 'next/navigation'
+import { cache } from 'react'
import { storeRenderer } from '@/lib/store-renderer'
// Forzar renderizado dinámico para acceder a variables de entorno en runtime
export const dynamic = 'force-dynamic'
+/**
+ * Verifica si el path corresponde a un asset estático
+ */
+function isAssetPath(path: string): boolean {
+ const assetExtensions = [
+ '.png',
+ '.jpg',
+ '.jpeg',
+ '.gif',
+ '.svg',
+ '.webp',
+ '.ico',
+ '.css',
+ '.js',
+ '.woff',
+ '.woff2',
+ '.ttf',
+ '.eot',
+ ]
+ return (
+ assetExtensions.some(ext => path.toLowerCase().endsWith(ext)) ||
+ path.startsWith('/assets/') ||
+ path.startsWith('/_next/') ||
+ path.includes('/icons/')
+ )
+}
+
+/**
+ * Función cacheada usando React.cache() que persiste entre generateMetadata y StorePage
+ * Esta es la forma oficial de Next.js para compartir datos entre estas funciones
+ */
+const getCachedRenderResult = cache(async (domain: string, path: string) => {
+ return await storeRenderer.renderPage(domain, path)
+})
+
interface StorePageProps {
params: Promise<{
store: string
@@ -24,12 +60,18 @@ export default async function StorePage({ params, searchParams }: StorePageProps
const { store } = resolvedParams
const path = resolvedSearchParams.path || '/'
+ // Validar que no sea una ruta de asset
+ if (isAssetPath(path)) {
+ console.warn(`[StorePage] Asset path ${path} should not be handled by page renderer`)
+ notFound()
+ }
+
try {
// Resolver dominio completo (el middleware ya reescribió la URL)
const domain = `${store}.fasttify.com`
- // Renderizar página usando el sistema
- const result = await storeRenderer.renderPage(domain, path)
+ // Renderizar página usando el sistema con caché temporal
+ const result = await getCachedRenderResult(domain, path)
// Retornar HTML renderizado como componente dangerouslySetInnerHTML
// Esto permite SSR completo con SEO optimizado
@@ -59,10 +101,19 @@ export async function generateMetadata({
const { store } = resolvedParams
const path = resolvedSearchParams.path || '/'
+ // No generar metadata para assets
+ if (isAssetPath(path)) {
+ return {
+ title: 'Asset',
+ description: 'Static asset',
+ }
+ }
+
try {
const domain = `${store}.fasttify.com`
- const result = await storeRenderer.renderPage(domain, path)
+ // Usar el cache global para obtener el resultado completo
+ const result = await getCachedRenderResult(domain, path)
const { metadata } = result
return {
diff --git a/app/api/stores/render/route.ts b/app/api/stores/render/route.ts
deleted file mode 100644
index 5dfa0bae..00000000
--- a/app/api/stores/render/route.ts
+++ /dev/null
@@ -1,152 +0,0 @@
-import { NextRequest, NextResponse } from 'next/server'
-import { storeRenderer } from '@/lib/store-renderer'
-
-/**
- * API endpoint para renderizar páginas de tiendas
- *
- * GET /api/stores/render?domain=example.com&path=/products/mi-producto
- *
- * Query params:
- * - domain: Dominio de la tienda (requerido)
- * - path: Path de la página a renderizar (opcional, default: '/')
- */
-export async function GET(request: NextRequest) {
- try {
- const searchParams = request.nextUrl.searchParams
- const domain = searchParams.get('domain')
- const path = searchParams.get('path') || '/'
-
- // Validar parámetros requeridos
- if (!domain) {
- return NextResponse.json({ error: 'Domain parameter is required' }, { status: 400 })
- }
-
- // Validar formato del dominio
- if (!isValidDomain(domain)) {
- return NextResponse.json({ error: 'Invalid domain format' }, { status: 400 })
- }
-
- // Renderizar página usando el sistema de renderizado
- const result = await storeRenderer.renderPage(domain, path)
-
- // Configurar headers de respuesta
- const headers = new Headers({
- 'Content-Type': 'text/html; charset=utf-8',
- 'Cache-Control': `public, max-age=${Math.floor(result.cacheTTL / 1000)}`,
- 'X-Store-Cache-Key': result.cacheKey,
- })
-
- // Añadir headers SEO si existen
- if (result.metadata.canonical) {
- headers.set('Link', `<${result.metadata.canonical}>; rel="canonical"`)
- }
-
- // Crear respuesta HTML con metadata incluida
- const fullHtml = generateFullHTML(result.html, result.metadata)
-
- return new NextResponse(fullHtml, {
- status: 200,
- headers,
- })
- } catch (error: any) {
- console.error('Error rendering store page:', error)
-
- // Manejar errores tipados del sistema de renderizado
- if (error.type && error.statusCode) {
- return NextResponse.json(
- {
- error: error.message,
- type: error.type,
- details: process.env.APP_ENV === 'development' ? error.details : undefined,
- },
- { status: error.statusCode }
- )
- }
-
- // Error genérico
- return NextResponse.json(
- {
- error: 'Internal server error',
- message: process.env.APP_ENV === 'development' ? error.message : 'Something went wrong',
- },
- { status: 500 }
- )
- }
-}
-
-/**
- * Valida si un dominio tiene formato correcto
- */
-function isValidDomain(domain: string): boolean {
- const domainRegex =
- /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]\.([a-zA-Z]{2,}|[a-zA-Z]{2,}\.[a-zA-Z]{2,})$/
- return domainRegex.test(domain) || domain.includes('.fasttify.com')
-}
-
-/**
- * Genera HTML completo con metadata SEO
- */
-function generateFullHTML(body: string, metadata: any): string {
- const { title, description, canonical, openGraph, schema } = metadata
-
- return `
-
-
-
-
-
-
- ${escapeHtml(title)}
-
- ${canonical ? `` : ''}
-
-
- ${openGraph ? generateOpenGraphTags(openGraph) : ''}
-
-
- ${schema ? `` : ''}
-
-
-
-
-
-
- ${body}
-
-
-`
-}
-
-/**
- * Genera tags de Open Graph
- */
-function generateOpenGraphTags(og: any): string {
- return `
-
-
-
-
- ${og.image ? `` : ''}
- ${og.site_name ? `` : ''}
-
-
-
-
-
- ${og.image ? `` : ''}
- `.trim()
-}
-
-/**
- * Escapa HTML para prevenir XSS
- */
-function escapeHtml(text: string): string {
- if (!text) return ''
-
- return text
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
-}
diff --git a/lib/store-renderer/index.ts b/lib/store-renderer/index.ts
index d34934a3..d2b5adcd 100644
--- a/lib/store-renderer/index.ts
+++ b/lib/store-renderer/index.ts
@@ -1,5 +1,8 @@
-import { DynamicPageRenderer, type PageRenderOptions } from './renderers/homepage'
-import type { RenderResult } from './types'
+import {
+ DynamicPageRenderer,
+ type PageRenderOptions,
+} from '@/lib/store-renderer/renderers/homepage'
+import type { RenderResult } from '@/lib/store-renderer/types'
/**
* Factory principal del sistema de renderizado de tiendas
@@ -134,11 +137,11 @@ export class StoreRendererFactory {
export const storeRenderer = new StoreRendererFactory()
// Exportar tipos para uso externo
-export type { RenderResult } from './types'
-export { DynamicPageRenderer } from './renderers/homepage'
+export type { RenderResult } from '@/lib/store-renderer/types'
+export { DynamicPageRenderer } from '@/lib/store-renderer/renderers/homepage'
// Exportar servicios para uso avanzado
-export { domainResolver } from './services/core/domain-resolver'
-export { templateLoader } from './services/templates/template-loader'
-export { dataFetcher } from './services/fetchers/data-fetcher'
-export { liquidEngine } from './liquid/engine'
+export { domainResolver } from '@/lib/store-renderer/services/core/domain-resolver'
+export { templateLoader } from '@/lib/store-renderer/services/templates/template-loader'
+export { dataFetcher } from '@/lib/store-renderer/services/fetchers/data-fetcher'
+export { liquidEngine } from '@/lib/store-renderer/liquid/engine'
diff --git a/lib/store-renderer/liquid/engine.ts b/lib/store-renderer/liquid/engine.ts
index 96b499f6..17b0cfac 100644
--- a/lib/store-renderer/liquid/engine.ts
+++ b/lib/store-renderer/liquid/engine.ts
@@ -5,25 +5,21 @@ import type {
TemplateCache,
LiquidContext,
TemplateError,
-} from '../types'
-import { ecommerceFilters } from './filters'
-import { SchemaTag } from './tags/schema-tag'
-import { ScriptTag } from './tags/script-tag'
-import { SectionTag } from './tags/section-tag'
-import { PaginateTag } from './tags/paginate-tag'
-import { RenderTag, IncludeTag } from './tags/render-tag'
-import { StyleTag, StylesheetTag } from './tags/style-tag'
-import { JavaScriptTag } from './tags/javascript-tag'
-
-interface EngineCache {
- [templatePath: string]: TemplateCache
-}
+} from '@/lib/store-renderer/types'
+import { ecommerceFilters } from '@/lib/store-renderer/liquid/filters'
+import { SchemaTag } from '@/lib/store-renderer/liquid/tags/schema-tag'
+import { ScriptTag } from '@/lib/store-renderer/liquid/tags/script-tag'
+import { SectionTag } from '@/lib/store-renderer/liquid/tags/section-tag'
+import { PaginateTag } from '@/lib/store-renderer/liquid/tags/paginate-tag'
+import { RenderTag, IncludeTag } from '@/lib/store-renderer/liquid/tags/render-tag'
+import { StyleTag, StylesheetTag } from '@/lib/store-renderer/liquid/tags/style-tag'
+import { JavaScriptTag } from '@/lib/store-renderer/liquid/tags/javascript-tag'
+import { FormTag } from '@/lib/store-renderer/liquid/tags/form-tag'
+import { cacheManager } from '@/lib/store-renderer/services/core/cache-manager'
class LiquidEngine {
private static instance: LiquidEngine
private liquid: Liquid
- private cache: EngineCache = {}
- private readonly TEMPLATE_CACHE_TTL = 60 * 60 * 1000 // 1 hora en ms
private constructor() {
this.liquid = this.createEngine()
@@ -112,6 +108,7 @@ class LiquidEngine {
this.liquid.registerTag('stylesheet', StylesheetTag)
this.liquid.registerTag('script', ScriptTag)
this.liquid.registerTag('javascript', JavaScriptTag)
+ this.liquid.registerTag('form', FormTag)
}
/**
@@ -215,21 +212,16 @@ class LiquidEngine {
* Obtiene una plantilla del caché si existe y es válida
*/
private getCachedTemplate(templatePath: string, content: string): any | null {
- const cached = this.cache[templatePath]
- if (!cached) {
- return null
- }
+ const cacheKey = `template_${templatePath}`
+ const cached = cacheManager.getCached(cacheKey) as TemplateCache | null
- const now = Date.now()
- if (now > cached.lastUpdated.getTime() + cached.ttl) {
- // Caché expirado
- delete this.cache[templatePath]
+ if (!cached) {
return null
}
// Verificar que el contenido no haya cambiado
if (cached.content !== content) {
- delete this.cache[templatePath]
+ cacheManager.invalidateTemplateCache(templatePath)
return null
}
@@ -240,12 +232,15 @@ class LiquidEngine {
* Guarda una plantilla compilada en caché
*/
private setCachedTemplate(templatePath: string, content: string, compiled: any): void {
- this.cache[templatePath] = {
+ const cacheKey = `template_${templatePath}`
+ const templateCache: TemplateCache = {
content,
compiledTemplate: compiled,
lastUpdated: new Date(),
- ttl: this.TEMPLATE_CACHE_TTL,
+ ttl: cacheManager.TEMPLATE_CACHE_TTL,
}
+
+ cacheManager.setCached(cacheKey, templateCache, cacheManager.TEMPLATE_CACHE_TTL)
}
/**
@@ -253,14 +248,14 @@ class LiquidEngine {
* @param templatePath - Path de la plantilla a invalidar
*/
public invalidateCache(templatePath: string): void {
- delete this.cache[templatePath]
+ cacheManager.invalidateTemplateCache(templatePath)
}
/**
* Limpia todo el caché de plantillas
*/
public clearCache(): void {
- this.cache = {}
+ cacheManager.clearCache()
// Recrear la instancia de Liquid para limpiar su caché interno
this.liquid = this.createEngine()
this.registerFilters()
@@ -270,34 +265,14 @@ class LiquidEngine {
* Limpia plantillas expiradas del caché
*/
public cleanExpiredCache(): void {
- const now = Date.now()
- Object.keys(this.cache).forEach(templatePath => {
- const cached = this.cache[templatePath]
- if (now > cached.lastUpdated.getTime() + cached.ttl) {
- delete this.cache[templatePath]
- }
- })
+ cacheManager.cleanExpiredCache()
}
/**
* Obtiene estadísticas del caché para debugging
*/
public getCacheStats(): { total: number; expired: number; active: number } {
- const now = Date.now()
- let total = 0
- let expired = 0
- let active = 0
-
- Object.values(this.cache).forEach(cached => {
- total++
- if (now > cached.lastUpdated.getTime() + cached.ttl) {
- expired++
- } else {
- active++
- }
- })
-
- return { total, expired, active }
+ return cacheManager.getCacheStats()
}
/**
diff --git a/lib/store-renderer/liquid/filters.ts b/lib/store-renderer/liquid/filters.ts
index 4645b1a8..6e5f513c 100644
--- a/lib/store-renderer/liquid/filters.ts
+++ b/lib/store-renderer/liquid/filters.ts
@@ -1,4 +1,4 @@
-import type { LiquidFilter } from '../types'
+import type { LiquidFilter } from '@/lib/store-renderer/types'
/**
* Filtro para formatear precios con moneda
diff --git a/lib/store-renderer/liquid/tags/form-tag.ts b/lib/store-renderer/liquid/tags/form-tag.ts
new file mode 100644
index 00000000..c1c54993
--- /dev/null
+++ b/lib/store-renderer/liquid/tags/form-tag.ts
@@ -0,0 +1,230 @@
+import {
+ TagToken,
+ Context,
+ Emitter,
+ Tag,
+ Template,
+ TopLevelToken,
+ TokenKind,
+ Liquid,
+} from 'liquidjs'
+
+/**
+ * Tag para manejar formularios de Shopify
+ * Sintaxis: {% form 'type', object %}...{% endform %}
+ *
+ * Tipos soportados:
+ * - 'contact' : Formulario de contacto
+ * - 'newsletter' : Formulario de newsletter
+ * - 'product' : Formulario de producto (add to cart)
+ * - 'login' : Formulario de login
+ * - 'register' : Formulario de registro
+ * - 'recover_password' : Formulario de recuperar contraseña
+ */
+export class FormTag extends Tag {
+ private formType: string = ''
+ private formObject: any = null
+ private formAttributes: Record = {}
+ private templateContent: string = ''
+
+ constructor(tagToken: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
+ super(tagToken, remainTokens, liquid)
+
+ // Parsear argumentos del tag
+ this.parseArguments(tagToken.args)
+
+ // Parsear contenido hasta endform
+ this.parseTemplateContent(remainTokens)
+ }
+
+ /**
+ * Parsea el contenido del template hasta encontrar endform
+ */
+ private parseTemplateContent(remainTokens: TopLevelToken[]): void {
+ const contentTokens: string[] = []
+ let closed = false
+
+ while (remainTokens.length) {
+ const token = remainTokens.shift()
+ if (!token) break
+
+ if (token.kind === TokenKind.Tag && (token as any).name === 'endform') {
+ closed = true
+ break
+ }
+
+ if (token.kind === TokenKind.HTML) {
+ // Acceder correctamente al contenido HTML
+ const htmlToken = token as any
+ const tokenContent = htmlToken.input
+ ? htmlToken.input.substring(htmlToken.begin, htmlToken.end)
+ : ''
+ contentTokens.push(tokenContent)
+ } else if (token.kind === TokenKind.Output) {
+ const outputToken = token as any
+ const tokenContent = outputToken.content || outputToken.value || ''
+ contentTokens.push(`{{ ${tokenContent} }}`)
+ } else if (token.kind === TokenKind.Tag) {
+ const tagToken = token as any
+ const tokenContent = tagToken.content || tagToken.value || ''
+ contentTokens.push(`{% ${tokenContent} %}`)
+ }
+ }
+
+ if (!closed) {
+ throw new Error('tag {% form %} not closed')
+ }
+
+ this.templateContent = contentTokens.join('')
+ }
+
+ /**
+ * Parsea los argumentos del tag form
+ */
+ private parseArguments(args: string): void {
+ // Remover espacios y dividir argumentos
+ const cleanArgs = args.trim()
+
+ // Ejemplo: 'contact', object, class: 'my-form'
+ const parts = cleanArgs.split(',').map(part => part.trim())
+
+ if (parts.length > 0) {
+ // El primer argumento es el tipo de formulario (entre comillas)
+ this.formType = parts[0].replace(/['"`]/g, '')
+ }
+
+ if (parts.length > 1) {
+ // El segundo argumento puede ser un objeto
+ this.formObject = parts[1]
+ }
+
+ // Parsear atributos adicionales (class, id, etc.)
+ for (let i = 2; i < parts.length; i++) {
+ const attr = parts[i]
+ const [key, value] = attr.split(':').map(s => s.trim())
+ if (key && value) {
+ this.formAttributes[key] = value.replace(/['"`]/g, '')
+ }
+ }
+ }
+
+ /**
+ * Renderiza el tag form
+ */
+ *render(ctx: Context, emitter: Emitter): Generator {
+ const formAction = this.getFormAction(this.formType)
+ const formMethod = this.getFormMethod(this.formType)
+ const formClass = this.getFormClass(this.formType)
+ const formId = this.getFormId(this.formType)
+
+ // Construir atributos del formulario
+ let attributes = `action="${formAction}" method="${formMethod}"`
+
+ if (formClass) {
+ attributes += ` class="${formClass}"`
+ }
+
+ if (formId) {
+ attributes += ` id="${formId}"`
+ }
+
+ // Agregar atributos personalizados
+ Object.entries(this.formAttributes).forEach(([key, value]) => {
+ attributes += ` ${key}="${value}"`
+ })
+
+ // Abrir el formulario
+ emitter.write(`')
+ }
+
+ /**
+ * Obtiene la acción del formulario según el tipo
+ */
+ private getFormAction(type: string): string {
+ const actions: Record = {
+ contact: '/contact',
+ newsletter: '/newsletter',
+ product: '/cart/add',
+ login: '/account/login',
+ register: '/account/register',
+ recover_password: '/account/recover',
+ customer: '/customer',
+ storefront_password: '/password',
+ }
+
+ return actions[type] || '/contact'
+ }
+
+ /**
+ * Obtiene el método HTTP del formulario
+ */
+ private getFormMethod(type: string): string {
+ // La mayoría de formularios de Shopify usan POST
+ return 'post'
+ }
+
+ /**
+ * Obtiene la clase CSS del formulario
+ */
+ private getFormClass(type: string): string {
+ const classes: Record = {
+ contact: 'contact-form',
+ newsletter: 'newsletter-form',
+ product: 'product-form',
+ login: 'login-form',
+ register: 'register-form',
+ recover_password: 'recover-form',
+ customer: 'customer-form',
+ }
+
+ return classes[type] || 'form'
+ }
+
+ /**
+ * Obtiene el ID del formulario
+ */
+ private getFormId(type: string): string {
+ return `${type}-form`
+ }
+
+ /**
+ * Agrega campos ocultos necesarios según el tipo de formulario
+ */
+ private addHiddenFields(emitter: Emitter, type: string): void {
+ // Token CSRF (simulado para desarrollo)
+ emitter.write('')
+ emitter.write('')
+
+ // Campos específicos por tipo
+ switch (type) {
+ case 'contact':
+ emitter.write('')
+ break
+
+ case 'newsletter':
+ emitter.write('')
+ break
+
+ case 'product':
+ emitter.write('')
+ break
+
+ case 'login':
+ emitter.write('')
+ break
+ }
+ }
+}
+
+// EndFormTag no es necesario ya que FormTag se encarga de abrir y cerrar el formulario
diff --git a/lib/store-renderer/renderers/homepage.ts b/lib/store-renderer/renderers/homepage.ts
index d6a98557..42ea05e8 100644
--- a/lib/store-renderer/renderers/homepage.ts
+++ b/lib/store-renderer/renderers/homepage.ts
@@ -1,11 +1,11 @@
-import { domainResolver } from '../services/core/domain-resolver'
-import { templateLoader } from '../services/templates/template-loader'
-import { dataFetcher } from '../services/fetchers/data-fetcher'
-import { liquidEngine } from '../liquid/engine'
-import { contextBuilder } from '../services/rendering/context-builder'
-import { metadataGenerator } from '../services/rendering/metadata-generator'
-import { sectionRenderer } from '../services/rendering/section-renderer'
-import type { RenderResult, TemplateError } from '../types'
+import { domainResolver } from '@/lib/store-renderer/services/core/domain-resolver'
+import { templateLoader } from '@/lib/store-renderer/services/templates/template-loader'
+import { dataFetcher } from '@/lib/store-renderer/services/fetchers/data-fetcher'
+import { liquidEngine } from '@/lib/store-renderer/liquid/engine'
+import { contextBuilder } from '@/lib/store-renderer/services/rendering/context-builder'
+import { metadataGenerator } from '@/lib/store-renderer/services/rendering/metadata-generator'
+import { sectionRenderer } from '@/lib/store-renderer/services/rendering/section-renderer'
+import type { RenderResult, TemplateError } from '@/lib/store-renderer/types'
export interface PageRenderOptions {
pageType:
@@ -291,9 +291,9 @@ export class DynamicPageRenderer {
product: 'templates/product.json',
collection: 'templates/collection.json',
page: 'templates/page.json',
- blog: 'templates/blog.liquid',
- article: 'templates/article.liquid',
- search: 'templates/search.liquid',
+ blog: 'templates/blog.json',
+ article: 'templates/article.json',
+ search: 'templates/search.json',
cart: 'templates/cart.json',
'404': 'templates/404.json',
}
diff --git a/lib/store-renderer/services/core/cache-manager.ts b/lib/store-renderer/services/core/cache-manager.ts
index 411e5536..27490621 100644
--- a/lib/store-renderer/services/core/cache-manager.ts
+++ b/lib/store-renderer/services/core/cache-manager.ts
@@ -14,6 +14,8 @@ export class CacheManager {
public readonly PRODUCT_CACHE_TTL = 15 * 60 * 1000 // 15 minutos
public readonly COLLECTION_CACHE_TTL = 30 * 60 * 1000 // 30 minutos
public readonly STORE_CACHE_TTL = 30 * 60 * 1000 // 30 minutos
+ public readonly DOMAIN_CACHE_TTL = 30 * 60 * 1000 // 30 minutos
+ public readonly TEMPLATE_CACHE_TTL = 60 * 60 * 1000 // 1 hora
private constructor() {}
@@ -83,6 +85,22 @@ export class CacheManager {
})
}
+ /**
+ * Invalida el caché para un dominio específico
+ */
+ public invalidateDomainCache(domain: string): void {
+ const key = `domain_${domain}`
+ delete this.cache[key]
+ }
+
+ /**
+ * Invalida el caché para un template específico
+ */
+ public invalidateTemplateCache(templatePath: string): void {
+ const key = `template_${templatePath}`
+ delete this.cache[key]
+ }
+
/**
* Limpia todo el caché
*/
diff --git a/lib/store-renderer/services/core/domain-resolver.ts b/lib/store-renderer/services/core/domain-resolver.ts
index 3b5f5b55..2d2c67e0 100644
--- a/lib/store-renderer/services/core/domain-resolver.ts
+++ b/lib/store-renderer/services/core/domain-resolver.ts
@@ -1,18 +1,9 @@
import { cookiesClient } from '@/utils/AmplifyServer'
-import type { DomainResolution, Store, TemplateError } from '../../types'
-
-interface DomainCache {
- [domain: string]: {
- data: DomainResolution | null
- timestamp: number
- ttl: number
- }
-}
+import type { DomainResolution, Store, TemplateError } from '@/lib/store-renderer/types'
+import { cacheManager } from '@/lib/store-renderer/services/core/cache-manager'
class DomainResolver {
private static instance: DomainResolver
- private cache: DomainCache = {}
- private readonly CACHE_TTL = 30 * 60 * 1000 // 30 minutos en ms
private constructor() {}
@@ -24,15 +15,16 @@ class DomainResolver {
}
/**
- * Resuelve un dominio a información de tienda
+ * Resuelve un dominio a información completa de tienda
* @param domain - El dominio completo (ej: "usuario.fasttify.com")
- * @returns DomainResolution o null si no se encuentra
+ * @returns Store completa o null si no se encuentra
*/
- public async resolveDomain(domain: string): Promise {
+ public async resolveDomain(domain: string): Promise {
try {
// Verificar caché primero
- const cached = this.getCached(domain)
- if (cached !== undefined) {
+ const cacheKey = `domain_${domain}`
+ const cached = cacheManager.getCached(cacheKey)
+ if (cached !== null) {
return cached
}
@@ -43,21 +35,15 @@ class DomainResolver {
if (!stores || stores.length === 0) {
// Cachear resultado negativo por menos tiempo (5 minutos)
- this.setCached(domain, null, 5 * 60 * 1000)
+ cacheManager.setCached(cacheKey, null, 5 * 60 * 1000)
return null
}
- const store = stores[0] // Debería ser único por dominio
- const resolution: DomainResolution = {
- storeId: store.storeId,
- storeName: store.storeName,
- customDomain: store.customDomain || '',
- isActive: store.onboardingCompleted && store.storeStatus !== 'inactive',
- }
+ const store = stores[0] as Store // Debería ser único por dominio
// Cachear resultado positivo
- this.setCached(domain, resolution, this.CACHE_TTL)
- return resolution
+ cacheManager.setCached(cacheKey, store, cacheManager.DOMAIN_CACHE_TTL)
+ return store
} catch (error) {
console.error(`Error resolving domain ${domain}:`, error)
@@ -65,38 +51,15 @@ class DomainResolver {
}
}
- /**
- * Obtiene la información completa de la tienda por storeId
- * @param storeId - ID de la tienda
- * @returns Store o null si no se encuentra
- */
- public async getStoreById(storeId: string): Promise {
- try {
- const { data: store } = await cookiesClient.models.UserStore.get({
- storeId: storeId,
- })
-
- if (!store) {
- return null
- }
-
- return store as Store
- } catch (error) {
- console.error(`Error fetching store ${storeId}:`, error)
-
- return null
- }
- }
-
/**
* Resuelve un dominio completo: busca y retorna información completa de la tienda
* @param domain - El dominio completo
* @returns Store completa o lanza error
*/
public async resolveStoreByDomain(domain: string): Promise {
- const resolution = await this.resolveDomain(domain)
+ const store = await this.resolveDomain(domain)
- if (!resolution) {
+ if (!store) {
const error: TemplateError = {
type: 'STORE_NOT_FOUND',
message: `No store found for domain: ${domain}`,
@@ -105,7 +68,8 @@ class DomainResolver {
throw error
}
- if (!resolution.isActive) {
+ const isActive = store.onboardingCompleted && store.storeStatus !== 'inactive'
+ if (!isActive) {
const error: TemplateError = {
type: 'STORE_NOT_FOUND',
message: `Store is not active for domain: ${domain}`,
@@ -114,17 +78,6 @@ class DomainResolver {
throw error
}
- const store = await this.getStoreById(resolution.storeId)
-
- if (!store) {
- const error: TemplateError = {
- type: 'DATA_ERROR',
- message: `Store data not found for ID: ${resolution.storeId}`,
- statusCode: 500,
- }
- throw error
- }
-
return store
}
@@ -133,77 +86,28 @@ class DomainResolver {
* @param domain - Dominio a invalidar
*/
public invalidateCache(domain: string): void {
- delete this.cache[domain]
+ cacheManager.invalidateDomainCache(domain)
}
/**
* Limpia todo el caché
*/
public clearCache(): void {
- this.cache = {}
+ cacheManager.clearCache()
}
/**
* Limpia entradas expiradas del caché
*/
public cleanExpiredCache(): void {
- const now = Date.now()
- Object.keys(this.cache).forEach(domain => {
- const entry = this.cache[domain]
- if (now > entry.timestamp + entry.ttl) {
- delete this.cache[domain]
- }
- })
- }
-
- /**
- * Obtiene una entrada del caché si existe y no ha expirado
- */
- private getCached(domain: string): DomainResolution | null | undefined {
- const entry = this.cache[domain]
- if (!entry) {
- return undefined
- }
-
- const now = Date.now()
- if (now > entry.timestamp + entry.ttl) {
- delete this.cache[domain]
- return undefined
- }
-
- return entry.data
- }
-
- /**
- * Guarda una entrada en el caché
- */
- private setCached(domain: string, data: DomainResolution | null, ttl: number): void {
- this.cache[domain] = {
- data,
- timestamp: Date.now(),
- ttl,
- }
+ cacheManager.cleanExpiredCache()
}
/**
* Obtiene estadísticas del caché para debugging
*/
public getCacheStats(): { total: number; expired: number; active: number } {
- const now = Date.now()
- let total = 0
- let expired = 0
- let active = 0
-
- Object.values(this.cache).forEach(entry => {
- total++
- if (now > entry.timestamp + entry.ttl) {
- expired++
- } else {
- active++
- }
- })
-
- return { total, expired, active }
+ return cacheManager.getCacheStats()
}
}
diff --git a/lib/store-renderer/services/fetchers/collection-fetcher.ts b/lib/store-renderer/services/fetchers/collection-fetcher.ts
index 7b6d2237..ef28600e 100644
--- a/lib/store-renderer/services/fetchers/collection-fetcher.ts
+++ b/lib/store-renderer/services/fetchers/collection-fetcher.ts
@@ -1,7 +1,7 @@
import { cookiesClient } from '@/utils/AmplifyServer'
-import { cacheManager } from '../core/cache-manager'
-import { dataTransformer } from '../core/data-transformer'
-import type { CollectionContext, ProductContext, TemplateError } from '../../types'
+import { cacheManager } from '@/lib/store-renderer/services/core/cache-manager'
+import { dataTransformer } from '@/lib/store-renderer/services/core/data-transformer'
+import type { CollectionContext, ProductContext, TemplateError } from '@/lib/store-renderer/types'
interface PaginationOptions {
limit?: number
@@ -127,7 +127,9 @@ export class CollectionFetcher {
* Transforma una colección de Amplify al formato Liquid
*/
private async transformCollection(collection: any, storeId: string): Promise {
- const handle = dataTransformer.createHandle(`collection-${collection.id}`)
+ const handle = dataTransformer.createHandle(
+ collection.name || collection.title || `collection-${collection.id}`
+ )
// Obtener productos de la colección si existe relación
const products: ProductContext[] = []
diff --git a/lib/store-renderer/services/fetchers/data-fetcher.ts b/lib/store-renderer/services/fetchers/data-fetcher.ts
index 6fcb1262..44733b83 100644
--- a/lib/store-renderer/services/fetchers/data-fetcher.ts
+++ b/lib/store-renderer/services/fetchers/data-fetcher.ts
@@ -1,8 +1,8 @@
-import { cacheManager } from '../core/cache-manager'
-import { productFetcher } from '../fetchers/product-fetcher'
-import { collectionFetcher } from '../fetchers/collection-fetcher'
-import { templateFetcher } from '../fetchers/template-fetcher'
-import type { ProductContext, CollectionContext } from '../../types'
+import { cacheManager } from '@/lib/store-renderer/services/core/cache-manager'
+import { productFetcher } from '@/lib/store-renderer/services/fetchers/product-fetcher'
+import { collectionFetcher } from '@/lib/store-renderer/services/fetchers/collection-fetcher'
+import { templateFetcher } from '@/lib/store-renderer/services/fetchers/template-fetcher'
+import type { ProductContext, CollectionContext } from '@/lib/store-renderer/types'
interface PaginationOptions {
limit?: number
diff --git a/lib/store-renderer/services/fetchers/product-fetcher.ts b/lib/store-renderer/services/fetchers/product-fetcher.ts
index 04cdfbab..1415ae01 100644
--- a/lib/store-renderer/services/fetchers/product-fetcher.ts
+++ b/lib/store-renderer/services/fetchers/product-fetcher.ts
@@ -1,7 +1,7 @@
import { cookiesClient } from '@/utils/AmplifyServer'
-import { cacheManager } from '../core/cache-manager'
-import { dataTransformer } from '../core/data-transformer'
-import type { ProductContext, TemplateError } from '../../types'
+import { cacheManager } from '@/lib/store-renderer/services/core/cache-manager'
+import { dataTransformer } from '@/lib/store-renderer/services/core/data-transformer'
+import type { ProductContext, TemplateError } from '@/lib/store-renderer/types'
interface PaginationOptions {
limit?: number
diff --git a/lib/store-renderer/services/fetchers/template-fetcher.ts b/lib/store-renderer/services/fetchers/template-fetcher.ts
index 8193a58e..2139e7c9 100644
--- a/lib/store-renderer/services/fetchers/template-fetcher.ts
+++ b/lib/store-renderer/services/fetchers/template-fetcher.ts
@@ -1,6 +1,6 @@
import { cookiesClient } from '@/utils/AmplifyServer'
-import { cacheManager } from '../core/cache-manager'
-import type { TemplateError } from '../../types'
+import { cacheManager } from '@/lib/store-renderer/services/core/cache-manager'
+import type { TemplateError } from '@/lib/store-renderer/types'
export class TemplateFetcher {
/**
diff --git a/lib/store-renderer/services/rendering/context-builder.ts b/lib/store-renderer/services/rendering/context-builder.ts
index 3e21c25f..a0631560 100644
--- a/lib/store-renderer/services/rendering/context-builder.ts
+++ b/lib/store-renderer/services/rendering/context-builder.ts
@@ -1,5 +1,5 @@
-import type { RenderContext, ShopContext, PageContext } from '../../types'
-import { linkListService } from '../core/linkList-service'
+import type { RenderContext, ShopContext, PageContext } from '@/lib/store-renderer/types'
+import { linkListService } from '@/lib/store-renderer/services/core/linkList-service'
export class ContextBuilder {
/**
diff --git a/lib/store-renderer/services/rendering/metadata-generator.ts b/lib/store-renderer/services/rendering/metadata-generator.ts
index 48d8088f..be30314c 100644
--- a/lib/store-renderer/services/rendering/metadata-generator.ts
+++ b/lib/store-renderer/services/rendering/metadata-generator.ts
@@ -1,4 +1,4 @@
-import type { RenderResult, OpenGraphData, SchemaData } from '../../types'
+import type { RenderResult, OpenGraphData, SchemaData } from '@/lib/store-renderer/types'
export class MetadataGenerator {
/**
diff --git a/lib/store-renderer/services/rendering/section-renderer.ts b/lib/store-renderer/services/rendering/section-renderer.ts
index 96466a20..f191cbdf 100644
--- a/lib/store-renderer/services/rendering/section-renderer.ts
+++ b/lib/store-renderer/services/rendering/section-renderer.ts
@@ -1,7 +1,7 @@
-import { liquidEngine } from '../../liquid/engine'
-import { templateLoader } from '../templates/template-loader'
-import { schemaParser } from '../templates/schema-parser'
-import type { RenderContext } from '../../types'
+import { liquidEngine } from '@/lib/store-renderer/liquid/engine'
+import { templateLoader } from '@/lib/store-renderer/services/templates/template-loader'
+import { schemaParser } from '@/lib/store-renderer/services/templates/schema-parser'
+import type { RenderContext } from '@/lib/store-renderer/types'
export class SectionRenderer {
/**
@@ -13,10 +13,9 @@ export class SectionRenderer {
baseContext: RenderContext,
storeTemplate?: any
): Promise {
- try {
- // Extraer settings del schema como fallback
- const schemaSettings = schemaParser.extractSchemaSettings(templateContent)
+ const schemaSettings = schemaParser.extractSchemaSettings(templateContent)
+ try {
// Obtener settings y blocks reales del storeTemplate si existe
const storeSection = storeTemplate?.sections?.[sectionName]
const actualSettings = storeSection?.settings || {}
@@ -39,7 +38,33 @@ export class SectionRenderer {
return await liquidEngine.render(templateContent, sectionContext, `section_${sectionName}`)
} catch (error) {
console.error(`Error rendering section ${sectionName}:`, error)
- return ``
+
+ // Intentar render con contexto más simple como fallback
+ if (error instanceof Error && error.message.includes('unexpected')) {
+ console.warn(`Attempting simplified render for section ${sectionName}...`)
+ try {
+ // Contexto más básico sin blocks complejos
+ const simpleContext = {
+ ...baseContext,
+ section: {
+ id: sectionName,
+ settings: schemaSettings, // Solo usar defaults del schema
+ },
+ }
+
+ return await liquidEngine.render(
+ templateContent,
+ simpleContext,
+ `section_${sectionName}_simple`
+ )
+ } catch (fallbackError) {
+ console.error(`Simplified render also failed for ${sectionName}:`, fallbackError)
+ }
+ }
+
+ // Si todo falla, mostrar placeholder informativo
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
+ return ``
}
}
diff --git a/lib/store-renderer/services/templates/schema-parser.ts b/lib/store-renderer/services/templates/schema-parser.ts
index eb56a0d6..a152a469 100644
--- a/lib/store-renderer/services/templates/schema-parser.ts
+++ b/lib/store-renderer/services/templates/schema-parser.ts
@@ -1,4 +1,86 @@
export class SchemaParser {
+ /**
+ * Limpia y valida el contenido JSON de un schema
+ */
+ private cleanSchemaJSON(jsonContent: string): string {
+ try {
+ // Remover comentarios tipo // (no válidos en JSON)
+ let cleaned = jsonContent.replace(/\/\/.*$/gm, '')
+
+ // Remover comentarios tipo /* */ (no válidos en JSON)
+ cleaned = cleaned.replace(/\/\*[\s\S]*?\*\//g, '')
+
+ // Arreglar comas finales antes de } o ]
+ cleaned = cleaned.replace(/,(\s*[}\]])/g, '$1')
+
+ // Arreglar comas dobles
+ cleaned = cleaned.replace(/,,+/g, ',')
+
+ // Remover espacios extra y saltos de línea extra
+ cleaned = cleaned.replace(/\s+/g, ' ').trim()
+
+ // Intentar validar brackets (pero no fallar si hay problemas)
+ try {
+ this.validateBracketsBalance(cleaned)
+ } catch (bracketError) {
+ const errorMessage =
+ bracketError instanceof Error ? bracketError.message : 'Unknown bracket error'
+ console.warn('Schema has unbalanced brackets, but continuing with parsing:', errorMessage)
+ // No retornamos error, intentamos parsear el JSON de todas formas
+ }
+
+ return cleaned
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
+ console.warn('Error cleaning schema JSON, using original:', errorMessage)
+ // Si hay error en la limpieza, devolver el contenido original
+ return jsonContent
+ }
+ }
+
+ /**
+ * Valida que los brackets estén balanceados en el JSON
+ */
+ private validateBracketsBalance(jsonContent: string): void {
+ let braceCount = 0
+ let bracketCount = 0
+ let inString = false
+ let escapeNext = false
+
+ for (let i = 0; i < jsonContent.length; i++) {
+ const char = jsonContent[i]
+
+ if (escapeNext) {
+ escapeNext = false
+ continue
+ }
+
+ if (char === '\\') {
+ escapeNext = true
+ continue
+ }
+
+ if (char === '"' && !escapeNext) {
+ inString = !inString
+ continue
+ }
+
+ if (!inString) {
+ if (char === '{') braceCount++
+ else if (char === '}') braceCount--
+ else if (char === '[') bracketCount++
+ else if (char === ']') bracketCount--
+ }
+ }
+
+ if (braceCount !== 0) {
+ throw new Error(`Unbalanced braces: ${braceCount > 0 ? 'missing }' : 'extra }'}`)
+ }
+ if (bracketCount !== 0) {
+ throw new Error(`Unbalanced brackets: ${bracketCount > 0 ? 'missing ]' : 'extra ]'}`)
+ }
+ }
+
/**
* Extrae los settings del schema de un template usando expresiones regulares
*/
@@ -12,8 +94,32 @@ export class SchemaParser {
return {}
}
- // Parsear el JSON del schema
- const schemaJSON = JSON.parse(match[1].trim())
+ // Limpiar el contenido del schema antes de parsear
+ const rawSchemaContent = match[1].trim()
+
+ // Intentar parsear directamente primero
+ let schemaJSON: any
+ try {
+ schemaJSON = JSON.parse(rawSchemaContent)
+ } catch (directParseError) {
+ // Si falla el parseo directo, intentar con limpieza
+ try {
+ const cleanedSchemaContent = this.cleanSchemaJSON(rawSchemaContent)
+
+ // Log para debug solo en desarrollo
+ if (process.env.NODE_ENV === 'development') {
+ console.log(
+ 'Schema content to parse (first 200 chars):',
+ cleanedSchemaContent.substring(0, 200) + '...'
+ )
+ }
+
+ schemaJSON = JSON.parse(cleanedSchemaContent)
+ } catch (cleanParseError) {
+ console.warn('Failed to parse schema JSON after cleaning, skipping schema settings')
+ return {}
+ }
+ }
if (!schemaJSON.settings) {
return {}
@@ -30,7 +136,15 @@ export class SchemaParser {
return settings
} catch (error) {
- console.warn('Error extracting schema settings:', error)
+ console.error('Error extracting schema settings:', error)
+
+ // Buscar nuevamente el match para el log de error
+ const schemaRegex = /{%\s*schema\s*%}([\s\S]*?){%\s*endschema\s*%}/i
+ const errorMatch = templateContent.match(schemaRegex)
+ if (errorMatch?.[1]) {
+ console.error('Schema content that failed:', errorMatch[1].substring(0, 500) + '...')
+ }
+
return {}
}
}
@@ -47,7 +161,23 @@ export class SchemaParser {
return []
}
- const schemaJSON = JSON.parse(match[1].trim())
+ const rawContent = match[1].trim()
+
+ // Intentar parseo directo primero
+ let schemaJSON: any
+ try {
+ schemaJSON = JSON.parse(rawContent)
+ } catch (directError) {
+ // Intentar con limpieza
+ try {
+ const cleanedContent = this.cleanSchemaJSON(rawContent)
+ schemaJSON = JSON.parse(cleanedContent)
+ } catch (cleanError) {
+ console.warn('Error extracting schema blocks:', cleanError)
+ return []
+ }
+ }
+
return schemaJSON.blocks || []
} catch (error) {
console.warn('Error extracting schema blocks:', error)
@@ -97,7 +227,19 @@ export class SchemaParser {
return {}
}
- return JSON.parse(match[1].trim())
+ const rawContent = match[1].trim()
+
+ try {
+ return JSON.parse(rawContent)
+ } catch (directError) {
+ try {
+ const cleanedContent = this.cleanSchemaJSON(rawContent)
+ return JSON.parse(cleanedContent)
+ } catch (cleanError) {
+ console.warn('Error extracting full schema:', cleanError)
+ return {}
+ }
+ }
} catch (error) {
console.warn('Error extracting schema:', error)
return {}
diff --git a/lib/store-renderer/services/templates/template-loader.ts b/lib/store-renderer/services/templates/template-loader.ts
index ecff1c6c..2e59e5d4 100644
--- a/lib/store-renderer/services/templates/template-loader.ts
+++ b/lib/store-renderer/services/templates/template-loader.ts
@@ -1,18 +1,11 @@
import { S3Client, GetObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3'
-import type { TemplateFile, TemplateCache, TemplateError } from '../../types'
+import type { TemplateFile, TemplateCache, TemplateError } from '@/lib/store-renderer/types'
import { cookiesClient } from '@/utils/AmplifyServer'
-
-interface S3TemplateCache {
- [storeId: string]: {
- [templatePath: string]: TemplateCache
- }
-}
+import { cacheManager } from '@/lib/store-renderer/services/core/cache-manager'
class TemplateLoader {
private static instance: TemplateLoader
private s3Client?: S3Client
- private cache: S3TemplateCache = {}
- private readonly TEMPLATE_CACHE_TTL = 60 * 60 * 1000 // 1 hora en ms
private readonly bucketName: string
private readonly cloudFrontDomain: string
private readonly appEnv: string
@@ -198,8 +191,8 @@ class TemplateLoader {
public async loadAsset(storeId: string, assetPath: string): Promise {
try {
// Verificar caché primero (para assets también)
- const cacheKey = `assets/${assetPath}`
- const cached = this.getCachedTemplate(storeId, cacheKey)
+ const cacheKey = `asset_${storeId}_${assetPath}`
+ const cached = cacheManager.getCached(cacheKey) as TemplateCache | null
if (cached) {
// Para assets, el contenido en caché es base64, convertir de vuelta a Buffer
return Buffer.from(cached.content, 'base64')
@@ -215,7 +208,12 @@ class TemplateLoader {
}
// Guardar en caché (convertir Buffer a base64 para almacenamiento)
- this.setCachedTemplate(storeId, cacheKey, assetBuffer.toString('base64'))
+ const assetCache: TemplateCache = {
+ content: assetBuffer.toString('base64'),
+ lastUpdated: new Date(),
+ ttl: cacheManager.TEMPLATE_CACHE_TTL,
+ }
+ cacheManager.setCached(cacheKey, assetCache, cacheManager.TEMPLATE_CACHE_TTL)
return assetBuffer
} catch (error) {
@@ -258,7 +256,7 @@ class TemplateLoader {
* @param storeId - ID de la tienda
*/
public invalidateStoreCache(storeId: string): void {
- delete this.cache[storeId]
+ cacheManager.invalidateStoreCache(storeId)
}
/**
@@ -267,39 +265,22 @@ class TemplateLoader {
* @param templatePath - Ruta de la plantilla
*/
public invalidateTemplateCache(storeId: string, templatePath: string): void {
- if (this.cache[storeId]) {
- delete this.cache[storeId][templatePath]
- }
+ const cacheKey = `template_${storeId}_${templatePath}`
+ cacheManager.setCached(cacheKey, null, 0) // Invalidar estableciendo a null
}
/**
* Limpia todo el caché
*/
public clearCache(): void {
- this.cache = {}
+ cacheManager.clearCache()
}
/**
* Limpia plantillas expiradas del caché
*/
public cleanExpiredCache(): void {
- const now = Date.now()
-
- Object.keys(this.cache).forEach(storeId => {
- const storeCache = this.cache[storeId]
-
- Object.keys(storeCache).forEach(templatePath => {
- const cached = storeCache[templatePath]
- if (now > cached.lastUpdated.getTime() + cached.ttl) {
- delete storeCache[templatePath]
- }
- })
-
- // Si no quedan plantillas en caché para esta tienda, eliminar la entrada
- if (Object.keys(storeCache).length === 0) {
- delete this.cache[storeId]
- }
- })
+ cacheManager.cleanExpiredCache()
}
/**
@@ -394,22 +375,8 @@ class TemplateLoader {
* Obtiene una plantilla del caché si existe y es válida
*/
private getCachedTemplate(storeId: string, templatePath: string): TemplateCache | null {
- const storeCache = this.cache[storeId]
- if (!storeCache) {
- return null
- }
-
- const cached = storeCache[templatePath]
- if (!cached) {
- return null
- }
-
- const now = Date.now()
- if (now > cached.lastUpdated.getTime() + cached.ttl) {
- delete storeCache[templatePath]
- return null
- }
-
+ const cacheKey = `template_${storeId}_${templatePath}`
+ const cached = cacheManager.getCached(cacheKey) as TemplateCache | null
return cached
}
@@ -417,15 +384,14 @@ class TemplateLoader {
* Guarda una plantilla en caché
*/
private setCachedTemplate(storeId: string, templatePath: string, content: string): void {
- if (!this.cache[storeId]) {
- this.cache[storeId] = {}
- }
-
- this.cache[storeId][templatePath] = {
+ const cacheKey = `template_${storeId}_${templatePath}`
+ const templateCache: TemplateCache = {
content,
lastUpdated: new Date(),
- ttl: this.TEMPLATE_CACHE_TTL,
+ ttl: cacheManager.TEMPLATE_CACHE_TTL,
}
+
+ cacheManager.setCached(cacheKey, templateCache, cacheManager.TEMPLATE_CACHE_TTL)
}
/**
@@ -456,26 +422,15 @@ class TemplateLoader {
expiredTemplates: number
activeTemplates: number
} {
- const now = Date.now()
- let stores = 0
- let totalTemplates = 0
- let expiredTemplates = 0
- let activeTemplates = 0
-
- Object.values(this.cache).forEach(storeCache => {
- stores++
-
- Object.values(storeCache).forEach(cached => {
- totalTemplates++
- if (now > cached.lastUpdated.getTime() + cached.ttl) {
- expiredTemplates++
- } else {
- activeTemplates++
- }
- })
- })
-
- return { stores, totalTemplates, expiredTemplates, activeTemplates }
+ const globalStats = cacheManager.getCacheStats()
+
+ // Para mantener compatibilidad, mapeamos las estadísticas globales
+ return {
+ stores: 0, // No podemos determinar esto fácilmente con el cache global
+ totalTemplates: globalStats.total,
+ expiredTemplates: globalStats.expired,
+ activeTemplates: globalStats.active,
+ }
}
}