backend-plus 2.5.1-betha.0 → 2.5.2-betha.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/install/get_app_user-fun.sql +2 -2
- package/lib/backend-plus.d.ts +8 -6
- package/lib/backend-plus.js +92 -22
- package/lib/table-def-adapt.js +3 -0
- package/package.json +14 -12
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
create or replace function get_app_user() returns text
|
|
1
|
+
create or replace function get_app_user(p_var text default 'user') returns text
|
|
2
2
|
stable language sql
|
|
3
3
|
as
|
|
4
4
|
$sql$
|
|
5
|
-
select
|
|
5
|
+
select current_setting('backend_plus._' || p_var);
|
|
6
6
|
$sql$;
|
package/lib/backend-plus.d.ts
CHANGED
|
@@ -172,6 +172,10 @@ export type SequenceDefinition = {
|
|
|
172
172
|
name:string
|
|
173
173
|
firstValue:number
|
|
174
174
|
prefix?:string /* Prefix for the generated value */
|
|
175
|
+
}
|
|
176
|
+
export type SequenceMadMaxDefinition = {
|
|
177
|
+
madMax: string[] // grouping of mad max sequences
|
|
178
|
+
firstValue?: number
|
|
175
179
|
}
|
|
176
180
|
export type ExportMetadataDefinition={ /* TODO: define */ }
|
|
177
181
|
export type PostInputOptions='upperSpanish' | 'upperWithoutDiacritics' | 'parseDecimal'
|
|
@@ -185,7 +189,7 @@ export type FieldDefinition = EditableDbDefinition & {
|
|
|
185
189
|
defaultValue?:any
|
|
186
190
|
defaultDbValue?:PgKnownDbValues|string
|
|
187
191
|
clientSide?:string /* keyof: myOwn.clientSides */
|
|
188
|
-
isName?:boolean
|
|
192
|
+
isName?:boolean|'known' /* is a name but it is a well known name (because the user uses it to thier code or because the code is enugh expresive)
|
|
189
193
|
isPk?:number /* internal: pos in the primaryKey array */
|
|
190
194
|
serverSide?:boolean /* default:!clientSide if the value is retrived from the database */
|
|
191
195
|
inTable?:boolean /* default:!clientSide && !sql.fields[...].expr. Is a real fisical field in the table */
|
|
@@ -218,11 +222,8 @@ export type FieldDefinition = EditableDbDefinition & {
|
|
|
218
222
|
alwaysShow?:boolean /* show when appears in fixed fields */
|
|
219
223
|
suggestingKeys?:string[]
|
|
220
224
|
postInput?:PostInputOptions
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
nullable:true
|
|
224
|
-
editable:false
|
|
225
|
-
})
|
|
225
|
+
sequence?: SequenceDefinition|SequenceMadMaxDefinition
|
|
226
|
+
}
|
|
226
227
|
export type EditableDbDefinition = {
|
|
227
228
|
editable?:boolean
|
|
228
229
|
allow?:{
|
|
@@ -415,6 +416,7 @@ export interface AppConfigLogin
|
|
|
415
416
|
{
|
|
416
417
|
schema: string // schema of the user table
|
|
417
418
|
table: string // user table
|
|
419
|
+
from: string // complete expression to get table or join where get the user
|
|
418
420
|
userFieldname: string // fieldname in user table that stores the user name
|
|
419
421
|
passFieldname: string // fieldname in user table that stores the password hash
|
|
420
422
|
rolFieldname: string // fieldname in user table that stores the rol
|
package/lib/backend-plus.js
CHANGED
|
@@ -20,6 +20,7 @@ MiniTools.globalOpts.serveErr.propertiesWhiteList=['message','detail','code','ta
|
|
|
20
20
|
var crypto = require('crypto');
|
|
21
21
|
var serveContent = require('serve-content');
|
|
22
22
|
var pg = require('pg-promise-strict');
|
|
23
|
+
var pgTriggers = require('pg-triggers');
|
|
23
24
|
var SessionFileStore = require('session-file-store');
|
|
24
25
|
var memorystore = require('memorystore');
|
|
25
26
|
var jsToHtml=require('js-to-html');
|
|
@@ -780,16 +781,17 @@ AppBackend.prototype.start = function start(opts){
|
|
|
780
781
|
}
|
|
781
782
|
be.config.db.search_path = be.config.db.search_path ?? [be.config.db.schema, 'public'];
|
|
782
783
|
be.getDbClient = function getDbClient(req){
|
|
783
|
-
var
|
|
784
|
+
var username = req?.user?.[be.config.login.userFieldName];
|
|
785
|
+
var paramsDb = be.DoubleDragon?.dbParams?.[username] ?? be.config.db;
|
|
784
786
|
return pg.connect(paramsDb).then(function(client){
|
|
785
787
|
var dbAppName=req?(
|
|
786
788
|
((req.user||{})[be.config.login.userFieldName]||'!logged')+
|
|
787
789
|
' '+(req.machineId||'?')+
|
|
788
790
|
' '+(((req.useragent)||{}).shortDescription||'?')
|
|
789
791
|
):'!app local internal';
|
|
790
|
-
return client.
|
|
791
|
-
"SET application_name = "+be.db.quoteLiteral(dbAppName)
|
|
792
|
-
).
|
|
792
|
+
return client.executeSentences([
|
|
793
|
+
"SET application_name = "+be.db.quoteLiteral(dbAppName),
|
|
794
|
+
]).then(function(){
|
|
793
795
|
var search_path = be.config.db.search_path;
|
|
794
796
|
if(search_path.length>0){
|
|
795
797
|
return client.query("set SEARCH_PATH TO "+be.db.quoteIdentList(search_path)).execute().then(function(){
|
|
@@ -798,6 +800,14 @@ AppBackend.prototype.start = function start(opts){
|
|
|
798
800
|
}else{
|
|
799
801
|
return client;
|
|
800
802
|
}
|
|
803
|
+
}).then(function(client){
|
|
804
|
+
if(username){
|
|
805
|
+
return client.query(`SELECT set_app_user(${be.db.quoteLiteral(username)})`).execute().then(function(){
|
|
806
|
+
return client;
|
|
807
|
+
});
|
|
808
|
+
}else{
|
|
809
|
+
return client;
|
|
810
|
+
}
|
|
801
811
|
}).then(function(client){
|
|
802
812
|
if(be.config["client-setup"].lang=='es'){
|
|
803
813
|
return client.query("set datestyle TO iso,dmy").execute().then(function(){
|
|
@@ -1012,13 +1022,17 @@ AppBackend.prototype.start = function start(opts){
|
|
|
1012
1022
|
}
|
|
1013
1023
|
be.getDbClient(req).then(function(cli){
|
|
1014
1024
|
client = cli;
|
|
1025
|
+
return client.query("select set_app_user('!login')").execute();
|
|
1026
|
+
}).then(function(){
|
|
1015
1027
|
var infoFieldList=be.config.login.infoFieldList||(be.config.login.rolFieldName?[be.config.login.userFieldName,be.config.login.rolFieldName]:[be.config.login.userFieldName]);
|
|
1016
1028
|
return client.query(
|
|
1017
1029
|
"SELECT "+infoFieldList.map(function(fieldOrPair){ return fieldOrPair.split(' as ').map(function(ident){ return be.db.quoteIdent(ident)}).join(' as '); })+
|
|
1018
1030
|
", "+be.config.login.activeClausule+" as active "+
|
|
1019
1031
|
", "+be.config.login.lockedClausule+" as locked "+
|
|
1020
|
-
" FROM "+(be.config.login.
|
|
1021
|
-
|
|
1032
|
+
" FROM "+(be.config.login.from ?? (
|
|
1033
|
+
(be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':'')+
|
|
1034
|
+
be.db.quoteIdent(be.config.login.table)
|
|
1035
|
+
))+
|
|
1022
1036
|
" WHERE "+be.db.quoteIdent(be.config.login.userFieldName)+" = $1 "+
|
|
1023
1037
|
" AND "+be.db.quoteIdent(be.config.login.passFieldName)+" = $2 ",
|
|
1024
1038
|
[username, md5(password+username)]
|
|
@@ -2409,10 +2423,16 @@ AppBackend.prototype.addLoggedServices = function addLoggedServices(){
|
|
|
2409
2423
|
throw err;
|
|
2410
2424
|
}
|
|
2411
2425
|
});
|
|
2412
|
-
be.app.get('/--version',function(req,res,next){
|
|
2426
|
+
be.app.get('/--version',async function(req,res,next){
|
|
2427
|
+
var simpleGit = require('simple-git');
|
|
2428
|
+
var git = simpleGit(process.cwd());
|
|
2413
2429
|
var info=[
|
|
2414
2430
|
html.h1(be.messages.server.versions),
|
|
2415
2431
|
html.h3([packagejson.name,' ',packagejson.version]),
|
|
2432
|
+
html.ul([
|
|
2433
|
+
html.li([(await git.revparse(['HEAD'])).trim()]),
|
|
2434
|
+
...((git.tags().all || []).map(tag => html.li([tag]))),
|
|
2435
|
+
])
|
|
2416
2436
|
];
|
|
2417
2437
|
Promise.resolve().then(function(){
|
|
2418
2438
|
if(be.isAdmin(req)){
|
|
@@ -2640,7 +2660,16 @@ AppBackend.prototype.dumpFkConstraint = function dumpFkConstraint(fk, tableDef,
|
|
|
2640
2660
|
return {consName, clause, sourceFieldList};
|
|
2641
2661
|
}
|
|
2642
2662
|
|
|
2663
|
+
AppBackend.prototype.isGeneratedSequence = function isGeneratedSequence(sequence, not){
|
|
2664
|
+
return sequence && ((!sequence.name && !sequence.madMax) == !not)
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
AppBackend.prototype.isSequenceNonGenerated = function isSequenceNonGenerated(sequence){
|
|
2668
|
+
return this.isGeneratedSequence(sequence, true);
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2643
2671
|
AppBackend.prototype.dumpDbTableFields = function dumpDbTableFields(tableDef, opts = {}, complements = null){
|
|
2672
|
+
var be = this;
|
|
2644
2673
|
var db = this.db;
|
|
2645
2674
|
var fields=[];
|
|
2646
2675
|
tableDef.fields.forEach(function(fieldDef){
|
|
@@ -2654,7 +2683,7 @@ AppBackend.prototype.dumpDbTableFields = function dumpDbTableFields(tableDef, op
|
|
|
2654
2683
|
' '+(fieldDef.dataLength?(fieldType=='text'?'varchar':fieldType)+'('+fieldDef.dataLength+')':fieldType)+
|
|
2655
2684
|
(fieldDef.defaultValue!=null?' default '+db.quoteLiteral(fieldDef.defaultValue):'')+
|
|
2656
2685
|
(fieldDef.defaultDbValue!=null?' default '+fieldDef.defaultDbValue:'')+
|
|
2657
|
-
(
|
|
2686
|
+
(be.isGeneratedSequence(fieldDef.sequence)?' generated always as identity':'')+
|
|
2658
2687
|
(fieldDef.generatedAs!=null?` generated always as (${fieldDef.generatedAs}) stored`:'')
|
|
2659
2688
|
);
|
|
2660
2689
|
if(complements){
|
|
@@ -2761,7 +2790,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2761
2790
|
lines.push('create table '+cualQuoteTableName+' (');
|
|
2762
2791
|
var fields = be.dumpDbTableFields(tableDef, opts,
|
|
2763
2792
|
function complements(fieldDef){
|
|
2764
|
-
if(
|
|
2793
|
+
if(be.isGeneratedSequence(fieldDef.sequence)){
|
|
2765
2794
|
tablesWithStrictSequence[tableName]={}
|
|
2766
2795
|
}
|
|
2767
2796
|
if(fieldDef.typeName==='text' && !fieldDef.allowEmptyText){
|
|
@@ -2785,7 +2814,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2785
2814
|
' alter column '+db.quoteIdent(fieldDef.name)+' set not null;'
|
|
2786
2815
|
);
|
|
2787
2816
|
}
|
|
2788
|
-
if(
|
|
2817
|
+
if(be.isSequenceNonGenerated(fieldDef.sequence)){
|
|
2789
2818
|
fieldsForSequences.push(fieldDef);
|
|
2790
2819
|
}
|
|
2791
2820
|
}
|
|
@@ -2888,19 +2917,26 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2888
2917
|
});
|
|
2889
2918
|
lines.push(tableDef.sql.postCreateSqls);
|
|
2890
2919
|
lines.push('');
|
|
2891
|
-
fieldsForSequences.
|
|
2920
|
+
await Promise.all(fieldsForSequences.map(async function(fieldDef) {
|
|
2892
2921
|
var sequence = fieldDef.sequence;
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
"
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2922
|
+
if (sequence.name) {
|
|
2923
|
+
if (sequence.madMax) throw new Error('a sequence with madMax cannot have name');
|
|
2924
|
+
lines.push("CREATE SEQUENCE "+db.quoteIdent(sequence.name)+" START "+db.quoteInteger(sequence.firstValue||1)+";");
|
|
2925
|
+
lines.push(
|
|
2926
|
+
"ALTER TABLE "+cualQuoteTableName+
|
|
2927
|
+
" ALTER COLUMN "+db.quoteIdent(fieldDef.name)+
|
|
2928
|
+
(sequence.prefix==null
|
|
2929
|
+
?" SET DEFAULT nextval("+db.quoteLiteral(sequence.name)+"::regclass);"
|
|
2930
|
+
:" SET DEFAULT ("+db.quoteLiteral(sequence.prefix)+" || nextval("+db.quoteLiteral(sequence.name)+"::regclass)::text);"
|
|
2931
|
+
)
|
|
2932
|
+
);
|
|
2933
|
+
lines.push('GRANT USAGE, SELECT ON SEQUENCE '+db.quoteIdent(sequence.name)+' TO '+user+';');
|
|
2934
|
+
} else if (sequence.madMax) {
|
|
2935
|
+
lines.push(await pgTriggers.dumpMaxIdTrigger(tableDef.sql.tableName, fieldDef.name, {grouping: sequence.madMax.grouping, firstValue: sequence.firstValue}));
|
|
2936
|
+
} else {
|
|
2937
|
+
throw new Error('a sequence without madMax nor name');
|
|
2938
|
+
}
|
|
2939
|
+
}));
|
|
2904
2940
|
lines.push('');
|
|
2905
2941
|
if(tableDef.sql.policies.enabled){
|
|
2906
2942
|
policyLines.push(`ALTER TABLE ${cualQuoteTableName} ENABLE ROW LEVEL SECURITY;`);
|
|
@@ -2935,6 +2971,40 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
|
|
|
2935
2971
|
throw err;
|
|
2936
2972
|
}
|
|
2937
2973
|
}
|
|
2974
|
+
lines.push(`
|
|
2975
|
+
create or replace function set_app_user(p_user text) returns text
|
|
2976
|
+
security definer volatile language plpgsql
|
|
2977
|
+
as
|
|
2978
|
+
$body$
|
|
2979
|
+
declare
|
|
2980
|
+
${be.config.login.infoFieldList.map(fieldName => `
|
|
2981
|
+
${db.quoteIdent('v_'+fieldName)} text;`
|
|
2982
|
+
).join('')}
|
|
2983
|
+
begin
|
|
2984
|
+
if p_user = '!login' then
|
|
2985
|
+
${be.config.login.infoFieldList.map(fieldName => `
|
|
2986
|
+
set backend_plus._${fieldName} = '!';`).join('')}
|
|
2987
|
+
|
|
2988
|
+
set backend_plus._mode = login;
|
|
2989
|
+
else
|
|
2990
|
+
select ${be.config.login.infoFieldList.map(fieldName => db.quoteIdent(fieldName)).join(', ')}
|
|
2991
|
+
into ${be.config.login.infoFieldList.map(fieldName => db.quoteIdent('v_'+fieldName)).join(', ')}
|
|
2992
|
+
|
|
2993
|
+
from ${(be.config.login.from ?? (
|
|
2994
|
+
(be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':'')+
|
|
2995
|
+
be.db.quoteIdent(be.config.login.table)))}
|
|
2996
|
+
where ${db.quoteIdent(be.config.login.userFieldName)} = p_user;
|
|
2997
|
+
${be.config.login.infoFieldList.map(fieldName => `
|
|
2998
|
+
perform set_config('backend_plus._${fieldName}', v_${fieldName}, false);`).join('')}
|
|
2999
|
+
|
|
3000
|
+
set backend_plus._mode = normal;
|
|
3001
|
+
end if;
|
|
3002
|
+
perform set_config('backend_plus._user', p_user, false);
|
|
3003
|
+
return p_user;
|
|
3004
|
+
end;
|
|
3005
|
+
$body$;
|
|
3006
|
+
|
|
3007
|
+
`)
|
|
2938
3008
|
var enancePart= 'do $SQL_ENANCE$\n begin\n' + enanceLines.join('\n')+'\n' + 'end\n$SQL_ENANCE$;';
|
|
2939
3009
|
var someNotFound=false;
|
|
2940
3010
|
try {
|
package/lib/table-def-adapt.js
CHANGED
|
@@ -137,6 +137,9 @@ function tableDefAdapt(tableDef, context){
|
|
|
137
137
|
resultTableDef.field[fieldDef.name]=fieldDef;
|
|
138
138
|
if(fieldDef.isName){
|
|
139
139
|
resultTableDef.nameFields.push(fieldDef.name);
|
|
140
|
+
if (fieldDef.isName == 'known' && resultTableDef.hiddenColumns && !resultTableDef.hiddenColumns.includes(fieldDef.name)) {
|
|
141
|
+
resultTableDef.hiddenColumns.push(fieldDef.name);
|
|
142
|
+
}
|
|
140
143
|
}
|
|
141
144
|
return fieldDef;
|
|
142
145
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-plus",
|
|
3
3
|
"description": "Backend for the anti Pareto rule",
|
|
4
|
-
"version": "2.5.
|
|
4
|
+
"version": "2.5.2-betha.12",
|
|
5
5
|
"author": "Codenautas <codenautas@googlegroups.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "codenautas/backend-plus",
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"best-globals": "^2.0.1",
|
|
36
36
|
"big.js": "^7.0.1",
|
|
37
37
|
"body-parser": "^2.2.0",
|
|
38
|
-
"cast-error": "^0.1.
|
|
38
|
+
"cast-error": "^0.1.2",
|
|
39
39
|
"castellano": "^0.1.4",
|
|
40
40
|
"connect-pg-simple": "^10.0.0",
|
|
41
41
|
"cookie-parser": "^1.4.7",
|
|
42
42
|
"cors": "^2.8.5",
|
|
43
|
-
"dialog-promise": "^0.10.
|
|
44
|
-
"discrepances": "^0.2.
|
|
43
|
+
"dialog-promise": "^0.10.2",
|
|
44
|
+
"discrepances": "^0.2.9",
|
|
45
45
|
"express": "^5.1.0",
|
|
46
46
|
"express-session": "^1.18.1",
|
|
47
47
|
"express-useragent": "^1.0.15",
|
|
@@ -51,14 +51,15 @@
|
|
|
51
51
|
"json4all": "^1.4.0",
|
|
52
52
|
"lazy-some": "^0.1.0",
|
|
53
53
|
"like-ar": "^0.5.1",
|
|
54
|
-
"login-plus": "^1.7.
|
|
54
|
+
"login-plus": "^1.7.3",
|
|
55
55
|
"memorystore": "^1.6.7",
|
|
56
|
-
"mini-tools": "^1.13.
|
|
56
|
+
"mini-tools": "^1.13.3",
|
|
57
57
|
"moment": "^2.30.1",
|
|
58
58
|
"multiparty": "^4.2.3",
|
|
59
59
|
"nodemailer": "^7.0.3",
|
|
60
60
|
"numeral": "^2.0.6",
|
|
61
|
-
"pg-promise-strict": "^1.4.
|
|
61
|
+
"pg-promise-strict": "^1.4.3",
|
|
62
|
+
"pg-triggers": "0.4.3",
|
|
62
63
|
"pikaday": "^1.8.2",
|
|
63
64
|
"pug": "^3.0.3",
|
|
64
65
|
"read-yaml-promise": "^1.0.2",
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
"self-explain": "^0.11.0",
|
|
68
69
|
"serve-content": "^0.4.0",
|
|
69
70
|
"session-file-store": "^1.5.0",
|
|
71
|
+
"simple-git": "^3.18.0",
|
|
70
72
|
"sql-tools": "^0.1.2",
|
|
71
73
|
"stack-trace": "^0.0.10",
|
|
72
74
|
"stylus": "0.64.0",
|
|
@@ -77,13 +79,13 @@
|
|
|
77
79
|
"devDependencies": {
|
|
78
80
|
"@types/big.js": "^6.2.2",
|
|
79
81
|
"@types/expect.js": "~0.3.32",
|
|
80
|
-
"@types/express": "^5.0.
|
|
82
|
+
"@types/express": "^5.0.3",
|
|
81
83
|
"@types/express-useragent": "^1.0.5",
|
|
82
84
|
"@types/fs-extra": "^11.0.4",
|
|
83
85
|
"@types/js-yaml": "^4.0.9",
|
|
84
86
|
"@types/mocha": "^10.0.10",
|
|
85
87
|
"@types/multiparty": "~4.2.1",
|
|
86
|
-
"@types/node": "^
|
|
88
|
+
"@types/node": "^24.0.3",
|
|
87
89
|
"@types/nodemailer": "^6.4.17",
|
|
88
90
|
"@types/numeral": "~2.0.5",
|
|
89
91
|
"@types/session-file-store": "^1.2.5",
|
|
@@ -98,10 +100,10 @@
|
|
|
98
100
|
"karma-ie-launcher": "^1.0.0",
|
|
99
101
|
"karma-mocha": "^2.0.1",
|
|
100
102
|
"kill-9": "~0.4.3",
|
|
101
|
-
"mocha": "^11.
|
|
103
|
+
"mocha": "^11.7.0",
|
|
102
104
|
"nyc": "^17.1.0",
|
|
103
|
-
"puppeteer": "^24.
|
|
104
|
-
"sinon": "^
|
|
105
|
+
"puppeteer": "^24.10.2",
|
|
106
|
+
"sinon": "^21.0.0",
|
|
105
107
|
"supertest": "^7.1.1",
|
|
106
108
|
"types.d.ts": "~0.6.22",
|
|
107
109
|
"typescript": "^5.8.3",
|