backend-plus 2.0.0-rc.2 → 2.0.0-rc.21
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-menu.js +15 -11
- package/for-client/my-tables.js +23 -19
- package/for-client/my-websqldb.d.ts +0 -1
- package/install/get_app_user-fun.sql +6 -0
- package/lib/backend-plus.d.ts +149 -23
- package/lib/backend-plus.js +193 -88
- package/lib/procedures-table.js +47 -18
- package/package.json +30 -29
- 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;
|
|
@@ -1894,6 +1938,9 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
1894
1938
|
// http://localhost:3033/img/login-logo-icon.png
|
|
1895
1939
|
mainApp.get(Path.posix.join(baseUrl,'/img/login-logo-icon.png'), async function(req,res,next){
|
|
1896
1940
|
var buscar = [
|
|
1941
|
+
'unlogged/img/login-logo-icon.svg',
|
|
1942
|
+
'dist/unlogged/img/login-logo-icon.svg',
|
|
1943
|
+
'dist/client/unlogged/img/login-logo-icon.svg',
|
|
1897
1944
|
'unlogged/img/login-logo-icon.png',
|
|
1898
1945
|
'dist/unlogged/img/login-logo-icon.png',
|
|
1899
1946
|
'dist/client/unlogged/img/login-logo-icon.png',
|
|
@@ -1921,7 +1968,7 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
1921
1968
|
if(baseUrl=='/'){
|
|
1922
1969
|
baseUrl='';
|
|
1923
1970
|
}
|
|
1924
|
-
let baseLib = baseUrl + '/' + (moduleDef.path ? moduleDef.path : moduleDef.type
|
|
1971
|
+
let baseLib = baseUrl + '/' + (moduleDef.path ? moduleDef.path : be.esJavascript(moduleDef.type)? 'lib': 'css');
|
|
1925
1972
|
try {
|
|
1926
1973
|
var allowedExts=(moduleDef.type=='js'?['js','map']:[moduleDef.type]);
|
|
1927
1974
|
mainApp.use(baseLib, serveContent(resolve_module_dir(moduleDef.module, moduleDef.modPath), { allowedExts }));
|
|
@@ -2056,6 +2103,7 @@ AppBackend.prototype.clientIncludes = function clientIncludes(req, opts) {
|
|
|
2056
2103
|
|
|
2057
2104
|
AppBackend.prototype.clientIncludesCompleted = function clientIncludesCompleted(req, opts) {
|
|
2058
2105
|
let list = this.clientIncludes(req, opts);
|
|
2106
|
+
var be = this;
|
|
2059
2107
|
if(this.config.devel && this.config.devel.useFileDevelopment){
|
|
2060
2108
|
list=list.map(includeDef=>{
|
|
2061
2109
|
if(includeDef.fileProduction){
|
|
@@ -2071,14 +2119,15 @@ AppBackend.prototype.clientIncludesCompleted = function clientIncludesCompleted(
|
|
|
2071
2119
|
return list.map(inclusion =>{
|
|
2072
2120
|
var filename = inclusion.file? inclusion.file: (inclusion.module? Path.basename(require_resolve(inclusion.module,rPaths)): '');
|
|
2073
2121
|
return changing({
|
|
2074
|
-
src: inclusion.type
|
|
2122
|
+
src: be.esJavascript(inclusion.type) ? ((inclusion.path ? inclusion.path : 'lib') + '/' + (inclusion.file ? inclusion.file : filename)) : '',
|
|
2075
2123
|
href: inclusion.type == 'css' ? ((inclusion.path ? inclusion.path : 'css') + '/' + (inclusion.file ? inclusion.file : (inclusion.module + '.css'))) : ''
|
|
2076
2124
|
}, inclusion);
|
|
2077
2125
|
});
|
|
2078
2126
|
}
|
|
2079
2127
|
|
|
2080
2128
|
AppBackend.prototype.clientModules = function clientModules(req, opts) {
|
|
2081
|
-
|
|
2129
|
+
var be = this;
|
|
2130
|
+
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
2131
|
}
|
|
2083
2132
|
|
|
2084
2133
|
/**
|
|
@@ -2181,7 +2230,7 @@ AppBackend.prototype.mainPage = function mainPage(req, offlineMode, opts){
|
|
|
2181
2230
|
}
|
|
2182
2231
|
return html.html(attr,[
|
|
2183
2232
|
html.head([
|
|
2184
|
-
html.title(be.config["client-setup"]
|
|
2233
|
+
html.title(be.config["client-setup"]?.title),
|
|
2185
2234
|
html.meta({charset:"utf-8"}),
|
|
2186
2235
|
viewportAttrs?html.meta(viewportAttrs):null,
|
|
2187
2236
|
html.link({href: opts.icons.iconShortcut , rel: "shortcut icon", type: "image/png"}),
|
|
@@ -2804,10 +2853,11 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2804
2853
|
lines.push('');
|
|
2805
2854
|
if(tableDef.sql.policies.enabled){
|
|
2806
2855
|
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
|
|
2856
|
+
[null, 'all', 'select', 'insert', 'update', 'delete'].forEach((command)=>{
|
|
2857
|
+
var polcom=tableDef.sql.policies[command] ?? {using: `true`, permissive:true};
|
|
2858
|
+
if(polcom?.using || polcom?.check){
|
|
2859
|
+
policyLines.push(`CREATE POLICY ${be.db.quoteIdent(polcom.name ?? `bp ${command ?? `base`}`)} ON ${cualQuoteTableName} `+
|
|
2860
|
+
`AS ${polcom.permissive ? `PERMISSIVE` : `RESTRICTIVE`} FOR ${command ?? `all`} TO ${be.config.db.user}`+
|
|
2811
2861
|
(polcom.using? ` USING ( ${polcom.using} )`:'')+
|
|
2812
2862
|
(polcom.check?` WITH CHECK ( ${polcom.check} )`:'')+';'
|
|
2813
2863
|
);
|
|
@@ -2842,12 +2892,18 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2842
2892
|
if (err.code != 'ENOENT') throw err;
|
|
2843
2893
|
try {
|
|
2844
2894
|
var allTableContent = await fs.readFile('install/local-dump.psql','utf-8');
|
|
2845
|
-
var startIndex = allTableContent.
|
|
2846
|
-
|
|
2895
|
+
var startIndex = allTableContent.indexOf('-- Data for Name: ');
|
|
2896
|
+
console.log('startIndex', startIndex);
|
|
2897
|
+
var lastUseful = allTableContent.lastIndexOf('\nSELECT pg_catalog.setval')
|
|
2898
|
+
console.log('lastUseful', lastUseful);
|
|
2899
|
+
if (lastUseful == -1) lastUseful = allTableContent.lastIndexOf('\n\\.\n');
|
|
2900
|
+
console.log('lastUseful', lastUseful);
|
|
2901
|
+
var lastIndex = allTableContent.indexOf('\n--', lastUseful);
|
|
2902
|
+
console.log('lastIndex', lastIndex);
|
|
2847
2903
|
allTableData = allTableContent.slice(startIndex, lastIndex);
|
|
2848
2904
|
} catch(err) {
|
|
2849
2905
|
if (err.code != 'ENOENT') throw err;
|
|
2850
|
-
throw err;
|
|
2906
|
+
//throw err;
|
|
2851
2907
|
}
|
|
2852
2908
|
}
|
|
2853
2909
|
if (allTableData) {
|
|
@@ -2920,29 +2976,39 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2920
2976
|
}else{
|
|
2921
2977
|
var lines=content.split(/\r?\n/)
|
|
2922
2978
|
.filter(line => !(/^[-| ]*$/.test(line)) )
|
|
2923
|
-
.map(line => line
|
|
2979
|
+
.map(line => splitRawRowIntoRow(line))
|
|
2924
2980
|
.filter(line => line.length>1 || line.length==1 && line[0].trim() );
|
|
2925
2981
|
if(lines.length>1){
|
|
2926
2982
|
if(lines[0][0].startsWith('\ufeff')){
|
|
2927
2983
|
lines[0][0]=lines[0][0].replace('\ufeff','')
|
|
2928
2984
|
}
|
|
2929
2985
|
rows=lines.slice(1);
|
|
2986
|
+
var filteredFieldDef = lines[0].filter(filterField);
|
|
2930
2987
|
if(tablesWithStrictSequence[tableName]){
|
|
2931
2988
|
var dataString="COPY "+db.quoteIdent(tableName)+" ("+
|
|
2932
|
-
|
|
2989
|
+
filteredFieldDef.map(db.quoteIdent).join(', ')+
|
|
2933
2990
|
') FROM stdin;\n'+
|
|
2934
2991
|
rows.map(function(line){
|
|
2935
|
-
return line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value){
|
|
2936
|
-
|
|
2992
|
+
return line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value,i){
|
|
2993
|
+
var def = tableDef.field[filteredFieldDef[i]];
|
|
2994
|
+
return value==='' ? (
|
|
2995
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? '' : '\\N'
|
|
2996
|
+
): value.replace(/\\/g,'\\\\').replace(/\t/g,'\\t').replace(/\n/g,'\\n').replace(/\r/g,'\\r');
|
|
2937
2997
|
}).join('\t')+'\n';
|
|
2938
2998
|
}).join('')+'\\.\n';
|
|
2939
2999
|
}else{
|
|
2940
3000
|
var dataString="insert into "+db.quoteIdent(tableName)+" ("+
|
|
2941
|
-
|
|
3001
|
+
filteredFieldDef.map(db.quoteIdent).join(', ')+
|
|
2942
3002
|
') values\n'+
|
|
2943
3003
|
rows.map(function(line){
|
|
2944
|
-
return "("+line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value){
|
|
2945
|
-
|
|
3004
|
+
return "("+line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value,i){
|
|
3005
|
+
var def = tableDef.field[filteredFieldDef[i]];
|
|
3006
|
+
if(def == null) {
|
|
3007
|
+
throw Error("no se encuentra la columna "+filteredFieldDef[i]+" en "+tableName);
|
|
3008
|
+
}
|
|
3009
|
+
return value==='' ? (
|
|
3010
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? "''" : 'null'
|
|
3011
|
+
) : db.quoteNullable(value);
|
|
2946
3012
|
}).join(', ')+")";
|
|
2947
3013
|
}).join(',\n')+';\n';
|
|
2948
3014
|
}
|
|
@@ -2965,28 +3031,65 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2965
3031
|
console.log('silence "skipping content" messages in "local-config.yaml".install.dump.skip-content=true');
|
|
2966
3032
|
}
|
|
2967
3033
|
}
|
|
3034
|
+
let installFolders = be.config.install.dump.folders ?? ['install']
|
|
2968
3035
|
let texts = await Promise.all(
|
|
2969
|
-
[
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3036
|
+
[
|
|
3037
|
+
['prepare.sql'],
|
|
3038
|
+
['pre-adapt.sql'].concat(be.config.install.dump.scripts['pre-adapt']),
|
|
3039
|
+
['adapt.sql'],
|
|
3040
|
+
be.config.install.dump.scripts['prepare'] ?? [],
|
|
3041
|
+
be.config.install.dump.scripts['post-adapt'] ?? []
|
|
3042
|
+
]
|
|
3043
|
+
.map(async function(fileNames){
|
|
3044
|
+
if (!fileNames) return '';
|
|
3045
|
+
var i = 0;
|
|
3046
|
+
return (await Promise.all(fileNames.map(async fileName => {
|
|
3047
|
+
var content;
|
|
3048
|
+
do {
|
|
3049
|
+
var folder = installFolders[i];
|
|
3050
|
+
try{
|
|
3051
|
+
content = await fs.readFile(be.rootPath+'/'+folder+'/'+fileName, {encoding:'UTF8'});
|
|
3052
|
+
} catch (err) {
|
|
3053
|
+
if(err.code!='ENOENT') throw err;
|
|
3054
|
+
}
|
|
3055
|
+
i++;
|
|
3056
|
+
} while (i < installFolders.length && !content);
|
|
3057
|
+
if (!content) {
|
|
3058
|
+
return '-- no '+fileName+'\n';
|
|
3059
|
+
} else {
|
|
3060
|
+
return '-- '+folder+'/'+fileName+'\n'+content;
|
|
3061
|
+
};
|
|
3062
|
+
}))).join('\n')
|
|
2982
3063
|
})
|
|
2983
3064
|
);
|
|
3065
|
+
|
|
3066
|
+
var common = (await Promise.all(be.appStack.map(async function(stackNode){
|
|
3067
|
+
var common = [];
|
|
3068
|
+
for (var prefix of ['../', '../../']) {
|
|
3069
|
+
try {
|
|
3070
|
+
var dirName = Path.join(stackNode.path,prefix+'install').replace(regexpDistReplacer,'$1$2')
|
|
3071
|
+
var list = await fs.readdir(dirName);
|
|
3072
|
+
} catch (err) {
|
|
3073
|
+
if (err.code != 'ENOENT') throw err;
|
|
3074
|
+
var list = [];
|
|
3075
|
+
}
|
|
3076
|
+
for(var fileName of list){
|
|
3077
|
+
if (fileName.endsWith('-fun.sql')) {
|
|
3078
|
+
common.push(await fs.readFile(Path.join(dirName,fileName), 'utf-8'));
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
return common.join('\n');
|
|
3083
|
+
}))).join('\n');
|
|
3084
|
+
|
|
2984
3085
|
var prepareList=(be.config.install.dump.scripts['prepare']||[]);
|
|
2985
3086
|
var mainSql=(
|
|
2986
3087
|
(complete? linesCreate.join('\n'): '')+
|
|
2987
3088
|
(complete||opts.forDump? searchPathline.join('\n'): '')+
|
|
3089
|
+
'\n-- common'+
|
|
3090
|
+
common+'\n'+
|
|
2988
3091
|
(complete? '\n\n--prepare.sql\n'+ texts[0]+'\n\n' :'' )+
|
|
2989
|
-
(complete? texts
|
|
3092
|
+
(complete? texts[3] + '\n\n' : '' )+
|
|
2990
3093
|
'\n-- functions\n' + functionLines.join('\n')+
|
|
2991
3094
|
'\n-- lines \n' + lines.join('\n')+
|
|
2992
3095
|
(complete? ('\n\n-- pre-ADAPTs\n'+texts[1]+'\n\n') : '' )+
|
|
@@ -2996,7 +3099,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2996
3099
|
'\n-- FKs\n' + fkLines.join('\n')+
|
|
2997
3100
|
'\n-- index\n' + indexLines.join('\n')+
|
|
2998
3101
|
'\n-- policies\n' + policyLines.join('\n')+
|
|
2999
|
-
(complete? texts
|
|
3102
|
+
(complete? texts[4] + '\n\n' : '' )+
|
|
3000
3103
|
(complete? (be.config.install.dump.enances==='inline'?enancePart:'') :'')
|
|
3001
3104
|
).replace(/\uFEFF/g /*inner BOM replacing*/,'\n\n').replace(
|
|
3002
3105
|
new RegExp(escapeRegExp(db.quoteIdent(be.config.install.dump.db["owner4special-scripts"])),'g'),
|
|
@@ -3117,6 +3220,8 @@ AppBackend.prototype.exportacionesGenerico = async function exportacionesGeneric
|
|
|
3117
3220
|
if (typeof result[0].title !== "string" || typeof result[0].rows !== "object" && !(result[0].rows instanceof Array) ) {
|
|
3118
3221
|
throw new Error ("exportacionesGenerico debe recibir {title:string, rows:Record<string, any>[]}")
|
|
3119
3222
|
}
|
|
3223
|
+
csvFileName = result[0].csvFileName ?? csvFileName;
|
|
3224
|
+
fileName = result[0].fileName ?? fileName;
|
|
3120
3225
|
await bestGlobals.sleep(100);
|
|
3121
3226
|
context.informProgress({message:`buscando archivos recién generados`})
|
|
3122
3227
|
/** @type {{url:string, label:string}[]} */
|