libreria-astro-lefebvre 0.0.57 → 0.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -3
- package/src/limbo/LimboProvider.astro +0 -87
- package/src/limbo/index.ts +0 -24
- package/src/limbo/init.ts +0 -288
- package/src/limbo/tokenHandler.ts +0 -207
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libreria-astro-lefebvre",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.59",
|
|
4
4
|
"description": "Librería de componentes Astro, React y Vue para Lefebvre",
|
|
5
5
|
"author": "Equipo web desarrollo Lefebvre",
|
|
6
6
|
"type": "module",
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
"design-system"
|
|
28
28
|
],
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"astro": "^5.11.0"
|
|
31
|
-
"limbo-component": "latest"
|
|
30
|
+
"astro": "^5.11.0"
|
|
32
31
|
},
|
|
33
32
|
"scripts": {
|
|
34
33
|
"generate:registry": "node scripts/generateRegistry.js"
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* LimboProvider.astro
|
|
4
|
-
*
|
|
5
|
-
* Componente Astro que provee la integración con Limbo.
|
|
6
|
-
* Incluye los assets necesarios (CSS/JS) y la inicialización automática.
|
|
7
|
-
*
|
|
8
|
-
* Uso:
|
|
9
|
-
* ```astro
|
|
10
|
-
* ---
|
|
11
|
-
* import LimboProvider from 'libreria-astro-lefebvre/limbo/LimboProvider.astro';
|
|
12
|
-
* ---
|
|
13
|
-
* <LimboProvider
|
|
14
|
-
* publicKey="pk_xxx"
|
|
15
|
-
* apiKey="sk_xxx"
|
|
16
|
-
* prod={false}
|
|
17
|
-
* sizeModal="fullscreen"
|
|
18
|
-
* />
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { generateLimboInitScript, getLimboAssetUrls } from './init';
|
|
23
|
-
|
|
24
|
-
interface Props {
|
|
25
|
-
/** Public Key del portal de Limbo */
|
|
26
|
-
publicKey: string;
|
|
27
|
-
/** Usar API de producción */
|
|
28
|
-
prod?: boolean;
|
|
29
|
-
/** Selector CSS para detectar inputs (default: '.js-limbo') */
|
|
30
|
-
selector?: string;
|
|
31
|
-
/** Tipo de retorno por defecto */
|
|
32
|
-
defaultReturnType?: 'url' | 'assetId' | 'object' | 'json' | 'base64';
|
|
33
|
-
/** Modo UI por defecto */
|
|
34
|
-
defaultModeUI?: 'full' | 'gallery-only' | 'upload-only' | 'crop-only';
|
|
35
|
-
/** Tamaño del modal (default: 'xlarge') */
|
|
36
|
-
sizeModal?: 'small' | 'medium' | 'large' | 'xlarge' | 'fullscreen';
|
|
37
|
-
/** Texto del botón por defecto */
|
|
38
|
-
defaultButtonText?: string;
|
|
39
|
-
/** Permitir crops adicionales además de los obligatorios */
|
|
40
|
-
allowAdditionalCrops?: boolean;
|
|
41
|
-
/** Máximo número de crops permitidos */
|
|
42
|
-
maxCrops?: number;
|
|
43
|
-
/** Versión de limbo-component a usar (default: 'latest') */
|
|
44
|
-
version?: string;
|
|
45
|
-
/** Usar assets locales en vez de CDN (para desarrollo con npm link) */
|
|
46
|
-
useLocalAssets?: boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const {
|
|
50
|
-
publicKey,
|
|
51
|
-
prod = false,
|
|
52
|
-
selector = '.js-limbo',
|
|
53
|
-
defaultReturnType = 'json',
|
|
54
|
-
defaultModeUI = 'full',
|
|
55
|
-
sizeModal = 'xlarge',
|
|
56
|
-
defaultButtonText = 'Seleccionar imagen',
|
|
57
|
-
allowAdditionalCrops = true,
|
|
58
|
-
maxCrops = 10,
|
|
59
|
-
version = 'latest',
|
|
60
|
-
useLocalAssets = !prod
|
|
61
|
-
} = Astro.props;
|
|
62
|
-
|
|
63
|
-
// Obtener URLs de assets
|
|
64
|
-
const assets = getLimboAssetUrls({ prod: !useLocalAssets, version });
|
|
65
|
-
|
|
66
|
-
// Generar script de inicialización
|
|
67
|
-
const initScript = generateLimboInitScript({
|
|
68
|
-
publicKey,
|
|
69
|
-
prod,
|
|
70
|
-
selector,
|
|
71
|
-
defaultReturnType,
|
|
72
|
-
defaultModeUI,
|
|
73
|
-
sizeModal,
|
|
74
|
-
defaultButtonText,
|
|
75
|
-
allowAdditionalCrops,
|
|
76
|
-
maxCrops
|
|
77
|
-
});
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
<!-- Limbo CSS -->
|
|
81
|
-
<link rel="stylesheet" href={assets.css} />
|
|
82
|
-
|
|
83
|
-
<!-- Limbo JS -->
|
|
84
|
-
<script is:inline src={assets.js}></script>
|
|
85
|
-
|
|
86
|
-
<!-- Limbo Initialization -->
|
|
87
|
-
<script is:inline set:html={initScript}></script>
|
package/src/limbo/index.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Limbo Integration Module
|
|
3
|
-
*
|
|
4
|
-
* Exportaciones para integrar limbo-component con la librería de page-builder.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Exportar funciones de inicialización
|
|
8
|
-
export {
|
|
9
|
-
generateLimboInitScript,
|
|
10
|
-
getLimboAssetUrls,
|
|
11
|
-
type LimboConfig,
|
|
12
|
-
type LimboInitOptions
|
|
13
|
-
} from './init';
|
|
14
|
-
|
|
15
|
-
// Exportar handler para API endpoint de token
|
|
16
|
-
export {
|
|
17
|
-
createLimboTokenHandler,
|
|
18
|
-
type LimboTokenConfig,
|
|
19
|
-
type LimboTokenResponse,
|
|
20
|
-
LIMBO_ENV_VARS
|
|
21
|
-
} from './tokenHandler';
|
|
22
|
-
|
|
23
|
-
// El componente Astro se importa directamente:
|
|
24
|
-
// import LimboProvider from 'libreria-astro-lefebvre/limbo/LimboProvider.astro';
|
package/src/limbo/init.ts
DELETED
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Limbo Integration Module for libreria-astro-lefebvre
|
|
3
|
-
*
|
|
4
|
-
* v2.0 - Actualizado para limbo-component v2.0
|
|
5
|
-
*
|
|
6
|
-
* Este módulo gestiona la inicialización y configuración de limbo-component
|
|
7
|
-
* para ser usado en cualquier portal que consuma esta librería.
|
|
8
|
-
*
|
|
9
|
-
* Soporta configuración via data attributes en inputs HTML:
|
|
10
|
-
* - data-mandatorycrops: JSON array de crops obligatorios
|
|
11
|
-
* - data-modeui: full | gallery-only | upload-only | crop-only
|
|
12
|
-
* - data-returntype: url | assetId | object | json | base64
|
|
13
|
-
* - data-allowadditionalcrops: true | false
|
|
14
|
-
* - data-maxcrops: number
|
|
15
|
-
* - data-buttontext: texto personalizado del botón
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
export interface MandatoryCrop {
|
|
19
|
-
label: string;
|
|
20
|
-
width?: number;
|
|
21
|
-
height?: number;
|
|
22
|
-
required?: boolean;
|
|
23
|
-
preset_aspect?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface LimboConfig {
|
|
27
|
-
publicKey: string;
|
|
28
|
-
prod?: boolean;
|
|
29
|
-
selector?: string;
|
|
30
|
-
defaultReturnType?: "url" | "assetId" | "object" | "json" | "base64";
|
|
31
|
-
defaultModeUI?: "full" | "gallery-only" | "upload-only" | "crop-only";
|
|
32
|
-
sizeModal?: "small" | "medium" | "large" | "xlarge" | "fullscreen";
|
|
33
|
-
defaultButtonText?: string;
|
|
34
|
-
allowAdditionalCrops?: boolean;
|
|
35
|
-
maxCrops?: number;
|
|
36
|
-
onSelect?: (data: any) => void;
|
|
37
|
-
onError?: (error: Error) => void;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface LimboInitOptions extends LimboConfig {
|
|
41
|
-
autoInit?: boolean;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Genera el script de inicialización de Limbo como string
|
|
46
|
-
* para ser inyectado en el HTML
|
|
47
|
-
*/
|
|
48
|
-
export function generateLimboInitScript(config: LimboConfig): string {
|
|
49
|
-
const {
|
|
50
|
-
publicKey,
|
|
51
|
-
prod = false,
|
|
52
|
-
selector = ".js-limbo",
|
|
53
|
-
defaultReturnType = "json",
|
|
54
|
-
defaultModeUI = "full",
|
|
55
|
-
sizeModal = "xlarge",
|
|
56
|
-
defaultButtonText = "Seleccionar imagen",
|
|
57
|
-
allowAdditionalCrops = true,
|
|
58
|
-
maxCrops = 10,
|
|
59
|
-
} = config;
|
|
60
|
-
|
|
61
|
-
// URL del proxy para evitar CORS (usa endpoint local del portal)
|
|
62
|
-
// El proxy está en /api/limbo-token del portal que consume la librería
|
|
63
|
-
const tokenProxyUrl = "/api/limbo-token";
|
|
64
|
-
|
|
65
|
-
return `
|
|
66
|
-
(function() {
|
|
67
|
-
// Estado del token
|
|
68
|
-
var cachedToken = null;
|
|
69
|
-
var tokenExpiry = null;
|
|
70
|
-
|
|
71
|
-
// Función para obtener token JWT (usa proxy local para evitar CORS)
|
|
72
|
-
async function getToken() {
|
|
73
|
-
// Si tenemos un token válido en caché, usarlo
|
|
74
|
-
if (cachedToken && tokenExpiry && Date.now() < tokenExpiry) {
|
|
75
|
-
return cachedToken;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
// Usamos el proxy local para evitar problemas de CORS
|
|
80
|
-
// Solo enviamos public_key - NO se requiere api_key
|
|
81
|
-
var response = await fetch('${tokenProxyUrl}', {
|
|
82
|
-
method: 'POST',
|
|
83
|
-
headers: {
|
|
84
|
-
'Content-Type': 'application/json',
|
|
85
|
-
},
|
|
86
|
-
body: JSON.stringify({
|
|
87
|
-
public_key: '${publicKey}'
|
|
88
|
-
})
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
var errorData = await response.json().catch(function() { return {}; });
|
|
93
|
-
throw new Error('Error obteniendo token: ' + response.status + ' - ' + (errorData.error || ''));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
var data = await response.json();
|
|
97
|
-
cachedToken = data.token;
|
|
98
|
-
// Guardar expiración con 30 segundos de margen
|
|
99
|
-
tokenExpiry = Date.now() + ((data.expires_in || 3600) * 1000) - 30000;
|
|
100
|
-
console.log('[Limbo] Token JWT obtenido correctamente');
|
|
101
|
-
return cachedToken;
|
|
102
|
-
} catch (error) {
|
|
103
|
-
console.error('[Limbo] Error en tokenProvider:', error);
|
|
104
|
-
throw error;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Estado de inicialización
|
|
109
|
-
var initRetrys = 0;
|
|
110
|
-
var maxRetrys = 20;
|
|
111
|
-
|
|
112
|
-
// Función global para re-escanear inputs (útil después de hydration de Vue/React)
|
|
113
|
-
window.LimboRescan = function(selector) {
|
|
114
|
-
var targetSelector = selector || '${selector}';
|
|
115
|
-
var inputs = document.querySelectorAll(targetSelector);
|
|
116
|
-
var processed = 0;
|
|
117
|
-
inputs.forEach(function(input) {
|
|
118
|
-
if (input.tagName === 'INPUT' && !input.dataset.limboProcessed) {
|
|
119
|
-
try {
|
|
120
|
-
Limbo.autoInputs._processInput(input);
|
|
121
|
-
input.dataset.limboProcessed = 'true';
|
|
122
|
-
processed++;
|
|
123
|
-
} catch (e) {
|
|
124
|
-
console.error('[Limbo] Error procesando input:', e);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
return processed;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// Esperamos a que Limbo esté disponible
|
|
132
|
-
function initLimbo() {
|
|
133
|
-
if (typeof Limbo === 'undefined') {
|
|
134
|
-
initRetrys++;
|
|
135
|
-
if (initRetrys <= maxRetrys) {
|
|
136
|
-
console.warn('[Limbo] Limbo no está cargado. Reintentando... (' + initRetrys + '/' + maxRetrys + ')');
|
|
137
|
-
setTimeout(initLimbo, 100);
|
|
138
|
-
} else {
|
|
139
|
-
console.error('[Limbo] No se pudo cargar Limbo después de ' + maxRetrys + ' intentos.');
|
|
140
|
-
}
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
console.log('[Limbo] Inicializando integración con Page Builder...');
|
|
145
|
-
|
|
146
|
-
// Configurar Limbo globalmente con tokenProvider como función
|
|
147
|
-
// IMPORTANTE: tokenProvider va en el nivel superior, no dentro de auth
|
|
148
|
-
Limbo.configure({
|
|
149
|
-
prod: ${prod},
|
|
150
|
-
publicKey: '${publicKey}',
|
|
151
|
-
authMode: 'jwt',
|
|
152
|
-
tokenProvider: getToken,
|
|
153
|
-
modal: {
|
|
154
|
-
size: '${sizeModal}'
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// Configurar auto-inputs para detectar elementos con el selector
|
|
159
|
-
Limbo.configureAutoInputs({
|
|
160
|
-
selector: '${selector}',
|
|
161
|
-
buttonText: '${defaultButtonText}',
|
|
162
|
-
returnType: '${defaultReturnType}',
|
|
163
|
-
modeUI: '${defaultModeUI}',
|
|
164
|
-
allowAdditionalCrops: ${allowAdditionalCrops},
|
|
165
|
-
maxCrops: ${maxCrops},
|
|
166
|
-
// 🔧 Los recortes se guardan en Limbo para garantizar persistencia
|
|
167
|
-
// Los recortes del page-builder se pueden identificar por su nombre (contienen dimensiones)
|
|
168
|
-
localCropsOnly: false,
|
|
169
|
-
|
|
170
|
-
// Parsear configuración del input desde datasets
|
|
171
|
-
// Los valores del dataset tienen prioridad sobre los defaults
|
|
172
|
-
parseInputConfig: function(input) {
|
|
173
|
-
var config = {};
|
|
174
|
-
|
|
175
|
-
// Parsear mandatoryCrops si existe
|
|
176
|
-
if (input.dataset.mandatorycrops) {
|
|
177
|
-
try {
|
|
178
|
-
config.mandatoryCrops = JSON.parse(input.dataset.mandatorycrops);
|
|
179
|
-
} catch (e) {
|
|
180
|
-
console.warn('[Limbo] Error parseando mandatoryCrops:', e);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Parsear returnType específico del input
|
|
185
|
-
if (input.dataset.returntype) {
|
|
186
|
-
config.returnType = input.dataset.returntype;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Parsear modo UI
|
|
190
|
-
if (input.dataset.modeui) {
|
|
191
|
-
config.modeUI = input.dataset.modeui;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Parsear allowAdditionalCrops
|
|
195
|
-
if (input.dataset.allowadditionalcrops !== undefined) {
|
|
196
|
-
config.allowAdditionalCrops = input.dataset.allowadditionalcrops === 'true';
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Parsear maxCrops
|
|
200
|
-
if (input.dataset.maxcrops) {
|
|
201
|
-
config.maxCrops = parseInt(input.dataset.maxcrops, 10);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Parsear buttonText personalizado
|
|
205
|
-
if (input.dataset.buttontext) {
|
|
206
|
-
config.buttonText = input.dataset.buttontext;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return config;
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
console.log('[Limbo] Integración configurada. Buscando inputs con selector: ${selector}');
|
|
214
|
-
|
|
215
|
-
// Debug: Verificar que el observer esté activo
|
|
216
|
-
console.log('[Limbo] AutoInputs stats:', Limbo.autoInputs?.getStats?.() || 'N/A');
|
|
217
|
-
|
|
218
|
-
// Debug: Escanear manualmente después de un delay para Vue/React hydration
|
|
219
|
-
setTimeout(function() {
|
|
220
|
-
var inputs = document.querySelectorAll('${selector}');
|
|
221
|
-
inputs.forEach(function(input, idx) {
|
|
222
|
-
// Forzar procesamiento si es un input válido
|
|
223
|
-
if (input.tagName === 'INPUT' && !input.dataset.limboProcessed) {
|
|
224
|
-
try {
|
|
225
|
-
Limbo.autoInputs._processInput(input);
|
|
226
|
-
input.dataset.limboProcessed = 'true';
|
|
227
|
-
} catch (e) {
|
|
228
|
-
console.error('[Limbo] Error procesando input:', e);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
}, 1000); // Esperar 1 segundo para Vue hydration
|
|
233
|
-
|
|
234
|
-
// Segunda pasada después de 2.5 segundos para componentes Vue más lentos
|
|
235
|
-
setTimeout(function() {
|
|
236
|
-
var inputs = document.querySelectorAll('${selector}');
|
|
237
|
-
var pendingInputs = Array.from(inputs).filter(function(input) {
|
|
238
|
-
return input.tagName === 'INPUT' && !input.dataset.limboProcessed;
|
|
239
|
-
});
|
|
240
|
-
if (pendingInputs.length > 0) {
|
|
241
|
-
pendingInputs.forEach(function(input) {
|
|
242
|
-
try {
|
|
243
|
-
Limbo.autoInputs._processInput(input);
|
|
244
|
-
input.dataset.limboProcessed = 'true';
|
|
245
|
-
} catch (e) {
|
|
246
|
-
console.error('[Limbo] Error procesando input:', e);
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
}, 2500);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Iniciar cuando el DOM esté listo
|
|
254
|
-
if (document.readyState === 'loading') {
|
|
255
|
-
document.addEventListener('DOMContentLoaded', initLimbo);
|
|
256
|
-
} else {
|
|
257
|
-
initLimbo();
|
|
258
|
-
}
|
|
259
|
-
})();
|
|
260
|
-
`;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Genera las URLs de los assets de Limbo (CSS y JS)
|
|
265
|
-
*/
|
|
266
|
-
export function getLimboAssetUrls(
|
|
267
|
-
options: { prod?: boolean; version?: string } = {}
|
|
268
|
-
) {
|
|
269
|
-
const { prod = false, version = "latest" } = options;
|
|
270
|
-
// En producción, usar CDN de npm o URL de producción
|
|
271
|
-
if (prod) {
|
|
272
|
-
return {
|
|
273
|
-
css: `https://unpkg.com/limbo-component@${version}/dist/limbo.css`,
|
|
274
|
-
js: `https://unpkg.com/limbo-component@${version}/dist/limbo.min.js`,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// En desarrollo, usar el build local desde public/limbo
|
|
279
|
-
return {
|
|
280
|
-
css: "/limbo/limbo.css",
|
|
281
|
-
js: "/limbo/limbo.min.js",
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export default {
|
|
286
|
-
generateLimboInitScript,
|
|
287
|
-
getLimboAssetUrls,
|
|
288
|
-
};
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Limbo Token Handler
|
|
3
|
-
*
|
|
4
|
-
* Handler genérico para obtener tokens JWT de Limbo.
|
|
5
|
-
* Framework-agnostic: funciona en Astro, Next.js, Express, etc.
|
|
6
|
-
*
|
|
7
|
-
* La URL de la API de Limbo se determina automáticamente según `isProduction`:
|
|
8
|
-
* - Producción: https://limbo.lefebvre.es
|
|
9
|
-
* - Desarrollo: https://led-dev-limbo-dev.eu.els.local
|
|
10
|
-
* - Local: http://localhost:8000
|
|
11
|
-
*
|
|
12
|
-
* @example Astro
|
|
13
|
-
* ```ts
|
|
14
|
-
* // src/pages/api/limbo-token.ts
|
|
15
|
-
* import { createLimboTokenHandler } from 'libreria-astro-lefebvre/limbo';
|
|
16
|
-
*
|
|
17
|
-
* export const POST = createLimboTokenHandler({
|
|
18
|
-
* publicKey: import.meta.env.PUBLIC_LIMBO_PUBLIC_KEY,
|
|
19
|
-
* isProduction: import.meta.env.IS_PROD === 'TRUE'
|
|
20
|
-
* });
|
|
21
|
-
* ```
|
|
22
|
-
*
|
|
23
|
-
* @example Next.js App Router
|
|
24
|
-
* ```ts
|
|
25
|
-
* // app/api/limbo-token/route.ts
|
|
26
|
-
* import { createLimboTokenHandler } from 'libreria-astro-lefebvre/limbo';
|
|
27
|
-
*
|
|
28
|
-
* export const POST = createLimboTokenHandler({
|
|
29
|
-
* publicKey: process.env.PUBLIC_LIMBO_PUBLIC_KEY!,
|
|
30
|
-
* isProduction: process.env.NODE_ENV === 'production'
|
|
31
|
-
* });
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
/** URLs por defecto de la API de Limbo */
|
|
36
|
-
const LIMBO_API_URLS = {
|
|
37
|
-
production: 'https://limbo.lefebvre.es',
|
|
38
|
-
development: 'https://led-dev-limbo-dev.eu.els.local',
|
|
39
|
-
} as const;
|
|
40
|
-
|
|
41
|
-
export interface LimboTokenConfig {
|
|
42
|
-
/** Public Key del portal de Limbo (pk_xxx) */
|
|
43
|
-
publicKey: string;
|
|
44
|
-
/** ¿Usar API de producción? (default: false = desarrollo) */
|
|
45
|
-
isProduction?: boolean;
|
|
46
|
-
/** URL base de la API de Limbo (opcional, se determina por isProduction si no se especifica) */
|
|
47
|
-
limboApiUrl?: string;
|
|
48
|
-
/** Permitir override de publicKey desde el body de la request (default: true) */
|
|
49
|
-
allowPublicKeyOverride?: boolean;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface LimboTokenResponse {
|
|
53
|
-
success: boolean;
|
|
54
|
-
token?: string;
|
|
55
|
-
expires_in?: number;
|
|
56
|
-
error?: string;
|
|
57
|
-
details?: string;
|
|
58
|
-
hint?: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Crea un handler para obtener tokens JWT de Limbo.
|
|
63
|
-
*
|
|
64
|
-
* Este handler actúa como proxy para evitar problemas de CORS,
|
|
65
|
-
* haciendo la petición a Limbo desde el servidor.
|
|
66
|
-
*
|
|
67
|
-
* @param config - Configuración con publicKey e isProduction
|
|
68
|
-
* @returns Handler compatible con Astro, Next.js, y otros frameworks
|
|
69
|
-
*/
|
|
70
|
-
export function createLimboTokenHandler(config: LimboTokenConfig) {
|
|
71
|
-
const {
|
|
72
|
-
publicKey: defaultPublicKey,
|
|
73
|
-
isProduction = false,
|
|
74
|
-
limboApiUrl: customApiUrl,
|
|
75
|
-
allowPublicKeyOverride = true,
|
|
76
|
-
} = config;
|
|
77
|
-
|
|
78
|
-
// Determinar URL de la API (custom > isProduction > desarrollo)
|
|
79
|
-
const limboApiUrl = customApiUrl || (isProduction ? LIMBO_API_URLS.production : LIMBO_API_URLS.development);
|
|
80
|
-
|
|
81
|
-
// Validar configuración al crear el handler
|
|
82
|
-
if (!defaultPublicKey) {
|
|
83
|
-
console.error('[Limbo Token Handler] PUBLIC_LIMBO_PUBLIC_KEY no está configurada');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
console.log(`[Limbo Token Handler] Configurado para ${isProduction ? 'PRODUCCIÓN' : 'DESARROLLO'}: ${limboApiUrl}`);
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Handler que procesa la petición de token
|
|
90
|
-
*/
|
|
91
|
-
return async function handler({ request }: { request: Request }): Promise<Response> {
|
|
92
|
-
try {
|
|
93
|
-
// Determinar qué publicKey usar
|
|
94
|
-
let publicKey = defaultPublicKey;
|
|
95
|
-
|
|
96
|
-
// Permitir override desde el body si está habilitado
|
|
97
|
-
if (allowPublicKeyOverride) {
|
|
98
|
-
try {
|
|
99
|
-
const body = await request.json();
|
|
100
|
-
if (body.public_key) {
|
|
101
|
-
publicKey = body.public_key;
|
|
102
|
-
}
|
|
103
|
-
} catch {
|
|
104
|
-
// Si no hay body válido, usar la del entorno
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Validar que tenemos publicKey
|
|
109
|
-
if (!publicKey) {
|
|
110
|
-
return new Response(
|
|
111
|
-
JSON.stringify({
|
|
112
|
-
success: false,
|
|
113
|
-
error: 'PUBLIC_LIMBO_PUBLIC_KEY no configurada',
|
|
114
|
-
} satisfies LimboTokenResponse),
|
|
115
|
-
{
|
|
116
|
-
status: 400,
|
|
117
|
-
headers: { 'Content-Type': 'application/json' },
|
|
118
|
-
}
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Construir URL del endpoint de token
|
|
123
|
-
const tokenUrl = `${limboApiUrl}/auth/token`;
|
|
124
|
-
console.log('[Limbo Proxy] Requesting token from:', tokenUrl);
|
|
125
|
-
console.log('[Limbo Proxy] Using public_key:', publicKey.substring(0, 10) + '...');
|
|
126
|
-
|
|
127
|
-
// Hacer petición a Limbo
|
|
128
|
-
const response = await fetch(tokenUrl, {
|
|
129
|
-
method: 'POST',
|
|
130
|
-
headers: {
|
|
131
|
-
'Content-Type': 'application/json',
|
|
132
|
-
},
|
|
133
|
-
body: JSON.stringify({
|
|
134
|
-
public_key: publicKey,
|
|
135
|
-
}),
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Manejar errores de la API
|
|
139
|
-
if (!response.ok) {
|
|
140
|
-
const errorText = await response.text();
|
|
141
|
-
console.error('[Limbo Proxy] Error response:', response.status, errorText);
|
|
142
|
-
|
|
143
|
-
return new Response(
|
|
144
|
-
JSON.stringify({
|
|
145
|
-
success: false,
|
|
146
|
-
error: `API error: ${response.status}`,
|
|
147
|
-
details: errorText,
|
|
148
|
-
} satisfies LimboTokenResponse),
|
|
149
|
-
{
|
|
150
|
-
status: response.status,
|
|
151
|
-
headers: { 'Content-Type': 'application/json' },
|
|
152
|
-
}
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Respuesta exitosa
|
|
157
|
-
const data = await response.json();
|
|
158
|
-
console.log('[Limbo Proxy] Token obtained successfully');
|
|
159
|
-
|
|
160
|
-
return new Response(JSON.stringify(data), {
|
|
161
|
-
status: 200,
|
|
162
|
-
headers: { 'Content-Type': 'application/json' },
|
|
163
|
-
});
|
|
164
|
-
} catch (error) {
|
|
165
|
-
console.error('[Limbo Proxy] Error completo:', error);
|
|
166
|
-
|
|
167
|
-
// Mensaje descriptivo para debug
|
|
168
|
-
let errorMsg = 'Unknown error';
|
|
169
|
-
if (error instanceof Error) {
|
|
170
|
-
errorMsg = error.message;
|
|
171
|
-
if (error.cause) {
|
|
172
|
-
errorMsg += ` (cause: ${JSON.stringify(error.cause)})`;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return new Response(
|
|
177
|
-
JSON.stringify({
|
|
178
|
-
success: false,
|
|
179
|
-
error: errorMsg,
|
|
180
|
-
hint: 'Verifica que la API de Limbo sea accesible desde el servidor',
|
|
181
|
-
} satisfies LimboTokenResponse),
|
|
182
|
-
{
|
|
183
|
-
status: 500,
|
|
184
|
-
headers: { 'Content-Type': 'application/json' },
|
|
185
|
-
}
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* URLs por defecto de la API de Limbo (exportadas para referencia).
|
|
193
|
-
*/
|
|
194
|
-
export { LIMBO_API_URLS };
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Helper para crear configuración desde variables de entorno comunes.
|
|
198
|
-
* Útil para documentación y ejemplos.
|
|
199
|
-
*/
|
|
200
|
-
export const LIMBO_ENV_VARS = {
|
|
201
|
-
/** Variable de entorno para la Public Key (con prefijo PUBLIC_ para cliente) */
|
|
202
|
-
PUBLIC_KEY: 'PUBLIC_LIMBO_PUBLIC_KEY',
|
|
203
|
-
/** Variable de entorno para indicar si es producción */
|
|
204
|
-
IS_PROD: 'IS_PROD',
|
|
205
|
-
} as const;
|
|
206
|
-
|
|
207
|
-
export default createLimboTokenHandler;
|