1246 lines
42 KiB
JavaScript
1246 lines
42 KiB
JavaScript
const { app, BrowserWindow, ipcMain } = require('electron')
|
|
const path = require('path')
|
|
const axios = require('axios')
|
|
const fs = require('fs')
|
|
const os = require('os')
|
|
|
|
// Variable globale pour stocker les données de session
|
|
let sessionData = {
|
|
token: null,
|
|
refreshToken: null,
|
|
apiUrl: null,
|
|
userData: null,
|
|
tokenExpiresAt: null,
|
|
// Données pour le refresh automatique
|
|
authData: {
|
|
tenantCode: null,
|
|
username: null,
|
|
password: null // Note: stocker le mot de passe n'est pas idéal pour la sécurité
|
|
}
|
|
}
|
|
|
|
// Chemin pour sauvegarder le token
|
|
const tokenFilePath = path.join(os.homedir(), '.janitor-session.json')
|
|
|
|
// Charger les données de session depuis le fichier
|
|
function loadSessionData() {
|
|
try {
|
|
if (fs.existsSync(tokenFilePath)) {
|
|
const data = fs.readFileSync(tokenFilePath, 'utf8')
|
|
sessionData = JSON.parse(data)
|
|
console.log('Données de session chargées')
|
|
}
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement des données de session:', error)
|
|
}
|
|
}
|
|
|
|
// Sauvegarder les données de session dans un fichier
|
|
function saveSessionData() {
|
|
try {
|
|
fs.writeFileSync(tokenFilePath, JSON.stringify(sessionData, null, 2))
|
|
console.log('Données de session sauvegardées')
|
|
} catch (error) {
|
|
console.error('Erreur lors de la sauvegarde des données de session:', error)
|
|
}
|
|
}
|
|
|
|
// Nettoyer les données de session
|
|
function clearSessionData() {
|
|
sessionData = {
|
|
token: null,
|
|
refreshToken: null,
|
|
apiUrl: null,
|
|
userData: null,
|
|
tokenExpiresAt: null,
|
|
authData: {
|
|
tenantCode: null,
|
|
username: null,
|
|
password: null
|
|
}
|
|
}
|
|
try {
|
|
if (fs.existsSync(tokenFilePath)) {
|
|
fs.unlinkSync(tokenFilePath)
|
|
console.log('Fichier de session supprimé')
|
|
}
|
|
} catch (error) {
|
|
console.error('Erreur lors de la suppression du fichier de session:', error)
|
|
}
|
|
}
|
|
|
|
// Fonction pour faire le refresh du token
|
|
async function refreshToken() {
|
|
if (!sessionData.refreshToken || !sessionData.apiUrl) {
|
|
console.log('Pas de refresh token disponible')
|
|
return { success: false, message: 'Pas de refresh token disponible' }
|
|
}
|
|
|
|
try {
|
|
// Préparer les données au format x-www-form-urlencoded
|
|
const params = new URLSearchParams()
|
|
params.append('grant_type', 'refresh_token')
|
|
params.append('refresh_token', sessionData.refreshToken)
|
|
params.append('forcerevoke', '1') // Optionnel selon la doc
|
|
|
|
// Utiliser le même authString que pour l'authentification initiale
|
|
const authString = "R05BOklFNGU3aXFoZHQ="
|
|
|
|
// Préparer l'URL
|
|
let apiUrl = sessionData.apiUrl
|
|
if (apiUrl.startsWith('http://')) {
|
|
apiUrl = apiUrl.replace('http://', 'https://')
|
|
} else if (!apiUrl.startsWith('https://')) {
|
|
apiUrl = 'https://' + apiUrl
|
|
}
|
|
|
|
console.log('Tentative de refresh du token...')
|
|
|
|
// Faire l'appel API de refresh
|
|
const response = await axios.post(
|
|
`${apiUrl}/EasySTS/OAuth/Token`,
|
|
params,
|
|
{
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'Authorization': `Basic ${authString}`
|
|
},
|
|
timeout: 30000,
|
|
httpsAgent: new (require('https').Agent)({
|
|
rejectUnauthorized: false
|
|
})
|
|
}
|
|
)
|
|
|
|
console.log('Refresh réussi:', response.data)
|
|
|
|
// Calculer la nouvelle date d'expiration
|
|
const expiresIn = response.data.expires_in || 3600
|
|
const expiresAt = new Date(Date.now() + (expiresIn * 1000))
|
|
|
|
// Mettre à jour les données de session
|
|
sessionData.token = response.data.access_token || response.data.token
|
|
sessionData.refreshToken = response.data.refresh_token || sessionData.refreshToken // Garder l'ancien si pas de nouveau
|
|
sessionData.tokenExpiresAt = expiresAt
|
|
sessionData.userData = response.data
|
|
|
|
// Sauvegarder la session mise à jour
|
|
saveSessionData()
|
|
|
|
console.log('Token refreshé avec succès, expire le:', expiresAt)
|
|
|
|
return {
|
|
success: true,
|
|
token: sessionData.token,
|
|
expiresAt: expiresAt,
|
|
message: 'Token refreshé avec succès'
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors du refresh du token:', error)
|
|
|
|
if (error.response) {
|
|
console.log('Détails de l\'erreur refresh:', error.response.data)
|
|
return {
|
|
success: false,
|
|
message: `Erreur refresh ${error.response.status}: ${error.response.data.message || error.response.data.error_description || 'Refresh échoué'}`,
|
|
error: error.response.data
|
|
}
|
|
} else if (error.request) {
|
|
return {
|
|
success: false,
|
|
message: 'Impossible de contacter le serveur pour le refresh',
|
|
error: 'Network error'
|
|
}
|
|
} else {
|
|
return {
|
|
success: false,
|
|
message: 'Erreur de configuration refresh',
|
|
error: error.message
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function createWindow() {
|
|
const win = new BrowserWindow({
|
|
width: 1400,
|
|
height: 900,
|
|
icon: path.join(__dirname, 'images', 'logo.ico'),
|
|
autoHideMenuBar: true,
|
|
webPreferences: {
|
|
nodeIntegration: false,
|
|
contextIsolation: true,
|
|
preload: path.join(__dirname, 'preload.js')
|
|
}
|
|
})
|
|
|
|
win.loadFile('index.html')
|
|
return win
|
|
}
|
|
|
|
// Fonction pour faire l'appel API d'authentification
|
|
async function authenticateUser(formData) {
|
|
try {
|
|
// Préparer les données au format x-www-form-urlencoded
|
|
const params = new URLSearchParams()
|
|
params.append('grant_type', 'password')
|
|
params.append('tenant_code', formData.tenantCode)
|
|
params.append('username', formData.user)
|
|
params.append('password', formData.password)
|
|
|
|
// Utiliser l'authString fourni (avec le = manquant)
|
|
const authString = "R05BOklFNGU3aXFoZHQ="
|
|
|
|
console.log('Paramètres envoyés:', {
|
|
grant_type: 'password',
|
|
tenant_code: formData.tenantCode,
|
|
username: formData.user,
|
|
password: '***' // Masquer le mot de passe dans les logs
|
|
})
|
|
|
|
// Préparer l'URL pour l'appel API
|
|
let apiUrl = formData.url // CORRECTION: utiliser formData.url au lieu de data.url
|
|
// Forcer HTTPS quoi qu'il arrive
|
|
if (apiUrl.startsWith('http://')) {
|
|
apiUrl = apiUrl.replace('http://', 'https://')
|
|
} else if (!apiUrl.startsWith('https://')) {
|
|
apiUrl = 'https://' + apiUrl
|
|
}
|
|
console.log("apiUrl : "+ apiUrl)
|
|
// Faire l'appel API
|
|
const response = await axios.post(
|
|
`${apiUrl}/EasySTS/OAuth/Token`, // Endpoint corrigé
|
|
params,
|
|
{
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'Authorization': `Basic ${authString}`
|
|
},
|
|
timeout: 30000, // Timeout de 30 secondes
|
|
httpsAgent: new (require('https').Agent)({
|
|
rejectUnauthorized: false // Ignore les certificats SSL non vérifiés
|
|
})
|
|
}
|
|
)
|
|
|
|
console.log('Réponse API:', response.data)
|
|
|
|
// Calculer la date d'expiration du token
|
|
const expiresIn = response.data.expires_in || 3600 // Par défaut 1 heure si non spécifié
|
|
const expiresAt = new Date(Date.now() + (expiresIn * 1000)) // Convertir en millisecondes
|
|
|
|
return {
|
|
success: true,
|
|
token: response.data.access_token || response.data.token,
|
|
refreshToken: response.data.refresh_token, // Peut être undefined si pas fourni
|
|
expiresAt: expiresAt,
|
|
message: 'Authentification réussie',
|
|
data: response.data
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de l\'authentification:', error)
|
|
|
|
if (error.response) {
|
|
// Le serveur a répondu avec un code d'erreur
|
|
console.log('Détails de l\'erreur:', error.response.data)
|
|
return {
|
|
success: false,
|
|
message: `Erreur ${error.response.status}: ${error.response.data.message || error.response.data.error_description || 'Authentification échouée'}`,
|
|
error: error.response.data
|
|
}
|
|
} else if (error.request) {
|
|
// La requête a été faite mais pas de réponse
|
|
return {
|
|
success: false,
|
|
message: 'Impossible de contacter le serveur',
|
|
error: 'Network error'
|
|
}
|
|
} else {
|
|
// Erreur dans la configuration de la requête
|
|
return {
|
|
success: false,
|
|
message: 'Erreur de configuration',
|
|
error: error.message
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Écouter les données du formulaire
|
|
ipcMain.on('form-data', async (event, data) => {
|
|
console.log('Données reçues du formulaire:', {
|
|
url: data.url,
|
|
tenantCode: data.tenantCode,
|
|
user: data.user,
|
|
password: '***' // Masquer le mot de passe
|
|
})
|
|
|
|
// Valider les données
|
|
if (!data.url || !data.user || !data.password) {
|
|
event.reply('form-response', {
|
|
success: false,
|
|
message: 'Tous les champs sont requis'
|
|
})
|
|
return
|
|
}
|
|
|
|
// Valider l'URL (accepte les IPs et domaines)
|
|
try {
|
|
// Ajouter http:// si pas de protocole
|
|
let urlToTest = data.url
|
|
if (!urlToTest.startsWith('http://') && !urlToTest.startsWith('https://')) {
|
|
urlToTest = 'http://' + urlToTest
|
|
}
|
|
new URL(urlToTest)
|
|
} catch (e) {
|
|
event.reply('form-response', {
|
|
success: false,
|
|
message: 'URL invalide'
|
|
})
|
|
return
|
|
}
|
|
|
|
// Envoyer une réponse immédiate pour indiquer le traitement
|
|
event.reply('form-response', {
|
|
success: true,
|
|
message: 'Authentification en cours...',
|
|
loading: true
|
|
})
|
|
|
|
// Faire l'appel API
|
|
const authResult = await authenticateUser(data)
|
|
|
|
// Envoyer le résultat final
|
|
event.reply('form-response', authResult)
|
|
|
|
// Si l'authentification réussit, rediriger vers le dashboard
|
|
if (authResult.success && !authResult.loading) {
|
|
// Stocker les données de session
|
|
sessionData.token = authResult.token
|
|
sessionData.refreshToken = authResult.refreshToken
|
|
sessionData.apiUrl = data.url // Correction: utiliser data.url au lieu de formData.url
|
|
sessionData.userData = authResult.data
|
|
sessionData.tokenExpiresAt = authResult.expiresAt
|
|
|
|
// Stocker les données d'authentification pour le refresh automatique
|
|
sessionData.authData.tenantCode = data.tenantCode
|
|
sessionData.authData.username = data.user
|
|
sessionData.authData.password = data.password // Note: pas idéal pour la sécurité
|
|
|
|
// Sauvegarder la session
|
|
saveSessionData()
|
|
|
|
console.log('Token expire le:', sessionData.tokenExpiresAt)
|
|
|
|
// Attendre un petit délai pour que l'utilisateur voie le message de succès
|
|
setTimeout(() => {
|
|
const win = BrowserWindow.getFocusedWindow()
|
|
if (win) {
|
|
win.loadFile('dashboard.html')
|
|
}
|
|
}, 1000) // Délai d'1 seconde
|
|
}
|
|
})
|
|
|
|
app.whenReady().then(() => {
|
|
// Charger les données de session au démarrage
|
|
loadSessionData()
|
|
|
|
createWindow()
|
|
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) {
|
|
createWindow()
|
|
}
|
|
})
|
|
})
|
|
|
|
app.on('window-all-closed', () => {
|
|
if (process.platform !== 'darwin') {
|
|
app.quit()
|
|
}
|
|
})
|
|
|
|
// Fonction pour vérifier la validité du token et refresh automatique
|
|
async function verifyToken() {
|
|
if (!sessionData.token || !sessionData.tokenExpiresAt) {
|
|
return { valid: false, message: 'Aucun token trouvé' }
|
|
}
|
|
|
|
// Vérifier si le token a expiré en comparant avec la date actuelle
|
|
const now = new Date()
|
|
const expiresAt = new Date(sessionData.tokenExpiresAt)
|
|
const timeRemaining = Math.floor((expiresAt - now) / 1000) // en secondes
|
|
|
|
// Si le token expire dans moins de 3 minutes (180 secondes), essayer de le refresh
|
|
if (timeRemaining <= 180 && timeRemaining > 0) {
|
|
console.log(`Token expire dans ${timeRemaining} secondes, tentative de refresh...`)
|
|
|
|
const refreshResult = await refreshToken()
|
|
if (refreshResult.success) {
|
|
console.log('Refresh automatique réussi')
|
|
// Recalculer le temps restant avec le nouveau token
|
|
const newExpiresAt = new Date(sessionData.tokenExpiresAt)
|
|
const newTimeRemaining = Math.floor((newExpiresAt - now) / 1000)
|
|
|
|
return {
|
|
valid: true,
|
|
message: 'Token refreshé automatiquement',
|
|
expiresAt: newExpiresAt,
|
|
timeRemaining: newTimeRemaining,
|
|
refreshed: true
|
|
}
|
|
} else {
|
|
console.log('Échec du refresh automatique:', refreshResult.message)
|
|
// Le refresh a échoué, mais le token est peut-être encore valide pour quelques minutes
|
|
if (timeRemaining > 0) {
|
|
return {
|
|
valid: true,
|
|
message: `Token valide encore ${timeRemaining}s, refresh échoué`,
|
|
expiresAt: expiresAt,
|
|
timeRemaining: timeRemaining,
|
|
refreshFailed: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si le token a expiré
|
|
if (now >= expiresAt) {
|
|
console.log('Token expiré:', {
|
|
maintenant: now.toISOString(),
|
|
expiration: expiresAt.toISOString()
|
|
})
|
|
return { valid: false, message: 'Token expiré' }
|
|
}
|
|
|
|
// Token encore valide (plus de 3 minutes restantes)
|
|
console.log(`Token valide encore ${timeRemaining} secondes`)
|
|
|
|
return {
|
|
valid: true,
|
|
message: 'Token valide',
|
|
expiresAt: expiresAt,
|
|
timeRemaining: timeRemaining
|
|
}
|
|
}
|
|
|
|
// Écouter les demandes de vérification de token
|
|
ipcMain.handle('verify-token', async () => {
|
|
const result = await verifyToken()
|
|
|
|
if (!result.valid) {
|
|
// Nettoyer les données de session
|
|
clearSessionData()
|
|
}
|
|
|
|
return result
|
|
})
|
|
|
|
// Écouter les demandes de refresh forcé du token
|
|
ipcMain.handle('force-refresh-token', async () => {
|
|
try {
|
|
const result = await refreshToken()
|
|
return result
|
|
} catch (error) {
|
|
console.error('Erreur lors du refresh forcé:', error)
|
|
return {
|
|
success: false,
|
|
message: error.message
|
|
}
|
|
}
|
|
})
|
|
|
|
// Écouter les demandes de déconnexion
|
|
ipcMain.on('logout', () => {
|
|
// Nettoyer les données de session
|
|
clearSessionData()
|
|
|
|
// Rediriger vers la page de connexion sans paramètre (session déjà nettoyée)
|
|
const win = BrowserWindow.getFocusedWindow()
|
|
if (win) {
|
|
win.loadFile('index.html')
|
|
}
|
|
})
|
|
|
|
// Écouter les demandes de redirection avec message d'erreur
|
|
ipcMain.on('show-token-expired', (event, message) => {
|
|
// Nettoyer les données de session
|
|
clearSessionData()
|
|
|
|
// Rediriger vers la page de connexion avec message
|
|
const win = BrowserWindow.getFocusedWindow()
|
|
if (win) {
|
|
// Stocker le message pour l'afficher sur la page de connexion
|
|
win.webContents.once('dom-ready', () => {
|
|
win.webContents.executeJavaScript(`
|
|
window.tokenExpiredMessage = "${message}";
|
|
`)
|
|
})
|
|
win.loadFile('index.html')
|
|
}
|
|
})
|
|
|
|
// Fonction pour exécuter une query de count sur une entité
|
|
async function executeCountQuery(entityName, whereClause = null) {
|
|
if (!sessionData.token || !sessionData.apiUrl) {
|
|
throw new Error('Pas de token ou d\'URL API disponible')
|
|
}
|
|
|
|
try {
|
|
let apiUrl = sessionData.apiUrl
|
|
if (apiUrl.startsWith('http://')) {
|
|
apiUrl = apiUrl.replace('http://', 'https://')
|
|
} else if (!apiUrl.startsWith('https://')) {
|
|
apiUrl = 'https://' + apiUrl
|
|
}
|
|
|
|
// Construire l'expression avec ou sans clause where
|
|
let expression = `Context.${entityName}`
|
|
if (whereClause) {
|
|
expression += `.Where${whereClause}`
|
|
}
|
|
expression += '.Count()'
|
|
|
|
const queryData = {
|
|
"Application": "EasyWMS",
|
|
"QueryType": 1,
|
|
"Expression": expression
|
|
}
|
|
|
|
console.log(`Exécution query count pour ${entityName}${whereClause ? ' avec where' : ''}:`, queryData)
|
|
|
|
const response = await axios.post(
|
|
`${apiUrl}/ApplicationService/api/QueryScalarExecute`,
|
|
queryData,
|
|
{
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${sessionData.token}`
|
|
},
|
|
timeout: 30000,
|
|
httpsAgent: new (require('https').Agent)({
|
|
rejectUnauthorized: false
|
|
})
|
|
}
|
|
)
|
|
|
|
// Log de la réponse complète du serveur pour les WHERE personnalisés
|
|
if (whereClause) {
|
|
console.log('=== RÉPONSE SERVEUR COMPLÈTE ===')
|
|
console.log(`URL: ${apiUrl}/ApplicationService/api/QueryScalarExecute`)
|
|
console.log(`Query Data:`, queryData)
|
|
console.log(`Status: ${response.status}`)
|
|
console.log(`Headers:`, response.headers)
|
|
console.log(`Data:`, JSON.stringify(response.data, null, 2))
|
|
console.log('===============================')
|
|
}
|
|
|
|
// Extraire la valeur selon le format spécifié
|
|
const count = response.data.Rows?.[0]?.Values?.Column
|
|
|
|
return {
|
|
success: true,
|
|
entity: entityName,
|
|
count: count || 0,
|
|
data: response.data
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`=== ERREUR EXECUTE COUNT QUERY ===`)
|
|
console.error(`Erreur lors du count ${entityName}${whereClause ? ' avec where' : ''}:`, error)
|
|
console.error(`Type d'erreur: ${error.name}`)
|
|
console.error(`Message: ${error.message}`)
|
|
console.error(`Stack: ${error.stack}`)
|
|
|
|
if (error.response?.data) {
|
|
console.error('Détails erreur API:', error.response.data)
|
|
console.error('Status:', error.response.status)
|
|
console.error('Headers:', error.response.headers)
|
|
}
|
|
console.error('=================================')
|
|
|
|
return {
|
|
success: false,
|
|
entity: entityName,
|
|
count: 0,
|
|
error: {
|
|
message: error.message,
|
|
response: error.response?.data,
|
|
status: error.response?.status,
|
|
stack: error.stack,
|
|
name: error.name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fonction pour charger la liste des entités depuis le fichier JSON
|
|
function loadEntitiesList() {
|
|
try {
|
|
const entitiesPath = path.join(__dirname, 'entities.json')
|
|
const data = fs.readFileSync(entitiesPath, 'utf8')
|
|
const entitiesConfig = JSON.parse(data)
|
|
|
|
// Trier les entités par ordre alphabétique sur le displayName
|
|
const sortedEntities = entitiesConfig.entities.sort((a, b) =>
|
|
a.displayName.localeCompare(b.displayName)
|
|
)
|
|
|
|
return sortedEntities
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement des entités:', error)
|
|
// Fallback sur la liste hardcodée en cas d'erreur (triée alphabétiquement)
|
|
const fallbackEntities = [
|
|
{ id: "Accounts", name: "Accounts", displayName: "Accounts" },
|
|
{ id: "Aliases", name: "Aliases", displayName: "Aliases" },
|
|
{ id: "Containers", name: "Containers", displayName: "Containers" },
|
|
{ id: "InboundOrders", name: "InboundOrders", displayName: "Inbound Orders" },
|
|
{ id: "Kits", name: "Kits", displayName: "Kits" },
|
|
{ id: "OutboundOrders", name: "OutboundOrders", displayName: "Outbound Orders" },
|
|
{ id: "ProductLocations", name: "ProductLocations", displayName: "Product Locations" },
|
|
{ id: "Products", name: "Products", displayName: "Products" },
|
|
{ id: "Receptions", name: "Receptions", displayName: "Receptions" },
|
|
{ id: "Stocks", name: "Stocks", displayName: "Stocks" },
|
|
{ id: "Suppliers", name: "Suppliers", displayName: "Suppliers" },
|
|
{ id: "Tasks", name: "Tasks", displayName: "Tasks" }
|
|
]
|
|
|
|
return fallbackEntities
|
|
}
|
|
}
|
|
|
|
// Fonction pour récupérer tous les counts
|
|
async function getAllEntityCounts() {
|
|
const entities = loadEntitiesList()
|
|
const results = {}
|
|
|
|
// Créer la liste de toutes les queries à exécuter (entité principale + where clauses)
|
|
const queries = []
|
|
|
|
entities.forEach(entity => {
|
|
// Query principale
|
|
queries.push({
|
|
entity: entity,
|
|
key: entity.name,
|
|
whereClause: null
|
|
})
|
|
|
|
// Queries pour les clauses where
|
|
let whereIndex = 1
|
|
while (entity[`where${whereIndex}`]) {
|
|
queries.push({
|
|
entity: entity,
|
|
key: `${entity.name}-where${whereIndex}`,
|
|
whereClause: entity[`where${whereIndex}`]
|
|
})
|
|
whereIndex++
|
|
}
|
|
})
|
|
|
|
// Exécuter toutes les queries en parallèle
|
|
const promises = queries.map(query => executeCountQuery(query.entity.name, query.whereClause))
|
|
const responses = await Promise.allSettled(promises)
|
|
|
|
responses.forEach((response, index) => {
|
|
const query = queries[index]
|
|
const entity = query.entity
|
|
|
|
if (response.status === 'fulfilled') {
|
|
results[query.key] = {
|
|
...response.value,
|
|
displayName: entity.displayName,
|
|
description: entity.description
|
|
}
|
|
} else {
|
|
results[query.key] = {
|
|
success: false,
|
|
entity: entity.name,
|
|
displayName: entity.displayName,
|
|
description: entity.description,
|
|
count: 0,
|
|
error: response.reason?.message || 'Erreur inconnue'
|
|
}
|
|
}
|
|
})
|
|
|
|
return results
|
|
}
|
|
|
|
// Écouter les demandes de count d'entités
|
|
ipcMain.handle('get-entity-counts', async () => {
|
|
try {
|
|
// Vérifier d'abord que le token est valide
|
|
const tokenCheck = await verifyToken()
|
|
if (!tokenCheck.valid) {
|
|
return {
|
|
success: false,
|
|
error: 'Token invalide ou expiré'
|
|
}
|
|
}
|
|
|
|
const counts = await getAllEntityCounts()
|
|
return {
|
|
success: true,
|
|
counts: counts,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la récupération des counts:', error)
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
}
|
|
}
|
|
})
|
|
|
|
// Écouter les demandes de la liste des entités
|
|
ipcMain.handle('get-entities-list', () => {
|
|
try {
|
|
const entities = loadEntitiesList()
|
|
return {
|
|
success: true,
|
|
entities: entities
|
|
}
|
|
} catch (error) {
|
|
console.error('Erreur lors de la récupération de la liste des entités:', error)
|
|
return {
|
|
success: false,
|
|
error: error.message,
|
|
entities: []
|
|
}
|
|
}
|
|
})
|
|
|
|
// Écouter les demandes de count personnalisées
|
|
ipcMain.handle('execute-custom-count', async (event, entityName, whereClause) => {
|
|
try {
|
|
console.log('=== IPC CUSTOM COUNT ===')
|
|
console.log(`Entité: ${entityName}`)
|
|
console.log(`Clause WHERE: ${whereClause}`)
|
|
|
|
// Vérifier d'abord que le token est valide
|
|
const tokenCheck = await verifyToken()
|
|
if (!tokenCheck.valid) {
|
|
console.log('Token invalide, arrêt du processus')
|
|
return {
|
|
success: false,
|
|
error: 'Token invalide ou expiré'
|
|
}
|
|
}
|
|
|
|
console.log('Token valide, exécution de la query...')
|
|
const result = await executeCountQuery(entityName, whereClause)
|
|
console.log('Résultat de executeCountQuery:', result)
|
|
console.log('=====================')
|
|
|
|
return result
|
|
|
|
} catch (error) {
|
|
console.error('=== ERREUR IPC CUSTOM COUNT ===')
|
|
console.error('Erreur lors de l\'exécution du count personnalisé:', error)
|
|
console.error('Stack:', error.stack)
|
|
console.error('==============================')
|
|
|
|
return {
|
|
success: false,
|
|
error: {
|
|
message: error.message,
|
|
stack: error.stack,
|
|
name: error.name
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
// Écouter les demandes de récupération des IDs avec filtre WHERE personnalisé
|
|
ipcMain.handle('get-filtered-entity-ids', async (event, entityName, component, whereClause) => {
|
|
try {
|
|
// Vérifier d'abord que le token est valide
|
|
const tokenCheck = await verifyToken()
|
|
if (!tokenCheck.valid) {
|
|
return {
|
|
success: false,
|
|
error: 'Token invalide ou expiré',
|
|
ids: []
|
|
}
|
|
}
|
|
|
|
const result = await getEntityIds(entityName, component, null, whereClause)
|
|
return result
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la récupération des IDs filtrés:', error)
|
|
return {
|
|
success: false,
|
|
error: error.message,
|
|
ids: []
|
|
}
|
|
}
|
|
})
|
|
|
|
// Écouter les demandes d'informations de session
|
|
ipcMain.handle('get-session-info', () => {
|
|
try {
|
|
return {
|
|
success: true,
|
|
apiUrl: sessionData.apiUrl || null,
|
|
tenantCode: sessionData.authData.tenantCode || null,
|
|
username: sessionData.authData.username || null
|
|
}
|
|
} catch (error) {
|
|
console.error('Erreur lors de la récupération des infos de session:', error)
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
}
|
|
}
|
|
})
|
|
|
|
// Fonction pour récupérer les IDs d'une entité
|
|
async function getEntityIds(entityName, component, whereIndex = null, customWhereClause = null) {
|
|
if (!sessionData.token || !sessionData.apiUrl) {
|
|
throw new Error('Pas de token ou d\'URL API disponible')
|
|
}
|
|
|
|
try {
|
|
let apiUrl = sessionData.apiUrl
|
|
if (apiUrl.startsWith('http://')) {
|
|
apiUrl = apiUrl.replace('http://', 'https://')
|
|
} else if (!apiUrl.startsWith('https://')) {
|
|
apiUrl = 'https://' + apiUrl
|
|
}
|
|
|
|
// Construire l'expression avec ou sans clause where
|
|
let expression = `Context.${entityName}`
|
|
|
|
// Si customWhereClause est spécifié, l'utiliser en priorité
|
|
if (customWhereClause) {
|
|
expression += `.Where${customWhereClause}`
|
|
}
|
|
// Sinon, si whereIndex est spécifié, ajouter la clause where correspondante
|
|
else if (whereIndex && whereIndex > 0) {
|
|
const entities = loadEntitiesList()
|
|
const entity = entities.find(e => e.name === entityName)
|
|
if (entity && entity[`where${whereIndex}`]) {
|
|
expression += `.Where${entity[`where${whereIndex}`]}`
|
|
}
|
|
}
|
|
|
|
expression += '.Select(z => z.Id)'
|
|
|
|
const queryData = {
|
|
"Application": "EasyWMS",
|
|
"QueryType": 1,
|
|
"Expression": expression
|
|
}
|
|
|
|
console.log(`Récupération des IDs pour ${entityName}:`, queryData)
|
|
|
|
const response = await axios.post(
|
|
`${apiUrl}/ApplicationService/api/QueryExecute`,
|
|
queryData,
|
|
{
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${sessionData.token}`
|
|
},
|
|
timeout: 30000, // Timeout plus long pour les requêtes de suppression
|
|
httpsAgent: new (require('https').Agent)({
|
|
rejectUnauthorized: false
|
|
})
|
|
}
|
|
)
|
|
|
|
console.log(`Réponse IDs ${entityName}:`, response.data)
|
|
|
|
// Extraire les IDs depuis la réponse
|
|
const ids = []
|
|
if (response.data.Table && response.data.Table.Rows && Array.isArray(response.data.Table.Rows)) {
|
|
response.data.Table.Rows.forEach(row => {
|
|
if (row.Values && row.Values.Column !== undefined && row.Values.Column !== null) {
|
|
ids.push(row.Values.Column)
|
|
}
|
|
})
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
ids: ids,
|
|
count: ids.length
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`Erreur lors de la récupération des IDs ${entityName}:`, error)
|
|
|
|
return {
|
|
success: false,
|
|
ids: [],
|
|
error: error.response?.data?.message || error.message
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fonction pour récupérer toutes les données d'une entité pour l'export
|
|
async function getEntityExportData(entityName, component, customWhereClause = null) {
|
|
if (!sessionData.token || !sessionData.apiUrl) {
|
|
throw new Error('Pas de token ou d\'URL API disponible')
|
|
}
|
|
|
|
try {
|
|
let apiUrl = sessionData.apiUrl
|
|
if (apiUrl.startsWith('http://')) {
|
|
apiUrl = apiUrl.replace('http://', 'https://')
|
|
} else if (!apiUrl.startsWith('https://')) {
|
|
apiUrl = 'https://' + apiUrl
|
|
}
|
|
|
|
// Construire l'expression pour récupérer toutes les données (pas juste les IDs)
|
|
let expression = `Context.${entityName}`
|
|
|
|
// Si customWhereClause est spécifié, l'utiliser
|
|
if (customWhereClause) {
|
|
expression += `.Where${customWhereClause}`
|
|
}
|
|
|
|
// Pas de .Select() pour récupérer toutes les colonnes
|
|
|
|
const queryData = {
|
|
"Application": "EasyWMS",
|
|
"QueryType": 1,
|
|
"Expression": expression
|
|
}
|
|
|
|
console.log(`Récupération des données d'export pour ${entityName}:`, queryData)
|
|
|
|
const response = await axios.post(
|
|
`${apiUrl}/ApplicationService/api/QueryExecute`,
|
|
queryData,
|
|
{
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${sessionData.token}`
|
|
},
|
|
timeout: 60000, // Timeout plus long pour les exports
|
|
httpsAgent: new (require('https').Agent)({
|
|
rejectUnauthorized: false
|
|
})
|
|
}
|
|
)
|
|
|
|
console.log(`Réponse données d'export ${entityName}:`, response.data)
|
|
|
|
// Extraire les données depuis la réponse
|
|
const data = []
|
|
if (response.data.Table && response.data.Table.Rows && Array.isArray(response.data.Table.Rows)) {
|
|
const columns = response.data.Table.Columns || []
|
|
|
|
response.data.Table.Rows.forEach(row => {
|
|
const rowData = {}
|
|
if (row.Values && typeof row.Values === 'object') {
|
|
// Si row.Values est un objet, itérer sur ses propriétés
|
|
for (const [key, value] of Object.entries(row.Values)) {
|
|
rowData[key] = value
|
|
}
|
|
} else if (Array.isArray(row.Values)) {
|
|
// Si row.Values est un tableau, mapper avec les noms de colonnes
|
|
columns.forEach((column, index) => {
|
|
rowData[column.Name || `Column${index}`] = row.Values[index]
|
|
})
|
|
}
|
|
data.push(rowData)
|
|
})
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
data: data,
|
|
count: data.length
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`Erreur lors de la récupération des données d'export ${entityName}:`, error)
|
|
|
|
return {
|
|
success: false,
|
|
data: [],
|
|
error: error.response?.data?.message || error.message
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fonction pour supprimer un élément d'une entité
|
|
async function deleteEntityItem(entity, itemId) {
|
|
if (!sessionData.token || !sessionData.apiUrl) {
|
|
throw new Error('Pas de token ou d\'URL API disponible')
|
|
}
|
|
|
|
try {
|
|
let apiUrl = sessionData.apiUrl
|
|
if (apiUrl.startsWith('http://')) {
|
|
apiUrl = apiUrl.replace('http://', 'https://')
|
|
} else if (!apiUrl.startsWith('https://')) {
|
|
apiUrl = 'https://' + apiUrl
|
|
}
|
|
|
|
// Construire la commande de suppression
|
|
const commandData = [{
|
|
"Name": `${entity.remove_command}, Mecalux.ITSW.EasyWMS.Modules.Contracts`,
|
|
"Properties": {
|
|
[entity.component]: itemId
|
|
}
|
|
}]
|
|
|
|
// Ajouter ForceDeletionOfEmptyHolderContainer si c'est un ComponentId
|
|
if (entity.component === 'ComponentId') {
|
|
commandData[0].Properties.ForceDeletionOfEmptyHolderContainer = true
|
|
}
|
|
|
|
console.log(`Suppression de l'élément ${itemId} pour ${entity.name}:`, commandData)
|
|
|
|
const response = await axios.post(
|
|
`${apiUrl}/ApplicationService/api/CommandExecute`,
|
|
commandData,
|
|
{
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${sessionData.token}`
|
|
},
|
|
timeout: 30000,
|
|
httpsAgent: new (require('https').Agent)({
|
|
rejectUnauthorized: false
|
|
})
|
|
}
|
|
)
|
|
|
|
console.log(`Réponse suppression ${entity.name} ID ${itemId}:`, response.data)
|
|
|
|
return {
|
|
success: true,
|
|
itemId: itemId,
|
|
response: response.data
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`Erreur lors de la suppression ${entity.name} ID ${itemId}:`, error)
|
|
|
|
let errorMessage = error.message
|
|
|
|
// Extraire le message d'erreur depuis la réponse du serveur
|
|
if (error.response && error.response.data) {
|
|
try {
|
|
// Si c'est une string JSON, la parser
|
|
let errorData = error.response.data
|
|
if (typeof errorData === 'string') {
|
|
// Cas où il peut y avoir plusieurs objets JSON concaténés
|
|
const jsonMatch = errorData.match(/\{[^}]*"Message":"([^"]*)"[^}]*\}/)
|
|
if (jsonMatch) {
|
|
errorMessage = jsonMatch[1]
|
|
} else {
|
|
errorData = JSON.parse(errorData)
|
|
errorMessage = errorData.Message || errorData.message || errorMessage
|
|
}
|
|
} else if (errorData.Message) {
|
|
// Si c'est déjà un objet avec le champ Message
|
|
errorMessage = errorData.Message
|
|
} else if (errorData.message) {
|
|
errorMessage = errorData.message
|
|
}
|
|
} catch (parseError) {
|
|
console.error('Erreur lors du parsing de la réponse d\'erreur:', parseError)
|
|
// Garder le message d'erreur original si le parsing échoue
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: false,
|
|
itemId: itemId,
|
|
error: errorMessage
|
|
}
|
|
}
|
|
}
|
|
|
|
// Écouter les demandes de récupération des IDs
|
|
ipcMain.handle('get-entity-ids', async (event, entityName, component, whereIndex = null) => {
|
|
try {
|
|
// Vérifier d'abord que le token est valide
|
|
const tokenCheck = await verifyToken()
|
|
if (!tokenCheck.valid) {
|
|
return {
|
|
success: false,
|
|
error: 'Token invalide ou expiré',
|
|
ids: []
|
|
}
|
|
}
|
|
|
|
const result = await getEntityIds(entityName, component, whereIndex)
|
|
return result
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la récupération des IDs:', error)
|
|
return {
|
|
success: false,
|
|
error: error.message,
|
|
ids: []
|
|
}
|
|
}
|
|
})
|
|
|
|
// Handler pour récupérer les données d'export d'une entité
|
|
ipcMain.handle('get-entity-export-data', async (event, entityName, component, whereClause = null) => {
|
|
try {
|
|
// Vérifier d'abord que le token est valide
|
|
const tokenCheck = await verifyToken()
|
|
if (!tokenCheck.valid) {
|
|
return {
|
|
success: false,
|
|
error: 'Token invalide ou expiré',
|
|
data: []
|
|
}
|
|
}
|
|
|
|
const result = await getEntityExportData(entityName, component, whereClause)
|
|
return result
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la récupération des données d\'export:', error)
|
|
return {
|
|
success: false,
|
|
error: error.message,
|
|
data: []
|
|
}
|
|
}
|
|
})
|
|
|
|
// Écouter les demandes de suppression d'éléments
|
|
ipcMain.handle('delete-entity-item', async (event, entity, itemId) => {
|
|
try {
|
|
// Vérifier d'abord que le token est valide
|
|
const tokenCheck = await verifyToken()
|
|
if (!tokenCheck.valid) {
|
|
return {
|
|
success: false,
|
|
error: 'Token invalide ou expiré'
|
|
}
|
|
}
|
|
|
|
const result = await deleteEntityItem(entity, itemId)
|
|
return result
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la suppression de l\'élément:', error)
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
}
|
|
}
|
|
})
|
|
|
|
// Fonction pour exécuter une action btn1 sur un élément d'une entité
|
|
async function executeBtn1EntityAction(entity, itemId) {
|
|
if (!sessionData.token || !sessionData.apiUrl) {
|
|
throw new Error('Pas de token ou d\'URL API disponible')
|
|
}
|
|
|
|
try {
|
|
let apiUrl = sessionData.apiUrl
|
|
if (apiUrl.startsWith('http://')) {
|
|
apiUrl = apiUrl.replace('http://', 'https://')
|
|
} else if (!apiUrl.startsWith('https://')) {
|
|
apiUrl = 'https://' + apiUrl
|
|
}
|
|
|
|
// Construire la commande btn1
|
|
const commandData = [{
|
|
"Name": `${entity.btn1_command}, Mecalux.ITSW.EasyWMS.Modules.Contracts`,
|
|
"Properties": {
|
|
[entity.component]: itemId
|
|
}
|
|
}]
|
|
|
|
console.log(`Exécution ${entity.btn1} de l'élément ${itemId} pour ${entity.name}:`, commandData)
|
|
|
|
const response = await axios.post(
|
|
`${apiUrl}/ApplicationService/api/CommandExecute`,
|
|
commandData,
|
|
{
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${sessionData.token}`
|
|
},
|
|
timeout: 30000,
|
|
httpsAgent: new (require('https').Agent)({
|
|
rejectUnauthorized: false
|
|
})
|
|
}
|
|
)
|
|
|
|
console.log(`Réponse ${entity.btn1} ${entity.name} ID ${itemId}:`, response.data)
|
|
|
|
return {
|
|
success: true,
|
|
itemId: itemId,
|
|
response: response.data
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`Erreur lors de ${entity.btn1} ${entity.name} ID ${itemId}:`, error)
|
|
|
|
let errorMessage = error.message
|
|
|
|
// Extraire le message d'erreur depuis la réponse du serveur
|
|
if (error.response && error.response.data) {
|
|
try {
|
|
let errorData = error.response.data
|
|
if (typeof errorData === 'string') {
|
|
const jsonMatch = errorData.match(/\{[^}]*"Message":"([^"]*)"[^}]*\}/)
|
|
if (jsonMatch) {
|
|
errorMessage = jsonMatch[1]
|
|
} else {
|
|
errorData = JSON.parse(errorData)
|
|
errorMessage = errorData.Message || errorData.message || errorMessage
|
|
}
|
|
} else if (errorData.Message) {
|
|
errorMessage = errorData.Message
|
|
} else if (errorData.message) {
|
|
errorMessage = errorData.message
|
|
}
|
|
} catch (parseError) {
|
|
console.error('Erreur lors du parsing de la réponse d\'erreur:', parseError)
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: false,
|
|
itemId: itemId,
|
|
error: errorMessage
|
|
}
|
|
}
|
|
}
|
|
|
|
// Écouter les demandes d'exécution d'actions btn1
|
|
ipcMain.handle('execute-btn1-action', async (event, entity, itemId) => {
|
|
try {
|
|
// Vérifier d'abord que le token est valide
|
|
const tokenCheck = await verifyToken()
|
|
if (!tokenCheck.valid) {
|
|
return {
|
|
success: false,
|
|
error: 'Token invalide ou expiré'
|
|
}
|
|
}
|
|
|
|
const result = await executeBtn1EntityAction(entity, itemId)
|
|
return result
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de l\'exécution de l\'action btn1:', error)
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
}
|
|
}
|
|
}) |