backend-plus 2.0.0-rc.9 → 2.0.2
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 +4 -0
- package/for-client/my-localdb.js +2 -2
- package/for-client/my-menu.js +14 -10
- package/for-client/my-tables.js +13 -17
- package/for-client/my-websqldb.d.ts +0 -1
- package/for-client/my-websqldb.js +2 -2
- package/lib/backend-plus.d.ts +158 -24
- package/lib/backend-plus.js +178 -85
- package/lib/procedures-table.js +44 -14
- package/package.json +36 -36
- package/unlogged/my-ajax.js +52 -33
package/lib/backend-plus.js
CHANGED
|
@@ -97,7 +97,13 @@ class AppBackend{
|
|
|
97
97
|
/** @type {{path:string}[]} */
|
|
98
98
|
this.appStack=[];
|
|
99
99
|
/** @type {{message:string; fun:()=>void}[]} */
|
|
100
|
-
this.
|
|
100
|
+
this.shutdownCallbackList=[];
|
|
101
|
+
this.sessionStores={
|
|
102
|
+
file: SessionFileStore,
|
|
103
|
+
memory: memorystore,
|
|
104
|
+
memoryDevel: BindMemoryPerodicallySaved(this),
|
|
105
|
+
"memory-saved": BindMemoryPerodicallySaved(this),
|
|
106
|
+
}
|
|
101
107
|
if(!this.rootPath){
|
|
102
108
|
console.log('ATENCIÓN hay que poner be.rootPath antes de llamar a super()');
|
|
103
109
|
this.rootPath=Path.resolve(__dirname,'..');
|
|
@@ -119,11 +125,14 @@ class AppBackend{
|
|
|
119
125
|
this.tableStructures = {};
|
|
120
126
|
this.configStaticConfig();
|
|
121
127
|
/** @type {null|()=>Promise<void>} */
|
|
122
|
-
this.
|
|
128
|
+
this.shutdownBackend = null;
|
|
123
129
|
/** @type {bp.Server} */
|
|
124
130
|
// @ts-ignore
|
|
125
131
|
this.server = null;
|
|
126
132
|
}
|
|
133
|
+
esJavascript(type){
|
|
134
|
+
return ['js','mjs'].includes(type)
|
|
135
|
+
}
|
|
127
136
|
clearCaches(){
|
|
128
137
|
this.caches={
|
|
129
138
|
procedures:{}
|
|
@@ -358,6 +367,8 @@ AppBackend.prototype.i18n.messages.es={
|
|
|
358
367
|
}
|
|
359
368
|
};
|
|
360
369
|
|
|
370
|
+
function BindMemoryPerodicallySaved(be){
|
|
371
|
+
return (
|
|
361
372
|
/**
|
|
362
373
|
* @param {Express.Session} session
|
|
363
374
|
*/
|
|
@@ -406,7 +417,7 @@ function MemoryPerodicallySaved(session){
|
|
|
406
417
|
console.log('dumping sessions every',dumpEverySeconds,'seconds');
|
|
407
418
|
var errorsToShow=4;
|
|
408
419
|
// TODO: hangs the server in devel mode
|
|
409
|
-
setInterval(function(){
|
|
420
|
+
var interval = setInterval(function(){
|
|
410
421
|
try{
|
|
411
422
|
fs.writeFile(fileName,json4all.stringify(store.store.dump()));
|
|
412
423
|
}catch(err){
|
|
@@ -417,18 +428,15 @@ function MemoryPerodicallySaved(session){
|
|
|
417
428
|
}
|
|
418
429
|
}
|
|
419
430
|
},dumpEverySeconds*1000);
|
|
431
|
+
be.shutdownCallbackListAdd({
|
|
432
|
+
message:'session saver',
|
|
433
|
+
fun:()=>clearInterval(interval)
|
|
434
|
+
})
|
|
420
435
|
});
|
|
421
436
|
}
|
|
422
437
|
}
|
|
423
438
|
return MemoryDevelConstructor;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
var sessionStores={
|
|
427
|
-
file: SessionFileStore,
|
|
428
|
-
memory: memorystore,
|
|
429
|
-
memoryDevel: MemoryPerodicallySaved,
|
|
430
|
-
"memory-saved": MemoryPerodicallySaved,
|
|
431
|
-
}
|
|
439
|
+
})}
|
|
432
440
|
|
|
433
441
|
/**
|
|
434
442
|
* @param {string} text
|
|
@@ -456,7 +464,13 @@ AppBackend.prototype.setStaticConfig = function setStaticConfig(defConfigYamlStr
|
|
|
456
464
|
}
|
|
457
465
|
|
|
458
466
|
AppBackend.prototype.configList = function configList(){
|
|
459
|
-
var list=[
|
|
467
|
+
var list=[
|
|
468
|
+
{
|
|
469
|
+
package:{
|
|
470
|
+
version: packagejson.version
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
this.staticConfig];
|
|
460
474
|
if(!this.staticConfig["client-setup"]?.title && fs.existsSync(this.rootPath+'/def-config.yaml')){
|
|
461
475
|
console.log('DEPRECATED!!!!!!')
|
|
462
476
|
console.error('ERROR el def-config hay que ponerlo dentro de staticConfig');
|
|
@@ -504,6 +518,11 @@ AppBackend.prototype.canChangePass = async function canChangePass(reqOrContext,
|
|
|
504
518
|
return be.isAdmin(reqOrContext);
|
|
505
519
|
}
|
|
506
520
|
|
|
521
|
+
AppBackend.prototype.shutdownCallbackListAdd = function(messageFun){
|
|
522
|
+
this.shutdownCallbackList.push(messageFun);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
|
|
507
526
|
AppBackend.prototype._Browsers = {
|
|
508
527
|
Edge: {short:'Ed' , minVer:14 , polly:true},
|
|
509
528
|
Konqueror: {short:'Kq' , minVer:null, polly:true},
|
|
@@ -520,7 +539,7 @@ var imgExts = ['jpg', 'png', 'jpeg', 'ico', 'gif', 'svg']
|
|
|
520
539
|
|
|
521
540
|
AppBackend.prototype.exts = {
|
|
522
541
|
img: imgExts,
|
|
523
|
-
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']
|
|
524
543
|
};
|
|
525
544
|
|
|
526
545
|
/**
|
|
@@ -626,32 +645,33 @@ AppBackend.prototype.start = function start(opts){
|
|
|
626
645
|
// @ts-ignore : only for testing */
|
|
627
646
|
this.getMainApp = function getMainApp(){ return mainApp; };
|
|
628
647
|
}
|
|
629
|
-
this.
|
|
648
|
+
this.shutdownBackend = async function shutdownBackend(){
|
|
630
649
|
console.log('shooting down:');
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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();
|
|
655
675
|
};
|
|
656
676
|
return Promise.resolve().then(function(){
|
|
657
677
|
var configList=be.configList();
|
|
@@ -682,10 +702,10 @@ AppBackend.prototype.start = function start(opts){
|
|
|
682
702
|
}
|
|
683
703
|
var sessionStoreName=be.config.server["session-store"];
|
|
684
704
|
if(sessionStoreName){
|
|
685
|
-
if(config.devel && sessionStores[sessionStoreName+'Devel']){
|
|
705
|
+
if(config.devel && be.sessionStores[sessionStoreName+'Devel']){
|
|
686
706
|
sessionStoreName+='Devel';
|
|
687
707
|
}
|
|
688
|
-
var storeModule = sessionStores[sessionStoreName];
|
|
708
|
+
var storeModule = be.sessionStores[sessionStoreName];
|
|
689
709
|
be.config.login.plus.store.module = storeModule;
|
|
690
710
|
}
|
|
691
711
|
be.config.install.dump.db.owner=coalesce(be.config.db.owner,be.config.install.dump.db.owner,be.config.db.user);
|
|
@@ -963,12 +983,18 @@ AppBackend.prototype.start = function start(opts){
|
|
|
963
983
|
loginPlusOpts.noLoggedUrlPath=be.config.login.unloggedLandPage;
|
|
964
984
|
}
|
|
965
985
|
mainApp.loginPlusManager.init(mainApp,changing(be.config.login.plus,loginPlusOpts));
|
|
966
|
-
be.
|
|
986
|
+
be.shutdownCallbackListAdd({
|
|
967
987
|
message:'login-plus manager',
|
|
968
988
|
fun:function(){
|
|
969
989
|
mainApp.loginPlusManager.closeManager();
|
|
970
990
|
}
|
|
971
991
|
});
|
|
992
|
+
be.shutdownCallbackListAdd({
|
|
993
|
+
message:'pg',
|
|
994
|
+
fun:function(){
|
|
995
|
+
pg.shutdown();
|
|
996
|
+
}
|
|
997
|
+
});
|
|
972
998
|
mainApp.loginPlusManager.setValidatorStrategy(
|
|
973
999
|
function(req, username, password, done) {
|
|
974
1000
|
var client;
|
|
@@ -1211,13 +1237,14 @@ AppBackend.prototype.checkDatabaseStructure = async function checkDatabaseStruct
|
|
|
1211
1237
|
}
|
|
1212
1238
|
if(be.config.login?.forget){
|
|
1213
1239
|
try{
|
|
1214
|
-
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();
|
|
1215
1241
|
}catch(err){
|
|
1216
1242
|
var mensaje = `
|
|
1217
1243
|
--------quizas falten los campos en la tabla tokens:
|
|
1218
1244
|
alter table tokens add column tokentype text;
|
|
1219
1245
|
alter table tokens add column info jsonb;
|
|
1220
1246
|
alter table tokens add column due timestamp;
|
|
1247
|
+
--------quizas falte moverla al esquema his.
|
|
1221
1248
|
`;
|
|
1222
1249
|
err.message += mensaje;
|
|
1223
1250
|
console.log(mensaje)
|
|
@@ -1528,17 +1555,17 @@ AppBackend.prototype.addProcedureServices = function addProcedureServices(forUnl
|
|
|
1528
1555
|
});
|
|
1529
1556
|
}else{
|
|
1530
1557
|
if(progressInfo.lengthComputable){
|
|
1531
|
-
if
|
|
1532
|
-
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();
|
|
1533
1563
|
res.write(JSON.stringify({progress:progressInfo})+"\n");
|
|
1534
1564
|
if(progressInfo.total){
|
|
1535
1565
|
var rate100=Math.floor(progressInfo.loaded*100/progressInfo.total);
|
|
1536
1566
|
var rate1000=Math.floor(progressInfo.loaded*1000/progressInfo.total);
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
}else{
|
|
1540
|
-
progress2send={message:rate100+'%', ephemeral:true};
|
|
1541
|
-
}
|
|
1567
|
+
var message = rate100<1 && progressInfo.loaded>0 ? '('+(rate1000||'½')+'‰)' : rate100+'%';
|
|
1568
|
+
progress2send={message, ephemeral:true, idGroup:progressInfo.idGroup};
|
|
1542
1569
|
}
|
|
1543
1570
|
}else{
|
|
1544
1571
|
progress2send=null;
|
|
@@ -1741,14 +1768,26 @@ function require_resolve(moduleName){
|
|
|
1741
1768
|
return resolved;
|
|
1742
1769
|
}
|
|
1743
1770
|
|
|
1744
|
-
function resolve_module_dir(moduleName,path){
|
|
1771
|
+
function resolve_module_dir(moduleName,path,fileToCheck){
|
|
1745
1772
|
var baseDir;
|
|
1746
1773
|
if(packagejson.name==moduleName){
|
|
1747
1774
|
baseDir=Path.join(process.cwd(), packagejson.main);
|
|
1748
1775
|
}else{
|
|
1749
1776
|
baseDir=require_resolve(moduleName);
|
|
1750
1777
|
}
|
|
1751
|
-
|
|
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;
|
|
1752
1791
|
}
|
|
1753
1792
|
|
|
1754
1793
|
AppBackend.prototype.optsGenericForFiles = function optsGenericForFiles(req, opts){
|
|
@@ -1911,6 +1950,9 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
1911
1950
|
// http://localhost:3033/img/login-logo-icon.png
|
|
1912
1951
|
mainApp.get(Path.posix.join(baseUrl,'/img/login-logo-icon.png'), async function(req,res,next){
|
|
1913
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',
|
|
1914
1956
|
'unlogged/img/login-logo-icon.png',
|
|
1915
1957
|
'dist/unlogged/img/login-logo-icon.png',
|
|
1916
1958
|
'dist/client/unlogged/img/login-logo-icon.png',
|
|
@@ -1938,7 +1980,7 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
1938
1980
|
if(baseUrl=='/'){
|
|
1939
1981
|
baseUrl='';
|
|
1940
1982
|
}
|
|
1941
|
-
let baseLib = baseUrl + '/' + (moduleDef.path ? moduleDef.path : moduleDef.type
|
|
1983
|
+
let baseLib = baseUrl + '/' + (moduleDef.path ? moduleDef.path : be.esJavascript(moduleDef.type)? 'lib': 'css');
|
|
1942
1984
|
try {
|
|
1943
1985
|
var allowedExts=(moduleDef.type=='js'?['js','map']:[moduleDef.type]);
|
|
1944
1986
|
mainApp.use(baseLib, serveContent(resolve_module_dir(moduleDef.module, moduleDef.modPath), { allowedExts }));
|
|
@@ -1952,7 +1994,7 @@ AppBackend.prototype.addUnloggedServices = function addUnloggedServices(mainApp,
|
|
|
1952
1994
|
}
|
|
1953
1995
|
}
|
|
1954
1996
|
} catch (error) {
|
|
1955
|
-
console.error('ERROR: No se pudo servir el módulo
|
|
1997
|
+
console.error('ERROR: No se pudo servir el módulo', moduleDef.module);
|
|
1956
1998
|
throw error;
|
|
1957
1999
|
}
|
|
1958
2000
|
})
|
|
@@ -2073,6 +2115,7 @@ AppBackend.prototype.clientIncludes = function clientIncludes(req, opts) {
|
|
|
2073
2115
|
|
|
2074
2116
|
AppBackend.prototype.clientIncludesCompleted = function clientIncludesCompleted(req, opts) {
|
|
2075
2117
|
let list = this.clientIncludes(req, opts);
|
|
2118
|
+
var be = this;
|
|
2076
2119
|
if(this.config.devel && this.config.devel.useFileDevelopment){
|
|
2077
2120
|
list=list.map(includeDef=>{
|
|
2078
2121
|
if(includeDef.fileProduction){
|
|
@@ -2088,14 +2131,15 @@ AppBackend.prototype.clientIncludesCompleted = function clientIncludesCompleted(
|
|
|
2088
2131
|
return list.map(inclusion =>{
|
|
2089
2132
|
var filename = inclusion.file? inclusion.file: (inclusion.module? Path.basename(require_resolve(inclusion.module,rPaths)): '');
|
|
2090
2133
|
return changing({
|
|
2091
|
-
src: inclusion.type
|
|
2134
|
+
src: be.esJavascript(inclusion.type) ? ((inclusion.path ? inclusion.path : 'lib') + '/' + (inclusion.file ? inclusion.file : filename)) : '',
|
|
2092
2135
|
href: inclusion.type == 'css' ? ((inclusion.path ? inclusion.path : 'css') + '/' + (inclusion.file ? inclusion.file : (inclusion.module + '.css'))) : ''
|
|
2093
2136
|
}, inclusion);
|
|
2094
2137
|
});
|
|
2095
2138
|
}
|
|
2096
2139
|
|
|
2097
2140
|
AppBackend.prototype.clientModules = function clientModules(req, opts) {
|
|
2098
|
-
|
|
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 }})};
|
|
2099
2143
|
}
|
|
2100
2144
|
|
|
2101
2145
|
/**
|
|
@@ -2638,6 +2682,30 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2638
2682
|
}
|
|
2639
2683
|
var schema=db.quoteIdent(be.config.db.schema);
|
|
2640
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
|
+
`);
|
|
2641
2709
|
linesCreate.push("drop schema if exists "+schema+' cascade;');
|
|
2642
2710
|
if (be.config.install.dump["drop-his"]) {
|
|
2643
2711
|
linesCreate.push("drop schema if exists his cascade;");
|
|
@@ -2796,6 +2864,9 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2796
2864
|
case 'check':
|
|
2797
2865
|
sql='check ('+cons.expr+')';
|
|
2798
2866
|
break;
|
|
2867
|
+
case 'exclude':
|
|
2868
|
+
sql=`exclude using ${cons.using} (${cons.fields.map(f => typeof f == "string"?`${f} WITH =`:`${f.fieldName} WITH ${f.operator}`).join(', ')})`;
|
|
2869
|
+
break;
|
|
2799
2870
|
default:
|
|
2800
2871
|
throw new Error('constraintType not implemented: '+cons.constraintType);
|
|
2801
2872
|
}
|
|
@@ -2860,8 +2931,14 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2860
2931
|
if (err.code != 'ENOENT') throw err;
|
|
2861
2932
|
try {
|
|
2862
2933
|
var allTableContent = await fs.readFile('install/local-dump.psql','utf-8');
|
|
2863
|
-
var startIndex = allTableContent.
|
|
2864
|
-
|
|
2934
|
+
var startIndex = allTableContent.indexOf('-- Data for Name: ');
|
|
2935
|
+
console.log('startIndex', startIndex);
|
|
2936
|
+
var lastUseful = allTableContent.lastIndexOf('\nSELECT pg_catalog.setval')
|
|
2937
|
+
console.log('lastUseful', lastUseful);
|
|
2938
|
+
if (lastUseful == -1) lastUseful = allTableContent.lastIndexOf('\n\\.\n');
|
|
2939
|
+
console.log('lastUseful', lastUseful);
|
|
2940
|
+
var lastIndex = allTableContent.indexOf('\n--', lastUseful);
|
|
2941
|
+
console.log('lastIndex', lastIndex);
|
|
2865
2942
|
allTableData = allTableContent.slice(startIndex, lastIndex);
|
|
2866
2943
|
} catch(err) {
|
|
2867
2944
|
if (err.code != 'ENOENT') throw err;
|
|
@@ -2945,22 +3022,32 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2945
3022
|
lines[0][0]=lines[0][0].replace('\ufeff','')
|
|
2946
3023
|
}
|
|
2947
3024
|
rows=lines.slice(1);
|
|
3025
|
+
var filteredFieldDef = lines[0].filter(filterField);
|
|
2948
3026
|
if(tablesWithStrictSequence[tableName]){
|
|
2949
3027
|
var dataString="COPY "+db.quoteIdent(tableName)+" ("+
|
|
2950
|
-
|
|
3028
|
+
filteredFieldDef.map(db.quoteIdent).join(', ')+
|
|
2951
3029
|
') FROM stdin;\n'+
|
|
2952
3030
|
rows.map(function(line){
|
|
2953
|
-
return line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value){
|
|
2954
|
-
|
|
3031
|
+
return line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value,i){
|
|
3032
|
+
var def = tableDef.field[filteredFieldDef[i]];
|
|
3033
|
+
return value==='' ? (
|
|
3034
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? '' : '\\N'
|
|
3035
|
+
): value.replace(/\\/g,'\\\\').replace(/\t/g,'\\t').replace(/\n/g,'\\n').replace(/\r/g,'\\r');
|
|
2955
3036
|
}).join('\t')+'\n';
|
|
2956
3037
|
}).join('')+'\\.\n';
|
|
2957
3038
|
}else{
|
|
2958
3039
|
var dataString="insert into "+db.quoteIdent(tableName)+" ("+
|
|
2959
|
-
|
|
3040
|
+
filteredFieldDef.map(db.quoteIdent).join(', ')+
|
|
2960
3041
|
') values\n'+
|
|
2961
3042
|
rows.map(function(line){
|
|
2962
|
-
return "("+line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value){
|
|
2963
|
-
|
|
3043
|
+
return "("+line.filter(function(_,i){ return filterField(lines[0][i]);}).map(function(value,i){
|
|
3044
|
+
var def = tableDef.field[filteredFieldDef[i]];
|
|
3045
|
+
if(def == null) {
|
|
3046
|
+
throw Error("no se encuentra la columna "+filteredFieldDef[i]+" en "+tableName);
|
|
3047
|
+
}
|
|
3048
|
+
return value==='' ? (
|
|
3049
|
+
def.allowEmptyText && ('nullable' in def) && !def.nullable ? "''" : 'null'
|
|
3050
|
+
) : db.quoteNullable(value);
|
|
2964
3051
|
}).join(', ')+")";
|
|
2965
3052
|
}).join(',\n')+';\n';
|
|
2966
3053
|
}
|
|
@@ -2985,27 +3072,33 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2985
3072
|
}
|
|
2986
3073
|
let installFolders = be.config.install.dump.folders ?? ['install']
|
|
2987
3074
|
let texts = await Promise.all(
|
|
2988
|
-
[
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
3075
|
+
[
|
|
3076
|
+
['prepare.sql'],
|
|
3077
|
+
['pre-adapt.sql'].concat(be.config.install.dump.scripts['pre-adapt']),
|
|
3078
|
+
['adapt.sql'],
|
|
3079
|
+
be.config.install.dump.scripts['prepare'] ?? [],
|
|
3080
|
+
be.config.install.dump.scripts['post-adapt'] ?? []
|
|
3081
|
+
]
|
|
3082
|
+
.map(async function(fileNames){
|
|
3083
|
+
if (!fileNames) return '';
|
|
2993
3084
|
var i = 0;
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3085
|
+
return (await Promise.all(fileNames.map(async fileName => {
|
|
3086
|
+
var content;
|
|
3087
|
+
do {
|
|
3088
|
+
var folder = installFolders[i];
|
|
3089
|
+
try{
|
|
3090
|
+
content = await fs.readFile(be.rootPath+'/'+folder+'/'+fileName, {encoding:'UTF8'});
|
|
3091
|
+
} catch (err) {
|
|
3092
|
+
if(err.code!='ENOENT') throw err;
|
|
3093
|
+
}
|
|
3094
|
+
i++;
|
|
3095
|
+
} while (i < installFolders.length && !content);
|
|
3096
|
+
if (!content) {
|
|
3097
|
+
return '-- no '+fileName+'\n';
|
|
3098
|
+
} else {
|
|
3099
|
+
return '-- '+folder+'/'+fileName+'\n'+content;
|
|
3100
|
+
};
|
|
3101
|
+
}))).join('\n')
|
|
3009
3102
|
})
|
|
3010
3103
|
);
|
|
3011
3104
|
|
|
@@ -3035,7 +3128,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3035
3128
|
'\n-- common'+
|
|
3036
3129
|
common+'\n'+
|
|
3037
3130
|
(complete? '\n\n--prepare.sql\n'+ texts[0]+'\n\n' :'' )+
|
|
3038
|
-
(complete? texts
|
|
3131
|
+
(complete? texts[3] + '\n\n' : '' )+
|
|
3039
3132
|
'\n-- functions\n' + functionLines.join('\n')+
|
|
3040
3133
|
'\n-- lines \n' + lines.join('\n')+
|
|
3041
3134
|
(complete? ('\n\n-- pre-ADAPTs\n'+texts[1]+'\n\n') : '' )+
|
|
@@ -3045,7 +3138,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
3045
3138
|
'\n-- FKs\n' + fkLines.join('\n')+
|
|
3046
3139
|
'\n-- index\n' + indexLines.join('\n')+
|
|
3047
3140
|
'\n-- policies\n' + policyLines.join('\n')+
|
|
3048
|
-
(complete? texts
|
|
3141
|
+
(complete? texts[4] + '\n\n' : '' )+
|
|
3049
3142
|
(complete? (be.config.install.dump.enances==='inline'?enancePart:'') :'')
|
|
3050
3143
|
).replace(/\uFEFF/g /*inner BOM replacing*/,'\n\n').replace(
|
|
3051
3144
|
new RegExp(escapeRegExp(db.quoteIdent(be.config.install.dump.db["owner4special-scripts"])),'g'),
|
package/lib/procedures-table.js
CHANGED
|
@@ -201,7 +201,7 @@ ProcedureTables = [
|
|
|
201
201
|
tables:{},
|
|
202
202
|
tableNames:{}
|
|
203
203
|
};
|
|
204
|
-
var expectedMainPrimaryKeyValues=parameters.primaryKeyValues;
|
|
204
|
+
var expectedMainPrimaryKeyValues = parameters.primaryKeyValues;
|
|
205
205
|
/** @type { TableUpdater } */
|
|
206
206
|
// @ts-expect-error parece no asignada pero al menos un nombre hay en el ciclo y por lo tanto va a tener valor
|
|
207
207
|
var updateTarget = {}
|
|
@@ -257,6 +257,7 @@ ProcedureTables = [
|
|
|
257
257
|
});
|
|
258
258
|
var result;
|
|
259
259
|
var mainResult;
|
|
260
|
+
var initialUpdatesLength = updates.length;
|
|
260
261
|
while(updates.length){
|
|
261
262
|
var {fieldNames, values, defTable, main} = notnull(updates.shift());
|
|
262
263
|
var values4Insert = values.slice(0);
|
|
@@ -272,7 +273,7 @@ ProcedureTables = [
|
|
|
272
273
|
values4Insert[i] = value;
|
|
273
274
|
}
|
|
274
275
|
}
|
|
275
|
-
if(defField.generatedAs){
|
|
276
|
+
if(defField.generatedAs || defField.inTable === false){
|
|
276
277
|
var i = fieldNames4Insert.indexOf(defField.name);
|
|
277
278
|
if(i!==-1){
|
|
278
279
|
fieldNames4Insert.splice(i,1);
|
|
@@ -284,10 +285,11 @@ ProcedureTables = [
|
|
|
284
285
|
var primaryKeyValues=[]
|
|
285
286
|
var primaryKeyValuesForUpdate=[]
|
|
286
287
|
var primaryKeyValueObject={};
|
|
288
|
+
var fieldNames4InsertWithoutPkAddings = fieldNames4Insert.slice(0);
|
|
287
289
|
defTable.primaryKey.forEach(function(name,i){
|
|
288
290
|
var value = main?parameters.primaryKeyValues[i]:coalesce(parameters.newRow[name],parameters.oldRow[name],undefined);
|
|
291
|
+
var defField = defTable.field[name];
|
|
289
292
|
if(value===undefined){
|
|
290
|
-
var defField = defTable.field[name];
|
|
291
293
|
value = defTable.prefilledField && name in defTable.prefilledField ? defTable.prefilledField[name] : (
|
|
292
294
|
'defaultValue' in defField?defField.defaultValue:(
|
|
293
295
|
'specialDefaultValue' in defField?new Error("my.specialDefaultValue[defField.specialDefaultValue](name, {row:{}}, {row:previousRow})"):undefined
|
|
@@ -304,13 +306,16 @@ ProcedureTables = [
|
|
|
304
306
|
}else{
|
|
305
307
|
primaryKeyValuesForUpdate[i]=value;
|
|
306
308
|
}
|
|
307
|
-
if(!fieldNames4Insert.includes(name) && value != null){
|
|
309
|
+
if(!fieldNames4Insert.includes(name) && value != null && defField.inTable !== false){
|
|
308
310
|
fieldNames4Insert.push(name);
|
|
309
311
|
values4Insert.push(value);
|
|
310
312
|
}
|
|
311
313
|
});
|
|
312
|
-
if(main){
|
|
313
|
-
expectedMainPrimaryKeyValues=primaryKeyValues;
|
|
314
|
+
if (main) {
|
|
315
|
+
expectedMainPrimaryKeyValues = primaryKeyValues;
|
|
316
|
+
} else if (defTable.sql.setExpectedPkValues) {
|
|
317
|
+
expectedMainPrimaryKeyValues = primaryKeyValues.slice(0);
|
|
318
|
+
while (expectedMainPrimaryKeyValues.length < mainDefTable.primaryKey.length) expectedMainPrimaryKeyValues.push(null);
|
|
314
319
|
}
|
|
315
320
|
var returningClausule='';
|
|
316
321
|
if(opts && opts.forImport){
|
|
@@ -331,7 +336,9 @@ ProcedureTables = [
|
|
|
331
336
|
return result;
|
|
332
337
|
}
|
|
333
338
|
var sql;
|
|
334
|
-
if(defTable &&
|
|
339
|
+
if (defTable?.sql?.isTable === false && initialUpdatesLength > 1 && fieldNames4InsertWithoutPkAddings.length == 0) {
|
|
340
|
+
// New feature. Ignore attempts to save to non tables when pseudo tables save in others
|
|
341
|
+
} else if(defTable && parameters.status=='new'){
|
|
335
342
|
result = await insertFun();
|
|
336
343
|
}else if(defTable && primaryKeyValues.length==defTable.primaryKey.length){
|
|
337
344
|
var values4Update = [];
|
|
@@ -400,6 +407,12 @@ ProcedureTables = [
|
|
|
400
407
|
var rowFrom = parameters.status!='new' && !plainUpdate.values.length?
|
|
401
408
|
likeAr.toPlainObject(mainDefTable.primaryKey,expectedMainPrimaryKeyValues):
|
|
402
409
|
(mainResult||result).row;
|
|
410
|
+
for (var fieldname of mainDefTable.primaryKey) {
|
|
411
|
+
if (rowFrom[fieldname] == null) {
|
|
412
|
+
var newValue = parameters.newRow[fieldname] ?? parameters.oldRow[fieldname];
|
|
413
|
+
if (newValue != null) rowFrom[fieldname] = newValue
|
|
414
|
+
}
|
|
415
|
+
}
|
|
403
416
|
result = await be.queryValuesOfUniqueRow(context, mainDefTable, rowFrom);
|
|
404
417
|
}
|
|
405
418
|
return {command:insertOrUpdate, row:result.row};
|
|
@@ -417,16 +430,26 @@ ProcedureTables = [
|
|
|
417
430
|
if(!defTable.allow.delete){
|
|
418
431
|
throw changing(new Error("Deletes not allowed"),{status:403});
|
|
419
432
|
}
|
|
420
|
-
|
|
421
|
-
|
|
433
|
+
if (defTable.primaryKey4Delete) {
|
|
434
|
+
console.log("DEPRECATED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! defTable.primaryKey4Delete", defTable.name);
|
|
435
|
+
}
|
|
436
|
+
var primaryKeyFields = defTable.primaryKey4Delete ?? defTable.sql.primaryKey4Delete ?? defTable.primaryKey;
|
|
437
|
+
var primaryKeyValues = [];
|
|
438
|
+
var tableName4Delete = defTable.sql.tableName4Delete ?? defTable.sql.tableName;
|
|
439
|
+
for (var fieldName of primaryKeyFields) {
|
|
440
|
+
var pos = defTable.primaryKey.indexOf(fieldName);
|
|
441
|
+
if (pos > -1) {
|
|
442
|
+
primaryKeyValues.push(parameters.primaryKeyValues[pos]);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
422
445
|
if(defTable && primaryKeyValues.length==primaryKeyFields.length){
|
|
423
446
|
var sqlDelete;
|
|
424
447
|
if(defTable.sql.logicalDeletes){
|
|
425
|
-
sqlDelete="UPDATE "+be.db.quoteIdent(
|
|
448
|
+
sqlDelete="UPDATE "+be.db.quoteIdent(tableName4Delete)+
|
|
426
449
|
" SET "+be.db.quoteIdent(defTable.sql.logicalDeletes.fieldName)+
|
|
427
450
|
" = "+be.db.quoteNullable(defTable.sql.logicalDeletes.valueToDelete);
|
|
428
451
|
}else{
|
|
429
|
-
sqlDelete="DELETE FROM "+be.db.quoteIdent(
|
|
452
|
+
sqlDelete="DELETE FROM "+be.db.quoteIdent(tableName4Delete);
|
|
430
453
|
}
|
|
431
454
|
sqlDelete+=" WHERE "+primaryKeyFields.map(function(fieldName, i){
|
|
432
455
|
return be.db.quoteIdent(fieldName)+" = $"+(i+1);
|
|
@@ -453,6 +476,7 @@ ProcedureTables = [
|
|
|
453
476
|
context.informProgress({message:be.messages.server.deleting})
|
|
454
477
|
var be=context.be;
|
|
455
478
|
var defTable=be.tableStructures[parameters.table](context);
|
|
479
|
+
var tableName4Delete = defTable.sql.tableName4Delete ?? defTable.sql.tableName;
|
|
456
480
|
if(!defTable.allow.delete && !defTable.allow.deleteAll){
|
|
457
481
|
throw changing(new Error("Deletes not allowed"),{status:403});
|
|
458
482
|
}
|
|
@@ -480,7 +504,7 @@ ProcedureTables = [
|
|
|
480
504
|
}
|
|
481
505
|
});
|
|
482
506
|
return context.client.query(
|
|
483
|
-
"DELETE FROM "+be.db.quoteIdent(
|
|
507
|
+
"DELETE FROM "+be.db.quoteIdent(tableName4Delete)+
|
|
484
508
|
" WHERE "+whereParts.join(" AND ")+" RETURNING 1",
|
|
485
509
|
dataParams
|
|
486
510
|
).fetchUniqueRow().then(function(){
|
|
@@ -490,7 +514,7 @@ ProcedureTables = [
|
|
|
490
514
|
};
|
|
491
515
|
var result = await Promise.all(parameters.rowsToDelete.map(deleteOneRow)).then(function(){
|
|
492
516
|
return context.client.query(
|
|
493
|
-
"SELECT count(*) remaining_record_count FROM "+be.db.quoteIdent(
|
|
517
|
+
"SELECT count(*) remaining_record_count FROM "+be.db.quoteIdent(tableName4Delete)
|
|
494
518
|
).fetchUniqueRow().then(function(result){
|
|
495
519
|
context.informProgress({lengthComputable:true, loaded:deleteCounter, total:parameters.rowsToDelete.length, force:true});
|
|
496
520
|
return result.row;
|
|
@@ -693,7 +717,7 @@ ProcedureTables = [
|
|
|
693
717
|
{name: 'replaceNewLineWithSpace'},
|
|
694
718
|
],
|
|
695
719
|
files:{count:1},
|
|
696
|
-
coreFunction:async function(context, parameters, files){
|
|
720
|
+
coreFunction:async function table_upload(context, parameters, files){
|
|
697
721
|
var be=context.be;
|
|
698
722
|
var doing="opening file";
|
|
699
723
|
var skipUnknownFieldsAtImport = coalesce(parameters.skipUnknownFieldsAtImport, be.config.skipUnknownFieldsAtImport);
|
|
@@ -823,6 +847,9 @@ ProcedureTables = [
|
|
|
823
847
|
if(value != null && parameters.skipUnknownFieldsAtImport){
|
|
824
848
|
value = value.replace(/(\s|\u00A0)+/g,' ').trim()
|
|
825
849
|
}
|
|
850
|
+
if (value == null && field.allowEmptyText && ('nullable' in field) && !field.nullable ) {
|
|
851
|
+
value = ''
|
|
852
|
+
}
|
|
826
853
|
newRow[field.name]=value;
|
|
827
854
|
}
|
|
828
855
|
});
|
|
@@ -902,6 +929,9 @@ ProcedureTables = [
|
|
|
902
929
|
if(Number.isNaN(value)){
|
|
903
930
|
value=null;
|
|
904
931
|
}
|
|
932
|
+
if (value == null && field.allowEmptyText && ('nullable' in field) && !field.nullable ) {
|
|
933
|
+
value = ''
|
|
934
|
+
}
|
|
905
935
|
if(field.defaultForOtherFields){
|
|
906
936
|
addFieldToOthers(othersArray, field, value);
|
|
907
937
|
}else{
|