From 07973fbf0abeee335583be410422825407bd7b74 Mon Sep 17 00:00:00 2001 From: Surya Paolo Date: Sat, 4 May 2024 14:49:02 +0200 Subject: [PATCH] - catalogo avanti, parte 1 --- package.json | 1 + src/server/middleware/authenticate.js | 1 + src/server/models/author.js | 54 +++ src/server/models/circuit.js | 4 +- src/server/models/product.js | 18 +- src/server/models/productInfo.js | 23 +- src/server/models/publisher.js | 50 +++ src/server/models/user.js | 19 ++ src/server/router/admin_router.js | 451 +++++++++++++++++--------- src/server/router/index_router.js | 9 +- src/server/router/users_router.js | 68 +++- src/server/tools/general.js | 29 +- src/server/tools/shared_nodejs.js | 24 ++ yarn.lock | 59 +++- 14 files changed, 639 insertions(+), 171 deletions(-) create mode 100755 src/server/models/author.js create mode 100755 src/server/models/publisher.js diff --git a/package.json b/package.json index 55632bd..72fe81f 100755 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "pug": "^3.0.2", "rate-limiter-flexible": "^2.3.9", "request": "^2.34", + "sanitize-html": "^2.13.0", "save": "^2.5.0", "sharp": "^0.30.7", "superagent": "^8.0.0", diff --git a/src/server/middleware/authenticate.js b/src/server/middleware/authenticate.js index b44cbae..59bf7b4 100755 --- a/src/server/middleware/authenticate.js +++ b/src/server/middleware/authenticate.js @@ -48,6 +48,7 @@ const authenticate = (req, res, next) => { if (!!ris.user) { // Save last time online ris.user.lasttimeonline = new Date(); + ris.user.retry_pwd = 0 ris.user.useragent = req.get('User-Agent'); return ris.user.save().then(() => { diff --git a/src/server/models/author.js b/src/server/models/author.js new file mode 100755 index 0000000..de7711d --- /dev/null +++ b/src/server/models/author.js @@ -0,0 +1,54 @@ +mongoose = require('mongoose').set('debug', false) +const Schema = mongoose.Schema; + +const tools = require('../tools/general'); + +mongoose.Promise = global.Promise; +mongoose.level = "F"; + +// Resolving error Unknown modifier: $pushAll +mongoose.plugin(schema => { + schema.options.usePushEach = true +}); + +const AuthorSchema = new Schema({ + idapp: { + type: String, + }, + name: { + type: String, + }, + surname: { + type: String, + }, + bio: { + type: String, + }, + img: { + type: String, + }, +}); + +var Author = module.exports = mongoose.model('Author', AuthorSchema); + +module.exports.getFieldsForSearch = function () { + return [ + { field: 'name', type: tools.FieldType.string }, + { field: 'surname', type: tools.FieldType.string }, + ] +}; + +module.exports.executeQueryTable = function (idapp, params) { + params.fieldsearch = this.getFieldsForSearch(); + return tools.executeQueryTable(this, idapp, params); +}; + +module.exports.findAllIdApp = async function (idapp) { + const myfind = { idapp }; + + return await Author.find(myfind); +}; + +module.exports.createIndexes((err) => { + if (err) throw err; +}); diff --git a/src/server/models/circuit.js b/src/server/models/circuit.js index 1c3c4e7..b67fc98 100755 --- a/src/server/models/circuit.js +++ b/src/server/models/circuit.js @@ -743,8 +743,8 @@ CircuitSchema.statics.sendCoins = async function (onlycheck, idapp, usernameOrig }; await Circuit.updateOne({ _id: circuittable }, { $set: paramstoupdate }); - extrarec.saldoOrig = accountorigTable.saldo; - extrarec.saldoDest = accountdestTable.saldo; + extrarec.saldoOrig = tools.arrotondaA2Decimali(accountorigTable.saldo); + extrarec.saldoDest = tools.arrotondaA2Decimali(accountdestTable.saldo); let orig = usernameOrig; if (extrarec.grouporig) { diff --git a/src/server/models/product.js b/src/server/models/product.js index 65cd789..e6143fb 100755 --- a/src/server/models/product.js +++ b/src/server/models/product.js @@ -33,6 +33,9 @@ const productSchema = new Schema({ type: Boolean, default: true, }, + isbn: { + type: String, + }, idProductInfo: { type: Schema.Types.ObjectId, ref: 'ProductInfo' }, idProducer: { type: Schema.Types.ObjectId, ref: 'Producer' }, idStorehouses: [ @@ -146,6 +149,9 @@ const productSchema = new Schema({ note: { type: String, }, + versione: { + type: Number, + }, producer_name: { type: String, }, @@ -346,6 +352,14 @@ module.exports.findAllIdApp = async function (idapp, code, id, all) { preserveNullAndEmptyArrays: true, }, }, + { + $lookup: { + from: 'authors', + localField: 'productInfo.idAuthors', + foreignField: '_id', + as: 'productInfo.authors' + } + }, { $lookup: { from: 'catprods', @@ -836,8 +850,8 @@ module.exports.singlerecconvert_AfterImport_AndSave = async function (idapp, pro ris = await Product.updateOne({ _id: ObjectID(prod._id) }, { $unset: objDelete }) - if (ris) { - console.log('ris', ris); + if (ris && ris.nModified > 0) { + console.log('Modificato: ', objtoset.name); } // const campodarimuovere = 'producer_name'; diff --git a/src/server/models/productInfo.js b/src/server/models/productInfo.js index 73fb05d..c211b14 100755 --- a/src/server/models/productInfo.js +++ b/src/server/models/productInfo.js @@ -88,6 +88,9 @@ const productInfoSchema = new Schema({ img3: { type: String, }, + img4: { + type: String, + }, ingredienti: { type: String, }, @@ -97,18 +100,20 @@ const productInfoSchema = new Schema({ note: { type: String, }, - author: { - type: String, - }, + idAuthors: [{ type: Schema.Types.ObjectId, ref: 'Author' }], + idPublisher: { type: Schema.Types.ObjectId, ref: 'Publisher' }, collezione: { type: String, }, - publisher: { //editore - type: String, + date_publishing: { + type: Date, }, numpages: { type: Number, }, + productType: { + type: Number, + }, }); @@ -163,6 +168,14 @@ module.exports.findAllIdApp = async function (idapp, code, id) { as: 'catprods' } }, + { + $lookup: { + from: 'authors', + localField: 'idAuthors', + foreignField: '_id', + as: 'authors' + } + }, { $lookup: { from: 'subcatprods', diff --git a/src/server/models/publisher.js b/src/server/models/publisher.js new file mode 100755 index 0000000..bf70c9c --- /dev/null +++ b/src/server/models/publisher.js @@ -0,0 +1,50 @@ +mongoose = require('mongoose').set('debug', false) +const Schema = mongoose.Schema; + +const tools = require('../tools/general'); + +mongoose.Promise = global.Promise; +mongoose.level = "F"; + +// Resolving error Unknown modifier: $pushAll +mongoose.plugin(schema => { + schema.options.usePushEach = true +}); + +const PublisherSchema = new Schema({ + idapp: { + type: String, + }, + name: { + type: String, + }, + link: { + type: String, + }, + img: { + type: String, + }, +}); + +var Publisher = module.exports = mongoose.model('Publisher', PublisherSchema); + +module.exports.getFieldsForSearch = function () { + return [ + { field: 'name', type: tools.FieldType.string }, + ] +}; + +module.exports.executeQueryTable = function (idapp, params) { + params.fieldsearch = this.getFieldsForSearch(); + return tools.executeQueryTable(this, idapp, params); +}; + +module.exports.findAllIdApp = async function (idapp) { + const myfind = { idapp }; + + return await Publisher.find(myfind); +}; + +module.exports.createIndexes((err) => { + if (err) throw err; +}); diff --git a/src/server/models/user.js b/src/server/models/user.js index 27fe497..3d82944 100755 --- a/src/server/models/user.js +++ b/src/server/models/user.js @@ -5721,6 +5721,25 @@ UserSchema.statics.tooManyReqPassword = async function (idapp, email, set) { } }; +UserSchema.statics.tooManyLoginWrong = async function (idapp, username, set) { + const User = this; + + const maxnum = 30; + + const user = await User.findByUsername(idapp, username, true, false); + if (user) { + if (!user.retry_pwd) + user.retry_pwd = 0 + if (set && user.retry_pwd <= maxnum) { + user.retry_pwd++; + + await User.findOneAndUpdate({ _id: user._id }, { $set: { retry_pwd: user.retry_pwd } }); + } + return {troppilogin: user.retry_pwd > maxnum, retry_pwd: user.retry_pwd}; + } + + return {troppilogin: false, retry_pwd: 0}; +}; UserSchema.statics.setLastCircuitOpened = async function (idapp, username, circuitpath) { diff --git a/src/server/router/admin_router.js b/src/server/router/admin_router.js index abb1799..a8de147 100755 --- a/src/server/router/admin_router.js +++ b/src/server/router/admin_router.js @@ -13,6 +13,8 @@ const Inventariogm = require('../models/inventariogm'); const Importamacro = require('../models/importamacro'); const ProductInfo = require('../models/productInfo'); const CatProd = require('../models/catprod'); +const Author = require('../models/author'); +const Publisher = require('../models/publisher'); const SubCatProd = require('../models/subcatprod'); const Gasordine = require('../models/gasordine'); @@ -113,6 +115,11 @@ async function completaSettaggioProduct_AndProductInfo(arrcampi_productInfo, arr productInfo.code = preccode; } } + if (rec.hasOwnProperty('productType')) { + productInfo.productType = rec.hasOwnProperty('productType'); + } else { + productInfo.productType = shared_consts.PRODUCTTYPE.PRODUCT; + } return { product, productInfo }; @@ -215,6 +222,17 @@ async function extractArrayDataFromCSV(idapp, rec) { }; +function extractNameAndSurnameByComplete(name_complete) { + + if (name_complete) { + const name = name_complete.split(' ')[0]; + const surname = name_complete.split(' ')[1]; + return { name, surname }; + } else { + return { name: '', surname: '' }; + } +} + router.post('/import', authenticate, async (req, res) => { try { @@ -263,7 +281,7 @@ router.post('/import', authenticate, async (req, res) => { if (dataObjects && dataObjects[0]) { // Cancella la collection ImportaMacros await Importamacro.deleteMany({ idapp }); - + // Aggiungi i record su ImportaMacros for (const recinv of dataObjects[0]) { let isnuovo = false @@ -297,191 +315,317 @@ router.post('/import', authenticate, async (req, res) => { if (cancella_categorie) { await CatProd.deleteMany({ idapp }); } - + // Rileggi tutti i record di ImportaMacros dataObjects = await Importamacro.find({ idapp }).lean(); + console.log('*** INIZIO IMPORT PRODOTTI ... '); + + let indprod = 0; + let newprod = 0; + for (const product of dataObjects) { let isnuovo = false let setta = false - let productInfo = { - idapp: product.idapp, - code: product.isbn, - id_wp: product._id, - name: product.title, - description: product.description, - publish: product.Editore, - collezione: product.Collezione, - numpages: product.NumPagine, - author: product.autore, - link: product.link, - idCatProds: [], - idSubCatProds: [], - //img: 'upload/products/' + product.code + '.jpg', - image_link: product.image_link, - } + let importa = true; - if (product.NumPagine) - productInfo.numpages = product.NumPagine; + if (!product.title || !product.isbn) + importa = false; - let reccateg = null; - if (product.categories) { - arrcat = product.categories.trim().split(','); - for (const mycat of arrcat) { - let mycatstr = mycat.trim(); + if (importa) { + let productInfo = { + idapp: product.idapp, + code: product.isbn, + id_wp: product._id, + name: product.title, + description: product.description, + publisher: product.Editore, + collezione: product.Collezione, + // author: product.Autore ? product.Autore : '', + link: product.link ? product.link : '', + idCatProds: [], + idSubCatProds: [], + //img: 'upload/products/' + product.code + '.jpg', + image_link: product.image_link ? product.image_link : '', + img2: product.img2 ? product.img2 : '', + img3: product.img3 ? product.img3 : '', + img4: product.img4 ? product.img4 : '', - // Controlla se ci sono le sottocategorie: - arrsubcat = mycatstr.trim().split('>'); + } - if (arrsubcat.length > 1) { - // Ci sono delle sottocategorie - mycatstr = arrsubcat[0].trim(); - product.subcat_name = arrsubcat[1].trim();; + productInfo.productType = shared_consts.PRODUCTTYPE.LIBRO; + + if (product.Data) + productInfo.date_publishing = new Date(product.Data * 1000); + + productInfo.name = productInfo.name.replace(/ - Usato$| - Nuovo$| - Epub$| - Ebook$| - Mobi$| - DVD$| - Streaming$| - Download$/, ""); + + if (product.Pagine) { + try { + productInfo.numpages = 0; + productInfo.numpages = parseInt(product.Pagine); + } catch (e) { + console.error(e); } + } - // Cerca la Categoria - reccateg = await CatProd.findOne({ idapp, name: mycatstr }).lean(); - if (!reccateg) { - // Non esiste questo produttore, quindi lo creo ! - reccateg = new CatProd({ idapp, name: mycatstr }); - ris = await reccateg.save(); + let reccateg = null; + if (product.categories) { + arrcat = product.categories.trim().split(','); + for (const mycat of arrcat) { + let mycatstr = mycat.trim(); + + // Controlla se ci sono le sottocategorie: + arrsubcat = mycatstr.trim().split('>'); + + if (arrsubcat.length > 1) { + // Ci sono delle sottocategorie + mycatstr = arrsubcat[0].trim(); + product.subcat_name = arrsubcat[1].trim();; + } + + // Cerca la Categoria reccateg = await CatProd.findOne({ idapp, name: mycatstr }).lean(); - } + if (!reccateg) { + // Non esiste questo produttore, quindi lo creo ! + reccateg = new CatProd({ idapp, name: mycatstr }); + ris = await reccateg.save(); + reccateg = await CatProd.findOne({ idapp, name: mycatstr }).lean(); + } - if (reccateg) { - productInfo.idCatProds.push(reccateg._id); - } - } - } - - if (product.subcat_name) { - arrsubcat = product.subcat_name.trim().split(','); - for (const mysubcat of arrsubcat) { - let mysubcatstr = mysubcat.trim(); - - // Cerca la Sotto Categoria - let recsubcateg = await SubCatProd.findOne({ idapp, name: mysubcatstr }).lean(); - if (!recsubcateg) { - // Non esiste questa Sotto Categoria, quindi la creo ! - const idCatProd = reccateg ? reccateg._id : '' - recsubcateg = new SubCatProd({ idapp, name: mysubcatstr, idCatProd }); - ris = await recsubcateg.save(); - recsubcateg = await SubCatProd.findOne({ idapp, name: mysubcatstr, idCatProd }).lean(); - } - - if (recsubcateg) { - productInfo.idSubCatProds.push(recsubcateg._id); - } - } - } - - if (!product.hasOwnProperty('active')) { - product.active = true; - } - - if (product.code) - delete product.code; - if (product.name) - delete product.name; - if (product.link) - delete product.link; - - let esisteindb = await ProductInfo.findOne({ code: productInfo.code }).lean(); - - // Update ProductInfo - let risrecInfo = await ProductInfo.findOneAndUpdate({ code: productInfo.code }, { $set: productInfo }, { new: true, upsert: true }); - if (risrecInfo) { - product.idProductInfo = risrecInfo._id; - - recnewInfo = await ProductInfo.findOne({ code: productInfo.code }).lean(); - - if (risrecInfo._id) { - // Record existed, so it was updated - let arrfieldchange = tools.differentObjects(productInfo, recnewInfo); - if (arrfieldchange && arrfieldchange.length > 0) { - // updated++; - console.log('Changed: ', recnewInfo.name + ': ' + arrfieldchange); + if (reccateg) { + productInfo.idCatProds.push(reccateg._id); + } } } - // Cerca il GAS - let recGas = null; - if (product.gas_name) { - // Cerca il GAS - recGas = await Gasordine.findOne({ idapp, name: product.gas_name }).lean(); + // "Autore" : "Silia,Marucelli, Stefano,Cattinelli", + let arrAuthor = []; + if (product.Autore) { + const arrrecauthor = product.Autore.trim().split(','); + + if (arrrecauthor.length >= 1) { + try { + let author = { + name: arrrecauthor[0].trim(), + } + if (arrrecauthor.length === 1) { + author = extractNameAndSurnameByComplete(arrrecauthor[0].trim()); + } else { + author.surname = arrrecauthor[1].trim(); + } + arrAuthor.push(author); + + if (arrrecauthor.length > 1) { + for (let i = 2; i < arrrecauthor.length; i = i + 2) { + try { + author = { + name: arrrecauthor[i].trim() + } + if (arrrecauthor.length > i + 1) { + surname = arrrecauthor[i + 1].trim() + } + arrAuthor.push(author); + } catch (e) { + } + } + } + productInfo.idAuthors = []; + + for (const myauthor of arrAuthor) { + // Cerca l'Autore + recauthor = await Author.findOne({ idapp, name: myauthor.name, surname: myauthor.surname }).lean(); + if (!recauthor) { + // Non esiste questo Autore, quindi lo creo ! + recauthor = new Author({ idapp, name: myauthor.name, surname: myauthor.surname }); + ris = await recauthor.save(); + recauthor = await Author.findOne({ idapp, name: myauthor.name, surname: myauthor.surname }).lean(); + } + + if (recauthor) { + productInfo.idAuthors.push(recauthor._id); + } + + } + + } catch (e) { + console.error(e); + } + } } - if (!recGas && !!product.gas_name) { - recGas = new Gasordine({ idapp, name: product.gas_name, active: true }); - // Non esiste questo GAS, quindi lo creo ! - ris = await recGas.save(); - recGas = await Gasordine.findOne({ idapp, name: product.gas_name }).lean(); + // Cancella l'Autore dal record product + delete product.Autore; + + if (product.subcat_name) { + arrsubcat = product.subcat_name.trim().split(','); + for (const mysubcat of arrsubcat) { + let mysubcatstr = mysubcat.trim(); + + // Cerca la Sotto Categoria + let recsubcateg = await SubCatProd.findOne({ idapp, name: mysubcatstr }).lean(); + if (!recsubcateg) { + // Non esiste questa Sotto Categoria, quindi la creo ! + const idCatProd = reccateg ? reccateg._id : '' + recsubcateg = new SubCatProd({ idapp, name: mysubcatstr, idCatProd }); + ris = await recsubcateg.save(); + recsubcateg = await SubCatProd.findOne({ idapp, name: mysubcatstr, idCatProd }).lean(); + } + + if (recsubcateg) { + productInfo.idSubCatProds.push(recsubcateg._id); + } + } } - let recProductExist = null; - let queryprod = { idProductInfo: product.idProductInfo }; - - if (recGas) { - queryprod = { ...queryprod, idGasordine: recGas._id }; + if (!product.hasOwnProperty('active')) { + product.active = true; } - recProductExist = await Product.findOne({ queryprod }).lean(); + if (product.code) + delete product.code; + if (product.name) + delete product.name; + if (product.link) + delete product.link; - if (!recProductExist) { - isnuovo = true; - } + let esisteindb = await ProductInfo.findOne({ code: productInfo.code }).lean(); + // Update ProductInfo + let risrecInfo = await ProductInfo.findOneAndUpdate({ code: productInfo.code }, { $set: productInfo }, { new: true, upsert: true }); + if (risrecInfo) { + product.idProductInfo = risrecInfo._id; + recnewInfo = await ProductInfo.findOne({ code: productInfo.code }).lean(); - if (!options.aggiornaStockQty && esisteindb && !isnuovo) { - delete product.stockQty; - delete product.maxbookableGASQty; - } - - let myproduct = { - stockQty: product.Quantita, - maxbookableGASQty: product.maxbookableGASQty, - active: product.active, - idGasordine: recGas ? recGas._id : null, - idProductInfo: product.idProductInfo, - idapp, - price: tools.convertPriceEurToValue(product.price), - sale_price: tools.convertPriceEurToValue(product.sale_price), - status: product.status, - } - - // AGGIORNA PRODUCT - let risrec = await Product.findOneAndUpdate(queryprod, { $set: myproduct }, { new: true, upsert: true }); - - let recnew = await Product.findOne(queryprod).lean(); - - if (risrec) { - if (risrec._id) { + if (risrecInfo._id) { // Record existed, so it was updated - let arrfieldchange = tools.differentObjects(myproduct, recnew); - if (arrfieldchange.length > 0) { + let arrfieldchange = tools.differentObjects(productInfo, recnewInfo); + if (arrfieldchange && arrfieldchange.length > 0) { updated++; - console.log('Changed: ', myproduct.idProductInfo + ': ' + arrfieldchange); + console.log('Changed: ', recnewInfo.name + ': ' + arrfieldchange); + } + } + + // Cerca il GAS + let recGas = null; + if (product.gas_name) { + // Cerca il GAS + recGas = await Gasordine.findOne({ idapp, name: product.gas_name }).lean(); + } + + if (!recGas && !!product.gas_name) { + recGas = new Gasordine({ idapp, name: product.gas_name, active: true }); + // Non esiste questo GAS, quindi lo creo ! + ris = await recGas.save(); + recGas = await Gasordine.findOne({ idapp, name: product.gas_name }).lean(); + } + + let myproduct = {}; + + // Download, DVD, Epub, Mobi, Nuovo, PDF, Streaming, Usato + if (product.Versione === 'Nuovo') + myproduct.versione = shared_consts.VERSIONE.NUOVO + else if (product.Versione === 'Usato') + myproduct.versione = shared_consts.VERSIONE.USATO; + else if (product.Versione === 'Download') + myproduct.versione = shared_consts.VERSIONE.DOWNLOAD; + else if (product.Versione === 'DVD') + myproduct.versione = shared_consts.VERSIONE.DVD; + else if (product.Versione === 'Epub') + myproduct.versione = shared_consts.VERSIONE.EPUB; + else if (product.Versione === 'Mobi') + myproduct.versione = shared_consts.VERSIONE.MOBI; + else if (product.Versione === 'PDF') + myproduct.versione = shared_consts.VERSIONE.PDF; + else if (product.Versione === 'Streaming') + myproduct.versione = shared_consts.VERSIONE.STREAMING; + else + myproduct.versione = 100; + + delete product.Versione; + + let recProductExist = null; + // ISBN e versione del prodotto sono le chiavi uniche + let queryprod = { idProductInfo: product.idProductInfo, versione: myproduct.versione }; + + if (recGas) { + queryprod = { ...queryprod, idGasordine: recGas._id }; + } + + recProductExist = await Product.findOne({ queryprod }).lean(); + + if (!recProductExist) { + isnuovo = true; + } + + if (!options.aggiornaStockQty && esisteindb && !isnuovo) { + delete product.Quantità; + // delete product.maxbookableGASQty; + } + + myproduct.isbn = product.isbn; + myproduct.maxbookableGASQty = product.maxbookableGASQty ? product.maxbookableGASQty : null; + myproduct.active = product.active; + myproduct.idGasordine = recGas ? recGas._id : null; + myproduct.idProductInfo = product.idProductInfo; + myproduct.price = product.price ? parseFloat(tools.convertPriceEurToValue(product.price)) : null; + myproduct.sale_price = product.sale_price ? parseFloat(tools.convertPriceEurToValue(product.sale_price)) : null; + myproduct.idapp = idapp; + myproduct.status = product.status ? product.status : null; + + + if (product.Quantità) { + myproduct.stockQty = parseInt(product.Quantità); + } + + let recold = await Product.findOne(queryprod).lean(); + + // AGGIORNA PRODUCT + let risrec = await Product.findOneAndUpdate(queryprod, { $set: myproduct }, { new: true, upsert: true }); + + let recnew = await Product.findOne(queryprod).lean(); + + if (risrec) { + if (risrec._id) { + + if (recold) { + // Record existed, so it was updated + let arrfieldchange = tools.differentObjects(recold, recnew); + if (arrfieldchange.length > 0) { + updated++; + let modif = ''; + for (const field of arrfieldchange) { + modif += field + ': ' + recold[field] + ' -> ' + recnew[field] + ' | '; + } + + console.log('Changed: [' + indprod + '/' + dataObjects.length + ']', productInfo.name, '[' + myproduct.idProductInfo + '] : ' + modif); + } + } else { + newprod++; + console.log('Nuovo Prodotto : [' + newprod + '/' + dataObjects.length + ']', productInfo.name); + } + } else { + // Record didn't exist, so it was created + imported++; } } else { - // Record didn't exist, so it was created - imported++; + // risrec is null or undefined, indicating an error + console.error('Error: ', myproduct.productInfo.name); + errors++; } + + await Product.singlerecconvert_AfterImport_AndSave(idapp, recnew, isnuovo); } else { - // risrec is null or undefined, indicating an error - console.error('Error: ', myproduct.productInfo.name); + console.error('Error ProductInfo: ', product.code); errors++; } - - await Product.singlerecconvert_AfterImport_AndSave(idapp, recnew, isnuovo); - } else { - console.error('Error ProductInfo: ', product.code); - errors++; } + indprod++; } - - console.log('*** IMPORTATI: ', imported); + + console.log('*** IMPORTATI: ', imported, 'AGGIORNATI = ' + updated + ' (su ' + dataObjects.length + ' RECORD)'); return res.status(200).send({ updated, imported, errors }); @@ -506,6 +650,7 @@ router.post('/import', authenticate, async (req, res) => { img: 'upload/products/' + product.code + '.jpg', weight: product.weight, unit: tools.getIdUnitsByText(product.unit), + productType: shared_consts.PRODUCTTYPE.PRODUCT, } let reccateg = null; @@ -622,7 +767,7 @@ router.post('/import', authenticate, async (req, res) => { let arrfieldchange = tools.differentObjects(product, recnew); if (arrfieldchange.length > 0) { updated++; - console.log('Changed: ', product.idProductInfo + ': ' + arrfieldchange); + console.log('Changed:', product.idProductInfo + ': ' + arrfieldchange); } } else { // Record didn't exist, so it was created @@ -669,6 +814,8 @@ router.post('/import', authenticate, async (req, res) => { let product = risprod.product; let productInfo = risprod.productInfo; + + let isnuovo = false let setta = false @@ -811,12 +958,12 @@ router.post('/import', authenticate, async (req, res) => { // L'opzione ordered: false gestisce gli errori senza interrompere l'inserimento /*return await Product.insertMany(dataObjects, { ordered: false }) .then((ris) => { - + Product.convertAfterImportALLPROD(idapp, dataObjects).then((ris) => { return res.status(200).send(true); }); - - + + }) .catch((errors) => { console.error(errors); diff --git a/src/server/router/index_router.js b/src/server/router/index_router.js index 5cc3a35..a33578a 100755 --- a/src/server/router/index_router.js +++ b/src/server/router/index_router.js @@ -25,6 +25,8 @@ const { CfgServer } = require('../models/cfgserver'); const formidable = require('formidable'); const folder = path.join(__dirname, 'upload'); +const sanitizeHtml = require('sanitize-html'); + if (!fs.existsSync(folder)) { fs.mkdirSync(folder); } @@ -655,8 +657,11 @@ router.post('/setsubrec', authenticate, (req, res) => { }); router.post('/gettable', authenticate, (req, res) => { - const params = req.body; - let idapp = req.user ? req.user.idapp : params.idapp; + let params = req.body; + + params.table = sanitizeHtml(params.table); + + let idapp = req.user ? req.user.idapp : sanitizeHtml(params.idapp); const mytable = globalTables.getTableByTableName(params.table); //console.log('mytable', mytable); if (!mytable) { diff --git a/src/server/router/users_router.js b/src/server/router/users_router.js index 1debf84..138f2a7 100755 --- a/src/server/router/users_router.js +++ b/src/server/router/users_router.js @@ -651,7 +651,36 @@ router.post('/newtok', async (req, res) => { }); -router.post('/login', (req, res) => { +// Dizionario per tenere traccia dei tentativi di accesso falliti per ogni utente +const failedLoginAttempts = {}; + +// Costante per il numero massimo di tentativi di accesso falliti prima del blocco +const MAX_FAILED_ATTEMPTS = 3; + +// Costante per la durata del blocco in millisecondi (ad esempio 30 minuti) +const BLOCK_DURATION = 30 * 60 * 1000; // 30 minuti + +// Funzione per bloccare un utente per un periodo di tempo dopo un numero specificato di tentativi falliti +function blockUser(username) { + failedLoginAttempts[username] = Date.now() + BLOCK_DURATION; +} + +// Middleware per controllare se l'utente è bloccato +function checkBlocked(req, res, next) { + const { username } = req.body; + const now = Date.now(); + + if (failedLoginAttempts[username] && failedLoginAttempts[username] > now) { + text = 'Utente bloccato. Riprova più tardi. (username=' + username + ')'; + console.log(text); + return res.status(403).json({ message: 'Utente bloccato. Riprova più tardi.' }); + } + + next(); +} + + +router.post('/login', checkBlocked, (req, res) => { var body = _.pick(req.body, ['username', 'password', 'idapp', 'keyappid', 'lang']); var user = new User(body); @@ -668,18 +697,47 @@ router.post('/login', (req, res) => { const myuser = user; - User.findByCredentials(user.idapp, user.username, user.password). + return User.findByCredentials(user.idapp, user.username, user.password). then(async (user) => { // tools.mylog("CREDENZIALI ! "); if (!user) { + + const rislogin = await User.tooManyLoginWrong(body.idapp, body.username, true); + if (rislogin.troppilogin) { + let text = 'Troppe richieste di Login ERRATE: ' + body.username + ' [IP: ' + tools.getiPAddressUser(req) + '] Tentativi: ' + rislogin.retry_pwd; + telegrambot.sendMsgTelegramToTheManagers(body.idapp, text); + console.log('/login', text); + res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: text }); + return false; + } + await tools.snooze(3000); - const msg = 'Tentativo di Login ERRATO [' + body.username + ' , ' + ']\n' + '[IP: ' + tools.getiPAddressUser(req) + - ']'; + + if (!failedLoginAttempts[body.username]) { + failedLoginAttempts[body.username] = 1; + } else { + failedLoginAttempts[body.username]++; + } + + + const msg = 'Tentativo (' + failedLoginAttempts[body.username] + ') di Login ERRATO [' + body.username + ' , ' + ']\n' + '[IP: ' + tools.getiPAddressUser(req) + ']'; tools.mylogshow(msg); await telegrambot.sendMsgTelegramToTheAdmin(myuser.idapp, msg, true); tools.writeErrorLog(msg); // telegrambot.sendMsgTelegramToTheManagers(body.idapp, msg); - res.status(404).send({ code: server_constants.RIS_CODE_LOGIN_ERR }); + + if (failedLoginAttempts[body.username] >= MAX_FAILED_ATTEMPTS) { + blockUser(body.username); + text = 'Troppi tentativi di accesso falliti. Utente bloccato (' + body.username + ')' + ' [IP: ' + tools.getiPAddressUser(req) + ']'; + tools.mylogshow(text); + telegrambot.sendMsgTelegramToTheManagers(req.body.idapp, text); + res.status(403).json({ message: text }); + resalreadysent = true; + } + + res.status(401).send({ code: server_constants.RIS_CODE_LOGIN_ERR }); + resalreadysent = true; + } return user; }). diff --git a/src/server/tools/general.js b/src/server/tools/general.js index 199d451..aec1b47 100755 --- a/src/server/tools/general.js +++ b/src/server/tools/general.js @@ -39,6 +39,8 @@ const privateVapidKey = process.env.PRIVATE_VAPI_KEY; const FIELDS_REGEX = ['username', 'name', 'surname']; +const sanitizeHtml = require('sanitize-html'); + // Code goes here const keySize = 256; const ivSize = 128; @@ -2404,6 +2406,8 @@ module.exports = { let query = []; if (params.filter && params.fieldsearch) { + params.filter = sanitizeHtml(params.filter); + params.fieldsearch = sanitizeHtml(params.fieldsearch); const querytemp = this.getFilterParam(params.filter, params.fieldsearch); if (querytemp) { query = [...query, ...querytemp]; @@ -2420,6 +2424,7 @@ module.exports = { // } if (params.filterand) { + params.filterand = sanitizeHtml(params.filterand); if (params.filterand.includes( shared_consts.FILTER_EXTRALIST_NOT_REGISTERED)) @@ -2540,6 +2545,7 @@ module.exports = { } if (params.filtercustom) { + params.filtercustom = sanitizeHtml(params.filtercustom); let condition = {}; for (const myfilter of params.filtercustom) { if (myfilter['userId']) { @@ -2566,6 +2572,7 @@ module.exports = { } if (params.filter_gte) { + params.filter_gte = sanitizeHtml(params.filter_gte); for (let ind = 0; ind < params.filter_gte.length; ind++) { for (const [key, value] of Object.entries(params.filter_gte[ind])) { if (value > 0) { @@ -2578,6 +2585,7 @@ module.exports = { } if (params.filtersearch) { + params.filtersearch = sanitizeHtml(params.filtersearch); filtriadded.push(...params.filtersearch); } @@ -2589,11 +2597,13 @@ module.exports = { if (params.filterextra) { + params.filterextra = sanitizeHtml(params.filterextra); if (params.filterextra.length > 0) query = [...query, ...params.filterextra] } if (filtriadded) { + filtriadded = sanitizeHtml(filtriadded); if (filtriadded.length > 0) query.push({ $match: { $and: filtriadded } }); } @@ -2844,6 +2854,12 @@ module.exports = { if (q1p) query = [...query, ...q1p]; } + params.lookup2 = this.sanitizzaLookup(params.lookup2); + params.lookup3 = this.sanitizzaLookup(params.lookup3); + params.lookup4 = this.sanitizzaLookup(params.lookup4); + params.lookup5 = this.sanitizzaLookup(params.lookup5); + + const q2 = this.getLookup(params.lookup2, 2, proj, objadd.proj); if (q2) query = [...query, ...q2]; @@ -4895,8 +4911,9 @@ module.exports = { commonFields.forEach((field) => { - if (obj1 && obj2 && obj1 && obj2 && ((obj1[field] && obj1[field].toString()) !== (obj2[field] && obj2[field].toString()))) { - fieldsUpdated.push(field); + if (obj1 && obj2 && ((obj1[field] && obj1[field].toString()) !== (obj2[field] && obj2[field].toString()))) { + if (obj1[field] !== undefined && obj2[field] !== undefined) + fieldsUpdated.push(field); } }); return fieldsUpdated @@ -5074,5 +5091,13 @@ module.exports = { } }, + arrotondaA2Decimali(valore) { + try { + return parseFloat(valore).toFixed(2); + } catch (error) { + return valore; + } + }, + // Scriviere qui SOPRA le funzioni }; diff --git a/src/server/tools/shared_nodejs.js b/src/server/tools/shared_nodejs.js index 0571fd1..166d13b 100755 --- a/src/server/tools/shared_nodejs.js +++ b/src/server/tools/shared_nodejs.js @@ -1049,4 +1049,28 @@ module.exports = { TYPEHOSP_SCAMBIOCASA: 1, TYPEHOSP_OSPITALITA: 2, + PRODUCTTYPE: { + BENE: 1, + SERVIZIO: 2, + OSPITALITA: 4, + PRODOTTO: 10, + LIBRO: 20, + EBOOK: 21, + AUDIOLIBRO: 22, + VIDEO: 23, + CARTE: 25, + }, + + // Download, DVD, Epub, Mobi, Nuovo, PDF, Streaming, Usato + VERSIONE: { + NUOVO: 1, + USATO: 2, + DOWNLOAD: 3, + DVD: 4, + EPUB: 5, + MOBI: 6, + PDF: 7, + STREAMING: 8, + }, + }; diff --git a/yarn.lock b/yarn.lock index 23c6c46..d3af3f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3898,6 +3898,11 @@ entities@^4.2.0, entities@^4.3.0: resolved "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz" integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + entities@~1.1.1: version "1.1.2" resolved "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz" @@ -4019,7 +4024,7 @@ escape-string-applescript@^1.0.0: resolved "https://registry.npmjs.org/escape-string-applescript/-/escape-string-applescript-1.0.0.tgz" integrity sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA== -escape-string-regexp@4.0.0: +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== @@ -5284,6 +5289,16 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" +htmlparser2@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" + integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + entities "^4.4.0" + htmlparser2@^8.0.1: version "8.0.1" resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz" @@ -5888,6 +5903,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-promise@^2.0.0: version "2.2.2" resolved "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz" @@ -7668,6 +7688,11 @@ nanoid@3.3.3: resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" @@ -8445,6 +8470,11 @@ parse-passwd@^1.0.0: resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz" integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== +parse-srcset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" + integrity sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q== + parse5-htmlparser2-tree-adapter@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz" @@ -8632,6 +8662,15 @@ posix-character-classes@^0.1.0: resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +postcss@^8.3.11: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.2.0" + prebuild-install@^7.1.1: version "7.1.1" resolved "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz" @@ -9401,6 +9440,18 @@ safe-regex@^1.1.0: resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sanitize-html@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.13.0.tgz#71aedcdb777897985a4ea1877bf4f895a1170dae" + integrity sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA== + dependencies: + deepmerge "^4.2.2" + escape-string-regexp "^4.0.0" + htmlparser2 "^8.0.0" + is-plain-object "^5.0.0" + parse-srcset "^1.0.2" + postcss "^8.3.11" + saslprep@^1.0.0, saslprep@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz" @@ -9820,6 +9871,11 @@ socks@^2.3.3, socks@^2.6.2, socks@^2.7.1: ip "^2.0.0" smart-buffer "^4.2.0" +source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz" @@ -11071,6 +11127,7 @@ workerpool@6.2.1: integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + name wrap-ansi-cjs version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==