backend-plus 2.7.0-beta.1 → 2.7.0-beta.12
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 +180 -140
- 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 +51 -36
- package/lib/backend-plus.js +204 -124
- 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 +35 -29
- 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');
|
|
@@ -62,7 +63,37 @@ var myOwn = require('../for-client/my-things.js');
|
|
|
62
63
|
|
|
63
64
|
var typeStore = require('type-store');
|
|
64
65
|
var json4all = require('json4all');
|
|
65
|
-
var
|
|
66
|
+
var xlsxNow = require('xlsx-now/node');
|
|
67
|
+
var xlsxNowCore = require('xlsx-now');
|
|
68
|
+
var Big = require('big.js');
|
|
69
|
+
|
|
70
|
+
// Los tipos que las queries devuelven y no son valores nativos de una celda.
|
|
71
|
+
// Sin esto xlsx-now los rechaza, porque un objeto que nadie reclama no tiene
|
|
72
|
+
// una única forma correcta de escribirse.
|
|
73
|
+
var xlsxTypes = xlsxNowCore.defaultTypes;
|
|
74
|
+
// Un timestamp es una fecha: se escribe como tal y no como texto.
|
|
75
|
+
xlsxTypes = xlsxNowCore.withType(xlsxTypes, bestGlobals.Datetime, {
|
|
76
|
+
convert: (value, context) => xlsxNowCore.dateValue(new Date(value.getTime()), context)
|
|
77
|
+
});
|
|
78
|
+
// Igual que en la exportación del cliente: días, con formato de duración.
|
|
79
|
+
xlsxTypes = xlsxNowCore.withType(xlsxTypes, bestGlobals.TimeInterval, {
|
|
80
|
+
convert: (value) => ({v:value.timeInterval.ms?value.timeInterval.ms/(1000*60*60*24):null, numFmt:'[h]:mm:ss'})
|
|
81
|
+
});
|
|
82
|
+
// decimal y hugeint. Como texto cuando el double ya no los representa exacto,
|
|
83
|
+
// para no devolver un número redondeado con apariencia de exacto.
|
|
84
|
+
xlsxTypes = xlsxNowCore.withType(xlsxTypes, Big, {
|
|
85
|
+
convert: function(value){
|
|
86
|
+
var text = value.toString();
|
|
87
|
+
var asNumber = Number(text);
|
|
88
|
+
return Number.isSafeInteger(asNumber) || String(asNumber) === text
|
|
89
|
+
? {v:asNumber}
|
|
90
|
+
: {v:text, t:'inlineStr'};
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
// jsonb, jsona, gpoint, point y cualquier otro objeto sin tipo propio.
|
|
94
|
+
xlsxTypes = xlsxNowCore.withType(xlsxTypes, Object, {
|
|
95
|
+
convert: (value) => ({v:JSON.stringify(value), t:'inlineStr'})
|
|
96
|
+
});
|
|
66
97
|
|
|
67
98
|
var loginPlus = require('login-plus');
|
|
68
99
|
var dashDashDir=Array.prototype.indexOf.call(process.argv,'--dir')
|
|
@@ -73,7 +104,7 @@ if(dashDashDir>0){
|
|
|
73
104
|
console.log('cwd',process.cwd());
|
|
74
105
|
}
|
|
75
106
|
|
|
76
|
-
/**
|
|
107
|
+
/**
|
|
77
108
|
* @param {string} text
|
|
78
109
|
* @return {string}
|
|
79
110
|
*/
|
|
@@ -81,14 +112,14 @@ function md5(text){
|
|
|
81
112
|
return crypto.createHash('md5').update(text).digest('hex');
|
|
82
113
|
}
|
|
83
114
|
|
|
84
|
-
const DEFAULT_ITERATIONS = 4096;
|
|
115
|
+
const DEFAULT_ITERATIONS = 4096;
|
|
85
116
|
const HASH_ALGORITHM = 'sha256';
|
|
86
117
|
|
|
87
118
|
// primera version de scram sha 256. El Verifier completo tenía 64 bytes.
|
|
88
|
-
const LEGACY_KEY_LENGTH = 64;
|
|
119
|
+
const LEGACY_KEY_LENGTH = 64;
|
|
89
120
|
|
|
90
121
|
// Formato nuevo/PG-Compatible: El Client Key tiene 32 bytes (SHA-256).
|
|
91
|
-
const SCRAM_KEY_LENGTH = 32;
|
|
122
|
+
const SCRAM_KEY_LENGTH = 32;
|
|
92
123
|
|
|
93
124
|
const bufferToBase64 = (buffer) => buffer.toString('base64');
|
|
94
125
|
|
|
@@ -96,31 +127,31 @@ const bufferToBase64 = (buffer) => buffer.toString('base64');
|
|
|
96
127
|
* Función central para PBKDF2 (Acepta KEY_LEN para compatibilidad).
|
|
97
128
|
* Devuelve la clave derivada con la longitud especificada.
|
|
98
129
|
*/
|
|
99
|
-
async function deriveKey(password, saltBuffer, iterations, keyLength) {
|
|
130
|
+
async function deriveKey(password, saltBuffer, iterations, keyLength) {
|
|
100
131
|
return new Promise((resolve, reject) => {
|
|
101
132
|
crypto.pbkdf2(
|
|
102
|
-
password,
|
|
133
|
+
password,
|
|
103
134
|
saltBuffer,
|
|
104
|
-
iterations,
|
|
135
|
+
iterations,
|
|
105
136
|
keyLength,
|
|
106
|
-
HASH_ALGORITHM,
|
|
137
|
+
HASH_ALGORITHM,
|
|
107
138
|
(err, derivedKey) => {
|
|
108
139
|
if (err) return reject(err);
|
|
109
|
-
resolve(derivedKey);
|
|
140
|
+
resolve(derivedKey);
|
|
110
141
|
}
|
|
111
142
|
);
|
|
112
143
|
});
|
|
113
144
|
}
|
|
114
145
|
|
|
115
146
|
async function generateScramVerifier(password) {
|
|
116
|
-
const saltBuffer = crypto.randomBytes(16);
|
|
147
|
+
const saltBuffer = crypto.randomBytes(16);
|
|
117
148
|
const saltBase64 = bufferToBase64(saltBuffer);
|
|
118
149
|
|
|
119
150
|
const Hi = await deriveKey(
|
|
120
|
-
password,
|
|
121
|
-
saltBuffer,
|
|
151
|
+
password,
|
|
152
|
+
saltBuffer,
|
|
122
153
|
DEFAULT_ITERATIONS,
|
|
123
|
-
SCRAM_KEY_LENGTH
|
|
154
|
+
SCRAM_KEY_LENGTH
|
|
124
155
|
);
|
|
125
156
|
const clientKey = crypto.createHmac('sha256', Hi).update('Client Key').digest();
|
|
126
157
|
const serverKey = crypto.createHmac('sha256', Hi).update('Server Key').digest();
|
|
@@ -144,27 +175,27 @@ async function verifyScramPG(password, storedScramString) {
|
|
|
144
175
|
if (!storedScramString.startsWith('SCRAM-SHA-256$')) {
|
|
145
176
|
return false;
|
|
146
177
|
}
|
|
147
|
-
|
|
178
|
+
|
|
148
179
|
const parts = storedScramString.split('$');
|
|
149
180
|
if (parts.length !== 3) {
|
|
150
181
|
return false;
|
|
151
182
|
}
|
|
152
|
-
|
|
183
|
+
|
|
153
184
|
const [storedIterations, saltBase64] = parts[1].split(':');
|
|
154
185
|
const [storedKeyBase64, serverKeyBase64] = parts[2].split(':'); // Asume StoredKey y ServerKey
|
|
155
186
|
|
|
156
187
|
if (!saltBase64 || !storedKeyBase64 || !serverKeyBase64 || isNaN(parseInt(storedIterations))) {
|
|
157
188
|
return false;
|
|
158
189
|
}
|
|
159
|
-
|
|
190
|
+
|
|
160
191
|
const iterations = parseInt(storedIterations);
|
|
161
192
|
const saltBuffer = Buffer.from(saltBase64, 'base64');
|
|
162
193
|
const storedKeyBuffer = Buffer.from(storedKeyBase64, 'base64');
|
|
163
194
|
const serverKeyBuffer = Buffer.from(serverKeyBase64, 'base64');
|
|
164
195
|
|
|
165
196
|
const Hi = await deriveKey(
|
|
166
|
-
password,
|
|
167
|
-
saltBuffer,
|
|
197
|
+
password,
|
|
198
|
+
saltBuffer,
|
|
168
199
|
iterations,
|
|
169
200
|
SCRAM_KEY_LENGTH
|
|
170
201
|
);
|
|
@@ -203,21 +234,21 @@ async function verifyScramLegacy(password, storedScramString) {
|
|
|
203
234
|
if (parts.length !== 3) {
|
|
204
235
|
throw new Error('Formato SCRAM almacenado inválido.');
|
|
205
236
|
}
|
|
206
|
-
|
|
237
|
+
|
|
207
238
|
// Obtiene iteraciones y salt de la segunda parte (ej: '4096:SALT_BASE64')
|
|
208
239
|
const [storedIterations, storedSalt] = parts[1].split(':');
|
|
209
240
|
const storedVerifier = parts[2];
|
|
210
|
-
|
|
241
|
+
|
|
211
242
|
if (!storedSalt || !storedVerifier || isNaN(parseInt(storedIterations))) {
|
|
212
243
|
throw new Error('Datos de SCRAM incompletos o malformados.');
|
|
213
244
|
}
|
|
214
|
-
|
|
245
|
+
|
|
215
246
|
const iterations = parseInt(storedIterations);
|
|
216
247
|
|
|
217
248
|
// Deriva la clave de la contraseña ingresada
|
|
218
249
|
const generatedVerifierBuffer = await deriveKey(
|
|
219
|
-
password,
|
|
220
|
-
storedSalt,
|
|
250
|
+
password,
|
|
251
|
+
storedSalt,
|
|
221
252
|
iterations,
|
|
222
253
|
LEGACY_KEY_LENGTH
|
|
223
254
|
);
|
|
@@ -313,7 +344,7 @@ AppBackend.prototype.configStaticConfig = function configStaticConfig(){
|
|
|
313
344
|
min-version: 12
|
|
314
345
|
fkOnUpdate: cascade
|
|
315
346
|
max: 50
|
|
316
|
-
log:
|
|
347
|
+
log:
|
|
317
348
|
db:
|
|
318
349
|
until: 2001-01-01 00:00
|
|
319
350
|
last-error: false
|
|
@@ -326,7 +357,7 @@ AppBackend.prototype.configStaticConfig = function configStaticConfig(){
|
|
|
326
357
|
"":
|
|
327
358
|
local-path: for-client
|
|
328
359
|
bin: {}
|
|
329
|
-
client-setup:
|
|
360
|
+
client-setup:
|
|
330
361
|
skin: ""
|
|
331
362
|
lang: en
|
|
332
363
|
version: 1.0
|
|
@@ -589,7 +620,7 @@ function MemoryPerodicallySaved(session){
|
|
|
589
620
|
})}
|
|
590
621
|
|
|
591
622
|
/**
|
|
592
|
-
* @param {string} text
|
|
623
|
+
* @param {string} text
|
|
593
624
|
*/
|
|
594
625
|
AppBackend.prototype.jsonPass = function jsonPass(text){
|
|
595
626
|
return JSON.stringify(text,null,' ').replace(/\n(.*".*(pass|clave|secret).*":\s*").*(",?)\n/gi,'\n$1********$3\n');
|
|
@@ -751,7 +782,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
751
782
|
while(iPosDbDump && iPosDbDump<process.argv.length && !process.argv[iPosDbDump].startsWith('--')){
|
|
752
783
|
dumps.push(process.argv[iPosDbDump]);
|
|
753
784
|
iPosDbDump++;
|
|
754
|
-
}
|
|
785
|
+
}
|
|
755
786
|
opts["dump-db"]={
|
|
756
787
|
complete:!dumps.length,
|
|
757
788
|
tableNames:dumps instanceof Array?dumps:null,
|
|
@@ -888,8 +919,8 @@ AppBackend.prototype.start = function start(opts){
|
|
|
888
919
|
}
|
|
889
920
|
be.db = pg;
|
|
890
921
|
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)}
|
|
922
|
+
be.dbUserRolExpr=`(select ${be.db.quoteIdent(be.config.login.rolFieldName)}
|
|
923
|
+
from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.db.quoteIdent(be.config.login.table)}
|
|
893
924
|
where ${be.db.quoteIdent(be.config.login.userFieldName)} = ${be.dbUserNameExpr})`
|
|
894
925
|
}).then(function(){
|
|
895
926
|
if(opts["dump-db"]){
|
|
@@ -1032,6 +1063,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1032
1063
|
console.log(err);
|
|
1033
1064
|
throw err;
|
|
1034
1065
|
}
|
|
1066
|
+
await pg.setAllTypes(client);
|
|
1035
1067
|
var data = await client.query("SELECT current_timestamp as cts").fetchUniqueRow();
|
|
1036
1068
|
if(verboseStartup){
|
|
1037
1069
|
console.log('NOW in Database',data.row.cts);
|
|
@@ -1047,7 +1079,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1047
1079
|
},function(){ /*OK, login.jade must not be here */ }),
|
|
1048
1080
|
fs.stat('client/login.jade').then(function(){
|
|
1049
1081
|
return Path.resolve(be.rootPath,'client/login');
|
|
1050
|
-
},function(){
|
|
1082
|
+
},function(){
|
|
1051
1083
|
return Path.join(__dirname,'../for-client/login');
|
|
1052
1084
|
}).then(function(loginFile){
|
|
1053
1085
|
be.config.login.plus.loginPageServe=function(req,res,next){
|
|
@@ -1164,7 +1196,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1164
1196
|
if(verboseStartup){
|
|
1165
1197
|
console.log('-------------------');
|
|
1166
1198
|
console.log(
|
|
1167
|
-
'be.config.login',
|
|
1199
|
+
'be.config.login',
|
|
1168
1200
|
be.jsonPass(be.config.login)
|
|
1169
1201
|
);
|
|
1170
1202
|
}
|
|
@@ -1193,7 +1225,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1193
1225
|
}
|
|
1194
1226
|
});
|
|
1195
1227
|
const updatePassword = async ({client, username, password, setUpdateDate, errorIfNoResult}) => {
|
|
1196
|
-
const {table, passFieldName, userFieldName, passUpdatedAtFieldName, passAlgorithmFieldName, schema} = be.config.login;
|
|
1228
|
+
const {table, passFieldName, userFieldName, passUpdatedAtFieldName, passAlgorithmFieldName, schema} = be.config.login;
|
|
1197
1229
|
const hashPass = await generateScramVerifier(password);
|
|
1198
1230
|
let params = [username, hashPass];
|
|
1199
1231
|
let setters = [`${be.db.quoteIdent(passFieldName)} = $2`];
|
|
@@ -1204,9 +1236,9 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1204
1236
|
if(setUpdateDate && passUpdatedAtFieldName){
|
|
1205
1237
|
setters.push(`${be.db.quoteIdent(passUpdatedAtFieldName)} = current_timestamp`)
|
|
1206
1238
|
}
|
|
1207
|
-
|
|
1239
|
+
|
|
1208
1240
|
const result = await client.query(`
|
|
1209
|
-
UPDATE ${(schema ? be.db.quoteIdent(schema) + '.' : '') + be.db.quoteIdent(table)}
|
|
1241
|
+
UPDATE ${(schema ? be.db.quoteIdent(schema) + '.' : '') + be.db.quoteIdent(table)}
|
|
1210
1242
|
SET ${setters.join(', ')}
|
|
1211
1243
|
WHERE ${be.db.quoteIdent(userFieldName)} = $1
|
|
1212
1244
|
returning 1 as ok
|
|
@@ -1236,7 +1268,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1236
1268
|
[userFieldName]
|
|
1237
1269
|
)
|
|
1238
1270
|
).concat(passFieldName);
|
|
1239
|
-
|
|
1271
|
+
|
|
1240
1272
|
const sql = "SELECT "+infoFieldList.map(function(fieldOrPair){ return fieldOrPair.split(' as ').map(function(ident){ return be.db.quoteIdent(ident)}).join(' as '); })+
|
|
1241
1273
|
", "+be.config.login.activeClausule+" as active "+
|
|
1242
1274
|
", "+be.config.login.lockedClausule+" as locked "+
|
|
@@ -1264,7 +1296,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1264
1296
|
if(await verifyScramPG(password, user[passFieldName])){
|
|
1265
1297
|
isScramValid = true;
|
|
1266
1298
|
}
|
|
1267
|
-
|
|
1299
|
+
|
|
1268
1300
|
// 2. Intento con formato Legacy (64 bytes)
|
|
1269
1301
|
else if(await verifyScramLegacy(password, user[passFieldName])){
|
|
1270
1302
|
isScramValid = true;
|
|
@@ -1274,7 +1306,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1274
1306
|
done(null,false,{message:be.messages.unlogged.login.userOrPassFail});
|
|
1275
1307
|
return
|
|
1276
1308
|
}
|
|
1277
|
-
}else{
|
|
1309
|
+
}else{
|
|
1278
1310
|
if (md5(password+username.toLowerCase()) === user[passFieldName]){
|
|
1279
1311
|
needsMigration = true;
|
|
1280
1312
|
}else{
|
|
@@ -1288,7 +1320,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1288
1320
|
if (be.config.log["pass-migration"]) console.log('Migración completada.');
|
|
1289
1321
|
}
|
|
1290
1322
|
}
|
|
1291
|
-
//continua validando
|
|
1323
|
+
//continua validando
|
|
1292
1324
|
if(data.rowCount==1){
|
|
1293
1325
|
if(!data.row.active){
|
|
1294
1326
|
done(null,false,{message:be.messages.unlogged.login.inactiveFail});
|
|
@@ -1344,7 +1376,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1344
1376
|
const data = await client.query(
|
|
1345
1377
|
`SELECT *
|
|
1346
1378
|
FROM ${(schema ? be.db.quoteIdent(schema) + '.' : '')}${be.db.quoteIdent(table)}
|
|
1347
|
-
WHERE ${be.db.quoteIdent(userFieldName)} = $1`,
|
|
1379
|
+
WHERE ${be.db.quoteIdent(userFieldName)} = $1`,
|
|
1348
1380
|
[username]
|
|
1349
1381
|
).fetchOneRowIfExists();
|
|
1350
1382
|
if (data.rowCount !== 1) {
|
|
@@ -1360,7 +1392,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1360
1392
|
ok = await verifyScramLegacy(oldPassword, storedHash);
|
|
1361
1393
|
}
|
|
1362
1394
|
} else { //Intento MD5
|
|
1363
|
-
const md5Hash = md5(oldPassword + username.toLowerCase());
|
|
1395
|
+
const md5Hash = md5(oldPassword + username.toLowerCase());
|
|
1364
1396
|
ok = (md5Hash === storedHash);
|
|
1365
1397
|
}
|
|
1366
1398
|
if (!ok) {
|
|
@@ -1454,7 +1486,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1454
1486
|
to: be.config.mailer?.supervise?.to,
|
|
1455
1487
|
subject: `npm start ${be.config["client-setup"]?.title || packagejson.name} ok ✔️`,
|
|
1456
1488
|
text:`Inicio del servicio: ${new Date().toJSON()}
|
|
1457
|
-
|
|
1489
|
+
|
|
1458
1490
|
Contexto: ${os.userInfo().username} ${process.cwd()}
|
|
1459
1491
|
`
|
|
1460
1492
|
}, {ignoreNoMailer:true, event:'restart-ok'})
|
|
@@ -1462,7 +1494,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1462
1494
|
if(err.dumping=='ok'){
|
|
1463
1495
|
console.log('db struct dumped');
|
|
1464
1496
|
process.exit(0);
|
|
1465
|
-
return;
|
|
1497
|
+
return;
|
|
1466
1498
|
}
|
|
1467
1499
|
console.log('ERROR',err.stack || err);
|
|
1468
1500
|
if(err.context){
|
|
@@ -1476,11 +1508,11 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1476
1508
|
to: be.config.mailer?.supervise?.to,
|
|
1477
1509
|
subject: `npm start ${be.config["client-setup"]?.title || packagejson.name} fallido 🛑`,
|
|
1478
1510
|
text:`Falla en el inicio del servicio: ${new Date().toJSON()}
|
|
1479
|
-
|
|
1511
|
+
|
|
1480
1512
|
Contexto: ${os.userInfo().username} ${process.cwd()}
|
|
1481
1513
|
|
|
1482
1514
|
Mensaje: ${err.message}
|
|
1483
|
-
|
|
1515
|
+
|
|
1484
1516
|
${err.stack}
|
|
1485
1517
|
`
|
|
1486
1518
|
}, {ignoreNoMailer:true, event:'restart-fail'})
|
|
@@ -1549,7 +1581,7 @@ AppBackend.prototype.checkDatabaseStructure = async function checkDatabaseStruct
|
|
|
1549
1581
|
throw err;
|
|
1550
1582
|
}
|
|
1551
1583
|
try{
|
|
1552
|
-
await client.query(`select ${be.db.quoteIdentList(be.config.login.forget.mailFields)}
|
|
1584
|
+
await client.query(`select ${be.db.quoteIdentList(be.config.login.forget.mailFields)}
|
|
1553
1585
|
from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.config.login.table} limit 1`).fetchOneRowIfExists();
|
|
1554
1586
|
}catch(err){
|
|
1555
1587
|
var mensaje = `
|
|
@@ -1564,7 +1596,7 @@ AppBackend.prototype.checkDatabaseStructure = async function checkDatabaseStruct
|
|
|
1564
1596
|
}
|
|
1565
1597
|
}
|
|
1566
1598
|
var bitacoraTableName = be.config.server.bitacoraTableName || 'bitacora';
|
|
1567
|
-
var bitacoraId = await client.query(`SELECT data_type
|
|
1599
|
+
var bitacoraId = await client.query(`SELECT data_type
|
|
1568
1600
|
FROM information_schema.columns
|
|
1569
1601
|
WHERE /*table_schema = 'his' AND*/ table_name = '${bitacoraTableName}' AND column_name = 'id'
|
|
1570
1602
|
`).fetchOneRowIfExists();
|
|
@@ -1609,9 +1641,9 @@ AppBackend.prototype.postConfig = function postConfig(){
|
|
|
1609
1641
|
AppBackend.prototype.getContext = function getContext(req){
|
|
1610
1642
|
var be = this;
|
|
1611
1643
|
return {
|
|
1612
|
-
be, user:req.user, session:req.session,
|
|
1613
|
-
username:req.user && req.user[be.config.login.userFieldName],
|
|
1614
|
-
machineId:req.machineId,
|
|
1644
|
+
be, user:req.user, session:req.session,
|
|
1645
|
+
username:req.user && req.user[be.config.login.userFieldName],
|
|
1646
|
+
machineId:req.machineId,
|
|
1615
1647
|
navigator:(req.userAgent||{}).shortDescription||'?'
|
|
1616
1648
|
};
|
|
1617
1649
|
}
|
|
@@ -1641,7 +1673,7 @@ AppBackend.prototype.generateInsertSQL = function generateInsertSQL(schemaName,
|
|
|
1641
1673
|
var cleanValues = [];
|
|
1642
1674
|
for (var key in insertElement) {
|
|
1643
1675
|
cleanKeys.push(db.quoteIdent(key));
|
|
1644
|
-
cleanValues.push(db.quoteNullable(insertElement[key]));
|
|
1676
|
+
cleanValues.push(db.quoteNullable(insertElement[key]));
|
|
1645
1677
|
}
|
|
1646
1678
|
var sql = `INSERT INTO ${db.quoteIdent(schemaName)}.${db.quoteIdent(tableName)}
|
|
1647
1679
|
(${cleanKeys.join(',')}) VALUES (${cleanValues.join(',')}) returning id`;
|
|
@@ -1659,7 +1691,7 @@ AppBackend.prototype.updateUpdateSQL = function updateUpdateSQL(schemaName, tabl
|
|
|
1659
1691
|
for (var key in updateConditions) {
|
|
1660
1692
|
filterPairs.push(be.db.quoteIdent(key) + " = " + be.db.quoteLiteral(updateConditions[key]));
|
|
1661
1693
|
};
|
|
1662
|
-
var sql = `UPDATE ${db.quoteIdent(schemaName)}.${db.quoteIdent(tableName)}
|
|
1694
|
+
var sql = `UPDATE ${db.quoteIdent(schemaName)}.${db.quoteIdent(tableName)}
|
|
1663
1695
|
SET ${setPairs.join(',')}
|
|
1664
1696
|
WHERE ${filterPairs.join(' AND ')}`;
|
|
1665
1697
|
return sql;
|
|
@@ -1667,7 +1699,7 @@ AppBackend.prototype.updateUpdateSQL = function updateUpdateSQL(schemaName, tabl
|
|
|
1667
1699
|
|
|
1668
1700
|
|
|
1669
1701
|
/** @param {boolean} forUnlogged */
|
|
1670
|
-
AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnlogged){
|
|
1702
|
+
AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnlogged){
|
|
1671
1703
|
var be = this;
|
|
1672
1704
|
if(forUnlogged){
|
|
1673
1705
|
var app = express();
|
|
@@ -1685,7 +1717,7 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1685
1717
|
be.procedures = defs;
|
|
1686
1718
|
be.clientSetup.procedure = be.procedure;
|
|
1687
1719
|
app.get('/client-setup',async function(req, res, next){
|
|
1688
|
-
if(forUnlogged && req.user){
|
|
1720
|
+
if(forUnlogged && req.user){
|
|
1689
1721
|
// este pedido es para unlogged y está logueado, va al próximo
|
|
1690
1722
|
next();
|
|
1691
1723
|
}else{
|
|
@@ -1715,12 +1747,12 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1715
1747
|
if(!isLowerIdent(procedureDef.action)){
|
|
1716
1748
|
console.error('**** DEPRECATED ***** procedureDef action '+JSON.stringify(procedureDef.action)+' must be a Lower Ident');
|
|
1717
1749
|
}
|
|
1718
|
-
app[procedureDef.method]('/'+procedureDef.action,
|
|
1750
|
+
app[procedureDef.method]('/'+procedureDef.action,
|
|
1719
1751
|
/**
|
|
1720
|
-
*
|
|
1721
|
-
* @param {Request} req
|
|
1722
|
-
* @param {Response} res
|
|
1723
|
-
* @param {import('express').NextFunction} next
|
|
1752
|
+
*
|
|
1753
|
+
* @param {Request} req
|
|
1754
|
+
* @param {Response} res
|
|
1755
|
+
* @param {import('express').NextFunction} next
|
|
1724
1756
|
*/
|
|
1725
1757
|
async function(req, res, next){
|
|
1726
1758
|
const BITACORA_SCHEMA = be.config.server.bitacoraSchema;
|
|
@@ -1773,9 +1805,9 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1773
1805
|
init_date: initDatetimeString,
|
|
1774
1806
|
};
|
|
1775
1807
|
var getFinalStatusBitacoraElement = function getFinalStatusBitacoraElement(){
|
|
1776
|
-
return {
|
|
1777
|
-
end_date: getDatetimeString(),
|
|
1778
|
-
end_status: status,
|
|
1808
|
+
return {
|
|
1809
|
+
end_date: getDatetimeString(),
|
|
1810
|
+
end_status: status,
|
|
1779
1811
|
has_error: hasError
|
|
1780
1812
|
}
|
|
1781
1813
|
}
|
|
@@ -1805,12 +1837,12 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1805
1837
|
var params = getParams();
|
|
1806
1838
|
if(status){
|
|
1807
1839
|
//terminó ejecucion
|
|
1808
|
-
updateElement[procedureDef.bitacora.targetTableBitacoraFields.end_date] = getDatetimeString();
|
|
1840
|
+
updateElement[procedureDef.bitacora.targetTableBitacoraFields.end_date] = getDatetimeString();
|
|
1809
1841
|
updateElement[procedureDef.bitacora.targetTableBitacoraFields.end_status] = status;
|
|
1810
1842
|
updateElement[procedureDef.bitacora.targetTableBitacoraFields.has_error] = hasError;
|
|
1811
1843
|
}else{
|
|
1812
1844
|
//empezó ejecucion
|
|
1813
|
-
updateElement[procedureDef.bitacora.targetTableBitacoraFields.init_date] = initDatetimeString;
|
|
1845
|
+
updateElement[procedureDef.bitacora.targetTableBitacoraFields.init_date] = initDatetimeString;
|
|
1814
1846
|
}
|
|
1815
1847
|
await be.inTransaction(req,async function(client){
|
|
1816
1848
|
targetTableUpdateFieldsCondition.forEach(function(field){
|
|
@@ -2114,7 +2146,7 @@ AppBackend.prototype.optsGenericForFiles = function optsGenericForFiles(req, opt
|
|
|
2114
2146
|
return changing(be.optsGenericForAll||{},{
|
|
2115
2147
|
allowedExts:be.exts.normal,
|
|
2116
2148
|
jade:{
|
|
2117
|
-
skin:skin,
|
|
2149
|
+
skin:skin,
|
|
2118
2150
|
skinUrl:skinUrl,
|
|
2119
2151
|
formTitle:title,
|
|
2120
2152
|
...(opts?.withFlash ? {flash:req?.flash?.()} : {}),
|
|
@@ -2133,8 +2165,8 @@ AppBackend.prototype.unloggedLandPage = function unloggedLandPage(req){
|
|
|
2133
2165
|
html.h2({style:'text-align:center; margin-top:15%; font-family:arial, sans-serif'},[be.messages.server.notLoggedIn]),
|
|
2134
2166
|
html.h2({style:'text-align:center'},[html.code([
|
|
2135
2167
|
html.a({
|
|
2136
|
-
id:'goto-login',
|
|
2137
|
-
style:'border:0.5px solid blue; border-radius: 6px; padding: 6px',
|
|
2168
|
+
id:'goto-login',
|
|
2169
|
+
style:'border:0.5px solid blue; border-radius: 6px; padding: 6px',
|
|
2138
2170
|
href:Path.posix.join(be.config.server["base-url"],(be.config.login.plus.loginUrlPath||'/login'))
|
|
2139
2171
|
},'login')
|
|
2140
2172
|
])]),
|
|
@@ -2157,8 +2189,8 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
2157
2189
|
var now = bestGlobals.datetime.now();
|
|
2158
2190
|
var token = crypto.randomUUID();
|
|
2159
2191
|
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)})
|
|
2192
|
+
from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.db.quoteIdent(be.config.login.table)}
|
|
2193
|
+
where $1 in (${be.db.quoteIdentList(be.config.login.forget.mailFields)})
|
|
2162
2194
|
and ${be.config.login.activeClausule}
|
|
2163
2195
|
and ${be.config.login.lockedClausule} is not true
|
|
2164
2196
|
`, [req.body.email.toLowerCase()]).fetchAll();
|
|
@@ -2266,21 +2298,21 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
2266
2298
|
// http://localhost:3033/img/login-logo-icon.png
|
|
2267
2299
|
mainApp.get(Path.posix.join(baseUrl,'/img/login-logo-icon.png'), async function(req,res,next){
|
|
2268
2300
|
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',
|
|
2301
|
+
'unlogged/img/login-logo-icon.svg',
|
|
2302
|
+
'dist/unlogged/img/login-logo-icon.svg',
|
|
2303
|
+
'dist/client/unlogged/img/login-logo-icon.svg',
|
|
2304
|
+
'unlogged/img/login-logo-icon.png',
|
|
2305
|
+
'dist/unlogged/img/login-logo-icon.png',
|
|
2306
|
+
'dist/client/unlogged/img/login-logo-icon.png',
|
|
2307
|
+
'unlogged/img/logo.png',
|
|
2308
|
+
'dist/unlogged/img/logo.png',
|
|
2309
|
+
'dist/client/unlogged/img/logo.png',
|
|
2310
|
+
'client/img/logo.png',
|
|
2311
|
+
'dist/client/img/logo.png',
|
|
2312
|
+
'dist/client/client/img/logo.png',
|
|
2281
2313
|
'unlogged/img/logo-128.png',
|
|
2282
2314
|
'dist/unlogged/img/logo-128.png',
|
|
2283
|
-
'dist/client/unlogged/img/logo-128.png'
|
|
2315
|
+
'dist/client/unlogged/img/logo-128.png'
|
|
2284
2316
|
];
|
|
2285
2317
|
buscar = buscar.map(n=>be.rootPath+'/'+n);
|
|
2286
2318
|
buscar.push(__dirname+'/../for-client/img/login-logo-icon.png');
|
|
@@ -2295,7 +2327,7 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
2295
2327
|
be.clientIncludesCompleted(null).filter(x => x.module).forEach(function (moduleDef) {
|
|
2296
2328
|
if(baseUrl=='/'){
|
|
2297
2329
|
baseUrl='';
|
|
2298
|
-
}
|
|
2330
|
+
}
|
|
2299
2331
|
let baseLib = baseUrl + '/' + (moduleDef.path ? moduleDef.path : be.esJavascript(moduleDef.type)? 'lib': 'css');
|
|
2300
2332
|
resolve_module_dir(moduleDef.module, moduleDef.modPath, moduleDef.file ?? '.')
|
|
2301
2333
|
try {
|
|
@@ -2319,7 +2351,7 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
2319
2351
|
// ----------------------------------------------------
|
|
2320
2352
|
var skin=be.config['client-setup'].skin;
|
|
2321
2353
|
var skinUrl=(skin?skin+'/':'');
|
|
2322
|
-
var optsGenericForFilesUnlogged=be.optsGenericForFiles();
|
|
2354
|
+
var optsGenericForFilesUnlogged=be.optsGenericForFiles();
|
|
2323
2355
|
var skinPaths=[Path.join(be.rootPath,'skins')];
|
|
2324
2356
|
if(be.config.server.skins[skin]['local-path']){
|
|
2325
2357
|
skinPaths=skinPaths.concat(be.config.server.skins[skin]['local-path']).map(function(path){
|
|
@@ -2372,12 +2404,14 @@ AppBackend.prototype.getVisibleMenu = function getVisibleMenu(menu, context){
|
|
|
2372
2404
|
|
|
2373
2405
|
AppBackend.prototype.clientIncludes = function clientIncludes(req, opts) {
|
|
2374
2406
|
const hideBEPlusInclusions = opts === true || opts && typeof opts == "object" && opts.hideBEPlusInclusions;
|
|
2375
|
-
opts = opts === true ? {} : opts || {};
|
|
2407
|
+
opts = opts === true ? {} : opts || {};
|
|
2376
2408
|
var list = [];
|
|
2377
2409
|
if (!hideBEPlusInclusions) {
|
|
2378
2410
|
list = [
|
|
2379
2411
|
// { type: 'js', module: 'xlsx', modPath: 'dist', file: 'xlsx.core.min.js' },
|
|
2380
|
-
{ type: 'js', module: '
|
|
2412
|
+
{ type: 'js', module: 'fflate', modPath: '../umd', file: 'index.js' },
|
|
2413
|
+
{ type: 'js', module: 'xlsx-now', modPath: '../../umd', file: 'xlsx-now.umd.js' },
|
|
2414
|
+
{ type: 'js', module: 'xlsx-now', modPath: '../../umd', file: 'xlsx-now-browser.umd.js' },
|
|
2381
2415
|
{ type: 'js', module: 'require-bro' },
|
|
2382
2416
|
{ type: 'js', module: 'js-yaml', modPath: 'browser', file: 'js-yaml.umd.min.js' },
|
|
2383
2417
|
{ type: 'js', module: 'cast-error' },
|
|
@@ -2463,7 +2497,7 @@ AppBackend.prototype.clientModules = function clientModules(req, opts) {
|
|
|
2463
2497
|
}
|
|
2464
2498
|
|
|
2465
2499
|
/**
|
|
2466
|
-
* @param {string} tableName
|
|
2500
|
+
* @param {string} tableName
|
|
2467
2501
|
* @param {(tableDef:typesOpe.TableDefinition, context?:TableContext)=>void} appenderFunction
|
|
2468
2502
|
*/
|
|
2469
2503
|
AppBackend.prototype.appendToTableDefinition = function appendToTableDefinition(tableName, appenderFunction){
|
|
@@ -2520,16 +2554,16 @@ AppBackend.prototype.csss = function csss(hideBEPlusInclusions){
|
|
|
2520
2554
|
AppBackend.prototype.scanSkinFiles = function scanSkinFiles() {
|
|
2521
2555
|
var be = this;
|
|
2522
2556
|
var skinName = be.config['client-setup'].skin;
|
|
2523
|
-
be.activeSkinFiles = new Set();
|
|
2557
|
+
be.activeSkinFiles = new Set();
|
|
2524
2558
|
|
|
2525
2559
|
var skinConfig = be.config.server.skins && be.config.server.skins[skinName];
|
|
2526
2560
|
|
|
2527
2561
|
if (skinName && skinConfig && skinConfig['local-path']) {
|
|
2528
2562
|
try {
|
|
2529
2563
|
var skinPath = Path.join(Path.resolve(skinConfig['local-path']), skinName);
|
|
2530
|
-
|
|
2564
|
+
|
|
2531
2565
|
if (fs.existsSync(skinPath)) {
|
|
2532
|
-
|
|
2566
|
+
|
|
2533
2567
|
const walk = (currentPath) => {
|
|
2534
2568
|
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
2535
2569
|
|
|
@@ -2544,7 +2578,7 @@ AppBackend.prototype.scanSkinFiles = function scanSkinFiles() {
|
|
|
2544
2578
|
var relativePath = Path.relative(skinPath, fullPath)
|
|
2545
2579
|
.replace(/\\/g, '/')
|
|
2546
2580
|
.replace(/^\//, '');
|
|
2547
|
-
|
|
2581
|
+
|
|
2548
2582
|
be.activeSkinFiles.add(relativePath);
|
|
2549
2583
|
}
|
|
2550
2584
|
}
|
|
@@ -2631,7 +2665,7 @@ AppBackend.prototype.mainPage = function mainPage(req, offlineMode, opts){
|
|
|
2631
2665
|
|
|
2632
2666
|
var lastDotIndex = css.lastIndexOf('.');
|
|
2633
2667
|
var cssBase = (lastDotIndex !== -1) ? css.substring(0, lastDotIndex) : css;
|
|
2634
|
-
|
|
2668
|
+
|
|
2635
2669
|
var existsInSkin = EXTENSIONES_SKIN.some(function(ext) {
|
|
2636
2670
|
return be.activeSkinFiles.has(cssBase + ext);
|
|
2637
2671
|
});
|
|
@@ -2897,7 +2931,7 @@ AppBackend.prototype.validateBitacora = function validateBitacora(procedureDef){
|
|
|
2897
2931
|
var tableDefFields = be.tableStructures[procedureDef.bitacora.targetTable](contextForDump).fields;
|
|
2898
2932
|
var targetTableBitacoraFields = procedureDef.bitacora.targetTableBitacoraFields;
|
|
2899
2933
|
for (var fieldForSearch in targetTableBitacoraFields) {
|
|
2900
|
-
var searchResult = tableDefFields.find(function findByName(field) {
|
|
2934
|
+
var searchResult = tableDefFields.find(function findByName(field) {
|
|
2901
2935
|
return field.name === targetTableBitacoraFields[fieldForSearch];
|
|
2902
2936
|
});
|
|
2903
2937
|
if(!searchResult){
|
|
@@ -2905,12 +2939,12 @@ AppBackend.prototype.validateBitacora = function validateBitacora(procedureDef){
|
|
|
2905
2939
|
}
|
|
2906
2940
|
}
|
|
2907
2941
|
var targetTableUpdateFieldsCondition = procedureDef.bitacora.targetTableUpdateFieldsCondition || ['init_date','end_date','has_error', 'end_status'];
|
|
2908
|
-
if(targetTableUpdateFieldsCondition){
|
|
2942
|
+
if(targetTableUpdateFieldsCondition){
|
|
2909
2943
|
if(targetTableUpdateFieldsCondition.length == 0){
|
|
2910
2944
|
throw Error("Bitacora bad definition in core function '" + procedureDef.action + "', targetTableUpdateFieldsCondition must to be defined for table '" + procedureDef.bitacora.targetTable + "'.");
|
|
2911
2945
|
}
|
|
2912
2946
|
targetTableUpdateFieldsCondition.forEach(function(fieldName){
|
|
2913
|
-
var searchResult = tableDefFields.find(function findByName(field) {
|
|
2947
|
+
var searchResult = tableDefFields.find(function findByName(field) {
|
|
2914
2948
|
return field.name === fieldName;
|
|
2915
2949
|
});
|
|
2916
2950
|
if(!searchResult){
|
|
@@ -3040,9 +3074,9 @@ AppBackend.prototype.dumpDbTableFields = function dumpDbTableFields(tableDef, op
|
|
|
3040
3074
|
(fieldDef.dataDecimals?','+fieldDef.dataDecimals:'')
|
|
3041
3075
|
+')':fieldType)+
|
|
3042
3076
|
( be.specialSqlDefaultExpressions[fieldDef.defaultDbValue] != null ? ' default ' + be.specialSqlDefaultExpressions[fieldDef.defaultDbValue]
|
|
3043
|
-
: fieldDef.defaultDbValue != null ? ' default ' + fieldDef.defaultDbValue
|
|
3077
|
+
: fieldDef.defaultDbValue != null ? ' default ' + fieldDef.defaultDbValue
|
|
3044
3078
|
: be.specialSqlDefaultExpressions[fieldDef.specialDefaultValue] != null ? ' default ' + be.specialSqlDefaultExpressions[fieldDef.specialDefaultValue]
|
|
3045
|
-
: fieldDef.defaultValue != null ? ' default ' + db.quoteLiteral(fieldDef.defaultValue)
|
|
3079
|
+
: fieldDef.defaultValue != null ? ' default ' + db.quoteLiteral(fieldDef.defaultValue)
|
|
3046
3080
|
: ''
|
|
3047
3081
|
) +
|
|
3048
3082
|
(be.isGeneratedSequence(fieldDef.sequence)?' generated always as identity':'')+
|
|
@@ -3195,7 +3229,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3195
3229
|
lines.push(');');
|
|
3196
3230
|
//TODO: REFACTOR: Hacerlo mas sencillo
|
|
3197
3231
|
// este codigo se encarga de convertir a rights de sql nuestros propios rights
|
|
3198
|
-
// por ej (import -> [insert, update])
|
|
3232
|
+
// por ej (import -> [insert, update])
|
|
3199
3233
|
var allows = tableDef.allow;
|
|
3200
3234
|
var appToSqlRights = {'import': ['insert', 'update'], 'export': ['select'], 'deleteAll': ['delete']};
|
|
3201
3235
|
[ 'import', 'export', 'deleteAll'].filter(function(right){
|
|
@@ -3252,7 +3286,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3252
3286
|
var prefix = 'alter table '+cualQuoteTableName+' add '+
|
|
3253
3287
|
(cons.consName?'constraint '+db.quoteIdent(cons.consName)+' ':'');
|
|
3254
3288
|
switch(cons.constraintType){
|
|
3255
|
-
case 'unique':
|
|
3289
|
+
case 'unique':
|
|
3256
3290
|
sql='('+cons.fields.map(function(field){ return db.quoteIdent(field); }).join(', ')+')';
|
|
3257
3291
|
if(cons.where){
|
|
3258
3292
|
if(cons.consName == null){
|
|
@@ -3264,10 +3298,10 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3264
3298
|
prefix += 'unique ';
|
|
3265
3299
|
}
|
|
3266
3300
|
break;
|
|
3267
|
-
case 'check':
|
|
3301
|
+
case 'check':
|
|
3268
3302
|
sql='check ('+cons.expr+')';
|
|
3269
3303
|
break;
|
|
3270
|
-
case 'exclude':
|
|
3304
|
+
case 'exclude':
|
|
3271
3305
|
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})`:``}`;
|
|
3272
3306
|
break;
|
|
3273
3307
|
default:
|
|
@@ -3310,7 +3344,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3310
3344
|
(polcom.using? ` USING ( ${polcom.using} )`:'')+
|
|
3311
3345
|
(polcom.check?` WITH CHECK ( ${polcom.check} )`:'')+';'
|
|
3312
3346
|
);
|
|
3313
|
-
}
|
|
3347
|
+
}
|
|
3314
3348
|
});
|
|
3315
3349
|
}
|
|
3316
3350
|
}else{
|
|
@@ -3351,7 +3385,7 @@ begin
|
|
|
3351
3385
|
else
|
|
3352
3386
|
select ${be.config.login.infoFieldList.map(fieldName => db.quoteIdent(fieldName)).join(', ')}
|
|
3353
3387
|
into ${be.config.login.infoFieldList.map(fieldName => db.quoteIdent('v_'+fieldName)).join(', ')}
|
|
3354
|
-
|
|
3388
|
+
|
|
3355
3389
|
from ${(be.config.login.from ?? (
|
|
3356
3390
|
(be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':'')+
|
|
3357
3391
|
be.db.quoteIdent(be.config.login.table)))}
|
|
@@ -3362,7 +3396,7 @@ begin
|
|
|
3362
3396
|
set backend_plus._mode = normal;
|
|
3363
3397
|
end if;
|
|
3364
3398
|
perform set_config('backend_plus._user', p_username, false);
|
|
3365
|
-
end;
|
|
3399
|
+
end;
|
|
3366
3400
|
$body$;
|
|
3367
3401
|
|
|
3368
3402
|
`)
|
|
@@ -3376,7 +3410,7 @@ $body$;
|
|
|
3376
3410
|
var allTableContent = await fs.readFile('install/local-dump.psql','utf-8');
|
|
3377
3411
|
var startIndex = allTableContent.indexOf('-- Data for Name: ');
|
|
3378
3412
|
console.log('startIndex', startIndex);
|
|
3379
|
-
var lastUseful = allTableContent.lastIndexOf('\nSELECT pg_catalog.setval')
|
|
3413
|
+
var lastUseful = allTableContent.lastIndexOf('\nSELECT pg_catalog.setval')
|
|
3380
3414
|
console.log('lastUseful', lastUseful);
|
|
3381
3415
|
if (lastUseful == -1) lastUseful = allTableContent.lastIndexOf('\n\\.\n');
|
|
3382
3416
|
console.log('lastUseful', lastUseful);
|
|
@@ -3456,6 +3490,7 @@ $body$;
|
|
|
3456
3490
|
});
|
|
3457
3491
|
rows=lines;
|
|
3458
3492
|
}else{
|
|
3493
|
+
/* PARSEO DEL ARCHIVO .TAB */
|
|
3459
3494
|
var lines=content.split(/\r?\n/)
|
|
3460
3495
|
.filter(line => !(/^[-| ]*$/.test(line)) )
|
|
3461
3496
|
.map(line => splitRawRowIntoRow(line))
|
|
@@ -3489,14 +3524,61 @@ $body$;
|
|
|
3489
3524
|
throw Error("no se encuentra la columna "+filteredFieldDef[i]+" en "+tableName);
|
|
3490
3525
|
}
|
|
3491
3526
|
return value==='' ? (
|
|
3492
|
-
def.allowEmptyText && ('nullable' in def) && !def.nullable ? "''" : 'null'
|
|
3527
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? "''" : 'null'
|
|
3493
3528
|
) : db.quoteNullable(value);
|
|
3494
3529
|
}).join(', ')+")";
|
|
3495
3530
|
}).join(',\n')+';\n';
|
|
3496
3531
|
}
|
|
3497
3532
|
dataText.push(dataString);
|
|
3533
|
+
if(process.env.TRY_TAB_PLUS){
|
|
3534
|
+
var tabPlusEmptySymbol = Symbol('empty');
|
|
3535
|
+
var parsedTabPlus = tabPlus.parseTab(content, {emptyField: tabPlusEmptySymbol});
|
|
3536
|
+
var tabPlusFields = parsedTabPlus.fields;
|
|
3537
|
+
var tabPlusRows = parsedTabPlus.rows;
|
|
3538
|
+
/* a partir de acá: solo construcción de SQL a partir de {fields,rows}, sin volver a tocar el parseo */
|
|
3539
|
+
var filteredFieldDefTabPlus = tabPlusFields.filter(filterField);
|
|
3540
|
+
var dataStringTabPlus;
|
|
3541
|
+
if(tablesWithStrictSequence[tableName]){
|
|
3542
|
+
dataStringTabPlus="COPY "+db.quoteIdent(tableName)+" ("+
|
|
3543
|
+
filteredFieldDefTabPlus.map(db.quoteIdent).join(', ')+
|
|
3544
|
+
') FROM stdin;\n'+
|
|
3545
|
+
tabPlusRows.map(function(line){
|
|
3546
|
+
return line.filter(function(_,i){ return filterField(tabPlusFields[i]);}).map(function(value,i){
|
|
3547
|
+
var def = tableDef.field[filteredFieldDefTabPlus[i]];
|
|
3548
|
+
return value===tabPlusEmptySymbol ? (
|
|
3549
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? '' : '\\N'
|
|
3550
|
+
): value.replace(/\\/g,'\\\\').replace(/\t/g,'\\t').replace(/\n/g,'\\n').replace(/\r/g,'\\r');
|
|
3551
|
+
}).join('\t')+'\n';
|
|
3552
|
+
}).join('')+'\\.\n';
|
|
3553
|
+
}else{
|
|
3554
|
+
dataStringTabPlus="insert into "+db.quoteIdent(tableName)+" ("+
|
|
3555
|
+
filteredFieldDefTabPlus.map(db.quoteIdent).join(', ')+
|
|
3556
|
+
') values\n'+
|
|
3557
|
+
tabPlusRows.map(function(line){
|
|
3558
|
+
return "("+line.filter(function(_,i){ return filterField(tabPlusFields[i]);}).map(function(value,i){
|
|
3559
|
+
var def = tableDef.field[filteredFieldDefTabPlus[i]];
|
|
3560
|
+
if(def == null) {
|
|
3561
|
+
throw Error("no se encuentra la columna "+filteredFieldDefTabPlus[i]+" en "+tableName);
|
|
3562
|
+
}
|
|
3563
|
+
return value===tabPlusEmptySymbol ? (
|
|
3564
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? "''" : 'null'
|
|
3565
|
+
) : db.quoteNullable(value);
|
|
3566
|
+
}).join(', ')+")";
|
|
3567
|
+
}).join(',\n')+';\n';
|
|
3568
|
+
}
|
|
3569
|
+
if(dataStringTabPlus !== dataString){
|
|
3570
|
+
var oldSqlPath = path.replace(/\.tab$/,'')+'-old-local.sql';
|
|
3571
|
+
var newSqlPath = path.replace(/\.tab$/,'')+'-new-local.sql';
|
|
3572
|
+
fs.writeFileSync(oldSqlPath, dataString, {encoding:'UTF8'});
|
|
3573
|
+
fs.writeFileSync(newSqlPath, dataStringTabPlus, {encoding:'UTF8'});
|
|
3574
|
+
if (process.env.TRY_TAB_PLUS == 'ON-ERROR-STOP') {
|
|
3575
|
+
throw new Error('TRY_TAB_PLUS: el parseo con tab-plus difiere del parseo anterior para '+tableName+' ('+path+'). Ver '+oldSqlPath+' y '+newSqlPath);
|
|
3576
|
+
} else {
|
|
3577
|
+
console.log('TRY_TAB_PLUS: el parseo con tab-plus difiere del parseo anterior para '+tableName+' ('+path+'). Ver '+oldSqlPath+' y '+newSqlPath);
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3498
3581
|
}
|
|
3499
|
-
// tablesWithStrictSequence[tableName]
|
|
3500
3582
|
}
|
|
3501
3583
|
}).catch(function(err){
|
|
3502
3584
|
if(err.code=='ENOENT'){
|
|
@@ -3524,7 +3606,7 @@ $body$;
|
|
|
3524
3606
|
]
|
|
3525
3607
|
.map(async function(fileNames){
|
|
3526
3608
|
if (!fileNames) return '';
|
|
3527
|
-
var i = 0;
|
|
3609
|
+
var i = 0;
|
|
3528
3610
|
return (await Promise.all(fileNames.map(async fileName => {
|
|
3529
3611
|
var content;
|
|
3530
3612
|
do {
|
|
@@ -3576,7 +3658,7 @@ $body$;
|
|
|
3576
3658
|
'\n-- functions\n' + functionLines.join('\n')+
|
|
3577
3659
|
'\n-- lines \n' + lines.join('\n')+
|
|
3578
3660
|
(complete? ('\n\n-- pre-ADAPTs\n'+texts[1]+'\n\n') : '' )+
|
|
3579
|
-
(complete? ('\n\n-- DATA\n'+ dataText.join('\n')) : '' )+
|
|
3661
|
+
(complete? ('\n\n-- DATA\n'+ dataText.join('\n')) : '' )+
|
|
3580
3662
|
(complete? ('\n\n-- ADAPTs\n'+ texts[2]+'\n\n') : '' )+
|
|
3581
3663
|
'\n-- conss\n' + consLines.join('\n')+
|
|
3582
3664
|
'\n-- FKs\n' + fkLines.join('\n')+
|
|
@@ -3606,14 +3688,17 @@ AppBackend.prototype.getDbFunctions = async function (){
|
|
|
3606
3688
|
AppBackend.prototype.dumpDbSchema = async function dumpDbSchema(opts){
|
|
3607
3689
|
var be = this;
|
|
3608
3690
|
var {mainSql,enancePart} = await be.dumpDbSchemaPartial(
|
|
3609
|
-
opts.complete?be.tableStructures:likeAr(be.tableStructures).filter((_, name)=>opts.tableNames.includes(name)),
|
|
3691
|
+
opts.complete?be.tableStructures:likeAr(be.tableStructures).filter((_, name)=>opts.tableNames.includes(name)),
|
|
3610
3692
|
opts
|
|
3611
3693
|
)
|
|
3612
3694
|
mainSql=be.config.install.dump.db.extensions.map(function(extension){
|
|
3613
3695
|
return ({
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3696
|
+
cube : "create extension if not exists cube ;",
|
|
3697
|
+
earthdistance : "create extension if not exists earthdistance ;",
|
|
3698
|
+
gist : "create extension if not exists btree_gist ;",
|
|
3699
|
+
pg_trgm : "create extension if not exists pg_trgm ;",
|
|
3700
|
+
pgcrypto : "create extension if not exists pgcrypto ;",
|
|
3701
|
+
postgis : "create extension if not exists postgis ;",
|
|
3617
3702
|
}[extension]||('--unknown exension '+extension))+'\n';
|
|
3618
3703
|
}).join('')+mainSql;
|
|
3619
3704
|
await fs.writeFile('local-db-dump.sql', mainSql);
|
|
@@ -3686,7 +3771,7 @@ AppBackend.prototype.transformInput = function transformInput(fieldDef, value){
|
|
|
3686
3771
|
return value;
|
|
3687
3772
|
}
|
|
3688
3773
|
|
|
3689
|
-
/**
|
|
3774
|
+
/**
|
|
3690
3775
|
* xxxparam {{ action:string, parameters:any, conRegistro:boolean, conPadron:boolean, fileName?:string, csvFileName?:string, csvSeparator?:string, queries:{titulo:string, sql:string, params:string[]}[] }}
|
|
3691
3776
|
* @param {{title:string, rows:Record<string, any>[]}[]} result
|
|
3692
3777
|
* @returns {Promise<void>}
|
|
@@ -3767,24 +3852,19 @@ AppBackend.prototype.exportacionesGenerico = async function exportacionesGeneric
|
|
|
3767
3852
|
}
|
|
3768
3853
|
if(fileName){
|
|
3769
3854
|
context.informProgress({message:'armando XLSX'})
|
|
3770
|
-
var
|
|
3771
|
-
result.map(({title, rows})=>{
|
|
3855
|
+
var sheets = result.map(({title, rows})=>{
|
|
3772
3856
|
var rowsA = rows.map(row=>Object.values(row));
|
|
3773
3857
|
if(rows[0]){
|
|
3774
|
-
rowsA.unshift(Object.keys(rows[0]));
|
|
3858
|
+
rowsA.unshift({'#line': 'array', values:Object.keys(rows[0]), s: {bold: true}});
|
|
3775
3859
|
}
|
|
3776
|
-
|
|
3777
|
-
XLSX.utils.book_append_sheet(wb, ws, title);
|
|
3778
|
-
return ws;
|
|
3860
|
+
return {name:title, rows:rowsA};
|
|
3779
3861
|
})
|
|
3780
3862
|
context.informProgress({message:`guardando XLSX: ${fileName}`})
|
|
3781
3863
|
await bestGlobals.sleep(100);
|
|
3782
|
-
await
|
|
3783
|
-
(err)=>err?reject(err):resolve()
|
|
3784
|
-
))
|
|
3864
|
+
await xlsxNow.writeXlsxFile(`dist/client/${fileName}`, {sheets, types:xlsxTypes, autoWidthMax:50, freezeRows:1});
|
|
3785
3865
|
}
|
|
3786
3866
|
return [
|
|
3787
|
-
...(fileName?[{url:fileName, label:csvFileName?'xlsx de control':fileName}]:[]),
|
|
3867
|
+
...(fileName?[{url:fileName, label:csvFileName?'xlsx de control':fileName}]:[]),
|
|
3788
3868
|
...(csvFileName?[{url:csvFileName, label:fileName?'csv (formato UTF-8)':csvFileName}]:[]),
|
|
3789
3869
|
];
|
|
3790
3870
|
}
|