backend-plus 2.7.0-beta.0 → 2.7.0-beta.11
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/for-client/css/my-menu.styl +13 -15
- package/for-client/css/my-tables.styl +28 -28
- package/for-client/css/my-things.styl +23 -12
- package/for-client/img/fuentes.txt +1 -1
- package/for-client/login.jade +3 -3
- package/for-client/my-localdb.js +2 -2
- package/for-client/my-localdb.js.map +1 -1
- package/for-client/my-menu.js +9 -9
- package/for-client/my-tables.js +177 -137
- package/for-client/my-things.js +18 -18
- package/for-client/my-websqldb.js +1 -1
- package/for-client/my-websqldb.js.map +1 -1
- package/for-client/new-password-result.jade +2 -2
- package/install/semver_to_decimal-fun.sql +1 -1
- package/lib/backend-plus.d.ts +52 -36
- package/lib/backend-plus.js +172 -115
- package/lib/in-backend-plus.d.ts +1 -1
- package/lib/my-debugger.js +2 -2
- package/lib/procedures-table.js +47 -31
- package/lib/table-def-adapt.js +5 -5
- package/lib/table-mixin.js +0 -2
- package/lib/tables/table-locks.js +1 -1
- package/lib/tables/table-tokens.js +1 -1
- package/package.json +37 -30
- package/src/for-client/my-localdb.ts +15 -15
- package/src/for-client/my-websqldb.ts +5 -5
- package/src/test/karma-test-localdb.ts +2 -2
- package/unlogged/auto-login.js +1 -1
- package/unlogged/compatibilidad.js +2 -2
- package/unlogged/css/chpass.styl +6 -6
- package/unlogged/css/login.styl +4 -4
- package/unlogged/globals.d.ts +1 -1
- package/unlogged/my-ajax.js +20 -20
- package/unlogged/my-start.js +1 -1
package/lib/backend-plus.js
CHANGED
|
@@ -29,6 +29,7 @@ var packagejson = require(process.cwd()+'/package.json');
|
|
|
29
29
|
var stackTrace = require('stack-trace');
|
|
30
30
|
var locatePath = require('@upgraded/locate-path');
|
|
31
31
|
var jsYaml = require('js-yaml');
|
|
32
|
+
var tabPlus = require('tab-plus');
|
|
32
33
|
var nodemailer = require('nodemailer');
|
|
33
34
|
var os = require('os');
|
|
34
35
|
const cors = require('cors');
|
|
@@ -73,7 +74,7 @@ if(dashDashDir>0){
|
|
|
73
74
|
console.log('cwd',process.cwd());
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
/**
|
|
77
|
+
/**
|
|
77
78
|
* @param {string} text
|
|
78
79
|
* @return {string}
|
|
79
80
|
*/
|
|
@@ -81,14 +82,14 @@ function md5(text){
|
|
|
81
82
|
return crypto.createHash('md5').update(text).digest('hex');
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
const DEFAULT_ITERATIONS = 4096;
|
|
85
|
+
const DEFAULT_ITERATIONS = 4096;
|
|
85
86
|
const HASH_ALGORITHM = 'sha256';
|
|
86
87
|
|
|
87
88
|
// primera version de scram sha 256. El Verifier completo tenía 64 bytes.
|
|
88
|
-
const LEGACY_KEY_LENGTH = 64;
|
|
89
|
+
const LEGACY_KEY_LENGTH = 64;
|
|
89
90
|
|
|
90
91
|
// Formato nuevo/PG-Compatible: El Client Key tiene 32 bytes (SHA-256).
|
|
91
|
-
const SCRAM_KEY_LENGTH = 32;
|
|
92
|
+
const SCRAM_KEY_LENGTH = 32;
|
|
92
93
|
|
|
93
94
|
const bufferToBase64 = (buffer) => buffer.toString('base64');
|
|
94
95
|
|
|
@@ -96,31 +97,31 @@ const bufferToBase64 = (buffer) => buffer.toString('base64');
|
|
|
96
97
|
* Función central para PBKDF2 (Acepta KEY_LEN para compatibilidad).
|
|
97
98
|
* Devuelve la clave derivada con la longitud especificada.
|
|
98
99
|
*/
|
|
99
|
-
async function deriveKey(password, saltBuffer, iterations, keyLength) {
|
|
100
|
+
async function deriveKey(password, saltBuffer, iterations, keyLength) {
|
|
100
101
|
return new Promise((resolve, reject) => {
|
|
101
102
|
crypto.pbkdf2(
|
|
102
|
-
password,
|
|
103
|
+
password,
|
|
103
104
|
saltBuffer,
|
|
104
|
-
iterations,
|
|
105
|
+
iterations,
|
|
105
106
|
keyLength,
|
|
106
|
-
HASH_ALGORITHM,
|
|
107
|
+
HASH_ALGORITHM,
|
|
107
108
|
(err, derivedKey) => {
|
|
108
109
|
if (err) return reject(err);
|
|
109
|
-
resolve(derivedKey);
|
|
110
|
+
resolve(derivedKey);
|
|
110
111
|
}
|
|
111
112
|
);
|
|
112
113
|
});
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
async function generateScramVerifier(password) {
|
|
116
|
-
const saltBuffer = crypto.randomBytes(16);
|
|
117
|
+
const saltBuffer = crypto.randomBytes(16);
|
|
117
118
|
const saltBase64 = bufferToBase64(saltBuffer);
|
|
118
119
|
|
|
119
120
|
const Hi = await deriveKey(
|
|
120
|
-
password,
|
|
121
|
-
saltBuffer,
|
|
121
|
+
password,
|
|
122
|
+
saltBuffer,
|
|
122
123
|
DEFAULT_ITERATIONS,
|
|
123
|
-
SCRAM_KEY_LENGTH
|
|
124
|
+
SCRAM_KEY_LENGTH
|
|
124
125
|
);
|
|
125
126
|
const clientKey = crypto.createHmac('sha256', Hi).update('Client Key').digest();
|
|
126
127
|
const serverKey = crypto.createHmac('sha256', Hi).update('Server Key').digest();
|
|
@@ -144,27 +145,27 @@ async function verifyScramPG(password, storedScramString) {
|
|
|
144
145
|
if (!storedScramString.startsWith('SCRAM-SHA-256$')) {
|
|
145
146
|
return false;
|
|
146
147
|
}
|
|
147
|
-
|
|
148
|
+
|
|
148
149
|
const parts = storedScramString.split('$');
|
|
149
150
|
if (parts.length !== 3) {
|
|
150
151
|
return false;
|
|
151
152
|
}
|
|
152
|
-
|
|
153
|
+
|
|
153
154
|
const [storedIterations, saltBase64] = parts[1].split(':');
|
|
154
155
|
const [storedKeyBase64, serverKeyBase64] = parts[2].split(':'); // Asume StoredKey y ServerKey
|
|
155
156
|
|
|
156
157
|
if (!saltBase64 || !storedKeyBase64 || !serverKeyBase64 || isNaN(parseInt(storedIterations))) {
|
|
157
158
|
return false;
|
|
158
159
|
}
|
|
159
|
-
|
|
160
|
+
|
|
160
161
|
const iterations = parseInt(storedIterations);
|
|
161
162
|
const saltBuffer = Buffer.from(saltBase64, 'base64');
|
|
162
163
|
const storedKeyBuffer = Buffer.from(storedKeyBase64, 'base64');
|
|
163
164
|
const serverKeyBuffer = Buffer.from(serverKeyBase64, 'base64');
|
|
164
165
|
|
|
165
166
|
const Hi = await deriveKey(
|
|
166
|
-
password,
|
|
167
|
-
saltBuffer,
|
|
167
|
+
password,
|
|
168
|
+
saltBuffer,
|
|
168
169
|
iterations,
|
|
169
170
|
SCRAM_KEY_LENGTH
|
|
170
171
|
);
|
|
@@ -203,21 +204,21 @@ async function verifyScramLegacy(password, storedScramString) {
|
|
|
203
204
|
if (parts.length !== 3) {
|
|
204
205
|
throw new Error('Formato SCRAM almacenado inválido.');
|
|
205
206
|
}
|
|
206
|
-
|
|
207
|
+
|
|
207
208
|
// Obtiene iteraciones y salt de la segunda parte (ej: '4096:SALT_BASE64')
|
|
208
209
|
const [storedIterations, storedSalt] = parts[1].split(':');
|
|
209
210
|
const storedVerifier = parts[2];
|
|
210
|
-
|
|
211
|
+
|
|
211
212
|
if (!storedSalt || !storedVerifier || isNaN(parseInt(storedIterations))) {
|
|
212
213
|
throw new Error('Datos de SCRAM incompletos o malformados.');
|
|
213
214
|
}
|
|
214
|
-
|
|
215
|
+
|
|
215
216
|
const iterations = parseInt(storedIterations);
|
|
216
217
|
|
|
217
218
|
// Deriva la clave de la contraseña ingresada
|
|
218
219
|
const generatedVerifierBuffer = await deriveKey(
|
|
219
|
-
password,
|
|
220
|
-
storedSalt,
|
|
220
|
+
password,
|
|
221
|
+
storedSalt,
|
|
221
222
|
iterations,
|
|
222
223
|
LEGACY_KEY_LENGTH
|
|
223
224
|
);
|
|
@@ -313,7 +314,7 @@ AppBackend.prototype.configStaticConfig = function configStaticConfig(){
|
|
|
313
314
|
min-version: 12
|
|
314
315
|
fkOnUpdate: cascade
|
|
315
316
|
max: 50
|
|
316
|
-
log:
|
|
317
|
+
log:
|
|
317
318
|
db:
|
|
318
319
|
until: 2001-01-01 00:00
|
|
319
320
|
last-error: false
|
|
@@ -326,7 +327,7 @@ AppBackend.prototype.configStaticConfig = function configStaticConfig(){
|
|
|
326
327
|
"":
|
|
327
328
|
local-path: for-client
|
|
328
329
|
bin: {}
|
|
329
|
-
client-setup:
|
|
330
|
+
client-setup:
|
|
330
331
|
skin: ""
|
|
331
332
|
lang: en
|
|
332
333
|
version: 1.0
|
|
@@ -589,7 +590,7 @@ function MemoryPerodicallySaved(session){
|
|
|
589
590
|
})}
|
|
590
591
|
|
|
591
592
|
/**
|
|
592
|
-
* @param {string} text
|
|
593
|
+
* @param {string} text
|
|
593
594
|
*/
|
|
594
595
|
AppBackend.prototype.jsonPass = function jsonPass(text){
|
|
595
596
|
return JSON.stringify(text,null,' ').replace(/\n(.*".*(pass|clave|secret).*":\s*").*(",?)\n/gi,'\n$1********$3\n');
|
|
@@ -751,7 +752,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
751
752
|
while(iPosDbDump && iPosDbDump<process.argv.length && !process.argv[iPosDbDump].startsWith('--')){
|
|
752
753
|
dumps.push(process.argv[iPosDbDump]);
|
|
753
754
|
iPosDbDump++;
|
|
754
|
-
}
|
|
755
|
+
}
|
|
755
756
|
opts["dump-db"]={
|
|
756
757
|
complete:!dumps.length,
|
|
757
758
|
tableNames:dumps instanceof Array?dumps:null,
|
|
@@ -888,8 +889,8 @@ AppBackend.prototype.start = function start(opts){
|
|
|
888
889
|
}
|
|
889
890
|
be.db = pg;
|
|
890
891
|
be.dbUserNameExpr="get_app_user()";
|
|
891
|
-
be.dbUserRolExpr=`(select ${be.db.quoteIdent(be.config.login.rolFieldName)}
|
|
892
|
-
from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.db.quoteIdent(be.config.login.table)}
|
|
892
|
+
be.dbUserRolExpr=`(select ${be.db.quoteIdent(be.config.login.rolFieldName)}
|
|
893
|
+
from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.db.quoteIdent(be.config.login.table)}
|
|
893
894
|
where ${be.db.quoteIdent(be.config.login.userFieldName)} = ${be.dbUserNameExpr})`
|
|
894
895
|
}).then(function(){
|
|
895
896
|
if(opts["dump-db"]){
|
|
@@ -1032,6 +1033,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1032
1033
|
console.log(err);
|
|
1033
1034
|
throw err;
|
|
1034
1035
|
}
|
|
1036
|
+
await pg.setAllTypes(client);
|
|
1035
1037
|
var data = await client.query("SELECT current_timestamp as cts").fetchUniqueRow();
|
|
1036
1038
|
if(verboseStartup){
|
|
1037
1039
|
console.log('NOW in Database',data.row.cts);
|
|
@@ -1047,7 +1049,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1047
1049
|
},function(){ /*OK, login.jade must not be here */ }),
|
|
1048
1050
|
fs.stat('client/login.jade').then(function(){
|
|
1049
1051
|
return Path.resolve(be.rootPath,'client/login');
|
|
1050
|
-
},function(){
|
|
1052
|
+
},function(){
|
|
1051
1053
|
return Path.join(__dirname,'../for-client/login');
|
|
1052
1054
|
}).then(function(loginFile){
|
|
1053
1055
|
be.config.login.plus.loginPageServe=function(req,res,next){
|
|
@@ -1164,7 +1166,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1164
1166
|
if(verboseStartup){
|
|
1165
1167
|
console.log('-------------------');
|
|
1166
1168
|
console.log(
|
|
1167
|
-
'be.config.login',
|
|
1169
|
+
'be.config.login',
|
|
1168
1170
|
be.jsonPass(be.config.login)
|
|
1169
1171
|
);
|
|
1170
1172
|
}
|
|
@@ -1193,7 +1195,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1193
1195
|
}
|
|
1194
1196
|
});
|
|
1195
1197
|
const updatePassword = async ({client, username, password, setUpdateDate, errorIfNoResult}) => {
|
|
1196
|
-
const {table, passFieldName, userFieldName, passUpdatedAtFieldName, passAlgorithmFieldName, schema} = be.config.login;
|
|
1198
|
+
const {table, passFieldName, userFieldName, passUpdatedAtFieldName, passAlgorithmFieldName, schema} = be.config.login;
|
|
1197
1199
|
const hashPass = await generateScramVerifier(password);
|
|
1198
1200
|
let params = [username, hashPass];
|
|
1199
1201
|
let setters = [`${be.db.quoteIdent(passFieldName)} = $2`];
|
|
@@ -1204,9 +1206,9 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1204
1206
|
if(setUpdateDate && passUpdatedAtFieldName){
|
|
1205
1207
|
setters.push(`${be.db.quoteIdent(passUpdatedAtFieldName)} = current_timestamp`)
|
|
1206
1208
|
}
|
|
1207
|
-
|
|
1209
|
+
|
|
1208
1210
|
const result = await client.query(`
|
|
1209
|
-
UPDATE ${(schema ? be.db.quoteIdent(schema) + '.' : '') + be.db.quoteIdent(table)}
|
|
1211
|
+
UPDATE ${(schema ? be.db.quoteIdent(schema) + '.' : '') + be.db.quoteIdent(table)}
|
|
1210
1212
|
SET ${setters.join(', ')}
|
|
1211
1213
|
WHERE ${be.db.quoteIdent(userFieldName)} = $1
|
|
1212
1214
|
returning 1 as ok
|
|
@@ -1236,7 +1238,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1236
1238
|
[userFieldName]
|
|
1237
1239
|
)
|
|
1238
1240
|
).concat(passFieldName);
|
|
1239
|
-
|
|
1241
|
+
|
|
1240
1242
|
const sql = "SELECT "+infoFieldList.map(function(fieldOrPair){ return fieldOrPair.split(' as ').map(function(ident){ return be.db.quoteIdent(ident)}).join(' as '); })+
|
|
1241
1243
|
", "+be.config.login.activeClausule+" as active "+
|
|
1242
1244
|
", "+be.config.login.lockedClausule+" as locked "+
|
|
@@ -1264,7 +1266,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1264
1266
|
if(await verifyScramPG(password, user[passFieldName])){
|
|
1265
1267
|
isScramValid = true;
|
|
1266
1268
|
}
|
|
1267
|
-
|
|
1269
|
+
|
|
1268
1270
|
// 2. Intento con formato Legacy (64 bytes)
|
|
1269
1271
|
else if(await verifyScramLegacy(password, user[passFieldName])){
|
|
1270
1272
|
isScramValid = true;
|
|
@@ -1274,7 +1276,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1274
1276
|
done(null,false,{message:be.messages.unlogged.login.userOrPassFail});
|
|
1275
1277
|
return
|
|
1276
1278
|
}
|
|
1277
|
-
}else{
|
|
1279
|
+
}else{
|
|
1278
1280
|
if (md5(password+username.toLowerCase()) === user[passFieldName]){
|
|
1279
1281
|
needsMigration = true;
|
|
1280
1282
|
}else{
|
|
@@ -1288,7 +1290,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1288
1290
|
if (be.config.log["pass-migration"]) console.log('Migración completada.');
|
|
1289
1291
|
}
|
|
1290
1292
|
}
|
|
1291
|
-
//continua validando
|
|
1293
|
+
//continua validando
|
|
1292
1294
|
if(data.rowCount==1){
|
|
1293
1295
|
if(!data.row.active){
|
|
1294
1296
|
done(null,false,{message:be.messages.unlogged.login.inactiveFail});
|
|
@@ -1344,7 +1346,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1344
1346
|
const data = await client.query(
|
|
1345
1347
|
`SELECT *
|
|
1346
1348
|
FROM ${(schema ? be.db.quoteIdent(schema) + '.' : '')}${be.db.quoteIdent(table)}
|
|
1347
|
-
WHERE ${be.db.quoteIdent(userFieldName)} = $1`,
|
|
1349
|
+
WHERE ${be.db.quoteIdent(userFieldName)} = $1`,
|
|
1348
1350
|
[username]
|
|
1349
1351
|
).fetchOneRowIfExists();
|
|
1350
1352
|
if (data.rowCount !== 1) {
|
|
@@ -1360,7 +1362,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1360
1362
|
ok = await verifyScramLegacy(oldPassword, storedHash);
|
|
1361
1363
|
}
|
|
1362
1364
|
} else { //Intento MD5
|
|
1363
|
-
const md5Hash = md5(oldPassword + username.toLowerCase());
|
|
1365
|
+
const md5Hash = md5(oldPassword + username.toLowerCase());
|
|
1364
1366
|
ok = (md5Hash === storedHash);
|
|
1365
1367
|
}
|
|
1366
1368
|
if (!ok) {
|
|
@@ -1454,7 +1456,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1454
1456
|
to: be.config.mailer?.supervise?.to,
|
|
1455
1457
|
subject: `npm start ${be.config["client-setup"]?.title || packagejson.name} ok ✔️`,
|
|
1456
1458
|
text:`Inicio del servicio: ${new Date().toJSON()}
|
|
1457
|
-
|
|
1459
|
+
|
|
1458
1460
|
Contexto: ${os.userInfo().username} ${process.cwd()}
|
|
1459
1461
|
`
|
|
1460
1462
|
}, {ignoreNoMailer:true, event:'restart-ok'})
|
|
@@ -1462,7 +1464,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1462
1464
|
if(err.dumping=='ok'){
|
|
1463
1465
|
console.log('db struct dumped');
|
|
1464
1466
|
process.exit(0);
|
|
1465
|
-
return;
|
|
1467
|
+
return;
|
|
1466
1468
|
}
|
|
1467
1469
|
console.log('ERROR',err.stack || err);
|
|
1468
1470
|
if(err.context){
|
|
@@ -1476,11 +1478,11 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1476
1478
|
to: be.config.mailer?.supervise?.to,
|
|
1477
1479
|
subject: `npm start ${be.config["client-setup"]?.title || packagejson.name} fallido 🛑`,
|
|
1478
1480
|
text:`Falla en el inicio del servicio: ${new Date().toJSON()}
|
|
1479
|
-
|
|
1481
|
+
|
|
1480
1482
|
Contexto: ${os.userInfo().username} ${process.cwd()}
|
|
1481
1483
|
|
|
1482
1484
|
Mensaje: ${err.message}
|
|
1483
|
-
|
|
1485
|
+
|
|
1484
1486
|
${err.stack}
|
|
1485
1487
|
`
|
|
1486
1488
|
}, {ignoreNoMailer:true, event:'restart-fail'})
|
|
@@ -1549,7 +1551,7 @@ AppBackend.prototype.checkDatabaseStructure = async function checkDatabaseStruct
|
|
|
1549
1551
|
throw err;
|
|
1550
1552
|
}
|
|
1551
1553
|
try{
|
|
1552
|
-
await client.query(`select ${be.db.quoteIdentList(be.config.login.forget.mailFields)}
|
|
1554
|
+
await client.query(`select ${be.db.quoteIdentList(be.config.login.forget.mailFields)}
|
|
1553
1555
|
from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.config.login.table} limit 1`).fetchOneRowIfExists();
|
|
1554
1556
|
}catch(err){
|
|
1555
1557
|
var mensaje = `
|
|
@@ -1564,7 +1566,7 @@ AppBackend.prototype.checkDatabaseStructure = async function checkDatabaseStruct
|
|
|
1564
1566
|
}
|
|
1565
1567
|
}
|
|
1566
1568
|
var bitacoraTableName = be.config.server.bitacoraTableName || 'bitacora';
|
|
1567
|
-
var bitacoraId = await client.query(`SELECT data_type
|
|
1569
|
+
var bitacoraId = await client.query(`SELECT data_type
|
|
1568
1570
|
FROM information_schema.columns
|
|
1569
1571
|
WHERE /*table_schema = 'his' AND*/ table_name = '${bitacoraTableName}' AND column_name = 'id'
|
|
1570
1572
|
`).fetchOneRowIfExists();
|
|
@@ -1609,9 +1611,9 @@ AppBackend.prototype.postConfig = function postConfig(){
|
|
|
1609
1611
|
AppBackend.prototype.getContext = function getContext(req){
|
|
1610
1612
|
var be = this;
|
|
1611
1613
|
return {
|
|
1612
|
-
be, user:req.user, session:req.session,
|
|
1613
|
-
username:req.user && req.user[be.config.login.userFieldName],
|
|
1614
|
-
machineId:req.machineId,
|
|
1614
|
+
be, user:req.user, session:req.session,
|
|
1615
|
+
username:req.user && req.user[be.config.login.userFieldName],
|
|
1616
|
+
machineId:req.machineId,
|
|
1615
1617
|
navigator:(req.userAgent||{}).shortDescription||'?'
|
|
1616
1618
|
};
|
|
1617
1619
|
}
|
|
@@ -1641,7 +1643,7 @@ AppBackend.prototype.generateInsertSQL = function generateInsertSQL(schemaName,
|
|
|
1641
1643
|
var cleanValues = [];
|
|
1642
1644
|
for (var key in insertElement) {
|
|
1643
1645
|
cleanKeys.push(db.quoteIdent(key));
|
|
1644
|
-
cleanValues.push(db.quoteNullable(insertElement[key]));
|
|
1646
|
+
cleanValues.push(db.quoteNullable(insertElement[key]));
|
|
1645
1647
|
}
|
|
1646
1648
|
var sql = `INSERT INTO ${db.quoteIdent(schemaName)}.${db.quoteIdent(tableName)}
|
|
1647
1649
|
(${cleanKeys.join(',')}) VALUES (${cleanValues.join(',')}) returning id`;
|
|
@@ -1659,7 +1661,7 @@ AppBackend.prototype.updateUpdateSQL = function updateUpdateSQL(schemaName, tabl
|
|
|
1659
1661
|
for (var key in updateConditions) {
|
|
1660
1662
|
filterPairs.push(be.db.quoteIdent(key) + " = " + be.db.quoteLiteral(updateConditions[key]));
|
|
1661
1663
|
};
|
|
1662
|
-
var sql = `UPDATE ${db.quoteIdent(schemaName)}.${db.quoteIdent(tableName)}
|
|
1664
|
+
var sql = `UPDATE ${db.quoteIdent(schemaName)}.${db.quoteIdent(tableName)}
|
|
1663
1665
|
SET ${setPairs.join(',')}
|
|
1664
1666
|
WHERE ${filterPairs.join(' AND ')}`;
|
|
1665
1667
|
return sql;
|
|
@@ -1667,7 +1669,7 @@ AppBackend.prototype.updateUpdateSQL = function updateUpdateSQL(schemaName, tabl
|
|
|
1667
1669
|
|
|
1668
1670
|
|
|
1669
1671
|
/** @param {boolean} forUnlogged */
|
|
1670
|
-
AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnlogged){
|
|
1672
|
+
AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnlogged){
|
|
1671
1673
|
var be = this;
|
|
1672
1674
|
if(forUnlogged){
|
|
1673
1675
|
var app = express();
|
|
@@ -1685,7 +1687,7 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1685
1687
|
be.procedures = defs;
|
|
1686
1688
|
be.clientSetup.procedure = be.procedure;
|
|
1687
1689
|
app.get('/client-setup',async function(req, res, next){
|
|
1688
|
-
if(forUnlogged && req.user){
|
|
1690
|
+
if(forUnlogged && req.user){
|
|
1689
1691
|
// este pedido es para unlogged y está logueado, va al próximo
|
|
1690
1692
|
next();
|
|
1691
1693
|
}else{
|
|
@@ -1715,12 +1717,12 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1715
1717
|
if(!isLowerIdent(procedureDef.action)){
|
|
1716
1718
|
console.error('**** DEPRECATED ***** procedureDef action '+JSON.stringify(procedureDef.action)+' must be a Lower Ident');
|
|
1717
1719
|
}
|
|
1718
|
-
app[procedureDef.method]('/'+procedureDef.action,
|
|
1720
|
+
app[procedureDef.method]('/'+procedureDef.action,
|
|
1719
1721
|
/**
|
|
1720
|
-
*
|
|
1721
|
-
* @param {Request} req
|
|
1722
|
-
* @param {Response} res
|
|
1723
|
-
* @param {import('express').NextFunction} next
|
|
1722
|
+
*
|
|
1723
|
+
* @param {Request} req
|
|
1724
|
+
* @param {Response} res
|
|
1725
|
+
* @param {import('express').NextFunction} next
|
|
1724
1726
|
*/
|
|
1725
1727
|
async function(req, res, next){
|
|
1726
1728
|
const BITACORA_SCHEMA = be.config.server.bitacoraSchema;
|
|
@@ -1773,9 +1775,9 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1773
1775
|
init_date: initDatetimeString,
|
|
1774
1776
|
};
|
|
1775
1777
|
var getFinalStatusBitacoraElement = function getFinalStatusBitacoraElement(){
|
|
1776
|
-
return {
|
|
1777
|
-
end_date: getDatetimeString(),
|
|
1778
|
-
end_status: status,
|
|
1778
|
+
return {
|
|
1779
|
+
end_date: getDatetimeString(),
|
|
1780
|
+
end_status: status,
|
|
1779
1781
|
has_error: hasError
|
|
1780
1782
|
}
|
|
1781
1783
|
}
|
|
@@ -1805,12 +1807,12 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1805
1807
|
var params = getParams();
|
|
1806
1808
|
if(status){
|
|
1807
1809
|
//terminó ejecucion
|
|
1808
|
-
updateElement[procedureDef.bitacora.targetTableBitacoraFields.end_date] = getDatetimeString();
|
|
1810
|
+
updateElement[procedureDef.bitacora.targetTableBitacoraFields.end_date] = getDatetimeString();
|
|
1809
1811
|
updateElement[procedureDef.bitacora.targetTableBitacoraFields.end_status] = status;
|
|
1810
1812
|
updateElement[procedureDef.bitacora.targetTableBitacoraFields.has_error] = hasError;
|
|
1811
1813
|
}else{
|
|
1812
1814
|
//empezó ejecucion
|
|
1813
|
-
updateElement[procedureDef.bitacora.targetTableBitacoraFields.init_date] = initDatetimeString;
|
|
1815
|
+
updateElement[procedureDef.bitacora.targetTableBitacoraFields.init_date] = initDatetimeString;
|
|
1814
1816
|
}
|
|
1815
1817
|
await be.inTransaction(req,async function(client){
|
|
1816
1818
|
targetTableUpdateFieldsCondition.forEach(function(field){
|
|
@@ -2114,7 +2116,7 @@ AppBackend.prototype.optsGenericForFiles = function optsGenericForFiles(req, opt
|
|
|
2114
2116
|
return changing(be.optsGenericForAll||{},{
|
|
2115
2117
|
allowedExts:be.exts.normal,
|
|
2116
2118
|
jade:{
|
|
2117
|
-
skin:skin,
|
|
2119
|
+
skin:skin,
|
|
2118
2120
|
skinUrl:skinUrl,
|
|
2119
2121
|
formTitle:title,
|
|
2120
2122
|
...(opts?.withFlash ? {flash:req?.flash?.()} : {}),
|
|
@@ -2133,8 +2135,8 @@ AppBackend.prototype.unloggedLandPage = function unloggedLandPage(req){
|
|
|
2133
2135
|
html.h2({style:'text-align:center; margin-top:15%; font-family:arial, sans-serif'},[be.messages.server.notLoggedIn]),
|
|
2134
2136
|
html.h2({style:'text-align:center'},[html.code([
|
|
2135
2137
|
html.a({
|
|
2136
|
-
id:'goto-login',
|
|
2137
|
-
style:'border:0.5px solid blue; border-radius: 6px; padding: 6px',
|
|
2138
|
+
id:'goto-login',
|
|
2139
|
+
style:'border:0.5px solid blue; border-radius: 6px; padding: 6px',
|
|
2138
2140
|
href:Path.posix.join(be.config.server["base-url"],(be.config.login.plus.loginUrlPath||'/login'))
|
|
2139
2141
|
},'login')
|
|
2140
2142
|
])]),
|
|
@@ -2157,8 +2159,8 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
2157
2159
|
var now = bestGlobals.datetime.now();
|
|
2158
2160
|
var token = crypto.randomUUID();
|
|
2159
2161
|
var {rows} = await client.query(`select ${be.db.quoteIdent(be.config.login.userFieldName)} as username
|
|
2160
|
-
from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.db.quoteIdent(be.config.login.table)}
|
|
2161
|
-
where $1 in (${be.db.quoteIdentList(be.config.login.forget.mailFields)})
|
|
2162
|
+
from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.db.quoteIdent(be.config.login.table)}
|
|
2163
|
+
where $1 in (${be.db.quoteIdentList(be.config.login.forget.mailFields)})
|
|
2162
2164
|
and ${be.config.login.activeClausule}
|
|
2163
2165
|
and ${be.config.login.lockedClausule} is not true
|
|
2164
2166
|
`, [req.body.email.toLowerCase()]).fetchAll();
|
|
@@ -2266,21 +2268,21 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
2266
2268
|
// http://localhost:3033/img/login-logo-icon.png
|
|
2267
2269
|
mainApp.get(Path.posix.join(baseUrl,'/img/login-logo-icon.png'), async function(req,res,next){
|
|
2268
2270
|
var buscar = [
|
|
2269
|
-
'unlogged/img/login-logo-icon.svg',
|
|
2270
|
-
'dist/unlogged/img/login-logo-icon.svg',
|
|
2271
|
-
'dist/client/unlogged/img/login-logo-icon.svg',
|
|
2272
|
-
'unlogged/img/login-logo-icon.png',
|
|
2273
|
-
'dist/unlogged/img/login-logo-icon.png',
|
|
2274
|
-
'dist/client/unlogged/img/login-logo-icon.png',
|
|
2275
|
-
'unlogged/img/logo.png',
|
|
2276
|
-
'dist/unlogged/img/logo.png',
|
|
2277
|
-
'dist/client/unlogged/img/logo.png',
|
|
2278
|
-
'client/img/logo.png',
|
|
2279
|
-
'dist/client/img/logo.png',
|
|
2280
|
-
'dist/client/client/img/logo.png',
|
|
2271
|
+
'unlogged/img/login-logo-icon.svg',
|
|
2272
|
+
'dist/unlogged/img/login-logo-icon.svg',
|
|
2273
|
+
'dist/client/unlogged/img/login-logo-icon.svg',
|
|
2274
|
+
'unlogged/img/login-logo-icon.png',
|
|
2275
|
+
'dist/unlogged/img/login-logo-icon.png',
|
|
2276
|
+
'dist/client/unlogged/img/login-logo-icon.png',
|
|
2277
|
+
'unlogged/img/logo.png',
|
|
2278
|
+
'dist/unlogged/img/logo.png',
|
|
2279
|
+
'dist/client/unlogged/img/logo.png',
|
|
2280
|
+
'client/img/logo.png',
|
|
2281
|
+
'dist/client/img/logo.png',
|
|
2282
|
+
'dist/client/client/img/logo.png',
|
|
2281
2283
|
'unlogged/img/logo-128.png',
|
|
2282
2284
|
'dist/unlogged/img/logo-128.png',
|
|
2283
|
-
'dist/client/unlogged/img/logo-128.png'
|
|
2285
|
+
'dist/client/unlogged/img/logo-128.png'
|
|
2284
2286
|
];
|
|
2285
2287
|
buscar = buscar.map(n=>be.rootPath+'/'+n);
|
|
2286
2288
|
buscar.push(__dirname+'/../for-client/img/login-logo-icon.png');
|
|
@@ -2295,7 +2297,7 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
2295
2297
|
be.clientIncludesCompleted(null).filter(x => x.module).forEach(function (moduleDef) {
|
|
2296
2298
|
if(baseUrl=='/'){
|
|
2297
2299
|
baseUrl='';
|
|
2298
|
-
}
|
|
2300
|
+
}
|
|
2299
2301
|
let baseLib = baseUrl + '/' + (moduleDef.path ? moduleDef.path : be.esJavascript(moduleDef.type)? 'lib': 'css');
|
|
2300
2302
|
resolve_module_dir(moduleDef.module, moduleDef.modPath, moduleDef.file ?? '.')
|
|
2301
2303
|
try {
|
|
@@ -2319,7 +2321,7 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
2319
2321
|
// ----------------------------------------------------
|
|
2320
2322
|
var skin=be.config['client-setup'].skin;
|
|
2321
2323
|
var skinUrl=(skin?skin+'/':'');
|
|
2322
|
-
var optsGenericForFilesUnlogged=be.optsGenericForFiles();
|
|
2324
|
+
var optsGenericForFilesUnlogged=be.optsGenericForFiles();
|
|
2323
2325
|
var skinPaths=[Path.join(be.rootPath,'skins')];
|
|
2324
2326
|
if(be.config.server.skins[skin]['local-path']){
|
|
2325
2327
|
skinPaths=skinPaths.concat(be.config.server.skins[skin]['local-path']).map(function(path){
|
|
@@ -2372,12 +2374,14 @@ AppBackend.prototype.getVisibleMenu = function getVisibleMenu(menu, context){
|
|
|
2372
2374
|
|
|
2373
2375
|
AppBackend.prototype.clientIncludes = function clientIncludes(req, opts) {
|
|
2374
2376
|
const hideBEPlusInclusions = opts === true || opts && typeof opts == "object" && opts.hideBEPlusInclusions;
|
|
2375
|
-
opts = opts === true ? {} : opts || {};
|
|
2377
|
+
opts = opts === true ? {} : opts || {};
|
|
2376
2378
|
var list = [];
|
|
2377
2379
|
if (!hideBEPlusInclusions) {
|
|
2378
2380
|
list = [
|
|
2379
2381
|
// { type: 'js', module: 'xlsx', modPath: 'dist', file: 'xlsx.core.min.js' },
|
|
2380
|
-
{ type: 'js', module: '
|
|
2382
|
+
{ type: 'js', module: 'fflate', modPath: '../umd', file: 'index.js' },
|
|
2383
|
+
{ type: 'js', module: 'xlsx-now', modPath: '../../umd', file: 'xlsx-now.umd.js' },
|
|
2384
|
+
{ type: 'js', module: 'xlsx-now', modPath: '../../umd', file: 'xlsx-now-browser.umd.js' },
|
|
2381
2385
|
{ type: 'js', module: 'require-bro' },
|
|
2382
2386
|
{ type: 'js', module: 'js-yaml', modPath: 'browser', file: 'js-yaml.umd.min.js' },
|
|
2383
2387
|
{ type: 'js', module: 'cast-error' },
|
|
@@ -2463,7 +2467,7 @@ AppBackend.prototype.clientModules = function clientModules(req, opts) {
|
|
|
2463
2467
|
}
|
|
2464
2468
|
|
|
2465
2469
|
/**
|
|
2466
|
-
* @param {string} tableName
|
|
2470
|
+
* @param {string} tableName
|
|
2467
2471
|
* @param {(tableDef:typesOpe.TableDefinition, context?:TableContext)=>void} appenderFunction
|
|
2468
2472
|
*/
|
|
2469
2473
|
AppBackend.prototype.appendToTableDefinition = function appendToTableDefinition(tableName, appenderFunction){
|
|
@@ -2520,16 +2524,16 @@ AppBackend.prototype.csss = function csss(hideBEPlusInclusions){
|
|
|
2520
2524
|
AppBackend.prototype.scanSkinFiles = function scanSkinFiles() {
|
|
2521
2525
|
var be = this;
|
|
2522
2526
|
var skinName = be.config['client-setup'].skin;
|
|
2523
|
-
be.activeSkinFiles = new Set();
|
|
2527
|
+
be.activeSkinFiles = new Set();
|
|
2524
2528
|
|
|
2525
2529
|
var skinConfig = be.config.server.skins && be.config.server.skins[skinName];
|
|
2526
2530
|
|
|
2527
2531
|
if (skinName && skinConfig && skinConfig['local-path']) {
|
|
2528
2532
|
try {
|
|
2529
2533
|
var skinPath = Path.join(Path.resolve(skinConfig['local-path']), skinName);
|
|
2530
|
-
|
|
2534
|
+
|
|
2531
2535
|
if (fs.existsSync(skinPath)) {
|
|
2532
|
-
|
|
2536
|
+
|
|
2533
2537
|
const walk = (currentPath) => {
|
|
2534
2538
|
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
2535
2539
|
|
|
@@ -2544,7 +2548,7 @@ AppBackend.prototype.scanSkinFiles = function scanSkinFiles() {
|
|
|
2544
2548
|
var relativePath = Path.relative(skinPath, fullPath)
|
|
2545
2549
|
.replace(/\\/g, '/')
|
|
2546
2550
|
.replace(/^\//, '');
|
|
2547
|
-
|
|
2551
|
+
|
|
2548
2552
|
be.activeSkinFiles.add(relativePath);
|
|
2549
2553
|
}
|
|
2550
2554
|
}
|
|
@@ -2631,7 +2635,7 @@ AppBackend.prototype.mainPage = function mainPage(req, offlineMode, opts){
|
|
|
2631
2635
|
|
|
2632
2636
|
var lastDotIndex = css.lastIndexOf('.');
|
|
2633
2637
|
var cssBase = (lastDotIndex !== -1) ? css.substring(0, lastDotIndex) : css;
|
|
2634
|
-
|
|
2638
|
+
|
|
2635
2639
|
var existsInSkin = EXTENSIONES_SKIN.some(function(ext) {
|
|
2636
2640
|
return be.activeSkinFiles.has(cssBase + ext);
|
|
2637
2641
|
});
|
|
@@ -2897,7 +2901,7 @@ AppBackend.prototype.validateBitacora = function validateBitacora(procedureDef){
|
|
|
2897
2901
|
var tableDefFields = be.tableStructures[procedureDef.bitacora.targetTable](contextForDump).fields;
|
|
2898
2902
|
var targetTableBitacoraFields = procedureDef.bitacora.targetTableBitacoraFields;
|
|
2899
2903
|
for (var fieldForSearch in targetTableBitacoraFields) {
|
|
2900
|
-
var searchResult = tableDefFields.find(function findByName(field) {
|
|
2904
|
+
var searchResult = tableDefFields.find(function findByName(field) {
|
|
2901
2905
|
return field.name === targetTableBitacoraFields[fieldForSearch];
|
|
2902
2906
|
});
|
|
2903
2907
|
if(!searchResult){
|
|
@@ -2905,12 +2909,12 @@ AppBackend.prototype.validateBitacora = function validateBitacora(procedureDef){
|
|
|
2905
2909
|
}
|
|
2906
2910
|
}
|
|
2907
2911
|
var targetTableUpdateFieldsCondition = procedureDef.bitacora.targetTableUpdateFieldsCondition || ['init_date','end_date','has_error', 'end_status'];
|
|
2908
|
-
if(targetTableUpdateFieldsCondition){
|
|
2912
|
+
if(targetTableUpdateFieldsCondition){
|
|
2909
2913
|
if(targetTableUpdateFieldsCondition.length == 0){
|
|
2910
2914
|
throw Error("Bitacora bad definition in core function '" + procedureDef.action + "', targetTableUpdateFieldsCondition must to be defined for table '" + procedureDef.bitacora.targetTable + "'.");
|
|
2911
2915
|
}
|
|
2912
2916
|
targetTableUpdateFieldsCondition.forEach(function(fieldName){
|
|
2913
|
-
var searchResult = tableDefFields.find(function findByName(field) {
|
|
2917
|
+
var searchResult = tableDefFields.find(function findByName(field) {
|
|
2914
2918
|
return field.name === fieldName;
|
|
2915
2919
|
});
|
|
2916
2920
|
if(!searchResult){
|
|
@@ -3036,11 +3040,13 @@ AppBackend.prototype.dumpDbTableFields = function dumpDbTableFields(tableDef, op
|
|
|
3036
3040
|
}
|
|
3037
3041
|
fields.push(
|
|
3038
3042
|
' '+db.quoteIdent(fieldDef.name)+
|
|
3039
|
-
' '+(fieldDef.dataLength?(fieldType=='text'?'varchar':fieldType)+'('+fieldDef.dataLength+
|
|
3043
|
+
' '+(fieldDef.dataLength?(fieldType=='text'?'varchar':fieldType)+'('+fieldDef.dataLength+
|
|
3044
|
+
(fieldDef.dataDecimals?','+fieldDef.dataDecimals:'')
|
|
3045
|
+
+')':fieldType)+
|
|
3040
3046
|
( be.specialSqlDefaultExpressions[fieldDef.defaultDbValue] != null ? ' default ' + be.specialSqlDefaultExpressions[fieldDef.defaultDbValue]
|
|
3041
|
-
: fieldDef.defaultDbValue != null ? ' default ' + fieldDef.defaultDbValue
|
|
3047
|
+
: fieldDef.defaultDbValue != null ? ' default ' + fieldDef.defaultDbValue
|
|
3042
3048
|
: be.specialSqlDefaultExpressions[fieldDef.specialDefaultValue] != null ? ' default ' + be.specialSqlDefaultExpressions[fieldDef.specialDefaultValue]
|
|
3043
|
-
: fieldDef.defaultValue != null ? ' default ' + db.quoteLiteral(fieldDef.defaultValue)
|
|
3049
|
+
: fieldDef.defaultValue != null ? ' default ' + db.quoteLiteral(fieldDef.defaultValue)
|
|
3044
3050
|
: ''
|
|
3045
3051
|
) +
|
|
3046
3052
|
(be.isGeneratedSequence(fieldDef.sequence)?' generated always as identity':'')+
|
|
@@ -3193,7 +3199,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3193
3199
|
lines.push(');');
|
|
3194
3200
|
//TODO: REFACTOR: Hacerlo mas sencillo
|
|
3195
3201
|
// este codigo se encarga de convertir a rights de sql nuestros propios rights
|
|
3196
|
-
// por ej (import -> [insert, update])
|
|
3202
|
+
// por ej (import -> [insert, update])
|
|
3197
3203
|
var allows = tableDef.allow;
|
|
3198
3204
|
var appToSqlRights = {'import': ['insert', 'update'], 'export': ['select'], 'deleteAll': ['delete']};
|
|
3199
3205
|
[ 'import', 'export', 'deleteAll'].filter(function(right){
|
|
@@ -3250,7 +3256,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3250
3256
|
var prefix = 'alter table '+cualQuoteTableName+' add '+
|
|
3251
3257
|
(cons.consName?'constraint '+db.quoteIdent(cons.consName)+' ':'');
|
|
3252
3258
|
switch(cons.constraintType){
|
|
3253
|
-
case 'unique':
|
|
3259
|
+
case 'unique':
|
|
3254
3260
|
sql='('+cons.fields.map(function(field){ return db.quoteIdent(field); }).join(', ')+')';
|
|
3255
3261
|
if(cons.where){
|
|
3256
3262
|
if(cons.consName == null){
|
|
@@ -3262,10 +3268,10 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3262
3268
|
prefix += 'unique ';
|
|
3263
3269
|
}
|
|
3264
3270
|
break;
|
|
3265
|
-
case 'check':
|
|
3271
|
+
case 'check':
|
|
3266
3272
|
sql='check ('+cons.expr+')';
|
|
3267
3273
|
break;
|
|
3268
|
-
case 'exclude':
|
|
3274
|
+
case 'exclude':
|
|
3269
3275
|
sql=`exclude using ${cons.using} (${cons.fields.map(f => typeof f == "string"?`${f} WITH =`:`${f.fieldName} WITH ${f.operator}`).join(', ')})${cons.where ? ` WHERE (${cons.where})`:``}`;
|
|
3270
3276
|
break;
|
|
3271
3277
|
default:
|
|
@@ -3308,7 +3314,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3308
3314
|
(polcom.using? ` USING ( ${polcom.using} )`:'')+
|
|
3309
3315
|
(polcom.check?` WITH CHECK ( ${polcom.check} )`:'')+';'
|
|
3310
3316
|
);
|
|
3311
|
-
}
|
|
3317
|
+
}
|
|
3312
3318
|
});
|
|
3313
3319
|
}
|
|
3314
3320
|
}else{
|
|
@@ -3349,7 +3355,7 @@ begin
|
|
|
3349
3355
|
else
|
|
3350
3356
|
select ${be.config.login.infoFieldList.map(fieldName => db.quoteIdent(fieldName)).join(', ')}
|
|
3351
3357
|
into ${be.config.login.infoFieldList.map(fieldName => db.quoteIdent('v_'+fieldName)).join(', ')}
|
|
3352
|
-
|
|
3358
|
+
|
|
3353
3359
|
from ${(be.config.login.from ?? (
|
|
3354
3360
|
(be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':'')+
|
|
3355
3361
|
be.db.quoteIdent(be.config.login.table)))}
|
|
@@ -3360,7 +3366,7 @@ begin
|
|
|
3360
3366
|
set backend_plus._mode = normal;
|
|
3361
3367
|
end if;
|
|
3362
3368
|
perform set_config('backend_plus._user', p_username, false);
|
|
3363
|
-
end;
|
|
3369
|
+
end;
|
|
3364
3370
|
$body$;
|
|
3365
3371
|
|
|
3366
3372
|
`)
|
|
@@ -3374,7 +3380,7 @@ $body$;
|
|
|
3374
3380
|
var allTableContent = await fs.readFile('install/local-dump.psql','utf-8');
|
|
3375
3381
|
var startIndex = allTableContent.indexOf('-- Data for Name: ');
|
|
3376
3382
|
console.log('startIndex', startIndex);
|
|
3377
|
-
var lastUseful = allTableContent.lastIndexOf('\nSELECT pg_catalog.setval')
|
|
3383
|
+
var lastUseful = allTableContent.lastIndexOf('\nSELECT pg_catalog.setval')
|
|
3378
3384
|
console.log('lastUseful', lastUseful);
|
|
3379
3385
|
if (lastUseful == -1) lastUseful = allTableContent.lastIndexOf('\n\\.\n');
|
|
3380
3386
|
console.log('lastUseful', lastUseful);
|
|
@@ -3454,6 +3460,7 @@ $body$;
|
|
|
3454
3460
|
});
|
|
3455
3461
|
rows=lines;
|
|
3456
3462
|
}else{
|
|
3463
|
+
/* PARSEO DEL ARCHIVO .TAB */
|
|
3457
3464
|
var lines=content.split(/\r?\n/)
|
|
3458
3465
|
.filter(line => !(/^[-| ]*$/.test(line)) )
|
|
3459
3466
|
.map(line => splitRawRowIntoRow(line))
|
|
@@ -3487,14 +3494,61 @@ $body$;
|
|
|
3487
3494
|
throw Error("no se encuentra la columna "+filteredFieldDef[i]+" en "+tableName);
|
|
3488
3495
|
}
|
|
3489
3496
|
return value==='' ? (
|
|
3490
|
-
def.allowEmptyText && ('nullable' in def) && !def.nullable ? "''" : 'null'
|
|
3497
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? "''" : 'null'
|
|
3491
3498
|
) : db.quoteNullable(value);
|
|
3492
3499
|
}).join(', ')+")";
|
|
3493
3500
|
}).join(',\n')+';\n';
|
|
3494
3501
|
}
|
|
3495
3502
|
dataText.push(dataString);
|
|
3503
|
+
if(process.env.TRY_TAB_PLUS){
|
|
3504
|
+
var tabPlusEmptySymbol = Symbol('empty');
|
|
3505
|
+
var parsedTabPlus = tabPlus.parseTab(content, {emptyField: tabPlusEmptySymbol});
|
|
3506
|
+
var tabPlusFields = parsedTabPlus.fields;
|
|
3507
|
+
var tabPlusRows = parsedTabPlus.rows;
|
|
3508
|
+
/* a partir de acá: solo construcción de SQL a partir de {fields,rows}, sin volver a tocar el parseo */
|
|
3509
|
+
var filteredFieldDefTabPlus = tabPlusFields.filter(filterField);
|
|
3510
|
+
var dataStringTabPlus;
|
|
3511
|
+
if(tablesWithStrictSequence[tableName]){
|
|
3512
|
+
dataStringTabPlus="COPY "+db.quoteIdent(tableName)+" ("+
|
|
3513
|
+
filteredFieldDefTabPlus.map(db.quoteIdent).join(', ')+
|
|
3514
|
+
') FROM stdin;\n'+
|
|
3515
|
+
tabPlusRows.map(function(line){
|
|
3516
|
+
return line.filter(function(_,i){ return filterField(tabPlusFields[i]);}).map(function(value,i){
|
|
3517
|
+
var def = tableDef.field[filteredFieldDefTabPlus[i]];
|
|
3518
|
+
return value===tabPlusEmptySymbol ? (
|
|
3519
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? '' : '\\N'
|
|
3520
|
+
): value.replace(/\\/g,'\\\\').replace(/\t/g,'\\t').replace(/\n/g,'\\n').replace(/\r/g,'\\r');
|
|
3521
|
+
}).join('\t')+'\n';
|
|
3522
|
+
}).join('')+'\\.\n';
|
|
3523
|
+
}else{
|
|
3524
|
+
dataStringTabPlus="insert into "+db.quoteIdent(tableName)+" ("+
|
|
3525
|
+
filteredFieldDefTabPlus.map(db.quoteIdent).join(', ')+
|
|
3526
|
+
') values\n'+
|
|
3527
|
+
tabPlusRows.map(function(line){
|
|
3528
|
+
return "("+line.filter(function(_,i){ return filterField(tabPlusFields[i]);}).map(function(value,i){
|
|
3529
|
+
var def = tableDef.field[filteredFieldDefTabPlus[i]];
|
|
3530
|
+
if(def == null) {
|
|
3531
|
+
throw Error("no se encuentra la columna "+filteredFieldDefTabPlus[i]+" en "+tableName);
|
|
3532
|
+
}
|
|
3533
|
+
return value===tabPlusEmptySymbol ? (
|
|
3534
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? "''" : 'null'
|
|
3535
|
+
) : db.quoteNullable(value);
|
|
3536
|
+
}).join(', ')+")";
|
|
3537
|
+
}).join(',\n')+';\n';
|
|
3538
|
+
}
|
|
3539
|
+
if(dataStringTabPlus !== dataString){
|
|
3540
|
+
var oldSqlPath = path.replace(/\.tab$/,'')+'-old-local.sql';
|
|
3541
|
+
var newSqlPath = path.replace(/\.tab$/,'')+'-new-local.sql';
|
|
3542
|
+
fs.writeFileSync(oldSqlPath, dataString, {encoding:'UTF8'});
|
|
3543
|
+
fs.writeFileSync(newSqlPath, dataStringTabPlus, {encoding:'UTF8'});
|
|
3544
|
+
if (process.env.TRY_TAB_PLUS == 'ON-ERROR-STOP') {
|
|
3545
|
+
throw new Error('TRY_TAB_PLUS: el parseo con tab-plus difiere del parseo anterior para '+tableName+' ('+path+'). Ver '+oldSqlPath+' y '+newSqlPath);
|
|
3546
|
+
} else {
|
|
3547
|
+
console.log('TRY_TAB_PLUS: el parseo con tab-plus difiere del parseo anterior para '+tableName+' ('+path+'). Ver '+oldSqlPath+' y '+newSqlPath);
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3496
3551
|
}
|
|
3497
|
-
// tablesWithStrictSequence[tableName]
|
|
3498
3552
|
}
|
|
3499
3553
|
}).catch(function(err){
|
|
3500
3554
|
if(err.code=='ENOENT'){
|
|
@@ -3522,7 +3576,7 @@ $body$;
|
|
|
3522
3576
|
]
|
|
3523
3577
|
.map(async function(fileNames){
|
|
3524
3578
|
if (!fileNames) return '';
|
|
3525
|
-
var i = 0;
|
|
3579
|
+
var i = 0;
|
|
3526
3580
|
return (await Promise.all(fileNames.map(async fileName => {
|
|
3527
3581
|
var content;
|
|
3528
3582
|
do {
|
|
@@ -3574,7 +3628,7 @@ $body$;
|
|
|
3574
3628
|
'\n-- functions\n' + functionLines.join('\n')+
|
|
3575
3629
|
'\n-- lines \n' + lines.join('\n')+
|
|
3576
3630
|
(complete? ('\n\n-- pre-ADAPTs\n'+texts[1]+'\n\n') : '' )+
|
|
3577
|
-
(complete? ('\n\n-- DATA\n'+ dataText.join('\n')) : '' )+
|
|
3631
|
+
(complete? ('\n\n-- DATA\n'+ dataText.join('\n')) : '' )+
|
|
3578
3632
|
(complete? ('\n\n-- ADAPTs\n'+ texts[2]+'\n\n') : '' )+
|
|
3579
3633
|
'\n-- conss\n' + consLines.join('\n')+
|
|
3580
3634
|
'\n-- FKs\n' + fkLines.join('\n')+
|
|
@@ -3604,14 +3658,17 @@ AppBackend.prototype.getDbFunctions = async function (){
|
|
|
3604
3658
|
AppBackend.prototype.dumpDbSchema = async function dumpDbSchema(opts){
|
|
3605
3659
|
var be = this;
|
|
3606
3660
|
var {mainSql,enancePart} = await be.dumpDbSchemaPartial(
|
|
3607
|
-
opts.complete?be.tableStructures:likeAr(be.tableStructures).filter((_, name)=>opts.tableNames.includes(name)),
|
|
3661
|
+
opts.complete?be.tableStructures:likeAr(be.tableStructures).filter((_, name)=>opts.tableNames.includes(name)),
|
|
3608
3662
|
opts
|
|
3609
3663
|
)
|
|
3610
3664
|
mainSql=be.config.install.dump.db.extensions.map(function(extension){
|
|
3611
3665
|
return ({
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3666
|
+
cube : "create extension if not exists cube ;",
|
|
3667
|
+
earthdistance : "create extension if not exists earthdistance ;",
|
|
3668
|
+
gist : "create extension if not exists btree_gist ;",
|
|
3669
|
+
pg_trgm : "create extension if not exists pg_trgm ;",
|
|
3670
|
+
pgcrypto : "create extension if not exists pgcrypto ;",
|
|
3671
|
+
postgis : "create extension if not exists postgis ;",
|
|
3615
3672
|
}[extension]||('--unknown exension '+extension))+'\n';
|
|
3616
3673
|
}).join('')+mainSql;
|
|
3617
3674
|
await fs.writeFile('local-db-dump.sql', mainSql);
|
|
@@ -3684,7 +3741,7 @@ AppBackend.prototype.transformInput = function transformInput(fieldDef, value){
|
|
|
3684
3741
|
return value;
|
|
3685
3742
|
}
|
|
3686
3743
|
|
|
3687
|
-
/**
|
|
3744
|
+
/**
|
|
3688
3745
|
* xxxparam {{ action:string, parameters:any, conRegistro:boolean, conPadron:boolean, fileName?:string, csvFileName?:string, csvSeparator?:string, queries:{titulo:string, sql:string, params:string[]}[] }}
|
|
3689
3746
|
* @param {{title:string, rows:Record<string, any>[]}[]} result
|
|
3690
3747
|
* @returns {Promise<void>}
|
|
@@ -3782,7 +3839,7 @@ AppBackend.prototype.exportacionesGenerico = async function exportacionesGeneric
|
|
|
3782
3839
|
))
|
|
3783
3840
|
}
|
|
3784
3841
|
return [
|
|
3785
|
-
...(fileName?[{url:fileName, label:csvFileName?'xlsx de control':fileName}]:[]),
|
|
3842
|
+
...(fileName?[{url:fileName, label:csvFileName?'xlsx de control':fileName}]:[]),
|
|
3786
3843
|
...(csvFileName?[{url:csvFileName, label:fileName?'csv (formato UTF-8)':csvFileName}]:[]),
|
|
3787
3844
|
];
|
|
3788
3845
|
}
|