Cette application exemple démontre l'intégration avec Quable PIM via une Quable App "Custom" (non intégrée à l'app Store).
- ✅ Configuration automatique de webhook
custom_app.helloworld - ✅ Configuration automatique d'une clé/valeur
custom_app.helloworld - ✅ Gestion de clé/valeur dans le PIM à la réception d'un event webhook
- ✅ Support de tous les slots de documents
- ✅ Validation HMAC pour sécurité
- Node.js >= 16.0.0
- Instance Quable PIM
- Token API Quable avec permissions full_access
git clone https://github.com/quable/customapp-helloworld.git
cd customapp-helloworldnpm install
# ou
yarn installcp .env.dist .envÉditer .env :
DATABASE_URL=file:./dev.db
QUABLE_APP_PORT=4000
QUABLE_APP_HOST_URL=https://votre-url.comPour l'URL, n'hésitez pas à utilier un tunnel pour que le PIM puisse joidnre votre application lancée localement.
npx prisma migrate dev --name init
npx prisma generate- Créer une nouvelle CustomApp
- Renseigner bien l'URL public de votre application
- Cocher TOUS les slots disponibles
- Noter le secret pour la validation HMAC
npx prisma studio// Mode développement
npm run dev
// Mode production
npm run build && npm start| Endpoint | Méthode | Description |
|---|---|---|
/ |
GET | Page de configuration |
/ |
POST | Lancement de slot |
/webhook/{instance} |
POST | Réception webhooks |
/slot/{slotName} |
GET | Affichage slot iframe |
-
Accès à la page de configuration depuis l'administration des Quable App
- L'app vérifie/crée le webhook
custom_app.helloworld - L'app vérifie/crée la key/value
custom_app.helloworld
- L'app vérifie/crée le webhook
-
Webhook document.update
- Réception du webhook
- Mise à jour key/value avec datetime
-
Affichage slots
- Validation HMAC de la requête
- Retour URL iframe
- Affichage "Hello World"
- Validation HMAC sur tous les endpoints
- Tokens stockés en base de données
- Utilisation de
timingSafeEqualcontre timing attacks
{
"statusCode": 500,
"message": "Invalid `databaseService.quableInstance.findFirst()` invocation...
Inconsistent column data: Conversion failed: premature end of input"
}L'erreur provient de la base de données SQLite qui contient des données corrompues ou mal formatées dans la table quable_instance.
# 1. Arrêter l'application
# 2. Sauvegarder l'ancienne base (optionnel)
cp database/dev.db database/dev.db.backup
# 3. Supprimer la base corrompue
rm database/dev.db
# 4. Recréer la base avec Prisma
npx prisma migrate dev --name init
npx prisma generate
# 5. Ajouter une instance de test
npx prisma studio
# OU utiliser un script SQLCréer un fichier scripts/init-db.js:
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
async function initDatabase() {
try {
// Nettoyer la table existante
await prisma.quableInstance.deleteMany();
// Créer une instance de test
const instance = await prisma.quableInstance.create({
data: {
name: 'test-instance',
authToken: 'your-api-token-here',
quableAppSecret: 'your-hmac-secret-here'
}
});
console.log('✅ Base de données initialisée avec succès');
console.log('Instance créée:', instance);
} catch (error) {
console.error('❌ Erreur lors de l\'initialisation:', error);
} finally {
await prisma.$disconnect();
}
}
initDatabase();Puis exécuter:
node scripts/init-db.jsVérifier que database/schema.prisma est correct:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model QuableInstance {
id Int @id @default(autoincrement())
name String
authToken String
quableAppSecret String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("quable_instance")
}# 1. Nettoyer tout
rm -rf database/dev.db
rm -rf node_modules/.prisma
rm -rf node_modules/@prisma
# 2. Réinstaller les dépendances
npm install
# 3. Générer Prisma
npx prisma generate
# 4. Créer la base
npx prisma db push
# 5. Vérifier avec Prisma Studio
npx prisma studioCréer scripts/test-db.js:
const { PrismaClient } = require('@prisma/client');
async function testDatabase() {
const prisma = new PrismaClient({
log: ['query', 'error', 'warn']
});
try {
// Test de connexion
await prisma.$connect();
console.log('✅ Connexion à la base réussie');
// Test de lecture
const count = await prisma.quableInstance.count();
console.log(`📊 Nombre d'instances: ${count}`);
// Test de création
const testInstance = await prisma.quableInstance.create({
data: {
name: `test-${Date.now()}`,
authToken: 'test-token',
quableAppSecret: 'test-secret'
}
});
console.log('✅ Instance créée:', testInstance.name);
// Test de lecture après création
const instances = await prisma.quableInstance.findMany();
console.log(`✅ ${instances.length} instance(s) trouvée(s)`);
// Nettoyage
await prisma.quableInstance.delete({
where: { id: testInstance.id }
});
console.log('✅ Instance de test supprimée');
} catch (error) {
console.error('❌ Erreur:', error);
} finally {
await prisma.$disconnect();
}
}
testDatabase();.env:
DATABASE_URL="file:./dev.db"
# Chemin relatif depuis la racine du projet# Vérifier les permissions du dossier database
ls -la database/
# Si nécessaire, corriger les permissions
chmod 755 database/
chmod 644 database/dev.db# Installer sqlite3 si nécessaire
npm install sqlite3
# Vérifier l'intégrité de la base
sqlite3 database/dev.db "PRAGMA integrity_check;"- Données de Production: Si c'est en production, sauvegarder TOUJOURS avant modification
- Tokens Sensibles: Ne pas commiter les vrais tokens dans le code
- Migration: Utiliser les migrations Prisma pour tracer les changements
Après correction, tester avec:
# 1. Lancer l'application
npm run dev
# 2. Tester l'endpoint
curl -X POST http://localhost:4000/ \
-H "Content-Type: application/json" \
-H "X-Signature: [signature]" \
-H "X-Timestamp: [timestamp]" \
-d '{
"instance": "test-instance",
"slot": "document.page.tab",
"data": {
"dataLocale": "fr_FR",
"interfaceLocale": "fr_FR",
"userId": "123"
}
}'MIT - Quable 2025