Recetas de integración

Siete casos habituales de component-limbo, con código que puedes copiar.

🔑 Token desde tu backend (producción)

Problema: dar token al navegador sin exponer credenciales. Tu backend expone un endpoint propio (aquí /api/limbo-token) que llama a POST https://limbo.lefebvre.es/api/v1/auth/token con { "public_key": "pk_de_tu_api_key" } y devuelve al navegador { token, expires_in }.

Cachea el token hasta poco antes de expires_in: /api/v1/auth/token tiene límite de peticiones y responde 429 con la cabecera Retry-After si te pasas.

Node.js (Express, backend)
let cached = null;   // { token, expiresAt }

app.get('/api/limbo-token', async (req, res) => {
    const now = Date.now();
    if (!cached || cached.expiresAt - 30000 <= now) {
        const response = await fetch('https://limbo.lefebvre.es/api/v1/auth/token', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ public_key: process.env.LIMBO_PUBLIC_KEY })
        });
        if (!response.ok) {
            const retryAfter = response.headers.get('Retry-After');
            if (retryAfter) res.set('Retry-After', retryAfter);
            return res.status(response.status).json({ error: 'limbo_token_failed' });
        }
        const { data } = await response.json();   // v1: { success, data: { token, expires_in, … } }
        cached = { token: data.token, expiresAt: now + data.expires_in * 1000 };
    }
    res.json({
        token: cached.token,
        expires_in: Math.floor((cached.expiresAt - now) / 1000)
    });
});
JavaScript (navegador)
Limbo.configure({
    publicKey: 'pk_tu_clave_publica',
    prod: true,
    authMode: 'jwt',
    tokenProvider: async () => {
        const res = await fetch('/api/limbo-token');
        return await res.json();   // { token, expires_in }
    }
});

Más sobre los tokens de la API en la documentación de autenticación y sobre los modos del componente en la configuración.

🧲 Limitar tipos de fichero

Problema: aceptar solo ciertos ficheros. validation.accept usa la sintaxis del atributo accept de HTML; los métodos prefab (openGallery, openUploader, createInlineGallery, createFullSelector...) lo aceptan directamente como opción accept. Sin él, se permite todo.

JavaScript
// Solo imágenes
Limbo.create({ container: '#a', validation: { accept: 'image/*' } });

// Solo PDF
Limbo.create({ container: '#b', validation: { accept: '.pdf' } });

// Imágenes o PDF
Limbo.create({ container: '#c', validation: { accept: 'image/*,.pdf' } });

Valores habituales: image/*, video/*, audio/*, document/* o extensiones como .pdf,.docx. Si accept excluye las imágenes, el componente oculta las pestañas de generación con IA, de stock y de otros portales, y desactiva el recortador. En un input de AutoInputs se indica con data-accept (atributos).

✂️ Recortes obligatorios

Problema: exigir dimensiones fijas (por ejemplo, un banner en escritorio y móvil) al elegir una imagen. Se definen en cropper.mandatoryCrops. Cada recorte admite label, width, height, required y format; si no fijas width y height a la vez, el recorte es libre y puedes acotarlo con minWidth, minHeight, maxWidth y maxHeight.

JavaScript
Limbo.create({
    container: '#banner-selector',
    mode: 'modal',
    modeUI: 'full',
    cropper: {
        mandatoryCrops: [
            { label: 'Desktop', width: 1920, height: 400, required: true },
            { label: 'Mobile', width: 640, height: 300, required: true },
            { label: 'Libre', maxWidth: 500, required: false }
        ],
        allowCustomCrops: false
    },
    callbacks: {
        onCropsSaved: (data) => {
            data.crops.forEach((crop) => console.log(crop.name, crop.url, crop.width, crop.height));
        }
    }
});

Para recortar una imagen que ya tienes, pasa su URL a Limbo.openCropper(imageUrl, options):

JavaScript
Limbo.openCropper('https://example.com/foto.jpg', {
    mandatoryCrops: [
        { label: 'Thumbnail', width: 300, height: 300, required: true }
    ],
    onComplete: (data) => console.log('Recortes:', data.crops),
    onCancelled: () => console.log('Cancelado'),
    onError: (error) => console.error(error)
});

📝 Rellenar un input de formulario (AutoInputs)

Problema: que un formulario existente guarde la URL o el ID del fichero sin escribir el selector a mano. Marca el input con data-limbo-input-file; el componente oculta el input, añade un botón y escribe el resultado en él.

HTML + JavaScript
<input type="hidden" name="avatar" data-limbo-input-file
    data-accept="image/*" data-returntype="url">

// Tras Limbo.configure({ ... })
Limbo.configureAutoInputs({
    buttonText: 'Elegir imagen'
});

const input = document.querySelector('input[name="avatar"]');
input.addEventListener('limbo:resultReady', (e) => {
    console.log('Valor del input:', e.detail.value);
});

Todos los atributos data-* están en la configuración y el detalle del evento en el catálogo de eventos.

📁 Subir a una carpeta y limitar carpetas

Problema: ordenar los ficheros por carpetas del portal. Con folders.defaultFolder las subidas van a una carpeta; con include o exclude limitas las carpetas visibles, por slug. allowCreateFolder permite crear carpetas nuevas.

JavaScript
// Galería limitada a dos carpetas
Limbo.create({
    container: '#limbo',
    mode: 'embed',
    modeUI: 'full',
    folders: {
        showFolderFilter: true,
        showFolderInfo: true,
        allowMoveFolder: true,
        allowCreateFolder: true,
        include: ['productos', 'marketing']
        // exclude: ['privado']
    }
});

// Subida que cae en una carpeta por defecto
Limbo.create({
    container: '#uploader',
    mode: 'embed',
    modeUI: 'upload-only',
    folders: {
        showFolderSelector: true,
        defaultFolder: 'marketing'
    }
});

🚀 Próximos Pasos