backend-plus 2.0.0-rc.3 → 2.0.0-rc.31
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/README.md +6 -6
- package/for-client/css/my-menu.styl +4 -0
- package/for-client/my-localdb.js +2 -2
- package/for-client/my-menu.js +15 -11
- package/for-client/my-tables.js +23 -19
- package/for-client/my-websqldb.d.ts +0 -1
- package/for-client/my-websqldb.js +2 -2
- package/install/get_app_user-fun.sql +6 -0
- package/lib/backend-plus.d.ts +162 -29
- package/lib/backend-plus.js +231 -90
- package/lib/procedures-table.js +51 -19
- package/package.json +37 -36
- package/unlogged/my-ajax.js +52 -33
package/lib/backend-plus.js
CHANGED
|
@@ -54,6 +54,7 @@ var bestGlobals = require('best-globals');
|
|
|
54
54
|
var coalesce = bestGlobals.coalesce;
|
|
55
55
|
var changing = bestGlobals.changing;
|
|
56
56
|
var datetime = bestGlobals.datetime;
|
|
57
|
+
var splitRawRowIntoRow = bestGlobals.splitRawRowIntoRow;
|
|
57
58
|
const escapeRegExp = bestGlobals.escapeRegExp;
|
|
58
59
|
var isLowerIdent = bestGlobals.isLowerIdent;
|
|
59
60
|
|
|
@@ -96,7 +97,13 @@ class AppBackend{
|
|
|
96
97
|
/** @type {{path:string}[]} */
|
|
97
98
|
this.appStack=[];
|
|
98
99
|
/** @type {{message:string; fun:()=>void}[]} */
|
|
99
|
-
this.
|
|
100
|
+
this.shutdownCallbackList=[];
|
|
101
|
+
this.sessionStores={
|
|
102
|
+
file: SessionFileStore,
|
|
103
|
+
memory: memorystore,
|
|
104
|
+
memoryDevel: BindMemoryPerodicallySaved(this),
|
|
105
|
+
"memory-saved": BindMemoryPerodicallySaved(this),
|
|
106
|
+
}
|
|
100
107
|
if(!this.rootPath){
|
|
101
108
|
console.log('ATENCIÓN hay que poner be.rootPath antes de llamar a super()');
|
|
102
109
|
this.rootPath=Path.resolve(__dirname,'..');
|
|
@@ -118,11 +125,14 @@ class AppBackend{
|
|
|
118
125
|
this.tableStructures = {};
|
|
119
126
|
this.configStaticConfig();
|
|
120
127
|
/** @type {null|()=>Promise<void>} */
|
|
121
|
-
this.
|
|
128
|
+
this.shutdownBackend = null;
|
|
122
129
|
/** @type {bp.Server} */
|
|
123
130
|
// @ts-ignore
|
|
124
131
|
this.server = null;
|
|
125
132
|
}
|
|
133
|
+
esJavascript(type){
|
|
134
|
+
return ['js','mjs'].includes(type)
|
|
135
|
+
}
|
|
126
136
|
clearCaches(){
|
|
127
137
|
this.caches={
|
|
128
138
|
procedures:{}
|
|
@@ -357,6 +367,8 @@ AppBackend.prototype.i18n.messages.es={
|
|
|
357
367
|
}
|
|
358
368
|
};
|
|
359
369
|
|
|
370
|
+
function BindMemoryPerodicallySaved(be){
|
|
371
|
+
return (
|
|
360
372
|
/**
|
|
361
373
|
* @param {Express.Session} session
|
|
362
374
|
*/
|
|
@@ -405,7 +417,7 @@ function MemoryPerodicallySaved(session){
|
|
|
405
417
|
console.log('dumping sessions every',dumpEverySeconds,'seconds');
|
|
406
418
|
var errorsToShow=4;
|
|
407
419
|
// TODO: hangs the server in devel mode
|
|
408
|
-
setInterval(function(){
|
|
420
|
+
var interval = setInterval(function(){
|
|
409
421
|
try{
|
|
410
422
|
fs.writeFile(fileName,json4all.stringify(store.store.dump()));
|
|
411
423
|
}catch(err){
|
|
@@ -416,18 +428,15 @@ function MemoryPerodicallySaved(session){
|
|
|
416
428
|
}
|
|
417
429
|
}
|
|
418
430
|
},dumpEverySeconds*1000);
|
|
431
|
+
be.shutdownCallbackListAdd({
|
|
432
|
+
message:'session saver',
|
|
433
|
+
fun:()=>clearInterval(interval)
|
|
434
|
+
})
|
|
419
435
|
});
|
|
420
436
|
}
|
|
421
437
|
}
|
|
422
438
|
return MemoryDevelConstructor;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
var sessionStores={
|
|
426
|
-
file: SessionFileStore,
|
|
427
|
-
memory: memorystore,
|
|
428
|
-
memoryDevel: MemoryPerodicallySaved,
|
|
429
|
-
"memory-saved": MemoryPerodicallySaved,
|
|
430
|
-
}
|
|
439
|
+
})}
|
|
431
440
|
|
|
432
441
|
/**
|
|
433
442
|
* @param {string} text
|
|
@@ -455,8 +464,14 @@ AppBackend.prototype.setStaticConfig = function setStaticConfig(defConfigYamlStr
|
|
|
455
464
|
}
|
|
456
465
|
|
|
457
466
|
AppBackend.prototype.configList = function configList(){
|
|
458
|
-
var list=[
|
|
459
|
-
|
|
467
|
+
var list=[
|
|
468
|
+
{
|
|
469
|
+
package:{
|
|
470
|
+
version: packagejson.version
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
this.staticConfig];
|
|
474
|
+
if(!this.staticConfig["client-setup"]?.title && fs.existsSync(this.rootPath+'/def-config.yaml')){
|
|
460
475
|
console.log('DEPRECATED!!!!!!')
|
|
461
476
|
console.error('ERROR el def-config hay que ponerlo dentro de staticConfig');
|
|
462
477
|
console.log('DEPRECATED!!!!!!')
|
|
@@ -466,7 +481,7 @@ AppBackend.prototype.configList = function configList(){
|
|
|
466
481
|
list.push(this.rootPath+'/def-config.yaml')
|
|
467
482
|
};
|
|
468
483
|
list.push(this.rootPath+'/local-config');
|
|
469
|
-
list.push(process.env.BACKEND_PLUS_LOCAL_CONFIG||{});
|
|
484
|
+
list.push(process.env.BACKEND_PLUS_LOCAL_CONFIG?.trim()||{});
|
|
470
485
|
return list;
|
|
471
486
|
};
|
|
472
487
|
|
|
@@ -503,6 +518,11 @@ AppBackend.prototype.canChangePass = async function canChangePass(reqOrContext,
|
|
|
503
518
|
return be.isAdmin(reqOrContext);
|
|
504
519
|
}
|
|
505
520
|
|
|
521
|
+
AppBackend.prototype.shutdownCallbackListAdd = function(messageFun){
|
|
522
|
+
this.shutdownCallbackList.push(messageFun);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
|
|
506
526
|
AppBackend.prototype._Browsers = {
|
|
507
527
|
Edge: {short:'Ed' , minVer:14 , polly:true},
|
|
508
528
|
Konqueror: {short:'Kq' , minVer:null, polly:true},
|
|
@@ -519,7 +539,7 @@ var imgExts = ['jpg', 'png', 'jpeg', 'ico', 'gif', 'svg']
|
|
|
519
539
|
|
|
520
540
|
AppBackend.prototype.exts = {
|
|
521
541
|
img: imgExts,
|
|
522
|
-
normal: ['', 'js', 'map', 'html', 'css', ...imgExts, 'appcache', 'manifest', 'json', 'webmanifest', 'zip', 'pdf', ...fontExts, 'xlsx', 'csv']
|
|
542
|
+
normal: ['', 'js', 'map', 'html', 'css', ...imgExts, 'appcache', 'manifest', 'json', 'webmanifest', 'zip', 'pdf', ...fontExts, 'xlsx', 'csv', 'mjs']
|
|
523
543
|
};
|
|
524
544
|
|
|
525
545
|
/**
|
|
@@ -625,32 +645,33 @@ AppBackend.prototype.start = function start(opts){
|
|
|
625
645
|
// @ts-ignore : only for testing */
|
|
626
646
|
this.getMainApp = function getMainApp(){ return mainApp; };
|
|
627
647
|
}
|
|
628
|
-
this.
|
|
648
|
+
this.shutdownBackend = async function shutdownBackend(){
|
|
629
649
|
console.log('shooting down:');
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
650
|
+
var waitFor = [
|
|
651
|
+
new Promise(function(resolve,reject){
|
|
652
|
+
console.log('*','express server');
|
|
653
|
+
be.server.close(/** @param {Error} err */function(err){
|
|
654
|
+
if(err){
|
|
655
|
+
console.log('*', err)
|
|
656
|
+
reject(err);
|
|
657
|
+
}else{
|
|
658
|
+
console.log('*','express server done!')
|
|
659
|
+
resolve();
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
}),
|
|
663
|
+
...(be.shutdownCallbackList.map(x => (async function(){
|
|
664
|
+
console.log('shut:', x.message)
|
|
665
|
+
await x.fun()
|
|
666
|
+
console.log('done:', x.message)
|
|
667
|
+
})()))
|
|
668
|
+
];
|
|
669
|
+
console.log('*', 'waiting for all')
|
|
670
|
+
await Promise.all(waitFor);
|
|
671
|
+
console.log('*', 'all done')
|
|
672
|
+
mainApp = null;
|
|
673
|
+
console.log('* logWhy',logWhy)
|
|
674
|
+
logWhy && logWhy();
|
|
654
675
|
};
|
|
655
676
|
return Promise.resolve().then(function(){
|
|
656
677
|
var configList=be.configList();
|
|
@@ -681,10 +702,10 @@ AppBackend.prototype.start = function start(opts){
|
|
|
681
702
|
}
|
|
682
703
|
var sessionStoreName=be.config.server["session-store"];
|
|
683
704
|
if(sessionStoreName){
|
|
684
|
-
if(config.devel && sessionStores[sessionStoreName+'Devel']){
|
|
705
|
+
if(config.devel && be.sessionStores[sessionStoreName+'Devel']){
|
|
685
706
|
sessionStoreName+='Devel';
|
|
686
707
|
}
|
|
687
|
-
var storeModule = sessionStores[sessionStoreName];
|
|
708
|
+
var storeModule = be.sessionStores[sessionStoreName];
|
|
688
709
|
be.config.login.plus.store.module = storeModule;
|
|
689
710
|
}
|
|
690
711
|
be.config.install.dump.db.owner=coalesce(be.config.db.owner,be.config.install.dump.db.owner,be.config.db.user);
|
|
@@ -704,7 +725,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
704
725
|
throw new Error("backend-plus: Motor not recongnized: "+be.config.db.motor);
|
|
705
726
|
}
|
|
706
727
|
be.db = pg;
|
|
707
|
-
be.dbUserNameExpr="
|
|
728
|
+
be.dbUserNameExpr="get_app_user()";
|
|
708
729
|
be.dbUserRolExpr=`(select ${be.db.quoteIdent(be.config.login.rolFieldName)}
|
|
709
730
|
from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.db.quoteIdent(be.config.login.table)}
|
|
710
731
|
where ${be.db.quoteIdent(be.config.login.userFieldName)} = ${be.dbUserNameExpr})`
|
|
@@ -760,6 +781,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
760
781
|
pg.log.inFileName = 'last-pg-error-local.sql'
|
|
761
782
|
pg.logLastError.inFileName = 'last-pg-error-local.sql'
|
|
762
783
|
}
|
|
784
|
+
be.config.db.search_path = be.config.db.search_path ?? [be.config.db.schema, 'public'];
|
|
763
785
|
be.getDbClient = function getDbClient(req){
|
|
764
786
|
var paramsDb = be.DoubleDragon?.dbParams?.[req?.user?.[be.config.login.userFieldName]] ?? be.config.db;
|
|
765
787
|
return pg.connect(paramsDb).then(function(client){
|
|
@@ -771,7 +793,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
771
793
|
return client.query(
|
|
772
794
|
"SET application_name = "+be.db.quoteLiteral(dbAppName)
|
|
773
795
|
).execute().then(function(){
|
|
774
|
-
var search_path = be.config.db.search_path
|
|
796
|
+
var search_path = be.config.db.search_path;
|
|
775
797
|
if(search_path.length>0){
|
|
776
798
|
return client.query("set SEARCH_PATH TO "+be.db.quoteIdentList(search_path)).execute().then(function(){
|
|
777
799
|
return client;
|
|
@@ -961,12 +983,18 @@ AppBackend.prototype.start = function start(opts){
|
|
|
961
983
|
loginPlusOpts.noLoggedUrlPath=be.config.login.unloggedLandPage;
|
|
962
984
|
}
|
|
963
985
|
mainApp.loginPlusManager.init(mainApp,changing(be.config.login.plus,loginPlusOpts));
|
|
964
|
-
be.
|
|
986
|
+
be.shutdownCallbackListAdd({
|
|
965
987
|
message:'login-plus manager',
|
|
966
988
|
fun:function(){
|
|
967
989
|
mainApp.loginPlusManager.closeManager();
|
|
968
990
|
}
|
|
969
991
|
});
|
|
992
|
+
be.shutdownCallbackListAdd({
|
|
993
|
+
message:'pg',
|
|
994
|
+
fun:function(){
|
|
995
|
+
pg.shutdown();
|
|
996
|
+
}
|
|
997
|
+
});
|
|
970
998
|
mainApp.loginPlusManager.setValidatorStrategy(
|
|
971
999
|
function(req, username, password, done) {
|
|
972
1000
|
var client;
|
|
@@ -1130,7 +1158,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1130
1158
|
}
|
|
1131
1159
|
return be.sendMail({
|
|
1132
1160
|
to: be.config.mailer?.supervise?.to,
|
|
1133
|
-
subject: `npm start ${be.config["client-setup"]
|
|
1161
|
+
subject: `npm start ${be.config["client-setup"]?.title || packagejson.name} ok ✔️`,
|
|
1134
1162
|
text:`Inicio del servicio: ${new Date().toJSON()}
|
|
1135
1163
|
|
|
1136
1164
|
Contexto: ${os.userInfo().username} ${process.cwd()}
|
|
@@ -1152,7 +1180,7 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1152
1180
|
}
|
|
1153
1181
|
var mailDeAvisoDeFalla = be.sendMail({
|
|
1154
1182
|
to: be.config.mailer?.supervise?.to,
|
|
1155
|
-
subject: `npm start ${be.config["client-setup"]
|
|
1183
|
+
subject: `npm start ${be.config["client-setup"]?.title || packagejson.name} fallido 🛑`,
|
|
1156
1184
|
text:`Falla en el inicio del servicio: ${new Date().toJSON()}
|
|
1157
1185
|
|
|
1158
1186
|
Contexto: ${os.userInfo().username} ${process.cwd()}
|
|
@@ -1209,13 +1237,14 @@ AppBackend.prototype.checkDatabaseStructure = async function checkDatabaseStruct
|
|
|
1209
1237
|
}
|
|
1210
1238
|
if(be.config.login?.forget){
|
|
1211
1239
|
try{
|
|
1212
|
-
await client.query(`select tokentype, info, due from tokens limit 1`).fetchOneRowIfExists();
|
|
1240
|
+
await client.query(`select tokentype, info, due from his.tokens limit 1`).fetchOneRowIfExists();
|
|
1213
1241
|
}catch(err){
|
|
1214
1242
|
var mensaje = `
|
|
1215
1243
|
--------quizas falten los campos en la tabla tokens:
|
|
1216
1244
|
alter table tokens add column tokentype text;
|
|
1217
1245
|
alter table tokens add column info jsonb;
|
|
1218
1246
|
alter table tokens add column due timestamp;
|
|
1247
|
+
--------quizas falte moverla al esquema his.
|
|
1219
1248
|
`;
|
|
1220
1249
|
err.message += mensaje;
|
|
1221
1250
|
console.log(mensaje)
|
|
@@ -1253,6 +1282,21 @@ AppBackend.prototype.checkDatabaseStructure = async function checkDatabaseStruct
|
|
|
1253
1282
|
`;
|
|
1254
1283
|
throw new Error(message);
|
|
1255
1284
|
}
|
|
1285
|
+
var {rows: sql_routines} = await client.query(`SELECT routine_name, routine_schema, routine_definition
|
|
1286
|
+
FROM information_schema.routines
|
|
1287
|
+
WHERE routine_schema in (${be.config.db.search_path.map(path => be.db.quoteLiteral(path)).join(', ')})
|
|
1288
|
+
`).fetchAll();
|
|
1289
|
+
var sqlRoutines = likeAr.toPlainObject(sql_routines, 'routine_name');
|
|
1290
|
+
var message = ''
|
|
1291
|
+
likeAr(AppBackend.prototype.sql_routines).forEach((routine_name, def) => {
|
|
1292
|
+
if (sqlRoutines[routine_name] && def.dump.includes(sqlRoutines[routine_name].routine_definition)) {
|
|
1293
|
+
message += `
|
|
1294
|
+
----- hay que crear o actualizar la rutina ${routine_name}:
|
|
1295
|
+
${dump}
|
|
1296
|
+
`;
|
|
1297
|
+
}
|
|
1298
|
+
})
|
|
1299
|
+
if (message) throw new Error(message);
|
|
1256
1300
|
};
|
|
1257
1301
|
|
|
1258
1302
|
AppBackend.prototype.postConfig = function postConfig(){
|
|
@@ -1511,17 +1555,17 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1511
1555
|
});
|
|
1512
1556
|
}else{
|
|
1513
1557
|
if(progressInfo.lengthComputable){
|
|
1514
|
-
if
|
|
1515
|
-
context.lastMessageSended=
|
|
1558
|
+
if (!context.lastMessageSended) {
|
|
1559
|
+
context.lastMessageSended = {}
|
|
1560
|
+
}
|
|
1561
|
+
if(new Date()-(context.lastMessageSended[progressInfo.idGroup]||0) > 500 || progressInfo.force){
|
|
1562
|
+
context.lastMessageSended[progressInfo.idGroup]=new Date();
|
|
1516
1563
|
res.write(JSON.stringify({progress:progressInfo})+"\n");
|
|
1517
1564
|
if(progressInfo.total){
|
|
1518
1565
|
var rate100=Math.floor(progressInfo.loaded*100/progressInfo.total);
|
|
1519
1566
|
var rate1000=Math.floor(progressInfo.loaded*1000/progressInfo.total);
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
}else{
|
|
1523
|
-
progress2send={message:rate100+'%', ephemeral:true};
|
|
1524
|
-
}
|
|
1567
|
+
var message = rate100<1 && progressInfo.loaded>0 ? '('+(rate1000||'½')+'‰)' : rate100+'%';
|
|
1568
|
+
progress2send={message, ephemeral:true, idGroup:progressInfo.idGroup};
|
|
1525
1569
|
}
|
|
1526
1570
|
}else{
|
|
1527
1571
|
progress2send=null;
|
|
@@ -1724,14 +1768,26 @@ function require_resolve(moduleName){
|
|
|
1724
1768
|
return resolved;
|
|
1725
1769
|
}
|
|
1726
1770
|
|
|
1727
|
-
function resolve_module_dir(moduleName,path){
|
|
1771
|
+
function resolve_module_dir(moduleName,path,fileToCheck){
|
|
1728
1772
|
var baseDir;
|
|
1729
1773
|
if(packagejson.name==moduleName){
|
|
1730
1774
|
baseDir=Path.join(process.cwd(), packagejson.main);
|
|
1731
1775
|
}else{
|
|
1732
1776
|
baseDir=require_resolve(moduleName);
|
|
1733
1777
|
}
|
|
1734
|
-
|
|
1778
|
+
var resultDir = Path.join(Path.dirname(baseDir),path||'');
|
|
1779
|
+
if (fileToCheck) {
|
|
1780
|
+
fs.stat(Path.join(resultDir, fileToCheck)).then(function(status){
|
|
1781
|
+
if (!status) {
|
|
1782
|
+
console.error('Error resolve_module_dir in:',resultDir,'no:',fileToCheck);
|
|
1783
|
+
} else if(!status.isFile()) {
|
|
1784
|
+
console.error('Error resolve_module_dir in:',resultDir,',',fileToCheck,'is not a file');
|
|
1785
|
+
}
|
|
1786
|
+
},function(err){
|
|
1787
|
+
console.error('Error resolve_module_dir in:',resultDir,'err:',err);
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
return resultDir;
|
|
1735
1791
|
}
|
|
1736
1792
|
|
|
1737
1793
|
AppBackend.prototype.optsGenericForFiles = function optsGenericForFiles(req, opts){
|
|
@@ -1894,6 +1950,9 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
1894
1950
|
// http://localhost:3033/img/login-logo-icon.png
|
|
1895
1951
|
mainApp.get(Path.posix.join(baseUrl,'/img/login-logo-icon.png'), async function(req,res,next){
|
|
1896
1952
|
var buscar = [
|
|
1953
|
+
'unlogged/img/login-logo-icon.svg',
|
|
1954
|
+
'dist/unlogged/img/login-logo-icon.svg',
|
|
1955
|
+
'dist/client/unlogged/img/login-logo-icon.svg',
|
|
1897
1956
|
'unlogged/img/login-logo-icon.png',
|
|
1898
1957
|
'dist/unlogged/img/login-logo-icon.png',
|
|
1899
1958
|
'dist/client/unlogged/img/login-logo-icon.png',
|
|
@@ -1921,7 +1980,7 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
1921
1980
|
if(baseUrl=='/'){
|
|
1922
1981
|
baseUrl='';
|
|
1923
1982
|
}
|
|
1924
|
-
let baseLib = baseUrl + '/' + (moduleDef.path ? moduleDef.path : moduleDef.type
|
|
1983
|
+
let baseLib = baseUrl + '/' + (moduleDef.path ? moduleDef.path : be.esJavascript(moduleDef.type)? 'lib': 'css');
|
|
1925
1984
|
try {
|
|
1926
1985
|
var allowedExts=(moduleDef.type=='js'?['js','map']:[moduleDef.type]);
|
|
1927
1986
|
mainApp.use(baseLib, serveContent(resolve_module_dir(moduleDef.module, moduleDef.modPath), { allowedExts }));
|
|
@@ -1935,7 +1994,7 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
1935
1994
|
}
|
|
1936
1995
|
}
|
|
1937
1996
|
} catch (error) {
|
|
1938
|
-
console.error('ERROR: No se pudo servir el módulo
|
|
1997
|
+
console.error('ERROR: No se pudo servir el módulo', moduleDef.module);
|
|
1939
1998
|
throw error;
|
|
1940
1999
|
}
|
|
1941
2000
|
})
|
|
@@ -2056,6 +2115,7 @@ AppBackend.prototype.clientIncludes = function clientIncludes(req, opts) {
|
|
|
2056
2115
|
|
|
2057
2116
|
AppBackend.prototype.clientIncludesCompleted = function clientIncludesCompleted(req, opts) {
|
|
2058
2117
|
let list = this.clientIncludes(req, opts);
|
|
2118
|
+
var be = this;
|
|
2059
2119
|
if(this.config.devel && this.config.devel.useFileDevelopment){
|
|
2060
2120
|
list=list.map(includeDef=>{
|
|
2061
2121
|
if(includeDef.fileProduction){
|
|
@@ -2071,14 +2131,15 @@ AppBackend.prototype.clientIncludesCompleted = function clientIncludesCompleted(
|
|
|
2071
2131
|
return list.map(inclusion =>{
|
|
2072
2132
|
var filename = inclusion.file? inclusion.file: (inclusion.module? Path.basename(require_resolve(inclusion.module,rPaths)): '');
|
|
2073
2133
|
return changing({
|
|
2074
|
-
src: inclusion.type
|
|
2134
|
+
src: be.esJavascript(inclusion.type) ? ((inclusion.path ? inclusion.path : 'lib') + '/' + (inclusion.file ? inclusion.file : filename)) : '',
|
|
2075
2135
|
href: inclusion.type == 'css' ? ((inclusion.path ? inclusion.path : 'css') + '/' + (inclusion.file ? inclusion.file : (inclusion.module + '.css'))) : ''
|
|
2076
2136
|
}, inclusion);
|
|
2077
2137
|
});
|
|
2078
2138
|
}
|
|
2079
2139
|
|
|
2080
2140
|
AppBackend.prototype.clientModules = function clientModules(req, opts) {
|
|
2081
|
-
|
|
2141
|
+
var be = this;
|
|
2142
|
+
return { scripts: this.clientIncludesCompleted(req, opts).filter(x => be.esJavascript(x.type)).map(mod => {return { src: mod.src, type:mod.type=='mjs'?"module":null }})};
|
|
2082
2143
|
}
|
|
2083
2144
|
|
|
2084
2145
|
/**
|
|
@@ -2181,7 +2242,7 @@ AppBackend.prototype.mainPage = function mainPage(req, offlineMode, opts){
|
|
|
2181
2242
|
}
|
|
2182
2243
|
return html.html(attr,[
|
|
2183
2244
|
html.head([
|
|
2184
|
-
html.title(be.config["client-setup"]
|
|
2245
|
+
html.title(be.config["client-setup"]?.title),
|
|
2185
2246
|
html.meta({charset:"utf-8"}),
|
|
2186
2247
|
viewportAttrs?html.meta(viewportAttrs):null,
|
|
2187
2248
|
html.link({href: opts.icons.iconShortcut , rel: "shortcut icon", type: "image/png"}),
|
|
@@ -2621,6 +2682,30 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2621
2682
|
}
|
|
2622
2683
|
var schema=db.quoteIdent(be.config.db.schema);
|
|
2623
2684
|
linesCreate.push("set role to "+owner+";");
|
|
2685
|
+
linesCreate.push(`do
|
|
2686
|
+
$do$
|
|
2687
|
+
declare
|
|
2688
|
+
vExpectedVersion text := '${be.config.db["min-version"]}';
|
|
2689
|
+
vCurrentVersion text := (select setting from pg_settings where name = 'server_version');
|
|
2690
|
+
begin
|
|
2691
|
+
if jsonb('['||replace(vCurrentVersion,'.',',')||']') < jsonb('['||replace(vExpectedVersion,'.',',')||']') then
|
|
2692
|
+
raise exception 'BACKEND-PLUS DUMP: Old PostrgreSQL Version % expected %', vCurrentVersion, vExpectedVersion;
|
|
2693
|
+
end if;
|
|
2694
|
+
end;
|
|
2695
|
+
$do$;
|
|
2696
|
+
`);
|
|
2697
|
+
linesCreate.push(`do
|
|
2698
|
+
$do$
|
|
2699
|
+
declare
|
|
2700
|
+
vExpected text := 'UTF8';
|
|
2701
|
+
vCurrent text := coalesce((select setting from pg_settings where name = 'server_encoding'),'unknown');
|
|
2702
|
+
begin
|
|
2703
|
+
if vCurrent is distinct from vExpected then
|
|
2704
|
+
raise exception 'BACKEND-PLUS DUMP: PostrgreSQL server_encoding %, expected %', vCurrent, vExpected;
|
|
2705
|
+
end if;
|
|
2706
|
+
end;
|
|
2707
|
+
$do$;
|
|
2708
|
+
`);
|
|
2624
2709
|
linesCreate.push("drop schema if exists "+schema+' cascade;');
|
|
2625
2710
|
if (be.config.install.dump["drop-his"]) {
|
|
2626
2711
|
linesCreate.push("drop schema if exists his cascade;");
|
|
@@ -2804,10 +2889,11 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2804
2889
|
lines.push('');
|
|
2805
2890
|
if(tableDef.sql.policies.enabled){
|
|
2806
2891
|
policyLines.push(`ALTER TABLE ${cualQuoteTableName} ENABLE ROW LEVEL SECURITY;`);
|
|
2807
|
-
['all', 'select', 'insert', 'update', 'delete'].forEach((command)=>{
|
|
2808
|
-
var polcom=tableDef.sql.policies[command];
|
|
2809
|
-
if(polcom
|
|
2810
|
-
policyLines.push(`CREATE POLICY
|
|
2892
|
+
[null, 'all', 'select', 'insert', 'update', 'delete'].forEach((command)=>{
|
|
2893
|
+
var polcom=tableDef.sql.policies[command] ?? {using: `true`, permissive:true};
|
|
2894
|
+
if(polcom?.using || polcom?.check){
|
|
2895
|
+
policyLines.push(`CREATE POLICY ${be.db.quoteIdent(polcom.name ?? `bp ${command ?? `base`}`)} ON ${cualQuoteTableName} `+
|
|
2896
|
+
`AS ${polcom.permissive ? `PERMISSIVE` : `RESTRICTIVE`} FOR ${command ?? `all`} TO ${be.config.db.user}`+
|
|
2811
2897
|
(polcom.using? ` USING ( ${polcom.using} )`:'')+
|
|
2812
2898
|
(polcom.check?` WITH CHECK ( ${polcom.check} )`:'')+';'
|
|
2813
2899
|
);
|
|
@@ -2842,8 +2928,14 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2842
2928
|
if (err.code != 'ENOENT') throw err;
|
|
2843
2929
|
try {
|
|
2844
2930
|
var allTableContent = await fs.readFile('install/local-dump.psql','utf-8');
|
|
2845
|
-
var startIndex = allTableContent.
|
|
2846
|
-
|
|
2931
|
+
var startIndex = allTableContent.indexOf('-- Data for Name: ');
|
|
2932
|
+
console.log('startIndex', startIndex);
|
|
2933
|
+
var lastUseful = allTableContent.lastIndexOf('\nSELECT pg_catalog.setval')
|
|
2934
|
+
console.log('lastUseful', lastUseful);
|
|
2935
|
+
if (lastUseful == -1) lastUseful = allTableContent.lastIndexOf('\n\\.\n');
|
|
2936
|
+
console.log('lastUseful', lastUseful);
|
|
2937
|
+
var lastIndex = allTableContent.indexOf('\n--', lastUseful);
|
|
2938
|
+
console.log('lastIndex', lastIndex);
|
|
2847
2939
|
allTableData = allTableContent.slice(startIndex, lastIndex);
|
|
2848
2940
|
} catch(err) {
|
|
2849
2941
|
if (err.code != 'ENOENT') throw err;
|
|
@@ -2920,29 +3012,39 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2920
3012
|
}else{
|
|
2921
3013
|
var lines=content.split(/\r?\n/)
|
|
2922
3014
|
.filter(line => !(/^[-| ]*$/.test(line)) )
|
|
2923
|
-
.map(line => line
|
|
3015
|
+
.map(line => splitRawRowIntoRow(line))
|
|
2924
3016
|
.filter(line => line.length>1 || line.length==1 && line[0].trim() );
|
|
2925
3017
|
if(lines.length>1){
|
|
2926
3018
|
if(lines[0][0].startsWith('\ufeff')){
|
|
2927
3019
|
lines[0][0]=lines[0][0].replace('\ufeff','')
|
|
2928
3020
|
}
|
|
2929
3021
|
rows=lines.slice(1);
|
|
3022
|
+
var filteredFieldDef = lines[0].filter(filterField);
|
|
2930
3023
|
if(tablesWithStrictSequence[tableName]){
|
|
2931
3024
|
var dataString="COPY "+db.quoteIdent(tableName)+" ("+
|
|
2932
|
-
|
|
3025
|
+
filteredFieldDef.map(db.quoteIdent).join(', ')+
|
|
2933
3026
|
') FROM stdin;\n'+
|
|
2934
3027
|
rows.map(function(line){
|
|
2935
|
-
return line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value){
|
|
2936
|
-
|
|
3028
|
+
return line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value,i){
|
|
3029
|
+
var def = tableDef.field[filteredFieldDef[i]];
|
|
3030
|
+
return value==='' ? (
|
|
3031
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? '' : '\\N'
|
|
3032
|
+
): value.replace(/\\/g,'\\\\').replace(/\t/g,'\\t').replace(/\n/g,'\\n').replace(/\r/g,'\\r');
|
|
2937
3033
|
}).join('\t')+'\n';
|
|
2938
3034
|
}).join('')+'\\.\n';
|
|
2939
3035
|
}else{
|
|
2940
3036
|
var dataString="insert into "+db.quoteIdent(tableName)+" ("+
|
|
2941
|
-
|
|
3037
|
+
filteredFieldDef.map(db.quoteIdent).join(', ')+
|
|
2942
3038
|
') values\n'+
|
|
2943
3039
|
rows.map(function(line){
|
|
2944
|
-
return "("+line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value){
|
|
2945
|
-
|
|
3040
|
+
return "("+line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value,i){
|
|
3041
|
+
var def = tableDef.field[filteredFieldDef[i]];
|
|
3042
|
+
if(def == null) {
|
|
3043
|
+
throw Error("no se encuentra la columna "+filteredFieldDef[i]+" en "+tableName);
|
|
3044
|
+
}
|
|
3045
|
+
return value==='' ? (
|
|
3046
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? "''" : 'null'
|
|
3047
|
+
) : db.quoteNullable(value);
|
|
2946
3048
|
}).join(', ')+")";
|
|
2947
3049
|
}).join(',\n')+';\n';
|
|
2948
3050
|
}
|
|
@@ -2965,28 +3067,65 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2965
3067
|
console.log('silence "skipping content" messages in "local-config.yaml".install.dump.skip-content=true');
|
|
2966
3068
|
}
|
|
2967
3069
|
}
|
|
3070
|
+
let installFolders = be.config.install.dump.folders ?? ['install']
|
|
2968
3071
|
let texts = await Promise.all(
|
|
2969
|
-
[
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3072
|
+
[
|
|
3073
|
+
['prepare.sql'],
|
|
3074
|
+
['pre-adapt.sql'].concat(be.config.install.dump.scripts['pre-adapt']),
|
|
3075
|
+
['adapt.sql'],
|
|
3076
|
+
be.config.install.dump.scripts['prepare'] ?? [],
|
|
3077
|
+
be.config.install.dump.scripts['post-adapt'] ?? []
|
|
3078
|
+
]
|
|
3079
|
+
.map(async function(fileNames){
|
|
3080
|
+
if (!fileNames) return '';
|
|
3081
|
+
var i = 0;
|
|
3082
|
+
return (await Promise.all(fileNames.map(async fileName => {
|
|
3083
|
+
var content;
|
|
3084
|
+
do {
|
|
3085
|
+
var folder = installFolders[i];
|
|
3086
|
+
try{
|
|
3087
|
+
content = await fs.readFile(be.rootPath+'/'+folder+'/'+fileName, {encoding:'UTF8'});
|
|
3088
|
+
} catch (err) {
|
|
3089
|
+
if(err.code!='ENOENT') throw err;
|
|
3090
|
+
}
|
|
3091
|
+
i++;
|
|
3092
|
+
} while (i < installFolders.length && !content);
|
|
3093
|
+
if (!content) {
|
|
3094
|
+
return '-- no '+fileName+'\n';
|
|
3095
|
+
} else {
|
|
3096
|
+
return '-- '+folder+'/'+fileName+'\n'+content;
|
|
3097
|
+
};
|
|
3098
|
+
}))).join('\n')
|
|
2982
3099
|
})
|
|
2983
3100
|
);
|
|
3101
|
+
|
|
3102
|
+
var common = (await Promise.all(be.appStack.map(async function(stackNode){
|
|
3103
|
+
var common = [];
|
|
3104
|
+
for (var prefix of ['../', '../../']) {
|
|
3105
|
+
try {
|
|
3106
|
+
var dirName = Path.join(stackNode.path,prefix+'install').replace(regexpDistReplacer,'$1$2')
|
|
3107
|
+
var list = await fs.readdir(dirName);
|
|
3108
|
+
} catch (err) {
|
|
3109
|
+
if (err.code != 'ENOENT') throw err;
|
|
3110
|
+
var list = [];
|
|
3111
|
+
}
|
|
3112
|
+
for(var fileName of list){
|
|
3113
|
+
if (fileName.endsWith('-fun.sql')) {
|
|
3114
|
+
common.push(await fs.readFile(Path.join(dirName,fileName), 'utf-8'));
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
return common.join('\n');
|
|
3119
|
+
}))).join('\n');
|
|
3120
|
+
|
|
2984
3121
|
var prepareList=(be.config.install.dump.scripts['prepare']||[]);
|
|
2985
3122
|
var mainSql=(
|
|
2986
3123
|
(complete? linesCreate.join('\n'): '')+
|
|
2987
3124
|
(complete||opts.forDump? searchPathline.join('\n'): '')+
|
|
3125
|
+
'\n-- common'+
|
|
3126
|
+
common+'\n'+
|
|
2988
3127
|
(complete? '\n\n--prepare.sql\n'+ texts[0]+'\n\n' :'' )+
|
|
2989
|
-
(complete? texts
|
|
3128
|
+
(complete? texts[3] + '\n\n' : '' )+
|
|
2990
3129
|
'\n-- functions\n' + functionLines.join('\n')+
|
|
2991
3130
|
'\n-- lines \n' + lines.join('\n')+
|
|
2992
3131
|
(complete? ('\n\n-- pre-ADAPTs\n'+texts[1]+'\n\n') : '' )+
|
|
@@ -2996,7 +3135,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2996
3135
|
'\n-- FKs\n' + fkLines.join('\n')+
|
|
2997
3136
|
'\n-- index\n' + indexLines.join('\n')+
|
|
2998
3137
|
'\n-- policies\n' + policyLines.join('\n')+
|
|
2999
|
-
(complete? texts
|
|
3138
|
+
(complete? texts[4] + '\n\n' : '' )+
|
|
3000
3139
|
(complete? (be.config.install.dump.enances==='inline'?enancePart:'') :'')
|
|
3001
3140
|
).replace(/\uFEFF/g /*inner BOM replacing*/,'\n\n').replace(
|
|
3002
3141
|
new RegExp(escapeRegExp(db.quoteIdent(be.config.install.dump.db["owner4special-scripts"])),'g'),
|
|
@@ -3117,6 +3256,8 @@ AppBackend.prototype.exportacionesGenerico = async function exportacionesGeneric
|
|
|
3117
3256
|
if (typeof result[0].title !== "string" || typeof result[0].rows !== "object" && !(result[0].rows instanceof Array) ) {
|
|
3118
3257
|
throw new Error ("exportacionesGenerico debe recibir {title:string, rows:Record<string, any>[]}")
|
|
3119
3258
|
}
|
|
3259
|
+
csvFileName = result[0].csvFileName ?? csvFileName;
|
|
3260
|
+
fileName = result[0].fileName ?? fileName;
|
|
3120
3261
|
await bestGlobals.sleep(100);
|
|
3121
3262
|
context.informProgress({message:`buscando archivos recién generados`})
|
|
3122
3263
|
/** @type {{url:string, label:string}[]} */
|