Compare commits
6 Commits
main
...
devel/fmic
Author | SHA1 | Date | |
---|---|---|---|
ac13aee9b4 | |||
9acd329c3d | |||
423c3063a8 | |||
337c7e5e3c | |||
df0c86d420 | |||
c7bf6d3775 |
15
.eslintrc.js
15
.eslintrc.js
@ -1,15 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
"extends": "eslint:recommended",
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
|
||||||
"arrow-body-style": "off"
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es6": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": "latest"
|
|
||||||
}
|
|
||||||
};
|
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -2,4 +2,10 @@
|
|||||||
/tmp
|
/tmp
|
||||||
/tribes
|
/tribes
|
||||||
/nationchains/blocks
|
/nationchains/blocks
|
||||||
/yarn*
|
/yarn*
|
||||||
|
|
||||||
|
# Jetbrains
|
||||||
|
*.ipr
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
119
apxtrib.js
119
apxtrib.js
@ -1,119 +0,0 @@
|
|||||||
const fs = require( 'fs-extra' );
|
|
||||||
const bodyParser = require( 'body-parser' );
|
|
||||||
const cors = require( 'cors' );
|
|
||||||
const express = require( 'express' );
|
|
||||||
|
|
||||||
/*******************************************
|
|
||||||
|
|
||||||
SEE http://gitlab.ndda.fr/philc/apixtribe/-/wikis/HOWTOoverview
|
|
||||||
To have a quick understanding before doing deeply in source code
|
|
||||||
|
|
||||||
*********************************************/
|
|
||||||
// check setup
|
|
||||||
if( !fs.existsSync( '/etc/nginx/nginx.conf' ) ) {
|
|
||||||
console.log( '\x1b[31m Check documentation, nginx have to be installed on this server first, no /etc/nginx/nginx.conf available, install then rerun yarn command.' );
|
|
||||||
process.exit();
|
|
||||||
}
|
|
||||||
if( !fs.existsSync( './tribes/townconf.js' ) ) {
|
|
||||||
console.log( `\x1b[42m####################################\nWellcome into apixtribe, you need to init your town by "yarn setup" the first time . \nCheck README's project to learn more. more.\n #####################################\x1b[0m` );
|
|
||||||
process.exit();
|
|
||||||
}
|
|
||||||
// config.js exist in any case from Setup.checkinit();
|
|
||||||
const config = require( './tribes/townconf.js' );
|
|
||||||
// Tribes allow to get local apixtribe instance context
|
|
||||||
// dataclient .tribeids [] .DOMs [] .routes (plugins {url:name route:path}) .appname {tribeid:[website]}
|
|
||||||
const dataclient = require( './models/Tribes' )
|
|
||||||
.init();
|
|
||||||
console.log( 'allowed DOMs to access to this apixtribe server: ', dataclient.DOMs )
|
|
||||||
const app = express();
|
|
||||||
app.set( 'trust proxy', true );
|
|
||||||
// To set depending of data form or get size to send
|
|
||||||
app.use( bodyParser.urlencoded( config.bodyparse.urlencoded ) );
|
|
||||||
// To set depending of post put json data size to send
|
|
||||||
app.use( express.json() )
|
|
||||||
app.use( bodyParser.json( config.bodyparse.json ) );
|
|
||||||
app.locals.tribeids = dataclient.tribeids;
|
|
||||||
console.log( 'app.locals.tribeids', app.locals.tribeids );
|
|
||||||
// User token authentification and user init user search
|
|
||||||
const datauser = require( './models/Pagans' )
|
|
||||||
.init( dataclient.tribeids );
|
|
||||||
app.locals.tokens = datauser.tokens;
|
|
||||||
console.log( 'app.locals.tokens key ', Object.keys( app.locals.tokens ) )
|
|
||||||
// Cors management
|
|
||||||
const corsOptions = {
|
|
||||||
origin: ( origin, callback ) => {
|
|
||||||
if( origin === undefined ) {
|
|
||||||
callback( null, true );
|
|
||||||
} else if( origin.indexOf( 'chrome-extension' ) > -1 ) {
|
|
||||||
callback( null, true );
|
|
||||||
} else {
|
|
||||||
//console.log( 'origin', origin )
|
|
||||||
//marchais avant modif eslint const rematch = ( /^https?\:\/\/(.*)\:.*/g ).exec( origin )
|
|
||||||
const rematch = ( /^https?:\/\/(.*):.*/g )
|
|
||||||
.exec( origin )
|
|
||||||
//console.log( rematch )
|
|
||||||
let tmp = origin.replace( /http.?:\/\//g, '' )
|
|
||||||
.split( '.' )
|
|
||||||
|
|
||||||
if( rematch && rematch.length > 1 ) tmp = rematch[ 1 ].split( '.' );
|
|
||||||
//console.log( 'tmp', tmp )
|
|
||||||
let dom = tmp[ tmp.length - 1 ];
|
|
||||||
if( tmp.length > 1 ) {
|
|
||||||
dom = `${tmp[tmp.length-2]}.${tmp[tmp.length-1]}`
|
|
||||||
}
|
|
||||||
console.log( `origin: ${origin}, dom:${dom}, CORS allowed? : ${dataclient.DOMs.includes( dom )}` );
|
|
||||||
if( dataclient.DOMs.includes( dom ) ) {
|
|
||||||
callback( null, true )
|
|
||||||
} else {
|
|
||||||
console.log( `Origin is not allowed by CORS` );
|
|
||||||
callback( new Error( 'Not allowed by CORS' ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exposedHeaders: Object.keys( config.exposedHeaders )
|
|
||||||
};
|
|
||||||
// CORS
|
|
||||||
app.use( cors( corsOptions ) );
|
|
||||||
// Static Routes
|
|
||||||
app.use( express.static( `${__dirname}/tribes/${config.mayorId}/www/cdn/public`, {
|
|
||||||
dotfiles: 'allow'
|
|
||||||
} ) );
|
|
||||||
|
|
||||||
//Allow to public access a space dev delivered by apixtribe
|
|
||||||
// this is just a static open route for dev purpose,
|
|
||||||
// for production, we'll use a nginx static set to /www/app/appname
|
|
||||||
/*console.log( `${config.dnsapixtribe}/space/tribeid/website`, dataclient.appname );
|
|
||||||
Object.keys( dataclient.appname )
|
|
||||||
.forEach( cid => {
|
|
||||||
dataclient.appname[ cid ].forEach( website => {
|
|
||||||
app.use( `/space/${cid}/${website}`, express.static( `${config.tribes}/${cid}/spacedev/${website}` ) );
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
*/
|
|
||||||
// Routers add any routes from /routes and /plugins
|
|
||||||
console.log( 'Routes available on this apixtribe instance' );
|
|
||||||
console.log( dataclient.routes );
|
|
||||||
// prefix only use for dev purpose in production a proxy nginx redirect /app/ to node apixtribe
|
|
||||||
|
|
||||||
dataclient.routes.forEach( r => {
|
|
||||||
try {
|
|
||||||
app.use( r.url, require( r.route ) );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( `\x1b[31m!!! WARNING issue with route ${r.route} from ${r.url} check err if route is key then solve err, if not just be aware that this route won't work on your server. If you are not the maintainer and no turn around please contact the email maintainer.\x1b[0m` )
|
|
||||||
console.log( 'raise err-:', err );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
// Listen web server from config profil (dev prod, other)
|
|
||||||
app.listen( config.porthttp, () => {
|
|
||||||
console.log( `check in your browser that api works http://${config.dnsapixtribe}:${config.porthttp}` );
|
|
||||||
} );
|
|
||||||
/*httpServer.setTimeout( config.settimeout );
|
|
||||||
if( config.withssl == "YES" ) {
|
|
||||||
const httpsServer = https.createServer( config.SSLCredentials, app );
|
|
||||||
httpsServer.listen( config.port.https, () => {
|
|
||||||
console.log( `check in your browser that api works https://${config.dnsapixtribe}:${config.port.https}` );
|
|
||||||
} );
|
|
||||||
httpsServer.setTimeout( config.settimeout );
|
|
||||||
};*/
|
|
||||||
|
|
||||||
console.log( "\x1b[42m\x1b[37m", "Made with love for people's freedom, enjoy !!!", "\x1b[0m" );
|
|
6
doc/GettingStarted.md
Normal file
6
doc/GettingStarted.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Getting started with Apxtrib
|
||||||
|
|
||||||
|
Initialize : `yarn setup`
|
||||||
|
|
||||||
|
Development mode : `npm run dev`
|
||||||
|
|
@ -1,95 +0,0 @@
|
|||||||
const jwt = require( 'jwt-simple' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
|
|
||||||
// A REMPLACER PAR hasAccessrighton.js
|
|
||||||
/*
|
|
||||||
qui permet de passer en parametre des tests d'actions autoriser sur une objet
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Check if package is installed or not to pickup the right config file
|
|
||||||
const src = ( __dirname.indexOf( '/node_modules/' ) > -1 ) ? '../../..' : '..';
|
|
||||||
const config = require( path.normalize( `${__dirname}/${src}/config.js` ) );
|
|
||||||
|
|
||||||
const haveAccessrighttoanobject = ( req, res, next ) => {
|
|
||||||
/*
|
|
||||||
from isAuthenticated req.session.header.accessrights={app:{'tribeid:projet':profile},
|
|
||||||
data:{ "sitewebsrc": "RWCDO",
|
|
||||||
"contacts": "RWCDO"}}
|
|
||||||
from the last successfull authentification.
|
|
||||||
profile is a keyword menu available into clientconf.json of tribeid
|
|
||||||
data, list of object accessright Read Write Create Delete Owner
|
|
||||||
a xuuid can read any objet if R
|
|
||||||
if O wner means that it can only read write its object create by himself
|
|
||||||
|
|
||||||
This middleware check that we apply RESTFull CRUD concept depending of access right of a xuuid trying to act onto a xworkon tribeid
|
|
||||||
Action get = Read put = Update post = Create delete = Delete
|
|
||||||
object = req.Urlpath.split(/)[0]
|
|
||||||
*/
|
|
||||||
console.log( 'haveAccessrighttoanobject()?' );
|
|
||||||
// req.originalUrl contain /object/action/id object id to run action
|
|
||||||
// req.route.methods ={ put:true, delete:true post:true, get:true }
|
|
||||||
const objet = req.baseUrl.slice( 1 ); //contain /object
|
|
||||||
const model = objet.charAt( 0 )
|
|
||||||
.toUpperCase() + objet.slice( 1 ); // model u object with first letter in uppercase
|
|
||||||
let droit = "";
|
|
||||||
let ownby = [];
|
|
||||||
/*
|
|
||||||
Check if object exist and get the OWNBY array, not relevant for referentials object that is only manage by CRUD no Owner logic
|
|
||||||
*/
|
|
||||||
if( objet != "referentials" ) {
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${req.session.header.xworkon}/${objet}/${req.params.id}.json` ) ) {
|
|
||||||
res.status( 404 )
|
|
||||||
.send( {
|
|
||||||
payload: {
|
|
||||||
info: [ 'idNotfound' ],
|
|
||||||
model,
|
|
||||||
moreinfo: `${config.tribes}/${req.session.header.xworkon}/${objet}/${req.params.id}.json does not exist `
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} else {
|
|
||||||
ownby = jsonfile.readFileSync( `${config.tribes}/${req.session.header.xworkon}/${objet}/${req.params.id}.json` )
|
|
||||||
.OWNBY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log( req.session.header )
|
|
||||||
if( req.session.header.xpaganid == config.devnoauthxuuid ) {
|
|
||||||
console.log( 'haveAccessrighttoanobject yes cause dev test user' );
|
|
||||||
} else {
|
|
||||||
// accessrights was load from isAuthenticated.js middleware to make it available in req.session.header to be used into route for specific access if needed mainly to filter data in the get request depending of profil and data accessright.
|
|
||||||
if( Object.keys( req.session.header.accessrights.data )
|
|
||||||
.includes( "Alltribeid" ) && req.session.header.accessrights.data[ "Alltribeid" ][ objet ] ) {
|
|
||||||
droit = req.session.header.accessrights.data[ "Alltribeid" ][ objet ];
|
|
||||||
}
|
|
||||||
// erase rights if tribeid is specified in addition of Alltribeid
|
|
||||||
if( ( req.session.header.accessrights.data[ req.session.header.xworkon ] ) &&
|
|
||||||
req.session.header.accessrights.data[ req.session.header.xworkon ][ objet ] ) {
|
|
||||||
droit = req.session.header.accessrights.data[ req.session.header.xworkon ][ objet ];
|
|
||||||
if( ( req.route.methods.get && droit.includes( 'R' ) ) ||
|
|
||||||
( req.route.methods.put && droit.includes( 'U' ) ) ||
|
|
||||||
( req.route.methods.delete && droit.includes( 'D' ) ) ||
|
|
||||||
ownby.includes( req.params.id ) ) {
|
|
||||||
console.log( 'haveAccessrighttoanobject yes' )
|
|
||||||
} else if( req.route.methods.post && droit.includes( 'C' ) ) {
|
|
||||||
console.log( 'haveAccessrighttoanobject yes create' );
|
|
||||||
} else {
|
|
||||||
console.log( 'haveAccessrighttoanobject no' )
|
|
||||||
res.status( 403 )
|
|
||||||
.send( {
|
|
||||||
payload: {
|
|
||||||
info: [ 'NoAccessrights' ],
|
|
||||||
model,
|
|
||||||
moreinfo: `User ${req.session.header.xpaganid} accessrights are not set to do this action`
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
module.exports = haveAccessrighttoanobject;
|
|
@ -1,88 +0,0 @@
|
|||||||
const path = require( 'path' );
|
|
||||||
|
|
||||||
// Check if package is installed or not to pickup the right config file
|
|
||||||
//const src = ( __dirname.indexOf( '/node_modules/' ) > -1 ) ? '../../..' : '..';
|
|
||||||
//const config = require( path.normalize( `${__dirname}/${src}/config.js` ) );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
/*
|
|
||||||
Check que le header contient des éléments necessaire pour les
|
|
||||||
routes utilisant tribeid / language / token / uuid
|
|
||||||
*/
|
|
||||||
const checkHeaders = ( req, res, next ) => {
|
|
||||||
//console.log( 'checkHeaders()' );
|
|
||||||
// These headers must be passed in the request
|
|
||||||
// X-Auth and X-Uuid could have any true value
|
|
||||||
// header is stored in req.app.locals.header to be pass to route
|
|
||||||
/* const header = {
|
|
||||||
xtribeid: req.header('x-client-id'),
|
|
||||||
xlang: req.header('x-language'),
|
|
||||||
xauth: req.header('x-auth'),
|
|
||||||
xuuid: req.header('x-uuid'),
|
|
||||||
xworkon: req.header('x-xorkon',
|
|
||||||
xapp:req.header('x-app'))
|
|
||||||
};
|
|
||||||
On recupere accessrights via is Authenticated
|
|
||||||
*/
|
|
||||||
req.session = {};
|
|
||||||
const header = {};
|
|
||||||
let missingheader = "";
|
|
||||||
//console.log( 'avant validation headers', req.headers );
|
|
||||||
//attention changement 7/11/2021 phil des exposedheader cf config.js
|
|
||||||
//If in httprequest url header are send then they are used inpriority
|
|
||||||
//Use case : send an email with a unique link that works without password and request to change password
|
|
||||||
for( const h of config.exposedHeaders ) {
|
|
||||||
//console.log( h, req.header( h ) )
|
|
||||||
if( req.params[ h ] ) {
|
|
||||||
header[ h ] = req.params[ h ]
|
|
||||||
} else if( req.header( h ) ) {
|
|
||||||
header[ h ] = req.header( h )
|
|
||||||
} else {
|
|
||||||
// Missing header
|
|
||||||
missingheader += " " + h
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//console.log( 'header', header )
|
|
||||||
if( req.params.xauth && req.params.xuuid ) {
|
|
||||||
// If this exist => it is a timeout limited token
|
|
||||||
req.app.locals.tokens[ req.params.xpaganid ] = req.params.xauth;
|
|
||||||
}
|
|
||||||
req.session.header = header;
|
|
||||||
// Each header have to be declared
|
|
||||||
if( missingheader != "" ) {
|
|
||||||
return res.status( 403 )
|
|
||||||
.send( {
|
|
||||||
info: [ 'forbiddenAccess' ],
|
|
||||||
model: 'Pagans',
|
|
||||||
moreinfo: 'checkHeader headerIsMissing:' + missingheader
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
//console.log( req.app.locals.tribeids )
|
|
||||||
if( !req.app.locals.tribeids.includes( header.xtribe ) ) {
|
|
||||||
return res.status( 404 )
|
|
||||||
.send( {
|
|
||||||
info: [ 'tribeiddoesnotexist' ],
|
|
||||||
model: 'Pagans',
|
|
||||||
moreinfo: `xtribe unknown: ${header.xtribe}`
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
if( !req.app.locals.tribeids.includes( header.xworkon ) ) {
|
|
||||||
return res.status( 404 )
|
|
||||||
.send( {
|
|
||||||
info: [ 'tribeiddoesnotexist' ],
|
|
||||||
model: 'Pagans',
|
|
||||||
moreinfo: `xworkon unknown: ${header.xworkon}`
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
if( !config.languagesAvailable.includes( header.xlang ) ) {
|
|
||||||
return res.status( 404 )
|
|
||||||
.send( {
|
|
||||||
info: [ 'langNotused' ],
|
|
||||||
model: 'Pagans',
|
|
||||||
moreinfo: `xlang unknown: ${header.xlang}`
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
//console.log( 'After middleare checkHeaders.js req.session.header', req.session.header )
|
|
||||||
//console.log( 'checkheaders next' )
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
module.exports = checkHeaders;
|
|
@ -1,42 +0,0 @@
|
|||||||
const fs = require( 'fs-extra' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
|
|
||||||
const hasAccessrighton = ( object, action, ownby ) => {
|
|
||||||
/*
|
|
||||||
@action (mandatory) : CRUDO
|
|
||||||
@object (mandatory)= name of a folder object in /tribeid space can be a tree for example objects/items
|
|
||||||
@ownby (option) = list des uuid propriétaire
|
|
||||||
return next() if all action exist in req.app.local.tokens[UUID].ACCESSRIGHTS.data[object]
|
|
||||||
OR if last action ="O" and uuid exist in ownBy
|
|
||||||
Careffull if you have many action CRO let O at the end this will force req.right at true if the owner try an action on this object
|
|
||||||
*/
|
|
||||||
return ( req, res, next ) => {
|
|
||||||
//console.log( 'err.stack hasAccessrights', err.statck )
|
|
||||||
//console.log( `test accessright on object:${object} for ${req.session.header.xworkon}:`, req.app.locals.tokens[ req.session.header.xpaganid ].ACCESSRIGHTS.data[ req.session.header.xworkon ] )
|
|
||||||
req.right = false;
|
|
||||||
if( req.app.locals.tokens[ req.session.header.xpaganid ].ACCESSRIGHTS.data[ req.session.header.xworkon ] && req.app.locals.tokens[ req.session.header.xpaganid ].ACCESSRIGHTS.data[ req.session.header.xworkon ][ object ] ) {
|
|
||||||
req.right = true;
|
|
||||||
[ ...action ].forEach( a => {
|
|
||||||
if( a == "O" && ownby && ownby.includes( req.session.header.xpaganid ) ) {
|
|
||||||
req.right = true;
|
|
||||||
} else {
|
|
||||||
req.right = req.right && req.app.locals.tokens[ req.session.header.xpaganid ].ACCESSRIGHTS.data[ req.session.header.xworkon ][ object ].includes( a )
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
//console.log( 'Access data autorise? ', req.right )
|
|
||||||
if( !req.right ) {
|
|
||||||
return res.status( 403 )
|
|
||||||
.send( {
|
|
||||||
info: [ 'forbiddenAccess' ],
|
|
||||||
model: 'middleware',
|
|
||||||
moreinfo: 'no auth to act on this object'
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = hasAccessrighton;
|
|
@ -1,113 +0,0 @@
|
|||||||
const jwt = require( 'jwt-simple' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
//const path = require( 'path' );
|
|
||||||
// Check if package is installed or not to pickup the right config file
|
|
||||||
//const src = '..'; // ( __dirname.indexOf( '/node_modules/' ) > -1 ) ? '../../..' : '..';
|
|
||||||
//const config = require( path.normalize( `${__dirname}/${src}/config.js` ) );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
const isAuthenticated = ( req, res, next ) => {
|
|
||||||
/*
|
|
||||||
check if authenticated with valid token
|
|
||||||
if not => set req.session.header.xauth=1
|
|
||||||
if yes => set for xWorkon
|
|
||||||
req.session.header.accessrights={
|
|
||||||
app:{'tribeid:website':[liste of menu]},
|
|
||||||
data:{ "sitewebsrc": "RWCDO",
|
|
||||||
"contacts": "RWCDO"}}
|
|
||||||
Liste of menu is linked with the app tht have to be consistent with accessrights.data
|
|
||||||
data, list of object accessright Read Write Create Delete Owner
|
|
||||||
a xuuid can read any objet if R
|
|
||||||
if O wner means that it can only read write its object create by himself
|
|
||||||
*/
|
|
||||||
console.log( 'isAuthenticated()?' );
|
|
||||||
//console.log( 'req.app.locals.tokens', req.app.locals.tokens )
|
|
||||||
//console.log( 'req.session.header', req.session.header );
|
|
||||||
// Check if token exist or not
|
|
||||||
req.session.header.accessrights = { app: "", data: {} }
|
|
||||||
if( req.session.header.xpaganid == config.devnoauthxuuid && req.session.header.xauth == config.devnoauthxauth ) {
|
|
||||||
console.log( 'isAuthenticated yes: carrefull using a bypass password give you accessrights={}' );
|
|
||||||
} else if( req.session.header.xpaganid == "1" || !req.app.locals.tokens[ req.session.header.xpaganid ] ) {
|
|
||||||
console.log( `isAuthenticated no : uuid=1 (value=${req.session.header.xpaganid}) or locals.tokens[uuid] empty ` );
|
|
||||||
console.log( 'req.app.locals.tokens de xpaganid', req.app.locals.tokens[ req.session.header.xpaganid ] );
|
|
||||||
console.log( 'list key uuid de req.app.locals.tokens', Object.keys( req.app.locals.tokens ) )
|
|
||||||
req.session.header.xauth = "1"
|
|
||||||
} else if( req.app.locals.tokens[ req.session.header.xpaganid ].TOKEN !== req.session.header.xauth ) {
|
|
||||||
// console.log(req.session.header.xuuid);
|
|
||||||
// console.log(req.session.header.xauth);
|
|
||||||
// update tokens from file in case recently logged
|
|
||||||
try {
|
|
||||||
console.log( 'token not in list of token (req.app.locals.tokens) try to refresh from file' );
|
|
||||||
req.app.locals.tokens = jsonfile.readFileSync( `${config.tmp}/tokens.json` );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( `check isAuthenticated issue in reading ${config.tmp}/tokens.json` );
|
|
||||||
}
|
|
||||||
if( req.app.locals.tokens[ req.session.header.xpaganid ].TOKEN !== req.session.header.xauth ) {
|
|
||||||
// if still does not exist then out
|
|
||||||
console.log( 'isAuthenticated no, token outdated' );
|
|
||||||
req.session.header.xauth = "1"
|
|
||||||
req.session.header.xpaganid = "1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( req.session.header.xauth == "1" ) {
|
|
||||||
//return res.status( 403 )
|
|
||||||
return res.status( 403 )
|
|
||||||
.send( {
|
|
||||||
info: [ 'forbiddenAccess' ],
|
|
||||||
model: 'Pagans',
|
|
||||||
moreinfo: 'isAuthenticated faill'
|
|
||||||
} )
|
|
||||||
} else {
|
|
||||||
console.log( 'isAuthenticated yes' );
|
|
||||||
if( req.app.locals.tokens[ req.session.header.xpaganid ] ) {
|
|
||||||
//console.log( `accessright pour ${req.session.header.xpaganid}`, req.app.locals.tokens[ req.session.header.xpaganid ].ACCESSRIGHTS );
|
|
||||||
//set header.accessrights from tokens.json
|
|
||||||
req.session.header.accessrights = req.app.locals.tokens[ req.session.header.xpaganid ].ACCESSRIGHTS
|
|
||||||
} else {
|
|
||||||
// case of bypass no accessright available
|
|
||||||
req.session.header.accessrights = {}
|
|
||||||
}
|
|
||||||
// Once per day, clean old token
|
|
||||||
const currentday = moment()
|
|
||||||
.date();
|
|
||||||
console.log( 'test si menagedone' + currentday, !fs.existsSync( `${config.tmp}/menagedone${currentday}` ) )
|
|
||||||
if( !fs.existsSync( `${config.tmp}/menagedone${currentday}` ) ) {
|
|
||||||
glob.sync( `${config.tmp}/menagedone*` )
|
|
||||||
.forEach( f => {
|
|
||||||
fs.remove( f, ( err ) => {
|
|
||||||
if( err ) {
|
|
||||||
console.log( 'err remove menagedone', err )
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
glob.sync( `${config.tmp}/mdcreator*.log` )
|
|
||||||
.forEach( f => {
|
|
||||||
fs.remove( f, ( err ) => {
|
|
||||||
if( err ) {
|
|
||||||
console.log( 'err remove mdcreator log', err )
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
const newtokens = {};
|
|
||||||
for( const k of Object.keys( req.app.locals.tokens ) ) {
|
|
||||||
try {
|
|
||||||
const decodedToken = jwt.decode( req.app.locals.tokens[ k ].TOKEN, config.jwtSecret );
|
|
||||||
//console.log( moment( decodedToken.expiration ), moment() )
|
|
||||||
//console.log( moment( decodedToken.expiration ) >= moment() )
|
|
||||||
if( moment( decodedToken.expiration ) >= moment() ) {
|
|
||||||
newtokens[ k ] = req.app.locals.tokens[ k ];
|
|
||||||
}
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( "Check isAuthenticated cleaning token ", err );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
req.app.locals.tokens = newtokens;
|
|
||||||
jsonfile.writeFileSync( `${config.tmp}/tokens.json`, newtokens );
|
|
||||||
fs.writeFileSync( `${config.tmp}/menagedone${currentday}`, 'fichier semaphore to clean data each day can be deleted with no consequence', 'utf-8' );
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
module.exports = isAuthenticated;
|
|
@ -1,121 +0,0 @@
|
|||||||
const fs = require( 'fs-extra' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
const axios = require( 'axios' );
|
|
||||||
const scrapeit = require( 'scrape-it' );
|
|
||||||
const cheerio = require( 'cheerio' );
|
|
||||||
const Mustache = require( 'mustache' );
|
|
||||||
const qrcode = require( 'qrcode' );
|
|
||||||
|
|
||||||
// Check if package is installed or not to pickup the right config file
|
|
||||||
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Model that will process actions plan for each client like sending email campain, or anything that
|
|
||||||
are plan in /tribes/tribeid/actions/todo
|
|
||||||
*/
|
|
||||||
const Cards = {}; //require('../../models/Cards');
|
|
||||||
const Contracts = {};
|
|
||||||
/*
|
|
||||||
Send if envoicampain a liste of email in param.msg.destperso with param.headers
|
|
||||||
if not envoicampain, it just return a test about what to send
|
|
||||||
@param = {headers, msg:{destperso}}
|
|
||||||
*/
|
|
||||||
Contracts.sendcampain = async ( param, envoicampain ) => {
|
|
||||||
if( envoicampain ) {
|
|
||||||
// Carefull w the action post outputs/msg just wait the feedback of the 1st message
|
|
||||||
const retcampain = await axios.post( 'https://mail.maildigit.fr/outputs/msg', param.msg, {
|
|
||||||
headers: param.headers
|
|
||||||
} );
|
|
||||||
if( retcampain.status !== 200 ) {
|
|
||||||
console.log( "err", retcampain.payload.moreinfo );
|
|
||||||
fs.appendFileSync( `${config.tribes}/log_erreurglobal.txt`, moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + ' - IMPOSSIBLE TO SEND CAMPAIN TODO for :' + param.tribeid + ' -- ' + retcampain.payload.moreinfo + '\n', 'utf-8' );
|
|
||||||
};
|
|
||||||
return retcampain;
|
|
||||||
} else {
|
|
||||||
// permet de tester ce qu'il y a à envoyer
|
|
||||||
let premieremail = "";
|
|
||||||
for( let i = 0; i < param.msg.destperso.length; i++ ) {
|
|
||||||
premieremail += param.msg.destperso[ 0 ].email + ",";
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 201,
|
|
||||||
payload: {
|
|
||||||
info: [ 'simplecomptage' ],
|
|
||||||
model: 'Contracts',
|
|
||||||
moreinfo: "#email: " + param.msg.destperso.length + " - 5 1st emails: " + premieremail
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Contracts.initActiontodo = async ( envoie ) => {
|
|
||||||
const datedeb = moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' );
|
|
||||||
let todo, actiondone;
|
|
||||||
let log = {
|
|
||||||
nbaction: 0,
|
|
||||||
nbactionexec: 0,
|
|
||||||
nbactionerr: 0,
|
|
||||||
actionlist: ""
|
|
||||||
};
|
|
||||||
const listclient = jsonfile.readFileSync( `${config.tribes}/tribeids.json` );
|
|
||||||
for( let clid in listclient ) {
|
|
||||||
console.log( listclient[ clid ] );
|
|
||||||
let listaction = glob.sync( `${config.tribes}/${listclient[clid]}/actions/todo/*.json` );
|
|
||||||
for( let action in listaction ) {
|
|
||||||
console.log( listaction[ action ] )
|
|
||||||
log.nbaction++;
|
|
||||||
todo = jsonfile.readFileSync( listaction[ action ] );
|
|
||||||
let passdate = true;
|
|
||||||
// currentdate doit etre après la startDate si existe et avant valideuntilDate si existe
|
|
||||||
// console.log('test now est avant date start ', moment() < moment(todo.startDate, 'YYYYMMDD HH:mm:ss').toDate());
|
|
||||||
if( todo.startDate && ( moment() < moment( todo.startDate, 'YYYYMMDD HH:mm:ss' )
|
|
||||||
.toDate() ) ) {
|
|
||||||
passdate = false;
|
|
||||||
};
|
|
||||||
// currentdate ne doit pas depasser la date de validité de la tache
|
|
||||||
// console.log('test now est après la date de validite ', moment() > moment(todo.validuntilDate, 'YYYYMMDD HH:mm:ss').toDate());
|
|
||||||
if( todo.valideuntilDate && ( moment() > moment( todo.validuntilDate, 'YYYYMMDD HH:mm:ss' )
|
|
||||||
.toDate() ) ) {
|
|
||||||
passdate = false;
|
|
||||||
};
|
|
||||||
// currentdate
|
|
||||||
if( passdate && todo.action && todo.error == "" ) {
|
|
||||||
log.nbactionexec++;
|
|
||||||
const actiondone = await Contracts[ todo.action ]( todo, envoie );
|
|
||||||
todo.datesRun.push( moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) );
|
|
||||||
//console.log("actiondone", actio jsonfile.writeFileSyncndone);
|
|
||||||
log.actionlist += "STATUS:" + actiondone.status + " -- " + listaction[ action ] + "\n";
|
|
||||||
if( actiondone.status == 200 ) {
|
|
||||||
todo.error = "";
|
|
||||||
} else {
|
|
||||||
log.nbactionerr++;
|
|
||||||
todo.error += "status : " + actiondone.status + ' ' + actiondone.payload.moreinfo;
|
|
||||||
};
|
|
||||||
if( parseInt( todo.maxnumberoftime ) && todo.maxnumberoftime != "999" && ( todo.datesRun.length >= parseInt( todo.maxnumberoftime ) ) ) {
|
|
||||||
//archive en done this triggeraction
|
|
||||||
jsonfile.writeFileSync( listaction[ action ].replace( '/todo/', '/done/' ), todo, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
fs.unlinkSync( listaction[ action ] );
|
|
||||||
} else {
|
|
||||||
jsonfile.writeFileSync( listaction[ action ], todo, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.actionlist += "STATUS : not executed " + listaction[ action ] + "\n";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
const trace = "###################### LOGS ####################\nSTART:" + datedeb + " END:" + moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + "\n nombre d'actions analysées : " + log.nbaction + " dont executées : " + log.nbactionexec + " dont en erreur: " + log.nbactionerr + "\n" + log.actionlist;
|
|
||||||
fs.appendFileSync( `${config.tribes}/log.txt`, trace, 'utf-8' );
|
|
||||||
return "done";
|
|
||||||
}
|
|
||||||
module.exports = Contracts;
|
|
@ -1,400 +0,0 @@
|
|||||||
const bcrypt = require( 'bcrypt' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const Mustache = require( 'mustache' );
|
|
||||||
const jwt = require( 'jwt-simple' );
|
|
||||||
const { DateTime } = require( 'luxon' );
|
|
||||||
const UUID = require( 'uuid' );
|
|
||||||
const Outputs = require( '../models/Outputs.js' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
const checkdata = require( `../nationchains/socialworld/contracts/checkdata.js`);
|
|
||||||
/*
|
|
||||||
Message manager
|
|
||||||
* Manage apixtribe message at different level
|
|
||||||
* this means that an object (most often a user or a contact) want to send a question to an other user.
|
|
||||||
|
|
||||||
|
|
||||||
To know more http://gitlab.ndda.fr/philc/apixtribe/-/wikis/HOWTONotification
|
|
||||||
|
|
||||||
*/
|
|
||||||
const Messages = {};
|
|
||||||
Messages.init = () => {
|
|
||||||
console.group( 'init Message' );
|
|
||||||
Messages.aggregate();
|
|
||||||
}
|
|
||||||
|
|
||||||
Messages.byEmailwithmailjet = ( tribeid, msg ) => {
|
|
||||||
/* @tribeid requester
|
|
||||||
@msg =[{
|
|
||||||
To:[{Email,Name}],
|
|
||||||
Cc:[{Email,Name}],
|
|
||||||
Bcc:[{Email,Name}]}],
|
|
||||||
Subject:"Subject",
|
|
||||||
TextPart:"texte content",
|
|
||||||
HTMLPart:"html content"
|
|
||||||
}]
|
|
||||||
If tribeid has a mailjet account it use it if not then it use the apixtribe @mailjetconf = {apikeypub:, apikeypriv:, From:{Email:,Name:}}
|
|
||||||
This send a bunch of messages check the mailjet account used
|
|
||||||
FYI: Basic account is 200 email /days 6000 /month
|
|
||||||
Log is stored into
|
|
||||||
tribeidsender/messages/logs/sent/timestamp.json
|
|
||||||
@todo GUI to manage statistics and notification alert limit sender email
|
|
||||||
*/
|
|
||||||
console.log( 'Envoie mailjet' )
|
|
||||||
const confclient = fs.readJsonSync( `${config.tribes}/${tribeid}/clientconf.json` );
|
|
||||||
let tribeidsender = tribeid;
|
|
||||||
if( confclient.smtp && confclient.smtp.mailjet ) {
|
|
||||||
mailjetconf = confclient.smtp.mailjet;
|
|
||||||
} else {
|
|
||||||
const confapixtribe = fs.readJsonSync( `${config.tribes}/${config.mayorId}/clientconf.json` );
|
|
||||||
if( !( confapixtribe.smtp && confapixtribe.smtp.mailjet ) ) {
|
|
||||||
return { status: 403, data: { models: "Messages", info: [ "nosmtpmailjet" ], moreinfo: "missing smtpmailjet parameter in apixtribe contact your admin to activate an mailjet account on this server." } }
|
|
||||||
}
|
|
||||||
tribeidsender = "apixtribe";
|
|
||||||
mailjetconf = confapixtribe.smtp.mailjet;
|
|
||||||
}
|
|
||||||
//add from from setings account
|
|
||||||
const MSG = msg.map( m => { m.From = mailjetconf.From; return m; } );
|
|
||||||
const mailjet = require( 'node-mailjet' )
|
|
||||||
.connect( mailjetconf.apikeypub, mailjetconf.apikeypriv );
|
|
||||||
const request = mailjet.post( 'send', { version: 'v3.1' } )
|
|
||||||
.request( { "Messages": MSG } );
|
|
||||||
request
|
|
||||||
.then( result => {
|
|
||||||
//store into tribeidsender/messages/logs/sent/timestamp.json
|
|
||||||
const t = Date.now();
|
|
||||||
MSG.result = result.body;
|
|
||||||
fs.outputJson( `${config.tribes}/${tribeidsender}/messages/logs/sent/${t}.json`, MSG )
|
|
||||||
console.log( result.body )
|
|
||||||
} )
|
|
||||||
.catch( err => {
|
|
||||||
const t = Date.now();
|
|
||||||
MSG.result = err;
|
|
||||||
fs.outputJson( `${config.tribes}/${tribeidsender}/messages/logs/error/${t}.json`, MSG )
|
|
||||||
console.log( err.statusCode, err )
|
|
||||||
} )
|
|
||||||
|
|
||||||
};
|
|
||||||
Messages.buildemail = ( tribeid, tplmessage, data ) => {
|
|
||||||
/* @tribeid => client concerned by sending email get info from clientconf.json (customization, useradmin:{ EMAIL,LOGIN} , ...)
|
|
||||||
@tplmessage => folder where template is available (ex: apixtribe/referentials/dataManagement/template/order)
|
|
||||||
@data { destemail = email to
|
|
||||||
if destuuid => then it get user EMAIL if exiat
|
|
||||||
subject = mail subject
|
|
||||||
// any relevant information for template message
|
|
||||||
msgcontenthtml = html msg content
|
|
||||||
msgcontenttxt = text msg content
|
|
||||||
....}
|
|
||||||
@return msg
|
|
||||||
mail part ready to send by mailjet or other smtp
|
|
||||||
*/
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${tribeid}/clientconf.json` ) ) {
|
|
||||||
return { status: 404, data: { model: "Messages", info: [ "tribeiddoesnotexist" ], moreinfo: `${tribeid} does not exist` } }
|
|
||||||
}
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${tplmessage}` ) ) {
|
|
||||||
return { status: 404, data: { model: "Messages", info: [ "tplmessagedoesnotexist" ], moreinfo: `tpl does not exist ${tplmessage}` } }
|
|
||||||
}
|
|
||||||
const clientconf = fs.readJsonSync( `${config.tribes}/${tribeid}/clientconf.json` )
|
|
||||||
//add clientconf.customization into data for template
|
|
||||||
data.customization = clientconf.customization;
|
|
||||||
//manage receiver
|
|
||||||
const msg = { To: [ { Email: clientconf.useradmin.EMAIL, Name: clientconf.useradmin.LOGIN } ] };
|
|
||||||
if( data.destemail ) {
|
|
||||||
msg.To.push( { Email: data.destemail } )
|
|
||||||
}
|
|
||||||
if( data.destuuid && fs.exist( `${config.tribes}/${tribeid}/users/${data.destuuid}.json` ) ) {
|
|
||||||
const uuidconf = fs.readJsonSync( `${config.tribes}/${tribeid}/users/${data.destuuid}.json` );
|
|
||||||
if( uuidconf.EMAIL && uuidconf.EMAIL.length > 4 ) {
|
|
||||||
msg.To.push( { Email: uuidconf.EMAIL, Name: uuidconf.LOGIN } )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log( data )
|
|
||||||
// email content
|
|
||||||
msg.Subject = `Message from ${tribeid}`;
|
|
||||||
if( data.subject ) msg.Subject = data.subject;
|
|
||||||
msg.TextPart = Mustache.render( fs.readFileSync( `${config.tribes}/${data.tplmessage}/contenttxt.mustache`, 'utf-8' ), data );
|
|
||||||
msg.HTMLPart = Mustache.render( fs.readFileSync( `${config.tribes}/${data.tplmessage}/contenthtml.mustache`, 'utf-8' ), data );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: { model: "Messages", info: [ "msgcustomsuccessfull" ], msg: msg }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Messages.postinfo = ( data ) => {
|
|
||||||
/*
|
|
||||||
Data must have:
|
|
||||||
at least one of desttribeid:,destuuid:,destemail:
|
|
||||||
if an email have to be sent tplmessage:"tribeid/referentials/dataManagement/template/tplname",
|
|
||||||
at least one of contactemail:,contactphone,contactlogin,contactuuid
|
|
||||||
any other usefull keys
|
|
||||||
|
|
||||||
Data is stored into contacts object .json
|
|
||||||
*/
|
|
||||||
let contact = "";
|
|
||||||
[ 'contactemail', 'contactphone', 'contactuuid', 'contactlogin' ].forEach( c => {
|
|
||||||
if( data[ c ] ) contact += c + "##" + data[ c ] + "###";
|
|
||||||
} )
|
|
||||||
console.log( contact )
|
|
||||||
if( contact == "" ) {
|
|
||||||
return { status: 404, data: { model: "Messages", info: [ "contactundefine" ], moreinfo: "no contact field found in this form" } }
|
|
||||||
}
|
|
||||||
if( !data.desttribeid ) {
|
|
||||||
return { status: 404, data: { model: "Messages", info: [ "tribeidundefine" ], moreinfo: "no desttribeid field found in this form" } }
|
|
||||||
}
|
|
||||||
// save new message in contacts
|
|
||||||
let messages = {};
|
|
||||||
if( fs.existsSync( `${config.tribes}/${data.desttribeid}/contacts/${contact}.json` ) ) {
|
|
||||||
messages = fs.readJsonSync( `${config.tribes}/${data.desttribeid}/contacts/${contact}.json`, 'utf-8' );
|
|
||||||
}
|
|
||||||
messages[ Date.now() ] = data;
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${data.desttribeid}/contacts/${contact}.json`, messages );
|
|
||||||
|
|
||||||
// if templatemessage exist then we send alert email
|
|
||||||
// data content have to be consistent with tplmessage to generate a clean email
|
|
||||||
if( data.tplmessage && data.tplmessage != "" &&
|
|
||||||
fs.existsSync( `${config.tribes}/${data.tplmessage}` ) ) {
|
|
||||||
if( !( data.msgtplhtml && data.msgtpltxt ) ) {
|
|
||||||
data.msgcontenthtml = `<p>${data.contactname} - ${contact.replace(/###/g,' ').replace(/##/g,":")}</p><p>Message: ${data.contactmessage}</p>`
|
|
||||||
data.msgcontenttxt = `${data.contactname} - ${contact}/nMessage:${data.contactmessage}\n`;
|
|
||||||
} else {
|
|
||||||
data.msgcontenthtml = Mustache.render( data.msgtplhtml, data )
|
|
||||||
data.msgcontenttxt = Mustache.render( data.msgtpltxt, data )
|
|
||||||
}
|
|
||||||
const msg = Messages.buildemail( data.desttribeid, data.tplmessage, data )
|
|
||||||
if( msg.status == 200 ) {
|
|
||||||
Messages.byEmailwithmailjet( data.desttribeid, [ msg.data.msg ] );
|
|
||||||
}
|
|
||||||
// we get error message eventualy but email feedback sent is not in real time
|
|
||||||
return msg;
|
|
||||||
} else {
|
|
||||||
return { status: 404, data: { info: "missingtpl", model: "Messages", moreinfo: `missing template ${data.tplmessage}` } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Messages.aggregate = () => {
|
|
||||||
// collect each json file and add them to a global.json notifat require level
|
|
||||||
const dest = {};
|
|
||||||
try {
|
|
||||||
glob.sync( `${ config.tribes }/**/notif_*.json` )
|
|
||||||
.forEach( f => {
|
|
||||||
//console.log( 'find ', f )
|
|
||||||
const repglob = `${path.dirname(f)}/global.json`;
|
|
||||||
if( !dest[ repglob ] ) { dest[ repglob ] = [] }
|
|
||||||
dest[ repglob ].push( fs.readJsonSync( f, 'utf-8' ) );
|
|
||||||
fs.removeSync( f );
|
|
||||||
} )
|
|
||||||
//console.log( dest )
|
|
||||||
Object.keys( dest )
|
|
||||||
.forEach( g => {
|
|
||||||
let notif = [];
|
|
||||||
if( fs.existsSync( g ) ) { notif = fs.readJsonSync( g ) };
|
|
||||||
fs.writeJsonSync( g, notif.concat( dest[ g ] ), 'utf-8' );
|
|
||||||
} )
|
|
||||||
} catch ( err ) {
|
|
||||||
Console.log( "ATTENTION, des Messages risquent de disparaitre et ne sont pas traitées." );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Messages.object = ( data, header ) => {
|
|
||||||
/*
|
|
||||||
Create or replace an object no check at all here this is only
|
|
||||||
a way to add / replace object
|
|
||||||
if deeper thing have to be checheck or done then a callback:{tribeidplugin,pluginname,functionname}
|
|
||||||
data.descttribeid tribeid to send at least to admin
|
|
||||||
data.tplmessage = folder of emailtemplate
|
|
||||||
*/
|
|
||||||
console.log( 'data', data )
|
|
||||||
console.log( `${config.tribes}/${header.xworkon}/${data.object}` )
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${header.xworkon}/${data.object}` ) ) {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
data: {
|
|
||||||
model: "Messages",
|
|
||||||
info: [ 'UnknownObjectfortribeid' ],
|
|
||||||
moreinfo: `This object ${data.object} does not exist for this tribeid ${header.xworkon}`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if( data.uuid == 0 ) {
|
|
||||||
data.uuid = UUID.v4();
|
|
||||||
}
|
|
||||||
if( data.callback ) {
|
|
||||||
// check from plugin data and add relevant data
|
|
||||||
const Plug = require( `${config.tribes}/${data.callback.tribeid}/plugins/${data.callback.plugins}/Model.js` );
|
|
||||||
const check = Plug[ data.callback.function ]( header.xworkon, data );
|
|
||||||
if( check.status == 200 ) {
|
|
||||||
data = check.data.data;
|
|
||||||
} else {
|
|
||||||
return check;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${header.xworkon}/${data.object}/${data.uuid}.json`, data );
|
|
||||||
// if templatemessage exist then we try to send alert email
|
|
||||||
if( data.tplmessage && data.tplmessage != "" &&
|
|
||||||
fs.existsSync( `${config.tribes}/${data.tplmessage}` ) ) {
|
|
||||||
const msg = Messages.buildemail( data.desttribeid, data.tplmessage, data )
|
|
||||||
if( msg.status == 200 ) {
|
|
||||||
console.log( 'WARN EMAIL DESACTIVATED CHANGE TO ACTIVATE in Messages.js' )
|
|
||||||
//Messages.byEmailwithmailjet( data.desttribeid, [ msg.data.msg ] );
|
|
||||||
}
|
|
||||||
// we get error message eventualy but email feedback sent is not in real time see notification alert in case of email not sent.
|
|
||||||
};
|
|
||||||
//Sendback data with new information
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
model: "Messages",
|
|
||||||
info: [ 'Successregisterobject' ],
|
|
||||||
msg: data
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Messages.notification = ( data, header ) => {
|
|
||||||
//check if valid notification
|
|
||||||
if( !req.body.elapseddays ) { req.body.elapseddays = 3 }
|
|
||||||
let missingkey = "";
|
|
||||||
[ 'uuid', 'title', 'desc', 'icon', 'elapseddays', 'classicon' ].forEach( k => {
|
|
||||||
if( !data[ k ] ) missingkey += ` ${k},`
|
|
||||||
if( k == "classicon" && !( [ 'text-danger', 'text-primary', 'text-success', 'text-warning', 'text-info' ].includes( data[ k ] ) ) ) {
|
|
||||||
missingkey += ` classicon is not well set ${data[k]}`;
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
if( missingkey != '' ) {
|
|
||||||
return {
|
|
||||||
status: 422,
|
|
||||||
data: {
|
|
||||||
model: "Messages",
|
|
||||||
info: [ 'InvalidNote' ],
|
|
||||||
moreinfo: `invalid notification sent cause missing key ${missingkey}`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${header.xworkon}/${data.object}` ) ) {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
data: {
|
|
||||||
model: "Messages",
|
|
||||||
info: [ 'UnknownObjectfortribeid' ],
|
|
||||||
moreinfo: `This object ${data.object} does not exist for this tribeid ${data.tribeid}`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//by default store in tmp/notification this is only for sysadmin
|
|
||||||
// user adminapixtribe
|
|
||||||
let notdest = `${config.tmp}`;
|
|
||||||
|
|
||||||
if( data.app ) {
|
|
||||||
const destapp = `${config.tribes}/${data.app.split(':')[0]}/spacedev/${data.app.split(':')[1]}`;
|
|
||||||
if( fs.existsSync( destapp ) ) {
|
|
||||||
notdest = destapp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( data.plugins ) {
|
|
||||||
const destplugin = `${config.tribes}/${data.plugins.split(':')[0]}/plugins/${data.plugins.split(':')[1]}`;
|
|
||||||
if( fs.existsSync( destplugin ) ) {
|
|
||||||
notdest = destplugin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( data.object ) {
|
|
||||||
const destobject = `${config.tribes}/${data.tribeid}/${data.object}/`;
|
|
||||||
if( fs.existsSync( destobject ) ) {
|
|
||||||
notdest = destobject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( !data.time ) {
|
|
||||||
data.time = Date.now();
|
|
||||||
}
|
|
||||||
fs.outputJsonSync( `${notdest}/Messages/notif_${data.time}.json`, data );
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
model: "Messages",
|
|
||||||
info: [ 'Successregisternotification' ],
|
|
||||||
notif: data
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Messages.request = ( tribeid, uuid, ACCESSRIGHTS, apprequest ) => {
|
|
||||||
// list notif for each app
|
|
||||||
// list notif for each tribeid / objects if Owned
|
|
||||||
// list notif for
|
|
||||||
// check uuid notification
|
|
||||||
// Collect all notification and agregate them at relevant level;
|
|
||||||
Messages.aggregate();
|
|
||||||
|
|
||||||
//for test purpose
|
|
||||||
//const notif = jsonfile.readFileSync( `${config.tribes}/ndda/spacedev/mesa/src/static/components/notification/data_notiflist_fr.json` );
|
|
||||||
let notif;
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${apprequest.tribeid}/spacedev/${apprequest.website}/src/static/components/notification/data_notiflist_${apprequest.lang}.json` ) ) {
|
|
||||||
// by default we send back this but this highlght an issue
|
|
||||||
notif = {
|
|
||||||
"iconnotif": "bell",
|
|
||||||
"number": 1,
|
|
||||||
"notifheader": "Your last Messages",
|
|
||||||
"notiffooter": "Check all Messages",
|
|
||||||
"actionnotifmanager": "",
|
|
||||||
"href": "?action=notification.view",
|
|
||||||
"notifs": [ {
|
|
||||||
"urldetail": "#",
|
|
||||||
"classicon": "text-danger",
|
|
||||||
"icon": "alert",
|
|
||||||
"title": `File does not exist`,
|
|
||||||
"desc": ` ${config.tribes}/${apprequest.tribeid}/spacedev/${apprequest.website}/src/static/components/notification/data_notiflist_${apprequest.lang}.json`,
|
|
||||||
"elapse": "il y a 13mn"
|
|
||||||
} ]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
notif = jsonfile.readFileSync( `${config.tribes}/${apprequest.tribeid}/spacedev/${apprequest.website}/src/static/components/notification/data_notiflist_${apprequest.lang}.json` );
|
|
||||||
//clean up example notif
|
|
||||||
notif.notifs = [];
|
|
||||||
}
|
|
||||||
//check notification for plugins of tribeid of the login
|
|
||||||
glob.sync( `${config.tribes}/${tribeid}/plugins/*/Messages/global.json` )
|
|
||||||
Object.keys( ACCESSRIGHTS.app )
|
|
||||||
.forEach( a => {
|
|
||||||
// check for each app if notification about app
|
|
||||||
const appnot = `${config.tribes}/${a.split(':')[0]}/spacedev/${a.split(':')[1]}/Messages/global.json`;
|
|
||||||
if( fs.existsSync( appnot ) ) {
|
|
||||||
notif.notifs = notif.notifs.concat( fs.readJsonSync( appnot ) );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
Object.keys( ACCESSRIGHTS.plugin )
|
|
||||||
.forEach( p => {
|
|
||||||
// each plugin
|
|
||||||
if( ACCESSRIGHTS.plugin[ p ].profil == "owner" ) {
|
|
||||||
const pluginnot = `${config.tribes}/${p.split(':')[0]}/plugins/${p.split(':')[1]}/Messages/global.json`;
|
|
||||||
if( fs.existsSync( pluginnot ) ) {
|
|
||||||
notif.notifs = notif.notifs.concat( fs.readJsonSync( pluginnot ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
Object.keys( ACCESSRIGHTS.data )
|
|
||||||
.forEach( c => {
|
|
||||||
// each tribeid
|
|
||||||
Object.keys( ACCESSRIGHTS.data[ c ] )
|
|
||||||
.forEach( o => {
|
|
||||||
const cliobjnot = `${config.tribes}/${c}/${o}/Messages/global.json`
|
|
||||||
//check for each tribeid / Object per accessright user
|
|
||||||
if( fs.existsSync( cliobjnot ) ) {
|
|
||||||
console.log( `droit sur client ${c} objet ${o} : ${ACCESSRIGHTS.data[ c ][ o ]}` );
|
|
||||||
//check if intersection between user accessrigth for this object and the notification accessright is not empty @Todo replace true by intersec
|
|
||||||
console.log( 'WARN no actif filter all notif are shared with any authenticated user' )
|
|
||||||
const newnotif = fs.readJsonSync( cliobjnot )
|
|
||||||
.filter( n => { return true } );
|
|
||||||
notif.notifs = notif.notifs.concat( newnotif );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
} )
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
model: "Messages",
|
|
||||||
info: [ 'successgetnotification' ],
|
|
||||||
notif: notif
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = Messages;
|
|
@ -1,105 +0,0 @@
|
|||||||
const bcrypt = require( 'bcrypt' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const jwt = require( 'jwt-simple' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
const axios = require( 'axios' );
|
|
||||||
const UUID = require( 'uuid' );
|
|
||||||
const Outputs = require( './Outputs.js' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
const checkdata = require( `../nationchains/socialworld/contracts/checkdata.js`);
|
|
||||||
/*
|
|
||||||
Blockchain manager
|
|
||||||
* Manage network directory
|
|
||||||
* read Blockchain and search,
|
|
||||||
* submit a transaction (now) or contract (futur) to store from userA.pubkey to userB.pubkey a number of AXESS
|
|
||||||
* mine to be able to register a block and create AXESS
|
|
||||||
* manage APIXP rules 20 M APIXP 1AXESS = 1 block validation
|
|
||||||
* manage contract = action if something appened validate by a proof of work
|
|
||||||
*/
|
|
||||||
const Nationchains = {};
|
|
||||||
Nationchains.init = () => {
|
|
||||||
console.group( 'init Nationchains' );
|
|
||||||
}
|
|
||||||
Nationchains.synchronize = () => {
|
|
||||||
/*
|
|
||||||
Run process to communicate with a list of apixtribe instance to update transaction and earn AXP
|
|
||||||
To creation of a new tribeid or a new login
|
|
||||||
*/
|
|
||||||
//update himself then send to other information
|
|
||||||
if( process.env.NODE_ENV != "prod" ) {
|
|
||||||
// Not concerned
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
const initcurrentinstance = {
|
|
||||||
"fixedIP": "",
|
|
||||||
"lastblocknumber": 0,
|
|
||||||
"firsttimeupdate": 0,
|
|
||||||
"lastimeupdate": 0,
|
|
||||||
"positifupdate": 0,
|
|
||||||
"negatifupdate": 0,
|
|
||||||
"pubkeyadmin": "",
|
|
||||||
"tribeids": [],
|
|
||||||
"logins": [],
|
|
||||||
"knowninstance": []
|
|
||||||
};
|
|
||||||
let currentinstance = initcurrentinstance;
|
|
||||||
try {
|
|
||||||
currentinstance = fs.readFileSync( `${config.tribes}/${config.mayorId}/nationchains/nodes/${config.rootURL}`, 'utf-8' )
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'first init' )
|
|
||||||
}
|
|
||||||
const loginsglob = fs.readJsonSync( `${config.tmp}/loginsglob.json`, 'utf-8' );
|
|
||||||
currentinstance.logins = Object.keys( loginsglob );
|
|
||||||
currentinstance.tribeids = [ ...new Set( Object.values( loginsglob ) ) ];
|
|
||||||
currentinstance.instanceknown = glob.Sync( `${config.tribes}/${config.mayorId}/nationchains/nodes/*` );
|
|
||||||
//Save it
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${config.mayorId}/nationchains/nodes/${config.rootURL}`, currentinstance );
|
|
||||||
// proof of work
|
|
||||||
// try to find a key based on last block with difficulty
|
|
||||||
// if find then send to all for update and try to get token
|
|
||||||
// in any case rerun Nationchains.synchronize()
|
|
||||||
currentinstance.instanceknown.forEach( u => {
|
|
||||||
if( u != config.rootURL ) {
|
|
||||||
//send currentinstance info and get back state of
|
|
||||||
axios.post( `https://${u}/nationchains/push`, currentinstance )
|
|
||||||
.then( rep => {
|
|
||||||
newdata = rep.payload.moreinfo
|
|
||||||
//Available update info
|
|
||||||
fs.readJson( `${config.tribes}/${config.mayorId}/nationchains/nodes/${u}`, ( err, data ) => {
|
|
||||||
if( err ) {
|
|
||||||
data.negatifupdate += 1;
|
|
||||||
data.lasttimeupdate = Date.now();
|
|
||||||
} else {
|
|
||||||
data.positifupdate += 1;
|
|
||||||
data.lastimeupdate = Date.now();
|
|
||||||
data.tribeids = newdata.tribeids;
|
|
||||||
data.logins = newdata.logins;
|
|
||||||
data.lastblocknumber = newdata.lastblocknumber;
|
|
||||||
newdata.knowninstance.forEach( k => {
|
|
||||||
if( !data.knowninstance.includes( k ) ) {
|
|
||||||
data.knowninstance.push( k );
|
|
||||||
//init the domain for next update
|
|
||||||
initcurrentinstance.firsttimeupdate = Date.now();
|
|
||||||
fs.outputJson( `${config.tribes}/${config.mayorId}/nationchains/nodes/${k}`, initcurrentinstance, 'utf-8' )
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
//save with info
|
|
||||||
fs.outputJson( `${config.tribes}/${config.mayorId}/nationchains/nodes/${u}`, data );
|
|
||||||
} )
|
|
||||||
} )
|
|
||||||
.catch( err => {
|
|
||||||
//Not available
|
|
||||||
data.negatifupdate += 1;
|
|
||||||
data.lasttimeupdate = Date.now();
|
|
||||||
fs.outputJson( `${config.tribes}/${config.mayorId}/nationchains/nodes/${u}`, data );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = Nationchains;
|
|
@ -1,557 +0,0 @@
|
|||||||
const fs = require( 'fs-extra' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const formidable = require( 'formidable' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const mustache = require( 'mustache' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
const UUID = require( 'uuid' );
|
|
||||||
const pdf = require( "pdf-creator-node" );
|
|
||||||
const nodemailer = require( 'nodemailer' );
|
|
||||||
const smtpTransport = require( 'nodemailer-smtp-transport' );
|
|
||||||
const axios = require( 'axios' );
|
|
||||||
const { GoogleSpreadsheet } = require( 'google-spreadsheet' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
const checkdata = require( `../nationchains/socialworld/contracts/checkdata.js` );
|
|
||||||
|
|
||||||
const Outputs = {};
|
|
||||||
|
|
||||||
Outputs.ggsheet2json = async ( req, header ) => {
|
|
||||||
/*
|
|
||||||
req.ggsource = key name of the ggsheet into clientconf
|
|
||||||
req.sheets = [list of sheet names to import]
|
|
||||||
into /ggsheets/ ggsource.json = {resultfolder,productIdSpreadsheet,
|
|
||||||
googleDriveCredentials}
|
|
||||||
*/
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${header.xworkon}/${req.ggsource}` ) ) {
|
|
||||||
// Misconfiguration
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
data: {},
|
|
||||||
info: [ 'Misconfiguration' ],
|
|
||||||
moreinfo: `${header.xworkon}/${req.ggsource} missing file check gitlabdocumentation`,
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const confgg = fs.readJsonSync( `${config.tribes}/${header.xworkon}/${req.ggsource}`, 'utf-8' )
|
|
||||||
//const ggconnect = clientconf.ggsheet[ req.ggsource ]
|
|
||||||
//googleDriveCredentials;
|
|
||||||
//console.log( ggconnect )
|
|
||||||
doc = new GoogleSpreadsheet( confgg.productIdSpreadsheet );
|
|
||||||
await doc.useServiceAccountAuth( confgg.googleDriveCredentials );
|
|
||||||
await doc.loadInfo();
|
|
||||||
let result = [];
|
|
||||||
let invalidfor = "";
|
|
||||||
//console.log( "sheets", req.sheets );
|
|
||||||
for( const sheetName of req.sheets ) {
|
|
||||||
console.log( 'loading: ', sheetName );
|
|
||||||
if( !doc.sheetsByTitle[ sheetName ] ) {
|
|
||||||
invalidfor += " " + sheetName;
|
|
||||||
} else {
|
|
||||||
sheet = doc.sheetsByTitle[ sheetName ]
|
|
||||||
await sheet.loadHeaderRow();
|
|
||||||
const records = await sheet.getRows( { offset: 1 } )
|
|
||||||
records.forEach( record => {
|
|
||||||
let offer = {}
|
|
||||||
for( let index = 0; index < record._sheet.headerValues.length; index++ ) {
|
|
||||||
offer[ record._sheet.headerValues[ index ] ] = record[ record._sheet.headerValues[ index ] ];
|
|
||||||
}
|
|
||||||
result.push( offer );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const idfile = UUID.v4();
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${header.xworkon}/${confgg.resultfolder}/${idfile}.json`, result, 'utf8' );
|
|
||||||
if( invalidfor == "" ) {
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
data: `${confgg.resultfolder}/${idfile}.json`,
|
|
||||||
info: [ 'Successfull' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 207,
|
|
||||||
payload: {
|
|
||||||
data: `${confgg.resultfolder}/${idfile}.json`,
|
|
||||||
info: [ 'PartialySuccessfull' ],
|
|
||||||
moreinfo: `Following sheetNames does not exist :${invalidfor}`,
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// REVOIR TOUT CE QU'IL Y A EN DESSOUS et ré-écrire avec la logique de ggsheet2json phil 25/10/2021
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
const sleep = ( milliseconds = 500 ) => new Promise( resolve => setTimeout( resolve, milliseconds ) );
|
|
||||||
/*
|
|
||||||
Process any data to a specific output:
|
|
||||||
emailer => generate a finale text file to send
|
|
||||||
csv => export json file to csv data
|
|
||||||
pdf => generate a customized document
|
|
||||||
*/
|
|
||||||
Outputs.envoiemail = async ( msg, nowait, nbfois ) => {
|
|
||||||
// console.log('{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}');
|
|
||||||
// console.log('msg to send', msg);
|
|
||||||
console.log( 'nbfois', nbfois );
|
|
||||||
let transporter = nodemailer.createTransport( msg.smtp );
|
|
||||||
if( !nowait ) {
|
|
||||||
console.log( 'attente 1er msg avant d envoyer les autres' );
|
|
||||||
const transport = await transporter.verify();
|
|
||||||
console.log( 'transport', transport );
|
|
||||||
if( transport.error ) {
|
|
||||||
console.log( 'Probleme de smtp', error );
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
payload: {
|
|
||||||
info: ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
let rep = await transporter.sendMail( msg );
|
|
||||||
console.log( 'rep sendMail', rep );
|
|
||||||
if( rep.accepted && rep.accepted.length > 0 && rep.rejected.length == 0 ) {
|
|
||||||
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + ' - Success after waiting 1st email to send ' + msg.to + '\n', 'utf-8' );
|
|
||||||
return {
|
|
||||||
status: '200',
|
|
||||||
payload: {
|
|
||||||
info: [ 'send1stemailok' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// status à tester si necessaire de detecter une erreur
|
|
||||||
// if (rep.rejectedErrors) {
|
|
||||||
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + ' - err after waiting result\n ' + msg.to, 'utf-8' );
|
|
||||||
return {
|
|
||||||
status: '500',
|
|
||||||
payload: {
|
|
||||||
info: [ 'rejectedError' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
transporter.sendMail( msg, async ( err, info ) => {
|
|
||||||
if( err ) {
|
|
||||||
if( nbfois < 4 ) {
|
|
||||||
console.log( 'nouvelle tentative ', nbfois );
|
|
||||||
await sleep( 600000 ); // attends 60sec pour faire une niéme tentative
|
|
||||||
Outputs.envoiemail( msg, true, nbfois + 1 );
|
|
||||||
} else {
|
|
||||||
// logerror in file
|
|
||||||
console.log( 'err', err )
|
|
||||||
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + ' - err after 4 tries to ' + info.rejected.join( ',' ) + '\n', 'utf-8' );
|
|
||||||
// console.log('msg.to not well sent', msg.to);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log( 'info', info )
|
|
||||||
// console.log('msg.to well sent', msg.to);
|
|
||||||
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + ' - Success after ' + nbfois + ' tries to ' + info.accepted.join( ',' ) + '\n', 'utf-8' );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
// return something to not wait the rest of email
|
|
||||||
return {
|
|
||||||
status: '200',
|
|
||||||
payload: {
|
|
||||||
info: [ 'send1stemailok' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Outputs.generemsg = async ( msg, header ) => {
|
|
||||||
/*
|
|
||||||
wait msg sent and return result sent
|
|
||||||
*/
|
|
||||||
// Recupere les parametre smtp du domainName à utiliser
|
|
||||||
console.log( 'pass Outputs.generemsg' )
|
|
||||||
try {
|
|
||||||
const confclientexpediteur = jsonfile.readFileSync( `${config.tribes}/${msg.tribeidperso.tribeidexpediteur}/clientconf.json` );
|
|
||||||
//console.log('expediteur', confclientexpediteur);
|
|
||||||
msg.smtp = confclientexpediteur.smtp;
|
|
||||||
/* const confclient = jsonfile.readFileSync(
|
|
||||||
`${config.tribes}/${msg.tribeidperso.tribeid}/clientconf.json`
|
|
||||||
);*/
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'la conf smtp du client n\'est pas definie' );
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'smtpnotdefined' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
console.log( msg );
|
|
||||||
if( !msg.template.sendascontent && msg.template.htmlfile ) {
|
|
||||||
try {
|
|
||||||
msg.template.html = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8' );
|
|
||||||
msg.template.text = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8' );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile );
|
|
||||||
console.log( err );
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'fileUnknown' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( msg.template.html.length == 0 ) {
|
|
||||||
console.log( 'template.html est vide' )
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'ERRnotemplate' ],
|
|
||||||
model: 'Outputs',
|
|
||||||
moreinfo: 'No template email check '
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// mustache any data into
|
|
||||||
// console.log(msg);
|
|
||||||
const msg2send = {};
|
|
||||||
msg2send.smtp = msg.smtp;
|
|
||||||
msg2send.from = msg.tribeidperso.from;
|
|
||||||
if( msg.tribeidperso.cc ) msg2send.cc = msg.tribeidperso.cc;
|
|
||||||
if( msg.tribeidperso.bcc ) msg2send.bcc = msg.tribeidperso.bcc;
|
|
||||||
if( msg.tribeidperso.replyTo ) msg2send.replyTo = msg.tribeidperso.replyTo;
|
|
||||||
msg2send.headers = {
|
|
||||||
'x-campaign-id': msg.tribeidperso.messageId,
|
|
||||||
'x-client-nd-id': msg.tribeidperso.tribeid,
|
|
||||||
'x-template-nd-id': msg.tribeidperso.templateId
|
|
||||||
};
|
|
||||||
// we get in datacust= {tribeidperso: with clientconf,destperso: personnalise data to send for email}
|
|
||||||
// console.log(msg);
|
|
||||||
console.log( 'nb de message to send:', msg.destperso.length );
|
|
||||||
//console.log(msg.destperso);
|
|
||||||
//msg.destperso.forEach(async (perso, pos) => {
|
|
||||||
let pos;
|
|
||||||
let pass1ermsg = false;
|
|
||||||
for( pos = 0; pos < msg.destperso.length; pos++ ) {
|
|
||||||
const datacust = {
|
|
||||||
tribeidperso: msg.tribeidperso,
|
|
||||||
destperso: msg.destperso[ pos ]
|
|
||||||
};
|
|
||||||
// Evaluation of each field if mustache exist
|
|
||||||
const datacusteval = {};
|
|
||||||
Object.keys( datacust.tribeidperso )
|
|
||||||
.forEach( k => {
|
|
||||||
if( typeof datacust.tribeidperso[ k ] === 'string' || datacust.tribeidperso[ k ] instanceof String ) {
|
|
||||||
datacusteval[ k ] = mustache.render( datacust.tribeidperso[ k ], datacust )
|
|
||||||
} else {
|
|
||||||
datacusteval[ k ] = datacust.tribeidperso[ k ];
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
Object.keys( datacust.destperso )
|
|
||||||
.forEach( k => {
|
|
||||||
if( typeof datacust.destperso[ k ] === 'string' || datacust.destperso[ k ] instanceof String ) {
|
|
||||||
datacusteval[ k ] = mustache.render( datacust.destperso[ k ], datacust )
|
|
||||||
} else {
|
|
||||||
datacusteval[ k ] = datacust.destperso[ k ];
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
msg2send.to = msg.destperso[ pos ].email;
|
|
||||||
console.log( 'msg2send.to ' + msg2send.to + ' pos:' + pos );
|
|
||||||
// console.log('avec datacusteval ', datacusteval)
|
|
||||||
msg2send.subject = mustache.render( msg.template.subject, datacusteval );
|
|
||||||
msg2send.text = mustache.render( msg.template.text, datacusteval );
|
|
||||||
msg2send.html = mustache.render( msg.template.html, datacusteval );
|
|
||||||
let nowait = true;
|
|
||||||
if( config.emailerurl == 'http://devapia.maildigit.fr:3015' ) {
|
|
||||||
fs.writeFileSync( 'devdata/tmp/test.html', msg2send.html, 'utf-8' );
|
|
||||||
console.log( 'lancement email sur dev, pour controler le mail générer voir ds ./config.js il faut changer config.emailerurl avec https://mail.maildigit.fr pour envoyer le mail ' )
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'msgsentok' ],
|
|
||||||
model: 'Outputs',
|
|
||||||
moreinfo: "parametrer sur emailer de dev et pas de production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( pos == 0 ) {
|
|
||||||
nowait = false;
|
|
||||||
/* we are waiting the first email was sent ok then we send all other
|
|
||||||
check NEEDDATA/OVH/workspace/emailerovh to send emailer with nodemailer and nodemailer-smtp-transport
|
|
||||||
*/
|
|
||||||
// console.log('envoie msg', msg);
|
|
||||||
//console.log(msg2send);
|
|
||||||
const ret = await Outputs.envoiemail( msg2send, nowait, 0 );
|
|
||||||
console.log( 'ret 1er msg', ret );
|
|
||||||
if( ret.status == 200 ) {
|
|
||||||
pass1ermsg = true;
|
|
||||||
};
|
|
||||||
} else if( pass1ermsg ) {
|
|
||||||
console.log( '###############################################' )
|
|
||||||
console.log( "envoie msg numero: " + pos + " email: " + msg2send.to )
|
|
||||||
//console.log(msg2send)
|
|
||||||
Outputs.envoiemail( msg2send, nowait, 0 );
|
|
||||||
/*Outputs.envoiemail(msg2send, nowait, 0).then(rep => {
|
|
||||||
console.log("envoie email" + pos)
|
|
||||||
}).catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
});*/
|
|
||||||
};
|
|
||||||
};
|
|
||||||
if( pass1ermsg ) {
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'msgsentok' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
payload: {
|
|
||||||
info: [ 'msgsentko' ],
|
|
||||||
model: 'Ouputs',
|
|
||||||
moreinfo: "1er msg non envoyé car erreur"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Outputs.sendMailcampain = async ( msg, headersmsg ) => {
|
|
||||||
/*
|
|
||||||
Permet de lancer une campagne de mail personnalisé en mode web service
|
|
||||||
avec axios config.emailerurl https://mail qui peut être sur un autre serveur que celui en cours
|
|
||||||
Attention headermsg doit être retraduit avec les champs envoyé par un navigateur
|
|
||||||
Si le serveur en cours appelle cette fonction les champs du header doivent changer
|
|
||||||
voir config.js node_env .exposedHeaders
|
|
||||||
|
|
||||||
Pour un exemple de msg voir u exemple type de message envoyé dans un tribeid/domain/clientconf.json
|
|
||||||
avec l'envoi d'email
|
|
||||||
*/
|
|
||||||
//console.log(msg)
|
|
||||||
// On ajoute le contenu du template directement dans la demande
|
|
||||||
if( msg.template.sendascontent && msg.template.htmlfile ) {
|
|
||||||
try {
|
|
||||||
console.log( 'test', msg.template.sendascontent )
|
|
||||||
msg.template.html = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8' );
|
|
||||||
msg.template.text = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8' );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile );
|
|
||||||
//console.log(err);
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'fileUnknown' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
delete msg.template.htmlfile;
|
|
||||||
if( msg.template.html.length == 0 ) {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'ERRnotemplate' ],
|
|
||||||
model: 'Outputs',
|
|
||||||
moreinfo: 'No template email check '
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log( 'envoie sur', `${config.emailerurl}/outputs/msg` )
|
|
||||||
//console.log(msg)
|
|
||||||
// on check si les key de headermsg sont des key traduite via exposedHeaders
|
|
||||||
// (cas ou c'est l'application qui envoie un email)
|
|
||||||
if( headersmsg.xtribeid ) {
|
|
||||||
Object.keys( config.exposedHeaders )
|
|
||||||
.forEach( h => {
|
|
||||||
headersmsg[ h ] = headersmsg[ config.exposedHeaders[ h ] ];
|
|
||||||
delete headersmsg[ config.exposedHeaders[ h ] ];
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
// on ajoute le code pour la signature
|
|
||||||
headersmsg.hashbody = msg.code;
|
|
||||||
console.log( 'header after traduction: ', headersmsg )
|
|
||||||
try {
|
|
||||||
const resCamp = await axios.post( `${config.emailerurl}/outputs/msg`, msg, {
|
|
||||||
headers: headersmsg
|
|
||||||
} );
|
|
||||||
//console.log('Etat:', resCamp);
|
|
||||||
console.log( 'Tried to send 1st email of the campain ' + msg.destperso[ 0 ].email );
|
|
||||||
// it just check the 1st email in destperso to return an answer if 1st is ok then all other are send in queue
|
|
||||||
if( resCamp ) {
|
|
||||||
return resCamp;
|
|
||||||
} else {
|
|
||||||
return { status: 200, payload: { info: [ 'CampainSent' ], model: 'Outputs' } };
|
|
||||||
}
|
|
||||||
} catch ( err ) {
|
|
||||||
// aios error handler
|
|
||||||
return { status: 401, payload: { info: [ 'erreuraxios' ], model: 'Outputs', moreinfo: err.message } }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Outputs.get = function ( filename, header ) {
|
|
||||||
// check file exist
|
|
||||||
const file = `${config.tribes}/${header.xworkon}/${filename}`;
|
|
||||||
// console.log('fichier demande ', file);
|
|
||||||
if( !fs.existsSync( file ) ) {
|
|
||||||
// console.log('le fichier demande n existe pas ', file);
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'fileUnknown' ],
|
|
||||||
model: 'UploadFiles'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
console.log( 'envoie le fichier ', file );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'fileknown' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
file: file
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Outputs.addjson = function ( data, header ) {
|
|
||||||
/*
|
|
||||||
Le header = {X-WorkOn:"",destinationfile:"", filename:""}
|
|
||||||
Le body = {jsonp:{},callback:function to launch after download,'code':'mot cle pour verifier que le fichier est à garder'}
|
|
||||||
*/
|
|
||||||
// console.log(req.body.jsonp);
|
|
||||||
try {
|
|
||||||
jsonfile.writeFileSync( header.destinationfile + '/' + header.filename, data.jsonp );
|
|
||||||
if( data.callback ) {
|
|
||||||
const execCB = require( `${config.mainDir}/models/tribeid/${header.xworkon
|
|
||||||
}` );
|
|
||||||
execCB[ data.callback ]();
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'wellUpload' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
render: {
|
|
||||||
destination: header.destinationfile,
|
|
||||||
filename: header.filename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'Impossible de sauvegarder le fichier, A COMPRENDRE', err );
|
|
||||||
return {
|
|
||||||
status: 503,
|
|
||||||
payload: {
|
|
||||||
info: [ 'savingError' ],
|
|
||||||
model: 'UploadFiles'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Outputs.add = function ( req, header ) {
|
|
||||||
const form = new formidable.IncomingForm();
|
|
||||||
console.log( 'req.headers', req.headers );
|
|
||||||
console.log( 'req.params', req.params );
|
|
||||||
console.log( 'req.query', req.query );
|
|
||||||
console.log( 'req.body', req.body );
|
|
||||||
let destinationfile = `${config.tribes}/${header.xworkon}/${header.destinationfile
|
|
||||||
}`;
|
|
||||||
form.parse( req, function ( err, fields, files ) {
|
|
||||||
console.log( 'files', files.file.path );
|
|
||||||
console.log( 'fields', fields );
|
|
||||||
const oldpath = files.file.path;
|
|
||||||
destinationfile += '/' + files.file.name;
|
|
||||||
console.log( 'oldpath', oldpath );
|
|
||||||
console.log( 'destinationfile', destinationfile );
|
|
||||||
fs.copyFile( oldpath, destinationfile, function ( err ) {
|
|
||||||
if( err ) {
|
|
||||||
console.log( err );
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
payload: {
|
|
||||||
info: [ 'savingError' ],
|
|
||||||
model: 'UploadFiles'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
console.log( 'passe' );
|
|
||||||
fs.unlink( oldpath );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'wellUpload' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
render: {
|
|
||||||
destination: destinationfile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
Outputs.generepdf = ( req, header ) => {
|
|
||||||
return new Promise( ( resolve, reject ) => {
|
|
||||||
let options = {
|
|
||||||
format: "A4",
|
|
||||||
orientation: "portrait",
|
|
||||||
border: "10mm",
|
|
||||||
footer: {
|
|
||||||
height: "20mm",
|
|
||||||
contents: {
|
|
||||||
default: '<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>', // html pagination if edit needed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let document = {
|
|
||||||
html: req.html,
|
|
||||||
data: {
|
|
||||||
data: req.data,
|
|
||||||
},
|
|
||||||
path: `${config.tribes}/${header.xtribe}/outputs/${UUID.v4()}.pdf`,
|
|
||||||
type: "",
|
|
||||||
};
|
|
||||||
pdf // generate pdf
|
|
||||||
.create( document, options )
|
|
||||||
.then( ( res ) => {
|
|
||||||
resolve( {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'wellPdfGenerated' ],
|
|
||||||
model: 'Outputs',
|
|
||||||
data: {
|
|
||||||
path: document.path,
|
|
||||||
filename: req.data.filename
|
|
||||||
},
|
|
||||||
render: {
|
|
||||||
filename: req.data.filename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} )
|
|
||||||
.catch( ( err ) => {
|
|
||||||
reject( {
|
|
||||||
status: 500,
|
|
||||||
payload: {
|
|
||||||
info: [ 'pdfGenerationError' ],
|
|
||||||
model: 'Outputs',
|
|
||||||
error: err
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
module.exports = Outputs;
|
|
@ -1,492 +0,0 @@
|
|||||||
const fs = require( 'fs-extra' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const formidable = require( 'formidable' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const mustache = require( 'mustache' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
const UUID = require( 'uuid' );
|
|
||||||
const pdf = require( "pdf-creator-node" );
|
|
||||||
const nodemailer = require( 'nodemailer' );
|
|
||||||
const smtpTransport = require( 'nodemailer-smtp-transport' );
|
|
||||||
const axios = require( 'axios' );
|
|
||||||
const { GoogleSpreadsheet } = require( 'google-spreadsheet' );
|
|
||||||
const async = require( 'async' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
const checkdata = require( `${config.tribes}/${config.mayorId}/www/cdn/public/js/checkdata` );
|
|
||||||
|
|
||||||
const Outputs = {};
|
|
||||||
const sleep = ( milliseconds = 500 ) => new Promise( resolve => setTimeout( resolve, milliseconds ) );
|
|
||||||
/*
|
|
||||||
Process any data to a specific output:
|
|
||||||
emailer => generate a finale text file to send
|
|
||||||
csv => export json file to csv data
|
|
||||||
pdf => generate a customized document
|
|
||||||
*/
|
|
||||||
Outputs.envoiemail = ( msg, next ) => {
|
|
||||||
let transporter = nodemailer.createTransport( msg.smtp );
|
|
||||||
transporter.sendMail( msg, async ( err, info ) => {
|
|
||||||
if( err ) {
|
|
||||||
next( err );
|
|
||||||
} else {
|
|
||||||
console.log( 'info', info )
|
|
||||||
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + ' - Success after ' + '0' + ' tries to ' + info.accepted.join( ',' ) + '\n', 'utf-8' );
|
|
||||||
next( null );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
Outputs.setupmail = ( msg, msg2send, index ) => {
|
|
||||||
const datacust = {
|
|
||||||
tribeidperso: msg.tribeidperso,
|
|
||||||
destperso: index
|
|
||||||
};
|
|
||||||
// Evaluation of each field if mustache exist
|
|
||||||
const datacusteval = {};
|
|
||||||
Object.keys( datacust.tribeidperso )
|
|
||||||
.forEach( k => {
|
|
||||||
if( typeof datacust.tribeidperso[ k ] === 'string' || datacust.tribeidperso[ k ] instanceof String ) {
|
|
||||||
datacusteval[ k ] = mustache.render( datacust.tribeidperso[ k ], datacust )
|
|
||||||
} else {
|
|
||||||
datacusteval[ k ] = datacust.tribeidperso[ k ];
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
Object.keys( datacust.destperso )
|
|
||||||
.forEach( k => {
|
|
||||||
if( typeof datacust.destperso[ k ] === 'string' || datacust.destperso[ k ] instanceof String ) {
|
|
||||||
datacusteval[ k ] = mustache.render( datacust.destperso[ k ], datacust )
|
|
||||||
} else {
|
|
||||||
datacusteval[ k ] = datacust.destperso[ k ];
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
msg2send.to = index.email;
|
|
||||||
console.log( 'msg2send.to ' + msg2send.to );
|
|
||||||
msg2send.subject = mustache.render( msg.template.subject, datacusteval );
|
|
||||||
msg2send.text = mustache.render( msg.template.text, datacusteval );
|
|
||||||
msg2send.html = mustache.render( msg.template.html, datacusteval );
|
|
||||||
// TODO need to move that in generemsg
|
|
||||||
// if (config.emailerurl == 'http://devapia.maildigit.fr:3015') {
|
|
||||||
// fs.writeFileSync('devdata/tmp/test.html', msg2send.html, 'utf-8');
|
|
||||||
// console.log('lancement email sur dev, pour controler le mail générer voir ds ./config.js il faut changer config.emailerurl avec https://mail.maildigit.fr pour envoyer le mail ')
|
|
||||||
// return {
|
|
||||||
// status: 200,
|
|
||||||
// payload: {
|
|
||||||
// info: ['msgsentok'],
|
|
||||||
// model: 'Outputs',
|
|
||||||
// moreinfo: "parametrer sur emailer de dev et pas de production"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
return msg2send;
|
|
||||||
}
|
|
||||||
Outputs.envoiefirstmail = async ( msg ) => {
|
|
||||||
console.log( '###############################################' )
|
|
||||||
console.log( "envoie first msg email: " + msg.to )
|
|
||||||
let transporter = nodemailer.createTransport( msg.smtp );
|
|
||||||
console.log( 'attente 1er msg avant d envoyer les autres' );
|
|
||||||
const transport = await transporter.verify();
|
|
||||||
console.log( 'transport', transport );
|
|
||||||
if( transport.error ) {
|
|
||||||
console.log( 'Probleme de smtp', error );
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
payload: {
|
|
||||||
info: ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
let rep = await transporter.sendMail( msg );
|
|
||||||
console.log( 'rep sendMail', rep );
|
|
||||||
if( rep.accepted && rep.accepted.length > 0 && rep.rejected.length == 0 ) {
|
|
||||||
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + ' - Success after waiting 1st email to send ' + msg.to + '\n', 'utf-8' );
|
|
||||||
return {
|
|
||||||
status: '200',
|
|
||||||
payload: {
|
|
||||||
info: [ 'send1stemailok' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// status à tester si necessaire de detecter une erreur
|
|
||||||
// if (rep.rejectedErrors) {
|
|
||||||
fs.appendFileSync( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + ' - err after waiting result\n ' + msg.to, 'utf-8' );
|
|
||||||
return {
|
|
||||||
status: '500',
|
|
||||||
payload: {
|
|
||||||
info: [ 'rejectedError' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Outputs.envoiemails = ( msg, msg2send, targets, iteration, resolve, reject ) => {
|
|
||||||
let newtargets = [];
|
|
||||||
async.each( targets, ( index, callback ) => { // iterate asynchronously in msg.destperso (targets)
|
|
||||||
let finalmsg = Outputs.setupmail( msg, msg2send, index );
|
|
||||||
console.log( '###############################################' )
|
|
||||||
console.log( "envoie msg email: " + finalmsg.to )
|
|
||||||
Outputs.envoiemail( finalmsg, ( err ) => {
|
|
||||||
if( err ) { // intentionally don't pass this error in callback, we dont want to break loop
|
|
||||||
newtargets.push( index ); // stock all errored mails for next try
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
callback();
|
|
||||||
}, function ( err ) { // part executed only once all iterations are done
|
|
||||||
if( newtargets.length > 0 && iteration < 4 ) {
|
|
||||||
setTimeout( () => {
|
|
||||||
Outputs.envoiemails( msg, msg2send, newtargets, iteration + 1, resolve, reject );
|
|
||||||
}, 600000 );
|
|
||||||
} else resolve( newtargets ); // return not resolved errors after 4 trys for log
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
Outputs.generemsg = async ( msg, header ) => {
|
|
||||||
/*
|
|
||||||
wait msg sent and return result sent
|
|
||||||
*/
|
|
||||||
// Recupere les parametre smtp du domainName à utiliser
|
|
||||||
console.log( 'pass Outputs.generemsg' )
|
|
||||||
try {
|
|
||||||
const confclientexpediteur = jsonfile.readFileSync( `${config.tribes}/${msg.tribeidperso.tribeidexpediteur}/clientconf.json` );
|
|
||||||
msg.smtp = confclientexpediteur.smtp;
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'la conf smtp du client n\'est pas definie' );
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'smtpnotdefined' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
console.log( msg );
|
|
||||||
if( !msg.template.sendascontent && msg.template.htmlfile ) {
|
|
||||||
try {
|
|
||||||
msg.template.html = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8' );
|
|
||||||
msg.template.text = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8' );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile );
|
|
||||||
console.log( err );
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'fileUnknown' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( msg.template.html.length == 0 ) {
|
|
||||||
console.log( 'template.html est vide' )
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'ERRnotemplate' ],
|
|
||||||
model: 'Outputs',
|
|
||||||
moreinfo: 'No template email check '
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// mustache any data into
|
|
||||||
const msg2send = {};
|
|
||||||
msg2send.smtp = msg.smtp;
|
|
||||||
msg2send.from = msg.tribeidperso.from;
|
|
||||||
if( msg.tribeidperso.cc ) msg2send.cc = msg.tribeidperso.cc;
|
|
||||||
if( msg.tribeidperso.bcc ) msg2send.bcc = msg.tribeidperso.bcc;
|
|
||||||
if( msg.tribeidperso.replyTo ) msg2send.replyTo = msg.tribeidperso.replyTo;
|
|
||||||
msg2send.headers = {
|
|
||||||
'x-campaign-id': msg.tribeidperso.messageId,
|
|
||||||
'x-client-nd-id': msg.tribeidperso.tribeid,
|
|
||||||
'x-template-nd-id': msg.tribeidperso.templateId
|
|
||||||
};
|
|
||||||
console.log( 'nb de message to send:', msg.destperso.length );
|
|
||||||
// send first mail
|
|
||||||
const ret = await Outputs.envoiefirstmail( Outputs.setupmail( msg, msg2send, msg.destperso[ 0 ] ) );
|
|
||||||
console.log( 'ret 1er msg', ret );
|
|
||||||
if( ret.status == 200 ) {
|
|
||||||
pass1ermsg = true;
|
|
||||||
msg.destperso.shift();
|
|
||||||
};
|
|
||||||
console.log( 'attente 1er msg avant d envoyer les autres' );
|
|
||||||
// send other mails
|
|
||||||
new Promise( ( resolve, reject ) => { // useless promise used for recursive calls in Outputs.envoiemails
|
|
||||||
Outputs.envoiemails( msg, msg2send, msg.destperso, 0, resolve, reject );
|
|
||||||
} )
|
|
||||||
.then( ( result ) => {
|
|
||||||
async.eachSeries( result, ( index, callback ) => { // variante of each but runs only a single operation at a time because of fs.appendFile
|
|
||||||
fs.appendFile( `${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment( new Date() )
|
|
||||||
.format( 'YYYYMMDD HH:mm:ss' ) + ' - err after 4 tries to ' + info.rejected.join( ',' ) + '\n', 'utf-8', ( err ) => {
|
|
||||||
callback( err );
|
|
||||||
}, ( err ) => {
|
|
||||||
if( err ) console.log( err );
|
|
||||||
} );
|
|
||||||
console.log( 'msg.to not well sent', msg.to );
|
|
||||||
} );
|
|
||||||
} )
|
|
||||||
if( pass1ermsg ) {
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'msgsentok' ],
|
|
||||||
model: 'Outputs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
payload: {
|
|
||||||
info: [ 'msgsentko' ],
|
|
||||||
model: 'Ouputs',
|
|
||||||
moreinfo: "1er msg non envoyé car erreur"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Outputs.sendMailcampain = async ( msg, headersmsg ) => {
|
|
||||||
/*
|
|
||||||
Permet de lancer une campagne de mail personnalisé en mode web service
|
|
||||||
avec axios config.emailerurl https://mail qui peut être sur un autre serveur que celui en cours
|
|
||||||
Attention headermsg doit être retraduit avec les champs envoyé par un navigateur
|
|
||||||
Si le serveur en cours appelle cette fonction les champs du header doivent changer
|
|
||||||
voir config.js node_env .exposedHeaders
|
|
||||||
|
|
||||||
Pour un exemple de msg voir u exemple type de message envoyé dans un tribeid/domain/clientconf.json
|
|
||||||
avec l'envoi d'email
|
|
||||||
*/
|
|
||||||
//console.log(msg)
|
|
||||||
// On ajoute le contenu du template directement dans la demande
|
|
||||||
if( msg.template.sendascontent && msg.template.htmlfile ) {
|
|
||||||
try {
|
|
||||||
console.log( 'test', msg.template.sendascontent )
|
|
||||||
msg.template.html = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8' );
|
|
||||||
msg.template.text = fs.readFileSync( config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8' );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile );
|
|
||||||
//console.log(err);
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'fileUnknown' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
delete msg.template.htmlfile;
|
|
||||||
if( msg.template.html.length == 0 ) {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'ERRnotemplate' ],
|
|
||||||
model: 'Outputs',
|
|
||||||
moreinfo: 'No template email check '
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log( 'envoie sur', `${config.emailerurl}/outputs/msg` )
|
|
||||||
//console.log(msg)
|
|
||||||
// on check si les key de headermsg sont des key traduite via exposedHeaders
|
|
||||||
// (cas ou c'est l'application qui envoie un email)
|
|
||||||
if( headersmsg.xtribeid ) {
|
|
||||||
Object.keys( config.exposedHeaders )
|
|
||||||
.forEach( h => {
|
|
||||||
headersmsg[ h ] = headersmsg[ config.exposedHeaders[ h ] ];
|
|
||||||
delete headersmsg[ config.exposedHeaders[ h ] ];
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
// on ajoute le code pour la signature
|
|
||||||
headersmsg.hashbody = msg.code;
|
|
||||||
console.log( 'header after traduction: ', headersmsg )
|
|
||||||
try {
|
|
||||||
const resCamp = await axios.post( `${config.emailerurl}/outputs/msg`, msg, {
|
|
||||||
headers: headersmsg
|
|
||||||
} );
|
|
||||||
//console.log('Etat:', resCamp);
|
|
||||||
console.log( 'Tried to send 1st email of the campain ' + msg.destperso[ 0 ].email );
|
|
||||||
// it just check the 1st email in destperso to return an answer if 1st is ok then all other are send in queue
|
|
||||||
if( resCamp ) {
|
|
||||||
return resCamp;
|
|
||||||
} else {
|
|
||||||
return { status: 200, payload: { info: [ 'CampainSent' ], model: 'Outputs' } };
|
|
||||||
}
|
|
||||||
} catch ( err ) {
|
|
||||||
// aios error handler
|
|
||||||
return { status: 401, payload: { info: [ 'erreuraxios' ], model: 'Outputs', moreinfo: err.message } }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Outputs.get = function ( filename, header ) {
|
|
||||||
// check file exist
|
|
||||||
const file = `${config.tribes}/${header.xworkon}/${filename}`;
|
|
||||||
// console.log('fichier demande ', file);
|
|
||||||
if( !fs.existsSync( file ) ) {
|
|
||||||
// console.log('le fichier demande n existe pas ', file);
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: {
|
|
||||||
info: [ 'fileUnknown' ],
|
|
||||||
model: 'UploadFiles'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
console.log( 'envoie le fichier ', file );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'fileknown' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
file: file
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Outputs.addjson = function ( data, header ) {
|
|
||||||
/*
|
|
||||||
Le header = {X-WorkOn:"",destinationfile:"", filename:""}
|
|
||||||
Le body = {jsonp:{},callback:function to launch after download,'code':'mot cle pour verifier que le fichier est à garder'}
|
|
||||||
*/
|
|
||||||
// console.log(req.body.jsonp);
|
|
||||||
try {
|
|
||||||
jsonfile.writeFileSync( header.destinationfile + '/' + header.filename, data.jsonp );
|
|
||||||
if( data.callback ) {
|
|
||||||
const execCB = require( `${config.mainDir}/models/tribeid/${header.xworkon
|
|
||||||
}` );
|
|
||||||
execCB[ data.callback ]();
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'wellUpload' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
render: {
|
|
||||||
destination: header.destinationfile,
|
|
||||||
filename: header.filename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'Impossible de sauvegarder le fichier, A COMPRENDRE', err );
|
|
||||||
return {
|
|
||||||
status: 503,
|
|
||||||
payload: {
|
|
||||||
info: [ 'savingError' ],
|
|
||||||
model: 'UploadFiles'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Outputs.add = function ( req, header ) {
|
|
||||||
const form = new formidable.IncomingForm();
|
|
||||||
console.log( 'req.headers', req.headers );
|
|
||||||
console.log( 'req.params', req.params );
|
|
||||||
console.log( 'req.query', req.query );
|
|
||||||
console.log( 'req.body', req.body );
|
|
||||||
let destinationfile = `${config.tribes}/${header.xworkon}/${header.destinationfile
|
|
||||||
}`;
|
|
||||||
form.parse( req, function ( err, fields, files ) {
|
|
||||||
console.log( 'files', files.file.path );
|
|
||||||
console.log( 'fields', fields );
|
|
||||||
const oldpath = files.file.path;
|
|
||||||
destinationfile += '/' + files.file.name;
|
|
||||||
console.log( 'oldpath', oldpath );
|
|
||||||
console.log( 'destinationfile', destinationfile );
|
|
||||||
fs.copyFile( oldpath, destinationfile, function ( err ) {
|
|
||||||
if( err ) {
|
|
||||||
console.log( err );
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
payload: {
|
|
||||||
info: [ 'savingError' ],
|
|
||||||
model: 'UploadFiles'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
console.log( 'passe' );
|
|
||||||
fs.unlink( oldpath );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'wellUpload' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
render: {
|
|
||||||
destination: destinationfile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
Outputs.sheettojson = async ( req, header ) => {
|
|
||||||
doc = new GoogleSpreadsheet( req.productIdSpreadsheet );
|
|
||||||
await doc.useServiceAccountAuth( req.googleDriveCredentials );
|
|
||||||
await doc.loadInfo();
|
|
||||||
let result = [];
|
|
||||||
for( const sheetName of req.sheets ) {
|
|
||||||
console.log( 'loading: ', sheetName );
|
|
||||||
sheet = doc.sheetsByTitle[ sheetName ]
|
|
||||||
await sheet.loadHeaderRow();
|
|
||||||
const records = await sheet.getRows( { offset: 1 } )
|
|
||||||
records.forEach( record => {
|
|
||||||
let offer = {}
|
|
||||||
for( let index = 0; index < record._sheet.headerValues.length; index++ ) {
|
|
||||||
offer[ record._sheet.headerValues[ index ] ] = record[ record._sheet.headerValues[ index ] ];
|
|
||||||
}
|
|
||||||
result.push( offer );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
Outputs.generepdf = ( req, header ) => {
|
|
||||||
return new Promise( ( resolve, reject ) => {
|
|
||||||
let options = {
|
|
||||||
format: "A4",
|
|
||||||
orientation: "portrait",
|
|
||||||
border: "10mm",
|
|
||||||
footer: {
|
|
||||||
height: "20mm",
|
|
||||||
contents: {
|
|
||||||
default: '<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>', // html pagination if edit needed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let document = {
|
|
||||||
html: req.html,
|
|
||||||
data: {
|
|
||||||
data: req.data,
|
|
||||||
},
|
|
||||||
path: `${config.tribes}/${header.xtribeid}/outputs/${UUID.v4()}.pdf`,
|
|
||||||
type: "",
|
|
||||||
};
|
|
||||||
pdf // generate pdf
|
|
||||||
.create( document, options )
|
|
||||||
.then( ( res ) => {
|
|
||||||
resolve( {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'wellPdfGenerated' ],
|
|
||||||
model: 'Outputs',
|
|
||||||
data: {
|
|
||||||
path: document.path,
|
|
||||||
filename: req.data.filename
|
|
||||||
},
|
|
||||||
render: {
|
|
||||||
filename: req.data.filename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} )
|
|
||||||
.catch( ( err ) => {
|
|
||||||
reject( {
|
|
||||||
status: 500,
|
|
||||||
payload: {
|
|
||||||
info: [ 'pdfGenerationError' ],
|
|
||||||
model: 'Outputs',
|
|
||||||
error: err
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
module.exports = Outputs;
|
|
774
models/Pagans.js
774
models/Pagans.js
@ -1,774 +0,0 @@
|
|||||||
const bcrypt = require( 'bcrypt' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
const jwt = require( 'jwt-simple' );
|
|
||||||
const UUID = require( 'uuid' );
|
|
||||||
const Outputs = require( './Outputs.js' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
const checkdata = require( `../nationchains/socialworld/contracts/checkdata.js`);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Gestion des utilisateurs connecte
|
|
||||||
|
|
||||||
/tribes/tribeid/users/
|
|
||||||
UUID.json
|
|
||||||
/searchindex/indexTOKEN = {TOKEN:UUID}
|
|
||||||
to check authentification
|
|
||||||
indexLOGIN = {LOGIN:UUID}
|
|
||||||
to check if LOGIN exist
|
|
||||||
|
|
||||||
Used for referntial:
|
|
||||||
To update
|
|
||||||
Global: datashared/referentials/dataManagement/object/users.json
|
|
||||||
Local: data/tribe/*${header.workOn}/referentials/dataManagement/object/users.json
|
|
||||||
To use after a server rerun
|
|
||||||
data/tribe/*${header.workOn}/referentials/${header.langue}/object/users.json
|
|
||||||
|
|
||||||
Faire comme contact charger les index au lancement
|
|
||||||
et changer la logique de user/id/profil o
|
|
||||||
|
|
||||||
|
|
||||||
On initialise les objets au lancement du serveur
|
|
||||||
this.uids[u.UUID] = [u.LOGIN, u.EMAIL, u.password, u.profil, u.TOKEN];
|
|
||||||
this.logins[u.LOGIN] = u.UUID;
|
|
||||||
this.tokens[u.UUID] = u.TOKEN;
|
|
||||||
on stocke /domain/tribeid/users/logins.json et /uids.json
|
|
||||||
on stocke /domain/tribeids.json et tokkens.json
|
|
||||||
|
|
||||||
On retourne l'objet {tribeids:[list tribeid], tokens:{UUID:TOKEN}}
|
|
||||||
*/
|
|
||||||
const Pagans = {};
|
|
||||||
Pagans.init = tribeids => {
|
|
||||||
console.group( 'init Pagans logins, tokens ...' );
|
|
||||||
// store globaly tokens
|
|
||||||
const tokens = {};
|
|
||||||
const emailsglob = {};
|
|
||||||
const loginsglob = {};
|
|
||||||
// For each tribeid create series of indexes
|
|
||||||
//console.log(tribeids);
|
|
||||||
tribeids.forEach( tribeid => {
|
|
||||||
// Reset for each domain
|
|
||||||
const uids = {};
|
|
||||||
const logins = {};
|
|
||||||
const emails = {};
|
|
||||||
//check folder exist
|
|
||||||
/*if( !fs.existsSync( `${config.tribes}/${tribeid}/users` ) ) {
|
|
||||||
fs.mkdirSync( `${config.tribes}/${tribeid}/users` )
|
|
||||||
}
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${tribeid}/users/searchindex` ) ) {
|
|
||||||
fs.mkdirSync( `${config.tribes}/${tribeid}/users/searchindex` )
|
|
||||||
}*/
|
|
||||||
glob.sync( `${config.tribes}/${tribeid}/users/*.json` )
|
|
||||||
.forEach( file => {
|
|
||||||
//console.log( file );
|
|
||||||
const u = fs.readJsonSync( file, 'utf-8' );
|
|
||||||
if( !u.TOKEN ) {
|
|
||||||
u.TOKEN = '';
|
|
||||||
}
|
|
||||||
//console.log( u )
|
|
||||||
uids[ u.UUID ] = [ u.LOGIN, u.EMAIL, u.PASSWORD, u.ACCESSRIGHTS, u.TOKEN ];
|
|
||||||
logins[ u.LOGIN ] = u.UUID;
|
|
||||||
loginsglob[ u.LOGIN ] = tribeid;
|
|
||||||
// On ne charge que les TOKEN valide
|
|
||||||
let decodedTOKEN = {};
|
|
||||||
if( u.TOKEN != '' ) {
|
|
||||||
try {
|
|
||||||
decodedTOKEN = jwt.decode( u.TOKEN, config.jwtSecret );
|
|
||||||
//console.log( 'decodeTOKEN', decodedTOKEN )
|
|
||||||
if( moment( decodedTOKEN.expiration ) > moment() ) {
|
|
||||||
tokens[ u.UUID ] = { TOKEN: u.TOKEN, ACCESSRIGHTS: u.ACCESSRIGHTS };
|
|
||||||
//console.log( `add token valid for ${u.UUID}:`, tokens[ u.UUID ] )
|
|
||||||
}
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'pb de TOKEN impossible a decoder' + u.TOKEN, err );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( u.EMAIL ) {
|
|
||||||
emails[ u.EMAIL ] = u.UUID;
|
|
||||||
emailsglob[ u.EMAIL ] = tribeid;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
// UIDS INDEX BY DOMAIN ={UUID:[LOGIN,EMAIL,psw,accessRight,TOKEN]}
|
|
||||||
fs.outputJson( `${config.tribes}/${tribeid}/users/searchindex/uids.json`, uids, {
|
|
||||||
spaces: 2
|
|
||||||
}, err => {
|
|
||||||
if( err ) throw err;
|
|
||||||
} );
|
|
||||||
// LOGINS INDEX BY DOMAIN ={LOGIN:UUID}
|
|
||||||
fs.outputJson( `${config.tribes}/${tribeid}/users/searchindex/logins.json`, logins, {
|
|
||||||
spaces: 2
|
|
||||||
}, err => {
|
|
||||||
if( err ) throw err;
|
|
||||||
} );
|
|
||||||
// EMAILS INDEX BY DOMAIN ={EMAIL:UUID}
|
|
||||||
fs.outputJson( `${config.tribes}/${tribeid}/users/searchindex/emails.json`, emails, {
|
|
||||||
spaces: 2
|
|
||||||
}, err => {
|
|
||||||
if( err ) throw err;
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
// EMAILS et appartenance domain
|
|
||||||
fs.outputJson( `${config.tmp}/emailsglob.json`, emailsglob, {
|
|
||||||
spaces: 2
|
|
||||||
}, err => {
|
|
||||||
if( err ) throw err;
|
|
||||||
} );
|
|
||||||
// logins et appartenance domain (permet de retrouver tribeid pour un LOGIN)
|
|
||||||
fs.outputJson( `${config.tmp}/loginsglob.json`, loginsglob, {
|
|
||||||
spaces: 2
|
|
||||||
}, err => {
|
|
||||||
if( err ) throw err;
|
|
||||||
} );
|
|
||||||
// TOKENS GLOBAL INDEX Centralise tous les TOKEN pour plus de rapidité
|
|
||||||
// {UUID:TOKEN}
|
|
||||||
fs.outputJson( `${config.tmp}/tokens.json`, tokens, {
|
|
||||||
spaces: 2
|
|
||||||
}, err => {
|
|
||||||
if( err ) throw err;
|
|
||||||
} );
|
|
||||||
console.groupEnd();
|
|
||||||
return { tokens };
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Update indexes: logins, uids and tokens
|
|
||||||
* @param {object} user User object
|
|
||||||
* @param {string} tribeid - The client identifier
|
|
||||||
* @rm {boolean} false => update, true => remove
|
|
||||||
*/
|
|
||||||
Pagans.updateDatabase = ( user, tribeid, rm = false ) => {
|
|
||||||
console.group( `Pagans.updateDatabase for ${tribeid} with user ${user.LOGIN}` )
|
|
||||||
console.assert( config.loglevel == "quiet", 'user', user );
|
|
||||||
const loginsIndex = `${config.tribes}/${tribeid}/users/searchindex/logins.json`;
|
|
||||||
jsonfile.readFile( loginsIndex, function ( err, logins ) {
|
|
||||||
console.assert( config.loglevel == "quiet", 'logins', logins );
|
|
||||||
try {
|
|
||||||
if( rm ) {
|
|
||||||
delete logins[ user.LOGIN ];
|
|
||||||
} else {
|
|
||||||
logins[ user.LOGIN ] = user.UUID;
|
|
||||||
}
|
|
||||||
jsonfile.writeFile( loginsIndex, logins, {
|
|
||||||
spaces: 2
|
|
||||||
}, err => {
|
|
||||||
if( err ) console.log( err );
|
|
||||||
} );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'Gros pb de mise à jour Pagans.updateDatabase conflit des logins' );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
const uidsIndex = `${config.tribes}/${tribeid}/users/searchindex/uids.json`;
|
|
||||||
jsonfile.readFile( uidsIndex, function ( err, uids ) {
|
|
||||||
try {
|
|
||||||
if( rm ) {
|
|
||||||
delete uids[ user.UUID ];
|
|
||||||
} else {
|
|
||||||
uids[ user.UUID ] = [
|
|
||||||
user.LOGIN,
|
|
||||||
user.EMAIL,
|
|
||||||
user.PASSWORD,
|
|
||||||
user.ACCESSRIGHTS,
|
|
||||||
user.TOKEN
|
|
||||||
];
|
|
||||||
}
|
|
||||||
jsonfile.writeFile( uidsIndex, uids, {
|
|
||||||
spaces: 2
|
|
||||||
}, err => {
|
|
||||||
if( err ) console.log( err );
|
|
||||||
} );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'Gros pb de mise à jour Pagans.updateDatabase conflit des uids si ce reproduit passer en mode sync bloquant' );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
const emailsIndex = `${config.tribes}/${tribeid}/users/searchindex/emails.json`;
|
|
||||||
jsonfile.readFile( emailsIndex, function ( err, emails ) {
|
|
||||||
console.assert( config.loglevel == "quiet", 'emailss', emails );
|
|
||||||
try {
|
|
||||||
if( rm ) {
|
|
||||||
delete emails[ user.EMAIL ];
|
|
||||||
} else {
|
|
||||||
emails[ user.EMAIL ] = user.UUID;
|
|
||||||
}
|
|
||||||
jsonfile.writeFile( emailsIndex, emails, {
|
|
||||||
spaces: 2
|
|
||||||
}, err => {
|
|
||||||
if( err ) console.log( err );
|
|
||||||
} );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'Gros pb de mise à jour Pagans.updateDatabase conflit des emails' );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
const tokensIndex = `${config.tmp}/tokens.json`;
|
|
||||||
let tokens = {};
|
|
||||||
try {
|
|
||||||
tokens = jsonfile.readFileSync( tokensIndex );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'tokens.json not available' )
|
|
||||||
}
|
|
||||||
// tokens[user.UUID] = user.TOKEN;
|
|
||||||
tokens[ user.UUID ] = { TOKEN: user.TOKEN, ACCESSRIGHTS: user.ACCESSRIGHTS };
|
|
||||||
jsonfile.writeFileSync( tokensIndex, tokens, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
/*
|
|
||||||
jsonfile.readFile(tokensIndex, function(err, tokens) {
|
|
||||||
tokens[user.UUID] = user.TOKEN;
|
|
||||||
jsonfile.writeFile(tokensIndex, tokens, { spaces: 2 }, err => {
|
|
||||||
if (err) console.log(err);
|
|
||||||
});
|
|
||||||
});*/
|
|
||||||
console.groupEnd();
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Read the profil.json of an user
|
|
||||||
* @param {[type]} UUID ID of the user
|
|
||||||
* @param {[type]} tribeid tribeid
|
|
||||||
* @return {Promise} - Return a promise resolved with user data or rejected with an error
|
|
||||||
*/
|
|
||||||
// A S U P P R I M E R utiliser getinfousers pour recuperer des indexs de users
|
|
||||||
// en créer d'autres si necessaire
|
|
||||||
Pagans.getUserlist = ( header, filter, field ) => {
|
|
||||||
console.group( `getUserlist filter with filter ${filter} to get ${field}` );
|
|
||||||
/* const getuser = Pagans.getUser(header.xuuid, header.xtribeid);
|
|
||||||
if (getuser.status != 200)
|
|
||||||
return { status: getuser.status, data: getuser.payload };
|
|
||||||
const user = getuser.payload.data;
|
|
||||||
// console.log(user);
|
|
||||||
// check if update accessright allowed
|
|
||||||
// choose the level depending of ownby xuuid
|
|
||||||
let accessright = user.objectRights[header.xtribeid].users[0];
|
|
||||||
if (user.ownby.includes(header.xtribeid)) {
|
|
||||||
accessright = user.objectRights[header.xtribeid].users[1];
|
|
||||||
}
|
|
||||||
// Check update is possible at least user itself ownby itself
|
|
||||||
console.log(accessright);
|
|
||||||
console.log(accessright & 4);
|
|
||||||
if ((accessright & 4) != 4) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
data: { info: ['forbiddenAccess'], model: 'Pagans' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const Userlist = [];
|
|
||||||
glob.sync( `${config.tribes}/${header.xworkon}/users/*/profil.json` )
|
|
||||||
.forEach( f => {
|
|
||||||
const infouser = jsonfile.readFileSync( f );
|
|
||||||
// Ajouter filter et liste de field
|
|
||||||
if( filter != 'all' ) {
|
|
||||||
// decode filter et test
|
|
||||||
}
|
|
||||||
const info = {};
|
|
||||||
field.split( '______' )
|
|
||||||
.forEach( k => {
|
|
||||||
if( ![ 'password' ].includes( k ) ) info[ k ] = infouser[ k ];
|
|
||||||
} );
|
|
||||||
Userlist.push( info );
|
|
||||||
} );
|
|
||||||
// console.log('userlist', Userlist);
|
|
||||||
console.groupEnd();
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
data: Userlist
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Pagans.getinfoPagans = ( tribeid, accessrights, listindex ) => {
|
|
||||||
const info = {}
|
|
||||||
const object = 'users';
|
|
||||||
const indexs = listindex.split( '_' )
|
|
||||||
let access = true;
|
|
||||||
indexs.forEach( index => {
|
|
||||||
access = !( [ 'emails', 'logins', 'uids' ].includes( index ) && !( accessrights.data[ object ] && accessrights.data[ object ].includes( 'R' ) ) );
|
|
||||||
if( access && fs.existsSync( `${config.tribes}/${tribeid}/${object}/searchindex/${index}.json` ) ) {
|
|
||||||
info[ index ] = jsonfile.readFileSync( `${config.tribes}/${tribeid}/${object}/searchindex/${index}.json` )
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
console.log( info )
|
|
||||||
return { status: 200, data: { info: info } }
|
|
||||||
}
|
|
||||||
Pagans.getUser = ( UUID, tribeid, accessrights ) => {
|
|
||||||
console.assert( config.loglevel == "quiet", `getUser on ${UUID} for ${tribeid} with ${JSON.stringify(accessrights)}` );
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${tribeid}/users/${UUID}.json` ) ) {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
data: {
|
|
||||||
info: [ 'useridNotfound' ],
|
|
||||||
model: 'Pagans',
|
|
||||||
render: {
|
|
||||||
UUID,
|
|
||||||
tribeid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const user = jsonfile.readFileSync( `${config.tribes}/${tribeid}/users/${UUID}.json` );
|
|
||||||
let access = true;
|
|
||||||
//console.log("test accessrights.data['users'].includes('R')", accessrights.data['users'].includes('R'))
|
|
||||||
console.assert( config.loglevel == "quiet", 'accessrights', accessrights )
|
|
||||||
access = accessrights.users && ( accessrights.users.includes( 'R' ) || ( accessrights.users.includes( 'O' ) && user.OWNEDBY.includes( UUID ) ) );
|
|
||||||
if( access ) {
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
user: user,
|
|
||||||
model: "User",
|
|
||||||
info: [ "Authorizedtogetuserinfo" ]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
data: {
|
|
||||||
info: [ 'Forbidden' ],
|
|
||||||
model: 'Pagans',
|
|
||||||
render: {
|
|
||||||
UUID,
|
|
||||||
tribeid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Pagans.getUserIdFromEMAIL = ( tribeid, EMAIL ) => {
|
|
||||||
if( !checkdata.test.EMAIL( EMAIL ) ) {
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
data: {
|
|
||||||
info: [ 'ERREMAIL' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const emailsIndex = jsonfile.readFileSync( `${config.tribes}/${tribeid}/users/searchindex/emails.json` );
|
|
||||||
if( !emailsIndex.hasOwnProperty( EMAIL ) ) {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
data: {
|
|
||||||
info: [ 'userEMAILNotfound' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return emailsIndex[ EMAIL ];
|
|
||||||
};
|
|
||||||
Pagans.updateUserpassword = ( UUID, header, data ) => {
|
|
||||||
const getUser = Pagans.getUser( UUID, header.xtribeid, { users: 'W' } );
|
|
||||||
if( getUser.status == 200 ) {
|
|
||||||
const user = getUser.data.user;
|
|
||||||
// console.log('user exist', user);
|
|
||||||
const match = bcrypt.compareSync( data.password, user.PASSWORD );
|
|
||||||
if( !match ) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
data: {
|
|
||||||
info: [ 'checkCredentials' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// console.log('Credentials are matching!');
|
|
||||||
if( checkdata.test.password( {}, data.pswnew ) ) {
|
|
||||||
user.PASSWORD = bcrypt.hashSync( data.pswnew, config.saltRounds );
|
|
||||||
jsonfile.writeFileSync( `${config.tribes}/${header.xworkon}/users/${UUID}.json`, user, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
Pagans.updateDatabase( user, header.xworkon, false );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
info: [ 'successfulUpdate' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
data: {
|
|
||||||
info: [ 'pswTooSimple' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Pagans.createUser = ( header, data ) => {
|
|
||||||
/*
|
|
||||||
@input data={PUBKEY,EMAIL,LOGIN,UUID} check and create for header xworkon a user with generic password
|
|
||||||
*/
|
|
||||||
console.log( 'createUser on header.xworkon:' + header.xworkon + ' by user:' + header.xpaganid );
|
|
||||||
console.assert( config.loglevel == "quiet", 'with data:', data );
|
|
||||||
const ref = jsonfile.readFileSync( `${config.tribes}/${header.xworkon}/referentials/${header.xlang}/object/users.json` );
|
|
||||||
const logins = jsonfile.readFileSync( `${config.tribes}/${header.xworkon}/users/searchindex/logins.json` );
|
|
||||||
const LOGIN = Object.keys( logins );
|
|
||||||
console.assert( config.loglevel == "quiet", 'LOGIN list', LOGIN );
|
|
||||||
const emails = jsonfile.readFileSync( `${config.tribes}/${header.xworkon}/users/searchindex/emails.json` );
|
|
||||||
console.assert( config.loglevel == "quiet", 'emails', emails );
|
|
||||||
const EMAIL = Object.keys( emails );
|
|
||||||
console.assert( config.loglevel == "quiet", 'EMAIL list', EMAIL );
|
|
||||||
// list.UUID est forcement unique car on est en update et pas en create
|
|
||||||
if( !data.UUID ) data.UUID = UUID.v4();
|
|
||||||
// pour la logique de checkdata il faut passer le parametre
|
|
||||||
const check = checkdata.evaluate( {
|
|
||||||
list: {
|
|
||||||
LOGIN,
|
|
||||||
EMAIL,
|
|
||||||
UUID: []
|
|
||||||
}
|
|
||||||
}, ref, data );
|
|
||||||
console.assert( config.loglevel == "quiet", 'check & clean data before update ', check );
|
|
||||||
if( check.invalidefor.length > 0 ) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
data: {
|
|
||||||
model: 'Pagans',
|
|
||||||
info: check.invalidefor
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const clientConfig = jsonfile.readFileSync( `${config.tribes}/${header.xworkon}/clientconf.json` );
|
|
||||||
const user = check.data;
|
|
||||||
user.DATE_CREATE = new Date()
|
|
||||||
.toISOString();
|
|
||||||
user.PASSWORD = bcrypt.hashSync( clientConfig.genericpsw, config.saltRounds );
|
|
||||||
user.OWNBY = [ data.UUID ];
|
|
||||||
user.OBJECT = 'users';
|
|
||||||
// set le minimum de droit sur l'objet users dont il est le Owner
|
|
||||||
if( data.ACCESSRIGHTS ) {
|
|
||||||
user.ACCESSRIGHTS = data.ACCESSRIGHTS
|
|
||||||
} else {
|
|
||||||
user.ACCESSRIGHTS = { app: {}, data: {} };
|
|
||||||
}
|
|
||||||
user.ACCESSRIGHTS.data[ header.xworkon ] = { users: "O" };
|
|
||||||
jsonfile.writeFileSync( `${config.tribes}/${header.xworkon}/users/${user.UUID}.json`, user, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
Pagans.updateDatabase( user, header.xworkon, false );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
uuid: user.UUID,
|
|
||||||
info: [ 'successfulCreate' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Pagans.updateUser = ( UUID, header, data ) => {
|
|
||||||
console.log( 'updateUser UUID:' + UUID + ' on header.xworkon:' + header.xworkon + ' by user' + header.xpaganid );
|
|
||||||
// console.log('header', header);
|
|
||||||
console.assert( config.loglevel == "quiet", 'with data', data );
|
|
||||||
const getuser = Pagans.getUser( UUID, header.xworkon, { users: 'R' } );
|
|
||||||
if( getuser.status != 200 ) return {
|
|
||||||
status: getuser.status,
|
|
||||||
data: getuser.data.user
|
|
||||||
};
|
|
||||||
const user = getuser.data.user;
|
|
||||||
/*
|
|
||||||
let userconnected = user;
|
|
||||||
if (UUID != header.xuuid) {
|
|
||||||
// mean connected user want to change other user
|
|
||||||
const getuserconnected = Pagans.getUser(header.xuuid, header.xtribeid);
|
|
||||||
userconnected = getuserconnected.payload.data;
|
|
||||||
}
|
|
||||||
console.log('user to update', user);
|
|
||||||
console.log('user connected that request update', userconnected);
|
|
||||||
|
|
||||||
// check if update accessright allowed
|
|
||||||
// choose the level depending of ownby xuuid
|
|
||||||
let accessright = userconnected.objectRights[header.xworkon].users[0];
|
|
||||||
if (user.ownby.includes(header.xuuid)) {
|
|
||||||
accessright = userconnected.objectRights[header.xworkon].users[1];
|
|
||||||
}
|
|
||||||
// Check update is possible at least user itself ownby itself
|
|
||||||
console.log(accessright);
|
|
||||||
console.log(accessright & 2);
|
|
||||||
if ((accessright & 2) != 2) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
data: { info: ['forbiddenAccess'], model: 'Pagans' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const ref = jsonfile.readFileSync( `${config.tribes}/${header.xworkon}/referentials/object/users_${
|
|
||||||
header.xlang
|
|
||||||
}.json` );
|
|
||||||
const logins = jsonfile.readFileSync( `${config.tribes}/${header.xworkon}/users/searchindex/logins.json` );
|
|
||||||
const LOGIN = Object.keys( logins )
|
|
||||||
.filter( l => logins[ l ] != user.UUID );
|
|
||||||
// console.log( 'LOGIN list', LOGIN );
|
|
||||||
const emails = jsonfile.readFileSync( `${config.tribes}/${header.xworkon}/users/searchindex/emails.json` );
|
|
||||||
// console.log( 'emails', emails );
|
|
||||||
const EMAIL = Object.keys( emails )
|
|
||||||
.filter( e => emails[ e ] != user.UUID );
|
|
||||||
// console.log( 'EMAIL list', EMAIL );
|
|
||||||
// list.UUID est forcement unique car on est en update et pas en create
|
|
||||||
// pour la logique de checkdata il faut passer le parametre
|
|
||||||
const check = checkdata.evaluate( {
|
|
||||||
profil: user[ 'apps' + header.xworkon + 'profil' ],
|
|
||||||
list: {
|
|
||||||
LOGIN,
|
|
||||||
EMAIL,
|
|
||||||
UUID: []
|
|
||||||
}
|
|
||||||
}, ref, data );
|
|
||||||
if( check.invalidefor.length > 0 ) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
data: {
|
|
||||||
model: 'Pagans',
|
|
||||||
info: check.invalidefor,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
data = check.data;
|
|
||||||
let saveuser = false;
|
|
||||||
let updateDatabase = false;
|
|
||||||
Object.keys( data )
|
|
||||||
.forEach( k => {
|
|
||||||
//console.log( user[ k ] )
|
|
||||||
//console.log( data[ k ] )
|
|
||||||
//console.log( '---' )
|
|
||||||
if( user[ k ] != data[ k ] ) {
|
|
||||||
user[ k ] = data[ k ];
|
|
||||||
saveuser = true;
|
|
||||||
if( [ 'TOKEN', 'LOGIN', 'EMAIL' ].includes( k ) ) updateDatabase = true;
|
|
||||||
// attention si LOGIN ou EMAIL change il faut checker qu il n existe pas dejà risque de conflit
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
if( saveuser ) {
|
|
||||||
//console.log( 'mise à jour user profile.json' );
|
|
||||||
if( data.TOKEN ) {
|
|
||||||
user.date_lastLOGIN = new Date()
|
|
||||||
.toISOString();
|
|
||||||
} else {
|
|
||||||
user.date_update = new Date()
|
|
||||||
.toISOString();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
jsonfile.writeFileSync( `${config.tribes}/${header.xworkon}/users/${UUID}.json`, user, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
//console.log( 'declenche updatabase', updateDatabase )
|
|
||||||
if( updateDatabase ) {
|
|
||||||
// mean index have to be updated
|
|
||||||
Pagans.updateDatabase( user, header.xworkon, false );
|
|
||||||
console.assert( config.loglevel == "quiet", 'MISE A JOUR DU TOKEN ou de l\'EMAIL ou du LOGIN' );
|
|
||||||
}
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'ERRRRR need to understand update impossible of user: ' + UUID + ' in domain:' + header.xworkon + ' from user ' + header.xpaganid + ' of domain:' + header.xtribe );
|
|
||||||
console.log( 'with data :', data );
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
data: {
|
|
||||||
info: [ 'failtoWritefs' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
info: [ 'successfulUpdate' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Pagans.deleteUser = ( UUID, header ) => {
|
|
||||||
// Delete remove from users object UUID and update index
|
|
||||||
// Activity is not deleted => means some activity can concern an UUID that does not exist anymore.
|
|
||||||
// update index
|
|
||||||
const infouser = jsonfile.readFileSync( `${config.tribes}/${header.xworkon}/users/${UUID}.json` );
|
|
||||||
Pagans.updateDatabase( infouser, header.xworkon, true );
|
|
||||||
fs.removeSync( `${config.tribes}/${header.xworkon}/users/${UUID}.json` );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
info: [ 'successfulDelete' ],
|
|
||||||
modedl: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
@header { xtribeid: client domain name data
|
|
||||||
A VERIFIER inutile ,xworkon: client domain name where user is store and where data are stored
|
|
||||||
,xuuid: 1 if unknown else if user id auhtneticated
|
|
||||||
,xlanguage: langue used fr en return referential in this lang
|
|
||||||
,xauth: TOKEN valid for 24h'}
|
|
||||||
workon=xtribeid in an app client usage
|
|
||||||
in case of xtribeid=mymaidigit,
|
|
||||||
if workon==mymaildigit => admin role on any xworkon
|
|
||||||
else adlin role only in xworkon specified
|
|
||||||
@body {LOGIN: password:}
|
|
||||||
return {status:200, data:{data:{TOKEN,UUID}}}
|
|
||||||
return {status:401,data:{info:[code list], model: referential}}
|
|
||||||
*/
|
|
||||||
Pagans.loginUser = ( header, body, checkpsw ) => {
|
|
||||||
// On recupere tribeid du LOGIN
|
|
||||||
// ATTENTION xworkon peut être different du user xtribeid
|
|
||||||
//(cas d'un user qui a des droits sur un autre domain et qui travail sur cet autre domain)
|
|
||||||
// les function Pagans utilise le domain de travail xWorkon
|
|
||||||
// il faut donc modifier le header au moment du LOGIN
|
|
||||||
// pour que l'update du user au moment du LOGIN concerne bien le bon domain
|
|
||||||
header.xworkon = header.xtribe
|
|
||||||
const LOGINdom = jsonfile.readFileSync( `${config.tmp}/loginsglob.json` );
|
|
||||||
console.assert( config.loglevel == "quiet", LOGINdom )
|
|
||||||
console.assert( config.loglevel == "quiet", body )
|
|
||||||
if( !LOGINdom[ body.LOGIN ] ) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
data: { info: [ 'LoginDoesNotExist' ], model: 'Pagans' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const logins = jsonfile.readFileSync( `${config.tribes}/${LOGINdom[body.LOGIN]}/users/searchindex/logins.json` );
|
|
||||||
if( !Object.keys( logins )
|
|
||||||
.includes( body.LOGIN ) ) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
data: {
|
|
||||||
info: [ 'LOGINDoesNotExist' ],
|
|
||||||
model: 'Pagans',
|
|
||||||
moreinfo: `Le login ${body.LOGIN} does not exist for tribeid ${LOGINdom[body.LOGIN]}`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Load user
|
|
||||||
const uid = logins[ body.LOGIN ];
|
|
||||||
const getUser = Pagans.getUser( uid, LOGINdom[ body.LOGIN ], { users: 'R' } );
|
|
||||||
console.log( 'getPagans', getUser )
|
|
||||||
if( getUser.status != 200 ) {
|
|
||||||
return { status: 200, data: { model: 'Pagans', user: getUser.data.user } };
|
|
||||||
}
|
|
||||||
const user = getUser.data.user;
|
|
||||||
console.log( 'user', user )
|
|
||||||
if( checkpsw ) {
|
|
||||||
const match = bcrypt.compareSync( body.PASSWORD, user.PASSWORD );
|
|
||||||
if( !match ) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
data: {
|
|
||||||
info: [ 'checkCredentials' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Mise à jour user pour app LOGIN
|
|
||||||
user.tribeid = LOGINdom[ body.LOGIN ]
|
|
||||||
user.TOKEN = jwt.encode( {
|
|
||||||
expiration: moment()
|
|
||||||
.add( 1, 'day' ),
|
|
||||||
UUID: user.UUID
|
|
||||||
}, config.jwtSecret );
|
|
||||||
// on met à jour le header qui authentifie le user connecte
|
|
||||||
header.xtribe = LOGINdom[ body.LOGIN ]
|
|
||||||
header.xpaganid = user.UUID;
|
|
||||||
header.xauth = user.TOKEN;
|
|
||||||
// On modifie xworkon car au LOGIN on est forcemenet identifiable sur tribeid
|
|
||||||
// xworkon est utiliser pour travailler sur un environnement client different de celui
|
|
||||||
// de l'appartenace du user
|
|
||||||
header.xworkon = LOGINdom[ body.LOGIN ];
|
|
||||||
const majuser = Pagans.updateUser( user.UUID, header, {
|
|
||||||
UUID: user.UUID,
|
|
||||||
TOKEN: user.TOKEN
|
|
||||||
} );
|
|
||||||
// Enleve info confidentiel
|
|
||||||
delete user.PASSWORD;
|
|
||||||
if( user.ACCESSRIGHTS.data[ "Alltribeid" ] ) {
|
|
||||||
//cas admin on transforme les droits sur tous les tribeid existant
|
|
||||||
const newaccessrightsdata = {}
|
|
||||||
jsonfile.readFileSync( `${config.tribes}/tribeids.json` )
|
|
||||||
.forEach( cid => {
|
|
||||||
newaccessrightsdata[ cid ] = user.ACCESSRIGHTS.data[ "Alltribeid" ]
|
|
||||||
} )
|
|
||||||
user.ACCESSRIGHTS.data = newaccessrightsdata;
|
|
||||||
}
|
|
||||||
// on recupere le menu de l app qui demande le LOGIN si existe dans user.ACCESSRIGHTS.app[]
|
|
||||||
// header['X-app'] = tribeid:Projet pour récuperer le menu correspondant
|
|
||||||
console.assert( config.loglevel == "quiet", "header.xapp", header.xapp )
|
|
||||||
console.assert( config.loglevel == "quiet", "user.ACCESSRIGHTS.app[header.xapp]", user.ACCESSRIGHTS.app[ header.xapp ] );
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
data: {
|
|
||||||
model: "Pagans",
|
|
||||||
info: [ 'loginSuccess' ],
|
|
||||||
user: user
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Pagans.getlinkwithoutpsw = async ( EMAIL, header ) => {
|
|
||||||
// check le domain d'appartenance de l'eamail dans /tribes/emailsglob.json={email:cleintId}
|
|
||||||
// on remplace le header.xtribeid
|
|
||||||
const domforemail = jsonfile.readFileSync( `${config.tribes}/emailsglob.json`, 'utf-8' );
|
|
||||||
if( domforemail[ EMAIL ] ) {
|
|
||||||
header.xtribe = domforemail[ EMAIL ]
|
|
||||||
} else {
|
|
||||||
return { status: 404, info: { model: 'Pagans', info: [ 'emailnotfound' ], moreinfo: "email does not exist in /emailsglob.json" } };
|
|
||||||
}
|
|
||||||
// recupere le uuid du user dans /tribes/tribeid/users/searchindex/emails.json
|
|
||||||
// puis l'ensemble des info des user du domain /uuids.json
|
|
||||||
// infoforuuid[uuidforemail[EMAIL]] permet de récupérer toutes info du user, droit, etc...
|
|
||||||
const uuidforemail = jsonfile.readFileSync( `${config.tribes}/${header.xtribe}/users/searchindex/emails.json`, 'utf8' );
|
|
||||||
const infoforuuid = jsonfile.readFileSync( `${config.tribes}/${header.xtribe}/users/searchindex/uids.json`, 'utf8' );
|
|
||||||
// On recupere le modele d'email appemailinfo qui doit être présent dans clientconf.json
|
|
||||||
let confdom = jsonfile.readFileSync( `${config.tribes}/${header.xtribe}/clientconf.json`, 'utf8' );
|
|
||||||
let checkinfomail = "";
|
|
||||||
if( !confdom.appemailinfo ) {
|
|
||||||
checkinfomail += ' Erreur de clientconfig il manque un objet appemailinfo pour poursuivre';
|
|
||||||
}
|
|
||||||
if( checkinfomail != "" ) {
|
|
||||||
console.log( `Pb de config pour ${header.xtribe} ${checkinfomail} ` )
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
info: {
|
|
||||||
model: 'Pagans',
|
|
||||||
info: [ 'objectmissingclientconf' ],
|
|
||||||
moreinfo: checkinfomail
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let simulelogin;
|
|
||||||
try {
|
|
||||||
simulelogin = await Pagans.loginUser( header, {
|
|
||||||
LOGIN: infoforuuid[ uuidforemail[ EMAIL ] ][ 0 ],
|
|
||||||
PASSWORD: ""
|
|
||||||
}, false );
|
|
||||||
console.log( 'info simulelogin', simulelogin )
|
|
||||||
} catch ( err ) {
|
|
||||||
return {
|
|
||||||
status: 501,
|
|
||||||
info: {
|
|
||||||
model: 'Pagans',
|
|
||||||
info: [ 'loginImpossible' ],
|
|
||||||
moreinfo: "Impossible de se loger avec " + infoforuuid[ uuidforemail[ EMAIL ] ][ 0 ]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const url = `${config.rootURL}?xauth=${simulelogin.data.TOKEN}&xuuid=${simulelogin.data.UUID}&xtribeid=${simulelogin.data.tribeid}&xworkOn=${header.xworkon}&xlang=${header.xlang}`
|
|
||||||
//console.log('envoi email avec' + url)
|
|
||||||
confdom.appemailinfo.msg.destperso = [ {} ];
|
|
||||||
confdom.appemailinfo.msg.destperso[ 0 ].email = EMAIL;
|
|
||||||
confdom.appemailinfo.msg.destperso[ 0 ].subject = "Lien de réinitialisation valable 1h"
|
|
||||||
confdom.appemailinfo.msg.destperso[ 0 ].titre = "Vous avez oublier votre mot de passe"
|
|
||||||
confdom.appemailinfo.msg.destperso[ 0 ].texte = `
|
|
||||||
<p style='color: #999999; font-size: 16px; line-height: 24px; margin: 0;text-align:justify;'>
|
|
||||||
Bonjour,<br> Vous nous avez signalé que vous avez oublié votre mot de passe pour cette email.
|
|
||||||
Avec le lien suivant vous allez pouvoir utiliser votre interface 1h maximum.
|
|
||||||
<a href="${url}">Clicker ICI</a>.<br>
|
|
||||||
Nous vous conseillons de changer votre mot de passe.</p>
|
|
||||||
`
|
|
||||||
//console.log('envoi header :', header);
|
|
||||||
Outputs.sendMailcampain( confdom.appemailinfo.msg, header );
|
|
||||||
console.log( confdom.appemailinfo );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
info: {
|
|
||||||
model: 'Pagans',
|
|
||||||
info: [ 'emailsentforReinit' ],
|
|
||||||
moreinfo: 'EMAIL sent with unique link ' + url
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
module.exports = Pagans;
|
|
@ -1,416 +0,0 @@
|
|||||||
const glob = require( 'glob' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
|
|
||||||
const Referentials = {};
|
|
||||||
/*
|
|
||||||
Manage Referential object data
|
|
||||||
|
|
||||||
each object is compose of a list of fields
|
|
||||||
each fields have it owns structur and check
|
|
||||||
those common rules allow to be used to manage:
|
|
||||||
- forms (creation to collect data)
|
|
||||||
- data check
|
|
||||||
- data access rights
|
|
||||||
common referential data stored in /data/shared/referential/
|
|
||||||
|
|
||||||
*/
|
|
||||||
Referentials.clientconf = ( xworkOn, listkey ) => {
|
|
||||||
/*
|
|
||||||
Retourne les info d'un clientconf.json sur une liste de [cle]
|
|
||||||
*/
|
|
||||||
let conf = {};
|
|
||||||
let dataconf = {};
|
|
||||||
//console.log( `${config.tribes}/${xworkOn}/clientconf.json` )
|
|
||||||
try {
|
|
||||||
conf = fs.readJsonSync( `${config.tribes}/${xworkOn}/clientconf.json` );
|
|
||||||
// remove information notrelevant for
|
|
||||||
[ 'emailFrom', 'emailClient', 'emailCc', 'commentkey', 'clezoomprivate', 'stripekeyprivate', 'genericpsw' ].forEach( c => {
|
|
||||||
delete conf[ c ];
|
|
||||||
} );
|
|
||||||
listkey.forEach( k => dataconf[ k ] = conf[ k ] )
|
|
||||||
//console.log( 'dataconf', dataconf )
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'Attention demande sur clienId inconnu ' + xworkOn );
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
data: dataconf
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Referentials.clientconfglob = () => ( {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
data: fs.readJsonSync( `${config.tmp}/clientconfglob.json` )
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
Referentials.getref = ( origin, source, ref, xworkOn, xlang ) => {
|
|
||||||
// If request origin then send back referential with all language in it
|
|
||||||
// if not origin or json source return by language
|
|
||||||
let referent = {};
|
|
||||||
let src;
|
|
||||||
if( origin && [ 'object', 'data' ].includes( source ) ) {
|
|
||||||
src = `${config.tribes}/${xworkOn}/referentials/${source}/${ref}.json`
|
|
||||||
} else {
|
|
||||||
src = `${config.tribes}/${xworkOn}/referentials/${source}/${ref}_${xlang}.json`;
|
|
||||||
}
|
|
||||||
//console.log( src )
|
|
||||||
try {
|
|
||||||
referent = fs.readJsonSync( src );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( `Request ${src} does not exist ` );
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
data: referent
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Referentials.putref = ( source, name, xworkOn, data ) => {
|
|
||||||
/*
|
|
||||||
We get a referential, we have 3 kinds of sources:
|
|
||||||
* data = [{uuid,DESC:{fr:,en,},DESCLONG:{fr:,en:}, other field}]
|
|
||||||
only DESC and DESCLONG have a translation
|
|
||||||
* json = are full complex object in language name_lg.json
|
|
||||||
* object = [{uuid,DESC:{fr,en},DESCLONG:{fr,en}},tpl,}]
|
|
||||||
Difference between data and object is that object defines rule to manage an object, and how to create a forms to get data each data is saved in one folder object/uuid.json and have to respect the corresponding object referentials definition.
|
|
||||||
|
|
||||||
For data and object it is possible only to put a file with all language available.
|
|
||||||
When store this script erase with the new file per _lg
|
|
||||||
|
|
||||||
name for source=json must end by _lg
|
|
||||||
*/
|
|
||||||
//console.log( data )
|
|
||||||
const pat = /.*_..\.json$/;
|
|
||||||
const file = `${config.tribes}/${xworkOn}/referentials/${source}/${name}.json`
|
|
||||||
if( [ 'object', 'data' ].includes( source ) ) {
|
|
||||||
if( pat.test( name ) ) {
|
|
||||||
return { status: 404, payload: { model: 'Referentials', info: [ 'nameunconsistent' ], moreinfo: "can not be update with one lang need a full file with language for object or data" } }
|
|
||||||
} else {
|
|
||||||
fs.outputJsonSync( file, data );
|
|
||||||
return Refernetials.update( xworkOn, source, name )
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if( !pat.test( name ) ) {
|
|
||||||
return { status: 404, payload: { model: 'Referentials', info: [ 'nameunconsistent' ], moreinfo: "can not be update without a lang _lg.json a referential json" } }
|
|
||||||
} else {
|
|
||||||
fs.outputJsonSync( file, data );
|
|
||||||
return { status: 200, payload: { model: 'Referentials', info: [ 'successfull' ], moreinfo: "ref json updated " } }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Referentials.updatefull = ( tribeid ) => {
|
|
||||||
let err = "";
|
|
||||||
let nbrefupdate = 0;
|
|
||||||
const pat = /.*_..\.json$/;
|
|
||||||
[ 'object', 'data' ].forEach( o => {
|
|
||||||
glob.sync( `${config.tribes}/${tribeid}/referentials/${o}/*.json` )
|
|
||||||
.forEach( f => {
|
|
||||||
if( !pat.test( f ) ) {
|
|
||||||
const res = Referentials.update( tribeid, o, path.basename( f, '.json' ) );
|
|
||||||
if( res.status != 200 ) {
|
|
||||||
err += `Error on ${o}/${path.basename(f)}`
|
|
||||||
} else {
|
|
||||||
nbrefupdate += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
if( err != "" ) {
|
|
||||||
return { status: 500, payload: { info: [ 'Errupdateref' ], model: "Referentials", moreinfo: err } }
|
|
||||||
}
|
|
||||||
return { status: 200, payload: { info: [ 'Success' ], model: "Referentials", moreinfo: `Number of object and data ref updated: ${nbrefupdate}` } }
|
|
||||||
};
|
|
||||||
|
|
||||||
Referentials.inittribeid = () => {
|
|
||||||
console.log( "Clientconf list for this server", `${config.tribes}/**/clientconf.json` );
|
|
||||||
const TribesGlobalConfig = glob.sync( `${config.tribes}/**/clientconf.json` )
|
|
||||||
.map( f => fs.readJsonSync( f ) );
|
|
||||||
// store global conf for sharing to other api
|
|
||||||
fs.outputJsonSync( `${config.tmp}/clientconfglob.json`, TribesGlobalConfig, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
return { status: 200, payload: { moreinfo: TribesGlobalConfig } }
|
|
||||||
}
|
|
||||||
Referentials.generetribeids = () => {
|
|
||||||
const tribeids = [];
|
|
||||||
fs.readJsonSync( `${config.tmp}/clientconfglob.json` )
|
|
||||||
.forEach( c => {
|
|
||||||
if( !tribeids.includes( c.tribeid ) ) tribeids.push( c.tribeid );
|
|
||||||
} );
|
|
||||||
fs.outputJsonSync( `${config.tmp}/tribeids.json`, tribeids );
|
|
||||||
console.log( `update ${config.tribes}/tribeids` );
|
|
||||||
return tribeids;
|
|
||||||
}
|
|
||||||
Referentials.genereallowedDOM = () => {
|
|
||||||
const confglob = fs.readJsonSync( `${config.tmp}/clientconfglob.json` );
|
|
||||||
let allDom = [];
|
|
||||||
confglob.forEach( c => {
|
|
||||||
c.allowedDOMs.forEach( d => {
|
|
||||||
if( !allDom.includes( d ) ) allDom = allDom.concat( d );
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
return allDom;
|
|
||||||
};
|
|
||||||
/* A voir si encore necessaire pour générer un environnement identique
|
|
||||||
sur un autre server
|
|
||||||
|
|
||||||
// Génére les domaines s'ils n'existe pas dans le repertoire
|
|
||||||
function genereEnvClient() {
|
|
||||||
const confglob = fs.readJsonSync(
|
|
||||||
`${config.sharedData}/clientconfglob.json`
|
|
||||||
);
|
|
||||||
confglob.forEach(function(c) {
|
|
||||||
config.tribeidsConf[c.tribeid] = c;
|
|
||||||
if (c.allowedURLs) {
|
|
||||||
c.allowedURLs.forEach(u => {
|
|
||||||
if (!config.allowedURLs.includes(u)) {
|
|
||||||
config.allowedURLs.push(u);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log('erreur de fichier config d\'un site pour ', c);
|
|
||||||
}
|
|
||||||
// GLOBAL Tribes IDS INDEX
|
|
||||||
maketribeidsIndex();
|
|
||||||
if (!fs.existsSync(`${config.tribes}/${c.tribeid}`)) {
|
|
||||||
const execSync = require('child_process').execSync;
|
|
||||||
execSync(`cp -r ${config.tribes}/modele ${config.tribes}/${c.tribeid}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
Referentials.update = ( tribeid, source, name ) => {
|
|
||||||
/*
|
|
||||||
Replace for each language the referential name for a tribeid
|
|
||||||
After each update the version number is incremented by 1 in clientconf.json
|
|
||||||
*/
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${tribeid}/referentials/${source}/${name}.json` ) ) {
|
|
||||||
return { status: 500, payload: { info: [ "unknownRef" ], model: "Referentials", moreinfo: `file does not exist ${config.tribes}/${tribeid}/referentials/${source}/${name}.json` } }
|
|
||||||
};
|
|
||||||
|
|
||||||
const clientconf = fs.readJsonSync( `${config.tribes}/${tribeid}/clientconf.json` );
|
|
||||||
if( !clientconf.langueReferential ) {
|
|
||||||
return { status: 500, payload: { info: [ "missingConf" ], model: "Referentials", moreinfo: ` ${config.tribes}/${tribeid}/clientconf.json does not contain langueReferential array` } }
|
|
||||||
}
|
|
||||||
const ref = fs.readJsonSync( `${config.tribes}/${tribeid}/referentials/${source}/${name}.json` );
|
|
||||||
|
|
||||||
clientconf.langueReferential.forEach( lg => {
|
|
||||||
//manage translation
|
|
||||||
let refnew = [];
|
|
||||||
|
|
||||||
refnew = [];
|
|
||||||
ref.forEach( d => {
|
|
||||||
if( d.DESC ) d.DESC = d.DESC[ lg ];
|
|
||||||
if( d.DESCLONG ) d.DESCLONG = d.DESCLONG[ lg ];
|
|
||||||
if( d.INFO ) d.INFO = d.INFO[ lg ];
|
|
||||||
if( d.desc ) d.desc = d.desc[ lg ];
|
|
||||||
if( d.desclong ) d.desclong = d.desclong[ lg ];
|
|
||||||
if( d.info ) d.info = d.info[ lg ];
|
|
||||||
if( d.placeholder ) d.placeholder = d.placeholder[ lg ];
|
|
||||||
refnew.push( d )
|
|
||||||
} )
|
|
||||||
//save new ref in language
|
|
||||||
//console.log( "New ref", refnew )
|
|
||||||
console.log( `Update referentials per lg ${config.tribes}/${tribeid}/referentials/${source}/${name}_${lg}.json` )
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${tribeid}/referentials/${source}/${name}_${lg}.json`, refnew, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
//upgrade version number
|
|
||||||
if( !clientconf.referentials ) clientconf.referentials = {};
|
|
||||||
if( !clientconf.referentials[ source ] ) clientconf.referentials[ source ] = {};
|
|
||||||
if( !clientconf.referentials[ source ][ name ] ) clientconf.referentials[ source ][ name ] = { version: 0 };
|
|
||||||
clientconf.referentials[ source ][ name ].version += 1;
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${tribeid}/clientconf.json`, clientconf, 'utf8' );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'successUpdate' ],
|
|
||||||
model: "Referentials",
|
|
||||||
moreinfo: `${name} updated`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//console.log( Referentials.update( 'apixtribe', "object", "user" ) )
|
|
||||||
|
|
||||||
Referentials.genereobjet = ( tribeid, destination, tplmustache, objet, filtre ) => {
|
|
||||||
/* @TODO
|
|
||||||
Genere des objets d'agregat
|
|
||||||
@tribeid = data/tribee/ identifiant client
|
|
||||||
@destinations = [] of destination
|
|
||||||
@tplmustache = fichier mustache de mise en page
|
|
||||||
@objet = nom d'objet contact, companies, items, users
|
|
||||||
@filtre = fonction a executer
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
//////// EN DESSOUS DE CETTE LIGNE A SUPPRIMER
|
|
||||||
|
|
||||||
/*
|
|
||||||
Le principe consistait à partager des referentiels dans shareddataLa gestion est trop compliqué => le principe chaque client duplique un referentiel pour que les app qui s'appuie sur des composants communs puissent fonctionner
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*Referentials.genereClientObjectASUPP = () => {
|
|
||||||
const confglob = fs.readJsonSync( `${config.tmp}/clientconfglob.json` );
|
|
||||||
// Check and update folder and data per lang
|
|
||||||
// c as tribeid
|
|
||||||
confglob.forEach( ( c, postribeid ) => {
|
|
||||||
//check folder are well create
|
|
||||||
const lstfolder = [ 'actions', 'actions/done', 'actions/todo', 'cards', 'logs', 'orders', 'orders/reservation', 'orders/purchase', 'orders/contact', 'public', 'public/reservation', 'tags', 'tags/stats', 'tags/archives', 'tags/hits', 'tags/imgtg', 'users' ];
|
|
||||||
lstfolder.forEach( fol => {
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${c.tribeid}/${fol}` ) ) {
|
|
||||||
fs.mkdirSync( `${config.tribes}/${c.tribeid}/${fol}` );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
if( c.referentials && !c.langue ) { console.log( `ERREUR referentials mais pas de langue:[] pour ${c.tribeid}/clientconf.json` ) }
|
|
||||||
if( c.referentials && c.langue ) {
|
|
||||||
let majclientconf = false;
|
|
||||||
// Create and check Object structure
|
|
||||||
Object.keys( c.referentials.object )
|
|
||||||
.forEach( o => {
|
|
||||||
// if object exist in shared then it merge sharedObject and domain referential object
|
|
||||||
let objfull = [];
|
|
||||||
const objshared = `${config.sharedData}/referentials/dataManagement/object/${o}.json`;
|
|
||||||
if( fs.existsSync( objshared ) ) {
|
|
||||||
objfull = objfull.concat( fs.readJsonSync( objshared ) );
|
|
||||||
}
|
|
||||||
const objdomain = `${config.tribes}/${c.tribeid}/referentials/dataManagement/object/${o}.json`;
|
|
||||||
if( fs.existsSync( objdomain ) ) {
|
|
||||||
objfull = objfull.concat( fs.readJsonSync( objdomain ) );
|
|
||||||
}
|
|
||||||
c.langue.forEach( lg => {
|
|
||||||
const objfulllg = objfull.map( field => {
|
|
||||||
if( field.DESC ) field.DESC = field.DESC[ lg ];
|
|
||||||
if( field.DESCLONG ) field.DESCLONG = field.DESCLONG[ lg ];
|
|
||||||
return field;
|
|
||||||
} );
|
|
||||||
const objectdomlg = `${config.tribes}/${c.tribeid}/referentials/${lg}/object/${o}.json`;
|
|
||||||
let savedObject = {};
|
|
||||||
if( fs.existsSync( objectdomlg ) ) {
|
|
||||||
savedObject = fs.readJsonSync( objectdomlg );
|
|
||||||
}
|
|
||||||
// TODO Always true change later to update only if needded
|
|
||||||
if( !fs.existsSync( objectdomlg ) || objfulllg.length !== savedObject.length || 1 == 1 ) {
|
|
||||||
fs.outputJsonSync( objectdomlg, objfulllg, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
confglob[ postribeid ].referentials.object[ o ].version += 1;
|
|
||||||
majclientconf = true;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
// datafile
|
|
||||||
Object.keys( c.referentials.data )
|
|
||||||
.forEach( d => {
|
|
||||||
// if object exist in shared then it merge sharedObject and domain referential object
|
|
||||||
// console.log(c.tribeid + '--' + d);
|
|
||||||
let datafull = [];
|
|
||||||
const datashared = `${
|
|
||||||
config.sharedData
|
|
||||||
}/referentials/dataManagement/data/${d}.json`;
|
|
||||||
if( fs.existsSync( datashared ) ) {
|
|
||||||
datafull = datafull.concat( fs.readJsonSync( datashared ) );
|
|
||||||
}
|
|
||||||
const datadomain = `${config.tribes}/${
|
|
||||||
c.tribeid
|
|
||||||
}/referentials/dataManagement/data/${d}.json`;
|
|
||||||
if( fs.existsSync( datadomain ) ) {
|
|
||||||
datafull = datafull.concat( fs.readJsonSync( datadomain ) );
|
|
||||||
}
|
|
||||||
/* const defdata = `${config.tribes}/${
|
|
||||||
c.tribeid
|
|
||||||
}/referentials/dataManagement/data/${d}.json`;
|
|
||||||
*/
|
|
||||||
// for each Langues => generate fr.obj and compare it with existing file
|
|
||||||
// if diff then => upgrade version number in clientconf
|
|
||||||
// console.log(datafull);
|
|
||||||
// this could be improved by usind d.meta wich is the object that DESCribe this data
|
|
||||||
/* c.langue.forEach( lg => {
|
|
||||||
let meta;
|
|
||||||
if( c.referentials.data[ d ].meta ) {
|
|
||||||
meta = fs.readJsonSync( `${config.tribes}/${c.tribeid}/referentials/${lg}/object/${
|
|
||||||
c.referentials.data[d].meta
|
|
||||||
}.json` );
|
|
||||||
}
|
|
||||||
let datalg;
|
|
||||||
const datafulllg = datafull.map( tup => {
|
|
||||||
datalg = {};
|
|
||||||
meta.forEach( ch => {
|
|
||||||
if( tup[ ch.idfield ] ) {
|
|
||||||
if( ch.multilangue ) {
|
|
||||||
datalg[ ch.idfield ] = tup[ ch.idfield ][ lg ];
|
|
||||||
} else {
|
|
||||||
datalg[ ch.idfield ] = tup[ ch.idfield ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
return datalg;
|
|
||||||
} );
|
|
||||||
// lit le fichier correspondant et le compare si différent le sauvegarde
|
|
||||||
// stocke l'information d'upgrade d ela version
|
|
||||||
const datadomlg = `${config.tribes}/${
|
|
||||||
c.tribeid
|
|
||||||
}/referentials/${lg}/data/${d}.json`;
|
|
||||||
let saveddata = {};
|
|
||||||
if( fs.existsSync( datadomlg ) ) {
|
|
||||||
saveddata = fs.readJsonSync( datadomlg );
|
|
||||||
}
|
|
||||||
// Condition to improve
|
|
||||||
// TODO always change file to improvelater by detecting real change.
|
|
||||||
if( !fs.existsSync( datadomlg ) || datafulllg.length != saveddata.length || 1 == 1 ) {
|
|
||||||
fs.outputJsonSync( datadomlg, datafulllg, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
confglob[ postribeid ].referentials.data[ d ].version += 1;
|
|
||||||
majclientconf = true;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
// json file that have to start with lg {lg:'':{json }}
|
|
||||||
Object.keys( c.referentials.json )
|
|
||||||
.forEach( j => {
|
|
||||||
// if object exist in shared then it merge sharedObject and domain referential object
|
|
||||||
// console.log(c.tribeid + '--' + d);
|
|
||||||
let jsonfull = [];
|
|
||||||
const jsondomain = `${config.tribes}/${c.tribeid}/referentials/dataManagement/json/${j}.json`;
|
|
||||||
if( fs.existsSync( jsondomain ) ) {
|
|
||||||
jsonfull = fs.readJsonSync( jsondomain );
|
|
||||||
}
|
|
||||||
c.langue.forEach( lg => {
|
|
||||||
const jsondomlg = `${config.tribes}/${
|
|
||||||
c.tribeid
|
|
||||||
}/referentials/${lg}/json/${j}.json`;
|
|
||||||
// console.log('jsondomlg', jsondomlg);
|
|
||||||
let datalg = jsonfull;
|
|
||||||
if( jsonfull[ lg ] ) {
|
|
||||||
datalg = jsonfull[ lg ];
|
|
||||||
}
|
|
||||||
fs.outputJsonSync( jsondomlg, datalg, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
// update clientconf domain with updated version
|
|
||||||
if( majclientconf ) {
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${c.tribeid}/clientconf.json`, c, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
// update global conf
|
|
||||||
fs.outputJsonSync( `${config.tmp}/clientconfglob.json`, confglob, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
};*/
|
|
||||||
|
|
||||||
module.exports = Referentials;
|
|
@ -1,220 +0,0 @@
|
|||||||
const glob = require( 'glob' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
// Check if package is installed or not to pickup the right config file
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
|
|
||||||
const Referentials = {};
|
|
||||||
/*
|
|
||||||
Manage Referential object data
|
|
||||||
|
|
||||||
each object is compose of a list of fields
|
|
||||||
each fields have it owns structur and check
|
|
||||||
those common rules allow to be used to manage:
|
|
||||||
- forms (creation to collect data)
|
|
||||||
- data check
|
|
||||||
- data access rights
|
|
||||||
common referential data stored in /data/shared/referential/
|
|
||||||
|
|
||||||
*/
|
|
||||||
Referentials.clientconf = ( xworkOn, listkey ) => {
|
|
||||||
/*
|
|
||||||
Retourne les info d'un clientconf.json sur une liste de [cle]
|
|
||||||
*/
|
|
||||||
let conf = {};
|
|
||||||
let dataconf = {};
|
|
||||||
console.log( `${config.tribes}/${xworkOn}/clientconf.json` )
|
|
||||||
try {
|
|
||||||
conf = fs.readJsonSync( `${config.tribes}/${xworkOn}/clientconf.json` );
|
|
||||||
// remove information notrelevant for
|
|
||||||
[ 'emailFrom', 'emailClient', 'emailCc', 'commentkey', 'clezoomprivate', 'stripekeyprivate', 'genericpsw' ].forEach( c => {
|
|
||||||
delete conf[ c ];
|
|
||||||
} );
|
|
||||||
listkey.forEach( k => dataconf[ k ] = conf[ k ] )
|
|
||||||
console.log( 'dataconf', dataconf )
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'Attention demande sur clienId inconnu ' + xworkOn );
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
data: dataconf
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Referentials.clientconfglob = () => ( {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
data: fs.readJsonSync( `${config.tmp}/clientconfglob.json` )
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
Referentials.inittribeid = () => {
|
|
||||||
console.log( "Clientconf list for this server", `${config.tribes}/**/clientconf.json` );
|
|
||||||
const TribesGlobalConfig = glob.sync( `${config.tribes}/**/clientconf.json` )
|
|
||||||
.map( f => fs.readJsonSync( f ) );
|
|
||||||
// store global conf for sharing to other api
|
|
||||||
fs.outputJsonSync( `${config.tmp}/clientconfglob.json`, TribesGlobalConfig, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
return { status: 200, payload: { moreinfo: TribesGlobalConfig } }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Referentials.generetribeids = () => {
|
|
||||||
const tribeids = [];
|
|
||||||
fs.readJsonSync( `${config.tmp}/clientconfglob.json` )
|
|
||||||
.forEach( c => {
|
|
||||||
if( !tribeids.includes( c.tribeid ) ) tribeids.push( c.tribeid );
|
|
||||||
} );
|
|
||||||
fs.outputJsonSync( `${config.tmp}/tribeids.json`, tribeids );
|
|
||||||
console.log( `update ${config.tribes}/tribeids` );
|
|
||||||
return tribeids;
|
|
||||||
}
|
|
||||||
Referentials.genereallowedDOM = () => {
|
|
||||||
const confglob = fs.readJsonSync( `${config.tmp}/clientconfglob.json` );
|
|
||||||
let allDom = [];
|
|
||||||
confglob.forEach( c => {
|
|
||||||
c.allowedDOMs.forEach( d => {
|
|
||||||
if( !allDom.includes( d ) ) allDom = allDom.concat( d );
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
return allDom;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Referentials.getref = ( source, ref, xworkOn, xlang, singlelang = true ) => {
|
|
||||||
let referent = {};
|
|
||||||
let src = `${config.tribes}/${xworkOn}/referentials/${xlang}/${source}/${ref}.json`;
|
|
||||||
if( !singlelang ) {
|
|
||||||
//request full referential to manage
|
|
||||||
src = `${config.tribes}/${xworkOn}/referentials/dataManagement/${source}/${ref}.json`
|
|
||||||
}
|
|
||||||
console.log( src )
|
|
||||||
try {
|
|
||||||
referent = fs.readJsonSync( src );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( `Attention demande de referentiel inexistant pour ${src} ` );
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
data: referent
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Referentials.putref = ( source, name, xworkOn, data ) => {
|
|
||||||
/*
|
|
||||||
We get a referential, we have 3 kinds of sources:
|
|
||||||
* data = [{uuid,desc:{fr:,en,},desclong:{fr:,en:}, other field}]
|
|
||||||
only desc and desclong have a translation
|
|
||||||
* json = {"fr":{},"en":{}, ...} are full complex object in language
|
|
||||||
* object = [{uuid,desc:{fr,en},desclong:{fr,en}},tpl,}]
|
|
||||||
Difference between data and object is that object defines rule to manage an object, and how to create a forms to get data each data is saved in one folder object/uuid.json and have to respect the corresponding object referentials definition.
|
|
||||||
|
|
||||||
*/
|
|
||||||
console.log( data )
|
|
||||||
// Create a backup of the day hour if exist
|
|
||||||
const file = `${config.tribes}/${xworkOn}/referentials/dataManagement/${source}/${name}.json`
|
|
||||||
|
|
||||||
if( fs.existsSync( file ) ) {
|
|
||||||
//backup change done per hour
|
|
||||||
const origin = fs.readJsonSync( file, 'utf-8' )
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${xworkOn}/referentials/dataManagementBackup/${source}/${name}${moment().format('YYYYMMDDHHmm')}.json`, origin, { spaces: 2 } )
|
|
||||||
} else {
|
|
||||||
console.log( `Referential ${name}.json does not exist this created it` )
|
|
||||||
}
|
|
||||||
console.log( 'ref backup before update', name );
|
|
||||||
fs.outputJsonSync( file, data, { spaces: 2 } );
|
|
||||||
// update/create new referential and new version
|
|
||||||
return Referentials.update( xworkOn, source, name );
|
|
||||||
};
|
|
||||||
Referentials.updatefull = ( tribeid ) => {
|
|
||||||
// source json are only per lg so no full update for json
|
|
||||||
let err = "";
|
|
||||||
[ 'object', 'data' ].forEach( o => {
|
|
||||||
glob.sync( `${config.tribes}/${tribeid}/referentials/dataManagement/${o}/*.json` )
|
|
||||||
.forEach( f => {
|
|
||||||
if( finipaspar_lg ) {
|
|
||||||
const res = Referentials.update( tribeid, o, path.basename( f, '.json' ) );
|
|
||||||
if( res.status != 200 ) {
|
|
||||||
err += `Error on ${o}/${path.basename(f)}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
if( err != "" ) {
|
|
||||||
return { status: 500, payload: { info: [ 'Errupdateref' ], model: "Referentials", moreinfo: err } }
|
|
||||||
}
|
|
||||||
return { status: 200, payload: { info: [ 'Success' ], model: "Referentials" } }
|
|
||||||
};
|
|
||||||
|
|
||||||
Referentials.update = ( tribeid, source, name ) => {
|
|
||||||
/*
|
|
||||||
Replace for each language the referential name for a tribeid
|
|
||||||
After each update the version number is incremented by 1 in clientconf.json
|
|
||||||
*/
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${tribeid}/referentials/${source}/${name}.json` ) ) {
|
|
||||||
return { status: 500, payload: { info: [ "unknownRef" ], model: "Referentials", moreinfo: `file does not exist ${config.tribes}/${tribeid}/referentials/${source}/${name}.json` } }
|
|
||||||
};
|
|
||||||
|
|
||||||
const clientconf = fs.readJsonSync( `${config.tribes}/${tribeid}/clientconf.json` );
|
|
||||||
if( !clientconf.langueReferential ) {
|
|
||||||
return { status: 500, payload: { info: [ "missingConf" ], model: "Referentials", moreinfo: ` ${config.tribes}/${tribeid}/clientconf.json does not contain langueReferential array` } }
|
|
||||||
}
|
|
||||||
const ref = fs.readJsonSync( `${config.tribes}/${tribeid}/referentials/${source}/${name}.json` );
|
|
||||||
|
|
||||||
clientconf.langueReferential.forEach( lg => {
|
|
||||||
/*if( !fs.existsSync( `${config.tribes}/${tribeid}/referentials/${lg}` ) ) {
|
|
||||||
[ '', '/data', '/json', '/object' ].forEach( p => {
|
|
||||||
fs.mkdirSync( `${config.tribes}/${tribeid}/referentials/${lg}${p}` );
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
//manage translation
|
|
||||||
let refnew = [];
|
|
||||||
if( source == 'json' ) {
|
|
||||||
refnew = ref[ lg ]
|
|
||||||
} else {
|
|
||||||
refnew = [];
|
|
||||||
ref.forEach( d => {
|
|
||||||
if( d.desc ) d.desc = d.desc[ lg ];
|
|
||||||
if( d.desclong ) d.desclong = d.desclong[ lg ];
|
|
||||||
if( d.info ) d.info = d.info[ lg ];
|
|
||||||
if( d.placeholder ) d.placeholder = d.placeholder[ lg ];
|
|
||||||
refnew.push( d )
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
//save new ref in language
|
|
||||||
console.log( "testtttt", refnew )
|
|
||||||
console.log( `${config.tribes}/${tribeid}/referentials/${source}/${name}_${lg}.json` )
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${tribeid}/referentials/${source}/${name}_${lg}.json`, refnew, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
//upgrade version number
|
|
||||||
if( !clientconf.referentials[ source ][ name ] ) clientconf.referentials[ source ][ name ] = { version: 0 };
|
|
||||||
clientconf.referentials[ source ][ name ].version += 1;
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'successUpdate' ],
|
|
||||||
model: "Referentials",
|
|
||||||
moreinfo: `${name} updated`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//console.log( Referentials.update( 'apixtribe', "object", "user" ) )
|
|
||||||
|
|
||||||
Referentials.genereobjet = ( tribeid, destination, tplmustache, objet, filtre ) => {
|
|
||||||
/* @TODO
|
|
||||||
Genere des objets d'agregat
|
|
||||||
@tribeid = data/tribee/ identifiant client
|
|
||||||
@destinations = [] of destination
|
|
||||||
@tplmustache = fichier mustache de mise en page
|
|
||||||
@objet = nom d'objet contact, companies, items, users
|
|
||||||
@filtre = fonction a executer
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Referentials;
|
|
229
models/Tags.js
229
models/Tags.js
@ -1,229 +0,0 @@
|
|||||||
const fs = require( 'fs' );
|
|
||||||
const formidable = require( 'formidable' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const mustache = require( 'mustache' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
// Check if package is installed or not to pickup the right config file
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
|
|
||||||
|
|
||||||
const Tags = {};
|
|
||||||
/*
|
|
||||||
Keyword definition:
|
|
||||||
|
|
||||||
id: b64 encoded field that can be : email , uuid
|
|
||||||
tribeid: apiamaildigit client Id, (a folder /tribes/tribeid have to exist)
|
|
||||||
email: identifiant of a person
|
|
||||||
uuid: identifiant o
|
|
||||||
contact database
|
|
||||||
operationId: code name of an operation
|
|
||||||
messageId: code name of an templatesource have to exist in /datashared/templatesource/generic/messageId
|
|
||||||
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
Manage tag data collection
|
|
||||||
Popup survey manager
|
|
||||||
*/
|
|
||||||
Tags.info = ( data, req ) => {
|
|
||||||
//console.log('headers:', req.headers)
|
|
||||||
/*console.log('hostname', req.hostname)
|
|
||||||
console.log('req.ip', req.ip)
|
|
||||||
console.log('req.ips', req.ips)
|
|
||||||
console.log('req key', Object.keys(req))
|
|
||||||
*/
|
|
||||||
//console.log('req.rawHeaders', req.body)
|
|
||||||
data.useragent = `${req.headers['user-agent']}__${req.headers['accept-language']}__${req.headers['accept-encoding']}__${req.headers['connection']}`;
|
|
||||||
data.ips = req.ips;
|
|
||||||
data.ip = req.ip;
|
|
||||||
data.proxyip = req.connection.remoteAddress;
|
|
||||||
data.cookie = ""
|
|
||||||
Object.keys( req.headers )
|
|
||||||
.forEach( k => {
|
|
||||||
if( ![ 'user-agent', 'accept-language', 'accept-encoding', 'connection' ].includes( k ) ) {
|
|
||||||
data.cookie += `${k}__${req.headers['cookie']}|`
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
//data.cookie = `${req.headers['cookie']}__${req.headers['upgrade-insecure-requests']}__${req.headers['if-modified-since']}__${req.headers['if-no-match']}__${req.headers['cache-control']}`;
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
Tags.getfile = ( filename, req ) => {
|
|
||||||
const infotg = filename.split( '__' );
|
|
||||||
if( infotg.length < 3 && !fs.existsSync( `${config.tribes}/${infotg[1]}` ) ) {
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
payload: { info: [ 'fileUnknown' ], model: 'UploadFiles' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( infotg[ 0 ] == "imgtg" ) {
|
|
||||||
jsonfile.writeFile( `${config.tribes}/${infotg[1]}/tags/imgtg/${Date.now()}.json`, Tags.info( { filename: filename, messageId: infotg[ 2 ], operationId: infotg[ 3 ], identifiant: infotg[ 4 ] }, req ), function ( err ) {
|
|
||||||
if( err ) {
|
|
||||||
console.log( `Erreur de sauvegarde de tag:${filename}` )
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: { moreinfo: "Declenche tag", filename: `${config.mainDir}/public/imgtg.png` }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { status: 404, payload: {} }
|
|
||||||
}
|
|
||||||
Tags.savehits = ( req ) => {
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${req.params.tribeid}` ) ) {
|
|
||||||
console.log( `Erreur d'envoi de tag sur ${req.params.tribeid} pour ${req.params.r}` );
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
const info = JSON.parse( JSON.stringify( req.body ) );
|
|
||||||
jsonfile.writeFile( `${config.tribes}/${req.params.tribeid}/tags/hits/${Date.now()}.json`, Tags.info( info, req ), function ( err ) {
|
|
||||||
if( err ) {
|
|
||||||
console.log( `Erreur de sauvegarde de tag pour ${req.params.tribeid} check si /tags/hits et /tags/imgtg exist bien ` )
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Tags.dataloadstat = ( tribeid ) => {
|
|
||||||
/*
|
|
||||||
@TODO à appeler via une route pour agregation de tag
|
|
||||||
@TODO ajouter la suppression des fichiers hits traités qd le prog aura fait ses preuves en prod
|
|
||||||
@TODO corriger la prod pour que l'ip passe
|
|
||||||
|
|
||||||
Si on recharge plusieurs fois un hits (on ne le comptabilise pas si même r et même timestamps)
|
|
||||||
|
|
||||||
Manage tag info to agregate by user and by statistique
|
|
||||||
stats/data.json = {r:{
|
|
||||||
info:{useragent:"",ip:[]},
|
|
||||||
data:[ [timestamps, tit, cookie] ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stats/graph.json = {"graphYears": {AAAA:#visites,"years":[AAAA,AAAA]},
|
|
||||||
"graphMonths": {AAAA:{"Jan":#visites,..},"monthsLabels":["Jan",..],"years":[AAAA,]},
|
|
||||||
"graphDaysOfWeek":{"labels":["Sun","Mon",...],"Sun":#visites},
|
|
||||||
"graphHoursOfDay":{"labels":["OOh","01h",...],"00h":#visites}
|
|
||||||
}
|
|
||||||
Pour tester des evolutions ou un client en dev (recuperer son repertoire de prod /tags )
|
|
||||||
ajouter Tags.dataloadstat('yes');
|
|
||||||
NODE_ENV=dev node ./models/Tags.js
|
|
||||||
*/
|
|
||||||
const agrege = {
|
|
||||||
data: {},
|
|
||||||
graph: {
|
|
||||||
visites: {
|
|
||||||
graphYears: { years: [] },
|
|
||||||
graphMonths: {
|
|
||||||
years: [],
|
|
||||||
monthsLabels: []
|
|
||||||
},
|
|
||||||
graphMonths: {
|
|
||||||
years: [],
|
|
||||||
monthsLabels: []
|
|
||||||
},
|
|
||||||
graphDayOfWeek: { labels: [] },
|
|
||||||
graphHourOfDay: { labels: [] }
|
|
||||||
},
|
|
||||||
visitors: {
|
|
||||||
graphMonths: {
|
|
||||||
years: [],
|
|
||||||
monthsLabels: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
agrege.data = jsonfile.readfileSync( `${config.tribes}/${tribeid}/tags/stats/data.json`, "utf-8" );
|
|
||||||
agrege.graph = jsonfile.readfileSync( `${config.tribes}/${tribeid}/tags/stats/graph.json`, "utf-8" );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( "ATTENTION tag reinitialisé en data.json et graph.json, s'il s'agit de 1ere connexion pas de pb. Le risque est de perdre les tag historiques" )
|
|
||||||
//return { status: 503, payload: { info: ['Errconfig'], model: 'Tags', moreinfo: `Il manque un ${config.tribes}/${tribeid}/tags/stats/data.json ou stats/graph.json` } }
|
|
||||||
}
|
|
||||||
glob.sync( `${config.tribes}/${tribeid}/tags/hits/*` )
|
|
||||||
.forEach( f => {
|
|
||||||
const hit = jsonfile.readFileSync( f );
|
|
||||||
const ts = parseInt( path.basename( f )
|
|
||||||
.split( ".json" )[ 0 ] );
|
|
||||||
//console.log(moment(ts).format('DD-MM-YYYY h:mm:ss'));
|
|
||||||
const tsm = moment( ts )
|
|
||||||
const year = tsm.format( 'YYYY' );
|
|
||||||
const month = tsm.format( 'MMM' );
|
|
||||||
const dayow = tsm.format( 'ddd' );
|
|
||||||
const hourod = tsm.format( 'HH' ) + "h";
|
|
||||||
let newvisitor = false;
|
|
||||||
let alreadydone = false;
|
|
||||||
//console.log(hit.r, ts)
|
|
||||||
// Agrege data pour # visiteur vs # de visiteur
|
|
||||||
if( agrege.data[ hit.r ] ) {
|
|
||||||
if( !agrege.data[ hit.r ].data.some( el => el[ 0 ] == ts ) ) {
|
|
||||||
//evite de charger plusieurs fois le même
|
|
||||||
agrege.data[ hit.r ].data.push( [ ts, hit.tit, hit.cookie ] )
|
|
||||||
} else {
|
|
||||||
alreadydone = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newvisitor = true;
|
|
||||||
agrege.data[ hit.r ] = {
|
|
||||||
info: { useragent: hit.useragent, ip: [ hit.ip ] },
|
|
||||||
data: [
|
|
||||||
[ ts, hit.tit, hit.cookie ]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( !alreadydone ) {
|
|
||||||
if( newvisitor ) {
|
|
||||||
//traite Month
|
|
||||||
if( !agrege.graph.visitors.graphMonths[ year ] ) {
|
|
||||||
agrege.graph.visitors.graphMonths[ year ] = {}
|
|
||||||
agrege.graph.visitors.graphMonths.years.push( year )
|
|
||||||
}
|
|
||||||
if( agrege.graph.visitors.graphMonths[ year ][ month ] ) {
|
|
||||||
agrege.graph.visitors.graphMonths[ year ][ month ] += 1
|
|
||||||
} else {
|
|
||||||
agrege.graph.visitors.graphMonths[ year ][ month ] = 1
|
|
||||||
agrege.graph.visitors.graphMonths.monthsLabels.push( month )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//traite graphe Year #visite
|
|
||||||
if( agrege.graph.visites.graphYears[ year ] ) {
|
|
||||||
agrege.graph.visites.graphYears[ year ] += 1
|
|
||||||
} else {
|
|
||||||
agrege.graph.visites.graphYears[ year ] = 1
|
|
||||||
agrege.graph.visites.graphYears.years.push( year )
|
|
||||||
agrege.graph.visites.graphMonths[ year ] = {}
|
|
||||||
agrege.graph.visites.graphMonths.years.push( year )
|
|
||||||
}
|
|
||||||
//traite graphe Month
|
|
||||||
if( agrege.graph.visites.graphMonths[ year ][ month ] ) {
|
|
||||||
agrege.graph.visites.graphMonths[ year ][ month ] += 1
|
|
||||||
} else {
|
|
||||||
agrege.graph.visites.graphMonths[ year ][ month ] = 1
|
|
||||||
agrege.graph.visites.graphMonths.monthsLabels.push( month )
|
|
||||||
}
|
|
||||||
//traite graphe Days of week
|
|
||||||
if( agrege.graph.visites.graphDayOfWeek[ dayow ] ) {
|
|
||||||
agrege.graph.visites.graphDayOfWeek[ dayow ] += 1
|
|
||||||
} else {
|
|
||||||
agrege.graph.visites.graphDayOfWeek[ dayow ] = 1
|
|
||||||
agrege.graph.visites.graphDayOfWeek.labels.push( dayow )
|
|
||||||
}
|
|
||||||
//traite graphe Hour of day
|
|
||||||
if( agrege.graph.visites.graphHourOfDay[ hourod ] ) {
|
|
||||||
agrege.graph.visites.graphHourOfDay[ hourod ] += 1
|
|
||||||
} else {
|
|
||||||
agrege.graph.visites.graphHourOfDay[ hourod ] = 1
|
|
||||||
agrege.graph.visites.graphHourOfDay.labels.push( hourod )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
jsonfile.writeFileSync( `${config.tribes}/${tribeid}/tags/stats/data.json`, agrege.data, 'utf-8' );
|
|
||||||
jsonfile.writeFileSync( `${config.tribes}/${tribeid}/tags/stats/graph.json`, agrege.graph, 'utf-8' );
|
|
||||||
return { status: 200, payload: { info: [ 'Statsupdated' ], model: 'Tags' } }
|
|
||||||
}
|
|
||||||
//console.log(Tags.dataloadstat('yes'));
|
|
||||||
/*const ar = [
|
|
||||||
[1, 1],
|
|
||||||
[1, 2]
|
|
||||||
]
|
|
||||||
console.log(ar.some(el => el[0] == 1 && el[1] == 1))
|
|
||||||
console.log(ar.some(el => el == [1, 3]))
|
|
||||||
*/
|
|
||||||
module.exports = Tags;
|
|
352
models/Tribes.js
352
models/Tribes.js
@ -1,352 +0,0 @@
|
|||||||
const bcrypt = require( 'bcrypt' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const Mustache = require( 'mustache' );
|
|
||||||
const execSync = require( 'child_process' )
|
|
||||||
.execSync;
|
|
||||||
const dnsSync = require( 'dns-sync' );
|
|
||||||
const jwt = require( 'jwt-simple' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
const UUID = require( 'uuid' );
|
|
||||||
const Outputs = require( './Outputs.js' );
|
|
||||||
const Pagans = require( './Pagans.js' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
|
|
||||||
const checkdata = require( `../nationchains/socialworld/contracts/checkdata.js`);
|
|
||||||
/*
|
|
||||||
tribeid manager
|
|
||||||
|
|
||||||
/tribes/tribeid
|
|
||||||
Manage a tribeid space
|
|
||||||
* create
|
|
||||||
* update by managing option and contract
|
|
||||||
* delete a tribeid
|
|
||||||
* check accountability and
|
|
||||||
|
|
||||||
*/
|
|
||||||
const Tribes = {};
|
|
||||||
Tribes.init = () => {
|
|
||||||
console.group( 'init Tribes' );
|
|
||||||
let tribeids = [];
|
|
||||||
let routes = glob.sync( './routes/*.js' )
|
|
||||||
.map( f => {
|
|
||||||
return { url: `/${path.basename(f,'.js')}`, route: f }
|
|
||||||
} );
|
|
||||||
let DOMs = [];
|
|
||||||
let appname = {};
|
|
||||||
TribesGlobalConfig = glob.sync( `${config.tribes}/**/clientconf.json` )
|
|
||||||
.map( f => {
|
|
||||||
const conf = fs.readJSONSync( f );
|
|
||||||
// check if plugins exist and add it in .plugins of each tribeid conf
|
|
||||||
conf.plugins = glob.sync( `${config.tribes}/${conf.tribeid}/plugins/**/package.json` )
|
|
||||||
.map( p => {
|
|
||||||
const pack = fs.readJsonSync( p, 'utf8' );
|
|
||||||
routes.push( { url: `/${pack.name}`, route: `${config.tribes}/${conf.tribeid}/plugins/${pack.name}/route.js` } );
|
|
||||||
return pack;
|
|
||||||
} );
|
|
||||||
//Add here any other info to get a global view and init
|
|
||||||
//...
|
|
||||||
tribeids.push( conf.tribeid );
|
|
||||||
DOMs = [ ...new Set( [ ...DOMs, ...conf.allowedDOMs ] ) ];
|
|
||||||
if( conf.website ) appname[ conf.tribeid ] = Object.keys( conf.website )
|
|
||||||
return conf;
|
|
||||||
} );
|
|
||||||
// store global conf fofs.existsSync( `${config.tmp}/clientconfglob.json` )r sharing to other api
|
|
||||||
fs.outputJsonSync( `${config.tmp}/clientconfglob.json`, TribesGlobalConfig, {
|
|
||||||
spaces: 2
|
|
||||||
} );
|
|
||||||
return { tribeids, routes, DOMs, appname }
|
|
||||||
}
|
|
||||||
Tribes.create = ( data ) => {
|
|
||||||
/* data = clientconf.json
|
|
||||||
{
|
|
||||||
"tribeid": "apixtribe",
|
|
||||||
"genericpsw": "Trze3aze!",
|
|
||||||
"website": {
|
|
||||||
"presentation":"https://www.apixtribe.org",
|
|
||||||
"webapp": "https://webapp.apixtribe.org"
|
|
||||||
},
|
|
||||||
"allowedDOMs": ["local.fr", "localhost:9002", "ndda.fr", "apixtribe.org"],
|
|
||||||
"clientname": "apixtribe",
|
|
||||||
"clientlogo": "",
|
|
||||||
"geoloc": [],
|
|
||||||
"useradmin": {PUBKEY:"",EMAIL:"",LOGIN:"adminapixtribe",UUID:"adminapixtribe"},
|
|
||||||
"smtp": {
|
|
||||||
"emailFrom": "support@apixtribe.org",
|
|
||||||
"emailcc": [],
|
|
||||||
"service": "gmail",
|
|
||||||
"auth": {
|
|
||||||
"user": "antonin.ha@gmail.com",
|
|
||||||
"pass": "Ha06110"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"accepted-language": "fr,en",
|
|
||||||
"langueReferential": ["fr"]
|
|
||||||
}
|
|
||||||
What about:
|
|
||||||
"tribeid": same than the folder where all the client's file are stored
|
|
||||||
"genericpsw": a generic password for new user need upper lowercase number ans special char
|
|
||||||
"dnsname": a domain name belonging to the client
|
|
||||||
"subdns": "www", a sub domain subdns.dnsname give a public web access to
|
|
||||||
"website": { keywebsite:url}, give access to config.tribes/tribeid/www/keywebsite/index.html,
|
|
||||||
"allowedDOMs": ["local.fr", "localhost:9002", "nnda.fr"], //for CORS, @TODO generate from prévious URL this allow this apixtribe instance to be accessible
|
|
||||||
"clientname": Name of the organisation if any,
|
|
||||||
"clientlogo": logo of the organisation if any,
|
|
||||||
"geoloc": [], if any
|
|
||||||
"useradmin": { this is the 1st user create automaticaly to make gui available for the 1st user
|
|
||||||
"PUBKEY":public key to be authentify without an email,
|
|
||||||
"EMAIL":user email, we need at least one of authentification set up after the user can use both or only one
|
|
||||||
"LOGIN": login to use for access admintribeid,
|
|
||||||
"UUID": unique id normaly UUID but a uuid admintribeid is the same person in any apixtribe instance so we use it by convention.
|
|
||||||
"xlang": lang used by this user
|
|
||||||
},
|
|
||||||
"smtp": { smtp used to send email by nodemailer lib basic example with a google account
|
|
||||||
"emailFrom": "support@xx.fr",
|
|
||||||
"emailcc": [],
|
|
||||||
"service": "gmail",
|
|
||||||
"auth": {
|
|
||||||
"user": "antonin.ha@gmail.com",
|
|
||||||
"pass": "Ha06110"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"accepted-language": "fr,en", list of accepted-language in terme of http request.
|
|
||||||
"langueReferential": ["fr"], list of the text that have to be translate in referentials
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
//update tmp/confglog.json
|
|
||||||
const dataclient = Tribes.init();
|
|
||||||
//return in prod all instance apxinfo={tribeids:[],logins:[]}
|
|
||||||
// in dev return only local
|
|
||||||
//check tribeid name is unique
|
|
||||||
console.log( 'liste des tribeid', dataclient.tribeids )
|
|
||||||
if( dataclient.tribeids.includes( data.tribeid ) ) {
|
|
||||||
return { status: 403, payload: { model: "client", info: [ 'tribeidalreadyexist' ] } }
|
|
||||||
}
|
|
||||||
//loginsglob = {login:tribeid}
|
|
||||||
let loginsglob = {};
|
|
||||||
if( fs.existsSync( `${config.tmp}/loginsglob.json`, 'utf-8' ) ) {
|
|
||||||
loginsglob = fs.readJsonSync( `${config.tmp}/loginsglob.json`, 'utf-8' );
|
|
||||||
}
|
|
||||||
const logins = Object.keys( loginsglob );
|
|
||||||
if( logins.includes( data.useradmin.login ) ) {
|
|
||||||
return { status: 403, payload: { model: "client", info: [ 'loginalreadyexist' ] } }
|
|
||||||
}
|
|
||||||
fs.ensureDirSync( `${config.tribes}/${data.tribeid}` );
|
|
||||||
[ 'users', 'www', 'referentials', 'nationchains' ].forEach( r => {
|
|
||||||
fs.copySync( `${config.mainDir}/setup/tribes/apixtribe/${r}`, `${config.tribes}/${data.tribeid}/${r}` );
|
|
||||||
} )
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${data.tribeid}/clientconf.json`, data );
|
|
||||||
const confcli = JSON.parse( Mustache.render( fs.readFileSync( `${config.mainDir}/setup/tribes/apixtribe/clientconf.mustache`, 'utf8' ), data ) );
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${data.tribeid}/clientconf.json`, confcli );
|
|
||||||
|
|
||||||
return Pagans.createUser( {
|
|
||||||
xpaganid: "setup",
|
|
||||||
xworkon: data.tribeid,
|
|
||||||
xlang: data.useradmin.xlang
|
|
||||||
}, data.useradmin );
|
|
||||||
};
|
|
||||||
Tribes.archive = ( tribeid ) => {
|
|
||||||
//A faire zip un repertoire tribeid dans
|
|
||||||
// remove tribeid de data ou devdata
|
|
||||||
try {
|
|
||||||
fs.moveSync( `${config.tribes}/${tribeid}`, `${config.archivefolder}/${tribeid}` );
|
|
||||||
//update apixtribeenv
|
|
||||||
Tribes.init();
|
|
||||||
return { status: 200, payload: { info: [ 'deletetribeidsuccessfull' ], models: 'Tribes', moreinfo: "TODO see in Tribes.archive" } }
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( "Erreur d'archivage", err )
|
|
||||||
return { status: 403, payload: { info: [ 'archiveerror' ], models: 'Tribes', moreinfo: err } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
////////////// Manage file for Tribes
|
|
||||||
Tribes.checkaccessfolder = ( folder, typeaccessrequested, useraccessrights, useruuid ) => {
|
|
||||||
// check folder right
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Tribes.checkaccessfiles = ( listfile, typeaccessrequested, useraccessrights, useruuid ) => {
|
|
||||||
// @listfile to check accessright on file or folder
|
|
||||||
// @typeaccessrequested on files R read or download, U for pdate, D for delete , O for owned a Owner has all rights RUD on its files
|
|
||||||
// @useraccessright from its account /userd/uuid.json
|
|
||||||
// @useruuid public uuid user
|
|
||||||
// return {'ok':[file auhtorized],'ko':[files not authorized]}
|
|
||||||
|
|
||||||
const checkauthlistfile = { 'ok': [], 'ko': [] }
|
|
||||||
let structf = []
|
|
||||||
let inforep = { file: {}, dir: {} }
|
|
||||||
let done;
|
|
||||||
for( const f of listfile ) {
|
|
||||||
done = false;
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${f}` ) ) {
|
|
||||||
done = true;
|
|
||||||
checkauthlistfile.ko.push( f )
|
|
||||||
console.log( `${f} file does not exist` )
|
|
||||||
} else {
|
|
||||||
structf = f.split( '/' );
|
|
||||||
}
|
|
||||||
//on ckeck tribeid existe / tribeid/object/
|
|
||||||
if( !done &&
|
|
||||||
useraccessrights.data[ structf[ 0 ] ] &&
|
|
||||||
useraccessrights.data[ structf[ 0 ] ][ structf[ 1 ] ] &&
|
|
||||||
useraccessrights.data[ structf[ 0 ] ][ structf[ 1 ] ].includes( typeaccessrequested ) ) {
|
|
||||||
done = true;
|
|
||||||
checkauthlistfile.ok.push( f );
|
|
||||||
} else {
|
|
||||||
// check if in folder we have a.info.json .file[f].shared{useruuid:'CRUDO'}
|
|
||||||
console.log( 'structf', structf )
|
|
||||||
if( fs.existsSync( `${config.tribes}/${structf.slice(0,-1).join('/')}/.info.json` ) ) {
|
|
||||||
inforep = fs.readJsonSync( `${config.tribes}/${structf.slice(0,-1).join('/')}/.info.json`, 'utf8' )
|
|
||||||
}
|
|
||||||
console.log( `no accessrights for ${f} for ${useruuid} ` )
|
|
||||||
}
|
|
||||||
if( !done && inforep.file[ f ] && inforep.file[ f ] && inforep.file[ f ].shared && inforep.file[ f ].shared[ useruuid ] && inforep.file[ f ].shared[ useruuid ].includes( typeaccessrequested ) ) {
|
|
||||||
done = true;
|
|
||||||
checkauthlistfile.ok.push( f )
|
|
||||||
}
|
|
||||||
// If no authorization then ko
|
|
||||||
if( !done ) {
|
|
||||||
checkauthlistfile.ko.push( f )
|
|
||||||
}
|
|
||||||
} // end loop for
|
|
||||||
//console.log( 'checkauthlistfile', checkauthlistfile )
|
|
||||||
return checkauthlistfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tribes.dirls = ( tribeid, dir ) => {
|
|
||||||
/*
|
|
||||||
Return list of file into tribeid/dir
|
|
||||||
*/
|
|
||||||
let comment = { src: `${tribeid}/${dir}`, file: {}, dir: {} };
|
|
||||||
if( fs.existsSync( `${config.tribes}/${tribeid}/${dir}/.info.json` ) ) {
|
|
||||||
comment = fs.readJsonSync( `${config.tribes}/${tribeid}/${dir}/.info.json`, 'utf-8' );
|
|
||||||
}
|
|
||||||
const listfile = []
|
|
||||||
const listdir = []
|
|
||||||
glob.sync( `${config.tribes}/${tribeid}/${dir}/*` )
|
|
||||||
.forEach( f => {
|
|
||||||
//console.log( f )
|
|
||||||
const stats = fs.statSync( f );
|
|
||||||
// console.log( stats )
|
|
||||||
if( stats.isFile() ) {
|
|
||||||
listfile.push( path.basename( f ) )
|
|
||||||
if( !comment.file[ path.basename( f ) ] ) {
|
|
||||||
comment.file[ path.basename( f ) ] = { tags: [], info: "", thumbb64: "" };
|
|
||||||
}
|
|
||||||
comment.file[ path.basename( f ) ].mtime = stats.mtime;
|
|
||||||
comment.file[ path.basename( f ) ].ctime = stats.ctime;
|
|
||||||
comment.file[ path.basename( f ) ].size = stats.size;
|
|
||||||
}
|
|
||||||
if( stats.isDirectory() ) {
|
|
||||||
listdir.push( path.basename( f ) )
|
|
||||||
if( !comment.dir[ path.basename( f ) ] ) {
|
|
||||||
comment.dir[ path.basename( f ) ] = { tags: [], info: "", thumbb64: "" }
|
|
||||||
}
|
|
||||||
comment.dir[ path.basename( f ) ].nbfile = glob.sync( `${f}/*.*` )
|
|
||||||
.length;
|
|
||||||
comment.dir[ path.basename( f ) ].mtime = stats.mtime;
|
|
||||||
comment.dir[ path.basename( f ) ].ctime = stats.mtime;
|
|
||||||
console.log( 'comment.dir', comment.dir )
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
// on remove les file or dir that was deleted
|
|
||||||
Object.keys( comment.file )
|
|
||||||
.forEach( f => {
|
|
||||||
if( !listfile.includes( f ) ) delete comment.file[ f ]
|
|
||||||
} )
|
|
||||||
Object.keys( comment.dir )
|
|
||||||
.forEach( d => {
|
|
||||||
if( !listdir.includes( d ) ) delete comment.dir[ d ]
|
|
||||||
} )
|
|
||||||
//console.log( comment )
|
|
||||||
fs.outputJson( `${config.tribes}/${tribeid}/${dir}/.info.json`, comment, 'utf-8' );
|
|
||||||
return { status: 200, payload: { info: [ 'succestogetls' ], models: 'Tribes', moreinfo: comment } }
|
|
||||||
};
|
|
||||||
Tribes.addspaceweb = ( data ) => {
|
|
||||||
/*
|
|
||||||
To create a public spaceweb accessible from https://dnsname/pageindex
|
|
||||||
|
|
||||||
input:
|
|
||||||
{dnsname:["archilinea.fr","www.archilinea.fr"], 1st is tha main dns other are just servername redirection
|
|
||||||
tribeid:"archilinea", from req.session.header.xworkon
|
|
||||||
website:"presentation",
|
|
||||||
pageindex:"app_index_fr.html"
|
|
||||||
mode:dev(local no ssl) | prod(IP + ssl)
|
|
||||||
}
|
|
||||||
output:
|
|
||||||
nginx conf and ssl to serve each https://dnsname to /{tribeid}/www/app/{website}
|
|
||||||
|
|
||||||
|
|
||||||
Carefull this action is executed with root and restart nginx + apixtribe to work
|
|
||||||
*/
|
|
||||||
data.configdomain = config.tribes;
|
|
||||||
data.porthttp = config.porthttp;
|
|
||||||
console.assert( config.loglevel == "quiet", 'data to create spaceweb:', data );
|
|
||||||
// create spaceweb app for tribeid/www/app/website/pageindexname.html
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${data.tribeid}/www/app/${data.website}` ) ) {
|
|
||||||
fs.outputFileSync( `${config.tribes}/${data.tribeid}/www/app/${data.website}/${data.pageindex}`, `<h1>Hello ${data.tribeid} ${data.website} onto ${data.dnsname.join(',')}`, 'utf-8' )
|
|
||||||
}
|
|
||||||
//create logs folder
|
|
||||||
fs.ensureDirSync( `${config.tribes}/${data.tribeid}/logs/nginx` );
|
|
||||||
// add nginx http config
|
|
||||||
const confnginx = fs.readFileSync( 'setup/nginx/modelwebsite.conf.mustache', 'utf-8' );
|
|
||||||
fs.outputFileSync( `/etc/nginx/conf.d/${data.dnsname[0]}.conf`, Mustache.render( confnginx, data ), 'utf-8' );
|
|
||||||
if( data.mode == "dev" ) {
|
|
||||||
//add in /etc/hosts
|
|
||||||
let hosts = fs.readFileSync( '/etc/hosts', 'utf8' );
|
|
||||||
let chg = false;
|
|
||||||
data.dnsname.forEach( d => {
|
|
||||||
if( !hosts.includes( `127.0.0.1 ${d}` ) ) {
|
|
||||||
hosts += `\n127.0.0.1 ${d}`;
|
|
||||||
chg = true;
|
|
||||||
}
|
|
||||||
if( chg ) {
|
|
||||||
fs.outputFileSync( '/etc/hosts', hosts, 'utf8' )
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
//Ckeck dns respond
|
|
||||||
data.dnsname.forEach( d => {
|
|
||||||
if( !dnsSync.resolve( `${d}` ) ) {
|
|
||||||
rep += `\nresolving ${d} will not responding valid IP, please setup domain redirection IP before runing this script`
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
//update clienconf.json
|
|
||||||
const clientconf = fs.readJsonSync( `${config.tribes}/${data.tribeid}/clientconf.json` );
|
|
||||||
clientconf.website[ data.website ] = data.dnsname[ 0 ];
|
|
||||||
//merge allowedDOMs in unique concat
|
|
||||||
clientconf.allowedDOMs = [ ...new Set( ...clientconf.allowedDOMs, ...data.dnsname ) ];
|
|
||||||
fs.outputJsonSync( `${config.tribes}/${data.tribeid}/clientconf.json`, clientconf, 'utf-8' );
|
|
||||||
if( !data.setup ) {
|
|
||||||
// in setup apixtribe is not running and it will be start manually at the 1st run
|
|
||||||
// in other case need to plan a restart for CORS
|
|
||||||
setTimeout( Tribes.restartapixtribe, 300000, data.clienId );
|
|
||||||
}
|
|
||||||
const nginxrestart = execSync( `sudo systemctl restart nginx` )
|
|
||||||
.toString();
|
|
||||||
console.log( 'Restart nginx', nginxrestart )
|
|
||||||
if( data.mode == "prod" ) {
|
|
||||||
// get ssl certificate ATTENTION il faut ajouter -d devant chaque domain qui redirige vers l'espace web.
|
|
||||||
const certbot = execSync( `sudo certbot --nginx -d ${data.dnsname.join(' -d ')}` )
|
|
||||||
.toString();
|
|
||||||
console.log( 'certbot is running A CHECKER POUR UNE VRAIE PROD ????', certbot )
|
|
||||||
}
|
|
||||||
//sh execution to update change requested
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
model: "Tribes",
|
|
||||||
info: [ 'webspacecreated' ],
|
|
||||||
moreinfo: "Space web well created"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Tribes.restartapixtribe = ( tribeid ) => {
|
|
||||||
console.log( 'A restarting was requested 5mn ago from a new spacedev for ' + tribeid )
|
|
||||||
execSync( 'yarn restartpm2' );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = Tribes;
|
|
@ -1,152 +0,0 @@
|
|||||||
const fs = require( 'fs-extra' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const formidable = require( 'formidable' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const mustache = require( 'mustache' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
|
|
||||||
/*
|
|
||||||
model A SUPPRIMER !!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
les functions d'upload de file et de droits d'accès doivent être gérer dans Tribes
|
|
||||||
|
|
||||||
*/
|
|
||||||
const UploadFiles = {};
|
|
||||||
UploadFiles.get = function ( filename, header ) {
|
|
||||||
// check file exist
|
|
||||||
const file = `${config.tribes}/${header.xworkon}/${filename}`;
|
|
||||||
// console.log('fichier demande ', file);
|
|
||||||
if( !fs.existsSync( file ) ) {
|
|
||||||
// console.log('le fichier demande n existe pas ', file);
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: { info: [ 'fileUnknown' ], model: 'UploadFiles' }
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
console.log( 'envoie le fichier ', file );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: { info: [ 'fileknown' ], model: 'UploadFiles', file: file }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UploadFiles.addjson = function ( data, header ) {
|
|
||||||
/*
|
|
||||||
Le header = {X-WorkOn:"",destinationfile:"", filename:""}
|
|
||||||
Le body = {jsonp:{},callback:function to launch after download,'code':'mot cle pour verifier que le fichier est à garder'}
|
|
||||||
*/
|
|
||||||
// console.log(req.body.jsonp);
|
|
||||||
try {
|
|
||||||
jsonfile.writeFileSync( header.destinationfile + '/' + header.filename, data.jsonp );
|
|
||||||
if( data.callback ) {
|
|
||||||
const execCB = require( `${config.mainDir}/models/tribeid/${
|
|
||||||
header.xworkon
|
|
||||||
}` );
|
|
||||||
execCB[ data.callback ]();
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'wellUpload' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
render: {
|
|
||||||
destination: header.destinationfile,
|
|
||||||
filename: header.filename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( 'Impossible de sauvegarder le fichier, A COMPRENDRE', err );
|
|
||||||
return {
|
|
||||||
status: 503,
|
|
||||||
payload: { info: [ 'savingError' ], model: 'UploadFiles' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UploadFiles.add = function ( req, header ) {
|
|
||||||
const form = new formidable.IncomingForm();
|
|
||||||
console.log( 'req.headers', req.headers );
|
|
||||||
console.log( 'req.params', req.params );
|
|
||||||
console.log( 'req.query', req.query );
|
|
||||||
console.log( 'req.body', req.body );
|
|
||||||
let destinationfile = `${config.tribes}/${header.xworkon}/${
|
|
||||||
header.destinationfile
|
|
||||||
}`;
|
|
||||||
form.parse( req, function ( err, fields, files ) {
|
|
||||||
console.log( 'files', files.file.path );
|
|
||||||
console.log( 'fields', fields );
|
|
||||||
const oldpath = files.file.path;
|
|
||||||
destinationfile += '/' + files.file.name;
|
|
||||||
console.log( 'oldpath', oldpath );
|
|
||||||
console.log( 'destinationfile', destinationfile );
|
|
||||||
fs.copyFile( oldpath, destinationfile, function ( err ) {
|
|
||||||
if( err ) {
|
|
||||||
console.log( err );
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
payload: { info: [ 'savingError' ], model: 'UploadFiles' }
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
console.log( 'passe' );
|
|
||||||
fs.unlink( oldpath );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: {
|
|
||||||
info: [ 'wellUpload' ],
|
|
||||||
model: 'UploadFiles',
|
|
||||||
render: {
|
|
||||||
destination: destinationfile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
UploadFiles.updateEvent = function ( domainId, eventId, event ) {
|
|
||||||
// checkAndCreateNeededDirectories(domainId);
|
|
||||||
const eventsFile = `${config.tribes}/${domainId}/actions/events/events.json`;
|
|
||||||
if( !fs.existsSync( eventsFile ) ) {
|
|
||||||
jsonfile.writeFileSync( eventsFile, {} );
|
|
||||||
return { status: 404, payload: 'You have not any events.' };
|
|
||||||
}
|
|
||||||
const events = jsonfile.readFileSync( eventsFile );
|
|
||||||
if( !events.hasOwnProperty( eventId ) ) {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: 'The event you are trying to update does not exist.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
events[ eventId ] = {
|
|
||||||
eventName: event.eventName,
|
|
||||||
eventDate: event.eventDate,
|
|
||||||
eventDescription: event.eventDescription
|
|
||||||
};
|
|
||||||
jsonfile.writeFileSync( eventsFile, events, { spaces: 2 } );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: events
|
|
||||||
};
|
|
||||||
};
|
|
||||||
UploadFiles.deleteEvent = function ( domainId, eventId ) {
|
|
||||||
// checkAndCreateNeededDirectories(domainId);
|
|
||||||
const eventsFile = `${config.tribes}/${domainId}/actions/events/events.json`;
|
|
||||||
if( !fs.existsSync( eventsFile ) ) {
|
|
||||||
jsonfile.writeFileSync( eventsFile, {} );
|
|
||||||
return { status: 404, payload: 'You have not any events.' };
|
|
||||||
}
|
|
||||||
const events = jsonfile.readFileSync( eventsFile );
|
|
||||||
if( events.hasOwnProperty( eventId ) ) {
|
|
||||||
delete events[ eventId ];
|
|
||||||
jsonfile.writeFileSync( eventsFile, events, { spaces: 2 } );
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
payload: events
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
payload: 'The event you are trying to delete does not exist.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
module.exports = UploadFiles;
|
|
@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
This module have to be independant of any external package
|
|
||||||
it is shared between back and front and is usefull
|
|
||||||
to apply common check in front before sending it in back
|
|
||||||
can be include in project with
|
|
||||||
<script src="https://apiback.maildigit.fr/js/checkdata.js"></script>
|
|
||||||
or with const checkdata = require('../public/js/checkdata.js')
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// --##
|
|
||||||
|
|
||||||
const checkdata = {};
|
|
||||||
// each checkdata.test. return true or false
|
|
||||||
checkdata.test = {};
|
|
||||||
|
|
||||||
checkdata.test.emailadress = ( ctx, email ) => {
|
|
||||||
const regExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
||||||
return regExp.test( email );
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* @emaillist = "email1,email2, email3"
|
|
||||||
* it check if each eamil separate by , are correct
|
|
||||||
*/
|
|
||||||
checkdata.test.emailadresslist = ( ctx, emaillist ) => {
|
|
||||||
//console.log(emaillist.split(','))
|
|
||||||
if( emaillist.length > 0 ) {
|
|
||||||
const emails = emaillist.split( ',' );
|
|
||||||
for( var i in emails ) {
|
|
||||||
//console.log(emails[i])
|
|
||||||
if( !checkdata.test.emailadress( "", emails[ i ].trim() ) ) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
checkdata.test.password = ( ctx, pwd ) => {
|
|
||||||
const regExp = new RegExp(
|
|
||||||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&.])[A-Za-z\d$@$!%*?&.{}:|\s]{8,}/
|
|
||||||
);
|
|
||||||
return regExp.test( pwd );
|
|
||||||
};
|
|
||||||
checkdata.test.required = ( ctx, val ) =>
|
|
||||||
( val != null && val != 'undefined' && val.length > 0 ) || ( !!val && val.constructor === Array && val.length > 0 ) || ( !!val && val.constructor === Object && Object.keys( val )
|
|
||||||
.length > 0 );
|
|
||||||
|
|
||||||
checkdata.test.isNumber = ( ctx, n ) => typeof n === 'number';
|
|
||||||
checkdata.test.isInt = ( ctx, n ) => n != '' && !isNaN( n ) && Math.round( n ) == n;
|
|
||||||
checkdata.test.isFloat = ( ctx, n ) => n != '' && !isNaN( n ) && Math.round( n ) != n;
|
|
||||||
checkdata.test.unique = ( ctx, val ) => {
|
|
||||||
if( ctx.list[ ctx.currentfield ] ) {
|
|
||||||
return !ctx.list[ ctx.currentfield ].includes( val );
|
|
||||||
} else {
|
|
||||||
console.log( 'ERR no list for field:' + ctx.currentfield );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
checkdata.test.isDateDay = ( ctx, dateDay ) => true;
|
|
||||||
/* checkdata.test.filterInvalidInArray = (array, validate) =>
|
|
||||||
array ? array.filter(el => !validate(el)) : true;
|
|
||||||
// return true when every elements is valid
|
|
||||||
*/
|
|
||||||
|
|
||||||
checkdata.test.postalCode = ( ctx, postalCode ) => {
|
|
||||||
if( postalCode.length == 0 ) return true;
|
|
||||||
const regExp = new RegExp( /(^\d{5}$)|(^\d{5}-\d{4}$)/ );
|
|
||||||
return regExp.test( postalCode );
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* PHONE
|
|
||||||
*/
|
|
||||||
checkdata.test.phoneNumber = ( ctx, phoneNumber ) => {
|
|
||||||
if( phoneNumber.length == 0 ) return true;
|
|
||||||
phoneNumber = phoneNumber.trim()
|
|
||||||
.replace( /[- .]/g, '' )
|
|
||||||
//french number
|
|
||||||
const regExpfr = new RegExp( /^0[1-9][0-9]{9}$/ );
|
|
||||||
const regExpInternational = new RegExp( /^\+*(\d{3})*[0-9,\-]{8,}/ );
|
|
||||||
return regExpfr.test( phoneNumber ) || regExpInternational.test( phoneNumber );
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* @phonelist = "phone1,phone2,phone3"
|
|
||||||
* it check if each phone separate by , are correct
|
|
||||||
*/
|
|
||||||
checkdata.test.phoneNumberlist = ( ctx, phonelist ) => {
|
|
||||||
//console.log(emaillist.split(','))
|
|
||||||
if( phonelist.length > 0 ) {
|
|
||||||
const phones = phonelist.split( ',' );
|
|
||||||
for( var i in phones ) {
|
|
||||||
//console.log(emails[i])
|
|
||||||
if( !checkdata.test.phoneNumber( "", phones[ i ].trim() ) ) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// checkdata.normalize take a correct data then reformat it to harmonise it
|
|
||||||
checkdata.normalize = {};
|
|
||||||
checkdata.normalize.phoneNumber = ( ctx, phone ) => {
|
|
||||||
phone = phone.trim()
|
|
||||||
.replace( /[- .]/g, '' );
|
|
||||||
if( checkdata.test.phoneNumber( '', phone ) && phone.length == 10 && phone[ 0 ] == "0" ) {
|
|
||||||
phone = '+33 ' + phone.substring( 1 );
|
|
||||||
}
|
|
||||||
return phone;
|
|
||||||
}
|
|
||||||
checkdata.normalize.upperCase = ( ctx, txt ) => txt.toUpperCase();
|
|
||||||
checkdata.normalize.lowerCase = ( ctx, txt ) => txt.toLowerCase();
|
|
||||||
// fixe 10 position et complete par des 0 devant
|
|
||||||
checkdata.normalize.zfill10 = ( ctx, num ) => {
|
|
||||||
let s = num + '';
|
|
||||||
while( s.length < 10 ) s = '0' + s;
|
|
||||||
return s;
|
|
||||||
};
|
|
||||||
/*let tt = "+33 1 02.03 04 05";
|
|
||||||
console.log(checkdata.test.phoneNumber('', tt))
|
|
||||||
console.log(checkdata.normalize.phoneNumber('', tt))
|
|
||||||
*/
|
|
||||||
checkdata.evaluate = ( contexte, referential, data ) => {
|
|
||||||
/*
|
|
||||||
* contexte object {} with full info for evaluation
|
|
||||||
* file referential path to get object to apply
|
|
||||||
* data related to object
|
|
||||||
- return {validefor =[keyword of error] if empty no error,
|
|
||||||
clean data eventually reformated
|
|
||||||
updateDatabase}
|
|
||||||
*/
|
|
||||||
console.log( 'contexte', contexte );
|
|
||||||
console.log( 'referentiel', referential );
|
|
||||||
console.log( 'data', data );
|
|
||||||
const invalidefor = [];
|
|
||||||
const objectdef = {};
|
|
||||||
const listfield = referential.map( ch => {
|
|
||||||
objectdef[ ch.idfield ] = ch;
|
|
||||||
return ch.idfield;
|
|
||||||
} );
|
|
||||||
|
|
||||||
Object.keys( data )
|
|
||||||
.forEach( field => {
|
|
||||||
if( !listfield.includes( field ) ) {
|
|
||||||
// some data can be inside an object with no control at all
|
|
||||||
// they are used for process only
|
|
||||||
// i leave it in case it will become a non sens
|
|
||||||
// invalidefor.push('ERRFIELD unknown of referentials ' + field);
|
|
||||||
} else {
|
|
||||||
if( objectdef[ field ].check ) {
|
|
||||||
// check data with rule list in check
|
|
||||||
objectdef[ field ].check.forEach( ctrl => {
|
|
||||||
console.log( 'ctrl', ctrl );
|
|
||||||
contexte.currentfield = field;
|
|
||||||
if( !checkdata.test[ ctrl ] ) {
|
|
||||||
invalidefor.push( 'ERR check function does not exist :' + ctrl + '___' + field )
|
|
||||||
} else {
|
|
||||||
if( !checkdata.test[ ctrl ]( contexte, data[ field ] ) )
|
|
||||||
invalidefor.push( 'ERR' + ctrl + '___' + field );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( objectdef[ field ].nouserupdate ) {
|
|
||||||
// check if user can modify this information
|
|
||||||
console.log(
|
|
||||||
'evaluation :' + field + ' -- ' + objectdef[ field ].nouserupdate,
|
|
||||||
eval( objectdef[ field ].nouserupdate )
|
|
||||||
);
|
|
||||||
const evalright = eval( objectdef[ field ].nouserupdate );
|
|
||||||
objectdef[ field ].nouserupdate = evalright;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
console.log( {
|
|
||||||
invalidefor,
|
|
||||||
data
|
|
||||||
} );
|
|
||||||
return {
|
|
||||||
invalidefor,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if( typeof module !== 'undefined' ) module.exports = checkdata;
|
|
@ -1,604 +0,0 @@
|
|||||||
/* eslint-disable no-useless-escape */
|
|
||||||
const fs = require( 'fs' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const bcrypt = require( 'bcrypt' );
|
|
||||||
const moment = require( 'moment' );
|
|
||||||
const config = require( '../config' );
|
|
||||||
const utils = {};
|
|
||||||
|
|
||||||
console.log( "Check in /utils/index.js to find usefull function for your dev.\n Feel free to send suggestion, code to maintainer of apixtribe project (see /package.json to get email).\n We'll add to the roadmap to add it." );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EMAIL
|
|
||||||
*/
|
|
||||||
/* const validateEmail = email => {
|
|
||||||
const regExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
||||||
return regExp.test(email);
|
|
||||||
};
|
|
||||||
|
|
||||||
const validatePassword = pwd => {
|
|
||||||
const regExp = new RegExp(
|
|
||||||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&.])[A-Za-z\d$@$!%*?&.{}:|\s]{8,}/
|
|
||||||
);
|
|
||||||
return regExp.test(pwd);
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterInvalidInArray = (array, validate) =>
|
|
||||||
array ? array.filter(el => !validate(el)) : undefined; // return undefined when every elements is valid
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POSTAL CODE
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
const validatePostalCode = postalCode =>
|
|
||||||
/(^\d{5}$)|(^\d{5}-\d{4}$)/.test(postalCode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PHONE
|
|
||||||
*/
|
|
||||||
/* const validatePhoneNumber = phoneNumber =>
|
|
||||||
/((^0[1-9]|\+[0-9]{3})([-. ]?[0-9]{2}){4}$)/.test(phoneNumber);
|
|
||||||
|
|
||||||
const correctPhoneNumber = phone =>
|
|
||||||
phone[0] === '0' ? '+33' + phone.substr(1) : phone;
|
|
||||||
|
|
||||||
const checkData = (appProfil, referential, data) => {
|
|
||||||
// @TODO get a referentiel per object then check data validity and allowed access
|
|
||||||
// need to add referentiel manager
|
|
||||||
const invalidefor = [];
|
|
||||||
let updateDatabase = false;
|
|
||||||
Object.keys(data).forEach(field => {
|
|
||||||
switch (field) {
|
|
||||||
case 'token':
|
|
||||||
updateDatabase = true;
|
|
||||||
break;
|
|
||||||
case 'email':
|
|
||||||
if (!validateEmail(data.email)) {
|
|
||||||
invalidefor.push('ERREMAIL:' + field);
|
|
||||||
} else {
|
|
||||||
updateDatabase = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'password':
|
|
||||||
if (!validatePassword(data.password)) {
|
|
||||||
invalidefor.push('ERRPWD:' + field);
|
|
||||||
} else {
|
|
||||||
data.password = bcrypt.hash(data.password, config.saltRounds);
|
|
||||||
updateDatabase = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { invalidefor, data, updateDatabase };
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
//Permet d'attendre en milliseconde
|
|
||||||
// s'utilise avec async ()=>{
|
|
||||||
// await sleep(2000)
|
|
||||||
//}
|
|
||||||
utils.sleep = ( ms ) => {
|
|
||||||
return new Promise( resolve => setTimeout( resolve, ms ) );
|
|
||||||
}
|
|
||||||
utils.generemdp = ( nbpos ) => {
|
|
||||||
const chaine = "ABCDEFGHIJKLMNPQRSTUVWZY123456789";
|
|
||||||
let mdp = "";
|
|
||||||
for( var i = 0; i < nbpos; i++ ) {
|
|
||||||
var pos = Math.floor( Math.random() * chaine.length );
|
|
||||||
mdp += chaine.substring( pos, pos + 1 );
|
|
||||||
}
|
|
||||||
return mdp;
|
|
||||||
}
|
|
||||||
utils.generecompteur = ( filecpt, typeincrement ) => {
|
|
||||||
let file = `${filecpt}/${typeincrement}.json`;
|
|
||||||
let prefix = "";
|
|
||||||
if( typeincrement = "ANNEESEMAINE" ) {
|
|
||||||
file = `${filecpt}/${typeincrement}${moment().format('YYYY')}${moment().format('WW')}.json`
|
|
||||||
prefix = `${moment().format('YYYY')}${moment().format('WW')}`
|
|
||||||
}
|
|
||||||
let num = 1;
|
|
||||||
try {
|
|
||||||
num = parseInt( fs.readFileSync( file, 'utf8' ) ) + 1;
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( "Nouveau compteur incrementale ", file )
|
|
||||||
}
|
|
||||||
fs.writeFileSync( file, num, 'utf8' );
|
|
||||||
return prefix + num
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* CSV
|
|
||||||
*/
|
|
||||||
utils.json2csv = ( jsondata, options, callback ) => {
|
|
||||||
// uniquement json = [{niv1:val,niv1:[liste of val]}]
|
|
||||||
// console.log('_________________________');
|
|
||||||
// console.log(jsondata)
|
|
||||||
// console.log('_________________________');
|
|
||||||
if( jsondata.length == 0 ) {
|
|
||||||
return callback( "Empty json", null );
|
|
||||||
}
|
|
||||||
if( !options.retln ) options.retln = '\n';
|
|
||||||
if( !options.sep ) options.sep = ';';
|
|
||||||
if( !options.arraysplitsep ) options.arraysplitsep = ",";
|
|
||||||
if( !options.replacespecialcarJson2Csv ) {
|
|
||||||
options.replacespecialcarJson2Csv = []
|
|
||||||
} else {
|
|
||||||
if( typeof options.replacespecialcarJson2Csv == "string" ) {
|
|
||||||
//permet de passer des regex en string
|
|
||||||
options.replacespecialcarJson2Csv = eval( options.replacespecialcarJson2Csv );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let etat = "";
|
|
||||||
let csv = '';
|
|
||||||
let entete = '';
|
|
||||||
let prem = true;
|
|
||||||
for( const j in jsondata ) {
|
|
||||||
// console.log(jsondata[j])
|
|
||||||
for( const c in options.champs ) {
|
|
||||||
if( prem ) {
|
|
||||||
entete += options.champs[ c ] + options.sep;
|
|
||||||
}
|
|
||||||
if( jsondata[ j ][ options.champs[ c ] ] ) {
|
|
||||||
if( options.array.indexOf( options.champs[ c ] ) > -1 ) {
|
|
||||||
csv += jsondata[ j ][ options.champs[ c ] ].join( options.arraysplitsep ) + options.sep;
|
|
||||||
} else {
|
|
||||||
let currentValue = "";
|
|
||||||
if( jsondata[ j ][ options.champs[ c ] ] ) currentValue += jsondata[ j ][ options.champs[ c ] ];
|
|
||||||
options.replacespecialcarJson2Csv.forEach( re => {
|
|
||||||
//console.log(currentValue)
|
|
||||||
currentValue = currentValue.replace( re[ 1 ], re[ 0 ] )
|
|
||||||
} )
|
|
||||||
csv += currentValue + options.sep;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
csv += options.sep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
csv = csv.substring( 0, csv.length - 1 ) + options.retln;
|
|
||||||
if( prem ) {
|
|
||||||
prem = false;
|
|
||||||
entete = entete.substring( 0, entete.length - 1 ) + options.retln;
|
|
||||||
// console.log(entete)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// return entete + csv;
|
|
||||||
if( etat == "" ) {
|
|
||||||
return callback( null, entete + csv );
|
|
||||||
} else {
|
|
||||||
return callback( etat, null );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Get headers from first line of CSV
|
|
||||||
* @param {array} lines array of string which contains each csv lines
|
|
||||||
* @return {array} string array of headers
|
|
||||||
*/
|
|
||||||
utils.getHeaders = ( lines, sep ) => lines[ 0 ].split( sep )
|
|
||||||
.map( i => i.replace( /"/g, '' ) );
|
|
||||||
/**
|
|
||||||
* [csv2json description]
|
|
||||||
* @param {object} csv object of csv file that has been read
|
|
||||||
* @param {object} options object containing csv options, headers, ...
|
|
||||||
{retln:'code de retour de ligne \n ou \n\r',
|
|
||||||
sep:'code to split cells',
|
|
||||||
champs:[ch1,ch2,...] catch only those field,
|
|
||||||
array:[ch1, ] can have more than one field champs with same name then data are push into an array }
|
|
||||||
* @param {Function} callback callback function
|
|
||||||
* @return {callback} - return an error if error, else return json
|
|
||||||
it convert a csv file into a json = [{field:value}]
|
|
||||||
|
|
||||||
Usage example:
|
|
||||||
fiche.csv2article = (err, fiche) => {
|
|
||||||
if (!err) {
|
|
||||||
console.log(fiche)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
utils.csv2json(fs.readFileSync('./devdata/tribee/aubergenville/infoexterne/localbusiness.csv', 'utf-8'), {
|
|
||||||
retln: "\n",
|
|
||||||
sep: ";",
|
|
||||||
champs: ["NOM", "OBJET", "ADRESSE_PRO", "CP_PRO", "VILLE_PRO", "ZONE", "PHONE_PRO", "HORAIRESDESC", "HORAIREDATA", "URL", "FACEBOOK", "INSTA", "EMAIL_PRO", "IMG", "TAG"],
|
|
||||||
array: ["TAG", "PHONE_PRO", "EMAIL_PRO"]
|
|
||||||
}, fiche.csv2article)
|
|
||||||
|
|
||||||
*/
|
|
||||||
utils.replacecarbtweendblquote = ( csv, car, carremplacant ) => {
|
|
||||||
/*
|
|
||||||
return csv text with any car betwenn 2 " by CARSEPARATOR
|
|
||||||
*/
|
|
||||||
let newcsv = "";
|
|
||||||
let txtencours = "";
|
|
||||||
let flagouvert = false
|
|
||||||
const sepreg = new RegExp( `${car}`, 'gmi' )
|
|
||||||
for( let j = 0; j < csv.length; j++ ) {
|
|
||||||
//if((csv[j] == "\"" && csv[j + 1] && csv[j + 1] != "\"") || (csv[j] == "\"" && csv[j - 1] && csv[j - 1] != "\"") || (csv[j] == "\"" && csv[j - 1] && csv[j - 2] && csv[j - 1] != "\"" && csv[j - 2] != "\"")) {
|
|
||||||
if( csv[ j ] == "\"" ) {
|
|
||||||
if( flagouvert ) {
|
|
||||||
// on cherche à ferme une chaine de texte
|
|
||||||
if( csv[ j + 1 ] == "\"" ) {
|
|
||||||
//on a "" consecutif qu'on remplace par "" et on fait j+1
|
|
||||||
txtencours += "\"\""
|
|
||||||
j++
|
|
||||||
} else {
|
|
||||||
// on a bien une fermeture
|
|
||||||
flagouvert = false
|
|
||||||
newcsv += txtencours.replace( sepreg, carremplacant )
|
|
||||||
txtencours = "\""
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// on ouvre une chaine
|
|
||||||
flagouvert = true
|
|
||||||
//on met le contenu précédent ds newcsv
|
|
||||||
newcsv += txtencours
|
|
||||||
txtencours = "\""
|
|
||||||
}
|
|
||||||
//} else if((csv[j] !== "\n") && (csv[j + 1] && csv[j] + csv[j + 1] !== "\n\r")) {
|
|
||||||
} else if( csv[ j ] !== "\n" ) {
|
|
||||||
txtencours += csv[ j ]
|
|
||||||
// } else if((csv[j] == "\n") || (csv[j + 1] && csv[j] + csv[j + 1] == "\n\r")) {
|
|
||||||
} else if( csv[ j ] == "\n" ) {
|
|
||||||
if( !flagouvert ) txtencours += "\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newcsv + txtencours
|
|
||||||
}
|
|
||||||
utils.analysestring = ( string ) => {
|
|
||||||
let buftxt = ""
|
|
||||||
let bufcode = ""
|
|
||||||
let i = 0
|
|
||||||
let avecRL = false
|
|
||||||
for( let p = 0; p < string.length; p++ ) {
|
|
||||||
if( string[ p ].charCodeAt() == 10 ) {
|
|
||||||
buftxt += "[RL]"
|
|
||||||
avecRL = true
|
|
||||||
} else {
|
|
||||||
buftxt += string[ p ]
|
|
||||||
}
|
|
||||||
bufcode += "-" + string[ p ].charCodeAt();
|
|
||||||
if( i == 20 ) {
|
|
||||||
if( avecRL ) {
|
|
||||||
console.log( `${buftxt} - ${bufcode}` )
|
|
||||||
} else {
|
|
||||||
console.log( `${buftxt} ---- ${bufcode}` )
|
|
||||||
}
|
|
||||||
i = 0;
|
|
||||||
buftxt = ""
|
|
||||||
bufcode = ""
|
|
||||||
avecRL = false
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const txtstring = `32932,BK_F2F_B_COM_10x1H-09,"My Communication Workshop ""Session N°9 - 1H""","<p> </p>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>Learner who needs to develop their ability to communicate effectively at work, both in writing and speaking</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>",,english,2,0,,2,0,classroom,"0000-00-00 00:00:00","0000-00-00 00:00:00",0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32932,1101,,"BUSINESS KEYS",0,
|
|
||||||
32933,BK_F2F_B_COM_10x1H-10,"My Communication Workshop Session N°10 - 1H","<p> </p>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>Learner who needs to develop their ability to communicate effectively at work, both in writing and speaking</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>",,english,2,0,,2,0,classroom,"0000-00-00 00:00:00","0000-00-00 00:00:00",0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32933,1101,,"BUSINESS KEYS",0,
|
|
||||||
32934,BK_F2F_B_JOB_10x1H-01,"My Job Search Workshop Session N°1 - 1H","<p>PACK JOB SEARCH</p>",,english,2,0,,2,0,classroom,,,0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32934,1108,,,0,
|
|
||||||
32935,BK_F2F_B_JOB_10x1H-02,"My Job Search Workshop Session N°2 - 1H","<p>PACK JOB SEARCH</p>",,english,2,0,,2,0,classroom,,,0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32935,1108,,,0,`
|
|
||||||
//utils.analysestring(txtstring)
|
|
||||||
//console.log(utils.replacecarbtweendblquote(txtstring, ",", 'CARSEPARATOR')
|
|
||||||
// .split("\n")[0].split(","))
|
|
||||||
utils.csv2json = ( csv, options, callback ) => {
|
|
||||||
// EN CAS DE PB AVEC UN FICHIER EXCEL RECALCITRANT
|
|
||||||
// l'ouvrir dans calc linux et sauvegarder csv utf8, ; , " enregistrer le contenu de la cellule comme affiché
|
|
||||||
console.log( '\n--------------- CSV2JSON ---------------\n' );
|
|
||||||
// Default CSV options
|
|
||||||
if( !options.retln ) options.retln = '\n';
|
|
||||||
if( csv.indexOf( '\n\r' ) > -1 ) options.retln = '\n\r';
|
|
||||||
if( !options.sep ) options.sep = ';';
|
|
||||||
//gestion d un separateur dans une chaine de texte
|
|
||||||
//const regseptext = new RegExp(`${options.sep}(?!(?:[^"]*"[^"]*")*[^"]*$)`, 'gm');
|
|
||||||
//csv = csv.replace(regseptext, "CARACSEPAR");
|
|
||||||
// csv = utils.replacecarbtweendblquote(csv, options.retln, "RETLIGNE")
|
|
||||||
csv = utils.replacecarbtweendblquote( csv, options.sep, "CARSEPARATOR" )
|
|
||||||
if( !options.replacespecialcarCsv2Json ) {
|
|
||||||
options.replacespecialcarCsv2Json = []
|
|
||||||
} else {
|
|
||||||
if( typeof options.replacespecialcarCsv2Json == "string" ) {
|
|
||||||
//permet de passer des regex en string
|
|
||||||
options.replacespecialcarCsv2Json = eval( options.replacespecialcarCsv2Json );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const result = [];
|
|
||||||
const lines = csv.split( options.retln );
|
|
||||||
const headers = utils.getHeaders( lines, options.sep );
|
|
||||||
let unknownHeaders = '';
|
|
||||||
//console.log('headers', headers)
|
|
||||||
//console.log('options.champs', options.champs)
|
|
||||||
headers.forEach( header => {
|
|
||||||
// Si un header n'est pas présent dans la liste des champs prédéfinis
|
|
||||||
// on l'ajoute aux champs inconnus
|
|
||||||
if( options.champs.indexOf( header ) === -1 ) {
|
|
||||||
unknownHeaders += `${header}, `;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
if( unknownHeaders !== '' ) {
|
|
||||||
const errorMsg = `CSV2JSON() - Champs inconnus : ${unknownHeaders}`;
|
|
||||||
return callback( errorMsg, null );
|
|
||||||
}
|
|
||||||
lines.forEach( ( line, index ) => {
|
|
||||||
// Skip headers line or empty lines
|
|
||||||
if( index === 0 || line.replace( /\s/g, '' )
|
|
||||||
.length === 0 ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// pour debuguer on met origincsv pour voir la ligne d'origine
|
|
||||||
const currentLineData = { 'origincsv': line, 'linenumber': index };
|
|
||||||
const currentLine = line.split( options.sep ); // Current string in the line
|
|
||||||
for( let j = 0; j < headers.length; j++ ) {
|
|
||||||
// Si la ligne n'est pas vide
|
|
||||||
if( currentLine[ j ] ) {
|
|
||||||
// On clean le champs
|
|
||||||
// ajout eventuel de modification de caracter reservé ; dans les libelléetc...
|
|
||||||
let currentValue = currentLine[ j ].trim()
|
|
||||||
//on transforme le caractere separateur modifié entre double quote
|
|
||||||
currentValue = currentValue.replace( 'CARSEPARATOR', options.sep );
|
|
||||||
options.replacespecialcarCsv2Json.forEach( re => {
|
|
||||||
currentValue = currentValue.replace( re[ 0 ], re[ 1 ] )
|
|
||||||
} )
|
|
||||||
// Si le header est un email
|
|
||||||
if( headers[ j ].includes( 'EMAIL' ) ) {
|
|
||||||
// Supprimer tous les espaces
|
|
||||||
currentValue = currentLine[ j ].replace( /\s/g, '' );
|
|
||||||
}
|
|
||||||
// on check si le chamos doit être numerique
|
|
||||||
if( options.numericfield.includes( headers[ j ] ) ) {
|
|
||||||
currentValue = currentLine[ j ].replace( /\,/g, '.' );
|
|
||||||
try {
|
|
||||||
const test = parseFloat( currentValue );
|
|
||||||
} catch ( er ) {
|
|
||||||
return callback( `${headers[j]} contiens la valeur -${currentValue}- et devrait être numerique`, null );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( currentValue ) {
|
|
||||||
// Si le header actuel est de type array
|
|
||||||
// Cela signifie que le header apparaît plusieurs fois dans le CSV
|
|
||||||
// et que les valeurs correspondantes à ce header
|
|
||||||
// doivent être mis dans un array
|
|
||||||
if( options.array && options.array.indexOf( headers[ j ] ) > -1 ) {
|
|
||||||
// Si le tableau pour ce header n'existe pas on le crée
|
|
||||||
if( !currentLineData[ headers[ j ] ] ) {
|
|
||||||
currentLineData[ headers[ j ] ] = [];
|
|
||||||
}
|
|
||||||
if( options.arraysplitsep ) {
|
|
||||||
currentValue.split( options.arraysplitsep )
|
|
||||||
.forEach( v => {
|
|
||||||
currentLineData[ headers[ j ] ].push( v );
|
|
||||||
} )
|
|
||||||
} else {
|
|
||||||
currentLineData[ headers[ j ] ].push( currentValue );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Si un header est déjà présent pour la ligne
|
|
||||||
// alors que il n'est pas spécifié comme étant un array
|
|
||||||
// on retourne une erreur
|
|
||||||
if( currentLineData[ headers[ j ] ] ) {
|
|
||||||
const errorMsg = `Le champ ${
|
|
||||||
headers[j]
|
|
||||||
} est présent plusieurs fois alors qu'il n'est pas spécifié comme étant un array !`;
|
|
||||||
return callback( errorMsg, null );
|
|
||||||
}
|
|
||||||
currentLineData[ headers[ j ] ] = currentValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.push( currentLineData );
|
|
||||||
} );
|
|
||||||
return callback( null, result );
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* [csvparam2json description]
|
|
||||||
* @param {object} csv object of csv file that has been read
|
|
||||||
* @param {object} options object containing csv options, headers, ...
|
|
||||||
{retln:'code de retour de ligne \n ou \n\r',
|
|
||||||
sep:'code to split cells',
|
|
||||||
champs:[ch1,ch2,...] catch only those field,
|
|
||||||
array:[ch1, ] can have more than one field champs with same name then data are push into an array }
|
|
||||||
* @param {Function} callback callback function
|
|
||||||
* @return {callback} - return an error if error, else return json
|
|
||||||
it converts a csv with 3 column col1;col2;col3 in a json in a tree
|
|
||||||
if in col1 we have __ => then it splits a leaf
|
|
||||||
col1 = xxxx__yyyy ; col2 = value ; col3 = comment that is ignored
|
|
||||||
return data = {xxxx:{yyyy:value}}
|
|
||||||
col1 = xxxx; col2 = value; col3 = comment ignored
|
|
||||||
return data = {xxxx:value}
|
|
||||||
|
|
||||||
Usage example:
|
|
||||||
fiche.csvparam2article = (err, fiche) => {
|
|
||||||
if (!err) {
|
|
||||||
console.log(fiche)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
utils.csvparam2json(fs.readFileSync('./devdata/tribee/aubergenville/infoexterne/localbusiness.csv', 'utf-8'), {
|
|
||||||
retln: "\n",
|
|
||||||
sep: ";",
|
|
||||||
champs: ["NOM", "OBJET", "ADRESSE_PRO", "CP_PRO", "VILLE_PRO", "ZONE", "PHONE_PRO", "HORAIRESDESC", "HORAIREDATA", "URL", "FACEBOOK", "INSTA", "EMAIL_PRO", "IMG", "TAG"],
|
|
||||||
array: ["TAG", "PHONE_PRO", "EMAIL_PRO"]
|
|
||||||
}, fiche.csv2article)
|
|
||||||
|
|
||||||
*/
|
|
||||||
utils.csvparam2json = ( csv, options, callback ) => {
|
|
||||||
console.log( '\n--------------- CSVPARAM2JSON ---------------\n' );
|
|
||||||
let etat = "";
|
|
||||||
const param = {};
|
|
||||||
if( !options.retln ) {
|
|
||||||
options.retln = '\n';
|
|
||||||
}
|
|
||||||
if( csv.indexOf( '\n\r' ) > -1 ) {
|
|
||||||
options.retln = '\n\r';
|
|
||||||
}
|
|
||||||
if( !options.sep ) {
|
|
||||||
options.sep = ';';
|
|
||||||
}
|
|
||||||
if( !options.seplevel ) {
|
|
||||||
options.seplevel = "__";
|
|
||||||
}
|
|
||||||
if( !options.replacespecialcarCsv2Json ) {
|
|
||||||
options.replacespecialcarCsv2Json = []
|
|
||||||
} else {
|
|
||||||
if( typeof options.replacespecialcarCsv2Json == "string" ) {
|
|
||||||
//permet de passer des regex en string
|
|
||||||
options.replacespecialcarCsv2Json = eval( options.replacespecialcarCsv2Json );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const lines = csv.split( options.retln );
|
|
||||||
for( let i = 0; i < lines.length; i++ ) {
|
|
||||||
const infol = lines[ i ].split( options.sep )
|
|
||||||
//console.log(infol)
|
|
||||||
if( infol[ 0 ].length > 4 && infol.length < 2 ) {
|
|
||||||
// si le 1er element à plus de 4 caractere et s'il y a moins de 3 colonnes c'est qu'il y a un pb
|
|
||||||
etat += `Erreur sur ${lines[i]} moins de 3 column separé par ${options.sep}`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// On ajoute ici la gestion de tous les caracteres spéciaux
|
|
||||||
// reservées pour le csv ; ' etc..'
|
|
||||||
if( infol[ 1 ] && infol[ 1 ] + "" == infol[ 1 ] ) {
|
|
||||||
options.replacespecialcarCsv2Json.forEach( re => {
|
|
||||||
//console.log("gggggggggggggggggggg", infol[1])
|
|
||||||
infol[ 1 ] = infol[ 1 ].replace( re[ 0 ], re[ 1 ] );
|
|
||||||
} )
|
|
||||||
// console.log(infol[1])
|
|
||||||
infol[ 1 ] = infol[ 1 ].replace( /'|’/g, "\"" );
|
|
||||||
//console.log(infol[1])
|
|
||||||
if( infol[ 1 ].toLowerCase() === 'true' ) {
|
|
||||||
infol[ 1 ] = true;
|
|
||||||
} else if( infol[ 1 ].toLowerCase() === 'false' ) {
|
|
||||||
infol[ 1 ] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log( infol[ 1 ] )
|
|
||||||
//supprime des lignes vides
|
|
||||||
if( infol[ 0 ] == '' ) continue;
|
|
||||||
if( infol[ 0 ].indexOf( options.seplevel ) == -1 ) {
|
|
||||||
param[ infol[ 0 ] ] = infol[ 1 ]
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
const arbre = infol[ 0 ].split( options.seplevel )
|
|
||||||
switch ( arbre.length ) {
|
|
||||||
case 1:
|
|
||||||
param[ arbre[ 0 ] ] = infol[ 1 ];
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if( arbre[ 1 ] != "ARRAY" ) {
|
|
||||||
if( !param[ arbre[ 0 ] ] ) param[ arbre[ 0 ] ] = {};
|
|
||||||
param[ arbre[ 0 ] ][ arbre[ 1 ] ] = infol[ 1 ];
|
|
||||||
} else {
|
|
||||||
if( !param[ arbre[ 0 ] ] ) param[ arbre[ 0 ] ] = [];
|
|
||||||
//console.log('aff', infol[1].substring(1, infol[1].length - 1).replace(/""/g, '"'))
|
|
||||||
eval( "result=" + infol[ 1 ] )
|
|
||||||
//.substring(1, infol[1].length - 1).replace(/""/g, '"'))
|
|
||||||
param[ arbre[ 0 ] ].push( result )
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
if( arbre[ 2 ] != "ARRAY" ) {
|
|
||||||
if( !param[ arbre[ 0 ] ] ) param[ arbre[ 0 ] ] = {};
|
|
||||||
if( !param[ arbre[ 0 ] ][ arbre[ 1 ] ] ) param[ arbre[ 0 ] ][ arbre[ 1 ] ] = {};
|
|
||||||
param[ arbre[ 0 ] ][ arbre[ 1 ] ][ arbre[ 2 ] ] = infol[ 1 ];
|
|
||||||
} else {
|
|
||||||
if( !param[ arbre[ 0 ] ] ) param[ arbre[ 0 ] ] = {};
|
|
||||||
if( !param[ arbre[ 0 ] ][ arbre[ 1 ] ] ) param[ arbre[ 0 ] ][ arbre[ 1 ] ] = [];
|
|
||||||
//eval("result = \"test\"");
|
|
||||||
//console.log(result);
|
|
||||||
eval( "result=" + infol[ 1 ] );
|
|
||||||
//.substring(1, infol[1].length - 1).replace(/""/g, '"'))
|
|
||||||
param[ arbre[ 0 ] ][ arbre[ 1 ] ].push( result )
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
if( arbre[ 3 ] != "ARRAY" ) {
|
|
||||||
if( !param[ arbre[ 0 ] ] ) param[ arbre[ 0 ] ] = {};
|
|
||||||
if( !param[ arbre[ 0 ] ][ arbre[ 1 ] ] ) param[ arbre[ 0 ] ][ arbre[ 1 ] ] = {};
|
|
||||||
if( !param[ arbre[ 0 ] ][ arbre[ 1 ] ][ arbre[ 2 ] ] ) param[ arbre[ 0 ] ][ arbre[ 1 ] ][ arbre[ 2 ] ] = {};
|
|
||||||
param[ arbre[ 0 ] ][ arbre[ 1 ] ][ arbre[ 2 ] ][ arbre[ 3 ] ] = infol[ 1 ];
|
|
||||||
} else {
|
|
||||||
if( !param[ arbre[ 0 ] ] ) param[ arbre[ 0 ] ] = {};
|
|
||||||
if( !param[ arbre[ 0 ] ][ arbre[ 1 ] ] ) param[ arbre[ 0 ] ][ arbre[ 1 ] ] = {};
|
|
||||||
if( !param[ arbre[ 0 ] ][ arbre[ 1 ] ][ arbre[ 2 ] ] ) param[ arbre[ 0 ] ][ arbre[ 1 ] ][ arbre[ 2 ] ] = [];
|
|
||||||
eval( "result=" + infol[ 1 ] )
|
|
||||||
//.substring(1, infol[1].length - 1).replace(/""/g, '"'))
|
|
||||||
param[ arbre[ 0 ] ][ arbre[ 1 ] ][ arbre[ 2 ] ].push( result )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// JSON.parse(JSON.stringify(param))
|
|
||||||
console.log( 'kkkkkkkkkkkkkkkkkk', param[ 'catalogue' ][ 'filtrecatalog' ][ 'searchengine' ] )
|
|
||||||
if( etat == "" ) {
|
|
||||||
return callback( null, JSON.parse( JSON.stringify( param ) ) );
|
|
||||||
} else {
|
|
||||||
return callback( etat, null );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
utils.levenshtein = ( a, b ) => {
|
|
||||||
if( a.length === 0 ) return b.length;
|
|
||||||
if( b.length === 0 ) return a.length;
|
|
||||||
let tmp, i, j, prev, val, row;
|
|
||||||
// swap to save some memory O(min(a,b)) instead of O(a)
|
|
||||||
if( a.length > b.length ) {
|
|
||||||
tmp = a;
|
|
||||||
a = b;
|
|
||||||
b = tmp;
|
|
||||||
}
|
|
||||||
row = Array( a.length + 1 );
|
|
||||||
// init the row
|
|
||||||
for( i = 0; i <= a.length; i++ ) {
|
|
||||||
row[ i ] = i;
|
|
||||||
}
|
|
||||||
// fill in the rest
|
|
||||||
for( i = 1; i <= b.length; i++ ) {
|
|
||||||
prev = i;
|
|
||||||
for( j = 1; j <= a.length; j++ ) {
|
|
||||||
if( b[ i - 1 ] === a[ j - 1 ] ) {
|
|
||||||
val = row[ j - 1 ]; // match
|
|
||||||
} else {
|
|
||||||
val = Math.min( row[ j - 1 ] + 1, // substitution
|
|
||||||
Math.min( prev + 1, // insertion
|
|
||||||
row[ j ] + 1 ) ); // deletion
|
|
||||||
}
|
|
||||||
row[ j - 1 ] = prev;
|
|
||||||
prev = val;
|
|
||||||
}
|
|
||||||
row[ a.length ] = prev;
|
|
||||||
}
|
|
||||||
return row[ a.length ];
|
|
||||||
};
|
|
||||||
utils.testinarray = ( array, arrayreferent ) => {
|
|
||||||
// au moins un element de array existe dans arryreferent
|
|
||||||
let exist = false;
|
|
||||||
if( arrayreferent ) {
|
|
||||||
//console.log('arrrrrrrrrrrrrrr', arrayreferent)
|
|
||||||
array.forEach( e => {
|
|
||||||
//console.log(e)
|
|
||||||
if( arrayreferent.includes( e ) ) exist = true
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
return exist
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
DIRECTORY
|
|
||||||
*/
|
|
||||||
const isDirectory = source => fs.lstatSync( source )
|
|
||||||
.isDirectory();
|
|
||||||
const getDirectories = source => fs.readdirSync( source )
|
|
||||||
.map( name => path.join( source, name ) )
|
|
||||||
.filter( isDirectory );
|
|
||||||
module.exports = utils;
|
|
12
package.json
12
package.json
@ -17,8 +17,8 @@
|
|||||||
"restartpm2": "pm2 restart apxtrib.js --log-date-format 'DD-MM HH:mm:ss.SSS'",
|
"restartpm2": "pm2 restart apxtrib.js --log-date-format 'DD-MM HH:mm:ss.SSS'",
|
||||||
"startblockchain": "pm2 start ./models/Blockchains.js --log-date-format 'DD-MM HH:mm:ss:SSS'",
|
"startblockchain": "pm2 start ./models/Blockchains.js --log-date-format 'DD-MM HH:mm:ss:SSS'",
|
||||||
"logpm2": "pm2 logs apxtrib.js --lines 200",
|
"logpm2": "pm2 logs apxtrib.js --lines 200",
|
||||||
"setup": "node models/Setup.js",
|
"setup": "node src/models/Setup.js",
|
||||||
"dev": "node apxtrib.js",
|
"dev": "node src/apxtrib.js",
|
||||||
"dev-watch": "nodemon apxtrib.js"
|
"dev-watch": "nodemon apxtrib.js"
|
||||||
},
|
},
|
||||||
"apidoc": {
|
"apidoc": {
|
||||||
@ -120,9 +120,15 @@
|
|||||||
"simplebar": "^5.3.6",
|
"simplebar": "^5.3.6",
|
||||||
"stripe": "^7.4.0",
|
"stripe": "^7.4.0",
|
||||||
"swiper": "^8.0.6",
|
"swiper": "^8.0.6",
|
||||||
"uuid": "^3.2.1"
|
"uuid": "^3.2.1",
|
||||||
|
"winston": "^3.8.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^1.17.3"
|
"nodemon": "^1.17.3"
|
||||||
|
},
|
||||||
|
"standard": {
|
||||||
|
"ignore": [
|
||||||
|
"src/setup/data/**/*.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
const express = require( 'express' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
// Classes
|
|
||||||
const Messages = require( '../models/Messages.js' );
|
|
||||||
// Middlewares ( prefix, object ) => {
|
|
||||||
const checkHeaders = require( '../middlewares/checkHeaders' );
|
|
||||||
const isAuthenticated = require( '../middlewares/isAuthenticated' );
|
|
||||||
const hasAccessrighton = require( '../middlewares/hasAccessrighton' );
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.post( '/', checkHeaders, ( req, res ) => {
|
|
||||||
/*
|
|
||||||
add message to (no authentification and accessright needs) :
|
|
||||||
a tribeid or uuid => create a contact based on email.json or phone.json or email_phone.json
|
|
||||||
if req.body.orderuuid exist then it store the req.body in /orders/orderuuid.json an order with state = order
|
|
||||||
*/
|
|
||||||
// check if a receiver is well identify if not then it send message to all user tribeid to inform **
|
|
||||||
if( !req.body.desttribeid ) req.body.desttribeid = req.session.header.xworkon;
|
|
||||||
if( !req.body.lang ) req.body.lang = req.session.header.xlang;
|
|
||||||
console.log( '/messages t send for ', req.session.header.xworkon );
|
|
||||||
//console.log(' Content: ',req.body);
|
|
||||||
const result = Messages.postinfo( req.body );
|
|
||||||
res.status( result.status )
|
|
||||||
.send( result.data )
|
|
||||||
} );
|
|
||||||
|
|
||||||
router.put( '/:objectname/:uuid', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
// message that will create an object and sendback an email.
|
|
||||||
// if objectnane/uuid_lg.json exist ans accessright is ste to U for the user then it replace object data with req.body.key value
|
|
||||||
// if does not exist and accessright C then it create it with uuid
|
|
||||||
// then if req.body.tplmessage => render email with data
|
|
||||||
// No data management are done here, if need you can add plugin to create a workflow based object
|
|
||||||
// if need specific data check => req.body.callback={tribeidpugin,pluginname,function} will run pluginname.function(data) add data run specific stuf before saved the message object in /objectname/data.uuid_lg/json
|
|
||||||
let result;
|
|
||||||
console.log( "object", req.params.objectname )
|
|
||||||
if( req.params.objectname == 'notifications' ) {
|
|
||||||
//uuid is a timestamp
|
|
||||||
req.body.time = req.params.uuid;
|
|
||||||
result = Messages.notification( req.body, req.session.header );
|
|
||||||
} else {
|
|
||||||
req.body.uuid = req.params.uuid;
|
|
||||||
req.body.object = req.params.objectname;
|
|
||||||
result = Messages.object( req.body, req.session.header );
|
|
||||||
}
|
|
||||||
//console.log( 'result', result );
|
|
||||||
res.status( result.status )
|
|
||||||
.json( result.data )
|
|
||||||
} );
|
|
||||||
|
|
||||||
router.get( '/user', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
// run agregate for tribeid concerned
|
|
||||||
//
|
|
||||||
console.log( "request notifiation for user", req.session.header.xpaganid );
|
|
||||||
const app = {
|
|
||||||
tribeid: req.session.header.xapp.split( ':' )[ 0 ],
|
|
||||||
website: req.session.header.xapp.split( ':' )[ 1 ],
|
|
||||||
lang: req.session.header.xlang
|
|
||||||
};
|
|
||||||
res.send( Messages.request( req.session.header.xtribe, req.session.header.xpaganid,
|
|
||||||
req.app.locals.tokens[ req.session.header.xpaganid ].ACCESSRIGHTS, app ) );
|
|
||||||
} );
|
|
||||||
|
|
||||||
module.exports = router;
|
|
@ -1,37 +0,0 @@
|
|||||||
const express = require( 'express' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
|
|
||||||
// Classes
|
|
||||||
const Nationchains = require( '../models/Nationchains.js' );
|
|
||||||
// Middlewares
|
|
||||||
const checkHeaders = require( '../middlewares/checkHeaders' );
|
|
||||||
const isAuthenticated = require( '../middlewares/isAuthenticated' );
|
|
||||||
const hasAccessrighton = require( '../middlewares/hasAccessrighton' );
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
/*
|
|
||||||
Manage the social world
|
|
||||||
|
|
||||||
@Todo
|
|
||||||
|
|
||||||
Manage a new nation
|
|
||||||
A major create a nation with at least a town => nation:{name, towns:[]} contracts/nationname.js + contracts/townsname.js
|
|
||||||
Manage a new towns in a nation => update nation:[nationname:towns:[]} contracts/townname.js
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
router.post( '/push', checkHeaders, ( req, res ) => {
|
|
||||||
// Get information from other apixtribe instance in req.body
|
|
||||||
// check req.body.hashnext => means this is a candidate to validate next block
|
|
||||||
//
|
|
||||||
// return it's own information back with the last call to Nationchains.synchronize()
|
|
||||||
res.send( { status: 200, payload: { moreinfo: fs.readFileSync( `${config.tribes}/${config.mayorId}/nationchains/nodes/${config.rootURL}`, 'utf-8' ) } } )
|
|
||||||
} )
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
|
@ -1,19 +0,0 @@
|
|||||||
const express = require( 'express' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
// Classes
|
|
||||||
const Odmdb = require( '../models/Odmdb.js' );
|
|
||||||
// Middlewares
|
|
||||||
const checkHeaders = require( '../middlewares/checkHeaders' );
|
|
||||||
const isAuthenticated = require( '../middlewares/isAuthenticated' );
|
|
||||||
const hasAccessrighton = require( '../middlewares/hasAccessrighton' );
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.get('/searchauth/:objectname/:question',checkHeaders,isAuthenticated,( req, res ) => {
|
|
||||||
console.log( 'route referentials get all language' + req.params.objectname + '-' + req.params.question );
|
|
||||||
const getref = Referentials.getref( true, req.params.source, req.params.idref, req.session.header.xworkon, req.session.header.xlang );
|
|
||||||
// Return any status the data if any erreur return empty object
|
|
||||||
res.jsonp( getref.payload.data );
|
|
||||||
} );
|
|
||||||
|
|
||||||
module.exports = router;
|
|
@ -1,65 +0,0 @@
|
|||||||
// Upload de file
|
|
||||||
const express = require( 'express' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
// Classes
|
|
||||||
const UploadFile = require( '../models/UploadFiles' );
|
|
||||||
const Outputs = require( '../models/Outputs' );
|
|
||||||
//const Outputstest = require('../models/Outputstest');
|
|
||||||
// Middlewares
|
|
||||||
const checkHeaders = require( '../middlewares/checkHeaders' );
|
|
||||||
const isAuthenticated = require( '../middlewares/isAuthenticated' );
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
|
|
||||||
router.post( '/ggsheet2json', checkHeaders, async ( req, res ) => {
|
|
||||||
console.log( 'route outputs sheet to json' );
|
|
||||||
let result = await Outputs.ggsheet2json( req.body, req.session.header );
|
|
||||||
res.send( result );
|
|
||||||
} );
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// checkHeaders, isuploadFileValid
|
|
||||||
router.post( '/msg', checkHeaders, async ( req, res ) => {
|
|
||||||
console.log( 'route outputs msg post ' );
|
|
||||||
const envoi = await Outputs.generemsg( req.body, req.session.header );
|
|
||||||
res.status( envoi.status )
|
|
||||||
.send( {
|
|
||||||
payload: envoi.payload
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
/*test functionnalité
|
|
||||||
router.post('/msgtest', checkHeaders, isemailValid, async (req, res) => {
|
|
||||||
console.log('route outputs msg post en test');
|
|
||||||
const envoi = await Outputstest.generemsg(req.body, req.session.header);
|
|
||||||
res.status(envoi.status).send({
|
|
||||||
payload: envoi.payload
|
|
||||||
});
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
router.post( '/template', checkHeaders, ( req, res ) => {
|
|
||||||
console.log( 'route outputs post de fichier template ' );
|
|
||||||
// a callback can be pass to req.body to run a specific process after upload
|
|
||||||
const saveFile = UploadFile.addjson( req.body, req.session.header );
|
|
||||||
console.log( saveFile );
|
|
||||||
res.send( saveFile );
|
|
||||||
// res.send({ status: 200, payload: { info: 'fine' } });
|
|
||||||
} );
|
|
||||||
|
|
||||||
router.post( '/pdf', checkHeaders, ( req, res ) => {
|
|
||||||
console.log( 'route outputs pdf post' );
|
|
||||||
Outputs.generepdf( req.body, req.session.header )
|
|
||||||
.then( ( doc ) => {
|
|
||||||
res.status( doc.status )
|
|
||||||
.download( doc.payload.data.path, doc.payload.data.name );
|
|
||||||
} )
|
|
||||||
.catch( ( err ) => {
|
|
||||||
console.log( err );
|
|
||||||
res.status( err.status )
|
|
||||||
.send( { payload: err.payload } );
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
|
211
routes/pagans.js
211
routes/pagans.js
@ -1,211 +0,0 @@
|
|||||||
const express = require( 'express' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
|
|
||||||
// Classes
|
|
||||||
const Pagans = require( '../models/Pagans.js' );
|
|
||||||
// Middlewares
|
|
||||||
const checkHeaders = require( '../middlewares/checkHeaders' );
|
|
||||||
const isAuthenticated = require( '../middlewares/isAuthenticated' );
|
|
||||||
const hasAccessrighton = require( '../middlewares/hasAccessrighton' );
|
|
||||||
const router = express.Router();
|
|
||||||
/*
|
|
||||||
models/Pagans.js
|
|
||||||
Managed:
|
|
||||||
/data/tribee/client-Id/users/uuid.json
|
|
||||||
/searchindex/emails.json {email:uuid}
|
|
||||||
/login.json {login:uuid}
|
|
||||||
/uids.json {uuid;[[
|
|
||||||
login,
|
|
||||||
email,
|
|
||||||
encrypted psw,
|
|
||||||
accessrights]}
|
|
||||||
|
|
||||||
ACCESSRIGHTS = {
|
|
||||||
app:{"tribeid:appname":"profil"},
|
|
||||||
data:{"tribeid":{object:"CRUDO"}}
|
|
||||||
}
|
|
||||||
ACCESSRIGHTS is store into the token and is load into req.session.header.accessrights by hasAccessrighton() middleware
|
|
||||||
|
|
||||||
appname is a website space object /sitewebsrc/appname
|
|
||||||
website live is strored into /dist source in /src
|
|
||||||
|
|
||||||
This can be managed by maildigitcreator or not.
|
|
||||||
apixtribe/sitewebs/webapp is the webinterface of apixtribe
|
|
||||||
|
|
||||||
profil: admin / manager / user are key word to give specific access to data into model. Any kind of other profil can exist. It is usefull to manage specific menu in an app.
|
|
||||||
It is also possible to authorize update a field's object depending of rule into dataManagement/object/
|
|
||||||
{ field:X
|
|
||||||
nouserupdate: "!(['admin','manager'].includes(contexte.profil))",
|
|
||||||
}
|
|
||||||
|
|
||||||
data allow a user to access tribeid with Create Read Update Delete Own (CRUDO) on each object of a tribeid independantly of any app.
|
|
||||||
|
|
||||||
Create allow to create a new object respecting rules defined into /referentials/dataManagement/object/name.json
|
|
||||||
Update idem
|
|
||||||
Delete idem
|
|
||||||
Owner means it can be Write/Delete if field OWNER contain the UUID that try to act on this object. Usefull to allow someone to fully manage its objects.
|
|
||||||
|
|
||||||
*/
|
|
||||||
router.get( '/isauth', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
if( req.session.header.xpaganid == "1" ) {
|
|
||||||
return res.status( 401 )
|
|
||||||
.send( { info: "not authenticate" } );
|
|
||||||
} else return res.status( 200 )
|
|
||||||
.send( { info: "well authenticated" } )
|
|
||||||
} )
|
|
||||||
router.post( '/login', checkHeaders, async ( req, res ) => {
|
|
||||||
// console.log('POST /users/login with: ', req.app.locals.header);
|
|
||||||
/*
|
|
||||||
Check un mot de passe pour un login pour obtenir un token d'authentification
|
|
||||||
valable 1 hour, 1 day
|
|
||||||
@header
|
|
||||||
@body.LOGIN
|
|
||||||
@body.PASSWORD
|
|
||||||
@checkpsw = true check si les 2 mot de passe cryptés correspondent
|
|
||||||
false bypass le contrôle et permet de générer un token
|
|
||||||
utile le temps de reinitialisé son mot de passe.
|
|
||||||
@return
|
|
||||||
*/
|
|
||||||
console.log( 'login for ', req.body, "in", req.session.header )
|
|
||||||
const log = await Pagans.loginUser( req.session.header, req.body, true );
|
|
||||||
console.log( "log user login", log );
|
|
||||||
if( log.status == 200 ) {
|
|
||||||
// update req.app.locals.tokens for this uuid just after login success then next isAuth will be valid
|
|
||||||
req.app.locals.tokens[ log.data.user.UUID ] = { TOKEN: log.data.user.TOKEN, ACCESSRIGHTS: log.data.user.ACCESSRIGHTS }
|
|
||||||
console.log( req.app.locals )
|
|
||||||
}
|
|
||||||
return res.status( log.status )
|
|
||||||
.send( log.data );
|
|
||||||
} );
|
|
||||||
|
|
||||||
|
|
||||||
router.get( '/getlinkwithoutpsw/:email', checkHeaders, async ( req, res ) => {
|
|
||||||
/*
|
|
||||||
Permet pour un email existant de renvoyer un email avec un lien valable 1h
|
|
||||||
@email est le compte pour lequel on demande un accès
|
|
||||||
Réponse:
|
|
||||||
Si email n'existe pas on n'envoie pas d'email
|
|
||||||
Si email existe on envoie un email avec un lien dont le token est valable 1h
|
|
||||||
|
|
||||||
@return
|
|
||||||
{status:200 ou erreur ,
|
|
||||||
payload:{
|
|
||||||
info:[list de key to appear in correct requester langue],
|
|
||||||
model:'Pagans',
|
|
||||||
moreinfo: 'texte pour log '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
console.log( `GET /users/getlinkwithoutpsw for email: ${req.params.email} tribeid :${req.header('X-Client-Id')}` );
|
|
||||||
if( !req.params.email ) {
|
|
||||||
return res.status( 404 )
|
|
||||||
.send( {
|
|
||||||
info: [ 'emailmissing' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
} );
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const getlink = await Pagans.getlinkwithoutpsw( req.params.email, req.session.header );
|
|
||||||
console.log( 'getlink', getlink )
|
|
||||||
//met à jour le token créer pour le uuid
|
|
||||||
req.app.locals.tokens[ getlink.data.info.xuuid ] = getlink.data.info.token;
|
|
||||||
// attention si on relance le serveur le token temporaire est perdu
|
|
||||||
return res.status( getlink.status )
|
|
||||||
.send( getlink.data );
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( err )
|
|
||||||
return res.status( 500 )
|
|
||||||
.send( { info: [ 'errServer' ], model: 'Pagans' } );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
router.post( '/register', checkHeaders, async ( req, res ) => {
|
|
||||||
console.log( `POST /users for ${req.session.header.xtribe}` );
|
|
||||||
if( req.session.header.xauth == '123123' ) {
|
|
||||||
// Creation d'un utilisateur avec information de base aucun droit
|
|
||||||
// On modifie le contenu du form pour n egarder que login/email et psw
|
|
||||||
// pour le client_id permet de traiter un user en attente de validation
|
|
||||||
console.log( 'req du post', req );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
router.get( '/info/:listindex', checkHeaders, isAuthenticated, hasAccessrighton( 'users', 'R' ), async ( req, res ) => {
|
|
||||||
console.log( `get users info on tribeid ${req.session.header.xworkon} for ${req.params.listindex} with accessright`, req.session.header.accessrights.data );
|
|
||||||
const result = await Pagans.getinfoPagans( req.session.header.xpresworkon, req.session.header.accessrights, req.params.listindex );
|
|
||||||
res.status( result.status )
|
|
||||||
.send( result.data );
|
|
||||||
} );
|
|
||||||
router.get( '/list/:filter/:field', checkHeaders, isAuthenticated, hasAccessrighton( 'users', 'R' ), async ( req, res ) => {
|
|
||||||
console.log( 'GET /users/list/filtre/champs list for ' + req.session.header.xworkon );
|
|
||||||
if(
|
|
||||||
[ 'admin', 'manager' ].includes( req.session.header.decodetoken[ 'apps' + req.session.header.xworkon + 'profil' ] ) ) {
|
|
||||||
try {
|
|
||||||
const userslist = await Pagans.getUserlist( req.session.header, req.params.filter, req.params.field );
|
|
||||||
console.log( 'userslist', userslist );
|
|
||||||
if( userslist.status == 200 ) {
|
|
||||||
return res.status( userslist.status )
|
|
||||||
.send( userslist.data );
|
|
||||||
}
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( err );
|
|
||||||
return res.status( 400 )
|
|
||||||
.send( { info: 'erreur' } );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.status( 403 )
|
|
||||||
.send( {
|
|
||||||
info: [ 'forbiddenAccess' ],
|
|
||||||
model: 'Pagans'
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
router.get( '/uuid/:id', checkHeaders, isAuthenticated, hasAccessrighton( 'users', 'R' ), async ( req, res ) => {
|
|
||||||
console.log( `GET /users/uuid/${req.params.id}` );
|
|
||||||
//console.log('req.app.locals: ', req.app.locals);
|
|
||||||
//console.log('req.session', req.session);
|
|
||||||
const result = await Pagans.getUser( req.params.id, req.session.header.xworkon, req.session.header.accessrights );
|
|
||||||
res.status( result.status )
|
|
||||||
.send( result.data );
|
|
||||||
} );
|
|
||||||
router.put( '/chgpsw/:id', checkHeaders, isAuthenticated, async ( req, res ) => {
|
|
||||||
console.log( `PUT update /users/chgpsw/${req.params.id}` );
|
|
||||||
try {
|
|
||||||
const majpsw = await Pagans.updateUserpassword( req.params.id, req.session.header, req.body );
|
|
||||||
res.status( majpsw.status )
|
|
||||||
.send( majpsw.data );
|
|
||||||
} catch ( {
|
|
||||||
status,
|
|
||||||
data
|
|
||||||
} ) {
|
|
||||||
res.status( status )
|
|
||||||
.send( data );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
router.post( '/uuid', checkHeaders, isAuthenticated, hasAccessrighton( 'users', 'C' ), async ( req, res ) => {
|
|
||||||
console.log( 'POST /users create for ' + req.session.header.xworkon, req.body );
|
|
||||||
const usercreate = await Pagans.createUser( req.session.header, req.body );
|
|
||||||
return res.status( usercreate.status )
|
|
||||||
.send( usercreate.data );
|
|
||||||
} );
|
|
||||||
router.put( '/uuid/:id', checkHeaders, isAuthenticated, hasAccessrighton( 'users', 'U' ), async ( req, res ) => {
|
|
||||||
console.log( `PUT update /users/${req.params.id}` );
|
|
||||||
// console.log('req.app.locals: ', req.app.locals);
|
|
||||||
// console.log('req.session', req.session);
|
|
||||||
try {
|
|
||||||
const majUser = await Pagans.updateUser( req.params.id, req.session.header, req.body );
|
|
||||||
res.status( majUser.status )
|
|
||||||
.send( majUser.data );
|
|
||||||
} catch ( {
|
|
||||||
status,
|
|
||||||
data
|
|
||||||
} ) {
|
|
||||||
res.status( status )
|
|
||||||
.send( data );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
router.delete( '/uuid/:id', checkHeaders, isAuthenticated, hasAccessrighton( 'users', 'D' ), ( req, res ) => {
|
|
||||||
console.log( `DELETE /users/uuid/${req.params.id}` );
|
|
||||||
const result = Pagans.deleteUser( req.params.id, req.session.header );
|
|
||||||
res.status( result.status )
|
|
||||||
.send( result.data );
|
|
||||||
} );
|
|
||||||
module.exports = router;
|
|
@ -1,94 +0,0 @@
|
|||||||
// Upload de file
|
|
||||||
const express = require( 'express' );
|
|
||||||
const glob = require( 'glob' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
// Classes
|
|
||||||
const Referentials = require( '../models/Referentials' );
|
|
||||||
// Middlewares
|
|
||||||
const checkHeaders = require( '../middlewares/checkHeaders' );
|
|
||||||
const isAuthenticated = require( '../middlewares/isAuthenticated' );
|
|
||||||
const hasAccessrighton = require( '../middlewares/hasAccessrighton' );
|
|
||||||
const router = express.Router();
|
|
||||||
/*
|
|
||||||
* keylist = list of key at 1st level in clientconf.json separated by _
|
|
||||||
* we use header.xworkon
|
|
||||||
* To manage AccesRight obkect referentials does not follow the same logic than other object this is why
|
|
||||||
*/
|
|
||||||
router.get( '/clientconf/:keylist', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
// retourne liste info (non sensible) du tribeid inside headers.xworkon sur keylist ="key1_key2"
|
|
||||||
/*
|
|
||||||
if (req.session.header.accessrights.data[ "Alltribeid" ] && req.session.header.accessrights.data[ "Alltribeid" ].referentials.includes('R') ;
|
|
||||||
*/
|
|
||||||
console.log( `get clientconf for ${req.session.header.xworkon} on ${req.params.keylist}` )
|
|
||||||
let dataref = {}
|
|
||||||
if( req.params.keylist.split( '_' )
|
|
||||||
.length > 0 ) {
|
|
||||||
const ref = Referentials.clientconf( req.session.header.xworkon, req.params.keylist.split( '_' ) )
|
|
||||||
if( ref.status == 200 ) {
|
|
||||||
dataref = ref.payload.data;
|
|
||||||
} else {
|
|
||||||
console.log( "erreur ", ref )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log( 'envoie en jsonp: dataref' )
|
|
||||||
res.jsonp( dataref )
|
|
||||||
} );
|
|
||||||
router.get( '/clientconfglob', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
res.jsonp( Referentials.clientconfglob()
|
|
||||||
.payload.data );
|
|
||||||
} );
|
|
||||||
router.get( '/contentlist/:source', checkHeaders, isAuthenticated,
|
|
||||||
( req, res ) => {
|
|
||||||
const payload = [];
|
|
||||||
console.log( req.params.source, `${config.tribes}/${req.session.header.xworkon}/referentials/dataManagement/${req.params.source}/*.json` )
|
|
||||||
glob.sync( `${config.tribes}/${req.session.header.xworkon}/referentials/dataManagement/${req.params.source}/*.json` )
|
|
||||||
.forEach( f => {
|
|
||||||
payload.push( path.basename( f, '.json' ) );
|
|
||||||
} )
|
|
||||||
res.json( payload );
|
|
||||||
} );
|
|
||||||
router.get( '/contentfull/:source/:idref', checkHeaders, isAuthenticated,
|
|
||||||
( req, res ) => {
|
|
||||||
//only for data and object
|
|
||||||
console.log( 'route referentials get all language' + req.params.source + '-' + req.params.idref );
|
|
||||||
const getref = Referentials.getref( true, req.params.source, req.params.idref, req.session.header.xworkon, req.session.header.xlang );
|
|
||||||
// Return any status the data if any erreur return empty object
|
|
||||||
res.jsonp( getref.payload.data );
|
|
||||||
} );
|
|
||||||
router.get( '/content/:source/:idref', checkHeaders, isAuthenticated,
|
|
||||||
( req, res ) => {
|
|
||||||
console.log( 'route referentials get ' + req.params.source + '-' + req.params.idref );
|
|
||||||
const getref = Referentials.getref( false, req.params.source, req.params.idref, req.session.header.xworkon, req.session.header.xlang );
|
|
||||||
res.jsonp( getref.payload.data );
|
|
||||||
} );
|
|
||||||
// get with no authentification
|
|
||||||
router.get( '/contentnoauth/:source/:idref', checkHeaders,
|
|
||||||
( req, res ) => {
|
|
||||||
console.log( 'route referentials get ' + req.params.source + '-' + req.params.idref );
|
|
||||||
// @TODO check access right in clientconf before sending back json file
|
|
||||||
const getref = Referentials.getref( false, req.params.source, req.params.idref, req.session.header.xworkon, req.session.header.xlang );
|
|
||||||
res.jsonp( getref.payload.data );
|
|
||||||
} );
|
|
||||||
router.get( '/lg', ( req, res ) => {
|
|
||||||
console.log( req.headers[ "accept-language" ] )
|
|
||||||
let lg = '??';
|
|
||||||
if( req.headers[ "accept-language" ] && req.headers[ "accept-language" ].split( ',' )
|
|
||||||
.length > 0 ) {
|
|
||||||
lg = req.headers[ "accept-language" ].split( ',' )[ 0 ];
|
|
||||||
}
|
|
||||||
res.json( { lg } )
|
|
||||||
} );
|
|
||||||
router.put( '/content/:source/:idref', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
console.log( `route put content for ${req.params.idref} that is a ${req.params.source}` );
|
|
||||||
const putref = Referentials.putref( req.params.source, req.params.idref, req.session.header.xworkon, req.body )
|
|
||||||
return res.status( putref.status )
|
|
||||||
.send( { payload: putref.payload } )
|
|
||||||
} );
|
|
||||||
//hasAccessrighton( 'referentials', 'U' )
|
|
||||||
router.get( '/updatefull', checkHeaders, isAuthenticated, hasAccessrighton( 'referentials', 'U' ), ( req, res ) => {
|
|
||||||
console.log( `route get to force update content updatefull is accessrighton` );
|
|
||||||
const updtref = Referentials.updatefull( req.session.header.xworkon )
|
|
||||||
return res.status( updtref.status )
|
|
||||||
.send( { payload: updtref.payload } )
|
|
||||||
} );
|
|
||||||
module.exports = router;
|
|
@ -1,29 +0,0 @@
|
|||||||
//Installation d'un tag
|
|
||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
// Upload de file
|
|
||||||
const express = require('express');
|
|
||||||
// Classes
|
|
||||||
const Tags = require('../models/Tags');
|
|
||||||
// Middlewares
|
|
||||||
const router = express.Router();
|
|
||||||
router.get('/:filename', (req, res) => {
|
|
||||||
//console.log('route tags get ', req.params.filename);
|
|
||||||
const savetag = Tags.getfile(req.params.filename, req);
|
|
||||||
if(savetag.status == 200) {
|
|
||||||
res.sendFile(savetag.payload.filename);
|
|
||||||
} else {
|
|
||||||
res.status(savetag.status)
|
|
||||||
.send({ payload: savetag.payload })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
router.post('/:tribeid', (req, res) => {
|
|
||||||
//console.log('route tags post ', req.params.tribeid);
|
|
||||||
const savetag = Tags.savehits(req);
|
|
||||||
res.status(200)
|
|
||||||
.send('');
|
|
||||||
})
|
|
||||||
module.exports = router;
|
|
374
routes/tribes.js
374
routes/tribes.js
@ -1,374 +0,0 @@
|
|||||||
const express = require( 'express' );
|
|
||||||
const fs = require( 'fs-extra' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const config = require( '../tribes/townconf.js' );
|
|
||||||
|
|
||||||
// Classes
|
|
||||||
const Tribes = require( '../models/Tribes.js' );
|
|
||||||
// Middlewares
|
|
||||||
const checkHeaders = require( '../middlewares/checkHeaders' );
|
|
||||||
const isAuthenticated = require( '../middlewares/isAuthenticated' );
|
|
||||||
const hasAccessrighton = require( '../middlewares/hasAccessrighton' );
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
|
|
||||||
router.get( '/clientconf/:tribeid', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
/*
|
|
||||||
get a clientconf.json for a tribeid depending of user accessright
|
|
||||||
if tribeid == all and user is admin of apixtribe => get /tmp/clientconfglob.json
|
|
||||||
req.session.header.accessrights, req.session.header.apixpaganid
|
|
||||||
*/
|
|
||||||
console.log( `Tribes/clientconf for tribeid:${req.params.tribeid}` )
|
|
||||||
if( req.params.tribeid == "all" && req.session.header.accessrights.data.apixtribe && req.session.header.accessrights.data.apixtribe.tribeid && req.session.header.accessrights.data.apixtribe.tribeid.includes( 'R' ) ) {
|
|
||||||
res.status( 200 )
|
|
||||||
.send( { moreinfo: fs.readJsonSync( `${config.tmp}/clientconfglob.json`, 'utf-8' ) } );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if( req.session.header.accessrights.data[ req.params.tribeid ] &&
|
|
||||||
req.session.header.accessrights.data[ req.params.tribeid ].tribeid &&
|
|
||||||
req.session.header.accessrights.data[ req.params.tribeid ].tribeid.includes( 'R' ) &&
|
|
||||||
fs.existsSync( `${config.tribes}/${req.params.tribeid}/clientconf.json` ) ) {
|
|
||||||
// const conftribeid = { moreinfo: {} }
|
|
||||||
// conftribeid.moreinfo[ req.params.tribeid ] = fs.readJsonSync( `${config.tribes}/${req.params.tribeid}/clientconf.json`, 'utf-8' );
|
|
||||||
res.status( 200 )
|
|
||||||
.send( { moreinfo: [ fs.readJsonSync( `${config.tribes}/${req.params.tribeid}/clientconf.json`, 'utf-8' ) ] } );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// if not authorized or dos not exist return empty
|
|
||||||
// no specific message is send for security reason (check only log)
|
|
||||||
res.status( 403 )
|
|
||||||
.send( { info: [ 'forbidenAccess' ], models: 'Tribes' } )
|
|
||||||
.end();
|
|
||||||
} )
|
|
||||||
router.put( '/', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
console.log( 'Create a new tribeid, with a useradmin' )
|
|
||||||
console.log( ' send data = clientconf.json with all parameter.' )
|
|
||||||
// !!!!! check for security any ; \n or so because data can be used into shell
|
|
||||||
const add = Tribes.create( req.body );
|
|
||||||
res.status( add.status )
|
|
||||||
.send( add.payload )
|
|
||||||
} )
|
|
||||||
router.delete( '/archivetribeid/:tribeid', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
console.log( "request archive tribeid" )
|
|
||||||
const archive = Tribes.archive( req.params.tribeid );
|
|
||||||
res.status( archive.status )
|
|
||||||
.send( archive.payload )
|
|
||||||
} );
|
|
||||||
router.post( '/spaceweb', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
// !!!!! check for security any ; \n or so because data can be used into shell
|
|
||||||
console.log( 'Create a new webapp for xworkon ' )
|
|
||||||
req.body.tribeid = req.session.header.xworkon;
|
|
||||||
const add = Tribes.addspaceweb( req.body )
|
|
||||||
res.status( add.status )
|
|
||||||
.send( add.payload )
|
|
||||||
} )
|
|
||||||
router.get( '/spaceweb/components/:tribeid/:website/:key', checkHeaders, ( req, res ) => {
|
|
||||||
// check if key is valid before continue
|
|
||||||
// exemple: get Tribes/spaceweb/components/ndda/mesa/123?rep=appmesatable/appsimpletable.mustache
|
|
||||||
const file = `${config.tribes}/${req.params.tribeid}/spacedev/${req.params.website}/src/ctatic/components/${req.query.path}`
|
|
||||||
console.log( `Request components file from ${file}` )
|
|
||||||
if( fs.existsSync( file ) ) {
|
|
||||||
res.sendFile( file );
|
|
||||||
} else {
|
|
||||||
res.send( `console.error("Missing components file in ${req.params.tribeid}/spacedev/${req.params.website}/src/ctatic/components/${req.query.path}");` );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
router.get( '/plugins/:tribeid/:pluginname/:key/:filename', ( req, res ) => {
|
|
||||||
// No accessright possible cause it is load on the fly
|
|
||||||
// @todo Check key to authorize access to the plugin (key comme from user ACCESSRIGHTS[tribeid plugin owner:pluginname]).key
|
|
||||||
// return a file into /:tribeid owner of plugin/plugins/:pluginname/components/:filename
|
|
||||||
// if not exist or invalid key then return console.error
|
|
||||||
const file = `${config.tribes}/${req.params.tribeid}/plugins/${req.params.pluginname}/components/${req.params.filename}`
|
|
||||||
console.log( 'Tribes/plugins/ ', file )
|
|
||||||
if( fs.existsSync( file ) ) {
|
|
||||||
res.sendFile( file );
|
|
||||||
} else {
|
|
||||||
res.send( `console.error("Missing plugin file in ${req.params.tribeid}/plugins/${req.params.pluginname}/components/${req.params.filename}");` );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
router.get( '/dirls', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
// url /Tribes/dirls?rep=referentials/dataManagement
|
|
||||||
// request information about a req.query.rep from header xworkon/
|
|
||||||
// return
|
|
||||||
// {file:[{}],dir:[{}]}
|
|
||||||
// @todo check if isAuthorized and exist
|
|
||||||
|
|
||||||
console.log( 'request dirls', `${config.tribes}/${req.session.header.xworkon}/${req.query.rep}` );
|
|
||||||
if( !fs.existsSync( `${config.tribes}/${req.session.header.xworkon}/${req.query.rep}` ) ) {
|
|
||||||
res.status( 404 )
|
|
||||||
.send( { 'info': [ 'dirnotexist' ], model: 'Tribes' } );
|
|
||||||
}
|
|
||||||
const info = Tribes.dirls( req.session.header.xworkon, req.query.rep );
|
|
||||||
console.log( info )
|
|
||||||
res.status( info.status )
|
|
||||||
.send( info.payload );
|
|
||||||
} )
|
|
||||||
router.delete( '/ls', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
// check Accessright with D or O on each
|
|
||||||
// url /Tribes/ls
|
|
||||||
// req.body.files=[listfiles file to delete ]
|
|
||||||
const authfiles = Tribes.checkaccessfiles( req.body, 'D', req.session.header.accessrights, req.session.header.apixpaganid );
|
|
||||||
authfiles.ok.forEach( f => { fs.remove( `${config.tribes}/${f}` ); } )
|
|
||||||
res.status( 200 )
|
|
||||||
.send( { 'info': [ 'fileauthdeleted' ], models: 'Tribes', moreinfo: authfiles } )
|
|
||||||
} );
|
|
||||||
router.put( '/sendjson', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
//req.body = {object:spacedev, path:website/src/data/tpldataname_lg.json, data:{...}}
|
|
||||||
//console.log( req.body )
|
|
||||||
const dest = `${config.tribes}/${req.session.header.xworkon}/${req.body.object}/${req.body.path}`;
|
|
||||||
console.log( `Send json to saved to ${dest}` );
|
|
||||||
if( !( req.body.object && fs.existsSync( `${config.tribes}/${req.session.header.xworkon}/${req.body.object}` ) ) ) {
|
|
||||||
res.status( '404' )
|
|
||||||
.send( { info: [ 'objectmissiong' ], models: 'Tribes', moreinfo: `object: ${req.body.object} does not exist req.body must {object, data, path} into data ${req.session.header.xworkon}/${req.body.object}` } )
|
|
||||||
} else {
|
|
||||||
if( fs.existsSync( `${config.tribes}/${req.session.header.xworkon}/${req.body.object}/${req.body.path}` ) ) {
|
|
||||||
// exist so can be update check accessright update on this
|
|
||||||
hasAccessrighton( req.body.object, "U" );
|
|
||||||
} else {
|
|
||||||
hasAccessrighton( req.body.object, "C" );
|
|
||||||
}
|
|
||||||
fs.outputJsonSync( dest, req.body.data );
|
|
||||||
res.status( 200 )
|
|
||||||
.send( { info: [ 'filesaved' ], models: 'Tribes' } )
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
router.post( '/downloadls', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
// midlleware hasAccessrighton.js is not apply here only to access/update/create information inside an object
|
|
||||||
// to get file a user need accessrights to data: object: R or to Own it
|
|
||||||
// or if exist a .info.json into folder get shared as R in uuid
|
|
||||||
|
|
||||||
//req.body contain list of path file or folder if only 1 file then download it, otherwise zip list and send zip file
|
|
||||||
|
|
||||||
const authfiles = Tribes.checkaccessfiles( req.body.files, 'R', req.session.header.accessrights, req.session.header.xpaganid );
|
|
||||||
if( authfiles.ok.length == 1 ) {
|
|
||||||
// bidouille en attendnat de faire un .zip binaire propre
|
|
||||||
if( !authfiles.ok[ 0 ].includes( '.xml' ) ) {
|
|
||||||
res.status( 200 )
|
|
||||||
.download( `${config.tribes}/${authfiles.ok[0]}`, authfiles.ok[ 0 ] );
|
|
||||||
} else {
|
|
||||||
fs.copySync( `${config.tribes}/${authfiles.ok[0]}`, `${config.tribes}/${config.mayorId}/www/app/webapp/static/tmp/${authfiles.ok[ 0 ]}` )
|
|
||||||
}
|
|
||||||
} else if( authfiles.ok.length > 1 ) {
|
|
||||||
// on zip et on envoie
|
|
||||||
//res.status( 200 )
|
|
||||||
// .download( `${config.tribes}/${authfiles.ok[0]}`, authfiles.ok[ 0 ])
|
|
||||||
res.status( 200 )
|
|
||||||
.attachment( `${config.tribes}/${authfiles.ok[0]}` );
|
|
||||||
|
|
||||||
} else {
|
|
||||||
req.body.filepon
|
|
||||||
res.status( 403 )
|
|
||||||
.send( 'Forbidden access' )
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
router.post( '/upfilepond', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
console.log( 'post /Tribes/uploadfilepond' );
|
|
||||||
// Store file and return a unique id to save button
|
|
||||||
// that provide folder where to store it
|
|
||||||
const formidable = require( 'formidable' );
|
|
||||||
const form = formidable( { multiples: false } );
|
|
||||||
form.parse( req, ( err, fields, files ) => {
|
|
||||||
if( err ) { next( err ); return; }
|
|
||||||
//console.log( 'fields',fields);
|
|
||||||
// fileMetadaObject send
|
|
||||||
let context = JSON.parse( fields.filepond );
|
|
||||||
let idfile = files.filepond.path;
|
|
||||||
let name = files.filepond.name;
|
|
||||||
let subfolder = context.subfolder;
|
|
||||||
name = name.replace( /[ ,'"’]/g, "_" );
|
|
||||||
//console.log( 'files.filepond:', files.filepond );
|
|
||||||
console.log( idfile, `${config.tribes}/${req.session.header.xworkon}/www/${subfolder}/${name}` )
|
|
||||||
// On le supprime s'il existe deja
|
|
||||||
fs.removeSync( `${config.tribes}/${req.session.header.xworkon}/www/${subfolder}/${name}` );
|
|
||||||
// mv tmp
|
|
||||||
fs.moveSync( idfile, `${config.tribes}/${req.session.header.xworkon}/www/${subfolder}/${name}` );
|
|
||||||
//res.status(200).send({models:"Tribes",info:["Savedsuccess"],moreinfo:{id:file.filepond.path}})
|
|
||||||
//return for filepond
|
|
||||||
res.writeHead( 200, { 'Content-Type': 'text/plain' } );
|
|
||||||
res.end( idfile );
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
router.delete( '/file', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
//src = objectfolder with accessright/...
|
|
||||||
//hasAccessrighton( "www", "D" ),
|
|
||||||
if( !req.query.src ) {
|
|
||||||
res.status( 404 )
|
|
||||||
.send( { info: [ 'deleteerror' ], models: "Tribes", moreinfo: "your del req need a src" } )
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
hasAccessrighton( req.query.src.split( '/' )[ 0 ], "D" );
|
|
||||||
console.log( 'Remove file', `${config.tribes}/${req.session.header.xworkon}/${req.query.src}` )
|
|
||||||
console.log( req.body )
|
|
||||||
fs.removeSync( `${config.tribes}/${req.session.header.xworkon}/${req.query.src}` );
|
|
||||||
res.status( 200 )
|
|
||||||
.send( { info: [ 'Successfullremove' ], models: "Tribes" } )
|
|
||||||
} );
|
|
||||||
router.post( '/uploadfile', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
console.log( 'upload a file ' )
|
|
||||||
/* Authentification is needed to get a TOKEN
|
|
||||||
curl -X POST -H "xtribe: apixtribe" -H "xworkon: pvmsaveurs" -H "xlang: fr" -H "xpaganid: 1" -H "xauth: 1" -H "xapp: pvmsaveurs:pvmsaveurs" -H "Content-Type: application/json" -d '{"LOGIN":"adminapixtribe","PASSWORD":"Trze3aze!"}' http://pvmsaveurs.pvmsaveurs.fr/app/users/login
|
|
||||||
if exist replace xpaganidTOKEN with payload.TOKEN value
|
|
||||||
|
|
||||||
curl -H "xtribe: pvmsaveurs" -H "xworkon: pvmsaveurs" -H "xlang: fr" -H "xpaganid: adminapixtribe" -H "xauth: xpressuuisToken" -H "xapp: pvmsaveurs:pvmsaveurs" -F 'data=@filename.xx' http://pvmsaveurs.pvmsaveurs.fr/app/Tribes/uploadfile
|
|
||||||
*/
|
|
||||||
const formidable = require( 'formidable' );
|
|
||||||
const form = formidable( { multiples: false } );
|
|
||||||
form.parse( req, function ( err, fields, files ) {
|
|
||||||
//console.log( files.data )
|
|
||||||
var oldPath = files.data.path;
|
|
||||||
var newPath = `${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[files.data.name].dest}`;
|
|
||||||
console.log( 'oldPath', oldPath )
|
|
||||||
console.log( 'newPath', newPath )
|
|
||||||
var rawData = fs.readFileSync( oldPath )
|
|
||||||
fs.outputFile( newPath, rawData, function ( err ) {
|
|
||||||
if( err ) {
|
|
||||||
console.log( err );
|
|
||||||
return res.status( 405 )
|
|
||||||
.send( { info: [ 'savederror' ], models: "Tribes", moreinfo: "your file was not able to be saved into the server" } )
|
|
||||||
} else {
|
|
||||||
return res.status( 200 )
|
|
||||||
.send( {
|
|
||||||
info: [ "successfullsent" ],
|
|
||||||
models: "Tribes"
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
router.post( '/uploadzip', checkHeaders, ( req, res ) => {
|
|
||||||
console.log( 'uploadzip a file ' )
|
|
||||||
|
|
||||||
/* no authentification to upload a zip filename into /tribes/${xworkon}/${clientconf.uploadzip[filename].dest}
|
|
||||||
unzip it using the password ${clientconf.uploadzip[filename].psw
|
|
||||||
if no error then run the callback ${clientconf.uploadzip[filename].callback
|
|
||||||
but a password to unzip
|
|
||||||
in clientconf.json need to be set
|
|
||||||
"uploadzip": {
|
|
||||||
"articlesTribespvm.zip": {
|
|
||||||
"comment": "unzip with overwrite if same name",
|
|
||||||
"psw": "azPI1209qtrse",
|
|
||||||
"dest": "importexport/tmp",
|
|
||||||
"unzipoption": "-aoa",
|
|
||||||
"callback": "importexport/integrationitem.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Example:
|
|
||||||
cd where zip file is stored
|
|
||||||
curl -H "xtribe: pvmsaveurs" -H "xworkon: pvmsaveurs" -H "xlang: fr" -H "xpaganid: adminapixtribe" -H "xauth: 1" -H "xapp: pvmsaveurs:pvmsaveurs" -F 'data=@articlesTribespvm.zip' http://pvmsaveurs.pvmsaveurs.fr/app/Tribes/uploadzip
|
|
||||||
|
|
||||||
*/
|
|
||||||
const clientconf = fs.readJSONSync( `${config.tribes}/${req.session.header.xworkon}/clientconf.json` )
|
|
||||||
if( !clientconf.uploadzip ) {
|
|
||||||
return res.status( '404' )
|
|
||||||
.send( { info: [ "missconf" ], models: "Tribes", moreinfo: `no uploadzip in clientconf for ${req.session.header.xworkon} please contact apixtribe admin ` } );
|
|
||||||
};
|
|
||||||
const uploadzip = clientconf.uploadzip;
|
|
||||||
const formidable = require( 'formidable' );
|
|
||||||
const form = formidable( { multiples: false } );
|
|
||||||
form.parse( req, function ( err, fields, files ) {
|
|
||||||
//console.log( files.data )
|
|
||||||
var oldPath = files.data.path;
|
|
||||||
if( !Object.keys( clientconf.uploadzip )
|
|
||||||
.includes( files.data.name ) ) {
|
|
||||||
return res.status( 403 )
|
|
||||||
.send( { info: [ "notAllowed" ], models: "Tribes", moreinfo: `file ${files.data.name} not allowed to be upload` } )
|
|
||||||
} else {
|
|
||||||
console.log( "context:", clientconf.uploadzip[ files.data.name ] )
|
|
||||||
var newPath = `${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[files.data.name].dest}`;
|
|
||||||
//console.log( 'oldPath', oldPath )
|
|
||||||
//console.log( 'newPath', `${newPath}/${files.data.name}` )
|
|
||||||
fs.moveSync( oldPath, `${newPath}/${files.data.name}`, { overwrite: true } );
|
|
||||||
const cp = require( 'child_process' );
|
|
||||||
//console.log( `7z e -p${clientconf.uploadzip[ files.data.name ].psw} ${newPath}/${files.data.name}` );
|
|
||||||
console.log( '7z', [ 'e', `-p${clientconf.uploadzip[ files.data.name ].psw}`, `${newPath}/${files.data.name}`, `-o${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[ files.data.name ].dest}`, clientconf.uploadzip[ files.data.name ].unzipoption ] );
|
|
||||||
var newFiles = cp.spawnSync( '7z', [ 'e', `-p${clientconf.uploadzip[ files.data.name ].psw}`, `${newPath}/${files.data.name}`, `-o${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[ files.data.name ].dest}`, clientconf.uploadzip[ files.data.name ].unzipoption ] );
|
|
||||||
console.log( newFiles.output.toString() )
|
|
||||||
if( newFiles.output.toString()
|
|
||||||
.includes( 'Everything is Ok' ) ) {
|
|
||||||
if( clientconf.uploadzip[ files.data.name ].callback ) {
|
|
||||||
const integ = require( `${config.tribes}/${req.session.header.xworkon}/${clientconf.uploadzip[files.data.name].callback}` )
|
|
||||||
.run();
|
|
||||||
console.log( 'integration', integ )
|
|
||||||
return res.status( integ.status )
|
|
||||||
.send( integ.payload );
|
|
||||||
} else {
|
|
||||||
return res.status( 200 )
|
|
||||||
.send( {
|
|
||||||
info: [ "successfullsent" ],
|
|
||||||
models: "Tribes"
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return res.status( 400 )
|
|
||||||
.send( {
|
|
||||||
info: [ "zipfileerror" ],
|
|
||||||
models: "Tribes",
|
|
||||||
moreinfo: newFiles.output.toString()
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
} );
|
|
||||||
|
|
||||||
router.post( '/upload', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
1 // ACHANGER VIA usage sendjson
|
|
||||||
// url /Tribes/upload?save=tmp&rep=referentials/dataManagement
|
|
||||||
// if save=tmp then store in a tmp file
|
|
||||||
// if save=ok then mv the tmp file to the folder
|
|
||||||
// midlleware hasAccessrighton.js is not apply here only to access/update/create information inside an object
|
|
||||||
// to upload a file a user need accessrights to data: object: C or to Own it
|
|
||||||
// or if dir.file exist a .info.json into folder get shared as C in uuid accessright
|
|
||||||
/*
|
|
||||||
to add in front
|
|
||||||
<form action="/upload" method="POST" enctype="multipart/form-data">
|
|
||||||
<input type="file" name="file"/>
|
|
||||||
<input type="submit" value="upload"/>
|
|
||||||
</form>
|
|
||||||
*/
|
|
||||||
console.log( 'Envoie image' )
|
|
||||||
console.log( 'body', req.body );
|
|
||||||
console.log( 'params', req.params );
|
|
||||||
//const authfolder = Tribes.checkaccessfiles( req.params.rep, 'C', req.session.header.accessrights, req.session.header.xpaganid );
|
|
||||||
// cheack autorisation to create or replace a file for this accessrights user
|
|
||||||
const authfolder = { ok: "tt" }
|
|
||||||
if( authfolder.ok ) {
|
|
||||||
if( req.params.save == 'file' ) {
|
|
||||||
if( fs.existsSync( req.body.filepond ) ) {
|
|
||||||
fs.mv( req.body.filepond, req.params.rep );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// voir si c'est toujours pertinent car upload est géré par filepond pour les image
|
|
||||||
if( req.params.save == 'upload' ) {
|
|
||||||
const form = formidable( { multiples: false } );
|
|
||||||
form.parse( req, ( err, fields, files ) => {
|
|
||||||
if( err ) { next( err ); return; }
|
|
||||||
let thefile = files.filebond.path;
|
|
||||||
fs.outputFileSync()
|
|
||||||
console.log( 'thefile:' + thefile );
|
|
||||||
res.writeHead( 200, { 'Content-Type': 'text/plain' } );
|
|
||||||
res.end( theFile );
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.status( 403 )
|
|
||||||
.send( 'forbiden access' );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
/*
|
|
||||||
Manage tribeid into /data/tribee/tribeid
|
|
||||||
client space dedicated
|
|
||||||
|
|
||||||
@Todo
|
|
||||||
clientconfglob copy cut from Referentials.clientconfglob
|
|
||||||
clientconf.json copy cut from Referentials.clientconf
|
|
||||||
list of tribeid copy cut from Referentials.
|
|
||||||
Add a tribeid
|
|
||||||
update clientconf
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
|
@ -1,49 +0,0 @@
|
|||||||
// Upload de file
|
|
||||||
const express = require( 'express' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
const jsonfile = require( 'jsonfile' );
|
|
||||||
const fs = require( 'fs' );
|
|
||||||
|
|
||||||
// Classes
|
|
||||||
const UploadFile = require( '../models/UploadFiles' );
|
|
||||||
// Middlewares
|
|
||||||
const checkHeaders = require( '../middlewares/checkHeaders' );
|
|
||||||
const isAuthenticated = require( '../middlewares/isAuthenticated' );
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.post( '/', checkHeaders, ( req, res ) => {
|
|
||||||
console.log( 'route uploadFile post ' );
|
|
||||||
const saveFile = UploadFile.add( req, req.session.header );
|
|
||||||
res.send( saveFile );
|
|
||||||
// res.send({ status: 200, payload: { info: 'fine' } });
|
|
||||||
} );
|
|
||||||
|
|
||||||
router.post( '/json', checkHeaders, ( req, res ) => {
|
|
||||||
console.log( 'route uploadFile post de fichier json ' );
|
|
||||||
// a callback can be pass to req.body to run a specific process after upload
|
|
||||||
const saveFile = UploadFile.addjson( req.body, req.session.header );
|
|
||||||
console.log( saveFile );
|
|
||||||
res.send( saveFile );
|
|
||||||
// res.send({ status: 200, payload: { info: 'fine' } });
|
|
||||||
} );
|
|
||||||
|
|
||||||
router.get( '/:filename', checkHeaders, isAuthenticated, ( req, res ) => {
|
|
||||||
console.log( 'route uploadFile get ', req.params.filename );
|
|
||||||
const pushFile = UploadFile.get(
|
|
||||||
req.params.filename.replace( /______/g, '/' ),
|
|
||||||
req.session.header
|
|
||||||
);
|
|
||||||
if( pushFile.status == 200 ) {
|
|
||||||
if( path.extname( pushFile.payload.file ) === '.json' ) {
|
|
||||||
jsonfile.readFile( pushFile.payload.file, ( err, p ) => {
|
|
||||||
if( err ) console.error( err );
|
|
||||||
res.jsonp( p );
|
|
||||||
} );
|
|
||||||
} else {
|
|
||||||
res.download( pushFile.payload.file, path.basename( pushFile.payload.file ) );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.send( pushFile );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
module.exports = router;
|
|
@ -1,216 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var pwa = pwa || {};
|
|
||||||
/*
|
|
||||||
Manage user authentification and registration
|
|
||||||
________________________
|
|
||||||
pwa.auth.route()
|
|
||||||
manage from state.json route if authenticated or not
|
|
||||||
redirect public page or app page
|
|
||||||
________________________
|
|
||||||
pwa.auth.screenlogin()
|
|
||||||
show login modal
|
|
||||||
________________________
|
|
||||||
pwa.auth.getlinkwithoutpsw()
|
|
||||||
special get with token and uuid workeable for 24h this link is une onetime
|
|
||||||
_________________________
|
|
||||||
pwa.auth.isAuthenticate()
|
|
||||||
test if token is still ok or not return false/true
|
|
||||||
_________________________
|
|
||||||
pwa.auth.authentification({LOGIN,PASSWORD})
|
|
||||||
if ok => load pwa.state.data.app .headers .userlogin
|
|
||||||
_________________________
|
|
||||||
pwa.auth.login()
|
|
||||||
Manage login modal to get login psw value and submit it to pwa.auth.authentification()
|
|
||||||
_________________________
|
|
||||||
pwa.auth.logout()
|
|
||||||
Remove localstorage and reload
|
|
||||||
_________________________
|
|
||||||
pwa.auth.register()
|
|
||||||
@TODO
|
|
||||||
__________________________
|
|
||||||
pwa.auth.forgetpsw()
|
|
||||||
Request to send an email with a unique get link to access from this link to the app
|
|
||||||
|
|
||||||
*/
|
|
||||||
/*MODULEJS*/
|
|
||||||
//--##
|
|
||||||
pwa.auth = {};
|
|
||||||
// Refresh browser state if exist else get pwa.state defaults
|
|
||||||
//pwa.state.ready( pwa.auth.check );
|
|
||||||
|
|
||||||
pwa.auth.check = () => {
|
|
||||||
if( pwa.state.data.login.isAuthenticated ) {
|
|
||||||
if( !pwa.auth.isAuthenticate() ) {
|
|
||||||
// Then reinit local storage and refresh page
|
|
||||||
pwa.state.data.login.isAuthenticated = false;
|
|
||||||
pwa.state.save();
|
|
||||||
//alert( 'reload page cause no more auth' )
|
|
||||||
window.location.reload();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pwa.auth.route = ( destination ) => {
|
|
||||||
console.log( 'auth.route to', destination );
|
|
||||||
//if check Authenticated && exist #signin button[data-routeto] then redirect browser to button[data-routeto]
|
|
||||||
//else manage component action auth
|
|
||||||
if( pwa.state && pwa.state.data && pwa.state.data.login && pwa.state.data.login.isAuthenticated ) {
|
|
||||||
if( destination )
|
|
||||||
window.location.pathname = `${pwa.state.data.ctx.urlbase}/${destination}`;
|
|
||||||
} else {
|
|
||||||
[ "#signin", "#resetpsw", "#register" ].forEach( e => {
|
|
||||||
if( e == destination ) {
|
|
||||||
document.querySelector( e )
|
|
||||||
.classList.remove( 'd-none' );
|
|
||||||
} else {
|
|
||||||
document.querySelector( e )
|
|
||||||
.classList.add( 'd-none' );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pwa.auth.isAuthenticate = async function () {
|
|
||||||
// in any request, if middleware isAuthenticated return false
|
|
||||||
// then headers Xuuid is set to 1
|
|
||||||
// then try pwa.auth.isAuthenticate if rememberMe auto reconnect
|
|
||||||
// if jwt is ok then return true in other case => false
|
|
||||||
// this is the first test then depending of action see ACCESSRIGHTS of user
|
|
||||||
console.log( 'lance isauth', {
|
|
||||||
headers: pwa.state.data.headers.xpaganid
|
|
||||||
} )
|
|
||||||
//alert( 'uuid ' + pwa.state.data.headers.xpaganid )
|
|
||||||
console.log( `https://${pwa.state.data.ctx.urlbackoffice}/users/isauth`, {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
} )
|
|
||||||
try {
|
|
||||||
const repisauth = await axios.get( `https://${pwa.state.data.ctx.urlbackoffice}/users/isauth`, {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
} )
|
|
||||||
console.log( repisauth )
|
|
||||||
console.log( 'isAauthenticate: yes' )
|
|
||||||
return true;
|
|
||||||
} catch ( err ) {
|
|
||||||
if( err.response ) { console.log( "response err ", err.response.data ) }
|
|
||||||
if( err.request ) { console.log( "request err", err.request ) }
|
|
||||||
console.log( 'isAuthenticate: no' )
|
|
||||||
pwa.state.data.headers.xpaganid = "1";
|
|
||||||
if( pwa.state.data.login.rememberMe.login ) {
|
|
||||||
if( await pwa.auth.authentification( pwa.state.data.login.rememberMe ) ) {
|
|
||||||
return await pwa.auth.isAuthenticate();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pwa.auth.authentification = async function ( data ) {
|
|
||||||
// Core client function to chech auth from login & psw
|
|
||||||
// In case of 403 error lauch pwa.authentification(pwa.app.rememberMe)
|
|
||||||
// in case of sucess update paw.state.data.login
|
|
||||||
console.groupCollapsed( "Post Authentification for standard on : https://" + pwa.state.data.ctx.urlbackoffice + "/users/login param data", data )
|
|
||||||
|
|
||||||
console.log( 'header de login', pwa.state.data.headers )
|
|
||||||
let auth;
|
|
||||||
try {
|
|
||||||
auth = await axios.post( `https://${pwa.state.data.ctx.urlbackoffice }/users/login`, data, {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
} );
|
|
||||||
console.log( "retour de login successfull ", auth );
|
|
||||||
//Maj variable globale authentifié
|
|
||||||
pwa.state.data.headers.xpaganid = auth.data.payload.data.UUID;
|
|
||||||
pwa.state.data.headers.xauth = auth.data.payload.data.TOKEN;
|
|
||||||
pwa.state.data.headers.xtribe = auth.data.payload.data.tribeid;
|
|
||||||
pwa.state.data.headers.xworkon = auth.data.payload.data.tribeid;
|
|
||||||
// Save local authentification uuid/token info user
|
|
||||||
pwa.state.data.login.user = auth.data.payload.data;
|
|
||||||
//request a refresh after a login
|
|
||||||
pwa.state.data.ctx.refreshstorage = true;
|
|
||||||
pwa.state.save();
|
|
||||||
//alert( 'pwa.state.save() fait avec uuid' + pwa.state.data.headers.xpaganid )
|
|
||||||
console.groupEnd();
|
|
||||||
return true;
|
|
||||||
} catch ( err ) {
|
|
||||||
if( err.response ) { console.log( "resp", err.response.data ) }
|
|
||||||
if( err.request ) { console.log( "req", err.request.data ) }
|
|
||||||
console.log( 'erreur de login reinit de rememberMe', err )
|
|
||||||
pwa.state.data.login.rememberMe = {};
|
|
||||||
document.querySelector( "#signin p.msginfo" )
|
|
||||||
.innerHTML = document.querySelector( "#signin [data-msgko]" )
|
|
||||||
.getAttribute( 'data-msgko' );
|
|
||||||
console.groupEnd();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pwa.auth.logout = function () {
|
|
||||||
console.log( "remove ", pwa.state.data.ctx.website );
|
|
||||||
localStorage.removeItem( pwa.state.data.ctx.website );
|
|
||||||
window.location.href = "/";
|
|
||||||
}
|
|
||||||
pwa.auth.login = async function () {
|
|
||||||
/*
|
|
||||||
Check login/psw
|
|
||||||
see auth.mustache & data_auth_lg.json for parameters
|
|
||||||
Context info used:
|
|
||||||
#signin p.msginfo contain message interaction with user
|
|
||||||
#signin data-msgok data-msgko
|
|
||||||
#signin button[data-routeto] is a redirection if authentification is successful
|
|
||||||
*/
|
|
||||||
document.querySelector( '#signin p.msginfo' )
|
|
||||||
.innerHTML = "";
|
|
||||||
const data = {
|
|
||||||
LOGIN: document.querySelector( "#signin input[name='login']" )
|
|
||||||
.value,
|
|
||||||
PASSWORD: document.querySelector( "#signin input[name='password']" )
|
|
||||||
.value
|
|
||||||
}
|
|
||||||
console.log( 'check password', checkdata.test.password( "", data.PASSWORD ) )
|
|
||||||
if( data.LOGIN.length < 4 || !checkdata.test.password( "", data.PASSWORD ) ) {
|
|
||||||
/*$("#loginpart p.msginfo")
|
|
||||||
.html("")
|
|
||||||
.fadeOut(2000)*/
|
|
||||||
document.querySelector( '#signin p.msginfo' )
|
|
||||||
.innerHTML = document.querySelector( '#signin [data-msgko]' )
|
|
||||||
.getAttribute( 'data-msgko' );
|
|
||||||
} else {
|
|
||||||
if( document.querySelector( "[name='rememberme']" )
|
|
||||||
.checked ) {
|
|
||||||
pwa.state.data.login.rememberMe = data;
|
|
||||||
}
|
|
||||||
if( await pwa.auth.authentification( data ) ) {
|
|
||||||
console.log( 'Authentification VALIDE' )
|
|
||||||
document.querySelector( '#signin p.msginfo' )
|
|
||||||
.innerHTML = document.querySelector( "#signin [data-msgok]" )
|
|
||||||
.getAttribute( 'data-msgok' );
|
|
||||||
//state l'état isAuthenticated et check la route
|
|
||||||
pwa.state.data.login.isAuthenticated = true;
|
|
||||||
pwa.state.save();
|
|
||||||
console.log( pwa.state.data.login )
|
|
||||||
console.log( 'Auth ok route to ', document.querySelector( '#signin button[data-routeto]' )
|
|
||||||
.getAttribute( 'data-routeto' ) );
|
|
||||||
pwa.auth.route( document.querySelector( '#signin button[data-routeto]' )
|
|
||||||
.getAttribute( 'data-routeto' ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pwa.auth.register = async function ( event ) {
|
|
||||||
event.preventDefault();
|
|
||||||
// gérer la cration du user
|
|
||||||
}
|
|
||||||
pwa.auth.forgetpsw = async function ( event ) {
|
|
||||||
event.preventDefault();
|
|
||||||
const tribeid = $( ".loginregister" )
|
|
||||||
.getAttribute( "data-tribeid" );
|
|
||||||
const email = $( '.forgetpsw .email' )
|
|
||||||
.val();
|
|
||||||
console.log( `Reinit email: ${email} for tribeid: ${tribeid}` )
|
|
||||||
try {
|
|
||||||
console.log( `https://${pwa.state.data.ctx.urlbackoffice }/users/getlinkwithoutpsw/${email}` )
|
|
||||||
const reinit = await axios.get( `https://${pwa.state.data.ctx.urlbackoffice }/users/getlinkwithoutpsw/${email}`, {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
} )
|
|
||||||
$( "#forgetpswpart p.msginfo" )
|
|
||||||
.html( "Regardez votre boite email" );
|
|
||||||
return true;
|
|
||||||
} catch ( er ) {
|
|
||||||
console.log( "Pb d'accès au back check apiamaildigit" )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
File diff suppressed because one or more lines are too long
@ -1,184 +0,0 @@
|
|||||||
/*
|
|
||||||
This module have to be independant of any external package
|
|
||||||
it is shared between back and front and is usefull
|
|
||||||
to apply common check in front before sending it in back
|
|
||||||
can be include in project with
|
|
||||||
<script src="https://apiback.maildigit.fr/js/checkdata.js"></script>
|
|
||||||
or with const checkdata = require('../public/js/checkdata.js')
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// --##
|
|
||||||
|
|
||||||
const checkdata = {};
|
|
||||||
// each checkdata.test. return true or false
|
|
||||||
checkdata.test = {};
|
|
||||||
|
|
||||||
checkdata.test.emailadress = ( ctx, email ) => {
|
|
||||||
const regExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
||||||
return regExp.test( email );
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* @emaillist = "email1,email2, email3"
|
|
||||||
* it check if each eamil separate by , are correct
|
|
||||||
*/
|
|
||||||
checkdata.test.emailadresslist = ( ctx, emaillist ) => {
|
|
||||||
//console.log(emaillist.split(','))
|
|
||||||
if( emaillist.length > 0 ) {
|
|
||||||
const emails = emaillist.split( ',' );
|
|
||||||
for( var i in emails ) {
|
|
||||||
//console.log(emails[i])
|
|
||||||
if( !checkdata.test.emailadress( "", emails[ i ].trim() ) ) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
checkdata.test.password = ( ctx, pwd ) => {
|
|
||||||
const regExp = new RegExp(
|
|
||||||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&.])[A-Za-z\d$@$!%*?&.{}:|\s]{8,}/
|
|
||||||
);
|
|
||||||
return regExp.test( pwd );
|
|
||||||
};
|
|
||||||
checkdata.test.required = ( ctx, val ) =>
|
|
||||||
val != null && val != 'undefined' && val.length > 0;
|
|
||||||
|
|
||||||
checkdata.test.isNumber = ( ctx, n ) => typeof n === 'number';
|
|
||||||
checkdata.test.isInt = ( ctx, n ) => n != '' && !isNaN( n ) && Math.round( n ) == n;
|
|
||||||
checkdata.test.isFloat = ( ctx, n ) => n != '' && !isNaN( n ) && Math.round( n ) != n;
|
|
||||||
checkdata.test.unique = ( ctx, val ) => {
|
|
||||||
if( ctx.list[ ctx.currentfield ] ) {
|
|
||||||
return !ctx.list[ ctx.currentfield ].includes( val );
|
|
||||||
} else {
|
|
||||||
console.log( 'ERR no list for field:' + ctx.currentfield );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
checkdata.test.isDateDay = ( ctx, dateDay ) => true;
|
|
||||||
/* checkdata.test.filterInvalidInArray = (array, validate) =>
|
|
||||||
array ? array.filter(el => !validate(el)) : true;
|
|
||||||
// return true when every elements is valid
|
|
||||||
*/
|
|
||||||
|
|
||||||
checkdata.test.postalCode = ( ctx, postalCode ) => {
|
|
||||||
if( postalCode.length == 0 ) return true;
|
|
||||||
const regExp = new RegExp( /(^\d{5}$)|(^\d{5}-\d{4}$)/ );
|
|
||||||
return regExp.test( postalCode );
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* PHONE
|
|
||||||
*/
|
|
||||||
checkdata.test.phoneNumber = ( ctx, phoneNumber ) => {
|
|
||||||
if( phoneNumber.length == 0 ) return true;
|
|
||||||
phoneNumber = phoneNumber.trim()
|
|
||||||
.replace( /[- .]/g, '' )
|
|
||||||
//french number
|
|
||||||
const regExpfr = new RegExp( /^0[1-9][0-9]{9}$/ );
|
|
||||||
const regExpInternational = new RegExp( /^\+*(\d{3})*[0-9,\-]{8,}/ );
|
|
||||||
return regExpfr.test( phoneNumber ) || regExpInternational.test( phoneNumber );
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* @phonelist = "phone1,phone2,phone3"
|
|
||||||
* it check if each phone separate by , are correct
|
|
||||||
*/
|
|
||||||
checkdata.test.phoneNumberlist = ( ctx, phonelist ) => {
|
|
||||||
//console.log(emaillist.split(','))
|
|
||||||
if( phonelist.length > 0 ) {
|
|
||||||
const phones = phonelist.split( ',' );
|
|
||||||
for( var i in phones ) {
|
|
||||||
//console.log(emails[i])
|
|
||||||
if( !checkdata.test.phoneNumber( "", phones[ i ].trim() ) ) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// checkdata.normalize take a correct data then reformat it to harmonise it
|
|
||||||
checkdata.normalize = {};
|
|
||||||
checkdata.normalize.phoneNumber = ( ctx, phone ) => {
|
|
||||||
phone = phone.trim()
|
|
||||||
.replace( /[- .]/g, '' );
|
|
||||||
if( checkdata.test.phoneNumber( '', phone ) && phone.length == 10 && phone[ 0 ] == "0" ) {
|
|
||||||
phone = '+33 ' + phone.substring( 1 );
|
|
||||||
}
|
|
||||||
return phone;
|
|
||||||
}
|
|
||||||
checkdata.normalize.upperCase = ( ctx, txt ) => txt.toUpperCase();
|
|
||||||
checkdata.normalize.lowerCase = ( ctx, txt ) => txt.toLowerCase();
|
|
||||||
// fixe 10 position et complete par des 0 devant
|
|
||||||
checkdata.normalize.zfill10 = ( ctx, num ) => {
|
|
||||||
let s = num + '';
|
|
||||||
while( s.length < 10 ) s = '0' + s;
|
|
||||||
return s;
|
|
||||||
};
|
|
||||||
/*let tt = "+33 1 02.03 04 05";
|
|
||||||
console.log(checkdata.test.phoneNumber('', tt))
|
|
||||||
console.log(checkdata.normalize.phoneNumber('', tt))
|
|
||||||
*/
|
|
||||||
checkdata.evaluate = ( contexte, referential, data ) => {
|
|
||||||
/*
|
|
||||||
* contexte object {} with full info for evaluation
|
|
||||||
* file referential path to get object to apply
|
|
||||||
* data related to object
|
|
||||||
- return {validefor =[keyword of error] if empty no error,
|
|
||||||
clean data eventually reformated
|
|
||||||
updateDatabase}
|
|
||||||
*/
|
|
||||||
console.log( 'contexte', contexte );
|
|
||||||
console.log( 'referentiel', referential );
|
|
||||||
console.log( 'data', data );
|
|
||||||
const invalidefor = [];
|
|
||||||
const objectdef = {};
|
|
||||||
const listfield = referential.map( ch => {
|
|
||||||
objectdef[ ch.idfield ] = ch;
|
|
||||||
return ch.idfield;
|
|
||||||
} );
|
|
||||||
|
|
||||||
Object.keys( data )
|
|
||||||
.forEach( field => {
|
|
||||||
if( !listfield.includes( field ) ) {
|
|
||||||
// some data can be inside an object with no control at all
|
|
||||||
// they are used for process only
|
|
||||||
// i leave it in case it will become a non sens
|
|
||||||
// invalidefor.push('ERRFIELD unknown of referentials ' + field);
|
|
||||||
} else {
|
|
||||||
if( objectdef[ field ].check ) {
|
|
||||||
// check data with rule list in check
|
|
||||||
objectdef[ field ].check.forEach( ctrl => {
|
|
||||||
console.log( 'ctrl', ctrl );
|
|
||||||
contexte.currentfield = field;
|
|
||||||
if( !checkdata.test[ ctrl ] ) {
|
|
||||||
invalidefor.push( 'ERR check function does not exist :' + ctrl + '___' + field )
|
|
||||||
} else {
|
|
||||||
if( !checkdata.test[ ctrl ]( contexte, data[ field ] ) )
|
|
||||||
invalidefor.push( 'ERR' + ctrl + '___' + field );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( objectdef[ field ].nouserupdate ) {
|
|
||||||
// check if user can modify this information
|
|
||||||
console.log(
|
|
||||||
'evaluation :' + field + ' -- ' + objectdef[ field ].nouserupdate,
|
|
||||||
eval( objectdef[ field ].nouserupdate )
|
|
||||||
);
|
|
||||||
const evalright = eval( objectdef[ field ].nouserupdate );
|
|
||||||
objectdef[ field ].nouserupdate = evalright;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
console.log( {
|
|
||||||
invalidefor,
|
|
||||||
data
|
|
||||||
} );
|
|
||||||
return {
|
|
||||||
invalidefor,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if( typeof module !== 'undefined' ) module.exports = checkdata;
|
|
File diff suppressed because one or more lines are too long
@ -1,150 +0,0 @@
|
|||||||
/*
|
|
||||||
After state.js / auth.js and all external js lib
|
|
||||||
Load
|
|
||||||
|
|
||||||
*/
|
|
||||||
var pwa = pwa || {};
|
|
||||||
pwa.main = pwa.main || {};
|
|
||||||
pwa.main.tpldata = pwa.main.tpldata || {};
|
|
||||||
pwa.main.tpl = pwa.main.tpl || {};
|
|
||||||
pwa.main.tpldata = pwa.main.tpldata || {};
|
|
||||||
pwa.main.ref = pwa.main.ref || {};
|
|
||||||
|
|
||||||
pwa.main.tpl.appsidebarmenu = 'static/components/appmesa/appsidebarmenu.mustache';
|
|
||||||
pwa.main.tpl.apptopbarmenu = 'static/components/appmesa/apptopbarmenu.mustache';
|
|
||||||
pwa.main.tpldata.sidebar = `static/components/appmesa/data_sidebar`;
|
|
||||||
pwa.main.tpldata.topbar = `static/components/appmesa/data_topbar`;
|
|
||||||
pwa.main.tpldata.topbarLogin = `static/components/appmesa/data_topbarLogin`;
|
|
||||||
|
|
||||||
pwa.main.init = () => {
|
|
||||||
const isempty = ( obj ) => {
|
|
||||||
return obj && Object.keys( obj )
|
|
||||||
.length === 0 && obj.constructor === Object
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load public env tpl & tpldata
|
|
||||||
//Manage action depending of html file currently show
|
|
||||||
const currenthtml = location.pathname.split( '/' )
|
|
||||||
.at( -1 );
|
|
||||||
console.groupCollapsed( `pwa.main.init for ${currenthtml} html page` );
|
|
||||||
if( currenthtml.includes( 'app_' ) ) {
|
|
||||||
pwa.main.loadmenu()
|
|
||||||
}
|
|
||||||
// To manage if authenticated or not in a simple way
|
|
||||||
/*
|
|
||||||
if( pwa.state.data.login.isAuthenticated ) {
|
|
||||||
//authenticated
|
|
||||||
// identity inside pwa.state.login.user
|
|
||||||
}else{
|
|
||||||
//anonymous
|
|
||||||
// can check if the url is relevant with isAuthenticated
|
|
||||||
//route to app if exist data-routo into a signin
|
|
||||||
//pwa.auth.route( document.querySelector( '#signin button[data-routeto]' ).getAttribute( 'data-routeto' ) );
|
|
||||||
// add and load dynamicaly the gui.js plugin if user that request it has access
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
console.groupEnd();
|
|
||||||
};
|
|
||||||
|
|
||||||
pwa.main.loadmenu = async () => {
|
|
||||||
console.log( 'pwa.main.loadmenu running' );
|
|
||||||
console.log( 'Status of pwa.state.data.login.isAuthenticated =', pwa.state.data.login.isAuthenticated );
|
|
||||||
let datasidebar, datatopbar;
|
|
||||||
/* Build datasidebar and datatopbar depending of list of module allowed by user in his ACCESSRIGHTS profil.
|
|
||||||
app[`${pwa.state.data.ctx.tribeid}:${pwa.state.data.ctx.website}`].js;
|
|
||||||
|
|
||||||
*/
|
|
||||||
//console.log( 'List of tpldata', pwa.main.tpldata )
|
|
||||||
//console.log( 'List of tpl', pwa.main.tpl )
|
|
||||||
console.log( `run pwa.state.loadfile with pwa.state.data.ctx.refreshstorage = ${pwa.state.data.ctx.refreshstorage} if true=> refresh anyway, if false refresh only if dest.name does not exist` );
|
|
||||||
await pwa.state.loadfile( pwa.main.tpl, 'tpl' );
|
|
||||||
await pwa.state.loadfile( pwa.main.tpldata, 'tpldata' );
|
|
||||||
datasidebar = pwa.state.data.tpldata.sidebar;
|
|
||||||
//any tpldata containing sidebar in pwa.state.data.tpldata is add to sbgroupmenu to be available
|
|
||||||
Object.keys( pwa.state.data.tpldata )
|
|
||||||
.filter( m => ( m != 'sidebar' && m.includes( 'sidebar' ) ) )
|
|
||||||
.some( k => {
|
|
||||||
datasidebar.sbgroupmenu.push( pwa.state.data.tpldata[ k ] )
|
|
||||||
} );
|
|
||||||
//merge les menu topbar
|
|
||||||
datatopbar = pwa.state.data.tpldata.topbar;
|
|
||||||
if( pwa.state.data.login.isAuthenticated ) {
|
|
||||||
// update user information if needed
|
|
||||||
datatopbar.name = pwa.state.data.login.user.LOGIN;
|
|
||||||
datatopbar.avatarimg = pwa.state.data.login.user.AVATARIMG;
|
|
||||||
delete pwa.state.data.tpldata.topbarLogin;
|
|
||||||
pwa.state.save();
|
|
||||||
}
|
|
||||||
datatopbar.menuprofil = [];
|
|
||||||
Object.keys( pwa.state.data.tpldata )
|
|
||||||
.filter( m => ( m != 'topbar' && m.includes( 'topbar' ) ) )
|
|
||||||
.some( k => {
|
|
||||||
datatopbar.menuprofil.push( pwa.state.data.tpldata[ k ] )
|
|
||||||
} );
|
|
||||||
if( pwa.state.data.tpl.appsidebarmenu ) {
|
|
||||||
document.querySelector( "#sidebar" )
|
|
||||||
.innerHTML = Mustache.render( pwa.state.data.tpl.appsidebarmenu, datasidebar )
|
|
||||||
document.querySelector( "#navbar" )
|
|
||||||
.innerHTML = Mustache.render( pwa.state.data.tpl.apptopbarmenu, datatopbar )
|
|
||||||
//active les icones svg de feather
|
|
||||||
feather.replace();
|
|
||||||
//active scroll presentation + sidebar animation
|
|
||||||
pwa.main.simplebar();
|
|
||||||
pwa.main.clickactive();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
// simplebar
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
pwa.main.simplebar = () => {
|
|
||||||
const simpleBarElement = document.getElementsByClassName( "js-simplebar" )[ 0 ];
|
|
||||||
|
|
||||||
if( simpleBarElement ) {
|
|
||||||
/* Initialize simplebar */
|
|
||||||
new SimpleBar( document.getElementsByClassName( "js-simplebar" )[ 0 ] )
|
|
||||||
|
|
||||||
const sidebarElement = document.getElementsByClassName( "sidebar" )[ 0 ];
|
|
||||||
const sidebarToggleElement = document.getElementsByClassName( "sidebar-toggle" )[ 0 ];
|
|
||||||
|
|
||||||
sidebarToggleElement.addEventListener( "click", () => {
|
|
||||||
sidebarElement.classList.toggle( "collapsed" );
|
|
||||||
|
|
||||||
sidebarElement.addEventListener( "transitionend", () => {
|
|
||||||
window.dispatchEvent( new Event( "resize" ) );
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/////////////////////////////////////////////////////////
|
|
||||||
// manage click effect
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
pwa.main.clickactive = () => {
|
|
||||||
const cleanactive = () => {
|
|
||||||
const el = document.querySelectorAll( '.sidebar-item' )
|
|
||||||
for( var i = 0; i < el.length; i++ ) {
|
|
||||||
//console.log( 'clean', el[ i ].classList )
|
|
||||||
el[ i ].classList.remove( 'active' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.addEventListener( "click", ( e ) => {
|
|
||||||
console.log( 'click', e );
|
|
||||||
if( e.target.classList.contains( 'sidebar-link' ) ) {
|
|
||||||
cleanactive();
|
|
||||||
e.target.closest( '.sidebar-item' )
|
|
||||||
.classList.add( 'active' );
|
|
||||||
// remonte au menu au dessus si existe
|
|
||||||
e.target.closest( '.sidebar-item' )
|
|
||||||
.closest( '.sidebar-item' )
|
|
||||||
.classList.add( 'active' );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
// If enter run the research
|
|
||||||
document.getElementById( 'globalsearch' )
|
|
||||||
.addEventListener( 'keypress', ( e ) => {
|
|
||||||
if( e.keyCode == 13 ) {
|
|
||||||
pwa.search.req( 'globalsearch' );
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
};
|
|
File diff suppressed because one or more lines are too long
@ -1,28 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var pwa = pwa || {};
|
|
||||||
/*
|
|
||||||
Manage notification
|
|
||||||
|
|
||||||
Get notification after tomenu was load
|
|
||||||
|
|
||||||
from /components/notification
|
|
||||||
____________________
|
|
||||||
|
|
||||||
*/
|
|
||||||
//--##
|
|
||||||
pwa.notification = {};
|
|
||||||
|
|
||||||
pwa.notification.update = () => {
|
|
||||||
console.log( 'get notification update for a user' );
|
|
||||||
axios.get( `https://${pwa.state.data.ctx.urlbackoffice}/notifications/user`, { headers: pwa.state.data.headers } )
|
|
||||||
.then( rep => {
|
|
||||||
console.log( "list des notifs", rep.data.payload.data )
|
|
||||||
rep.data.payload.data.number = rep.data.payload.data.notifs.length;
|
|
||||||
document.getElementById( "topbarmenuright" )
|
|
||||||
.innerHTML = Mustache.render( pwa.state.data.tpl.notiflist, rep.data.payload.data ) + document.getElementById( "topbarmenuright" )
|
|
||||||
.innerHTML;
|
|
||||||
} )
|
|
||||||
.catch( err => {
|
|
||||||
console.log( `Err pwa.notification.update data for user into header ${pwa.state.data.headers}`, err );
|
|
||||||
} );
|
|
||||||
};
|
|
File diff suppressed because one or more lines are too long
@ -1,261 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var pwa = pwa || {};
|
|
||||||
/*
|
|
||||||
FROM ndda/plugins/maildigitcreator/appjs/state.js
|
|
||||||
Manage state of the webapp
|
|
||||||
author: Phil Colzy - yzlocp@gmail.com
|
|
||||||
____________________________
|
|
||||||
pwa.state.save(): save in localstorage pwa.state.data avec le Nom du projet
|
|
||||||
____________________________
|
|
||||||
pwa.state.update(): update localstorage data from referential version
|
|
||||||
____________________________
|
|
||||||
pwa.state.ready(callback): await DOM loaded before running the callback
|
|
||||||
____________________________
|
|
||||||
pwa.state.refresh(): check if newversion of webapp to refresh page for dev purpose (do not use to refresh data)
|
|
||||||
____________________________
|
|
||||||
pwa.state.route(); check url and reroute it to function(urlparameter) or hide show part into a onepage website.
|
|
||||||
____________________________
|
|
||||||
pwa.state.loadfile({key:url},'dest') store in pwa.state.data.dest.key= file from url await all key to store result
|
|
||||||
___________________________
|
|
||||||
pwa.state.confdev() if ENV is dev then change urlbase for axios to looking for relevant file and refresh at a frequency pwa.state.refresh()
|
|
||||||
|
|
||||||
*/
|
|
||||||
//--##
|
|
||||||
pwa.state = {};
|
|
||||||
|
|
||||||
pwa.state.refresh = () => {
|
|
||||||
//console.warn(`Lance le refresh de${pwa.state.env.page}`)
|
|
||||||
//console.warn( 'ggg', pwa.state.data.ctx.urlbase )
|
|
||||||
const currenthtml = location.pathname.split( '/' )
|
|
||||||
.at( -1 )
|
|
||||||
.replace( '.html', '.json' );
|
|
||||||
//console.log( currenthtml )
|
|
||||||
axios.get( ` ${pwa.state.data.ctx.urlbase}/static/lastchange/${currenthtml}` )
|
|
||||||
.then(
|
|
||||||
data => {
|
|
||||||
//console.log( data.data.time, pwa.state.data.ctx.version )
|
|
||||||
if( data.data.time > pwa.state.data.ctx.version ) {
|
|
||||||
//console.log( "reload la page pour cause de lastchange detecté" );
|
|
||||||
pwa.state.data.ctx.version = data.data.time;
|
|
||||||
pwa.state.data.ctx.refreshstorage = true;
|
|
||||||
pwa.state.save();
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
//console.log( 'nothing change' )
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log( error );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
pwa.state.ready = callback => {
|
|
||||||
// Equivalent of jquery Document.ready()
|
|
||||||
// in case the document is already rendered
|
|
||||||
if( document.readyState != "loading" ) callback();
|
|
||||||
// modern browsers
|
|
||||||
else if( document.addEventListener )
|
|
||||||
document.addEventListener( "DOMContentLoaded", callback );
|
|
||||||
// IE <= 8
|
|
||||||
else
|
|
||||||
document.attachEvent( "onreadystatechange", function () {
|
|
||||||
if( document.readyState == "complete" ) callback();
|
|
||||||
} );
|
|
||||||
};
|
|
||||||
pwa.state.route = async () => {
|
|
||||||
/* Allow to create dynamic content
|
|
||||||
?action=pwa object&id=&hh& => pwa.action("?action=pwa object&id=&hh&")
|
|
||||||
ex: ?action=test.toto&id=1&t=123
|
|
||||||
Each function that can be called have to start with
|
|
||||||
if (e[1]=="?"){
|
|
||||||
const urlpar = new URLSearchParams( loc.search );
|
|
||||||
Then set param with urlpar.get('variable name')
|
|
||||||
}
|
|
||||||
OR ?pagemd=name used into .html (div)
|
|
||||||
Then it hide all <div class="pagemd" and show the one with <div id="page"+name
|
|
||||||
*/
|
|
||||||
console.groupCollapsed( `pwa.state.route with window.location` );
|
|
||||||
console.log( 'List of pwa available ', Object.keys( pwa ) );
|
|
||||||
if( !pwa.auth ) {
|
|
||||||
console.log( 'Warning, no auth.js, not a pb if no authentification need, if not check js order to be sur auth.js load before state.js' )
|
|
||||||
} else {
|
|
||||||
// check if still authenticated
|
|
||||||
if( pwa.state.data.login.isAuthenticated ) {
|
|
||||||
pwa.state.data.login.isAuthenticated = await pwa.auth.isAuthenticate();
|
|
||||||
}
|
|
||||||
//check if need authentification to show this page
|
|
||||||
if( pwa.state.data.ctx.pageneedauthentification && !pwa.state.data.login.isAuthenticated ) {
|
|
||||||
console.log( 'reload page cause not auth and page require an auth' )
|
|
||||||
window.location = `${pwa.state.data.ctx.pageredirectforauthentification}_${pwa.state.data.ctx.lang}.html`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const loc = window.location;
|
|
||||||
if( loc.search ) {
|
|
||||||
console.log( Object.keys( pwa ) )
|
|
||||||
const urlpar = new URLSearchParams( loc.search );
|
|
||||||
if( urlpar.get( 'action' ) ) {
|
|
||||||
const act = 'pwa.' + urlpar.get( 'action' ) + '("' + loc.search + '")';
|
|
||||||
try {
|
|
||||||
eval( act );
|
|
||||||
console.log( 'Specific action request to pwa.' + act )
|
|
||||||
} catch ( err ) {
|
|
||||||
console.log( err )
|
|
||||||
console.error( `You request ${act}, this action does not exist ` );
|
|
||||||
alert( `Sorry but you have no access to ${act}, ask your admin` );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let pgid = "pageindex"
|
|
||||||
if( urlpar.get( 'pagemd' ) ) {
|
|
||||||
pgid = "page" + urlpar.get( 'pagemd' )
|
|
||||||
}
|
|
||||||
//route to page content
|
|
||||||
Array.from( document.getElementsByClassName( "pagemd" ) )
|
|
||||||
.forEach( e => {
|
|
||||||
console.log( "detect pagemd", e.getAttribute( 'data-url' ) );
|
|
||||||
e.classList.add( "d-none" );
|
|
||||||
} );
|
|
||||||
if( document.getElementById( pgid ) ) {
|
|
||||||
document.getElementById( pgid )
|
|
||||||
.classList.remove( 'd-none' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.groupEnd();
|
|
||||||
// If pwa.main exist then start pwa.main.init();
|
|
||||||
if( pwa.main ) pwa.main.init();
|
|
||||||
}
|
|
||||||
pwa.state.loadfile = async ( list, dest ) => {
|
|
||||||
// load external file if flag pwa.state.data.ctx.refreshstorage is true from pwa.state.refresh();
|
|
||||||
//from list = {name:url} request are done with ctx.urlbase/url and store in localstorage pwa.state.data[dest][name]=data
|
|
||||||
// if dest=='js' then it eval the js and store origin in pwa.state.data.js={name:url}
|
|
||||||
//For at true refreshstorage if destination pwa.state.dest does not exist
|
|
||||||
|
|
||||||
//console.log( 'list', list )
|
|
||||||
//console.log( 'pwa.state.data.ctx.refreshstorage', pwa.state.data.ctx.refreshstorage )
|
|
||||||
if( pwa.state.data.ctx.refreshstorage || !pwa.state.data[ dest ] || Object.keys( pwa.state.data[ dest ] )
|
|
||||||
.length == 0 ) {
|
|
||||||
if( !pwa.state.data[ dest ] ) pwa.state.data[ dest ] = {};
|
|
||||||
try {
|
|
||||||
let reqname = [];
|
|
||||||
let reqload = [];
|
|
||||||
for( const [ k, v ] of Object.entries( list ) ) {
|
|
||||||
if( !pwa.state.data[ dest ][ k ] || pwa.state.data.ctx.refreshstorage ) {
|
|
||||||
// if still not exist or refresstorage is set to true then add to load
|
|
||||||
//@TODO check it works well on production
|
|
||||||
reqname.push( k );
|
|
||||||
reqload.push( v );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//console.log( pwa.state.data.ctx.urlbase, reqload )
|
|
||||||
let resload = await Promise.all( reqload.map( r => {
|
|
||||||
if( dest == 'tpldata' ) r = `${r}_${pwa.state.data.ctx.lang}.json`;
|
|
||||||
return axios.get( `${pwa.state.data.ctx.urlbase}/${r}`, { headers: pwa.state.data.headers } )
|
|
||||||
} ) );
|
|
||||||
resload.forEach( ( d, i ) => {
|
|
||||||
pwa.state.data[ dest ][ reqname[ i ] ] = d.data;
|
|
||||||
pwa.state.save();
|
|
||||||
} )
|
|
||||||
} catch ( err ) {
|
|
||||||
console.error( 'FATAL ERROR check that list exist remove if not', list, err.message )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pwa.state.save = function () {
|
|
||||||
localStorage.setItem( pwa.state.data.ctx.website, JSON.stringify( pwa.state.data ) );
|
|
||||||
};
|
|
||||||
pwa.state.update = async function () {
|
|
||||||
const domhtml = document.querySelector( "html" );
|
|
||||||
const ctx = {
|
|
||||||
tribeid: domhtml.getAttribute( 'data-tribeid' ),
|
|
||||||
website: domhtml.getAttribute( "data-website" ),
|
|
||||||
nametpl: domhtml.getAttribute( "data-nametpl" ),
|
|
||||||
pagename: domhtml.getAttribute( "data-pagename" ),
|
|
||||||
urlbackoffice: domhtml.getAttribute( "data-urlbackoffice" ),
|
|
||||||
pageneedauthentification: true,
|
|
||||||
pageredirectforauthentification: "",
|
|
||||||
lang: domhtml.getAttribute( "lang" ),
|
|
||||||
env: domhtml.getAttribute( "data-env" ),
|
|
||||||
version: domhtml.getAttribute( "data-version" ),
|
|
||||||
refreshstorage: false
|
|
||||||
}
|
|
||||||
if( !domhtml.getAttribute( 'data-pageneedauthentification' ) || domhtml.getAttribute( 'data-pageneedauthentification' ) == 'false' ) {
|
|
||||||
ctx.pageneedauthentification = false;
|
|
||||||
}
|
|
||||||
if( domhtml.getAttribute( 'data-pageforauthentification' ) ) {
|
|
||||||
ctx.pageforauthentification = domhtml.getAttribute( 'data-pageforauthentification' );
|
|
||||||
}
|
|
||||||
console.groupCollapsed( `update pwa.state with html attribut or from localstorage into ${ctx.website}` );
|
|
||||||
console.log( 'html context:', ctx );
|
|
||||||
if( localStorage.getItem( ctx.website ) ) {
|
|
||||||
pwa.state.data = JSON.parse( localStorage.getItem( ctx.website ) );
|
|
||||||
//alert( 'recupere pwa.state.data xpaganid:' + pwa.state.data.headers.xpaganid )
|
|
||||||
}
|
|
||||||
if( !( pwa.state.data && pwa.state.data.ctx.tribeid == ctx.tribeid && pwa.state.data.ctx.website == ctx.website ) ) {
|
|
||||||
console.log( " reinitialise localstorage cause work on a different project or first access" );
|
|
||||||
delete pwa.state.data;
|
|
||||||
localStorage.removeItem( ctx.website )
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if( pwa.state.data && statejson.data.app.version && ( parseInt( pwa.state.data.app.version ) < parseInt( statejson.data.app.version ) ) ) {
|
|
||||||
// le numero de version en mémoire est < au numero disponible sur le serveur
|
|
||||||
// force un logout pour reinitialiser les menus
|
|
||||||
delete pwa.state.data;
|
|
||||||
localStorage.removeItem( pwa.PROJET )
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if( !pwa.state.data ) {
|
|
||||||
// No localstorage available et one by default
|
|
||||||
pwa.state.data = {
|
|
||||||
ctx,
|
|
||||||
login: {
|
|
||||||
isAuthenticated: false,
|
|
||||||
user: {},
|
|
||||||
rememberMe: {}
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
'xauth': '1',
|
|
||||||
'xpaganid': '1',
|
|
||||||
'xtribe': ctx.tribeid,
|
|
||||||
'xlang': ctx.lang,
|
|
||||||
'xworkon': ctx.tribeid,
|
|
||||||
'xapp': `${ctx.tribeid}:${ctx.website}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log( 'load new state.data', pwa.state.data )
|
|
||||||
}
|
|
||||||
// Check if external component need to be load
|
|
||||||
const app = `${pwa.state.data.ctx.tribeid}:${pwa.state.data.ctx.website}`;
|
|
||||||
if( pwa.state.data.login.isAuthenticated &&
|
|
||||||
pwa.state.data.login.user.ACCESSRIGHTS.app[ app ] &&
|
|
||||||
pwa.state.data.login.user.ACCESSRIGHTS.app[ app ].js
|
|
||||||
) {
|
|
||||||
console.log( 'tttt', pwa.state.data.login.isAuthenticated, pwa.state.data.login.user.ACCESSRIGHTS.app[ app ].js )
|
|
||||||
pwa.state.data.login.user.ACCESSRIGHTS.app[ app ].js.some( ( u ) => {
|
|
||||||
console.log( `load from user ACCESSRIGHTS app[${pwa.state.data.ctx.tribeid}:${pwa.state.data.ctx.website}].js : ${u}` )
|
|
||||||
const script = document.createElement( 'script' );
|
|
||||||
script.src = u;
|
|
||||||
script.async = false;
|
|
||||||
document.body.appendChild( script );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
//Check dev to set parameter to simplify dev app
|
|
||||||
//check in confdev version and set pwa.state.data.ctx.refreshstorage at true if new version
|
|
||||||
pwa.state.confdev();
|
|
||||||
console.groupEnd();
|
|
||||||
pwa.state.route();
|
|
||||||
pwa.state.save();
|
|
||||||
};
|
|
||||||
pwa.state.confdev = () => {
|
|
||||||
if( pwa.state.data.ctx.env == 'dev' ) {
|
|
||||||
pwa.state.data.ctx.urlbase = `/space/${pwa.state.data.ctx.tribeid}/${pwa.state.data.ctx.website}/dist`;
|
|
||||||
// check each 3 sec if new version to reload
|
|
||||||
setInterval( "pwa.state.refresh()", 3000 );
|
|
||||||
} else {
|
|
||||||
//pwa.state.axios = axios.create();
|
|
||||||
pwa.state.data.ctx.urlbase = "/";
|
|
||||||
// check and refresh if new version only one time
|
|
||||||
pwa.state.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Refresh browser state if exist else get pwa.state defaults
|
|
||||||
pwa.state.ready( pwa.state.update );
|
|
@ -1,34 +0,0 @@
|
|||||||
pwa = pwa || {};
|
|
||||||
pwa.main = pwa.main || {};
|
|
||||||
pwa.main.tpldata = pwa.main.tpldata || {};
|
|
||||||
pwa.main.tpl = pwa.main.tpl || {};
|
|
||||||
//menu
|
|
||||||
pwa.main.tpldata.sidebarAdminapixtribe = `static/components/adminapixtribe/sidebaradminapixtribe`;
|
|
||||||
// Custom data in user lang
|
|
||||||
|
|
||||||
//tremplate to store
|
|
||||||
pwa.main.tpl.adminapixtribe = 'static/components/adminapixtribe/adminapixtribe.mustache';
|
|
||||||
pwa.main.tpl.adminapixtribesysinfo = 'static/components/adminapixtribe/adminapixtribesysinfo.mustache';
|
|
||||||
pwa.main.tpl.adminapixtribeactivity = 'static/components/adminapixtribe/adminapixtribeactivity.mustache';
|
|
||||||
|
|
||||||
pwa.adminapixtribe = {};
|
|
||||||
|
|
||||||
pwa.adminapixtribe.init = () => {
|
|
||||||
// Run back stuff to update data
|
|
||||||
}
|
|
||||||
pwa.adminapixtribe.sysinfoview = () => {
|
|
||||||
const datasysinfo = {}; //request to get info and push them in template
|
|
||||||
document.getElementById( 'maincontent' )
|
|
||||||
.innerHTML = Mustache.render( pwa.state.data.tpl.adminapixtribesysinfo, datasysinfo );
|
|
||||||
}
|
|
||||||
pwa.adminapixtribe.activityview = () => {
|
|
||||||
const dataactivity = {}; //request to get info and push them in template
|
|
||||||
document.getElementById( 'maincontent' )
|
|
||||||
.innerHTML = Mustache.render( pwa.state.data.tpl.adminapixtribeactivity, dataactivity );
|
|
||||||
}
|
|
||||||
|
|
||||||
pwa.adminapixtribe.tribeidview = () => {
|
|
||||||
const datatribeid = {}; //request to get info and push them in template
|
|
||||||
document.getElementById( 'maincontent' )
|
|
||||||
.innerHTML = Mustache.render( pwa.state.data.tpl.adminapixtribe, datatribeid );
|
|
||||||
}
|
|
@ -1,145 +0,0 @@
|
|||||||
// app management sidebar and navbar
|
|
||||||
/*
|
|
||||||
Load into pwa.state.data.tpl[name] templates content
|
|
||||||
appsidebarmenu.mustache side menu sbgroupmenu:[list of submenu]
|
|
||||||
apptopbarmenu.mustache top menu menuprofil:[ list of submenu]
|
|
||||||
apptopbarnotification.mustache show a dropdown list if withnotification is true
|
|
||||||
apptopbarmessage.mustache show a dropdown list if withmessage is true
|
|
||||||
|
|
||||||
Load list of public data into pwa.state.data.menu
|
|
||||||
sidebar
|
|
||||||
topbar
|
|
||||||
Then add specific menu in based on login list into
|
|
||||||
pwa.state.data.login.user.ACCESSRIGHTS.app[tribeid:website]
|
|
||||||
each name starting by sidebar is added into sidebar.subgroupmenu
|
|
||||||
each name starting by topbar is added into topbar.menuprofil
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
var pwa = pwa || {};
|
|
||||||
pwa.app = {};
|
|
||||||
pwa.app.init = async () => {
|
|
||||||
//Load template in pwa.state.data.tpl
|
|
||||||
console.log( 'app.init()' );
|
|
||||||
const tpllist = {};
|
|
||||||
[ 'appsidebarmenu', 'apptopbarmenu', 'apptopbarnotification', 'apptopbarmessage' ].forEach( c => {
|
|
||||||
tpllist[ c ] = `static/components/appmesa/${c}.mustache`
|
|
||||||
} );
|
|
||||||
// add other generic public template, carefull name have to be unique
|
|
||||||
tpllist[ 'notiflist' ] = `static/components/notification/notiflist.mustache`;
|
|
||||||
// tpllist[ 'msglist' ] = `static/components/messagerie/msglist.mustache`;
|
|
||||||
|
|
||||||
tpllist[ 'verticaltab' ] = `static/components/verticaltab/verticaltab.mustache`;
|
|
||||||
|
|
||||||
await pwa.state.loadfile( tpllist, "tpl" );
|
|
||||||
await pwa.app.getcontent();
|
|
||||||
//get menu depending of user profile: tribeid:app
|
|
||||||
};
|
|
||||||
pwa.app.getcontent = async () => {
|
|
||||||
const menubase = {};
|
|
||||||
let menu = [ 'sidebar', 'topbar' ];
|
|
||||||
//check if authentify and add specific menu
|
|
||||||
if( pwa.state.data.login && pwa.state.data.login.isAuthenticated ) {
|
|
||||||
//Get personnal menu user.ACCESSRIGHTS={app:{ "tribeid:website":{sidebar,top}
|
|
||||||
const appname = `${pwa.state.data.ctx.tribeid}:${pwa.state.data.ctx.website}`
|
|
||||||
//console.log( 'with ', appname )
|
|
||||||
menu = menu.concat( pwa.state.data.login.user.ACCESSRIGHTS.app[ appname ] )
|
|
||||||
}
|
|
||||||
//console.log( 'update pwa.state.data.menu with ', menu )
|
|
||||||
menu.forEach( c => { menubase[ c ] = `static/data/${c}_${pwa.state.data.ctx.lang}.json` } );
|
|
||||||
await pwa.state.loadfile( menubase, 'menu' );
|
|
||||||
pwa.app.loadsidebarmenu( menu.filter( m => m.includes( 'sidebar' ) ) )
|
|
||||||
pwa.app.loadtopmenu( menu.filter( m => m.includes( 'topbar' ) ) );
|
|
||||||
//active les icones svg de feather
|
|
||||||
feather.replace();
|
|
||||||
//active scroll presentation + sidebar animation
|
|
||||||
pwa.app.simplebar();
|
|
||||||
pwa.app.clickactive();
|
|
||||||
}
|
|
||||||
pwa.app.loadsidebarmenu = ( list ) => {
|
|
||||||
//console.log( 'list de menu sidebar', list )
|
|
||||||
const data = pwa.state.data.menu.sidebar
|
|
||||||
for( let m of list ) {
|
|
||||||
if( m != 'sidebar' ) {
|
|
||||||
console.log( m )
|
|
||||||
data.sbgroupmenu = data.sbgroupmenu.concat( pwa.state.data.menu[ m ] )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log( data )
|
|
||||||
document.querySelector( "#sidebar" )
|
|
||||||
.innerHTML = Mustache.render( pwa.state.data.tpl.appsidebarmenu, data )
|
|
||||||
}
|
|
||||||
pwa.app.loadtopmenu = ( list ) => {
|
|
||||||
const data = pwa.state.data.menu.topbar
|
|
||||||
for( let m of list ) {
|
|
||||||
if( m != 'topbar' ) {
|
|
||||||
data.menuprofil = data.menuprofil.concat( pwa.state.data.menu[ m ] )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//update date from login if authenticated
|
|
||||||
if( pwa.state.data.login.isAuthenticated ) {
|
|
||||||
//remove the login item from menuprofil
|
|
||||||
data.menuprofil.shift();
|
|
||||||
data.name = pwa.state.data.login.user.NAME;
|
|
||||||
data.avatarimg = pwa.state.data.login.user.AVATARIMG;
|
|
||||||
// get notification / message if exist
|
|
||||||
//const notifsrc= `${pwa.urlbackauth}/Tribes/logs/`;
|
|
||||||
if( pwa.state.data.menu.topbar.withnotification ) {
|
|
||||||
pwa.notification.update();
|
|
||||||
}
|
|
||||||
if( pwa.state.data.menu.topbar.withmessage ) {
|
|
||||||
//pwa.message.update( );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log( 'topbar data', data.menuprofil );
|
|
||||||
document.querySelector( "#navbar" )
|
|
||||||
.innerHTML = Mustache.render( pwa.state.data.tpl.apptopbarmenu, data )
|
|
||||||
}
|
|
||||||
pwa.app.simplebar = () => {
|
|
||||||
const simpleBarElement = document.getElementsByClassName( "js-simplebar" )[ 0 ];
|
|
||||||
|
|
||||||
if( simpleBarElement ) {
|
|
||||||
/* Initialize simplebar */
|
|
||||||
new SimpleBar( document.getElementsByClassName( "js-simplebar" )[ 0 ] )
|
|
||||||
|
|
||||||
const sidebarElement = document.getElementsByClassName( "sidebar" )[ 0 ];
|
|
||||||
const sidebarToggleElement = document.getElementsByClassName( "sidebar-toggle" )[ 0 ];
|
|
||||||
|
|
||||||
sidebarToggleElement.addEventListener( "click", () => {
|
|
||||||
sidebarElement.classList.toggle( "collapsed" );
|
|
||||||
|
|
||||||
sidebarElement.addEventListener( "transitionend", () => {
|
|
||||||
window.dispatchEvent( new Event( "resize" ) );
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pwa.app.clickactive = () => {
|
|
||||||
const cleanactive = () => {
|
|
||||||
const el = document.querySelectorAll( '.sidebar-item' )
|
|
||||||
for( var i = 0; i < el.length; i++ ) {
|
|
||||||
//console.log( 'clean', el[ i ].classList )
|
|
||||||
el[ i ].classList.remove( 'active' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.addEventListener( "click", ( e ) => {
|
|
||||||
console.log( 'click', e );
|
|
||||||
if( e.target.classList.contains( 'sidebar-link' ) ) {
|
|
||||||
cleanactive();
|
|
||||||
e.target.closest( '.sidebar-item' )
|
|
||||||
.classList.add( 'active' );
|
|
||||||
// remonte au menu au dessus si existe
|
|
||||||
e.target.closest( '.sidebar-item' )
|
|
||||||
.closest( '.sidebar-item' )
|
|
||||||
.classList.add( 'active' );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
// If enter run the research
|
|
||||||
document.getElementById( 'globalsearch' )
|
|
||||||
.addEventListener( 'keypress', ( e ) => {
|
|
||||||
if( e.keyCode == 13 ) {
|
|
||||||
pwa.search.req( 'globalsearch' );
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
};
|
|
@ -1,28 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var pwa = pwa || {};
|
|
||||||
/*
|
|
||||||
Manage message
|
|
||||||
|
|
||||||
____________________
|
|
||||||
|
|
||||||
*/
|
|
||||||
//--##
|
|
||||||
pwa.message = {};
|
|
||||||
|
|
||||||
pwa.message.update = ( urlmsg ) => {
|
|
||||||
console.log( 'get message update' );
|
|
||||||
axios.get( urlmsg, { headers: pwa.state.data.headers } )
|
|
||||||
.then( rep => {
|
|
||||||
const tpl = document.getElementById( "app_menutopmessage" );
|
|
||||||
document.getElementById( "topmenumessages" )
|
|
||||||
.innerHTML = Mustache.render( tpl.innerHTML, { messagess: rep.data } );
|
|
||||||
} )
|
|
||||||
.catch( err => {
|
|
||||||
console.log( `Err pwa.notification.update data from ${urlmsg}`, err );
|
|
||||||
} );
|
|
||||||
|
|
||||||
};
|
|
||||||
/*pwa
|
|
||||||
.state
|
|
||||||
.ready( pwa.notification.update( 'static/data/staticContentwebsite/notification.json' ) );
|
|
||||||
*/
|
|
@ -1,271 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var pwa = pwa || {};
|
|
||||||
/*
|
|
||||||
Manage referentials for a tribeid
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
//--##
|
|
||||||
pwa.referential = {};
|
|
||||||
pwa.referential.content = `
|
|
||||||
<div class="row gap-20 masonry pos-r">
|
|
||||||
<div class="blocA masonry-item col-md-6">
|
|
||||||
<div class="bgc-white p-20 bd">
|
|
||||||
<h6 class="c-grey-900">Affiche json</h6>
|
|
||||||
<div id="jsoneditor" class="jsoneditor">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="blocB masonry-item col-md-6">
|
|
||||||
<div class="bgc-white p-20 bd">
|
|
||||||
<h6 class="c-grey-900">Changer son mot de passe</h6>
|
|
||||||
<div class="formdata">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
pwa.referential.set = async ( sourceref ) => {
|
|
||||||
/*
|
|
||||||
@sourceref app (recupere le referentiel de l'application)
|
|
||||||
workon (recupere le referentiel du client id sur lequel on travail state.data.headers[X-workOn])
|
|
||||||
On alimente le referentiel ds localStorage
|
|
||||||
state.app.referentials = {menu:}
|
|
||||||
*/
|
|
||||||
console.groupCollapsed( 'Load pwa.referential.set with ', sourceref )
|
|
||||||
//let reftoload;
|
|
||||||
let savexworkon;
|
|
||||||
if( sourceref == "app" ) {
|
|
||||||
//reftoload = pwa.state.data.app.referentials
|
|
||||||
// affecte le menu en fonction du profil
|
|
||||||
console.assert( pwa.state.data.app.profil && pwa.state.data.app.profil[ pwa.state.data.userlogin.ACCESSRIGHTS.app[ `${pwa.tribeid}:${pwa.PROJET}` ] ], `profil de l app ${pwa.tribeid}:${pwa.PROJET} n'existe pas pour le user dans userlogin.ACCESSRIGHTS.app , verifier qu'on a bien pour cette app un menu correspondant` );
|
|
||||||
const menuapp = pwa.state.data.app.profil[ pwa.state.data.userlogin.ACCESSRIGHTS.app[ `${pwa.tribeid}:${pwa.PROJET}` ] ].menu;
|
|
||||||
if( !pwa.state.data.app.referentials.json[ menuapp ] ) {
|
|
||||||
//on ajoute ce menu avec une version à O pour forcer sa mise à jour
|
|
||||||
pwa.state.data.app.referentials.json[ menuapp ] = { version: 0 }
|
|
||||||
}
|
|
||||||
//set xworkon on tribeid where app is stored
|
|
||||||
savexworkon = pwa.state.data.headers[ 'X-WorkOn' ];
|
|
||||||
pwa.state.data.headers[ 'X-WorkOn' ] = pwa.tribeid
|
|
||||||
}
|
|
||||||
/* else if(sourceref == "xworkon") {
|
|
||||||
// on recupere le referentiel utile pour clientcon.json du xworkon pour le projet
|
|
||||||
reftoload = pwa.state.data.xworkon.referentials
|
|
||||||
}*/
|
|
||||||
// recupere les versions du clientconf.json
|
|
||||||
const clientconfref = await axios.get( `${pwa.urlbackauth}/referentials/clientconf/referentials`, { headers: pwa.state.data.headers } );
|
|
||||||
console.debug( 'clientconfref', clientconfref );
|
|
||||||
for( const o of [ 'object', 'json', 'data' ] ) {
|
|
||||||
console.debug( o )
|
|
||||||
//update by adding or upgraded version if in dev or in prod and pwa.state.data[sourceref].referentials[type=object|json|data][nomref].version is upper than the local one
|
|
||||||
// Take care app is load first with previous referential then need to refresh app to use the new version.
|
|
||||||
if( !clientconfref.data.referentials[ o ] ) {
|
|
||||||
clientconfref.data.referentials[ o ] = {};
|
|
||||||
}
|
|
||||||
//Warning do not use foreach in async cause foreach is not sync
|
|
||||||
for( const r of Object.keys( clientconfref.data.referentials[ o ] ) ) {
|
|
||||||
console.debug( r )
|
|
||||||
if( !pwa.state.data.app.referentials[ o ][ r ] ) {
|
|
||||||
// New referential added need to reload app to refresh app
|
|
||||||
pwa.state.data.app.referentials[ o ][ r ] = { version: -1 };
|
|
||||||
}
|
|
||||||
if( pwa.MODE_ENV == "dev" || !( pwa.state.data.app.referentials[ o ][ r ].version == clientconfref[ o ][ r ].version ) ) {
|
|
||||||
// alors nouvelle version à mettre à jour en dev on le fait systematiquement
|
|
||||||
console.log( `${pwa.urlbackauth}/referentials/content/${o}/${r}`, pwa.state.data.headers );
|
|
||||||
const dataref = await axios.get( `${pwa.urlbackauth}/referentials/content/${o}/${r}`, { headers: pwa.state.data.headers } )
|
|
||||||
if( dataref.status == 200 ) {
|
|
||||||
console.log( `${o} - ${r}`, dataref.data )
|
|
||||||
pwa.state.data[ sourceref ].referentials[ o ][ r ] = dataref.data;
|
|
||||||
pwa.state.save()
|
|
||||||
} else {
|
|
||||||
console.log( `ERREUR de recuperation de referentiel ${o} ${r}` )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// we remove from local if nomref disapear of app clientconfref.data.referential
|
|
||||||
Object.keys( pwa.state.data[ sourceref ].referentials[ o ] )
|
|
||||||
.forEach( r => {
|
|
||||||
if( !clientconfref.data.referentials[ o ] ) {
|
|
||||||
delete pwa.state.data[ sourceref ].referentials[ o ];
|
|
||||||
pwa.state.save();
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
};
|
|
||||||
if( sourceref == "app" ) {
|
|
||||||
// on remet le xworkon en cours
|
|
||||||
pwa.state.data.headers[ 'X-WorkOn' ] = savexworkon;
|
|
||||||
}
|
|
||||||
console.groupEnd();
|
|
||||||
};
|
|
||||||
pwa.referential.forceupdate = () => {
|
|
||||||
axios.get( `${pwa.urlbackauth}/referentials/updatefull`, {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
} )
|
|
||||||
.then( data => {
|
|
||||||
alert( data.status )
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
// GUI to manage referential
|
|
||||||
pwa.referential.setting = async ( objecttype ) => {
|
|
||||||
// Convert location.search to get objecttype
|
|
||||||
const urlpar = new URLSearchParams( objecttype );
|
|
||||||
objecttype = ( urlpar.get( 'objecttype' ) ) ? urlpar.get( 'objecttype' ) : objecttype;
|
|
||||||
|
|
||||||
const tpleditor = `
|
|
||||||
<div class="input-group mb-3" data-idref="{{id}}">
|
|
||||||
<button class="btn btn-outline-primary actionobject save" >
|
|
||||||
{{{btnsave}}}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-outline-primary actionobject delete" >
|
|
||||||
{{{btndelete}}}
|
|
||||||
</button>
|
|
||||||
<button class=" btn btn-outline-primary actionobject copy" >
|
|
||||||
{{{btncopy}}}
|
|
||||||
</button>
|
|
||||||
<input type="text" class="form-control nameas" placeholder="{{{copyplaceholder}}}" >
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="jsoneditor mb-3"></div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
console.groupCollapsed( `load referentials setting for ${objecttype}` );
|
|
||||||
//reinit submenuitems
|
|
||||||
pwa.state.data.app.referentials.json.referentialsetting.submenuitems = [];
|
|
||||||
//Requested directly the back warranty to get always the last updated referential
|
|
||||||
const data = await axios.get( `${pwa.urlbackauth}/referentials/contentlist/${objecttype}`, {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
} );
|
|
||||||
console.log( "Liste des referentiels ", data.data )
|
|
||||||
let reqref = []
|
|
||||||
let ref = []
|
|
||||||
//init a temporary (opposite od state that is save) data to work on referential (each time referentials/object is load this variable is refresh)
|
|
||||||
pwa.tmp = {};
|
|
||||||
data.data.forEach( o => {
|
|
||||||
reqref.push( axios.get( `${pwa.urlbackauth}/referentials/contentfull/${objecttype}/${o}`, {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
} ) )
|
|
||||||
ref.push( o );
|
|
||||||
} );
|
|
||||||
axios.all( reqref )
|
|
||||||
.then( axios.spread( ( ...rep ) => {
|
|
||||||
console.log( rep )
|
|
||||||
rep.forEach( ( d, i ) => {
|
|
||||||
pwa.tmp[ ref[ i ] ] = d.data
|
|
||||||
const submenuit = {
|
|
||||||
active: "",
|
|
||||||
groupinfo: ref[ i ],
|
|
||||||
id: `referential${ref[i]}`,
|
|
||||||
optionjsoneditor: {},
|
|
||||||
onclick: `pwa.referential.save(event,'referential${ref[i]}')`,
|
|
||||||
btnsave: pwa.state.data.app.referentials.json.referentialsetting.btnsave,
|
|
||||||
btndelete: pwa.state.data.app.referentials.json.referentialsetting.btndelete,
|
|
||||||
btncopy: pwa.state.data.app.referentials.json.referentialsetting.btncopy,
|
|
||||||
copyplaceholder: pwa.state.data.app.referentials.json.referentialsetting.copyplaceholder,
|
|
||||||
objecttype: objecttype
|
|
||||||
};
|
|
||||||
pwa.state.data.app.referentials.json.referentialsetting.submenuitems.push( submenuit )
|
|
||||||
} );
|
|
||||||
document.getElementById( 'maincontent' )
|
|
||||||
.innerHTML = Mustache.render( document.getElementById( 'referential' )
|
|
||||||
.innerHTML, pwa.state.data.app.referentials.json.referentialsetting );
|
|
||||||
|
|
||||||
pwa.state.data.app.referentials.json.referentialsetting.submenuitems.forEach( tab => {
|
|
||||||
document.getElementById( tab.id )
|
|
||||||
.innerHTML = Mustache.render( tpleditor, tab );
|
|
||||||
//console.log( tab.id, tab )
|
|
||||||
// Convert each div with formfieldtype to a form field set with value if exist and listen button to run callback
|
|
||||||
pwa.referential.init( tab );
|
|
||||||
} );
|
|
||||||
} ) )
|
|
||||||
.catch( err => {
|
|
||||||
console.log( "eeeee", err );
|
|
||||||
const submenuit = {
|
|
||||||
active: "",
|
|
||||||
groupinfo: objecttype,
|
|
||||||
id: `referential${objecttype}`,
|
|
||||||
optionjsoneditor: {},
|
|
||||||
onclick: `pwa.referential.save(event,'referential${objecttype}')`,
|
|
||||||
data: { erreur: err }
|
|
||||||
};
|
|
||||||
pwa.state.data.app.referentials.json.referentialsetting.submenuitems.push( submenuit )
|
|
||||||
} );
|
|
||||||
console.groupEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
pwa.referential.init = ( tab ) => {
|
|
||||||
const doctab = document.querySelector( `#${tab.id}` );
|
|
||||||
const editor = new JSONEditor( doctab.querySelector( `.jsoneditor` ), tab.optionjsoneditor );
|
|
||||||
console.table( tab )
|
|
||||||
console.log( tab.objecttype, tab.groupinfo )
|
|
||||||
editor.set( pwa.tmp[ tab.groupinfo ] );
|
|
||||||
editor.expandAll();
|
|
||||||
// ajoute un listener sur btn pour faire axios post with editor.get()
|
|
||||||
Array.from( doctab.querySelectorAll( '.save, .delete, .copy' ) )
|
|
||||||
.forEach( act => {
|
|
||||||
act.addEventListener( 'click', e => {
|
|
||||||
e.preventDefault();
|
|
||||||
console.log( 'cliiiiiiiiiiiiiiiiiiiick', tab.id )
|
|
||||||
if( e.target.classList.contains( 'save' ) ) {
|
|
||||||
/*
|
|
||||||
axios.put( `${pwa.urlbackauth}/referentials/content/${tab.objecttype}/${tab.groupinfo}`, editor.get(), {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
} )
|
|
||||||
.then( data => {
|
|
||||||
console.log( "affiche message done" );
|
|
||||||
} )
|
|
||||||
.catch( err => {
|
|
||||||
console.log( "affiche message err ", err )
|
|
||||||
} );
|
|
||||||
*/
|
|
||||||
console.log( 'editor', editor.get() );
|
|
||||||
}
|
|
||||||
if( e.target.classList.contains( 'delete' ) ) {
|
|
||||||
//axios.get( @tTODO la mise à jour du nouveau referentiel avec la version)
|
|
||||||
}
|
|
||||||
if( e.target.classList.contains( 'copy' ) ) {
|
|
||||||
//@TODO create a new referential file localy from an existing one
|
|
||||||
}
|
|
||||||
/*console.log( e.target.closest( '[data-idref]' )
|
|
||||||
.getAttribute( 'data-idref' ) )
|
|
||||||
console.log( editor.get() );
|
|
||||||
envoyer à axios et modifier pwa.state. en cours
|
|
||||||
*/
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pwa.referential.initold = async ( object ) => {
|
|
||||||
$( '#mainContent' )
|
|
||||||
.html( pwa.referential.content );
|
|
||||||
// charge ul#menuleft
|
|
||||||
// create the editor
|
|
||||||
const container = document.getElementById( "jsoneditor" )
|
|
||||||
const options = {}
|
|
||||||
const editor = new JSONEditor( container, options )
|
|
||||||
axios.get( `${pwa.urlbackauth}/referentials/content/object/${object}`, {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
} )
|
|
||||||
.then(
|
|
||||||
( ref ) => { editor.set( ref.data ) },
|
|
||||||
( error ) => {
|
|
||||||
window.location.pathname == "/"
|
|
||||||
} )
|
|
||||||
// set json
|
|
||||||
/*const initialJson = {
|
|
||||||
"Array": [1, 2, 3],
|
|
||||||
"Boolean": true,
|
|
||||||
"Null": null,
|
|
||||||
"Number": 123,
|
|
||||||
"Object": { "a": "b", "c": "d" },
|
|
||||||
"String": "Hello World"
|
|
||||||
}
|
|
||||||
editor.set(initialJson)
|
|
||||||
*/
|
|
||||||
// get json
|
|
||||||
const updatedJson = editor.get()
|
|
||||||
console.log( 'updatedJson', updatedJson )
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var pwa = pwa || {};
|
|
||||||
/*
|
|
||||||
Reporting js managing sceen
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
//--##
|
|
||||||
pwa.reporting = pwa.reporting || {};
|
|
||||||
pwa.reporting.init = () => {
|
|
||||||
console.log('charge reporting');
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
// app management for search request
|
|
||||||
/*
|
|
||||||
@todo create a search/index content for an apixtribe instance
|
|
||||||
//store request to analyse kind of looking for
|
|
||||||
|
|
||||||
*/
|
|
||||||
var pwa = pwa || {};
|
|
||||||
pwa.search = {};
|
|
||||||
pwa.search.tpl = `
|
|
||||||
<div class="container-fluid p-0">
|
|
||||||
<h1 class="h3 mb-3">Recherche pour: {{searchtxt}}</h1>
|
|
||||||
<div class="row">
|
|
||||||
{{#results}}
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<p><a href="{{url}}" target="_blank">{{url}}</a> - {{format}}</p>
|
|
||||||
<h5 class="card-title mb-0">{{{title}}}</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
{{{desc}}}
|
|
||||||
</div>
|
|
||||||
<div class="card-footer">
|
|
||||||
{{#relatedtag}}
|
|
||||||
<button type="button" class="btn btn-dark" >{{.}}</button>
|
|
||||||
{{/relatedtag}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/results}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
pwa.search.dataexample = {
|
|
||||||
searchtxt: "search string to looking for",
|
|
||||||
results: [ {
|
|
||||||
url: "https://apixtribe.org",
|
|
||||||
format: "pdf",
|
|
||||||
title: "Search finding title of the page",
|
|
||||||
desc: "sentence contexte of the searchtxt",
|
|
||||||
relatedtag: [ "keyword1", "keyword2" ]
|
|
||||||
} ]
|
|
||||||
}
|
|
||||||
|
|
||||||
pwa.search.req = ( info ) => {
|
|
||||||
if( info[ 1 ] == "?" ) {
|
|
||||||
const urlparse = new URLSearchParams( info );
|
|
||||||
//Then set param with urlpar.get('variable name')
|
|
||||||
info = {
|
|
||||||
idsearch: urlparse.get( 'idsearchtxt' )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const searchtxt = document.getElementById( info )
|
|
||||||
.value;
|
|
||||||
console.log( info, searchtxt );
|
|
||||||
|
|
||||||
const req = { searchtxt };
|
|
||||||
req.results = [ {
|
|
||||||
url: "",
|
|
||||||
format: "",
|
|
||||||
title: "Nous n'avons rien à vous proposer sur ce sujet",
|
|
||||||
desc: `<p>To find more click on <a href='https://google.com/search?q=${searchtxt.replace(/ /g,'+')}' target="_blanck"> google search </a></p> `,
|
|
||||||
relatedtag: [ 'tag1', "tag2", "tag3" ]
|
|
||||||
} ];
|
|
||||||
|
|
||||||
document.getElementsByTagName( 'main' )[ 0 ]
|
|
||||||
.innerHTML = Mustache.render( pwa.search.tpl, req );
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
var pwa = pwa || {};
|
|
||||||
//--##
|
|
||||||
pwa.userManager = pwa.userManager || {};
|
|
||||||
|
|
||||||
pwa.userManager.init = function() {
|
|
||||||
$("#espaceMasonry")
|
|
||||||
.html("");
|
|
||||||
pwa.userProfile.ficheUser("0", "ADMIN");
|
|
||||||
};
|
|
||||||
pwa.userManager.userList = async function() {
|
|
||||||
//check ADMIN
|
|
||||||
if(pwa.MODE_ENV) {
|
|
||||||
return [
|
|
||||||
{ uuid: "pcolzy", desc: "pcolzy" },
|
|
||||||
{ uuid: "user2", desc: "User2" }
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
const datauser = await axios.get(pwa.urlbackauth + "/login", {
|
|
||||||
headers: pwa.state.data.headers
|
|
||||||
});
|
|
||||||
const lst = datauser.data.items.map(u => {
|
|
||||||
return { uuid: u.uuid, desc: u.name };
|
|
||||||
});
|
|
||||||
return lst;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pwa.userManager.ficheUser = function(iduser) {
|
|
||||||
// Génere un form de user pre-rempli avec iduser
|
|
||||||
let userForm = {
|
|
||||||
includein: "#espaceMasonry",
|
|
||||||
template: "#mainContentMasonry",
|
|
||||||
idmasonry: "UserForm",
|
|
||||||
destinationclass: "formUser",
|
|
||||||
header: "Information du compte",
|
|
||||||
formclass: "formUserProfil",
|
|
||||||
nombreColonne: 6,
|
|
||||||
commentaire: "",
|
|
||||||
footer: ""
|
|
||||||
};
|
|
||||||
let datauser = pwa.state.data.user;
|
|
||||||
if(iduser) {
|
|
||||||
//On recupere les informations iduser
|
|
||||||
// faire un get (iduser) datauser =
|
|
||||||
userForm.button = [{
|
|
||||||
action: "update",
|
|
||||||
label: "Sauvegarder",
|
|
||||||
msgok: "Votre sauvegarde a bien été effectuée en local",
|
|
||||||
msgko: "Votre sauvegarde n'a pas pu être réalisée ",
|
|
||||||
actionclick: "pwa.userManager.update()",
|
|
||||||
idObject: pwa.state.data.user.uuid,
|
|
||||||
objectSelected: "user"
|
|
||||||
}];
|
|
||||||
} else {
|
|
||||||
userForm.button = [{
|
|
||||||
action: "create",
|
|
||||||
label: "Créer",
|
|
||||||
msgok: "Utilisateur créer en local",
|
|
||||||
msgko: "La création d'un utilisateur n'est pas possible ",
|
|
||||||
actionclick: "pwa.userManager.create()",
|
|
||||||
idObject: "0",
|
|
||||||
objectSelected: "user"
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
userForm.main = pwa.forms.genereForm(pwa.state.app.ref.users, datauser);
|
|
||||||
console.log("button envoye", userForm.button);
|
|
||||||
userForm.buttonup = Mustache.render($("#actionButton")
|
|
||||||
.html(), {
|
|
||||||
button: userForm.button
|
|
||||||
});
|
|
||||||
pwa.masonry.append(userForm, pwa.forms.actionForm);
|
|
||||||
};
|
|
@ -1,20 +0,0 @@
|
|||||||
$( 'a.contenu' )
|
|
||||||
.on( 'click', function () {
|
|
||||||
// $( this )
|
|
||||||
// .preventDefault();
|
|
||||||
// hide all
|
|
||||||
$( '.face.back >div' )
|
|
||||||
.addClass( 'd-none' );
|
|
||||||
const dest = $( this )
|
|
||||||
.attr( 'data-dest' )
|
|
||||||
$( '.face.back >.' + dest )
|
|
||||||
.removeClass( 'd-none' );
|
|
||||||
$( '.flip >.card' )
|
|
||||||
.toggleClass( 'flipped' );
|
|
||||||
} )
|
|
||||||
|
|
||||||
$( '.face.back >> a.retour' )
|
|
||||||
.on( 'click', function () {
|
|
||||||
$( '.flip >.card' )
|
|
||||||
.toggleClass( 'flipped' );
|
|
||||||
} )
|
|
@ -1,28 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var pwa = pwa || {};
|
|
||||||
/*
|
|
||||||
Manage notification
|
|
||||||
|
|
||||||
Get notification after tomenu was load
|
|
||||||
|
|
||||||
from /components/notification
|
|
||||||
____________________
|
|
||||||
|
|
||||||
*/
|
|
||||||
//--##
|
|
||||||
pwa.notification = {};
|
|
||||||
|
|
||||||
pwa.notification.update = () => {
|
|
||||||
console.log( 'get notification update for a user' );
|
|
||||||
axios.get( `https://${pwa.state.data.ctx.urlbackoffice}/notifications/user`, { headers: pwa.state.data.headers } )
|
|
||||||
.then( rep => {
|
|
||||||
console.log( "list des notifs", rep.data.payload.data )
|
|
||||||
rep.data.payload.data.number = rep.data.payload.data.notifs.length;
|
|
||||||
document.getElementById( "topbarmenuright" )
|
|
||||||
.innerHTML = Mustache.render( pwa.state.data.tpl.notiflist, rep.data.payload.data ) + document.getElementById( "topbarmenuright" )
|
|
||||||
.innerHTML;
|
|
||||||
} )
|
|
||||||
.catch( err => {
|
|
||||||
console.log( `Err pwa.notification.update data for user into header ${pwa.state.data.headers}`, err );
|
|
||||||
} );
|
|
||||||
};
|
|
@ -1,66 +0,0 @@
|
|||||||
var pwa = pwa || {};
|
|
||||||
/*
|
|
||||||
Manage user data for current login user
|
|
||||||
|
|
||||||
From user data = pwa.state.data.userlogin
|
|
||||||
|
|
||||||
*/
|
|
||||||
//--##
|
|
||||||
pwa.main = pwa.main || {};
|
|
||||||
pwa.main.tpldata = pwa.main.tpldata || {};
|
|
||||||
pwa.main.tpl = pwa.main.tpl || {};
|
|
||||||
pwa.main.ref = pwa.main.ref || {};
|
|
||||||
//Add template here to make it available after authentification
|
|
||||||
// in pwa.state.data.tpl[name]
|
|
||||||
// pwa.state.data.tpldata[name] in the user's language by adding at the end of value _{pwa.state.data.ctx.lang}.json
|
|
||||||
pwa.main.tpl.usersettingaccount = `static/components/userprofile/usersettingaccount.mustache`;
|
|
||||||
pwa.main.tpldata.topbaruserprofile = `static/components/userprofile/topbaruserprofile`;
|
|
||||||
pwa.main.tpldata.topbaruseractivity = `static/components/userprofile/topbaruseractivity`;
|
|
||||||
pwa.main.tpldata.topbaruserLogout = `static/components/userprofile/topbaruserLogout`;
|
|
||||||
pwa.main.referential.users = 'url ... obeject/users.json';
|
|
||||||
//header check if user is allow to get this
|
|
||||||
|
|
||||||
pwa.userprofile = pwa.userprofile || {};
|
|
||||||
|
|
||||||
pwa.userprofile.settings = ( e ) => {
|
|
||||||
console.groupCollapsed( 'load user settings' );
|
|
||||||
console.log( Object.keys( pwa ) )
|
|
||||||
//data form init from pwa.state or axios.get( data from user )
|
|
||||||
const data = pwa.state.data.userlogin;
|
|
||||||
// add meta data object to create forms
|
|
||||||
const meta = { "users": {} };
|
|
||||||
pwa.state.data.app.referentials.object.users.forEach( f => {
|
|
||||||
// genere tag
|
|
||||||
const test = Object.keys( f )
|
|
||||||
f.tag = " object='users' " + test.reduce( ( ac, k ) => {
|
|
||||||
return ac + k + '="' + f[ k ] + '" ';
|
|
||||||
} );
|
|
||||||
// genere html field care do html after tag if not
|
|
||||||
f.html = pwa.form.tpl[ f.tpl ].html( f )
|
|
||||||
meta.users[ f.idfield ] = f
|
|
||||||
//console.log( f.idfield )
|
|
||||||
//console.log( f )
|
|
||||||
} );
|
|
||||||
pwa.state.data.app.referentials.json.usersetting.submenuitems[ 0 ].meta = meta;
|
|
||||||
console.log( "meta", pwa.state.data.app.referentials.json.usersetting )
|
|
||||||
// tpl in #usersetting data in referentials json usersetting
|
|
||||||
document.getElementById( 'maincontent' )
|
|
||||||
.innerHTML = Mustache.render( document.getElementById( 'setting' )
|
|
||||||
.innerHTML, pwa.state.data.app.referentials.json.usersetting );
|
|
||||||
// load each content of tab then init form with data merged with tab info
|
|
||||||
pwa.state.data.app.referentials.json.usersetting.submenuitems.forEach( tab => {
|
|
||||||
document.getElementById( tab.id )
|
|
||||||
.innerHTML = Mustache.render( document.getElementById( `${tab.id}tpl` )
|
|
||||||
.innerHTML, tab );
|
|
||||||
console.log( tab.id, tab )
|
|
||||||
// Convert each div with formfieldtype to a form field set with value if exist and listen button to run callback
|
|
||||||
pwa.form.init( tab.id, { ...tab, ...data }, pwa.userprofile.save )
|
|
||||||
} )
|
|
||||||
console.groupEnd();
|
|
||||||
}
|
|
||||||
pwa.userprofile.save = ( data ) => {
|
|
||||||
console.log( "data to save", data )
|
|
||||||
}
|
|
||||||
pwa.userprofile.activities = () => {
|
|
||||||
console.group( 'load user activity' );
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
(function($) {
|
|
||||||
$.fn.articlesaffiche = function(contenu){
|
|
||||||
$(this).html(Mustache.to_html($('#tpl_articlelist').html(),contenu)).promise().done(function(){
|
|
||||||
$('figure.article-item').on ('click',function(e){
|
|
||||||
e.preventDefault();
|
|
||||||
var data = {};
|
|
||||||
console.log($(this).find('button').attr('href').replace('.html','.json'));
|
|
||||||
$.getJSON($(this).find('button').attr('href').replace('.html','.json') , function( contenuarticle){
|
|
||||||
$('#articlecontent').html(Mustache.to_html($('#tpl_article').html(),contenuarticle))
|
|
||||||
.promise().done(function(){
|
|
||||||
$('#maincontent').css('display','none');
|
|
||||||
$('#articlecontent').css('display','block');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
$.fn.articlemaj = function(contenu){
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}(jQuery));
|
|
@ -1,212 +0,0 @@
|
|||||||
(function($) {
|
|
||||||
const ROOT_TAAPP_URL = 'http://taapp.ndda.fr:3010';
|
|
||||||
const ROOT_URL = 'http://maildigit.ndda.fr'
|
|
||||||
|
|
||||||
$.fn.initlogin = function(URLCENTRALE) {
|
|
||||||
function register(blocauth) {
|
|
||||||
blocauth.html($('#register').html()).promise().done(function(){
|
|
||||||
|
|
||||||
$('a.oubliemdp').on('click', function(e){e.preventDefault(); forget(blocauth)});
|
|
||||||
$('a.login').on('click', function(e){e.preventDefault(); login(blocauth)});
|
|
||||||
$('.register-button').on('click',function(e){
|
|
||||||
e.preventDefault();
|
|
||||||
var etat = "";
|
|
||||||
var senddata = {'action':'register'};
|
|
||||||
var reg = new RegExp('^[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*@[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*[\.]{1}[a-z]{2,6}$', 'i');
|
|
||||||
if (! reg.test($('#reg_email').val()) ) {
|
|
||||||
etat += $('#reg_email').data('msgko') +"<br>";
|
|
||||||
}else{
|
|
||||||
senddata['email'] = $('#reg_email').val();
|
|
||||||
};
|
|
||||||
if ($('#reg_password').val() != $('#reg_password_confirm') && $('#reg_password').val().length < 5 ) {
|
|
||||||
etat+=$('#reg_password').data('msgko') + "<br>";
|
|
||||||
}else{
|
|
||||||
senddata['psw'] = $('#reg_password').val();
|
|
||||||
};
|
|
||||||
if ( $('#reg_prenom').val().length < 3 ) {
|
|
||||||
etat += $('#reg_prenom').data('msgko') +"<br>";
|
|
||||||
}else{
|
|
||||||
senddata['prenom'] = $('#reg_prenom').val();
|
|
||||||
};
|
|
||||||
if ( $('#reg_nom').val().length < 3 ) {
|
|
||||||
etat += $('#reg_nom').data('msgko') +"<br>";
|
|
||||||
}else{
|
|
||||||
senddata['nom'] = $('#reg_nom').val();
|
|
||||||
};
|
|
||||||
senddata['genre'] = $("input[name='reg_gender']:checked").attr('id');
|
|
||||||
if (!($('#reg_agree').is(':checked'))) {
|
|
||||||
etat+=$('#reg_agree').data('msgko')+"<br>";
|
|
||||||
};
|
|
||||||
|
|
||||||
if (etat != "") {
|
|
||||||
$('#register-form p.msgreg').html(etat);
|
|
||||||
}else{
|
|
||||||
$.ajax({ url: "/log", type: "POST", data:senddata, cache: false,
|
|
||||||
success: function(e) {
|
|
||||||
e=JSON.parse(e);
|
|
||||||
console.log(e);
|
|
||||||
if (e.etat == "ok") {
|
|
||||||
login(blocauth);
|
|
||||||
}else{
|
|
||||||
console.log(e.etat);
|
|
||||||
$('#register-form p.msgreg').html(e.etat);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
error: function(e) {
|
|
||||||
console.log(e);
|
|
||||||
$('#register-form p.msgreg').html($('#register-form').data('msgko'));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function directorySelector(blocauth) {
|
|
||||||
blocauth.html($('.choose-directory').html()).promise().done(function () {
|
|
||||||
$('.choose-directory__submit').on('click', function(e) {
|
|
||||||
const selectedDirectory = $('.directory-selector option:selected').val();
|
|
||||||
$.ajax({
|
|
||||||
url: `${ROOT_TAAPP_URL}/nas/need-data/${selectedDirectory}`,
|
|
||||||
type: "POST",
|
|
||||||
beforeSend: function(request) {
|
|
||||||
request.setRequestHeader('x-auth', localStorage.getItem('token'))
|
|
||||||
},
|
|
||||||
data: { login: 'max' },
|
|
||||||
cache: false,
|
|
||||||
success: function (data, textStatus, request) {
|
|
||||||
console.log('URL Node-File-Manager here!');
|
|
||||||
console.log('data: ', data);
|
|
||||||
displayIframe(blocauth, data.url);
|
|
||||||
},
|
|
||||||
error: function (response) {
|
|
||||||
console.log('err: ', response);
|
|
||||||
},
|
|
||||||
always: function (response) {
|
|
||||||
console.log('Hello!')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayIframe(blocauth, url) {
|
|
||||||
blocauth.html(`<iframe height="600" width="800" src=${url}> </iframe>`).promise().done(function() {
|
|
||||||
console.log('displayIframe');
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function login(blocauth) {
|
|
||||||
blocauth.html($('#proclogin').html()).promise().done(function() {
|
|
||||||
$('a.oubliemdp').on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
forget(blocauth);
|
|
||||||
});
|
|
||||||
$('a.register').on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
register(blocauth);
|
|
||||||
});
|
|
||||||
$('button.login-button').on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const ROOT_URL = 'http://maildigit.ndda.fr'
|
|
||||||
const tribeid = $(this).attr('data-tribeid');
|
|
||||||
const $form = $('#login-form');
|
|
||||||
const inputs = $form.serializeArray();
|
|
||||||
let loginData = {};
|
|
||||||
loginData.tribeid = tribeid;
|
|
||||||
|
|
||||||
inputs.forEach(input => {
|
|
||||||
if (input.value.trim().length > 0) {
|
|
||||||
loginData[input.name] = input.value.trim();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Si les champs sont bien remplis on envoie la requête
|
|
||||||
if (Object.keys(loginData).length === 3) {
|
|
||||||
$.ajax({
|
|
||||||
url: `${ROOT_URL}/login`,
|
|
||||||
type: "POST",
|
|
||||||
data: loginData,
|
|
||||||
cache: false,
|
|
||||||
success: function (data, textStatus, request) {
|
|
||||||
const token = request.getResponseHeader('x-auth');
|
|
||||||
console.log('token: ', token);
|
|
||||||
localStorage.setItem('token', token);
|
|
||||||
directorySelector(blocauth);
|
|
||||||
},
|
|
||||||
error: function (response) {
|
|
||||||
console.log('err: ', response.responseJSON.error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function forget(blocauth){
|
|
||||||
blocauth.html($('#forgotpw').html()).promise().done(function(){
|
|
||||||
$('a.login').on('click', function(e){e.preventDefault(); login(blocauth)});
|
|
||||||
$('a.register').on('click', function(e){e.preventDefault(); register(blocauth)});
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
login($(this));
|
|
||||||
};
|
|
||||||
|
|
||||||
$.fn.login = function(){
|
|
||||||
var form = $(this);
|
|
||||||
var lastdatasend = lastdatasend || "";
|
|
||||||
form.find('.btnsendform').on('click',function(){
|
|
||||||
var senddata = {};
|
|
||||||
var etat = "";
|
|
||||||
var msgok = form.data('msgok');
|
|
||||||
var msgko = form.data('msgko');
|
|
||||||
|
|
||||||
senddata.fichier = form.data('fichier');
|
|
||||||
|
|
||||||
form.find('input').each(function(i){
|
|
||||||
senddata[$(this).data('champs')]=$(this).val();
|
|
||||||
var reg = "{{"+$(this).data('champs')+"}}";
|
|
||||||
msgok = msgok.replace(reg,$(this).val());
|
|
||||||
msgko = msgko.replace(reg,$(this).val());
|
|
||||||
|
|
||||||
if ( $(this).data('check') == 'email') {
|
|
||||||
var reg = new RegExp('^[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*@[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*[\.]{1}[a-z]{2,6}$', 'i');
|
|
||||||
if (! reg.test($(this).val()) ) { etat += $(this).data('msgerreur') +"<br>";};
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( $(this).data('check') == 'phone') {
|
|
||||||
var phone = $(this).val().replace(/[^0-9]/g, '');
|
|
||||||
if (phone.length != 10) {etat += $(this).data('msgerreur') +"<br>";};
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
||||||
var diff = false;
|
|
||||||
if (lastdatasend == "") {diff=true;}
|
|
||||||
$.each(lastdatasend,function(k,v){
|
|
||||||
if (k in senddata && v!= senddata[k] ) {diff = true;}
|
|
||||||
});
|
|
||||||
if (!(diff)) {
|
|
||||||
etat = "Action déjà effectuée";
|
|
||||||
}
|
|
||||||
if (etat != "") {
|
|
||||||
form.find("p.msgform").html(etat);
|
|
||||||
}else{
|
|
||||||
$.ajax({ url: "http://maildigit.ndda.fr:3000/msg", type: "POST", data:senddata, cache: false,
|
|
||||||
success: function(e) {
|
|
||||||
//console.log(e);
|
|
||||||
lastdatasend = senddata;
|
|
||||||
console.log(form.data('msgok'));
|
|
||||||
form.find("p.msgform").html(msgok);
|
|
||||||
},
|
|
||||||
error: function(e) {
|
|
||||||
console.log(e);
|
|
||||||
form.find("p.msgform").html(msgko);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$.fn.functionname = function(contenu){
|
|
||||||
};
|
|
||||||
}(jQuery));
|
|
File diff suppressed because one or more lines are too long
@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
This module have to be independant of any external package
|
|
||||||
it is shared between back and front and is usefull
|
|
||||||
to apply common check in front before sending it in back
|
|
||||||
can be include in project with
|
|
||||||
<script src="https://apiback.maildigit.fr/js/checkdata.js"></script>
|
|
||||||
or with const checkdata = require('../public/js/checkdata.js')
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// --##
|
|
||||||
|
|
||||||
const checkdata = {};
|
|
||||||
// each checkdata.test. return true or false
|
|
||||||
checkdata.test = {};
|
|
||||||
|
|
||||||
checkdata.test.emailadress = ( ctx, email ) => {
|
|
||||||
const regExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
||||||
return regExp.test( email );
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* @emaillist = "email1,email2, email3"
|
|
||||||
* it check if each eamil separate by , are correct
|
|
||||||
*/
|
|
||||||
checkdata.test.emailadresslist = ( ctx, emaillist ) => {
|
|
||||||
//console.log(emaillist.split(','))
|
|
||||||
if( emaillist.length > 0 ) {
|
|
||||||
const emails = emaillist.split( ',' );
|
|
||||||
for( var i in emails ) {
|
|
||||||
//console.log(emails[i])
|
|
||||||
if( !checkdata.test.emailadress( "", emails[ i ].trim() ) ) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
checkdata.test.password = ( ctx, pwd ) => {
|
|
||||||
const regExp = new RegExp(
|
|
||||||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&.])[A-Za-z\d$@$!%*?&.{}:|\s]{8,}/
|
|
||||||
);
|
|
||||||
return regExp.test( pwd );
|
|
||||||
};
|
|
||||||
checkdata.test.required = ( ctx, val ) =>
|
|
||||||
( val != null && val != 'undefined' && val.length > 0 ) || ( !!val && val.constructor === Array && val.length > 0 ) || ( !!val && val.constructor === Object && Object.keys( val )
|
|
||||||
.length > 0 );
|
|
||||||
|
|
||||||
checkdata.test.isNumber = ( ctx, n ) => typeof n === 'number';
|
|
||||||
checkdata.test.isInt = ( ctx, n ) => n != '' && !isNaN( n ) && Math.round( n ) == n;
|
|
||||||
checkdata.test.isFloat = ( ctx, n ) => n != '' && !isNaN( n ) && Math.round( n ) != n;
|
|
||||||
checkdata.test.unique = ( ctx, val ) => {
|
|
||||||
if( ctx.list[ ctx.currentfield ] ) {
|
|
||||||
return !ctx.list[ ctx.currentfield ].includes( val );
|
|
||||||
} else {
|
|
||||||
console.log( 'ERR no list for field:' + ctx.currentfield );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
checkdata.test.isDateDay = ( ctx, dateDay ) => true;
|
|
||||||
/* checkdata.test.filterInvalidInArray = (array, validate) =>
|
|
||||||
array ? array.filter(el => !validate(el)) : true;
|
|
||||||
// return true when every elements is valid
|
|
||||||
*/
|
|
||||||
|
|
||||||
checkdata.test.postalCode = ( ctx, postalCode ) => {
|
|
||||||
if( postalCode.length == 0 ) return true;
|
|
||||||
const regExp = new RegExp( /(^\d{5}$)|(^\d{5}-\d{4}$)/ );
|
|
||||||
return regExp.test( postalCode );
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* PHONE
|
|
||||||
*/
|
|
||||||
checkdata.test.phoneNumber = ( ctx, phoneNumber ) => {
|
|
||||||
if( phoneNumber.length == 0 ) return true;
|
|
||||||
phoneNumber = phoneNumber.trim()
|
|
||||||
.replace( /[- .]/g, '' )
|
|
||||||
//french number
|
|
||||||
const regExpfr = new RegExp( /^0[1-9][0-9]{9}$/ );
|
|
||||||
const regExpInternational = new RegExp( /^\+*(\d{3})*[0-9,\-]{8,}/ );
|
|
||||||
return regExpfr.test( phoneNumber ) || regExpInternational.test( phoneNumber );
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* @phonelist = "phone1,phone2,phone3"
|
|
||||||
* it check if each phone separate by , are correct
|
|
||||||
*/
|
|
||||||
checkdata.test.phoneNumberlist = ( ctx, phonelist ) => {
|
|
||||||
//console.log(emaillist.split(','))
|
|
||||||
if( phonelist.length > 0 ) {
|
|
||||||
const phones = phonelist.split( ',' );
|
|
||||||
for( var i in phones ) {
|
|
||||||
//console.log(emails[i])
|
|
||||||
if( !checkdata.test.phoneNumber( "", phones[ i ].trim() ) ) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// checkdata.normalize take a correct data then reformat it to harmonise it
|
|
||||||
checkdata.normalize = {};
|
|
||||||
checkdata.normalize.phoneNumber = ( ctx, phone ) => {
|
|
||||||
phone = phone.trim()
|
|
||||||
.replace( /[- .]/g, '' );
|
|
||||||
if( checkdata.test.phoneNumber( '', phone ) && phone.length == 10 && phone[ 0 ] == "0" ) {
|
|
||||||
phone = '+33 ' + phone.substring( 1 );
|
|
||||||
}
|
|
||||||
return phone;
|
|
||||||
}
|
|
||||||
checkdata.normalize.upperCase = ( ctx, txt ) => txt.toUpperCase();
|
|
||||||
checkdata.normalize.lowerCase = ( ctx, txt ) => txt.toLowerCase();
|
|
||||||
// fixe 10 position et complete par des 0 devant
|
|
||||||
checkdata.normalize.zfill10 = ( ctx, num ) => {
|
|
||||||
let s = num + '';
|
|
||||||
while( s.length < 10 ) s = '0' + s;
|
|
||||||
return s;
|
|
||||||
};
|
|
||||||
/*let tt = "+33 1 02.03 04 05";
|
|
||||||
console.log(checkdata.test.phoneNumber('', tt))
|
|
||||||
console.log(checkdata.normalize.phoneNumber('', tt))
|
|
||||||
*/
|
|
||||||
checkdata.evaluate = ( contexte, referential, data ) => {
|
|
||||||
/*
|
|
||||||
* contexte object {} with full info for evaluation
|
|
||||||
* file referential path to get object to apply
|
|
||||||
* data related to object
|
|
||||||
- return {validefor =[keyword of error] if empty no error,
|
|
||||||
clean data eventually reformated
|
|
||||||
updateDatabase}
|
|
||||||
*/
|
|
||||||
console.log( 'contexte', contexte );
|
|
||||||
console.log( 'referentiel', referential );
|
|
||||||
console.log( 'data', data );
|
|
||||||
const invalidefor = [];
|
|
||||||
const objectdef = {};
|
|
||||||
const listfield = referential.map( ch => {
|
|
||||||
objectdef[ ch.idfield ] = ch;
|
|
||||||
return ch.idfield;
|
|
||||||
} );
|
|
||||||
|
|
||||||
Object.keys( data )
|
|
||||||
.forEach( field => {
|
|
||||||
if( !listfield.includes( field ) ) {
|
|
||||||
// some data can be inside an object with no control at all
|
|
||||||
// they are used for process only
|
|
||||||
// i leave it in case it will become a non sens
|
|
||||||
// invalidefor.push('ERRFIELD unknown of referentials ' + field);
|
|
||||||
} else {
|
|
||||||
if( objectdef[ field ].check ) {
|
|
||||||
// check data with rule list in check
|
|
||||||
objectdef[ field ].check.forEach( ctrl => {
|
|
||||||
console.log( 'ctrl', ctrl );
|
|
||||||
contexte.currentfield = field;
|
|
||||||
if( !checkdata.test[ ctrl ] ) {
|
|
||||||
invalidefor.push( 'ERR check function does not exist :' + ctrl + '___' + field )
|
|
||||||
} else {
|
|
||||||
if( !checkdata.test[ ctrl ]( contexte, data[ field ] ) )
|
|
||||||
invalidefor.push( 'ERR' + ctrl + '___' + field );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( objectdef[ field ].nouserupdate ) {
|
|
||||||
// check if user can modify this information
|
|
||||||
console.log(
|
|
||||||
'evaluation :' + field + ' -- ' + objectdef[ field ].nouserupdate,
|
|
||||||
eval( objectdef[ field ].nouserupdate )
|
|
||||||
);
|
|
||||||
const evalright = eval( objectdef[ field ].nouserupdate );
|
|
||||||
objectdef[ field ].nouserupdate = evalright;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
console.log( {
|
|
||||||
invalidefor,
|
|
||||||
data
|
|
||||||
} );
|
|
||||||
return {
|
|
||||||
invalidefor,
|
|
||||||
data
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if( typeof module !== 'undefined' ) module.exports = checkdata;
|
|
@ -1,104 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
var edit = edit || {};
|
|
||||||
|
|
||||||
edit.init = function() {
|
|
||||||
var DOWNLOAD_NAME = 'index.maildigit';
|
|
||||||
// Apply contenteditable attributes on text elements
|
|
||||||
$('p,h1,h2,h3,h4,h5,h6,a,li,ul').attr('contenteditable', 'true');
|
|
||||||
|
|
||||||
// Add Save/Load menu
|
|
||||||
$('body').prepend(
|
|
||||||
"<div class='editor' style='position:fixed; top: 250px; z-index: 10000;'><button class='btn btn-default editersauve'>Sauvegarder les modifications</button> \
|
|
||||||
<!--<button class='btn btn-default editercharge'>Charger des modifications</button>--> \
|
|
||||||
<input style='display:none;' type='file' class='chargehisto'/></div>"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add button to upload file below each image
|
|
||||||
$('img').each(function(i) {
|
|
||||||
//console.log($(this).attr('src'));
|
|
||||||
$(this).attr('src', $(this).attr('src'));
|
|
||||||
$(this).attr('H', $(this).css('height'));
|
|
||||||
$(this).attr('L', $(this).attr('width'));
|
|
||||||
$(this)
|
|
||||||
.closest('div')
|
|
||||||
.append("<input type='file' class='imageLoader'/>");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Replace old image with the new uploaded image in base64
|
|
||||||
$('.imageLoader').on('change', function(e) {
|
|
||||||
var imgEl = $(this)
|
|
||||||
.closest('div')
|
|
||||||
.find('img');
|
|
||||||
// Get img tree like /images/equipe or /images/
|
|
||||||
var tree = imgEl[0].src.split(window.location.origin)[1];
|
|
||||||
// This is needed because if we change two times an image at the same place
|
|
||||||
// first time we replaced it the src will be in base64
|
|
||||||
// then we need to find the computed src which is stored in dataset.tree
|
|
||||||
if (!tree) tree = imgEl[0].dataset.tree;
|
|
||||||
tree = tree.substr(0, tree.lastIndexOf('/') + 1);
|
|
||||||
if ($(this)[0].files.length === 1) {
|
|
||||||
var fileName = $(this)[0].files[0].name;
|
|
||||||
fileName = fileName.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); // remove accents
|
|
||||||
fileName = fileName.replace(/\s+/g, '_'); // remove spaces
|
|
||||||
var ext = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
|
|
||||||
// Add fileName and tree in data attributes
|
|
||||||
imgEl[0].dataset.fileName = fileName;
|
|
||||||
imgEl[0].dataset.tree = tree + fileName;
|
|
||||||
console.log(imgEl[0].dataset.tree);
|
|
||||||
// console.log(imgEl);
|
|
||||||
|
|
||||||
if (ext === 'gif' || ext === 'png' || ext === 'jpeg' || ext === 'jpg') {
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.onload = function(e) {
|
|
||||||
imgEl.attr('src', e.target.result);
|
|
||||||
};
|
|
||||||
reader.readAsDataURL($(this)[0].files[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('button.editercharge').on('click', function() {
|
|
||||||
$(this)
|
|
||||||
.closest('div')
|
|
||||||
.find('.chargehisto')
|
|
||||||
.trigger('click');
|
|
||||||
});
|
|
||||||
$('.chargehisto').on('change', function(e) {
|
|
||||||
if ($(this)[0].files !== undefined) {
|
|
||||||
//charge un dom avec les info et recupere tous les editables et les changes dans le dom d'origine pour mettre à jour
|
|
||||||
// a developper en prod recupere fichier
|
|
||||||
var ext = $(this)[0]
|
|
||||||
.files[0]['name'].substring(
|
|
||||||
$(this)[0].files[0]['name'].lastIndexOf('.') + 1
|
|
||||||
)
|
|
||||||
.toLowerCase();
|
|
||||||
if ($(this)[0].files && $(this)[0].files[0] && ext == 'maildigit')
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.onload = function(e) {
|
|
||||||
$('body').html(e.target.result);
|
|
||||||
};
|
|
||||||
reader.readAsText($(this)[0].files[0]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('button.editersauve').on('click', function() {
|
|
||||||
//nettoie editeur
|
|
||||||
//$('p,h1,h2,h3,h4,a').removeAttr('contenteditable');
|
|
||||||
//$('button.editersauve, p.editinfo').remove();
|
|
||||||
//recupere body
|
|
||||||
var a = window.document.createElement('a');
|
|
||||||
a.href = window.URL.createObjectURL(
|
|
||||||
new Blob([$('body').html()], {
|
|
||||||
type: 'text/csv;charset=utf-8;'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
a.download = DOWNLOAD_NAME;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
if (window.location.href.indexOf('#editer_') > -1) edit.init();
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load Diff
@ -1,53 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
var app = app || {};
|
|
||||||
var URLCENTRALE = "http://maildigit.ndda.fr";
|
|
||||||
if (location.href.indexOf('https:')>-1){
|
|
||||||
URLCENTRALE='https://maildigit.ndda.fr';
|
|
||||||
}
|
|
||||||
|
|
||||||
app.test = function(){
|
|
||||||
$('a.test').on('click',function(){
|
|
||||||
console.log('lance test pdf');
|
|
||||||
$.ajax({
|
|
||||||
url: "/pdf",
|
|
||||||
type: "POST",
|
|
||||||
data: {
|
|
||||||
tpl: 'tete',
|
|
||||||
info : 'tyty',
|
|
||||||
},
|
|
||||||
cache: false,
|
|
||||||
success: function(res) {
|
|
||||||
console.log(res.success)
|
|
||||||
},
|
|
||||||
error: function(res) {
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$('.btncode').on('click',function(){
|
|
||||||
|
|
||||||
var clair = $('.clair').val();
|
|
||||||
var code = $('.code').val();
|
|
||||||
if (clair !='' && code!='') {
|
|
||||||
$('p.msgcode').html("Vider un champs pour générer le codage")
|
|
||||||
}
|
|
||||||
if (code=='') {
|
|
||||||
$('.code').val(window.btoa(unescape(encodeURIComponent( clair ))));
|
|
||||||
};
|
|
||||||
if (clair ==''){
|
|
||||||
$('.clair').val(decodeURIComponent(escape(window.atob( code ))));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
//permet d'afficher du code à copier coller du site
|
|
||||||
$( 'code' ).each(function(){$(this).find('pre').text( $(this).html() )});
|
|
||||||
//app.init()
|
|
||||||
md.tg("&s=s0");
|
|
||||||
$('form.formmaildigit').gestionform(URLCENTRALE);
|
|
||||||
$('.loginprocess').initlogin();
|
|
||||||
//app.sendform();
|
|
||||||
// app.test();
|
|
||||||
//pour tester
|
|
||||||
});
|
|
File diff suppressed because one or more lines are too long
@ -1,24 +0,0 @@
|
|||||||
// Avoid `console` errors in browsers that lack a console.
|
|
||||||
(function() {
|
|
||||||
var method;
|
|
||||||
var noop = function () {};
|
|
||||||
var methods = [
|
|
||||||
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
|
|
||||||
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
|
|
||||||
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
|
|
||||||
'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
|
|
||||||
];
|
|
||||||
var length = methods.length;
|
|
||||||
var console = (window.console = window.console || {});
|
|
||||||
|
|
||||||
while (length--) {
|
|
||||||
method = methods[length];
|
|
||||||
|
|
||||||
// Only stub undefined methods.
|
|
||||||
if (!console[method]) {
|
|
||||||
console[method] = noop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
|
|
||||||
// Place any jQuery/helper plugins in here.
|
|
@ -1,50 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
var md = md || {};
|
|
||||||
var $ = $ || jQuery;
|
|
||||||
md.u = 'http://maildigit.ndda.fr';
|
|
||||||
if (location.href.indexOf('https:')>-1){
|
|
||||||
md.u='https://maildigit.ndda.fr';
|
|
||||||
}
|
|
||||||
//survey.js doit être installé sur la page qui heberge les declencheurs
|
|
||||||
md.questio = function(quest,iduser){
|
|
||||||
|
|
||||||
function sendDataToServer(survey) {
|
|
||||||
console.log(survey)
|
|
||||||
$.ajax({url:md.u+'/questiodone',type: "POST", data:{'questio':survey.data,'quest':quest,'iduser':iduser}, cache: false,
|
|
||||||
success:function(e){
|
|
||||||
console.log(e.msg)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$.ajax({ url: md.u+"/questiotodo", type: "GET",
|
|
||||||
data:{quest:quest,iduser:iduser}, cache: false,
|
|
||||||
success: function(e) {
|
|
||||||
console.log(e);
|
|
||||||
if (e.surveyjson) {
|
|
||||||
console.log('survey')
|
|
||||||
console.log(e.surveyjson)
|
|
||||||
var survey = new Survey.Model(e.surveyjson);
|
|
||||||
$("#modalsurvey .survey").Survey({
|
|
||||||
model:survey,
|
|
||||||
onComplete:sendDataToServer
|
|
||||||
});
|
|
||||||
$('#modalsurvey').modal('show');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function(e) {
|
|
||||||
console.log(e);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$(document).ready(function(){
|
|
||||||
$('[data-tgsurvey][data-tgid]').on('click',function(){
|
|
||||||
console.log($(this).data('tgsurvey')+"-"+$(this).data('tgid'))
|
|
||||||
//pour tester enlever cette ligne pour la prod
|
|
||||||
var idt = $(this).data('tgid')
|
|
||||||
idt = $("input.testid").val()
|
|
||||||
md.questio($(this).data('tgsurvey'),idt);
|
|
||||||
});
|
|
||||||
$('[data-tgsurvey][data-tgid]').on('load',function(){
|
|
||||||
md.questio($(this).data('tgsurvey'),$(this).data('tgid'));
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,164 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
var tg = tg || {};
|
|
||||||
|
|
||||||
var pwa = pwa || {};
|
|
||||||
|
|
||||||
$.fn.isInViewport = function() {
|
|
||||||
var elementTop = $(this)
|
|
||||||
.offset()
|
|
||||||
.top;
|
|
||||||
var elementBottom = elementTop + $(this)
|
|
||||||
.outerHeight();
|
|
||||||
var viewportTop = $(window)
|
|
||||||
.scrollTop();
|
|
||||||
var viewportBottom = viewportTop + $(window)
|
|
||||||
.height();
|
|
||||||
return elementBottom > viewportTop && elementTop < viewportBottom;
|
|
||||||
};
|
|
||||||
// A cutomiser par client
|
|
||||||
tg.plan = {
|
|
||||||
action: [
|
|
||||||
{ selector: '*[data-tgmd]', action: 'click' }
|
|
||||||
],
|
|
||||||
showed: [{ selector: '*[data-tgmd="partietext"]' }]
|
|
||||||
};
|
|
||||||
tg.content = [];
|
|
||||||
if(localStorage.getItem('tgcontent')) tg.content = JSON.parse(localStorage.getItem('tgcontent'));
|
|
||||||
tg.listen = function() {
|
|
||||||
//$('.data-tgmd').onclick(function(){console.log("test")})
|
|
||||||
tg.plan.action.forEach(e => {
|
|
||||||
$(e.selector)
|
|
||||||
.on(e.action, function() {
|
|
||||||
//envoie un poste avec tag et onclick
|
|
||||||
console.log('declenche ' + e.action + ' sur ' + e.selector + 'avec valeur de ' + $(e.selector)
|
|
||||||
.val());
|
|
||||||
tgcontent.push([e.action, e.selector, $(e.selector)
|
|
||||||
.val(), Date.now()
|
|
||||||
])
|
|
||||||
})
|
|
||||||
});
|
|
||||||
$(window)
|
|
||||||
.on('resize scroll', function() {
|
|
||||||
tg.plan.showed.foreach((e, i) => {
|
|
||||||
if($(e.selector)
|
|
||||||
.isInViewport) {
|
|
||||||
//marque l'affichage
|
|
||||||
tg.plan.showed[i].timeon = Date.now();
|
|
||||||
} else {
|
|
||||||
if(tg.plan.showed[i].timeon) {
|
|
||||||
//affichage qui passe of
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
$(window)
|
|
||||||
.unload(function() {
|
|
||||||
// on quitte la fenetre
|
|
||||||
tg.action({ tgcontent: tg.content })
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
tg.getUrlParams = function(url) {
|
|
||||||
var params = {};
|
|
||||||
(url + '?')
|
|
||||||
.split('?')[1].split('&')
|
|
||||||
.forEach(function(pair) {
|
|
||||||
pair = (pair + '=')
|
|
||||||
.split('=')
|
|
||||||
.map(decodeURIComponent);
|
|
||||||
if(pair[0].length) {
|
|
||||||
params[pair[0]] = pair[1];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return params;
|
|
||||||
};
|
|
||||||
|
|
||||||
tg.setCookie = (key, value) => {var expires = new Date();
|
|
||||||
|
|
||||||
expires.setTime(expires.getTime() + 365 * 24 * 60 * 60 * 1000);
|
|
||||||
document.cookie = key + '=' + value + ';expires=' + expires.toUTCString();
|
|
||||||
};
|
|
||||||
|
|
||||||
tg.getCookie = (key) => {
|
|
||||||
var keyValue = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)');
|
|
||||||
return keyValue ? keyValue[2] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
tg.setlocalS = (info) => {
|
|
||||||
let r = localStorage.getItem('r');
|
|
||||||
console.log(r);
|
|
||||||
if(r == undefined || !r) {
|
|
||||||
r = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
||||||
var r = (Math.random() * 16) | 0,
|
|
||||||
v = c == 'x' ? r : (r & 0x3) | 0x8;
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
localStorage.setItem('r', r);
|
|
||||||
tg.setCookie(r, r);
|
|
||||||
}
|
|
||||||
if(info.id) {
|
|
||||||
localStorage.setItem('id', info.id);
|
|
||||||
}
|
|
||||||
console.log(info)
|
|
||||||
tg.action(info)
|
|
||||||
}
|
|
||||||
tg.agreement = () => {
|
|
||||||
if(tg.getCookie('accept') == undefined) {
|
|
||||||
$('.cookiewarning')
|
|
||||||
.css('display', 'inline-block');
|
|
||||||
}
|
|
||||||
$('button.acceptcookie')
|
|
||||||
.on('click', function() {
|
|
||||||
let accord = "ok"
|
|
||||||
if($(this)
|
|
||||||
.attr('data-accord')) {
|
|
||||||
accord = $(this)
|
|
||||||
.attr('data-accord')
|
|
||||||
}
|
|
||||||
tg.setCookie('accept', accord);
|
|
||||||
$('.cookiewarning')
|
|
||||||
.css('display', 'none');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tg.action = (post) => {
|
|
||||||
post.tit = encodeURIComponent(document.title);
|
|
||||||
if(localStorage.getItem('r')) post.r = localStorage.getItem('r');
|
|
||||||
if(localStorage.getItem('id')) post.id = localStorage.getItem('id');
|
|
||||||
if(localStorage.getItem('accept')) post.accept = localStorage.getItem('accept');
|
|
||||||
console.log('post content', post);
|
|
||||||
let xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {};
|
|
||||||
xhttp.open(
|
|
||||||
'POST',
|
|
||||||
`${pwa.urlapixtribe}/tags/${pwa.tribeid}`,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|
||||||
xhttp.onload = function(post) {
|
|
||||||
if(xhttp.status != 200) {
|
|
||||||
//pb d'acces on stock tag:
|
|
||||||
localStorage.setItem('tgcontent', JSON.stringify(tg.content));
|
|
||||||
} else {
|
|
||||||
//envoyer on vide tgcontent
|
|
||||||
localStorage.setItem('tgcontent', '[]')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhttp.send(JSON.stringify(post));
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document)
|
|
||||||
.ready(function() {
|
|
||||||
if(!pwa.tribeid) {
|
|
||||||
pwa.urlapixtribe = 'https://api' + $('#mxp')
|
|
||||||
.attr('data-destin') + '.maildigit.fr';
|
|
||||||
pwa.tribeid = $('#mxp')
|
|
||||||
.attr('data-tribeid');
|
|
||||||
}
|
|
||||||
console.log('pwa', pwa)
|
|
||||||
console.log('url', window.location.href)
|
|
||||||
console.log('info', tg.getUrlParams(window.location.href))
|
|
||||||
tg.setlocalS(tg.getUrlParams(window.location.href));
|
|
||||||
tg.agreement();
|
|
||||||
});
|
|
@ -1,67 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
console.log('md.u:' + md.u);
|
|
||||||
md.cookiepol = function() {
|
|
||||||
function setCookie(key, value) {
|
|
||||||
var expires = new Date();
|
|
||||||
expires.setTime(expires.getTime() + 365 * 24 * 60 * 60 * 1000);
|
|
||||||
document.cookie = key + '=' + value + ';expires=' + expires.toUTCString();
|
|
||||||
}
|
|
||||||
function getCookie(key) {
|
|
||||||
var keyValue = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)');
|
|
||||||
return keyValue ? keyValue[2] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getCookie('accept') == undefined) {
|
|
||||||
$('.cookiewarning').css('display', 'inline-block');
|
|
||||||
}
|
|
||||||
$('button.acceptcookie').on('click', function() {
|
|
||||||
let accord ="ok"
|
|
||||||
if ($(this).attr('data-accord')){
|
|
||||||
accord = $(this).attr('data-accord')
|
|
||||||
}
|
|
||||||
setCookie('accept', accord);
|
|
||||||
$('.cookiewarning').css('display', 'none');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
md.tg = function(v) {
|
|
||||||
md.cookiepol();
|
|
||||||
var l = window.location.href.split('#coo');
|
|
||||||
console.log(l);
|
|
||||||
if (l.length == 2) {
|
|
||||||
v += l[1];
|
|
||||||
}
|
|
||||||
var r = localStorage.getItem('r');
|
|
||||||
console.log(r);
|
|
||||||
if (r == undefined) {
|
|
||||||
r = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
||||||
var r = (Math.random() * 16) | 0,
|
|
||||||
v = c == 'x' ? r : (r & 0x3) | 0x8;
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
localStorage.setItem('r', r);
|
|
||||||
}
|
|
||||||
//$.get(md.u+'/?a=tg&r='+r+v,function(data,status){});
|
|
||||||
console.log(
|
|
||||||
md.u + '/?a=tg&r=' + r + '&tit=' + encodeURIComponent(document.title) + v
|
|
||||||
);
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {};
|
|
||||||
xhttp.open(
|
|
||||||
'GET',
|
|
||||||
md.u + '/?a=tg&r=' + r + '&tit=' + encodeURIComponent(document.title) + v,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
xhttp.send();
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
md.cookiepol();
|
|
||||||
$('[data-tgmd]').on('click', function() {
|
|
||||||
md.tg('&s=' + $(this).data('tgmd'));
|
|
||||||
});
|
|
||||||
$('[data-tgmd]').on('load', function() {
|
|
||||||
md.tg('&s=' + $(this).data('tgmd'));
|
|
||||||
});
|
|
||||||
md.tg('&s=s0');
|
|
||||||
});
|
|
132
src/apxtrib.js
Executable file
132
src/apxtrib.js
Executable file
@ -0,0 +1,132 @@
|
|||||||
|
const fs = require('fs')
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
const cors = require('cors')
|
||||||
|
const express = require('express')
|
||||||
|
|
||||||
|
const logger = require('./core/logger')
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************
|
||||||
|
|
||||||
|
SEE http://gitlab.ndda.fr/philc/apixtribe/-/wikis/HOWTOoverview
|
||||||
|
To have a quick understanding before doing deeply in source code
|
||||||
|
|
||||||
|
*********************************************/
|
||||||
|
// check setup*
|
||||||
|
|
||||||
|
fs.stat('/etc/nginx/nginx.conf', (err, stats) => {
|
||||||
|
if (err === null) {
|
||||||
|
logger.debug(`File exists.`)
|
||||||
|
} else if (err.code === 'ENOENT') {
|
||||||
|
logger.error('Check documentation, nginx have to be installed on this server first, no /etc/nginx/nginx.conf available, install then rerun yarn command.')
|
||||||
|
process.exit(1)
|
||||||
|
} else {
|
||||||
|
logger.error(`Exception: ` + err)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// config.js exist in any case from Setup.checkinit();
|
||||||
|
try {
|
||||||
|
const config = require('./tribes/townconf.js')
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Welcome into apixtribe, you need to init your town by "yarn setup" the first time . Check README\'s project to learn more. more.')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
// Tribes allow to get local apixtribe instance context
|
||||||
|
// dataclient .tribeids [] .DOMs [] .routes (plugins {url:name route:path}) .appname {tribeid:[website]}
|
||||||
|
const dataclient = require('./models/Tribes')
|
||||||
|
.init()
|
||||||
|
logger.info('allowed DOMs to access to this apixtribe server: ', dataclient.DOMs)
|
||||||
|
const app = express()
|
||||||
|
app.set('trust proxy', true)
|
||||||
|
// To set depending of data form or get size to send
|
||||||
|
app.use(bodyParser.urlencoded(config.bodyparse.urlencoded))
|
||||||
|
// To set depending of post put json data size to send
|
||||||
|
app.use(express.json())
|
||||||
|
app.use(bodyParser.json(config.bodyparse.json))
|
||||||
|
app.locals.tribeids = dataclient.tribeids
|
||||||
|
logger.info('app.locals.tribeids', app.locals.tribeids)
|
||||||
|
// User token authentification and user init user search
|
||||||
|
const datauser = require('./models/Pagans')
|
||||||
|
.init(dataclient.tribeids)
|
||||||
|
app.locals.tokens = datauser.tokens
|
||||||
|
logger.info('app.locals.tokens key ', Object.keys(app.locals.tokens))
|
||||||
|
// Cors management
|
||||||
|
const corsOptions = {
|
||||||
|
origin: (origin, callback) => {
|
||||||
|
if (origin === undefined) {
|
||||||
|
callback(null, true)
|
||||||
|
} else if (origin.indexOf('chrome-extension') > -1) {
|
||||||
|
callback(null, true)
|
||||||
|
} else {
|
||||||
|
// logger.info( 'origin', origin )
|
||||||
|
// marchais avant modif eslint const rematch = ( /^https?\:\/\/(.*)\:.*/g ).exec( origin )
|
||||||
|
const rematch = (/^https?:\/\/(.*):.*/g)
|
||||||
|
.exec(origin)
|
||||||
|
// logger.info( rematch )
|
||||||
|
let tmp = origin.replace(/http.?:\/\//g, '')
|
||||||
|
.split('.')
|
||||||
|
|
||||||
|
if (rematch && rematch.length > 1) tmp = rematch[1].split('.')
|
||||||
|
// logger.info( 'tmp', tmp )
|
||||||
|
let dom = tmp[tmp.length - 1]
|
||||||
|
if (tmp.length > 1) {
|
||||||
|
dom = `${tmp[tmp.length - 2]}.${tmp[tmp.length - 1]}`
|
||||||
|
}
|
||||||
|
logger.info(`origin: ${origin}, dom:${dom}, CORS allowed? : ${dataclient.DOMs.includes(dom)}`)
|
||||||
|
if (dataclient.DOMs.includes(dom)) {
|
||||||
|
callback(null, true)
|
||||||
|
} else {
|
||||||
|
logger.info('Origin is not allowed by CORS')
|
||||||
|
callback(new Error('Not allowed by CORS'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
exposedHeaders: Object.keys(config.exposedHeaders)
|
||||||
|
}
|
||||||
|
// CORS
|
||||||
|
app.use(cors(corsOptions))
|
||||||
|
// Static Routes
|
||||||
|
app.use(express.static(`${__dirname}/tribes/${config.mayorId}/www/cdn/public`, {
|
||||||
|
dotfiles: 'allow'
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Allow to public access a space dev delivered by apixtribe
|
||||||
|
// this is just a static open route for dev purpose,
|
||||||
|
// for production, we'll use a nginx static set to /www/app/appname
|
||||||
|
/* logger.info( `${config.dnsapixtribe}/space/tribeid/website`, dataclient.appname );
|
||||||
|
Object.keys( dataclient.appname )
|
||||||
|
.forEach( cid => {
|
||||||
|
dataclient.appname[ cid ].forEach( website => {
|
||||||
|
app.use( `/space/${cid}/${website}`, express.static( `${config.tribes}/${cid}/spacedev/${website}` ) );
|
||||||
|
} )
|
||||||
|
} );
|
||||||
|
*/
|
||||||
|
// Routers add any routes from /routes and /plugins
|
||||||
|
logger.info('Routes available on this apixtribe instance')
|
||||||
|
logger.info(dataclient.routes)
|
||||||
|
// prefix only use for dev purpose in production a proxy nginx redirect /app/ to node apixtribe
|
||||||
|
|
||||||
|
dataclient.routes.forEach(r => {
|
||||||
|
try {
|
||||||
|
app.use(r.url, require(r.route))
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`WARNING issue with route ${r.route} from ${r.url} check err if route is key then solve err, if not just be aware that this route won't work on your server. If you are not the maintainer and no turn around please contact the email maintainer.`)
|
||||||
|
logger.error('raise err-:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Listen web server from config profil (dev prod, other)
|
||||||
|
app.listen(config.porthttp, () => {
|
||||||
|
logger.info(`check in your browser that api works http://${config.dnsapixtribe}:${config.porthttp}`)
|
||||||
|
})
|
||||||
|
/* httpServer.setTimeout( config.settimeout );
|
||||||
|
if( config.withssl === "YES" ) {
|
||||||
|
const httpsServer = https.createServer( config.SSLCredentials, app );
|
||||||
|
httpsServer.listen( config.port.https, () => {
|
||||||
|
logger.info( `check in your browser that api works https://${config.dnsapixtribe}:${config.port.https}` );
|
||||||
|
} );
|
||||||
|
httpsServer.setTimeout( config.settimeout );
|
||||||
|
}; */
|
||||||
|
|
||||||
|
logger.info("Made with love for people's freedom, enjoy !!!")
|
9
src/core/logger.js
Normal file
9
src/core/logger.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const winston = require('winston')
|
||||||
|
|
||||||
|
const logConfiguration = {
|
||||||
|
transports: [
|
||||||
|
new winston.transports.Console()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = winston.createLogger(logConfiguration)
|
96
src/middlewares/ASUPhaveAccessrighttoanobject.js
Executable file
96
src/middlewares/ASUPhaveAccessrighttoanobject.js
Executable file
@ -0,0 +1,96 @@
|
|||||||
|
const jwt = require('jwt-simple')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const moment = require('moment')
|
||||||
|
const glob = require('glob')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
// A REMPLACER PAR hasAccessrighton.js
|
||||||
|
/*
|
||||||
|
qui permet de passer en parametre des tests d'actions autoriser sur une objet
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check if package is installed or not to pickup the right config file
|
||||||
|
const src = (__dirname.indexOf('/node_modules/') > -1) ? '../../..' : '..'
|
||||||
|
const config = require(path.normalize(`${__dirname}/${src}/config.js`))
|
||||||
|
|
||||||
|
const haveAccessrighttoanobject = (req, res, next) => {
|
||||||
|
/*
|
||||||
|
from isAuthenticated req.session.header.accessrights={app:{'tribeid:projet':profile},
|
||||||
|
data:{ "sitewebsrc": "RWCDO",
|
||||||
|
"contacts": "RWCDO"}}
|
||||||
|
from the last successfull authentification.
|
||||||
|
profile is a keyword menu available into clientconf.json of tribeid
|
||||||
|
data, list of object accessright Read Write Create Delete Owner
|
||||||
|
a xuuid can read any objet if R
|
||||||
|
if O wner means that it can only read write its object create by himself
|
||||||
|
|
||||||
|
This middleware check that we apply RESTFull CRUD concept depending of access right of a xuuid trying to act onto a xworkon tribeid
|
||||||
|
Action get = Read put = Update post = Create delete = Delete
|
||||||
|
object = req.Urlpath.split(/)[0]
|
||||||
|
*/
|
||||||
|
logger.info('haveAccessrighttoanobject()?')
|
||||||
|
// req.originalUrl contain /object/action/id object id to run action
|
||||||
|
// req.route.methods ={ put:true, delete:true post:true, get:true }
|
||||||
|
const objet = req.baseUrl.slice(1) // contain /object
|
||||||
|
const model = objet.charAt(0)
|
||||||
|
.toUpperCase() + objet.slice(1) // model u object with first letter in uppercase
|
||||||
|
let droit = ''
|
||||||
|
let ownby = []
|
||||||
|
/*
|
||||||
|
Check if object exist and get the OWNBY array, not relevant for referentials object that is only manage by CRUD no Owner logic
|
||||||
|
*/
|
||||||
|
if (objet !== 'referentials') {
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${req.session.header.xworkon}/${objet}/${req.params.id}.json`)) {
|
||||||
|
res.status(404)
|
||||||
|
.send({
|
||||||
|
payload: {
|
||||||
|
info: ['idNotfound'],
|
||||||
|
model,
|
||||||
|
moreinfo: `${config.tribes}/${req.session.header.xworkon}/${objet}/${req.params.id}.json does not exist `
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ownby = jsonfile.readFileSync(`${config.tribes}/${req.session.header.xworkon}/${objet}/${req.params.id}.json`)
|
||||||
|
.OWNBY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// logger.info( req.session.header )
|
||||||
|
if (req.session.header.xpaganid === config.devnoauthxuuid) {
|
||||||
|
logger.info('haveAccessrighttoanobject yes cause dev test user')
|
||||||
|
} else {
|
||||||
|
// accessrights was load from isAuthenticated.js middleware to make it available in req.session.header to be used into route for specific access if needed mainly to filter data in the get request depending of profil and data accessright.
|
||||||
|
if (Object.keys(req.session.header.accessrights.data)
|
||||||
|
.includes('Alltribeid') && req.session.header.accessrights.data.Alltribeid[objet]) {
|
||||||
|
droit = req.session.header.accessrights.data.Alltribeid[objet]
|
||||||
|
}
|
||||||
|
// erase rights if tribeid is specified in addition of Alltribeid
|
||||||
|
if ((req.session.header.accessrights.data[req.session.header.xworkon]) &&
|
||||||
|
req.session.header.accessrights.data[req.session.header.xworkon][objet]) {
|
||||||
|
droit = req.session.header.accessrights.data[req.session.header.xworkon][objet]
|
||||||
|
if ((req.route.methods.get && droit.includes('R')) ||
|
||||||
|
(req.route.methods.put && droit.includes('U')) ||
|
||||||
|
(req.route.methods.delete && droit.includes('D')) ||
|
||||||
|
ownby.includes(req.params.id)) {
|
||||||
|
logger.info('haveAccessrighttoanobject yes')
|
||||||
|
} else if (req.route.methods.post && droit.includes('C')) {
|
||||||
|
logger.info('haveAccessrighttoanobject yes create')
|
||||||
|
} else {
|
||||||
|
logger.info('haveAccessrighttoanobject no')
|
||||||
|
res.status(403)
|
||||||
|
.send({
|
||||||
|
payload: {
|
||||||
|
info: ['NoAccessrights'],
|
||||||
|
model,
|
||||||
|
moreinfo: `User ${req.session.header.xpaganid} accessrights are not set to do this action`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
module.exports = haveAccessrighttoanobject
|
92
src/middlewares/checkHeaders.js
Executable file
92
src/middlewares/checkHeaders.js
Executable file
@ -0,0 +1,92 @@
|
|||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
// Check if package is installed or not to pickup the right config file
|
||||||
|
// const src = ( __dirname.indexOf( '/node_modules/' ) > -1 ) ? '../../..' : '..';
|
||||||
|
// const config = require( path.normalize( `${__dirname}/${src}/config.js` ) );
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
/*
|
||||||
|
Check que le header contient des éléments necessaire pour les
|
||||||
|
routes utilisant tribeid / language / token / uuid
|
||||||
|
*/
|
||||||
|
const checkHeaders = (req, res, next) => {
|
||||||
|
// logger.info( 'checkHeaders()' );
|
||||||
|
// These headers must be passed in the request
|
||||||
|
// X-Auth and X-Uuid could have any true value
|
||||||
|
// header is stored in req.app.locals.header to be pass to route
|
||||||
|
/* const header = {
|
||||||
|
xtribeid: req.header('x-client-id'),
|
||||||
|
xlang: req.header('x-language'),
|
||||||
|
xauth: req.header('x-auth'),
|
||||||
|
xuuid: req.header('x-uuid'),
|
||||||
|
xworkon: req.header('x-xorkon',
|
||||||
|
xapp:req.header('x-app'))
|
||||||
|
};
|
||||||
|
On recupere accessrights via is Authenticated
|
||||||
|
*/
|
||||||
|
req.session = {}
|
||||||
|
const header = {}
|
||||||
|
let missingheader = ''
|
||||||
|
// logger.info( 'avant validation headers', req.headers );
|
||||||
|
// attention changement 7/11/2021 phil des exposedheader cf config.js
|
||||||
|
// If in httprequest url header are send then they are used inpriority
|
||||||
|
// Use case : send an email with a unique link that works without password and request to change password
|
||||||
|
for (const h of config.exposedHeaders) {
|
||||||
|
// logger.info( h, req.header( h ) )
|
||||||
|
if (req.params[h]) {
|
||||||
|
header[h] = req.params[h]
|
||||||
|
} else if (req.header(h)) {
|
||||||
|
header[h] = req.header(h)
|
||||||
|
} else {
|
||||||
|
// Missing header
|
||||||
|
missingheader += ' ' + h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
// logger.info( 'header', header )
|
||||||
|
if (req.params.xauth && req.params.xuuid) {
|
||||||
|
// If this exist => it is a timeout limited token
|
||||||
|
req.app.locals.tokens[req.params.xpaganid] = req.params.xauth
|
||||||
|
}
|
||||||
|
req.session.header = header
|
||||||
|
// Each header have to be declared
|
||||||
|
if (missingheader !== '') {
|
||||||
|
return res.status(403)
|
||||||
|
.send({
|
||||||
|
info: ['forbiddenAccess'],
|
||||||
|
model: 'Pagans',
|
||||||
|
moreinfo: 'checkHeader headerIsMissing:' + missingheader
|
||||||
|
})
|
||||||
|
}
|
||||||
|
;
|
||||||
|
// logger.info( req.app.locals.tribeids )
|
||||||
|
if (!req.app.locals.tribeids.includes(header.xtribe)) {
|
||||||
|
return res.status(404)
|
||||||
|
.send({
|
||||||
|
info: ['tribeiddoesnotexist'],
|
||||||
|
model: 'Pagans',
|
||||||
|
moreinfo: `xtribe unknown: ${header.xtribe}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!req.app.locals.tribeids.includes(header.xworkon)) {
|
||||||
|
return res.status(404)
|
||||||
|
.send({
|
||||||
|
info: ['tribeiddoesnotexist'],
|
||||||
|
model: 'Pagans',
|
||||||
|
moreinfo: `xworkon unknown: ${header.xworkon}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!config.languagesAvailable.includes(header.xlang)) {
|
||||||
|
return res.status(404)
|
||||||
|
.send({
|
||||||
|
info: ['langNotused'],
|
||||||
|
model: 'Pagans',
|
||||||
|
moreinfo: `xlang unknown: ${header.xlang}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// logger.info( 'After middleare checkHeaders.js req.session.header', req.session.header )
|
||||||
|
// logger.info( 'checkheaders next' )
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
module.exports = checkHeaders
|
44
src/middlewares/hasAccessrighton.js
Executable file
44
src/middlewares/hasAccessrighton.js
Executable file
@ -0,0 +1,44 @@
|
|||||||
|
const fs = require('fs-extra')
|
||||||
|
const glob = require('glob')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
const hasAccessrighton = (object, action, ownby) => {
|
||||||
|
/*
|
||||||
|
@action (mandatory) : CRUDO
|
||||||
|
@object (mandatory)= name of a folder object in /tribeid space can be a tree for example objects/items
|
||||||
|
@ownby (option) = list des uuid propriétaire
|
||||||
|
return next() if all action exist in req.app.local.tokens[UUID].ACCESSRIGHTS.data[object]
|
||||||
|
OR if last action ="O" and uuid exist in ownBy
|
||||||
|
Careffull if you have many action CRO let O at the end this will force req.right at true if the owner try an action on this object
|
||||||
|
*/
|
||||||
|
return (req, res, next) => {
|
||||||
|
// logger.info( 'err.stack hasAccessrights', err.statck )
|
||||||
|
// logger.info( `test accessright on object:${object} for ${req.session.header.xworkon}:`, req.app.locals.tokens[ req.session.header.xpaganid ].ACCESSRIGHTS.data[ req.session.header.xworkon ] )
|
||||||
|
req.right = false
|
||||||
|
if (req.app.locals.tokens[req.session.header.xpaganid].ACCESSRIGHTS.data[req.session.header.xworkon] && req.app.locals.tokens[req.session.header.xpaganid].ACCESSRIGHTS.data[req.session.header.xworkon][object]) {
|
||||||
|
req.right = true;
|
||||||
|
[...action].forEach(a => {
|
||||||
|
if (a === 'O' && ownby && ownby.includes(req.session.header.xpaganid)) {
|
||||||
|
req.right = true
|
||||||
|
} else {
|
||||||
|
req.right = req.right && req.app.locals.tokens[req.session.header.xpaganid].ACCESSRIGHTS.data[req.session.header.xworkon][object].includes(a)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// logger.info( 'Access data autorise? ', req.right )
|
||||||
|
if (!req.right) {
|
||||||
|
return res.status(403)
|
||||||
|
.send({
|
||||||
|
info: ['forbiddenAccess'],
|
||||||
|
model: 'middleware',
|
||||||
|
moreinfo: 'no auth to act on this object'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = hasAccessrighton
|
116
src/middlewares/isAuthenticated.js
Executable file
116
src/middlewares/isAuthenticated.js
Executable file
@ -0,0 +1,116 @@
|
|||||||
|
const jwt = require('jwt-simple')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const moment = require('moment')
|
||||||
|
const glob = require('glob')
|
||||||
|
// const path = require( 'path' );
|
||||||
|
// Check if package is installed or not to pickup the right config file
|
||||||
|
// const src = '..'; // ( __dirname.indexOf( '/node_modules/' ) > -1 ) ? '../../..' : '..';
|
||||||
|
// const config = require( path.normalize( `${__dirname}/${src}/config.js` ) );
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
const isAuthenticated = (req, res, next) => {
|
||||||
|
/*
|
||||||
|
check if authenticated with valid token
|
||||||
|
if not => set req.session.header.xauth=1
|
||||||
|
if yes => set for xWorkon
|
||||||
|
req.session.header.accessrights={
|
||||||
|
app:{'tribeid:website':[liste of menu]},
|
||||||
|
data:{ "sitewebsrc": "RWCDO",
|
||||||
|
"contacts": "RWCDO"}}
|
||||||
|
Liste of menu is linked with the app tht have to be consistent with accessrights.data
|
||||||
|
data, list of object accessright Read Write Create Delete Owner
|
||||||
|
a xuuid can read any objet if R
|
||||||
|
if O wner means that it can only read write its object create by himself
|
||||||
|
*/
|
||||||
|
logger.info('isAuthenticated()?')
|
||||||
|
// logger.info( 'req.app.locals.tokens', req.app.locals.tokens )
|
||||||
|
// logger.info( 'req.session.header', req.session.header );
|
||||||
|
// Check if token exist or not
|
||||||
|
req.session.header.accessrights = { app: '', data: {} }
|
||||||
|
if (req.session.header.xpaganid === config.devnoauthxuuid && req.session.header.xauth === config.devnoauthxauth) {
|
||||||
|
logger.info('isAuthenticated yes: carrefull using a bypass password give you accessrights={}')
|
||||||
|
} else if (req.session.header.xpaganid === '1' || !req.app.locals.tokens[req.session.header.xpaganid]) {
|
||||||
|
logger.info(`isAuthenticated no : uuid=1 (value=${req.session.header.xpaganid}) or locals.tokens[uuid] empty `)
|
||||||
|
logger.info('req.app.locals.tokens de xpaganid', req.app.locals.tokens[req.session.header.xpaganid])
|
||||||
|
logger.info('list key uuid de req.app.locals.tokens', Object.keys(req.app.locals.tokens))
|
||||||
|
req.session.header.xauth = '1'
|
||||||
|
} else if (req.app.locals.tokens[req.session.header.xpaganid].TOKEN !==== req.session.header.xauth) {
|
||||||
|
// logger.info(req.session.header.xuuid);
|
||||||
|
// logger.info(req.session.header.xauth);
|
||||||
|
// update tokens from file in case recently logged
|
||||||
|
try {
|
||||||
|
logger.info('token not in list of token (req.app.locals.tokens) try to refresh from file')
|
||||||
|
req.app.locals.tokens = jsonfile.readFileSync(`${config.tmp}/tokens.json`)
|
||||||
|
} catch (err) {
|
||||||
|
logger.info(`check isAuthenticated issue in reading ${config.tmp}/tokens.json`)
|
||||||
|
}
|
||||||
|
if (req.app.locals.tokens[req.session.header.xpaganid].TOKEN !==== req.session.header.xauth) {
|
||||||
|
// if still does not exist then out
|
||||||
|
logger.info('isAuthenticated no, token outdated')
|
||||||
|
req.session.header.xauth = '1'
|
||||||
|
req.session.header.xpaganid = '1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (req.session.header.xauth === '1') {
|
||||||
|
// return res.status( 403 )
|
||||||
|
return res.status(403)
|
||||||
|
.send({
|
||||||
|
info: ['forbiddenAccess'],
|
||||||
|
model: 'Pagans',
|
||||||
|
moreinfo: 'isAuthenticated faill'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
logger.info('isAuthenticated yes')
|
||||||
|
if (req.app.locals.tokens[req.session.header.xpaganid]) {
|
||||||
|
// logger.info( `accessright pour ${req.session.header.xpaganid}`, req.app.locals.tokens[ req.session.header.xpaganid ].ACCESSRIGHTS );
|
||||||
|
// set header.accessrights from tokens.json
|
||||||
|
req.session.header.accessrights = req.app.locals.tokens[req.session.header.xpaganid].ACCESSRIGHTS
|
||||||
|
} else {
|
||||||
|
// case of bypass no accessright available
|
||||||
|
req.session.header.accessrights = {}
|
||||||
|
}
|
||||||
|
// Once per day, clean old token
|
||||||
|
const currentday = moment()
|
||||||
|
.date()
|
||||||
|
logger.info('test si menagedone' + currentday, !fs.existsSync(`${config.tmp}/menagedone${currentday}`))
|
||||||
|
if (!fs.existsSync(`${config.tmp}/menagedone${currentday}`)) {
|
||||||
|
glob.sync(`${config.tmp}/menagedone*`)
|
||||||
|
.forEach(f => {
|
||||||
|
fs.remove(f, (err) => {
|
||||||
|
if (err) {
|
||||||
|
logger.info('err remove menagedone', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
glob.sync(`${config.tmp}/mdcreator*.log`)
|
||||||
|
.forEach(f => {
|
||||||
|
fs.remove(f, (err) => {
|
||||||
|
if (err) {
|
||||||
|
logger.info('err remove mdcreator log', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const newtokens = {}
|
||||||
|
for (const k of Object.keys(req.app.locals.tokens)) {
|
||||||
|
try {
|
||||||
|
const decodedToken = jwt.decode(req.app.locals.tokens[k].TOKEN, config.jwtSecret)
|
||||||
|
// logger.info( moment( decodedToken.expiration ), moment() )
|
||||||
|
// logger.info( moment( decodedToken.expiration ) >= moment() )
|
||||||
|
if (moment(decodedToken.expiration) >= moment()) {
|
||||||
|
newtokens[k] = req.app.locals.tokens[k]
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Check isAuthenticated cleaning token ', err)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
req.app.locals.tokens = newtokens
|
||||||
|
jsonfile.writeFileSync(`${config.tmp}/tokens.json`, newtokens)
|
||||||
|
fs.writeFileSync(`${config.tmp}/menagedone${currentday}`, 'fichier semaphore to clean data each day can be deleted with no consequence', 'utf-8')
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = isAuthenticated
|
122
src/models/Contracts.js
Executable file
122
src/models/Contracts.js
Executable file
@ -0,0 +1,122 @@
|
|||||||
|
const fs = require('fs-extra')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const glob = require('glob')
|
||||||
|
const path = require('path')
|
||||||
|
const moment = require('moment')
|
||||||
|
const axios = require('axios')
|
||||||
|
const scrapeit = require('scrape-it')
|
||||||
|
const cheerio = require('cheerio')
|
||||||
|
const Mustache = require('mustache')
|
||||||
|
const qrcode = require('qrcode')
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
// Check if package is installed or not to pickup the right config file
|
||||||
|
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
|
||||||
|
/*
|
||||||
|
Model that will process actions plan for each client like sending email campain, or anything that
|
||||||
|
are plan in /tribes/tribeid/actions/todo
|
||||||
|
*/
|
||||||
|
const Cards = {} // require('../../models/Cards');
|
||||||
|
const Contracts = {}
|
||||||
|
/*
|
||||||
|
Send if envoicampain a liste of email in param.msg.destperso with param.headers
|
||||||
|
if not envoicampain, it just return a test about what to send
|
||||||
|
@param = {headers, msg:{destperso}}
|
||||||
|
*/
|
||||||
|
Contracts.sendcampain = async (param, envoicampain) => {
|
||||||
|
if (envoicampain) {
|
||||||
|
// Carefull w the action post outputs/msg just wait the feedback of the 1st message
|
||||||
|
const retcampain = await axios.post('https://mail.maildigit.fr/outputs/msg', param.msg, {
|
||||||
|
headers: param.headers
|
||||||
|
})
|
||||||
|
if (retcampain.status !==== 200) {
|
||||||
|
logger.info('err', retcampain.payload.moreinfo)
|
||||||
|
fs.appendFileSync(`${config.tribes}/log_erreurglobal.txt`, moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + ' - IMPOSSIBLE TO SEND CAMPAIN TODO for :' + param.tribeid + ' -- ' + retcampain.payload.moreinfo + '\n', 'utf-8')
|
||||||
|
};
|
||||||
|
return retcampain
|
||||||
|
} else {
|
||||||
|
// permet de tester ce qu'il y a à envoyer
|
||||||
|
let premieremail = ''
|
||||||
|
for (let i = 0; i < param.msg.destperso.length; i++) {
|
||||||
|
premieremail += param.msg.destperso[0].email + ','
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 201,
|
||||||
|
payload: {
|
||||||
|
info: ['simplecomptage'],
|
||||||
|
model: 'Contracts',
|
||||||
|
moreinfo: '#email: ' + param.msg.destperso.length + ' - 5 1st emails: ' + premieremail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Contracts.initActiontodo = async (envoie) => {
|
||||||
|
const datedeb = moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss')
|
||||||
|
let todo, actiondone
|
||||||
|
const log = {
|
||||||
|
nbaction: 0,
|
||||||
|
nbactionexec: 0,
|
||||||
|
nbactionerr: 0,
|
||||||
|
actionlist: ''
|
||||||
|
}
|
||||||
|
const listclient = jsonfile.readFileSync(`${config.tribes}/tribeids.json`)
|
||||||
|
for (const clid in listclient) {
|
||||||
|
logger.info(listclient[clid])
|
||||||
|
const listaction = glob.sync(`${config.tribes}/${listclient[clid]}/actions/todo/*.json`)
|
||||||
|
for (const action in listaction) {
|
||||||
|
logger.info(listaction[action])
|
||||||
|
log.nbaction++
|
||||||
|
todo = jsonfile.readFileSync(listaction[action])
|
||||||
|
let passdate = true
|
||||||
|
// currentdate doit etre après la startDate si existe et avant valideuntilDate si existe
|
||||||
|
// logger.info('test now est avant date start ', moment() < moment(todo.startDate, 'YYYYMMDD HH:mm:ss').toDate());
|
||||||
|
if (todo.startDate && (moment() < moment(todo.startDate, 'YYYYMMDD HH:mm:ss')
|
||||||
|
.toDate())) {
|
||||||
|
passdate = false
|
||||||
|
};
|
||||||
|
// currentdate ne doit pas depasser la date de validité de la tache
|
||||||
|
// logger.info('test now est après la date de validite ', moment() > moment(todo.validuntilDate, 'YYYYMMDD HH:mm:ss').toDate());
|
||||||
|
if (todo.valideuntilDate && (moment() > moment(todo.validuntilDate, 'YYYYMMDD HH:mm:ss')
|
||||||
|
.toDate())) {
|
||||||
|
passdate = false
|
||||||
|
};
|
||||||
|
// currentdate
|
||||||
|
if (passdate && todo.action && todo.error === '') {
|
||||||
|
log.nbactionexec++
|
||||||
|
const actiondone = await Contracts[todo.action](todo, envoie)
|
||||||
|
todo.datesRun.push(moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss'))
|
||||||
|
// logger.info("actiondone", actio jsonfile.writeFileSyncndone);
|
||||||
|
log.actionlist += 'STATUS:' + actiondone.status + ' -- ' + listaction[action] + '\n'
|
||||||
|
if (actiondone.status === 200) {
|
||||||
|
todo.error = ''
|
||||||
|
} else {
|
||||||
|
log.nbactionerr++
|
||||||
|
todo.error += 'status : ' + actiondone.status + ' ' + actiondone.payload.moreinfo
|
||||||
|
};
|
||||||
|
if (parseInt(todo.maxnumberoftime) && todo.maxnumberoftime !== '999' && (todo.datesRun.length >= parseInt(todo.maxnumberoftime))) {
|
||||||
|
// archive en done this triggeraction
|
||||||
|
jsonfile.writeFileSync(listaction[action].replace('/todo/', '/done/'), todo, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
fs.unlinkSync(listaction[action])
|
||||||
|
} else {
|
||||||
|
jsonfile.writeFileSync(listaction[action], todo, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.actionlist += 'STATUS : not executed ' + listaction[action] + '\n'
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const trace = '###################### LOGS ####################\nSTART:' + datedeb + ' END:' + moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + "\n nombre d'actions analysées : " + log.nbaction + ' dont executées : ' + log.nbactionexec + ' dont en erreur: ' + log.nbactionerr + '\n' + log.actionlist
|
||||||
|
fs.appendFileSync(`${config.tribes}/log.txt`, trace, 'utf-8')
|
||||||
|
return 'done'
|
||||||
|
}
|
||||||
|
module.exports = Contracts
|
437
src/models/Messages.js
Executable file
437
src/models/Messages.js
Executable file
@ -0,0 +1,437 @@
|
|||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
|
const bcrypt = require('bcrypt')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const path = require('path')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const glob = require('glob')
|
||||||
|
const Mustache = require('mustache')
|
||||||
|
const jwt = require('jwt-simple')
|
||||||
|
const { DateTime } = require('luxon')
|
||||||
|
const UUID = require('uuid')
|
||||||
|
const Outputs = require('../models/Outputs.js')
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
const checkdata = require('../nationchains/socialworld/contracts/checkdata.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
/*
|
||||||
|
Message manager
|
||||||
|
* Manage apixtribe message at different level
|
||||||
|
* this means that an object (most often a user or a contact) want to send a question to an other user.
|
||||||
|
|
||||||
|
To know more http://gitlab.ndda.fr/philc/apixtribe/-/wikis/HOWTONotification
|
||||||
|
|
||||||
|
*/
|
||||||
|
const Messages = {}
|
||||||
|
Messages.init = () => {
|
||||||
|
console.group('init Message')
|
||||||
|
Messages.aggregate()
|
||||||
|
}
|
||||||
|
|
||||||
|
Messages.byEmailwithmailjet = (tribeid, msg) => {
|
||||||
|
/* @tribeid requester
|
||||||
|
@msg =[{
|
||||||
|
To:[{Email,Name}],
|
||||||
|
Cc:[{Email,Name}],
|
||||||
|
Bcc:[{Email,Name}]}],
|
||||||
|
Subject:"Subject",
|
||||||
|
TextPart:"texte content",
|
||||||
|
HTMLPart:"html content"
|
||||||
|
}]
|
||||||
|
If tribeid has a mailjet account it use it if not then it use the apixtribe @mailjetconf = {apikeypub:, apikeypriv:, From:{Email:,Name:}}
|
||||||
|
This send a bunch of messages check the mailjet account used
|
||||||
|
FYI: Basic account is 200 email /days 6000 /month
|
||||||
|
Log is stored into
|
||||||
|
tribeidsender/messages/logs/sent/timestamp.json
|
||||||
|
@todo GUI to manage statistics and notification alert limit sender email
|
||||||
|
*/
|
||||||
|
logger.info('Envoie mailjet')
|
||||||
|
const confclient = fs.readJsonSync(`${config.tribes}/${tribeid}/clientconf.json`)
|
||||||
|
let tribeidsender = tribeid
|
||||||
|
if (confclient.smtp && confclient.smtp.mailjet) {
|
||||||
|
mailjetconf = confclient.smtp.mailjet
|
||||||
|
} else {
|
||||||
|
const confapixtribe = fs.readJsonSync(`${config.tribes}/${config.mayorId}/clientconf.json`)
|
||||||
|
if (!(confapixtribe.smtp && confapixtribe.smtp.mailjet)) {
|
||||||
|
return {
|
||||||
|
status: 403,
|
||||||
|
data: {
|
||||||
|
models: 'Messages',
|
||||||
|
info: ['nosmtpmailjet'],
|
||||||
|
moreinfo: 'missing smtpmailjet parameter in apixtribe contact your admin to activate an mailjet account on this server.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tribeidsender = 'apixtribe'
|
||||||
|
mailjetconf = confapixtribe.smtp.mailjet
|
||||||
|
}
|
||||||
|
// add from from setings account
|
||||||
|
const MSG = msg.map(m => {
|
||||||
|
m.From = mailjetconf.From
|
||||||
|
return m
|
||||||
|
})
|
||||||
|
const mailjet = require('node-mailjet')
|
||||||
|
.connect(mailjetconf.apikeypub, mailjetconf.apikeypriv)
|
||||||
|
const request = mailjet.post('send', { version: 'v3.1' })
|
||||||
|
.request({ Messages: MSG })
|
||||||
|
request
|
||||||
|
.then(result => {
|
||||||
|
// store into tribeidsender/messages/logs/sent/timestamp.json
|
||||||
|
const t = Date.now()
|
||||||
|
MSG.result = result.body
|
||||||
|
fs.outputJson(`${config.tribes}/${tribeidsender}/messages/logs/sent/${t}.json`, MSG)
|
||||||
|
logger.info(result.body)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
const t = Date.now()
|
||||||
|
MSG.result = err
|
||||||
|
fs.outputJson(`${config.tribes}/${tribeidsender}/messages/logs/error/${t}.json`, MSG)
|
||||||
|
logger.info(err.statusCode, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Messages.buildemail = (tribeid, tplmessage, data) => {
|
||||||
|
/* @tribeid => client concerned by sending email get info from clientconf.json (customization, useradmin:{ EMAIL,LOGIN} , ...)
|
||||||
|
@tplmessage => folder where template is available (ex: apixtribe/referentials/dataManagement/template/order)
|
||||||
|
@data { destemail = email to
|
||||||
|
if destuuid => then it get user EMAIL if exiat
|
||||||
|
subject = mail subject
|
||||||
|
// any relevant information for template message
|
||||||
|
msgcontenthtml = html msg content
|
||||||
|
msgcontenttxt = text msg content
|
||||||
|
....}
|
||||||
|
@return msg
|
||||||
|
mail part ready to send by mailjet or other smtp
|
||||||
|
*/
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${tribeid}/clientconf.json`)) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
data: { model: 'Messages', info: ['tribeiddoesnotexist'], moreinfo: `${tribeid} does not exist` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${tplmessage}`)) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
data: { model: 'Messages', info: ['tplmessagedoesnotexist'], moreinfo: `tpl does not exist ${tplmessage}` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const clientconf = fs.readJsonSync(`${config.tribes}/${tribeid}/clientconf.json`)
|
||||||
|
// add clientconf.customization into data for template
|
||||||
|
data.customization = clientconf.customization
|
||||||
|
// manage receiver
|
||||||
|
const msg = { To: [{ Email: clientconf.useradmin.EMAIL, Name: clientconf.useradmin.LOGIN }] }
|
||||||
|
if (data.destemail) {
|
||||||
|
msg.To.push({ Email: data.destemail })
|
||||||
|
}
|
||||||
|
if (data.destuuid && fs.exist(`${config.tribes}/${tribeid}/users/${data.destuuid}.json`)) {
|
||||||
|
const uuidconf = fs.readJsonSync(`${config.tribes}/${tribeid}/users/${data.destuuid}.json`)
|
||||||
|
if (uuidconf.EMAIL && uuidconf.EMAIL.length > 4) {
|
||||||
|
msg.To.push({ Email: uuidconf.EMAIL, Name: uuidconf.LOGIN })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// logger.info( data )
|
||||||
|
// email content
|
||||||
|
msg.Subject = `Message from ${tribeid}`
|
||||||
|
if (data.subject) msg.Subject = data.subject
|
||||||
|
msg.TextPart = Mustache.render(fs.readFileSync(`${config.tribes}/${data.tplmessage}/contenttxt.mustache`, 'utf-8'), data)
|
||||||
|
msg.HTMLPart = Mustache.render(fs.readFileSync(`${config.tribes}/${data.tplmessage}/contenthtml.mustache`, 'utf-8'), data)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: { model: 'Messages', info: ['msgcustomsuccessfull'], msg }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Messages.postinfo = (data) => {
|
||||||
|
/*
|
||||||
|
Data must have:
|
||||||
|
at least one of desttribeid:,destuuid:,destemail:
|
||||||
|
if an email have to be sent tplmessage:"tribeid/referentials/dataManagement/template/tplname",
|
||||||
|
at least one of contactemail:,contactphone,contactlogin,contactuuid
|
||||||
|
any other usefull keys
|
||||||
|
|
||||||
|
Data is stored into contacts object .json
|
||||||
|
*/
|
||||||
|
let contact = '';
|
||||||
|
['contactemail', 'contactphone', 'contactuuid', 'contactlogin'].forEach(c => {
|
||||||
|
if (data[c]) contact += c + '##' + data[c] + '###'
|
||||||
|
})
|
||||||
|
logger.info(contact)
|
||||||
|
if (contact === '') {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
data: { model: 'Messages', info: ['contactundefine'], moreinfo: 'no contact field found in this form' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!data.desttribeid) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
data: { model: 'Messages', info: ['tribeidundefine'], moreinfo: 'no desttribeid field found in this form' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// save new message in contacts
|
||||||
|
let messages = {}
|
||||||
|
if (fs.existsSync(`${config.tribes}/${data.desttribeid}/contacts/${contact}.json`)) {
|
||||||
|
messages = fs.readJsonSync(`${config.tribes}/${data.desttribeid}/contacts/${contact}.json`, 'utf-8')
|
||||||
|
}
|
||||||
|
messages[Date.now()] = data
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${data.desttribeid}/contacts/${contact}.json`, messages)
|
||||||
|
|
||||||
|
// if templatemessage exist then we send alert email
|
||||||
|
// data content have to be consistent with tplmessage to generate a clean email
|
||||||
|
if (data.tplmessage && data.tplmessage !== '' &&
|
||||||
|
fs.existsSync(`${config.tribes}/${data.tplmessage}`)) {
|
||||||
|
if (!(data.msgtplhtml && data.msgtpltxt)) {
|
||||||
|
data.msgcontenthtml = `<p>${data.contactname} - ${contact.replace(/###/g, ' ').replace(/##/g, ':')}</p><p>Message: ${data.contactmessage}</p>`
|
||||||
|
data.msgcontenttxt = `${data.contactname} - ${contact}/nMessage:${data.contactmessage}\n`
|
||||||
|
} else {
|
||||||
|
data.msgcontenthtml = Mustache.render(data.msgtplhtml, data)
|
||||||
|
data.msgcontenttxt = Mustache.render(data.msgtpltxt, data)
|
||||||
|
}
|
||||||
|
const msg = Messages.buildemail(data.desttribeid, data.tplmessage, data)
|
||||||
|
if (msg.status === 200) {
|
||||||
|
Messages.byEmailwithmailjet(data.desttribeid, [msg.data.msg])
|
||||||
|
}
|
||||||
|
// we get error message eventualy but email feedback sent is not in real time
|
||||||
|
return msg
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
data: { info: 'missingtpl', model: 'Messages', moreinfo: `missing template ${data.tplmessage}` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Messages.aggregate = () => {
|
||||||
|
// collect each json file and add them to a global.json notifat require level
|
||||||
|
const dest = {}
|
||||||
|
try {
|
||||||
|
glob.sync(`${config.tribes}/**/notif_*.json`)
|
||||||
|
.forEach(f => {
|
||||||
|
// logger.info( 'find ', f )
|
||||||
|
const repglob = `${path.dirname(f)}/global.json`
|
||||||
|
if (!dest[repglob]) {
|
||||||
|
dest[repglob] = []
|
||||||
|
}
|
||||||
|
dest[repglob].push(fs.readJsonSync(f, 'utf-8'))
|
||||||
|
fs.removeSync(f)
|
||||||
|
})
|
||||||
|
// logger.info( dest )
|
||||||
|
Object.keys(dest)
|
||||||
|
.forEach(g => {
|
||||||
|
let notif = []
|
||||||
|
if (fs.existsSync(g)) {
|
||||||
|
notif = fs.readJsonSync(g)
|
||||||
|
}
|
||||||
|
;
|
||||||
|
fs.writeJsonSync(g, notif.concat(dest[g]), 'utf-8')
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
Console.log('ATTENTION, des Messages risquent de disparaitre et ne sont pas traitées.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Messages.object = (data, header) => {
|
||||||
|
/*
|
||||||
|
Create or replace an object no check at all here this is only
|
||||||
|
a way to add / replace object
|
||||||
|
if deeper thing have to be checheck or done then a callback:{tribeidplugin,pluginname,functionname}
|
||||||
|
data.descttribeid tribeid to send at least to admin
|
||||||
|
data.tplmessage = folder of emailtemplate
|
||||||
|
*/
|
||||||
|
logger.info('data', data)
|
||||||
|
logger.info(`${config.tribes}/${header.xworkon}/${data.object}`)
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${header.xworkon}/${data.object}`)) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
data: {
|
||||||
|
model: 'Messages',
|
||||||
|
info: ['UnknownObjectfortribeid'],
|
||||||
|
moreinfo: `This object ${data.object} does not exist for this tribeid ${header.xworkon}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.uuid === 0) {
|
||||||
|
data.uuid = UUID.v4()
|
||||||
|
}
|
||||||
|
if (data.callback) {
|
||||||
|
// check from plugin data and add relevant data
|
||||||
|
const Plug = require(`${config.tribes}/${data.callback.tribeid}/plugins/${data.callback.plugins}/Model.js`)
|
||||||
|
const check = Plug[data.callback.function](header.xworkon, data)
|
||||||
|
if (check.status === 200) {
|
||||||
|
data = check.data.data
|
||||||
|
} else {
|
||||||
|
return check
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${header.xworkon}/${data.object}/${data.uuid}.json`, data)
|
||||||
|
// if templatemessage exist then we try to send alert email
|
||||||
|
if (data.tplmessage && data.tplmessage !== '' &&
|
||||||
|
fs.existsSync(`${config.tribes}/${data.tplmessage}`)) {
|
||||||
|
const msg = Messages.buildemail(data.desttribeid, data.tplmessage, data)
|
||||||
|
if (msg.status === 200) {
|
||||||
|
logger.info('WARN EMAIL DESACTIVATED CHANGE TO ACTIVATE in Messages.js')
|
||||||
|
// Messages.byEmailwithmailjet( data.desttribeid, [ msg.data.msg ] );
|
||||||
|
}
|
||||||
|
// we get error message eventualy but email feedback sent is not in real time see notification alert in case of email not sent.
|
||||||
|
}
|
||||||
|
;
|
||||||
|
// Sendback data with new information
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
model: 'Messages',
|
||||||
|
info: ['Successregisterobject'],
|
||||||
|
msg: data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Messages.notification = (data, header) => {
|
||||||
|
// check if valid notification
|
||||||
|
if (!req.body.elapseddays) {
|
||||||
|
req.body.elapseddays = 3
|
||||||
|
}
|
||||||
|
let missingkey = '';
|
||||||
|
['uuid', 'title', 'desc', 'icon', 'elapseddays', 'classicon'].forEach(k => {
|
||||||
|
if (!data[k]) missingkey += ` ${k},`
|
||||||
|
if (k === 'classicon' && !(['text-danger', 'text-primary', 'text-success', 'text-warning', 'text-info'].includes(data[k]))) {
|
||||||
|
missingkey += ` classicon is not well set ${data[k]}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (missingkey !== '') {
|
||||||
|
return {
|
||||||
|
status: 422,
|
||||||
|
data: {
|
||||||
|
model: 'Messages',
|
||||||
|
info: ['InvalidNote'],
|
||||||
|
moreinfo: `invalid notification sent cause missing key ${missingkey}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${header.xworkon}/${data.object}`)) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
data: {
|
||||||
|
model: 'Messages',
|
||||||
|
info: ['UnknownObjectfortribeid'],
|
||||||
|
moreinfo: `This object ${data.object} does not exist for this tribeid ${data.tribeid}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// by default store in tmp/notification this is only for sysadmin
|
||||||
|
// user adminapixtribe
|
||||||
|
let notdest = `${config.tmp}`
|
||||||
|
|
||||||
|
if (data.app) {
|
||||||
|
const destapp = `${config.tribes}/${data.app.split(':')[0]}/spacedev/${data.app.split(':')[1]}`
|
||||||
|
if (fs.existsSync(destapp)) {
|
||||||
|
notdest = destapp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.plugins) {
|
||||||
|
const destplugin = `${config.tribes}/${data.plugins.split(':')[0]}/plugins/${data.plugins.split(':')[1]}`
|
||||||
|
if (fs.existsSync(destplugin)) {
|
||||||
|
notdest = destplugin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.object) {
|
||||||
|
const destobject = `${config.tribes}/${data.tribeid}/${data.object}/`
|
||||||
|
if (fs.existsSync(destobject)) {
|
||||||
|
notdest = destobject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!data.time) {
|
||||||
|
data.time = Date.now()
|
||||||
|
}
|
||||||
|
fs.outputJsonSync(`${notdest}/Messages/notif_${data.time}.json`, data)
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
model: 'Messages',
|
||||||
|
info: ['Successregisternotification'],
|
||||||
|
notif: data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Messages.request = (tribeid, uuid, ACCESSRIGHTS, apprequest) => {
|
||||||
|
// list notif for each app
|
||||||
|
// list notif for each tribeid / objects if Owned
|
||||||
|
// list notif for
|
||||||
|
// check uuid notification
|
||||||
|
// Collect all notification and agregate them at relevant level;
|
||||||
|
Messages.aggregate()
|
||||||
|
|
||||||
|
// for test purpose
|
||||||
|
// const notif = jsonfile.readFileSync( `${config.tribes}/ndda/spacedev/mesa/src/static/components/notification/data_notiflist_fr.json` );
|
||||||
|
let notif
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${apprequest.tribeid}/spacedev/${apprequest.website}/src/static/components/notification/data_notiflist_${apprequest.lang}.json`)) {
|
||||||
|
// by default we send back this but this highlght an issue
|
||||||
|
notif = {
|
||||||
|
iconnotif: 'bell',
|
||||||
|
number: 1,
|
||||||
|
notifheader: 'Your last Messages',
|
||||||
|
notiffooter: 'Check all Messages',
|
||||||
|
actionnotifmanager: '',
|
||||||
|
href: '?action=notification.view',
|
||||||
|
notifs: [{
|
||||||
|
urldetail: '#',
|
||||||
|
classicon: 'text-danger',
|
||||||
|
icon: 'alert',
|
||||||
|
title: 'File does not exist',
|
||||||
|
desc: ` ${config.tribes}/${apprequest.tribeid}/spacedev/${apprequest.website}/src/static/components/notification/data_notiflist_${apprequest.lang}.json`,
|
||||||
|
elapse: 'il y a 13mn'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
notif = jsonfile.readFileSync(`${config.tribes}/${apprequest.tribeid}/spacedev/${apprequest.website}/src/static/components/notification/data_notiflist_${apprequest.lang}.json`)
|
||||||
|
// clean up example notif
|
||||||
|
notif.notifs = []
|
||||||
|
}
|
||||||
|
// check notification for plugins of tribeid of the login
|
||||||
|
glob.sync(`${config.tribes}/${tribeid}/plugins/*/Messages/global.json`)
|
||||||
|
Object.keys(ACCESSRIGHTS.app)
|
||||||
|
.forEach(a => {
|
||||||
|
// check for each app if notification about app
|
||||||
|
const appnot = `${config.tribes}/${a.split(':')[0]}/spacedev/${a.split(':')[1]}/Messages/global.json`
|
||||||
|
if (fs.existsSync(appnot)) {
|
||||||
|
notif.notifs = notif.notifs.concat(fs.readJsonSync(appnot))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Object.keys(ACCESSRIGHTS.plugin)
|
||||||
|
.forEach(p => {
|
||||||
|
// each plugin
|
||||||
|
if (ACCESSRIGHTS.plugin[p].profil === 'owner') {
|
||||||
|
const pluginnot = `${config.tribes}/${p.split(':')[0]}/plugins/${p.split(':')[1]}/Messages/global.json`
|
||||||
|
if (fs.existsSync(pluginnot)) {
|
||||||
|
notif.notifs = notif.notifs.concat(fs.readJsonSync(pluginnot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Object.keys(ACCESSRIGHTS.data)
|
||||||
|
.forEach(c => {
|
||||||
|
// each tribeid
|
||||||
|
Object.keys(ACCESSRIGHTS.data[c])
|
||||||
|
.forEach(o => {
|
||||||
|
const cliobjnot = `${config.tribes}/${c}/${o}/Messages/global.json`
|
||||||
|
// check for each tribeid / Object per accessright user
|
||||||
|
if (fs.existsSync(cliobjnot)) {
|
||||||
|
logger.info(`droit sur client ${c} objet ${o} : ${ACCESSRIGHTS.data[c][o]}`)
|
||||||
|
// check if intersection between user accessrigth for this object and the notification accessright is not empty @Todo replace true by intersec
|
||||||
|
logger.info('WARN no actif filter all notif are shared with any authenticated user')
|
||||||
|
const newnotif = fs.readJsonSync(cliobjnot)
|
||||||
|
.filter(n => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
notif.notifs = notif.notifs.concat(newnotif)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
model: 'Messages',
|
||||||
|
info: ['successgetnotification'],
|
||||||
|
notif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Messages
|
106
src/models/Nationchains.js
Executable file
106
src/models/Nationchains.js
Executable file
@ -0,0 +1,106 @@
|
|||||||
|
const bcrypt = require('bcrypt')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const path = require('path')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const glob = require('glob')
|
||||||
|
const jwt = require('jwt-simple')
|
||||||
|
const moment = require('moment')
|
||||||
|
const axios = require('axios')
|
||||||
|
const UUID = require('uuid')
|
||||||
|
const Outputs = require('./Outputs.js')
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
const checkdata = require('../nationchains/socialworld/contracts/checkdata.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
/*
|
||||||
|
Blockchain manager
|
||||||
|
* Manage network directory
|
||||||
|
* read Blockchain and search,
|
||||||
|
* submit a transaction (now) or contract (futur) to store from userA.pubkey to userB.pubkey a number of AXESS
|
||||||
|
* mine to be able to register a block and create AXESS
|
||||||
|
* manage APIXP rules 20 M APIXP 1AXESS = 1 block validation
|
||||||
|
* manage contract = action if something appened validate by a proof of work
|
||||||
|
*/
|
||||||
|
const Nationchains = {}
|
||||||
|
Nationchains.init = () => {
|
||||||
|
console.group('init Nationchains')
|
||||||
|
}
|
||||||
|
Nationchains.synchronize = () => {
|
||||||
|
/*
|
||||||
|
Run process to communicate with a list of apixtribe instance to update transaction and earn AXP
|
||||||
|
To creation of a new tribeid or a new login
|
||||||
|
*/
|
||||||
|
// update himself then send to other information
|
||||||
|
if (process.env.NODE_ENV !== 'prod') {
|
||||||
|
// Not concerned
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
const initcurrentinstance = {
|
||||||
|
fixedIP: '',
|
||||||
|
lastblocknumber: 0,
|
||||||
|
firsttimeupdate: 0,
|
||||||
|
lastimeupdate: 0,
|
||||||
|
positifupdate: 0,
|
||||||
|
negatifupdate: 0,
|
||||||
|
pubkeyadmin: '',
|
||||||
|
tribeids: [],
|
||||||
|
logins: [],
|
||||||
|
knowninstance: []
|
||||||
|
}
|
||||||
|
let currentinstance = initcurrentinstance
|
||||||
|
try {
|
||||||
|
currentinstance = fs.readFileSync(`${config.tribes}/${config.mayorId}/nationchains/nodes/${config.rootURL}`, 'utf-8')
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('first init')
|
||||||
|
}
|
||||||
|
const loginsglob = fs.readJsonSync(`${config.tmp}/loginsglob.json`, 'utf-8')
|
||||||
|
currentinstance.logins = Object.keys(loginsglob)
|
||||||
|
currentinstance.tribeids = [...new Set(Object.values(loginsglob))]
|
||||||
|
currentinstance.instanceknown = glob.Sync(`${config.tribes}/${config.mayorId}/nationchains/nodes/*`)
|
||||||
|
// Save it
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${config.mayorId}/nationchains/nodes/${config.rootURL}`, currentinstance)
|
||||||
|
// proof of work
|
||||||
|
// try to find a key based on last block with difficulty
|
||||||
|
// if find then send to all for update and try to get token
|
||||||
|
// in any case rerun Nationchains.synchronize()
|
||||||
|
currentinstance.instanceknown.forEach(u => {
|
||||||
|
if (u !== config.rootURL) {
|
||||||
|
// send currentinstance info and get back state of
|
||||||
|
axios.post(`https://${u}/nationchains/push`, currentinstance)
|
||||||
|
.then(rep => {
|
||||||
|
newdata = rep.payload.moreinfo
|
||||||
|
// Available update info
|
||||||
|
fs.readJson(`${config.tribes}/${config.mayorId}/nationchains/nodes/${u}`, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
data.negatifupdate += 1
|
||||||
|
data.lasttimeupdate = Date.now()
|
||||||
|
} else {
|
||||||
|
data.positifupdate += 1
|
||||||
|
data.lastimeupdate = Date.now()
|
||||||
|
data.tribeids = newdata.tribeids
|
||||||
|
data.logins = newdata.logins
|
||||||
|
data.lastblocknumber = newdata.lastblocknumber
|
||||||
|
newdata.knowninstance.forEach(k => {
|
||||||
|
if (!data.knowninstance.includes(k)) {
|
||||||
|
data.knowninstance.push(k)
|
||||||
|
// init the domain for next update
|
||||||
|
initcurrentinstance.firsttimeupdate = Date.now()
|
||||||
|
fs.outputJson(`${config.tribes}/${config.mayorId}/nationchains/nodes/${k}`, initcurrentinstance, 'utf-8')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// save with info
|
||||||
|
fs.outputJson(`${config.tribes}/${config.mayorId}/nationchains/nodes/${u}`, data)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
// Not available
|
||||||
|
data.negatifupdate += 1
|
||||||
|
data.lasttimeupdate = Date.now()
|
||||||
|
fs.outputJson(`${config.tribes}/${config.mayorId}/nationchains/nodes/${u}`, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Nationchains
|
@ -1,9 +1,11 @@
|
|||||||
const glob = require( 'glob' );
|
const glob = require('glob')
|
||||||
const path = require( 'path' );
|
const path = require('path')
|
||||||
const fs = require( 'fs-extra' );
|
const fs = require('fs-extra')
|
||||||
const config = require( '../tribes/townconf.js' );
|
const config = require('../tribes/townconf.js')
|
||||||
|
|
||||||
const Odmdb = {};
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
const Odmdb = {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Input: metaobject => data mapper of Key: Value
|
Input: metaobject => data mapper of Key: Value
|
||||||
@ -14,4 +16,4 @@ Input: metaobject => data mapper of Key: Value
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = Odmdb;
|
module.exports = Odmdb
|
556
src/models/Outputs.js
Executable file
556
src/models/Outputs.js
Executable file
@ -0,0 +1,556 @@
|
|||||||
|
const fs = require('fs-extra')
|
||||||
|
const path = require('path')
|
||||||
|
const formidable = require('formidable')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const mustache = require('mustache')
|
||||||
|
const moment = require('moment')
|
||||||
|
const UUID = require('uuid')
|
||||||
|
const pdf = require('pdf-creator-node')
|
||||||
|
const nodemailer = require('nodemailer')
|
||||||
|
const smtpTransport = require('nodemailer-smtp-transport')
|
||||||
|
const axios = require('axios')
|
||||||
|
const { GoogleSpreadsheet } = require('google-spreadsheet')
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
const checkdata = require('../nationchains/socialworld/contracts/checkdata.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
const Outputs = {}
|
||||||
|
|
||||||
|
Outputs.ggsheet2json = async (req, header) => {
|
||||||
|
/*
|
||||||
|
req.ggsource = key name of the ggsheet into clientconf
|
||||||
|
req.sheets = [list of sheet names to import]
|
||||||
|
into /ggsheets/ ggsource.json = {resultfolder,productIdSpreadsheet,
|
||||||
|
googleDriveCredentials}
|
||||||
|
*/
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${header.xworkon}/${req.ggsource}`)) {
|
||||||
|
// Misconfiguration
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
data: {},
|
||||||
|
info: ['Misconfiguration'],
|
||||||
|
moreinfo: `${header.xworkon}/${req.ggsource} missing file check gitlabdocumentation`,
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const confgg = fs.readJsonSync(`${config.tribes}/${header.xworkon}/${req.ggsource}`, 'utf-8')
|
||||||
|
// const ggconnect = clientconf.ggsheet[ req.ggsource ]
|
||||||
|
// googleDriveCredentials;
|
||||||
|
// logger.info( ggconnect )
|
||||||
|
doc = new GoogleSpreadsheet(confgg.productIdSpreadsheet)
|
||||||
|
await doc.useServiceAccountAuth(confgg.googleDriveCredentials)
|
||||||
|
await doc.loadInfo()
|
||||||
|
const result = []
|
||||||
|
let invalidfor = ''
|
||||||
|
// logger.info( "sheets", req.sheets );
|
||||||
|
for (const sheetName of req.sheets) {
|
||||||
|
logger.info('loading: ', sheetName)
|
||||||
|
if (!doc.sheetsByTitle[sheetName]) {
|
||||||
|
invalidfor += ' ' + sheetName
|
||||||
|
} else {
|
||||||
|
sheet = doc.sheetsByTitle[sheetName]
|
||||||
|
await sheet.loadHeaderRow()
|
||||||
|
const records = await sheet.getRows({ offset: 1 })
|
||||||
|
records.forEach(record => {
|
||||||
|
const offer = {}
|
||||||
|
for (let index = 0; index < record._sheet.headerValues.length; index++) {
|
||||||
|
offer[record._sheet.headerValues[index]] = record[record._sheet.headerValues[index]]
|
||||||
|
}
|
||||||
|
result.push(offer)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const idfile = UUID.v4()
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${header.xworkon}/${confgg.resultfolder}/${idfile}.json`, result, 'utf8')
|
||||||
|
if (invalidfor === '') {
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
data: `${confgg.resultfolder}/${idfile}.json`,
|
||||||
|
info: ['Successfull'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: 207,
|
||||||
|
payload: {
|
||||||
|
data: `${confgg.resultfolder}/${idfile}.json`,
|
||||||
|
info: ['PartialySuccessfull'],
|
||||||
|
moreinfo: `Following sheetNames does not exist :${invalidfor}`,
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVOIR TOUT CE QU'IL Y A EN DESSOUS et ré-écrire avec la logique de ggsheet2json phil 25/10/2021
|
||||||
|
/// ////////////////////////////////////////////////
|
||||||
|
const sleep = (milliseconds = 500) => new Promise(resolve => setTimeout(resolve, milliseconds))
|
||||||
|
/*
|
||||||
|
Process any data to a specific output:
|
||||||
|
emailer => generate a finale text file to send
|
||||||
|
csv => export json file to csv data
|
||||||
|
pdf => generate a customized document
|
||||||
|
*/
|
||||||
|
Outputs.envoiemail = async (msg, nowait, nbfois) => {
|
||||||
|
// logger.info('{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}');
|
||||||
|
// logger.info('msg to send', msg);
|
||||||
|
logger.info('nbfois', nbfois)
|
||||||
|
const transporter = nodemailer.createTransport(msg.smtp)
|
||||||
|
if (!nowait) {
|
||||||
|
logger.info('attente 1er msg avant d envoyer les autres')
|
||||||
|
const transport = await transporter.verify()
|
||||||
|
logger.info('transport', transport)
|
||||||
|
if (transport.error) {
|
||||||
|
logger.info('Probleme de smtp', error)
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const rep = await transporter.sendMail(msg)
|
||||||
|
logger.info('rep sendMail', rep)
|
||||||
|
if (rep.accepted && rep.accepted.length > 0 && rep.rejected.length === 0) {
|
||||||
|
fs.appendFileSync(`${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + ' - Success after waiting 1st email to send ' + msg.to + '\n', 'utf-8')
|
||||||
|
return {
|
||||||
|
status: '200',
|
||||||
|
payload: {
|
||||||
|
info: ['send1stemailok'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// status à tester si necessaire de detecter une erreur
|
||||||
|
// if (rep.rejectedErrors) {
|
||||||
|
fs.appendFileSync(`${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + ' - err after waiting result\n ' + msg.to, 'utf-8')
|
||||||
|
return {
|
||||||
|
status: '500',
|
||||||
|
payload: {
|
||||||
|
info: ['rejectedError'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
transporter.sendMail(msg, async (err, info) => {
|
||||||
|
if (err) {
|
||||||
|
if (nbfois < 4) {
|
||||||
|
logger.info('nouvelle tentative ', nbfois)
|
||||||
|
await sleep(600000) // attends 60sec pour faire une niéme tentative
|
||||||
|
Outputs.envoiemail(msg, true, nbfois + 1)
|
||||||
|
} else {
|
||||||
|
// logerror in file
|
||||||
|
logger.info('err', err)
|
||||||
|
fs.appendFileSync(`${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + ' - err after 4 tries to ' + info.rejected.join(',') + '\n', 'utf-8')
|
||||||
|
// logger.info('msg.to not well sent', msg.to);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info('info', info)
|
||||||
|
// logger.info('msg.to well sent', msg.to);
|
||||||
|
fs.appendFileSync(`${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + ' - Success after ' + nbfois + ' tries to ' + info.accepted.join(',') + '\n', 'utf-8')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// return something to not wait the rest of email
|
||||||
|
return {
|
||||||
|
status: '200',
|
||||||
|
payload: {
|
||||||
|
info: ['send1stemailok'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.generemsg = async (msg, header) => {
|
||||||
|
/*
|
||||||
|
wait msg sent and return result sent
|
||||||
|
*/
|
||||||
|
// Recupere les parametre smtp du domainName à utiliser
|
||||||
|
logger.info('pass Outputs.generemsg')
|
||||||
|
try {
|
||||||
|
const confclientexpediteur = jsonfile.readFileSync(`${config.tribes}/${msg.tribeidperso.tribeidexpediteur}/clientconf.json`)
|
||||||
|
// logger.info('expediteur', confclientexpediteur);
|
||||||
|
msg.smtp = confclientexpediteur.smtp
|
||||||
|
/* const confclient = jsonfile.readFileSync(
|
||||||
|
`${config.tribes}/${msg.tribeidperso.tribeid}/clientconf.json`
|
||||||
|
); */
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('la conf smtp du client n\'est pas definie')
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['smtpnotdefined'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info(msg)
|
||||||
|
if (!msg.template.sendascontent && msg.template.htmlfile) {
|
||||||
|
try {
|
||||||
|
msg.template.html = fs.readFileSync(config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8')
|
||||||
|
msg.template.text = fs.readFileSync(config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8')
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile)
|
||||||
|
logger.info(err)
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['fileUnknown'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg.template.html.length === 0) {
|
||||||
|
logger.info('template.html est vide')
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['ERRnotemplate'],
|
||||||
|
model: 'Outputs',
|
||||||
|
moreinfo: 'No template email check '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mustache any data into
|
||||||
|
// logger.info(msg);
|
||||||
|
const msg2send = {}
|
||||||
|
msg2send.smtp = msg.smtp
|
||||||
|
msg2send.from = msg.tribeidperso.from
|
||||||
|
if (msg.tribeidperso.cc) msg2send.cc = msg.tribeidperso.cc
|
||||||
|
if (msg.tribeidperso.bcc) msg2send.bcc = msg.tribeidperso.bcc
|
||||||
|
if (msg.tribeidperso.replyTo) msg2send.replyTo = msg.tribeidperso.replyTo
|
||||||
|
msg2send.headers = {
|
||||||
|
'x-campaign-id': msg.tribeidperso.messageId,
|
||||||
|
'x-client-nd-id': msg.tribeidperso.tribeid,
|
||||||
|
'x-template-nd-id': msg.tribeidperso.templateId
|
||||||
|
}
|
||||||
|
// we get in datacust= {tribeidperso: with clientconf,destperso: personnalise data to send for email}
|
||||||
|
// logger.info(msg);
|
||||||
|
logger.info('nb de message to send:', msg.destperso.length)
|
||||||
|
// logger.info(msg.destperso);
|
||||||
|
// msg.destperso.forEach(async (perso, pos) => {
|
||||||
|
let pos
|
||||||
|
let pass1ermsg = false
|
||||||
|
for (pos = 0; pos < msg.destperso.length; pos++) {
|
||||||
|
const datacust = {
|
||||||
|
tribeidperso: msg.tribeidperso,
|
||||||
|
destperso: msg.destperso[pos]
|
||||||
|
}
|
||||||
|
// Evaluation of each field if mustache exist
|
||||||
|
const datacusteval = {}
|
||||||
|
Object.keys(datacust.tribeidperso)
|
||||||
|
.forEach(k => {
|
||||||
|
if (typeof datacust.tribeidperso[k] ==== 'string' || datacust.tribeidperso[k] instanceof String) {
|
||||||
|
datacusteval[k] = mustache.render(datacust.tribeidperso[k], datacust)
|
||||||
|
} else {
|
||||||
|
datacusteval[k] = datacust.tribeidperso[k]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Object.keys(datacust.destperso)
|
||||||
|
.forEach(k => {
|
||||||
|
if (typeof datacust.destperso[k] ==== 'string' || datacust.destperso[k] instanceof String) {
|
||||||
|
datacusteval[k] = mustache.render(datacust.destperso[k], datacust)
|
||||||
|
} else {
|
||||||
|
datacusteval[k] = datacust.destperso[k]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
msg2send.to = msg.destperso[pos].email
|
||||||
|
logger.info('msg2send.to ' + msg2send.to + ' pos:' + pos)
|
||||||
|
// logger.info('avec datacusteval ', datacusteval)
|
||||||
|
msg2send.subject = mustache.render(msg.template.subject, datacusteval)
|
||||||
|
msg2send.text = mustache.render(msg.template.text, datacusteval)
|
||||||
|
msg2send.html = mustache.render(msg.template.html, datacusteval)
|
||||||
|
let nowait = true
|
||||||
|
if (config.emailerurl === 'http://devapia.maildigit.fr:3015') {
|
||||||
|
fs.writeFileSync('devdata/tmp/test.html', msg2send.html, 'utf-8')
|
||||||
|
logger.info('lancement email sur dev, pour controler le mail générer voir ds ./config.js il faut changer config.emailerurl avec https://mail.maildigit.fr pour envoyer le mail ')
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['msgsentok'],
|
||||||
|
model: 'Outputs',
|
||||||
|
moreinfo: 'parametrer sur emailer de dev et pas de production'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pos === 0) {
|
||||||
|
nowait = false
|
||||||
|
/* we are waiting the first email was sent ok then we send all other
|
||||||
|
check NEEDDATA/OVH/workspace/emailerovh to send emailer with nodemailer and nodemailer-smtp-transport
|
||||||
|
*/
|
||||||
|
// logger.info('envoie msg', msg);
|
||||||
|
// logger.info(msg2send);
|
||||||
|
const ret = await Outputs.envoiemail(msg2send, nowait, 0)
|
||||||
|
logger.info('ret 1er msg', ret)
|
||||||
|
if (ret.status === 200) {
|
||||||
|
pass1ermsg = true
|
||||||
|
};
|
||||||
|
} else if (pass1ermsg) {
|
||||||
|
logger.info('###############################################')
|
||||||
|
logger.info('envoie msg numero: ' + pos + ' email: ' + msg2send.to)
|
||||||
|
// logger.info(msg2send)
|
||||||
|
Outputs.envoiemail(msg2send, nowait, 0)
|
||||||
|
/* Outputs.envoiemail(msg2send, nowait, 0).then(rep => {
|
||||||
|
logger.info("envoie email" + pos)
|
||||||
|
}).catch(err => {
|
||||||
|
logger.info(err);
|
||||||
|
}); */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (pass1ermsg) {
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['msgsentok'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['msgsentko'],
|
||||||
|
model: 'Ouputs',
|
||||||
|
moreinfo: '1er msg non envoyé car erreur'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.sendMailcampain = async (msg, headersmsg) => {
|
||||||
|
/*
|
||||||
|
Permet de lancer une campagne de mail personnalisé en mode web service
|
||||||
|
avec axios config.emailerurl https://mail qui peut être sur un autre serveur que celui en cours
|
||||||
|
Attention headermsg doit être retraduit avec les champs envoyé par un navigateur
|
||||||
|
Si le serveur en cours appelle cette fonction les champs du header doivent changer
|
||||||
|
voir config.js node_env .exposedHeaders
|
||||||
|
|
||||||
|
Pour un exemple de msg voir u exemple type de message envoyé dans un tribeid/domain/clientconf.json
|
||||||
|
avec l'envoi d'email
|
||||||
|
*/
|
||||||
|
// logger.info(msg)
|
||||||
|
// On ajoute le contenu du template directement dans la demande
|
||||||
|
if (msg.template.sendascontent && msg.template.htmlfile) {
|
||||||
|
try {
|
||||||
|
logger.info('test', msg.template.sendascontent)
|
||||||
|
msg.template.html = fs.readFileSync(config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8')
|
||||||
|
msg.template.text = fs.readFileSync(config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8')
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile)
|
||||||
|
// logger.info(err);
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['fileUnknown'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete msg.template.htmlfile
|
||||||
|
if (msg.template.html.length === 0) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['ERRnotemplate'],
|
||||||
|
model: 'Outputs',
|
||||||
|
moreinfo: 'No template email check '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info('envoie sur', `${config.emailerurl}/outputs/msg`)
|
||||||
|
// logger.info(msg)
|
||||||
|
// on check si les key de headermsg sont des key traduite via exposedHeaders
|
||||||
|
// (cas ou c'est l'application qui envoie un email)
|
||||||
|
if (headersmsg.xtribeid) {
|
||||||
|
Object.keys(config.exposedHeaders)
|
||||||
|
.forEach(h => {
|
||||||
|
headersmsg[h] = headersmsg[config.exposedHeaders[h]]
|
||||||
|
delete headersmsg[config.exposedHeaders[h]]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// on ajoute le code pour la signature
|
||||||
|
headersmsg.hashbody = msg.code
|
||||||
|
logger.info('header after traduction: ', headersmsg)
|
||||||
|
try {
|
||||||
|
const resCamp = await axios.post(`${config.emailerurl}/outputs/msg`, msg, {
|
||||||
|
headers: headersmsg
|
||||||
|
})
|
||||||
|
// logger.info('Etat:', resCamp);
|
||||||
|
logger.info('Tried to send 1st email of the campain ' + msg.destperso[0].email)
|
||||||
|
// it just check the 1st email in destperso to return an answer if 1st is ok then all other are send in queue
|
||||||
|
if (resCamp) {
|
||||||
|
return resCamp
|
||||||
|
} else {
|
||||||
|
return { status: 200, payload: { info: ['CampainSent'], model: 'Outputs' } }
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// aios error handler
|
||||||
|
return { status: 401, payload: { info: ['erreuraxios'], model: 'Outputs', moreinfo: err.message } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.get = function (filename, header) {
|
||||||
|
// check file exist
|
||||||
|
const file = `${config.tribes}/${header.xworkon}/${filename}`
|
||||||
|
// logger.info('fichier demande ', file);
|
||||||
|
if (!fs.existsSync(file)) {
|
||||||
|
// logger.info('le fichier demande n existe pas ', file);
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['fileUnknown'],
|
||||||
|
model: 'UploadFiles'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info('envoie le fichier ', file)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['fileknown'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.addjson = function (data, header) {
|
||||||
|
/*
|
||||||
|
Le header = {X-WorkOn:"",destinationfile:"", filename:""}
|
||||||
|
Le body = {jsonp:{},callback:function to launch after download,'code':'mot cle pour verifier que le fichier est à garder'}
|
||||||
|
*/
|
||||||
|
// logger.info(req.body.jsonp);
|
||||||
|
try {
|
||||||
|
jsonfile.writeFileSync(header.destinationfile + '/' + header.filename, data.jsonp)
|
||||||
|
if (data.callback) {
|
||||||
|
const execCB = require(`${config.mainDir}/models/tribeid/${header.xworkon
|
||||||
|
}`)
|
||||||
|
execCB[data.callback]()
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['wellUpload'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
render: {
|
||||||
|
destination: header.destinationfile,
|
||||||
|
filename: header.filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Impossible de sauvegarder le fichier, A COMPRENDRE', err)
|
||||||
|
return {
|
||||||
|
status: 503,
|
||||||
|
payload: {
|
||||||
|
info: ['savingError'],
|
||||||
|
model: 'UploadFiles'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.add = function (req, header) {
|
||||||
|
const form = new formidable.IncomingForm()
|
||||||
|
logger.info('req.headers', req.headers)
|
||||||
|
logger.info('req.params', req.params)
|
||||||
|
logger.info('req.query', req.query)
|
||||||
|
logger.info('req.body', req.body)
|
||||||
|
let destinationfile = `${config.tribes}/${header.xworkon}/${header.destinationfile
|
||||||
|
}`
|
||||||
|
form.parse(req, function (err, fields, files) {
|
||||||
|
logger.info('files', files.file.path)
|
||||||
|
logger.info('fields', fields)
|
||||||
|
const oldpath = files.file.path
|
||||||
|
destinationfile += '/' + files.file.name
|
||||||
|
logger.info('oldpath', oldpath)
|
||||||
|
logger.info('destinationfile', destinationfile)
|
||||||
|
fs.copyFile(oldpath, destinationfile, function (err) {
|
||||||
|
if (err) {
|
||||||
|
logger.info(err)
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['savingError'],
|
||||||
|
model: 'UploadFiles'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info('passe')
|
||||||
|
fs.unlink(oldpath)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['wellUpload'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
render: {
|
||||||
|
destination: destinationfile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Outputs.generepdf = (req, header) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const options = {
|
||||||
|
format: 'A4',
|
||||||
|
orientation: 'portrait',
|
||||||
|
border: '10mm',
|
||||||
|
footer: {
|
||||||
|
height: '20mm',
|
||||||
|
contents: {
|
||||||
|
default: '<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>' // html pagination if edit needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const document = {
|
||||||
|
html: req.html,
|
||||||
|
data: {
|
||||||
|
data: req.data
|
||||||
|
},
|
||||||
|
path: `${config.tribes}/${header.xtribe}/outputs/${UUID.v4()}.pdf`,
|
||||||
|
type: ''
|
||||||
|
}
|
||||||
|
pdf // generate pdf
|
||||||
|
.create(document, options)
|
||||||
|
.then((res) => {
|
||||||
|
resolve({
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['wellPdfGenerated'],
|
||||||
|
model: 'Outputs',
|
||||||
|
data: {
|
||||||
|
path: document.path,
|
||||||
|
filename: req.data.filename
|
||||||
|
},
|
||||||
|
render: {
|
||||||
|
filename: req.data.filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject({
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['pdfGenerationError'],
|
||||||
|
model: 'Outputs',
|
||||||
|
error: err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
module.exports = Outputs
|
494
src/models/OutputsDev.js
Executable file
494
src/models/OutputsDev.js
Executable file
@ -0,0 +1,494 @@
|
|||||||
|
const fs = require('fs-extra')
|
||||||
|
const path = require('path')
|
||||||
|
const formidable = require('formidable')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const mustache = require('mustache')
|
||||||
|
const moment = require('moment')
|
||||||
|
const UUID = require('uuid')
|
||||||
|
const pdf = require('pdf-creator-node')
|
||||||
|
const nodemailer = require('nodemailer')
|
||||||
|
const smtpTransport = require('nodemailer-smtp-transport')
|
||||||
|
const axios = require('axios')
|
||||||
|
const { GoogleSpreadsheet } = require('google-spreadsheet')
|
||||||
|
const async = require('async')
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
const checkdata = require(`${config.tribes}/${config.mayorId}/www/cdn/public/js/checkdata`)
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
const Outputs = {}
|
||||||
|
const sleep = (milliseconds = 500) => new Promise(resolve => setTimeout(resolve, milliseconds))
|
||||||
|
/*
|
||||||
|
Process any data to a specific output:
|
||||||
|
emailer => generate a finale text file to send
|
||||||
|
csv => export json file to csv data
|
||||||
|
pdf => generate a customized document
|
||||||
|
*/
|
||||||
|
Outputs.envoiemail = (msg, next) => {
|
||||||
|
const transporter = nodemailer.createTransport(msg.smtp)
|
||||||
|
transporter.sendMail(msg, async (err, info) => {
|
||||||
|
if (err) {
|
||||||
|
next(err)
|
||||||
|
} else {
|
||||||
|
logger.info('info', info)
|
||||||
|
fs.appendFileSync(`${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + ' - Success after ' + '0' + ' tries to ' + info.accepted.join(',') + '\n', 'utf-8')
|
||||||
|
next(null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Outputs.setupmail = (msg, msg2send, index) => {
|
||||||
|
const datacust = {
|
||||||
|
tribeidperso: msg.tribeidperso,
|
||||||
|
destperso: index
|
||||||
|
}
|
||||||
|
// Evaluation of each field if mustache exist
|
||||||
|
const datacusteval = {}
|
||||||
|
Object.keys(datacust.tribeidperso)
|
||||||
|
.forEach(k => {
|
||||||
|
if (typeof datacust.tribeidperso[k] ==== 'string' || datacust.tribeidperso[k] instanceof String) {
|
||||||
|
datacusteval[k] = mustache.render(datacust.tribeidperso[k], datacust)
|
||||||
|
} else {
|
||||||
|
datacusteval[k] = datacust.tribeidperso[k]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Object.keys(datacust.destperso)
|
||||||
|
.forEach(k => {
|
||||||
|
if (typeof datacust.destperso[k] ==== 'string' || datacust.destperso[k] instanceof String) {
|
||||||
|
datacusteval[k] = mustache.render(datacust.destperso[k], datacust)
|
||||||
|
} else {
|
||||||
|
datacusteval[k] = datacust.destperso[k]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
msg2send.to = index.email
|
||||||
|
logger.info('msg2send.to ' + msg2send.to)
|
||||||
|
msg2send.subject = mustache.render(msg.template.subject, datacusteval)
|
||||||
|
msg2send.text = mustache.render(msg.template.text, datacusteval)
|
||||||
|
msg2send.html = mustache.render(msg.template.html, datacusteval)
|
||||||
|
// TODO need to move that in generemsg
|
||||||
|
// if (config.emailerurl === 'http://devapia.maildigit.fr:3015') {
|
||||||
|
// fs.writeFileSync('devdata/tmp/test.html', msg2send.html, 'utf-8');
|
||||||
|
// logger.info('lancement email sur dev, pour controler le mail générer voir ds ./config.js il faut changer config.emailerurl avec https://mail.maildigit.fr pour envoyer le mail ')
|
||||||
|
// return {
|
||||||
|
// status: 200,
|
||||||
|
// payload: {
|
||||||
|
// info: ['msgsentok'],
|
||||||
|
// model: 'Outputs',
|
||||||
|
// moreinfo: "parametrer sur emailer de dev et pas de production"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return msg2send
|
||||||
|
}
|
||||||
|
Outputs.envoiefirstmail = async (msg) => {
|
||||||
|
logger.info('###############################################')
|
||||||
|
logger.info('envoie first msg email: ' + msg.to)
|
||||||
|
const transporter = nodemailer.createTransport(msg.smtp)
|
||||||
|
logger.info('attente 1er msg avant d envoyer les autres')
|
||||||
|
const transport = await transporter.verify()
|
||||||
|
logger.info('transport', transport)
|
||||||
|
if (transport.error) {
|
||||||
|
logger.info('Probleme de smtp', error)
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const rep = await transporter.sendMail(msg)
|
||||||
|
logger.info('rep sendMail', rep)
|
||||||
|
if (rep.accepted && rep.accepted.length > 0 && rep.rejected.length === 0) {
|
||||||
|
fs.appendFileSync(`${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_success.txt`, moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + ' - Success after waiting 1st email to send ' + msg.to + '\n', 'utf-8')
|
||||||
|
return {
|
||||||
|
status: '200',
|
||||||
|
payload: {
|
||||||
|
info: ['send1stemailok'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// status à tester si necessaire de detecter une erreur
|
||||||
|
// if (rep.rejectedErrors) {
|
||||||
|
fs.appendFileSync(`${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + ' - err after waiting result\n ' + msg.to, 'utf-8')
|
||||||
|
return {
|
||||||
|
status: '500',
|
||||||
|
payload: {
|
||||||
|
info: ['rejectedError'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.envoiemails = (msg, msg2send, targets, iteration, resolve, reject) => {
|
||||||
|
const newtargets = []
|
||||||
|
async.each(targets, (index, callback) => { // iterate asynchronously in msg.destperso (targets)
|
||||||
|
const finalmsg = Outputs.setupmail(msg, msg2send, index)
|
||||||
|
logger.info('###############################################')
|
||||||
|
logger.info('envoie msg email: ' + finalmsg.to)
|
||||||
|
Outputs.envoiemail(finalmsg, (err) => {
|
||||||
|
if (err) { // intentionally don't pass this error in callback, we dont want to break loop
|
||||||
|
newtargets.push(index) // stock all errored mails for next try
|
||||||
|
}
|
||||||
|
})
|
||||||
|
callback()
|
||||||
|
}, function (err) { // part executed only once all iterations are done
|
||||||
|
if (newtargets.length > 0 && iteration < 4) {
|
||||||
|
setTimeout(() => {
|
||||||
|
Outputs.envoiemails(msg, msg2send, newtargets, iteration + 1, resolve, reject)
|
||||||
|
}, 600000)
|
||||||
|
} else resolve(newtargets) // return not resolved errors after 4 trys for log
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Outputs.generemsg = async (msg, header) => {
|
||||||
|
/*
|
||||||
|
wait msg sent and return result sent
|
||||||
|
*/
|
||||||
|
// Recupere les parametre smtp du domainName à utiliser
|
||||||
|
logger.info('pass Outputs.generemsg')
|
||||||
|
try {
|
||||||
|
const confclientexpediteur = jsonfile.readFileSync(`${config.tribes}/${msg.tribeidperso.tribeidexpediteur}/clientconf.json`)
|
||||||
|
msg.smtp = confclientexpediteur.smtp
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('la conf smtp du client n\'est pas definie')
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['smtpnotdefined'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info(msg)
|
||||||
|
if (!msg.template.sendascontent && msg.template.htmlfile) {
|
||||||
|
try {
|
||||||
|
msg.template.html = fs.readFileSync(config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8')
|
||||||
|
msg.template.text = fs.readFileSync(config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8')
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile)
|
||||||
|
logger.info(err)
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['fileUnknown'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg.template.html.length === 0) {
|
||||||
|
logger.info('template.html est vide')
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['ERRnotemplate'],
|
||||||
|
model: 'Outputs',
|
||||||
|
moreinfo: 'No template email check '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mustache any data into
|
||||||
|
const msg2send = {}
|
||||||
|
msg2send.smtp = msg.smtp
|
||||||
|
msg2send.from = msg.tribeidperso.from
|
||||||
|
if (msg.tribeidperso.cc) msg2send.cc = msg.tribeidperso.cc
|
||||||
|
if (msg.tribeidperso.bcc) msg2send.bcc = msg.tribeidperso.bcc
|
||||||
|
if (msg.tribeidperso.replyTo) msg2send.replyTo = msg.tribeidperso.replyTo
|
||||||
|
msg2send.headers = {
|
||||||
|
'x-campaign-id': msg.tribeidperso.messageId,
|
||||||
|
'x-client-nd-id': msg.tribeidperso.tribeid,
|
||||||
|
'x-template-nd-id': msg.tribeidperso.templateId
|
||||||
|
}
|
||||||
|
logger.info('nb de message to send:', msg.destperso.length)
|
||||||
|
// send first mail
|
||||||
|
const ret = await Outputs.envoiefirstmail(Outputs.setupmail(msg, msg2send, msg.destperso[0]))
|
||||||
|
logger.info('ret 1er msg', ret)
|
||||||
|
if (ret.status === 200) {
|
||||||
|
pass1ermsg = true
|
||||||
|
msg.destperso.shift()
|
||||||
|
};
|
||||||
|
logger.info('attente 1er msg avant d envoyer les autres')
|
||||||
|
// send other mails
|
||||||
|
new Promise((resolve, reject) => { // useless promise used for recursive calls in Outputs.envoiemails
|
||||||
|
Outputs.envoiemails(msg, msg2send, msg.destperso, 0, resolve, reject)
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
async.eachSeries(result, (index, callback) => { // variante of each but runs only a single operation at a time because of fs.appendFile
|
||||||
|
fs.appendFile(`${config.tribes}/${msg.headers['x-client-nd-id']}/logs/${msg.headers['x-campaign-id']}_error.txt`, moment(new Date())
|
||||||
|
.format('YYYYMMDD HH:mm:ss') + ' - err after 4 tries to ' + info.rejected.join(',') + '\n', 'utf-8', (err) => {
|
||||||
|
callback(err)
|
||||||
|
}, (err) => {
|
||||||
|
if (err) logger.info(err)
|
||||||
|
})
|
||||||
|
logger.info('msg.to not well sent', msg.to)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (pass1ermsg) {
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['msgsentok'],
|
||||||
|
model: 'Outputs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['msgsentko'],
|
||||||
|
model: 'Ouputs',
|
||||||
|
moreinfo: '1er msg non envoyé car erreur'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.sendMailcampain = async (msg, headersmsg) => {
|
||||||
|
/*
|
||||||
|
Permet de lancer une campagne de mail personnalisé en mode web service
|
||||||
|
avec axios config.emailerurl https://mail qui peut être sur un autre serveur que celui en cours
|
||||||
|
Attention headermsg doit être retraduit avec les champs envoyé par un navigateur
|
||||||
|
Si le serveur en cours appelle cette fonction les champs du header doivent changer
|
||||||
|
voir config.js node_env .exposedHeaders
|
||||||
|
|
||||||
|
Pour un exemple de msg voir u exemple type de message envoyé dans un tribeid/domain/clientconf.json
|
||||||
|
avec l'envoi d'email
|
||||||
|
*/
|
||||||
|
// logger.info(msg)
|
||||||
|
// On ajoute le contenu du template directement dans la demande
|
||||||
|
if (msg.template.sendascontent && msg.template.htmlfile) {
|
||||||
|
try {
|
||||||
|
logger.info('test', msg.template.sendascontent)
|
||||||
|
msg.template.html = fs.readFileSync(config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache', 'utf-8')
|
||||||
|
msg.template.text = fs.readFileSync(config.sharedData + '/' + msg.template.htmlfile + '/contenttxt.mustache', 'utf-8')
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('WARNING, html file template missing ' + config.sharedData + '/' + msg.template.htmlfile)
|
||||||
|
// logger.info(err);
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['fileUnknown'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
moreinfo: 'Template unavailable, check ' + config.sharedData + '/' + msg.template.htmlfile + '/contentinline.mustache and contenttxt.mustache'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete msg.template.htmlfile
|
||||||
|
if (msg.template.html.length === 0) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['ERRnotemplate'],
|
||||||
|
model: 'Outputs',
|
||||||
|
moreinfo: 'No template email check '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info('envoie sur', `${config.emailerurl}/outputs/msg`)
|
||||||
|
// logger.info(msg)
|
||||||
|
// on check si les key de headermsg sont des key traduite via exposedHeaders
|
||||||
|
// (cas ou c'est l'application qui envoie un email)
|
||||||
|
if (headersmsg.xtribeid) {
|
||||||
|
Object.keys(config.exposedHeaders)
|
||||||
|
.forEach(h => {
|
||||||
|
headersmsg[h] = headersmsg[config.exposedHeaders[h]]
|
||||||
|
delete headersmsg[config.exposedHeaders[h]]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// on ajoute le code pour la signature
|
||||||
|
headersmsg.hashbody = msg.code
|
||||||
|
logger.info('header after traduction: ', headersmsg)
|
||||||
|
try {
|
||||||
|
const resCamp = await axios.post(`${config.emailerurl}/outputs/msg`, msg, {
|
||||||
|
headers: headersmsg
|
||||||
|
})
|
||||||
|
// logger.info('Etat:', resCamp);
|
||||||
|
logger.info('Tried to send 1st email of the campain ' + msg.destperso[0].email)
|
||||||
|
// it just check the 1st email in destperso to return an answer if 1st is ok then all other are send in queue
|
||||||
|
if (resCamp) {
|
||||||
|
return resCamp
|
||||||
|
} else {
|
||||||
|
return { status: 200, payload: { info: ['CampainSent'], model: 'Outputs' } }
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// aios error handler
|
||||||
|
return { status: 401, payload: { info: ['erreuraxios'], model: 'Outputs', moreinfo: err.message } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.get = function (filename, header) {
|
||||||
|
// check file exist
|
||||||
|
const file = `${config.tribes}/${header.xworkon}/${filename}`
|
||||||
|
// logger.info('fichier demande ', file);
|
||||||
|
if (!fs.existsSync(file)) {
|
||||||
|
// logger.info('le fichier demande n existe pas ', file);
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
info: ['fileUnknown'],
|
||||||
|
model: 'UploadFiles'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info('envoie le fichier ', file)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['fileknown'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.addjson = function (data, header) {
|
||||||
|
/*
|
||||||
|
Le header = {X-WorkOn:"",destinationfile:"", filename:""}
|
||||||
|
Le body = {jsonp:{},callback:function to launch after download,'code':'mot cle pour verifier que le fichier est à garder'}
|
||||||
|
*/
|
||||||
|
// logger.info(req.body.jsonp);
|
||||||
|
try {
|
||||||
|
jsonfile.writeFileSync(header.destinationfile + '/' + header.filename, data.jsonp)
|
||||||
|
if (data.callback) {
|
||||||
|
const execCB = require(`${config.mainDir}/models/tribeid/${header.xworkon
|
||||||
|
}`)
|
||||||
|
execCB[data.callback]()
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['wellUpload'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
render: {
|
||||||
|
destination: header.destinationfile,
|
||||||
|
filename: header.filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Impossible de sauvegarder le fichier, A COMPRENDRE', err)
|
||||||
|
return {
|
||||||
|
status: 503,
|
||||||
|
payload: {
|
||||||
|
info: ['savingError'],
|
||||||
|
model: 'UploadFiles'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs.add = function (req, header) {
|
||||||
|
const form = new formidable.IncomingForm()
|
||||||
|
logger.info('req.headers', req.headers)
|
||||||
|
logger.info('req.params', req.params)
|
||||||
|
logger.info('req.query', req.query)
|
||||||
|
logger.info('req.body', req.body)
|
||||||
|
let destinationfile = `${config.tribes}/${header.xworkon}/${header.destinationfile
|
||||||
|
}`
|
||||||
|
form.parse(req, function (err, fields, files) {
|
||||||
|
logger.info('files', files.file.path)
|
||||||
|
logger.info('fields', fields)
|
||||||
|
const oldpath = files.file.path
|
||||||
|
destinationfile += '/' + files.file.name
|
||||||
|
logger.info('oldpath', oldpath)
|
||||||
|
logger.info('destinationfile', destinationfile)
|
||||||
|
fs.copyFile(oldpath, destinationfile, function (err) {
|
||||||
|
if (err) {
|
||||||
|
logger.info(err)
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['savingError'],
|
||||||
|
model: 'UploadFiles'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info('passe')
|
||||||
|
fs.unlink(oldpath)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['wellUpload'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
render: {
|
||||||
|
destination: destinationfile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Outputs.sheettojson = async (req, header) => {
|
||||||
|
doc = new GoogleSpreadsheet(req.productIdSpreadsheet)
|
||||||
|
await doc.useServiceAccountAuth(req.googleDriveCredentials)
|
||||||
|
await doc.loadInfo()
|
||||||
|
const result = []
|
||||||
|
for (const sheetName of req.sheets) {
|
||||||
|
logger.info('loading: ', sheetName)
|
||||||
|
sheet = doc.sheetsByTitle[sheetName]
|
||||||
|
await sheet.loadHeaderRow()
|
||||||
|
const records = await sheet.getRows({ offset: 1 })
|
||||||
|
records.forEach(record => {
|
||||||
|
const offer = {}
|
||||||
|
for (let index = 0; index < record._sheet.headerValues.length; index++) {
|
||||||
|
offer[record._sheet.headerValues[index]] = record[record._sheet.headerValues[index]]
|
||||||
|
}
|
||||||
|
result.push(offer)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
Outputs.generepdf = (req, header) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const options = {
|
||||||
|
format: 'A4',
|
||||||
|
orientation: 'portrait',
|
||||||
|
border: '10mm',
|
||||||
|
footer: {
|
||||||
|
height: '20mm',
|
||||||
|
contents: {
|
||||||
|
default: '<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>' // html pagination if edit needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const document = {
|
||||||
|
html: req.html,
|
||||||
|
data: {
|
||||||
|
data: req.data
|
||||||
|
},
|
||||||
|
path: `${config.tribes}/${header.xtribeid}/outputs/${UUID.v4()}.pdf`,
|
||||||
|
type: ''
|
||||||
|
}
|
||||||
|
pdf // generate pdf
|
||||||
|
.create(document, options)
|
||||||
|
.then((res) => {
|
||||||
|
resolve({
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['wellPdfGenerated'],
|
||||||
|
model: 'Outputs',
|
||||||
|
data: {
|
||||||
|
path: document.path,
|
||||||
|
filename: req.data.filename
|
||||||
|
},
|
||||||
|
render: {
|
||||||
|
filename: req.data.filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject({
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['pdfGenerationError'],
|
||||||
|
model: 'Outputs',
|
||||||
|
error: err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
module.exports = Outputs
|
779
src/models/Pagans.js
Executable file
779
src/models/Pagans.js
Executable file
@ -0,0 +1,779 @@
|
|||||||
|
/* eslint-disable no-tabs */
|
||||||
|
|
||||||
|
const bcrypt = require('bcrypt')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const glob = require('glob')
|
||||||
|
const moment = require('moment')
|
||||||
|
const jwt = require('jwt-simple')
|
||||||
|
const UUID = require('uuid')
|
||||||
|
const Outputs = require('./Outputs.js')
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
const checkdata = require('../nationchains/socialworld/contracts/checkdata.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gestion des utilisateurs connecte
|
||||||
|
|
||||||
|
/tribes/tribeid/users/
|
||||||
|
UUID.json
|
||||||
|
/searchindex/indexTOKEN = {TOKEN:UUID}
|
||||||
|
to check authentification
|
||||||
|
indexLOGIN = {LOGIN:UUID}
|
||||||
|
to check if LOGIN exist
|
||||||
|
|
||||||
|
Used for referntial:
|
||||||
|
To update
|
||||||
|
Global: datashared/referentials/dataManagement/object/users.json
|
||||||
|
Local: data/tribe/*${header.workOn}/referentials/dataManagement/object/users.json
|
||||||
|
To use after a server rerun
|
||||||
|
data/tribe/*${header.workOn}/referentials/${header.langue}/object/users.json
|
||||||
|
|
||||||
|
Faire comme contact charger les index au lancement
|
||||||
|
et changer la logique de user/id/profil o
|
||||||
|
|
||||||
|
On initialise les objets au lancement du serveur
|
||||||
|
this.uids[u.UUID] = [u.LOGIN, u.EMAIL, u.password, u.profil, u.TOKEN];
|
||||||
|
this.logins[u.LOGIN] = u.UUID;
|
||||||
|
this.tokens[u.UUID] = u.TOKEN;
|
||||||
|
on stocke /domain/tribeid/users/logins.json et /uids.json
|
||||||
|
on stocke /domain/tribeids.json et tokkens.json
|
||||||
|
|
||||||
|
On retourne l'objet {tribeids:[list tribeid], tokens:{UUID:TOKEN}}
|
||||||
|
*/
|
||||||
|
const Pagans = {}
|
||||||
|
Pagans.init = tribeids => {
|
||||||
|
console.group('init Pagans logins, tokens ...')
|
||||||
|
// store globaly tokens
|
||||||
|
const tokens = {}
|
||||||
|
const emailsglob = {}
|
||||||
|
const loginsglob = {}
|
||||||
|
// For each tribeid create series of indexes
|
||||||
|
// logger.info(tribeids);
|
||||||
|
tribeids.forEach(tribeid => {
|
||||||
|
// Reset for each domain
|
||||||
|
const uids = {}
|
||||||
|
const logins = {}
|
||||||
|
const emails = {}
|
||||||
|
// check folder exist
|
||||||
|
/* if( !fs.existsSync( `${config.tribes}/${tribeid}/users` ) ) {
|
||||||
|
fs.mkdirSync( `${config.tribes}/${tribeid}/users` )
|
||||||
|
}
|
||||||
|
if( !fs.existsSync( `${config.tribes}/${tribeid}/users/searchindex` ) ) {
|
||||||
|
fs.mkdirSync( `${config.tribes}/${tribeid}/users/searchindex` )
|
||||||
|
} */
|
||||||
|
glob.sync(`${config.tribes}/${tribeid}/users/*.json`)
|
||||||
|
.forEach(file => {
|
||||||
|
// logger.info( file );
|
||||||
|
const u = fs.readJsonSync(file, 'utf-8')
|
||||||
|
if (!u.TOKEN) {
|
||||||
|
u.TOKEN = ''
|
||||||
|
}
|
||||||
|
// logger.info( u )
|
||||||
|
uids[u.UUID] = [u.LOGIN, u.EMAIL, u.PASSWORD, u.ACCESSRIGHTS, u.TOKEN]
|
||||||
|
logins[u.LOGIN] = u.UUID
|
||||||
|
loginsglob[u.LOGIN] = tribeid
|
||||||
|
// On ne charge que les TOKEN valide
|
||||||
|
let decodedTOKEN = {}
|
||||||
|
if (u.TOKEN !== '') {
|
||||||
|
try {
|
||||||
|
decodedTOKEN = jwt.decode(u.TOKEN, config.jwtSecret)
|
||||||
|
// logger.info( 'decodeTOKEN', decodedTOKEN )
|
||||||
|
if (moment(decodedTOKEN.expiration) > moment()) {
|
||||||
|
tokens[u.UUID] = { TOKEN: u.TOKEN, ACCESSRIGHTS: u.ACCESSRIGHTS }
|
||||||
|
// logger.info( `add token valid for ${u.UUID}:`, tokens[ u.UUID ] )
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('pb de TOKEN impossible a decoder' + u.TOKEN, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (u.EMAIL) {
|
||||||
|
emails[u.EMAIL] = u.UUID
|
||||||
|
emailsglob[u.EMAIL] = tribeid
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// UIDS INDEX BY DOMAIN ={UUID:[LOGIN,EMAIL,psw,accessRight,TOKEN]}
|
||||||
|
fs.outputJson(`${config.tribes}/${tribeid}/users/searchindex/uids.json`, uids, {
|
||||||
|
spaces: 2
|
||||||
|
}, err => {
|
||||||
|
if (err) throw err
|
||||||
|
})
|
||||||
|
// LOGINS INDEX BY DOMAIN ={LOGIN:UUID}
|
||||||
|
fs.outputJson(`${config.tribes}/${tribeid}/users/searchindex/logins.json`, logins, {
|
||||||
|
spaces: 2
|
||||||
|
}, err => {
|
||||||
|
if (err) throw err
|
||||||
|
})
|
||||||
|
// EMAILS INDEX BY DOMAIN ={EMAIL:UUID}
|
||||||
|
fs.outputJson(`${config.tribes}/${tribeid}/users/searchindex/emails.json`, emails, {
|
||||||
|
spaces: 2
|
||||||
|
}, err => {
|
||||||
|
if (err) throw err
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// EMAILS et appartenance domain
|
||||||
|
fs.outputJson(`${config.tmp}/emailsglob.json`, emailsglob, {
|
||||||
|
spaces: 2
|
||||||
|
}, err => {
|
||||||
|
if (err) throw err
|
||||||
|
})
|
||||||
|
// logins et appartenance domain (permet de retrouver tribeid pour un LOGIN)
|
||||||
|
fs.outputJson(`${config.tmp}/loginsglob.json`, loginsglob, {
|
||||||
|
spaces: 2
|
||||||
|
}, err => {
|
||||||
|
if (err) throw err
|
||||||
|
})
|
||||||
|
// TOKENS GLOBAL INDEX Centralise tous les TOKEN pour plus de rapidité
|
||||||
|
// {UUID:TOKEN}
|
||||||
|
fs.outputJson(`${config.tmp}/tokens.json`, tokens, {
|
||||||
|
spaces: 2
|
||||||
|
}, err => {
|
||||||
|
if (err) throw err
|
||||||
|
})
|
||||||
|
console.groupEnd()
|
||||||
|
return { tokens }
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update indexes: logins, uids and tokens
|
||||||
|
* @param {object} user User object
|
||||||
|
* @param {string} tribeid - The client identifier
|
||||||
|
* @rm {boolean} false => update, true => remove
|
||||||
|
*/
|
||||||
|
Pagans.updateDatabase = (user, tribeid, rm = false) => {
|
||||||
|
console.group(`Pagans.updateDatabase for ${tribeid} with user ${user.LOGIN}`)
|
||||||
|
console.assert(config.loglevel === 'quiet', 'user', user)
|
||||||
|
const loginsIndex = `${config.tribes}/${tribeid}/users/searchindex/logins.json`
|
||||||
|
jsonfile.readFile(loginsIndex, function (err, logins) {
|
||||||
|
console.assert(config.loglevel === 'quiet', 'logins', logins)
|
||||||
|
try {
|
||||||
|
if (rm) {
|
||||||
|
delete logins[user.LOGIN]
|
||||||
|
} else {
|
||||||
|
logins[user.LOGIN] = user.UUID
|
||||||
|
}
|
||||||
|
jsonfile.writeFile(loginsIndex, logins, {
|
||||||
|
spaces: 2
|
||||||
|
}, err => {
|
||||||
|
if (err) logger.info(err)
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Gros pb de mise à jour Pagans.updateDatabase conflit des logins')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const uidsIndex = `${config.tribes}/${tribeid}/users/searchindex/uids.json`
|
||||||
|
jsonfile.readFile(uidsIndex, function (err, uids) {
|
||||||
|
try {
|
||||||
|
if (rm) {
|
||||||
|
delete uids[user.UUID]
|
||||||
|
} else {
|
||||||
|
uids[user.UUID] = [
|
||||||
|
user.LOGIN,
|
||||||
|
user.EMAIL,
|
||||||
|
user.PASSWORD,
|
||||||
|
user.ACCESSRIGHTS,
|
||||||
|
user.TOKEN
|
||||||
|
]
|
||||||
|
}
|
||||||
|
jsonfile.writeFile(uidsIndex, uids, {
|
||||||
|
spaces: 2
|
||||||
|
}, err => {
|
||||||
|
if (err) logger.info(err)
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Gros pb de mise à jour Pagans.updateDatabase conflit des uids si ce reproduit passer en mode sync bloquant')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emailsIndex = `${config.tribes}/${tribeid}/users/searchindex/emails.json`
|
||||||
|
jsonfile.readFile(emailsIndex, function (err, emails) {
|
||||||
|
console.assert(config.loglevel === 'quiet', 'emailss', emails)
|
||||||
|
try {
|
||||||
|
if (rm) {
|
||||||
|
delete emails[user.EMAIL]
|
||||||
|
} else {
|
||||||
|
emails[user.EMAIL] = user.UUID
|
||||||
|
}
|
||||||
|
jsonfile.writeFile(emailsIndex, emails, {
|
||||||
|
spaces: 2
|
||||||
|
}, err => {
|
||||||
|
if (err) logger.info(err)
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Gros pb de mise à jour Pagans.updateDatabase conflit des emails')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const tokensIndex = `${config.tmp}/tokens.json`
|
||||||
|
let tokens = {}
|
||||||
|
try {
|
||||||
|
tokens = jsonfile.readFileSync(tokensIndex)
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('tokens.json not available')
|
||||||
|
}
|
||||||
|
// tokens[user.UUID] = user.TOKEN;
|
||||||
|
tokens[user.UUID] = { TOKEN: user.TOKEN, ACCESSRIGHTS: user.ACCESSRIGHTS }
|
||||||
|
jsonfile.writeFileSync(tokensIndex, tokens, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
/*
|
||||||
|
jsonfile.readFile(tokensIndex, function(err, tokens) {
|
||||||
|
tokens[user.UUID] = user.TOKEN;
|
||||||
|
jsonfile.writeFile(tokensIndex, tokens, { spaces: 2 }, err => {
|
||||||
|
if (err) logger.info(err);
|
||||||
|
});
|
||||||
|
}); */
|
||||||
|
console.groupEnd()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Read the profil.json of an user
|
||||||
|
* @param {[type]} UUID ID of the user
|
||||||
|
* @param {[type]} tribeid tribeid
|
||||||
|
* @return {Promise} - Return a promise resolved with user data or rejected with an error
|
||||||
|
*/
|
||||||
|
// A S U P P R I M E R utiliser getinfousers pour recuperer des indexs de users
|
||||||
|
// en créer d'autres si necessaire
|
||||||
|
Pagans.getUserlist = (header, filter, field) => {
|
||||||
|
console.group(`getUserlist filter with filter ${filter} to get ${field}`)
|
||||||
|
/* const getuser = Pagans.getUser(header.xuuid, header.xtribeid);
|
||||||
|
if (getuser.status !== 200)
|
||||||
|
return { status: getuser.status, data: getuser.payload };
|
||||||
|
const user = getuser.payload.data;
|
||||||
|
// logger.info(user);
|
||||||
|
// check if update accessright allowed
|
||||||
|
// choose the level depending of ownby xuuid
|
||||||
|
let accessright = user.objectRights[header.xtribeid].users[0];
|
||||||
|
if (user.ownby.includes(header.xtribeid)) {
|
||||||
|
accessright = user.objectRights[header.xtribeid].users[1];
|
||||||
|
}
|
||||||
|
// Check update is possible at least user itself ownby itself
|
||||||
|
logger.info(accessright);
|
||||||
|
logger.info(accessright & 4);
|
||||||
|
if ((accessright & 4) !== 4) {
|
||||||
|
return {
|
||||||
|
status: 403,
|
||||||
|
data: { info: ['forbiddenAccess'], model: 'Pagans' }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const Userlist = []
|
||||||
|
glob.sync(`${config.tribes}/${header.xworkon}/users/*/profil.json`)
|
||||||
|
.forEach(f => {
|
||||||
|
const infouser = jsonfile.readFileSync(f)
|
||||||
|
// Ajouter filter et liste de field
|
||||||
|
if (filter !== 'all') {
|
||||||
|
// decode filter et test
|
||||||
|
}
|
||||||
|
const info = {}
|
||||||
|
field.split('______')
|
||||||
|
.forEach(k => {
|
||||||
|
if (!['password'].includes(k)) info[k] = infouser[k]
|
||||||
|
})
|
||||||
|
Userlist.push(info)
|
||||||
|
})
|
||||||
|
// logger.info('userlist', Userlist);
|
||||||
|
console.groupEnd()
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
data: Userlist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pagans.getinfoPagans = (tribeid, accessrights, listindex) => {
|
||||||
|
const info = {}
|
||||||
|
const object = 'users'
|
||||||
|
const indexs = listindex.split('_')
|
||||||
|
let access = true
|
||||||
|
indexs.forEach(index => {
|
||||||
|
access = !(['emails', 'logins', 'uids'].includes(index) && !(accessrights.data[object] && accessrights.data[object].includes('R')))
|
||||||
|
if (access && fs.existsSync(`${config.tribes}/${tribeid}/${object}/searchindex/${index}.json`)) {
|
||||||
|
info[index] = jsonfile.readFileSync(`${config.tribes}/${tribeid}/${object}/searchindex/${index}.json`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
logger.info(info)
|
||||||
|
return { status: 200, data: { info } }
|
||||||
|
}
|
||||||
|
Pagans.getUser = (UUID, tribeid, accessrights) => {
|
||||||
|
console.assert(config.loglevel === 'quiet', `getUser on ${UUID} for ${tribeid} with ${JSON.stringify(accessrights)}`)
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${tribeid}/users/${UUID}.json`)) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
data: {
|
||||||
|
info: ['useridNotfound'],
|
||||||
|
model: 'Pagans',
|
||||||
|
render: {
|
||||||
|
UUID,
|
||||||
|
tribeid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const user = jsonfile.readFileSync(`${config.tribes}/${tribeid}/users/${UUID}.json`)
|
||||||
|
let access = true
|
||||||
|
// logger.info("test accessrights.data['users'].includes('R')", accessrights.data['users'].includes('R'))
|
||||||
|
console.assert(config.loglevel === 'quiet', 'accessrights', accessrights)
|
||||||
|
access = accessrights.users && (accessrights.users.includes('R') || (accessrights.users.includes('O') && user.OWNEDBY.includes(UUID)))
|
||||||
|
if (access) {
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
user,
|
||||||
|
model: 'User',
|
||||||
|
info: ['Authorizedtogetuserinfo']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 403,
|
||||||
|
data: {
|
||||||
|
info: ['Forbidden'],
|
||||||
|
model: 'Pagans',
|
||||||
|
render: {
|
||||||
|
UUID,
|
||||||
|
tribeid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pagans.getUserIdFromEMAIL = (tribeid, EMAIL) => {
|
||||||
|
if (!checkdata.test.EMAIL(EMAIL)) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
data: {
|
||||||
|
info: ['ERREMAIL'],
|
||||||
|
model: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const emailsIndex = jsonfile.readFileSync(`${config.tribes}/${tribeid}/users/searchindex/emails.json`)
|
||||||
|
if (!emailsIndex.hasOwnProperty(EMAIL)) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
data: {
|
||||||
|
info: ['userEMAILNotfound'],
|
||||||
|
model: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emailsIndex[EMAIL]
|
||||||
|
}
|
||||||
|
Pagans.updateUserpassword = (UUID, header, data) => {
|
||||||
|
const getUser = Pagans.getUser(UUID, header.xtribeid, { users: 'W' })
|
||||||
|
if (getUser.status === 200) {
|
||||||
|
const user = getUser.data.user
|
||||||
|
// logger.info('user exist', user);
|
||||||
|
const match = bcrypt.compareSync(data.password, user.PASSWORD)
|
||||||
|
if (!match) {
|
||||||
|
return {
|
||||||
|
status: 401,
|
||||||
|
data: {
|
||||||
|
info: ['checkCredentials'],
|
||||||
|
model: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// logger.info('Credentials are matching!');
|
||||||
|
if (checkdata.test.password({}, data.pswnew)) {
|
||||||
|
user.PASSWORD = bcrypt.hashSync(data.pswnew, config.saltRounds)
|
||||||
|
jsonfile.writeFileSync(`${config.tribes}/${header.xworkon}/users/${UUID}.json`, user, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
Pagans.updateDatabase(user, header.xworkon, false)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
info: ['successfulUpdate'],
|
||||||
|
model: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: 401,
|
||||||
|
data: {
|
||||||
|
info: ['pswTooSimple'],
|
||||||
|
model: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pagans.createUser = (header, data) => {
|
||||||
|
/*
|
||||||
|
@input data={PUBKEY,EMAIL,LOGIN,UUID} check and create for header xworkon a user with generic password
|
||||||
|
*/
|
||||||
|
logger.info('createUser on header.xworkon:' + header.xworkon + ' by user:' + header.xpaganid)
|
||||||
|
console.assert(config.loglevel === 'quiet', 'with data:', data)
|
||||||
|
const ref = jsonfile.readFileSync(`${config.tribes}/${header.xworkon}/referentials/${header.xlang}/object/users.json`)
|
||||||
|
const logins = jsonfile.readFileSync(`${config.tribes}/${header.xworkon}/users/searchindex/logins.json`)
|
||||||
|
const LOGIN = Object.keys(logins)
|
||||||
|
console.assert(config.loglevel === 'quiet', 'LOGIN list', LOGIN)
|
||||||
|
const emails = jsonfile.readFileSync(`${config.tribes}/${header.xworkon}/users/searchindex/emails.json`)
|
||||||
|
console.assert(config.loglevel === 'quiet', 'emails', emails)
|
||||||
|
const EMAIL = Object.keys(emails)
|
||||||
|
console.assert(config.loglevel === 'quiet', 'EMAIL list', EMAIL)
|
||||||
|
// list.UUID est forcement unique car on est en update et pas en create
|
||||||
|
if (!data.UUID) data.UUID = UUID.v4()
|
||||||
|
// pour la logique de checkdata il faut passer le parametre
|
||||||
|
const check = checkdata.evaluate({
|
||||||
|
list: {
|
||||||
|
LOGIN,
|
||||||
|
EMAIL,
|
||||||
|
UUID: []
|
||||||
|
}
|
||||||
|
}, ref, data)
|
||||||
|
console.assert(config.loglevel === 'quiet', 'check & clean data before update ', check)
|
||||||
|
if (check.invalidefor.length > 0) {
|
||||||
|
return {
|
||||||
|
status: 403,
|
||||||
|
data: {
|
||||||
|
model: 'Pagans',
|
||||||
|
info: check.invalidefor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const clientConfig = jsonfile.readFileSync(`${config.tribes}/${header.xworkon}/clientconf.json`)
|
||||||
|
const user = check.data
|
||||||
|
user.DATE_CREATE = new Date()
|
||||||
|
.toISOString()
|
||||||
|
user.PASSWORD = bcrypt.hashSync(clientConfig.genericpsw, config.saltRounds)
|
||||||
|
user.OWNBY = [data.UUID]
|
||||||
|
user.OBJECT = 'users'
|
||||||
|
// set le minimum de droit sur l'objet users dont il est le Owner
|
||||||
|
if (data.ACCESSRIGHTS) {
|
||||||
|
user.ACCESSRIGHTS = data.ACCESSRIGHTS
|
||||||
|
} else {
|
||||||
|
user.ACCESSRIGHTS = { app: {}, data: {} }
|
||||||
|
}
|
||||||
|
user.ACCESSRIGHTS.data[header.xworkon] = { users: 'O' }
|
||||||
|
jsonfile.writeFileSync(`${config.tribes}/${header.xworkon}/users/${user.UUID}.json`, user, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
Pagans.updateDatabase(user, header.xworkon, false)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
uuid: user.UUID,
|
||||||
|
info: ['successfulCreate'],
|
||||||
|
model: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pagans.updateUser = (UUID, header, data) => {
|
||||||
|
logger.info('updateUser UUID:' + UUID + ' on header.xworkon:' + header.xworkon + ' by user' + header.xpaganid)
|
||||||
|
// logger.info('header', header);
|
||||||
|
console.assert(config.loglevel === 'quiet', 'with data', data)
|
||||||
|
const getuser = Pagans.getUser(UUID, header.xworkon, { users: 'R' })
|
||||||
|
if (getuser.status !== 200) {
|
||||||
|
return {
|
||||||
|
status: getuser.status,
|
||||||
|
data: getuser.data.user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const user = getuser.data.user
|
||||||
|
/*
|
||||||
|
let userconnected = user;
|
||||||
|
if (UUID !== header.xuuid) {
|
||||||
|
// mean connected user want to change other user
|
||||||
|
const getuserconnected = Pagans.getUser(header.xuuid, header.xtribeid);
|
||||||
|
userconnected = getuserconnected.payload.data;
|
||||||
|
}
|
||||||
|
logger.info('user to update', user);
|
||||||
|
logger.info('user connected that request update', userconnected);
|
||||||
|
|
||||||
|
// check if update accessright allowed
|
||||||
|
// choose the level depending of ownby xuuid
|
||||||
|
let accessright = userconnected.objectRights[header.xworkon].users[0];
|
||||||
|
if (user.ownby.includes(header.xuuid)) {
|
||||||
|
accessright = userconnected.objectRights[header.xworkon].users[1];
|
||||||
|
}
|
||||||
|
// Check update is possible at least user itself ownby itself
|
||||||
|
logger.info(accessright);
|
||||||
|
logger.info(accessright & 2);
|
||||||
|
if ((accessright & 2) !== 2) {
|
||||||
|
return {
|
||||||
|
status: 403,
|
||||||
|
data: { info: ['forbiddenAccess'], model: 'Pagans' }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const ref = jsonfile.readFileSync(`${config.tribes}/${header.xworkon}/referentials/object/users_${
|
||||||
|
header.xlang
|
||||||
|
}.json`)
|
||||||
|
const logins = jsonfile.readFileSync(`${config.tribes}/${header.xworkon}/users/searchindex/logins.json`)
|
||||||
|
const LOGIN = Object.keys(logins)
|
||||||
|
.filter(l => logins[l] !== user.UUID)
|
||||||
|
// logger.info( 'LOGIN list', LOGIN );
|
||||||
|
const emails = jsonfile.readFileSync(`${config.tribes}/${header.xworkon}/users/searchindex/emails.json`)
|
||||||
|
// logger.info( 'emails', emails );
|
||||||
|
const EMAIL = Object.keys(emails)
|
||||||
|
.filter(e => emails[e] !== user.UUID)
|
||||||
|
// logger.info( 'EMAIL list', EMAIL );
|
||||||
|
// list.UUID est forcement unique car on est en update et pas en create
|
||||||
|
// pour la logique de checkdata il faut passer le parametre
|
||||||
|
const check = checkdata.evaluate({
|
||||||
|
profil: user['apps' + header.xworkon + 'profil'],
|
||||||
|
list: {
|
||||||
|
LOGIN,
|
||||||
|
EMAIL,
|
||||||
|
UUID: []
|
||||||
|
}
|
||||||
|
}, ref, data)
|
||||||
|
if (check.invalidefor.length > 0) {
|
||||||
|
return {
|
||||||
|
status: 403,
|
||||||
|
data: {
|
||||||
|
model: 'Pagans',
|
||||||
|
info: check.invalidefor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = check.data
|
||||||
|
let saveuser = false
|
||||||
|
let updateDatabase = false
|
||||||
|
Object.keys(data)
|
||||||
|
.forEach(k => {
|
||||||
|
// logger.info( user[ k ] )
|
||||||
|
// logger.info( data[ k ] )
|
||||||
|
// logger.info( '---' )
|
||||||
|
if (user[k] !== data[k]) {
|
||||||
|
user[k] = data[k]
|
||||||
|
saveuser = true
|
||||||
|
if (['TOKEN', 'LOGIN', 'EMAIL'].includes(k)) updateDatabase = true
|
||||||
|
// attention si LOGIN ou EMAIL change il faut checker qu il n existe pas dejà risque de conflit
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (saveuser) {
|
||||||
|
// logger.info( 'mise à jour user profile.json' );
|
||||||
|
if (data.TOKEN) {
|
||||||
|
user.date_lastLOGIN = new Date()
|
||||||
|
.toISOString()
|
||||||
|
} else {
|
||||||
|
user.date_update = new Date()
|
||||||
|
.toISOString()
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
jsonfile.writeFileSync(`${config.tribes}/${header.xworkon}/users/${UUID}.json`, user, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
// logger.info( 'declenche updatabase', updateDatabase )
|
||||||
|
if (updateDatabase) {
|
||||||
|
// mean index have to be updated
|
||||||
|
Pagans.updateDatabase(user, header.xworkon, false)
|
||||||
|
console.assert(config.loglevel === 'quiet', 'MISE A JOUR DU TOKEN ou de l\'EMAIL ou du LOGIN')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('ERRRRR need to understand update impossible of user: ' + UUID + ' in domain:' + header.xworkon + ' from user ' + header.xpaganid + ' of domain:' + header.xtribe)
|
||||||
|
logger.info('with data :', data)
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
data: {
|
||||||
|
info: ['failtoWritefs'],
|
||||||
|
model: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
info: ['successfulUpdate'],
|
||||||
|
model: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pagans.deleteUser = (UUID, header) => {
|
||||||
|
// Delete remove from users object UUID and update index
|
||||||
|
// Activity is not deleted => means some activity can concern an UUID that does not exist anymore.
|
||||||
|
// update index
|
||||||
|
const infouser = jsonfile.readFileSync(`${config.tribes}/${header.xworkon}/users/${UUID}.json`)
|
||||||
|
Pagans.updateDatabase(infouser, header.xworkon, true)
|
||||||
|
fs.removeSync(`${config.tribes}/${header.xworkon}/users/${UUID}.json`)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
info: ['successfulDelete'],
|
||||||
|
modedl: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
@header { xtribeid: client domain name data
|
||||||
|
A VERIFIER inutile ,xworkon: client domain name where user is store and where data are stored
|
||||||
|
,xuuid: 1 if unknown else if user id auhtneticated
|
||||||
|
,xlanguage: langue used fr en return referential in this lang
|
||||||
|
,xauth: TOKEN valid for 24h'}
|
||||||
|
workon=xtribeid in an app client usage
|
||||||
|
in case of xtribeid=mymaidigit,
|
||||||
|
if workon===mymaildigit => admin role on any xworkon
|
||||||
|
else adlin role only in xworkon specified
|
||||||
|
@body {LOGIN: password:}
|
||||||
|
return {status:200, data:{data:{TOKEN,UUID}}}
|
||||||
|
return {status:401,data:{info:[code list], model: referential}}
|
||||||
|
*/
|
||||||
|
Pagans.loginUser = (header, body, checkpsw) => {
|
||||||
|
// On recupere tribeid du LOGIN
|
||||||
|
// ATTENTION xworkon peut être different du user xtribeid
|
||||||
|
// (cas d'un user qui a des droits sur un autre domain et qui travail sur cet autre domain)
|
||||||
|
// les function Pagans utilise le domain de travail xWorkon
|
||||||
|
// il faut donc modifier le header au moment du LOGIN
|
||||||
|
// pour que l'update du user au moment du LOGIN concerne bien le bon domain
|
||||||
|
header.xworkon = header.xtribe
|
||||||
|
const LOGINdom = jsonfile.readFileSync(`${config.tmp}/loginsglob.json`)
|
||||||
|
console.assert(config.loglevel === 'quiet', LOGINdom)
|
||||||
|
console.assert(config.loglevel === 'quiet', body)
|
||||||
|
if (!LOGINdom[body.LOGIN]) {
|
||||||
|
return {
|
||||||
|
status: 401,
|
||||||
|
data: { info: ['LoginDoesNotExist'], model: 'Pagans' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const logins = jsonfile.readFileSync(`${config.tribes}/${LOGINdom[body.LOGIN]}/users/searchindex/logins.json`)
|
||||||
|
if (!Object.keys(logins)
|
||||||
|
.includes(body.LOGIN)) {
|
||||||
|
return {
|
||||||
|
status: 401,
|
||||||
|
data: {
|
||||||
|
info: ['LOGINDoesNotExist'],
|
||||||
|
model: 'Pagans',
|
||||||
|
moreinfo: `Le login ${body.LOGIN} does not exist for tribeid ${LOGINdom[body.LOGIN]}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Load user
|
||||||
|
const uid = logins[body.LOGIN]
|
||||||
|
const getUser = Pagans.getUser(uid, LOGINdom[body.LOGIN], { users: 'R' })
|
||||||
|
logger.info('getPagans', getUser)
|
||||||
|
if (getUser.status !== 200) {
|
||||||
|
return { status: 200, data: { model: 'Pagans', user: getUser.data.user } }
|
||||||
|
}
|
||||||
|
const user = getUser.data.user
|
||||||
|
logger.info('user', user)
|
||||||
|
if (checkpsw) {
|
||||||
|
const match = bcrypt.compareSync(body.PASSWORD, user.PASSWORD)
|
||||||
|
if (!match) {
|
||||||
|
return {
|
||||||
|
status: 401,
|
||||||
|
data: {
|
||||||
|
info: ['checkCredentials'],
|
||||||
|
model: 'Pagans'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Mise à jour user pour app LOGIN
|
||||||
|
user.tribeid = LOGINdom[body.LOGIN]
|
||||||
|
user.TOKEN = jwt.encode({
|
||||||
|
expiration: moment()
|
||||||
|
.add(1, 'day'),
|
||||||
|
UUID: user.UUID
|
||||||
|
}, config.jwtSecret)
|
||||||
|
// on met à jour le header qui authentifie le user connecte
|
||||||
|
header.xtribe = LOGINdom[body.LOGIN]
|
||||||
|
header.xpaganid = user.UUID
|
||||||
|
header.xauth = user.TOKEN
|
||||||
|
// On modifie xworkon car au LOGIN on est forcemenet identifiable sur tribeid
|
||||||
|
// xworkon est utiliser pour travailler sur un environnement client different de celui
|
||||||
|
// de l'appartenace du user
|
||||||
|
header.xworkon = LOGINdom[body.LOGIN]
|
||||||
|
const majuser = Pagans.updateUser(user.UUID, header, {
|
||||||
|
UUID: user.UUID,
|
||||||
|
TOKEN: user.TOKEN
|
||||||
|
})
|
||||||
|
// Enleve info confidentiel
|
||||||
|
delete user.PASSWORD
|
||||||
|
if (user.ACCESSRIGHTS.data.Alltribeid) {
|
||||||
|
// cas admin on transforme les droits sur tous les tribeid existant
|
||||||
|
const newaccessrightsdata = {}
|
||||||
|
jsonfile.readFileSync(`${config.tribes}/tribeids.json`)
|
||||||
|
.forEach(cid => {
|
||||||
|
newaccessrightsdata[cid] = user.ACCESSRIGHTS.data.Alltribeid
|
||||||
|
})
|
||||||
|
user.ACCESSRIGHTS.data = newaccessrightsdata
|
||||||
|
}
|
||||||
|
// on recupere le menu de l app qui demande le LOGIN si existe dans user.ACCESSRIGHTS.app[]
|
||||||
|
// header['X-app'] = tribeid:Projet pour récuperer le menu correspondant
|
||||||
|
console.assert(config.loglevel === 'quiet', 'header.xapp', header.xapp)
|
||||||
|
console.assert(config.loglevel === 'quiet', 'user.ACCESSRIGHTS.app[header.xapp]', user.ACCESSRIGHTS.app[header.xapp])
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
model: 'Pagans',
|
||||||
|
info: ['loginSuccess'],
|
||||||
|
user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pagans.getlinkwithoutpsw = async (EMAIL, header) => {
|
||||||
|
// check le domain d'appartenance de l'eamail dans /tribes/emailsglob.json={email:cleintId}
|
||||||
|
// on remplace le header.xtribeid
|
||||||
|
const domforemail = jsonfile.readFileSync(`${config.tribes}/emailsglob.json`, 'utf-8')
|
||||||
|
if (domforemail[EMAIL]) {
|
||||||
|
header.xtribe = domforemail[EMAIL]
|
||||||
|
} else {
|
||||||
|
return { status: 404, info: { model: 'Pagans', info: ['emailnotfound'], moreinfo: 'email does not exist in /emailsglob.json' } }
|
||||||
|
}
|
||||||
|
// recupere le uuid du user dans /tribes/tribeid/users/searchindex/emails.json
|
||||||
|
// puis l'ensemble des info des user du domain /uuids.json
|
||||||
|
// infoforuuid[uuidforemail[EMAIL]] permet de récupérer toutes info du user, droit, etc...
|
||||||
|
const uuidforemail = jsonfile.readFileSync(`${config.tribes}/${header.xtribe}/users/searchindex/emails.json`, 'utf8')
|
||||||
|
const infoforuuid = jsonfile.readFileSync(`${config.tribes}/${header.xtribe}/users/searchindex/uids.json`, 'utf8')
|
||||||
|
// On recupere le modele d'email appemailinfo qui doit être présent dans clientconf.json
|
||||||
|
const confdom = jsonfile.readFileSync(`${config.tribes}/${header.xtribe}/clientconf.json`, 'utf8')
|
||||||
|
let checkinfomail = ''
|
||||||
|
if (!confdom.appemailinfo) {
|
||||||
|
checkinfomail += ' Erreur de clientconfig il manque un objet appemailinfo pour poursuivre'
|
||||||
|
}
|
||||||
|
if (checkinfomail !== '') {
|
||||||
|
logger.info(`Pb de config pour ${header.xtribe} ${checkinfomail} `)
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
info: {
|
||||||
|
model: 'Pagans',
|
||||||
|
info: ['objectmissingclientconf'],
|
||||||
|
moreinfo: checkinfomail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let simulelogin
|
||||||
|
try {
|
||||||
|
simulelogin = await Pagans.loginUser(header, {
|
||||||
|
LOGIN: infoforuuid[uuidforemail[EMAIL]][0],
|
||||||
|
PASSWORD: ''
|
||||||
|
}, false)
|
||||||
|
logger.info('info simulelogin', simulelogin)
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
status: 501,
|
||||||
|
info: {
|
||||||
|
model: 'Pagans',
|
||||||
|
info: ['loginImpossible'],
|
||||||
|
moreinfo: 'Impossible de se loger avec ' + infoforuuid[uuidforemail[EMAIL]][0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const url = `${config.rootURL}?xauth=${simulelogin.data.TOKEN}&xuuid=${simulelogin.data.UUID}&xtribeid=${simulelogin.data.tribeid}&xworkOn=${header.xworkon}&xlang=${header.xlang}`
|
||||||
|
// logger.info('envoi email avec' + url)
|
||||||
|
confdom.appemailinfo.msg.destperso = [{}]
|
||||||
|
confdom.appemailinfo.msg.destperso[0].email = EMAIL
|
||||||
|
confdom.appemailinfo.msg.destperso[0].subject = 'Lien de réinitialisation valable 1h'
|
||||||
|
confdom.appemailinfo.msg.destperso[0].titre = 'Vous avez oublier votre mot de passe'
|
||||||
|
confdom.appemailinfo.msg.destperso[0].texte = `
|
||||||
|
<p style='color: #999999; font-size: 16px; line-height: 24px; margin: 0;text-align:justify;'>
|
||||||
|
Bonjour,<br> Vous nous avez signalé que vous avez oublié votre mot de passe pour cette email.
|
||||||
|
Avec le lien suivant vous allez pouvoir utiliser votre interface 1h maximum.
|
||||||
|
<a href="${url}">Clicker ICI</a>.<br>
|
||||||
|
Nous vous conseillons de changer votre mot de passe.</p>
|
||||||
|
`
|
||||||
|
// logger.info('envoi header :', header);
|
||||||
|
Outputs.sendMailcampain(confdom.appemailinfo.msg, header)
|
||||||
|
logger.info(confdom.appemailinfo)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
info: {
|
||||||
|
model: 'Pagans',
|
||||||
|
info: ['emailsentforReinit'],
|
||||||
|
moreinfo: 'EMAIL sent with unique link ' + url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = Pagans
|
455
src/models/Referentials.js
Executable file
455
src/models/Referentials.js
Executable file
@ -0,0 +1,455 @@
|
|||||||
|
/* eslint-disable no-tabs */
|
||||||
|
|
||||||
|
const glob = require('glob')
|
||||||
|
const path = require('path')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
const Referentials = {}
|
||||||
|
/*
|
||||||
|
Manage Referential object data
|
||||||
|
|
||||||
|
each object is compose of a list of fields
|
||||||
|
each fields have it owns structur and check
|
||||||
|
those common rules allow to be used to manage:
|
||||||
|
- forms (creation to collect data)
|
||||||
|
- data check
|
||||||
|
- data access rights
|
||||||
|
common referential data stored in /data/shared/referential/
|
||||||
|
|
||||||
|
*/
|
||||||
|
Referentials.clientconf = (xworkOn, listkey) => {
|
||||||
|
/*
|
||||||
|
Retourne les info d'un clientconf.json sur une liste de [cle]
|
||||||
|
*/
|
||||||
|
let conf = {}
|
||||||
|
const dataconf = {}
|
||||||
|
// logger.info( `${config.tribes}/${xworkOn}/clientconf.json` )
|
||||||
|
try {
|
||||||
|
conf = fs.readJsonSync(`${config.tribes}/${xworkOn}/clientconf.json`);
|
||||||
|
// remove information notrelevant for
|
||||||
|
['emailFrom', 'emailClient', 'emailCc', 'commentkey', 'clezoomprivate', 'stripekeyprivate', 'genericpsw'].forEach(c => {
|
||||||
|
delete conf[c]
|
||||||
|
})
|
||||||
|
listkey.forEach(k => dataconf[k] = conf[k])
|
||||||
|
// logger.info( 'dataconf', dataconf )
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Attention demande sur clienId inconnu ' + xworkOn)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
data: dataconf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Referentials.clientconfglob = () => ({
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
data: fs.readJsonSync(`${config.tmp}/clientconfglob.json`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Referentials.getref = (origin, source, ref, xworkOn, xlang) => {
|
||||||
|
// If request origin then send back referential with all language in it
|
||||||
|
// if not origin or json source return by language
|
||||||
|
let referent = {}
|
||||||
|
let src
|
||||||
|
if (origin && ['object', 'data'].includes(source)) {
|
||||||
|
src = `${config.tribes}/${xworkOn}/referentials/${source}/${ref}.json`
|
||||||
|
} else {
|
||||||
|
src = `${config.tribes}/${xworkOn}/referentials/${source}/${ref}_${xlang}.json`
|
||||||
|
}
|
||||||
|
// logger.info( src )
|
||||||
|
try {
|
||||||
|
referent = fs.readJsonSync(src)
|
||||||
|
} catch (err) {
|
||||||
|
logger.info(`Request ${src} does not exist `)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
data: referent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Referentials.putref = (source, name, xworkOn, data) => {
|
||||||
|
/*
|
||||||
|
We get a referential, we have 3 kinds of sources:
|
||||||
|
* data = [{uuid,DESC:{fr:,en,},DESCLONG:{fr:,en:}, other field}]
|
||||||
|
only DESC and DESCLONG have a translation
|
||||||
|
* json = are full complex object in language name_lg.json
|
||||||
|
* object = [{uuid,DESC:{fr,en},DESCLONG:{fr,en}},tpl,}]
|
||||||
|
Difference between data and object is that object defines rule to manage an object, and how to create a forms to get data each data is saved in one folder object/uuid.json and have to respect the corresponding object referentials definition.
|
||||||
|
|
||||||
|
For data and object it is possible only to put a file with all language available.
|
||||||
|
When store this script erase with the new file per _lg
|
||||||
|
|
||||||
|
name for source=json must end by _lg
|
||||||
|
*/
|
||||||
|
// logger.info( data )
|
||||||
|
const pat = /.*_..\.json$/
|
||||||
|
const file = `${config.tribes}/${xworkOn}/referentials/${source}/${name}.json`
|
||||||
|
if (['object', 'data'].includes(source)) {
|
||||||
|
if (pat.test(name)) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
model: 'Referentials',
|
||||||
|
info: ['nameunconsistent'],
|
||||||
|
moreinfo: 'can not be update with one lang need a full file with language for object or data'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fs.outputJsonSync(file, data)
|
||||||
|
return Refernetials.update(xworkOn, source, name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!pat.test(name)) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: {
|
||||||
|
model: 'Referentials',
|
||||||
|
info: ['nameunconsistent'],
|
||||||
|
moreinfo: 'can not be update without a lang _lg.json a referential json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fs.outputJsonSync(file, data)
|
||||||
|
return { status: 200, payload: { model: 'Referentials', info: ['successfull'], moreinfo: 'ref json updated ' } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
}
|
||||||
|
Referentials.updatefull = (tribeid) => {
|
||||||
|
let err = ''
|
||||||
|
let nbrefupdate = 0
|
||||||
|
const pat = /.*_..\.json$/;
|
||||||
|
['object', 'data'].forEach(o => {
|
||||||
|
glob.sync(`${config.tribes}/${tribeid}/referentials/${o}/*.json`)
|
||||||
|
.forEach(f => {
|
||||||
|
if (!pat.test(f)) {
|
||||||
|
const res = Referentials.update(tribeid, o, path.basename(f, '.json'))
|
||||||
|
if (res.status !== 200) {
|
||||||
|
err += `Error on ${o}/${path.basename(f)}`
|
||||||
|
} else {
|
||||||
|
nbrefupdate += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (err !== '') {
|
||||||
|
return { status: 500, payload: { info: ['Errupdateref'], model: 'Referentials', moreinfo: err } }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['Success'],
|
||||||
|
model: 'Referentials',
|
||||||
|
moreinfo: `Number of object and data ref updated: ${nbrefupdate}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Referentials.inittribeid = () => {
|
||||||
|
logger.info('Clientconf list for this server', `${config.tribes}/**/clientconf.json`)
|
||||||
|
const TribesGlobalConfig = glob.sync(`${config.tribes}/**/clientconf.json`)
|
||||||
|
.map(f => fs.readJsonSync(f))
|
||||||
|
// store global conf for sharing to other api
|
||||||
|
fs.outputJsonSync(`${config.tmp}/clientconfglob.json`, TribesGlobalConfig, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
return { status: 200, payload: { moreinfo: TribesGlobalConfig } }
|
||||||
|
}
|
||||||
|
Referentials.generetribeids = () => {
|
||||||
|
const tribeids = []
|
||||||
|
fs.readJsonSync(`${config.tmp}/clientconfglob.json`)
|
||||||
|
.forEach(c => {
|
||||||
|
if (!tribeids.includes(c.tribeid)) tribeids.push(c.tribeid)
|
||||||
|
})
|
||||||
|
fs.outputJsonSync(`${config.tmp}/tribeids.json`, tribeids)
|
||||||
|
logger.info(`update ${config.tribes}/tribeids`)
|
||||||
|
return tribeids
|
||||||
|
}
|
||||||
|
Referentials.genereallowedDOM = () => {
|
||||||
|
const confglob = fs.readJsonSync(`${config.tmp}/clientconfglob.json`)
|
||||||
|
let allDom = []
|
||||||
|
confglob.forEach(c => {
|
||||||
|
c.allowedDOMs.forEach(d => {
|
||||||
|
if (!allDom.includes(d)) allDom = allDom.concat(d)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return allDom
|
||||||
|
}
|
||||||
|
/* A voir si encore necessaire pour générer un environnement identique
|
||||||
|
sur un autre server
|
||||||
|
|
||||||
|
// Génére les domaines s'ils n'existe pas dans le repertoire
|
||||||
|
function genereEnvClient() {
|
||||||
|
const confglob = fs.readJsonSync(
|
||||||
|
`${config.sharedData}/clientconfglob.json`
|
||||||
|
);
|
||||||
|
confglob.forEach(function(c) {
|
||||||
|
config.tribeidsConf[c.tribeid] = c;
|
||||||
|
if (c.allowedURLs) {
|
||||||
|
c.allowedURLs.forEach(u => {
|
||||||
|
if (!config.allowedURLs.includes(u)) {
|
||||||
|
config.allowedURLs.push(u);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.info('erreur de fichier config d\'un site pour ', c);
|
||||||
|
}
|
||||||
|
// GLOBAL Tribes IDS INDEX
|
||||||
|
maketribeidsIndex();
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${c.tribeid}`)) {
|
||||||
|
const execSync = require('child_process').execSync;
|
||||||
|
execSync(`cp -r ${config.tribes}/modele ${config.tribes}/${c.tribeid}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
Referentials.update = (tribeid, source, name) => {
|
||||||
|
/*
|
||||||
|
Replace for each language the referential name for a tribeid
|
||||||
|
After each update the version number is incremented by 1 in clientconf.json
|
||||||
|
*/
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${tribeid}/referentials/${source}/${name}.json`)) {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['unknownRef'],
|
||||||
|
model: 'Referentials',
|
||||||
|
moreinfo: `file does not exist ${config.tribes}/${tribeid}/referentials/${source}/${name}.json`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
const clientconf = fs.readJsonSync(`${config.tribes}/${tribeid}/clientconf.json`)
|
||||||
|
if (!clientconf.langueReferential) {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['missingConf'],
|
||||||
|
model: 'Referentials',
|
||||||
|
moreinfo: ` ${config.tribes}/${tribeid}/clientconf.json does not contain langueReferential array`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ref = fs.readJsonSync(`${config.tribes}/${tribeid}/referentials/${source}/${name}.json`)
|
||||||
|
|
||||||
|
clientconf.langueReferential.forEach(lg => {
|
||||||
|
// manage translation
|
||||||
|
let refnew = []
|
||||||
|
|
||||||
|
refnew = []
|
||||||
|
ref.forEach(d => {
|
||||||
|
if (d.DESC) d.DESC = d.DESC[lg]
|
||||||
|
if (d.DESCLONG) d.DESCLONG = d.DESCLONG[lg]
|
||||||
|
if (d.INFO) d.INFO = d.INFO[lg]
|
||||||
|
if (d.desc) d.desc = d.desc[lg]
|
||||||
|
if (d.desclong) d.desclong = d.desclong[lg]
|
||||||
|
if (d.info) d.info = d.info[lg]
|
||||||
|
if (d.placeholder) d.placeholder = d.placeholder[lg]
|
||||||
|
refnew.push(d)
|
||||||
|
})
|
||||||
|
// save new ref in language
|
||||||
|
// logger.info( "New ref", refnew )
|
||||||
|
logger.info(`Update referentials per lg ${config.tribes}/${tribeid}/referentials/${source}/${name}_${lg}.json`)
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${tribeid}/referentials/${source}/${name}_${lg}.json`, refnew, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// upgrade version number
|
||||||
|
if (!clientconf.referentials) clientconf.referentials = {}
|
||||||
|
if (!clientconf.referentials[source]) clientconf.referentials[source] = {}
|
||||||
|
if (!clientconf.referentials[source][name]) clientconf.referentials[source][name] = { version: 0 }
|
||||||
|
clientconf.referentials[source][name].version += 1
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${tribeid}/clientconf.json`, clientconf, 'utf8')
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['successUpdate'],
|
||||||
|
model: 'Referentials',
|
||||||
|
moreinfo: `${name} updated`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// logger.info( Referentials.update( 'apixtribe', "object", "user" ) )
|
||||||
|
|
||||||
|
Referentials.genereobjet = (tribeid, destination, tplmustache, objet, filtre) => {
|
||||||
|
/* @TODO
|
||||||
|
Genere des objets d'agregat
|
||||||
|
@tribeid = data/tribee/ identifiant client
|
||||||
|
@destinations = [] of destination
|
||||||
|
@tplmustache = fichier mustache de mise en page
|
||||||
|
@objet = nom d'objet contact, companies, items, users
|
||||||
|
@filtre = fonction a executer
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ///// EN DESSOUS DE CETTE LIGNE A SUPPRIMER
|
||||||
|
|
||||||
|
/*
|
||||||
|
Le principe consistait à partager des referentiels dans shareddataLa gestion est trop compliqué => le principe chaque client duplique un referentiel pour que les app qui s'appuie sur des composants communs puissent fonctionner
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Referentials.genereClientObjectASUPP = () => {
|
||||||
|
const confglob = fs.readJsonSync( `${config.tmp}/clientconfglob.json` );
|
||||||
|
// Check and update folder and data per lang
|
||||||
|
// c as tribeid
|
||||||
|
confglob.forEach( ( c, postribeid ) => {
|
||||||
|
//check folder are well create
|
||||||
|
const lstfolder = [ 'actions', 'actions/done', 'actions/todo', 'cards', 'logs', 'orders', 'orders/reservation', 'orders/purchase', 'orders/contact', 'public', 'public/reservation', 'tags', 'tags/stats', 'tags/archives', 'tags/hits', 'tags/imgtg', 'users' ];
|
||||||
|
lstfolder.forEach( fol => {
|
||||||
|
if( !fs.existsSync( `${config.tribes}/${c.tribeid}/${fol}` ) ) {
|
||||||
|
fs.mkdirSync( `${config.tribes}/${c.tribeid}/${fol}` );
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
if( c.referentials && !c.langue ) { logger.info( `ERREUR referentials mais pas de langue:[] pour ${c.tribeid}/clientconf.json` ) }
|
||||||
|
if( c.referentials && c.langue ) {
|
||||||
|
let majclientconf = false;
|
||||||
|
// Create and check Object structure
|
||||||
|
Object.keys( c.referentials.object )
|
||||||
|
.forEach( o => {
|
||||||
|
// if object exist in shared then it merge sharedObject and domain referential object
|
||||||
|
let objfull = [];
|
||||||
|
const objshared = `${config.sharedData}/referentials/dataManagement/object/${o}.json`;
|
||||||
|
if( fs.existsSync( objshared ) ) {
|
||||||
|
objfull = objfull.concat( fs.readJsonSync( objshared ) );
|
||||||
|
}
|
||||||
|
const objdomain = `${config.tribes}/${c.tribeid}/referentials/dataManagement/object/${o}.json`;
|
||||||
|
if( fs.existsSync( objdomain ) ) {
|
||||||
|
objfull = objfull.concat( fs.readJsonSync( objdomain ) );
|
||||||
|
}
|
||||||
|
c.langue.forEach( lg => {
|
||||||
|
const objfulllg = objfull.map( field => {
|
||||||
|
if( field.DESC ) field.DESC = field.DESC[ lg ];
|
||||||
|
if( field.DESCLONG ) field.DESCLONG = field.DESCLONG[ lg ];
|
||||||
|
return field;
|
||||||
|
} );
|
||||||
|
const objectdomlg = `${config.tribes}/${c.tribeid}/referentials/${lg}/object/${o}.json`;
|
||||||
|
let savedObject = {};
|
||||||
|
if( fs.existsSync( objectdomlg ) ) {
|
||||||
|
savedObject = fs.readJsonSync( objectdomlg );
|
||||||
|
}
|
||||||
|
// TODO Always true change later to update only if needded
|
||||||
|
if( !fs.existsSync( objectdomlg ) || objfulllg.length !==== savedObject.length || 1 === 1 ) {
|
||||||
|
fs.outputJsonSync( objectdomlg, objfulllg, {
|
||||||
|
spaces: 2
|
||||||
|
} );
|
||||||
|
confglob[ postribeid ].referentials.object[ o ].version += 1;
|
||||||
|
majclientconf = true;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
// datafile
|
||||||
|
Object.keys( c.referentials.data )
|
||||||
|
.forEach( d => {
|
||||||
|
// if object exist in shared then it merge sharedObject and domain referential object
|
||||||
|
// logger.info(c.tribeid + '--' + d);
|
||||||
|
let datafull = [];
|
||||||
|
const datashared = `${
|
||||||
|
config.sharedData
|
||||||
|
}/referentials/dataManagement/data/${d}.json`;
|
||||||
|
if( fs.existsSync( datashared ) ) {
|
||||||
|
datafull = datafull.concat( fs.readJsonSync( datashared ) );
|
||||||
|
}
|
||||||
|
const datadomain = `${config.tribes}/${
|
||||||
|
c.tribeid
|
||||||
|
}/referentials/dataManagement/data/${d}.json`;
|
||||||
|
if( fs.existsSync( datadomain ) ) {
|
||||||
|
datafull = datafull.concat( fs.readJsonSync( datadomain ) );
|
||||||
|
}
|
||||||
|
/* const defdata = `${config.tribes}/${
|
||||||
|
c.tribeid
|
||||||
|
}/referentials/dataManagement/data/${d}.json`;
|
||||||
|
*/
|
||||||
|
// for each Langues => generate fr.obj and compare it with existing file
|
||||||
|
// if diff then => upgrade version number in clientconf
|
||||||
|
// logger.info(datafull);
|
||||||
|
// this could be improved by usind d.meta wich is the object that DESCribe this data
|
||||||
|
/* c.langue.forEach( lg => {
|
||||||
|
let meta;
|
||||||
|
if( c.referentials.data[ d ].meta ) {
|
||||||
|
meta = fs.readJsonSync( `${config.tribes}/${c.tribeid}/referentials/${lg}/object/${
|
||||||
|
c.referentials.data[d].meta
|
||||||
|
}.json` );
|
||||||
|
}
|
||||||
|
let datalg;
|
||||||
|
const datafulllg = datafull.map( tup => {
|
||||||
|
datalg = {};
|
||||||
|
meta.forEach( ch => {
|
||||||
|
if( tup[ ch.idfield ] ) {
|
||||||
|
if( ch.multilangue ) {
|
||||||
|
datalg[ ch.idfield ] = tup[ ch.idfield ][ lg ];
|
||||||
|
} else {
|
||||||
|
datalg[ ch.idfield ] = tup[ ch.idfield ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
return datalg;
|
||||||
|
} );
|
||||||
|
// lit le fichier correspondant et le compare si différent le sauvegarde
|
||||||
|
// stocke l'information d'upgrade d ela version
|
||||||
|
const datadomlg = `${config.tribes}/${
|
||||||
|
c.tribeid
|
||||||
|
}/referentials/${lg}/data/${d}.json`;
|
||||||
|
let saveddata = {};
|
||||||
|
if( fs.existsSync( datadomlg ) ) {
|
||||||
|
saveddata = fs.readJsonSync( datadomlg );
|
||||||
|
}
|
||||||
|
// Condition to improve
|
||||||
|
// TODO always change file to improvelater by detecting real change.
|
||||||
|
if( !fs.existsSync( datadomlg ) || datafulllg.length !== saveddata.length || 1 === 1 ) {
|
||||||
|
fs.outputJsonSync( datadomlg, datafulllg, {
|
||||||
|
spaces: 2
|
||||||
|
} );
|
||||||
|
confglob[ postribeid ].referentials.data[ d ].version += 1;
|
||||||
|
majclientconf = true;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
// json file that have to start with lg {lg:'':{json }}
|
||||||
|
Object.keys( c.referentials.json )
|
||||||
|
.forEach( j => {
|
||||||
|
// if object exist in shared then it merge sharedObject and domain referential object
|
||||||
|
// logger.info(c.tribeid + '--' + d);
|
||||||
|
let jsonfull = [];
|
||||||
|
const jsondomain = `${config.tribes}/${c.tribeid}/referentials/dataManagement/json/${j}.json`;
|
||||||
|
if( fs.existsSync( jsondomain ) ) {
|
||||||
|
jsonfull = fs.readJsonSync( jsondomain );
|
||||||
|
}
|
||||||
|
c.langue.forEach( lg => {
|
||||||
|
const jsondomlg = `${config.tribes}/${
|
||||||
|
c.tribeid
|
||||||
|
}/referentials/${lg}/json/${j}.json`;
|
||||||
|
// logger.info('jsondomlg', jsondomlg);
|
||||||
|
let datalg = jsonfull;
|
||||||
|
if( jsonfull[ lg ] ) {
|
||||||
|
datalg = jsonfull[ lg ];
|
||||||
|
}
|
||||||
|
fs.outputJsonSync( jsondomlg, datalg, {
|
||||||
|
spaces: 2
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
// update clientconf domain with updated version
|
||||||
|
if( majclientconf ) {
|
||||||
|
fs.outputJsonSync( `${config.tribes}/${c.tribeid}/clientconf.json`, c, {
|
||||||
|
spaces: 2
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
// update global conf
|
||||||
|
fs.outputJsonSync( `${config.tmp}/clientconfglob.json`, confglob, {
|
||||||
|
spaces: 2
|
||||||
|
} );
|
||||||
|
}; */
|
||||||
|
|
||||||
|
module.exports = Referentials
|
235
src/models/Referentialssave.js
Executable file
235
src/models/Referentialssave.js
Executable file
@ -0,0 +1,235 @@
|
|||||||
|
const glob = require('glob')
|
||||||
|
const path = require('path')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
// Check if package is installed or not to pickup the right config file
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
const Referentials = {}
|
||||||
|
/*
|
||||||
|
Manage Referential object data
|
||||||
|
|
||||||
|
each object is compose of a list of fields
|
||||||
|
each fields have it owns structur and check
|
||||||
|
those common rules allow to be used to manage:
|
||||||
|
- forms (creation to collect data)
|
||||||
|
- data check
|
||||||
|
- data access rights
|
||||||
|
common referential data stored in /data/shared/referential/
|
||||||
|
|
||||||
|
*/
|
||||||
|
Referentials.clientconf = (xworkOn, listkey) => {
|
||||||
|
/*
|
||||||
|
Retourne les info d'un clientconf.json sur une liste de [cle]
|
||||||
|
*/
|
||||||
|
let conf = {}
|
||||||
|
const dataconf = {}
|
||||||
|
logger.info(`${config.tribes}/${xworkOn}/clientconf.json`)
|
||||||
|
try {
|
||||||
|
conf = fs.readJsonSync(`${config.tribes}/${xworkOn}/clientconf.json`);
|
||||||
|
// remove information notrelevant for
|
||||||
|
['emailFrom', 'emailClient', 'emailCc', 'commentkey', 'clezoomprivate', 'stripekeyprivate', 'genericpsw'].forEach(c => {
|
||||||
|
delete conf[c]
|
||||||
|
})
|
||||||
|
listkey.forEach(k => dataconf[k] = conf[k])
|
||||||
|
logger.info('dataconf', dataconf)
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Attention demande sur clienId inconnu ' + xworkOn)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
data: dataconf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Referentials.clientconfglob = () => ({
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
data: fs.readJsonSync(`${config.tmp}/clientconfglob.json`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Referentials.inittribeid = () => {
|
||||||
|
logger.info('Clientconf list for this server', `${config.tribes}/**/clientconf.json`)
|
||||||
|
const TribesGlobalConfig = glob.sync(`${config.tribes}/**/clientconf.json`)
|
||||||
|
.map(f => fs.readJsonSync(f))
|
||||||
|
// store global conf for sharing to other api
|
||||||
|
fs.outputJsonSync(`${config.tmp}/clientconfglob.json`, TribesGlobalConfig, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
return { status: 200, payload: { moreinfo: TribesGlobalConfig } }
|
||||||
|
}
|
||||||
|
|
||||||
|
Referentials.generetribeids = () => {
|
||||||
|
const tribeids = []
|
||||||
|
fs.readJsonSync(`${config.tmp}/clientconfglob.json`)
|
||||||
|
.forEach(c => {
|
||||||
|
if (!tribeids.includes(c.tribeid)) tribeids.push(c.tribeid)
|
||||||
|
})
|
||||||
|
fs.outputJsonSync(`${config.tmp}/tribeids.json`, tribeids)
|
||||||
|
logger.info(`update ${config.tribes}/tribeids`)
|
||||||
|
return tribeids
|
||||||
|
}
|
||||||
|
Referentials.genereallowedDOM = () => {
|
||||||
|
const confglob = fs.readJsonSync(`${config.tmp}/clientconfglob.json`)
|
||||||
|
let allDom = []
|
||||||
|
confglob.forEach(c => {
|
||||||
|
c.allowedDOMs.forEach(d => {
|
||||||
|
if (!allDom.includes(d)) allDom = allDom.concat(d)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return allDom
|
||||||
|
}
|
||||||
|
|
||||||
|
Referentials.getref = (source, ref, xworkOn, xlang, singlelang = true) => {
|
||||||
|
let referent = {}
|
||||||
|
let src = `${config.tribes}/${xworkOn}/referentials/${xlang}/${source}/${ref}.json`
|
||||||
|
if (!singlelang) {
|
||||||
|
// request full referential to manage
|
||||||
|
src = `${config.tribes}/${xworkOn}/referentials/dataManagement/${source}/${ref}.json`
|
||||||
|
}
|
||||||
|
logger.info(src)
|
||||||
|
try {
|
||||||
|
referent = fs.readJsonSync(src)
|
||||||
|
} catch (err) {
|
||||||
|
logger.info(`Attention demande de referentiel inexistant pour ${src} `)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
data: referent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Referentials.putref = (source, name, xworkOn, data) => {
|
||||||
|
/*
|
||||||
|
We get a referential, we have 3 kinds of sources:
|
||||||
|
* data = [{uuid,desc:{fr:,en,},desclong:{fr:,en:}, other field}]
|
||||||
|
only desc and desclong have a translation
|
||||||
|
* json = {"fr":{},"en":{}, ...} are full complex object in language
|
||||||
|
* object = [{uuid,desc:{fr,en},desclong:{fr,en}},tpl,}]
|
||||||
|
Difference between data and object is that object defines rule to manage an object, and how to create a forms to get data each data is saved in one folder object/uuid.json and have to respect the corresponding object referentials definition.
|
||||||
|
|
||||||
|
*/
|
||||||
|
logger.info(data)
|
||||||
|
// Create a backup of the day hour if exist
|
||||||
|
const file = `${config.tribes}/${xworkOn}/referentials/dataManagement/${source}/${name}.json`
|
||||||
|
|
||||||
|
if (fs.existsSync(file)) {
|
||||||
|
// backup change done per hour
|
||||||
|
const origin = fs.readJsonSync(file, 'utf-8')
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${xworkOn}/referentials/dataManagementBackup/${source}/${name}${moment().format('YYYYMMDDHHmm')}.json`, origin, { spaces: 2 })
|
||||||
|
} else {
|
||||||
|
logger.info(`Referential ${name}.json does not exist this created it`)
|
||||||
|
}
|
||||||
|
logger.info('ref backup before update', name)
|
||||||
|
fs.outputJsonSync(file, data, { spaces: 2 })
|
||||||
|
// update/create new referential and new version
|
||||||
|
return Referentials.update(xworkOn, source, name)
|
||||||
|
}
|
||||||
|
Referentials.updatefull = (tribeid) => {
|
||||||
|
// source json are only per lg so no full update for json
|
||||||
|
let err = '';
|
||||||
|
['object', 'data'].forEach(o => {
|
||||||
|
glob.sync(`${config.tribes}/${tribeid}/referentials/dataManagement/${o}/*.json`)
|
||||||
|
.forEach(f => {
|
||||||
|
if (finipaspar_lg) {
|
||||||
|
const res = Referentials.update(tribeid, o, path.basename(f, '.json'))
|
||||||
|
if (res.status !== 200) {
|
||||||
|
err += `Error on ${o}/${path.basename(f)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (err !== '') {
|
||||||
|
return { status: 500, payload: { info: ['Errupdateref'], model: 'Referentials', moreinfo: err } }
|
||||||
|
}
|
||||||
|
return { status: 200, payload: { info: ['Success'], model: 'Referentials' } }
|
||||||
|
}
|
||||||
|
|
||||||
|
Referentials.update = (tribeid, source, name) => {
|
||||||
|
/*
|
||||||
|
Replace for each language the referential name for a tribeid
|
||||||
|
After each update the version number is incremented by 1 in clientconf.json
|
||||||
|
*/
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${tribeid}/referentials/${source}/${name}.json`)) {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['unknownRef'],
|
||||||
|
model: 'Referentials',
|
||||||
|
moreinfo: `file does not exist ${config.tribes}/${tribeid}/referentials/${source}/${name}.json`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
const clientconf = fs.readJsonSync(`${config.tribes}/${tribeid}/clientconf.json`)
|
||||||
|
if (!clientconf.langueReferential) {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: {
|
||||||
|
info: ['missingConf'],
|
||||||
|
model: 'Referentials',
|
||||||
|
moreinfo: ` ${config.tribes}/${tribeid}/clientconf.json does not contain langueReferential array`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ref = fs.readJsonSync(`${config.tribes}/${tribeid}/referentials/${source}/${name}.json`)
|
||||||
|
|
||||||
|
clientconf.langueReferential.forEach(lg => {
|
||||||
|
/* if( !fs.existsSync( `${config.tribes}/${tribeid}/referentials/${lg}` ) ) {
|
||||||
|
[ '', '/data', '/json', '/object' ].forEach( p => {
|
||||||
|
fs.mkdirSync( `${config.tribes}/${tribeid}/referentials/${lg}${p}` );
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// manage translation
|
||||||
|
let refnew = []
|
||||||
|
if (source === 'json') {
|
||||||
|
refnew = ref[lg]
|
||||||
|
} else {
|
||||||
|
refnew = []
|
||||||
|
ref.forEach(d => {
|
||||||
|
if (d.desc) d.desc = d.desc[lg]
|
||||||
|
if (d.desclong) d.desclong = d.desclong[lg]
|
||||||
|
if (d.info) d.info = d.info[lg]
|
||||||
|
if (d.placeholder) d.placeholder = d.placeholder[lg]
|
||||||
|
refnew.push(d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// save new ref in language
|
||||||
|
logger.info('testtttt', refnew)
|
||||||
|
logger.info(`${config.tribes}/${tribeid}/referentials/${source}/${name}_${lg}.json`)
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${tribeid}/referentials/${source}/${name}_${lg}.json`, refnew, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// upgrade version number
|
||||||
|
if (!clientconf.referentials[source][name]) clientconf.referentials[source][name] = { version: 0 }
|
||||||
|
clientconf.referentials[source][name].version += 1
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['successUpdate'],
|
||||||
|
model: 'Referentials',
|
||||||
|
moreinfo: `${name} updated`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// logger.info( Referentials.update( 'apixtribe', "object", "user" ) )
|
||||||
|
|
||||||
|
Referentials.genereobjet = (tribeid, destination, tplmustache, objet, filtre) => {
|
||||||
|
/* @TODO
|
||||||
|
Genere des objets d'agregat
|
||||||
|
@tribeid = data/tribee/ identifiant client
|
||||||
|
@destinations = [] of destination
|
||||||
|
@tplmustache = fichier mustache de mise en page
|
||||||
|
@objet = nom d'objet contact, companies, items, users
|
||||||
|
@filtre = fonction a executer
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Referentials
|
@ -3,17 +3,21 @@ const path = require( 'path' );
|
|||||||
const dnsSync = require( 'dns-sync' );
|
const dnsSync = require( 'dns-sync' );
|
||||||
const Mustache = require( 'mustache' );
|
const Mustache = require( 'mustache' );
|
||||||
|
|
||||||
|
const logger = require("../core/logger")
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
const Setup = {};
|
const Setup = {};
|
||||||
|
|
||||||
if( !fs.existsSync( '/etc/nginx/nginx.conf' ) ) {
|
if( !fs.existsSync( '/etc/nginx/nginx.conf' ) ) {
|
||||||
console.log( '\x1b[31m Check documentation, nginx have to be installed on this server first, no /etc/nginx/nginx.conf available' );
|
// logger.error( '\x1b[31m Check documentation, nginx have to be installed on this server first, no /etc/nginx/nginx.conf available' );
|
||||||
process.exit();
|
// process.exit();
|
||||||
}
|
}
|
||||||
if( !fs.existsSync( '../config.js' ) ) {
|
if( !fs.existsSync( '../config.js' ) ) {
|
||||||
console.log( `\x1b[42m####################################\nWellcome into apixtribe, this is a first install.\nWe need to make this server accessible from internet subdomain.domain to current IP. This setup will create your unique tribeid, with an admin login user to let you connect to the parameter interface.\nCheck README's project to learn more. more.\n#####################################\x1b[0m` );
|
logger.info( `\x1b[42m####################################\nWellcome into apixtribe, this is a first install.\nWe need to make this server accessible from internet subdomain.domain to current IP. This setup will create your unique tribeid, with an admin login user to let you connect to the parameter interface.\nCheck README's project to learn more. more.\n#####################################\x1b[0m` );
|
||||||
const confdata = fs.readJsonSync( path.normalize( `${__dirname}/../setup/configsetup.json` ) );
|
const confdata = fs.readJsonSync( path.normalize( `${__dirname}/../setup/configsetup.json` ) );
|
||||||
console.log( 'Current setup conf from :\n' + path.normalize( `${__dirname}/../setup/configsetup.json` + '\nChange with relevant setup data and rerun yarn setup' ) );
|
logger.info( 'Current setup conf from :\n' + path.normalize( `${__dirname}/../setup/configsetup.json` + '\nChange with relevant setup data and rerun yarn setup' ) );
|
||||||
console.log( confdata )
|
logger.info( confdata )
|
||||||
const readline = require( 'readline' );
|
const readline = require( 'readline' );
|
||||||
const rl = readline.createInterface( {
|
const rl = readline.createInterface( {
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
@ -22,30 +26,30 @@ if( !fs.existsSync( '../config.js' ) ) {
|
|||||||
|
|
||||||
rl.question( 'This is the data from setup/configsetup.json used, is it correct to use as first install (Yes/no)?', function ( rep1 ) {
|
rl.question( 'This is the data from setup/configsetup.json used, is it correct to use as first install (Yes/no)?', function ( rep1 ) {
|
||||||
let quest = `This is a production install, please check that ${confdata.subdomain}.${confdata.domain} IP is well redirect to tour server`;
|
let quest = `This is a production install, please check that ${confdata.subdomain}.${confdata.domain} IP is well redirect to tour server`;
|
||||||
if( rep1 !== "Yes" ) process.exit( 0 );
|
if( rep1 !==== "Yes" ) process.exit( 0 );
|
||||||
if( confdata.domain == 'local.fr' ) {
|
if( confdata.domain === 'local.fr' ) {
|
||||||
quest = `This is a development installation, please add in your /etc/hosts "127.0.0.1 ${confdata.subdomain}.${confdata.domain} " `;
|
quest = `This is a development installation, please add in your /etc/hosts "127.0.0.1 ${confdata.subdomain}.${confdata.domain} " `;
|
||||||
}
|
}
|
||||||
rl.question( quest + '\nAre you sure to set this? (Yes/no)', function ( rep2 ) {
|
rl.question( quest + '\nAre you sure to set this? (Yes/no)', function ( rep2 ) {
|
||||||
if( rep2 == "Yes" ) {
|
if( rep2 === "Yes" ) {
|
||||||
const check = Setup.checkdata( confdata );
|
const check = Setup.checkdata( confdata );
|
||||||
if( check == "" ) {
|
if( check === "" ) {
|
||||||
Setup.config( confdata );
|
Setup.config( confdata );
|
||||||
} else {
|
} else {
|
||||||
console.log( check );
|
logger.info( check );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log( 'Nothing done please, check setup/configsetup.json and answer twice Yes' )
|
logger.info( 'Nothing done please, check setup/configsetup.json and answer twice Yes' )
|
||||||
}
|
}
|
||||||
rl.close();
|
rl.close();
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
rl.on( 'close', function () {
|
rl.on( 'close', function () {
|
||||||
console.log( '\n Setup process ended' );
|
logger.info( '\n Setup process ended' );
|
||||||
process.exit( 0 );
|
process.exit( 0 );
|
||||||
} );
|
} );
|
||||||
} else {
|
} else {
|
||||||
console.log( 'Carefull you have already a config.js that is running. If you want to change remove config.js file and run again yarn setup' );
|
logger.info( 'Carefull you have already a config.js that is running. If you want to change remove config.js file and run again yarn setup' );
|
||||||
}
|
}
|
||||||
|
|
||||||
Setup.checkdata = conf => {
|
Setup.checkdata = conf => {
|
||||||
@ -72,7 +76,7 @@ Setup.checkdata = conf => {
|
|||||||
if( conf.jwtsecret.length < 32 ) {
|
if( conf.jwtsecret.length < 32 ) {
|
||||||
rep += "Your jwtsecretkey must have at least 32 characters"
|
rep += "Your jwtsecretkey must have at least 32 characters"
|
||||||
}
|
}
|
||||||
if( conf.mode != 'dev' && !dnsSync.resolve( `${conf.subdomain}.${conf.domain}` ) ) {
|
if( conf.mode !== 'dev' && !dnsSync.resolve( `${conf.subdomain}.${conf.domain}` ) ) {
|
||||||
rep += `\nresolving ${conf.subdomain}.${conf.domain} will not responding valid IP, please setup domain redirection IP before runing this script`
|
rep += `\nresolving ${conf.subdomain}.${conf.domain} will not responding valid IP, please setup domain redirection IP before runing this script`
|
||||||
}
|
}
|
||||||
return rep
|
return rep
|
||||||
@ -88,10 +92,10 @@ Setup.configjs = ( confdata ) => {
|
|||||||
let confapixtribe = fs.readFileSync( './setup/config.mustache', 'utf-8' );
|
let confapixtribe = fs.readFileSync( './setup/config.mustache', 'utf-8' );
|
||||||
fs.writeFileSync( './config.js', Mustache.render( confapixtribe, confdata ), 'utf-8' );
|
fs.writeFileSync( './config.js', Mustache.render( confapixtribe, confdata ), 'utf-8' );
|
||||||
if( fs.existsSync( './config.js' ) ) {
|
if( fs.existsSync( './config.js' ) ) {
|
||||||
console.log( 'config.js successfully created.' );
|
logger.info( 'config.js successfully created.' );
|
||||||
} else {
|
} else {
|
||||||
console.log( "config.js not created, check what's wrong in tpl:", confapixtribe );
|
logger.info( "config.js not created, check what's wrong in tpl:", confapixtribe );
|
||||||
console.log( "for data :", confdata );
|
logger.info( "for data :", confdata );
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -99,7 +103,7 @@ Setup.druidid = ( confdata ) => {
|
|||||||
// create a tribeid with a user that will admin this instance into /tribes/tribeid /users
|
// create a tribeid with a user that will admin this instance into /tribes/tribeid /users
|
||||||
const config = require( '../config.js' );
|
const config = require( '../config.js' );
|
||||||
// Need to do it on setup this is also done again in models/Tribes.js
|
// Need to do it on setup this is also done again in models/Tribes.js
|
||||||
console.log( `${config.tribes}/${confdata.druidid}` )
|
logger.info( `${config.tribes}/${confdata.druidid}` )
|
||||||
fs.ensureDirSync( `${config.tribes}/${confdata.druidid}` );
|
fs.ensureDirSync( `${config.tribes}/${confdata.druidid}` );
|
||||||
[ 'users', 'www', 'referentials', 'nationchains' ].forEach( r => {
|
[ 'users', 'www', 'referentials', 'nationchains' ].forEach( r => {
|
||||||
fs.copySync( `${config.mainDir}/setup/tribes/apixtribe/${r}`, `${config.tribes}/${confdata.druidid}/${r}` );
|
fs.copySync( `${config.mainDir}/setup/tribes/apixtribe/${r}`, `${config.tribes}/${confdata.druidid}/${r}` );
|
||||||
@ -123,8 +127,8 @@ Setup.druidid = ( confdata ) => {
|
|||||||
ACCESSRIGHTS: access
|
ACCESSRIGHTS: access
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
if( createclient.status == 200 ) {
|
if( createclient.status === 200 ) {
|
||||||
console.log( `Your tribeid domain was created with login : ${confdata.login} and password: ${confdata.genericpsw}, change it after the 1st login on https://${confdata.subdomain}.${confdata.domain}` );
|
logger.info( `Your tribeid domain was created with login : ${confdata.login} and password: ${confdata.genericpsw}, change it after the 1st login on https://${confdata.subdomain}.${confdata.domain}` );
|
||||||
// Create nginx conf for a first install
|
// Create nginx conf for a first install
|
||||||
const confnginx = fs.readFileSync( './setup/nginx/nginx.conf.mustache', 'utf8' );
|
const confnginx = fs.readFileSync( './setup/nginx/nginx.conf.mustache', 'utf8' );
|
||||||
fs.outputFileSync( '/etc/nginx/nginx.conf', Mustache.render( confnginx, confdata ), 'utf-8' );
|
fs.outputFileSync( '/etc/nginx/nginx.conf', Mustache.render( confnginx, confdata ), 'utf-8' );
|
||||||
@ -138,11 +142,11 @@ Setup.druidid = ( confdata ) => {
|
|||||||
website: 'webapp',
|
website: 'webapp',
|
||||||
pageindex: "app_index_fr.html"
|
pageindex: "app_index_fr.html"
|
||||||
} );
|
} );
|
||||||
if( addspaceweb.status == 200 ) {
|
if( addspaceweb.status === 200 ) {
|
||||||
console.log( `WELL DONE run yarn dev to test then yarn startpm2 ` )
|
logger.info( `WELL DONE run yarn dev to test then yarn startpm2 ` )
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log( 'Issue ', createclient )
|
logger.info( 'Issue ', createclient )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
235
src/models/Tags.js
Executable file
235
src/models/Tags.js
Executable file
@ -0,0 +1,235 @@
|
|||||||
|
const fs = require('fs')
|
||||||
|
const formidable = require('formidable')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const path = require('path')
|
||||||
|
const glob = require('glob')
|
||||||
|
const mustache = require('mustache')
|
||||||
|
const moment = require('moment')
|
||||||
|
// Check if package is installed or not to pickup the right config file
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
const Tags = {}
|
||||||
|
/*
|
||||||
|
Keyword definition:
|
||||||
|
|
||||||
|
id: b64 encoded field that can be : email , uuid
|
||||||
|
tribeid: apiamaildigit client Id, (a folder /tribes/tribeid have to exist)
|
||||||
|
email: identifiant of a person
|
||||||
|
uuid: identifiant o
|
||||||
|
contact database
|
||||||
|
operationId: code name of an operation
|
||||||
|
messageId: code name of an templatesource have to exist in /datashared/templatesource/generic/messageId
|
||||||
|
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Manage tag data collection
|
||||||
|
Popup survey manager
|
||||||
|
*/
|
||||||
|
Tags.info = (data, req) => {
|
||||||
|
// logger.info('headers:', req.headers)
|
||||||
|
/* logger.info('hostname', req.hostname)
|
||||||
|
logger.info('req.ip', req.ip)
|
||||||
|
logger.info('req.ips', req.ips)
|
||||||
|
logger.info('req key', Object.keys(req))
|
||||||
|
*/
|
||||||
|
// logger.info('req.rawHeaders', req.body)
|
||||||
|
data.useragent = `${req.headers['user-agent']}__${req.headers['accept-language']}__${req.headers['accept-encoding']}__${req.headers.connection}`
|
||||||
|
data.ips = req.ips
|
||||||
|
data.ip = req.ip
|
||||||
|
data.proxyip = req.connection.remoteAddress
|
||||||
|
data.cookie = ''
|
||||||
|
Object.keys(req.headers)
|
||||||
|
.forEach(k => {
|
||||||
|
if (!['user-agent', 'accept-language', 'accept-encoding', 'connection'].includes(k)) {
|
||||||
|
data.cookie += `${k}__${req.headers.cookie}|`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// data.cookie = `${req.headers['cookie']}__${req.headers['upgrade-insecure-requests']}__${req.headers['if-modified-since']}__${req.headers['if-no-match']}__${req.headers['cache-control']}`;
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
Tags.getfile = (filename, req) => {
|
||||||
|
const infotg = filename.split('__')
|
||||||
|
if (infotg.length < 3 && !fs.existsSync(`${config.tribes}/${infotg[1]}`)) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
payload: { info: ['fileUnknown'], model: 'UploadFiles' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (infotg[0] === 'imgtg') {
|
||||||
|
jsonfile.writeFile(`${config.tribes}/${infotg[1]}/tags/imgtg/${Date.now()}.json`, Tags.info({
|
||||||
|
filename,
|
||||||
|
messageId: infotg[2],
|
||||||
|
operationId: infotg[3],
|
||||||
|
identifiant: infotg[4]
|
||||||
|
}, req), function (err) {
|
||||||
|
if (err) {
|
||||||
|
logger.info(`Erreur de sauvegarde de tag:${filename}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: { moreinfo: 'Declenche tag', filename: `${config.mainDir}/public/imgtg.png` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { status: 404, payload: {} }
|
||||||
|
}
|
||||||
|
Tags.savehits = (req) => {
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${req.params.tribeid}`)) {
|
||||||
|
logger.info(`Erreur d'envoi de tag sur ${req.params.tribeid} pour ${req.params.r}`)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
const info = JSON.parse(JSON.stringify(req.body))
|
||||||
|
jsonfile.writeFile(`${config.tribes}/${req.params.tribeid}/tags/hits/${Date.now()}.json`, Tags.info(info, req), function (err) {
|
||||||
|
if (err) {
|
||||||
|
logger.info(`Erreur de sauvegarde de tag pour ${req.params.tribeid} check si /tags/hits et /tags/imgtg exist bien `)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
Tags.dataloadstat = (tribeid) => {
|
||||||
|
/*
|
||||||
|
@TODO à appeler via une route pour agregation de tag
|
||||||
|
@TODO ajouter la suppression des fichiers hits traités qd le prog aura fait ses preuves en prod
|
||||||
|
@TODO corriger la prod pour que l'ip passe
|
||||||
|
|
||||||
|
Si on recharge plusieurs fois un hits (on ne le comptabilise pas si même r et même timestamps)
|
||||||
|
|
||||||
|
Manage tag info to agregate by user and by statistique
|
||||||
|
stats/data.json = {r:{
|
||||||
|
info:{useragent:"",ip:[]},
|
||||||
|
data:[ [timestamps, tit, cookie] ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats/graph.json = {"graphYears": {AAAA:#visites,"years":[AAAA,AAAA]},
|
||||||
|
"graphMonths": {AAAA:{"Jan":#visites,..},"monthsLabels":["Jan",..],"years":[AAAA,]},
|
||||||
|
"graphDaysOfWeek":{"labels":["Sun","Mon",...],"Sun":#visites},
|
||||||
|
"graphHoursOfDay":{"labels":["OOh","01h",...],"00h":#visites}
|
||||||
|
}
|
||||||
|
Pour tester des evolutions ou un client en dev (recuperer son repertoire de prod /tags )
|
||||||
|
ajouter Tags.dataloadstat('yes');
|
||||||
|
NODE_ENV=dev node ./models/Tags.js
|
||||||
|
*/
|
||||||
|
const agrege = {
|
||||||
|
data: {},
|
||||||
|
graph: {
|
||||||
|
visites: {
|
||||||
|
graphYears: { years: [] },
|
||||||
|
graphMonths: {
|
||||||
|
years: [],
|
||||||
|
monthsLabels: []
|
||||||
|
},
|
||||||
|
graphMonths: {
|
||||||
|
years: [],
|
||||||
|
monthsLabels: []
|
||||||
|
},
|
||||||
|
graphDayOfWeek: { labels: [] },
|
||||||
|
graphHourOfDay: { labels: [] }
|
||||||
|
},
|
||||||
|
visitors: {
|
||||||
|
graphMonths: {
|
||||||
|
years: [],
|
||||||
|
monthsLabels: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
agrege.data = jsonfile.readfileSync(`${config.tribes}/${tribeid}/tags/stats/data.json`, 'utf-8')
|
||||||
|
agrege.graph = jsonfile.readfileSync(`${config.tribes}/${tribeid}/tags/stats/graph.json`, 'utf-8')
|
||||||
|
} catch (err) {
|
||||||
|
logger.info("ATTENTION tag reinitialisé en data.json et graph.json, s'il s'agit de 1ere connexion pas de pb. Le risque est de perdre les tag historiques")
|
||||||
|
// return { status: 503, payload: { info: ['Errconfig'], model: 'Tags', moreinfo: `Il manque un ${config.tribes}/${tribeid}/tags/stats/data.json ou stats/graph.json` } }
|
||||||
|
}
|
||||||
|
glob.sync(`${config.tribes}/${tribeid}/tags/hits/*`)
|
||||||
|
.forEach(f => {
|
||||||
|
const hit = jsonfile.readFileSync(f)
|
||||||
|
const ts = parseInt(path.basename(f)
|
||||||
|
.split('.json')[0])
|
||||||
|
// logger.info(moment(ts).format('DD-MM-YYYY h:mm:ss'));
|
||||||
|
const tsm = moment(ts)
|
||||||
|
const year = tsm.format('YYYY')
|
||||||
|
const month = tsm.format('MMM')
|
||||||
|
const dayow = tsm.format('ddd')
|
||||||
|
const hourod = tsm.format('HH') + 'h'
|
||||||
|
let newvisitor = false
|
||||||
|
let alreadydone = false
|
||||||
|
// logger.info(hit.r, ts)
|
||||||
|
// Agrege data pour # visiteur vs # de visiteur
|
||||||
|
if (agrege.data[hit.r]) {
|
||||||
|
if (!agrege.data[hit.r].data.some(el => el[0] === ts)) {
|
||||||
|
// evite de charger plusieurs fois le même
|
||||||
|
agrege.data[hit.r].data.push([ts, hit.tit, hit.cookie])
|
||||||
|
} else {
|
||||||
|
alreadydone = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newvisitor = true
|
||||||
|
agrege.data[hit.r] = {
|
||||||
|
info: { useragent: hit.useragent, ip: [hit.ip] },
|
||||||
|
data: [
|
||||||
|
[ts, hit.tit, hit.cookie]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!alreadydone) {
|
||||||
|
if (newvisitor) {
|
||||||
|
// traite Month
|
||||||
|
if (!agrege.graph.visitors.graphMonths[year]) {
|
||||||
|
agrege.graph.visitors.graphMonths[year] = {}
|
||||||
|
agrege.graph.visitors.graphMonths.years.push(year)
|
||||||
|
}
|
||||||
|
if (agrege.graph.visitors.graphMonths[year][month]) {
|
||||||
|
agrege.graph.visitors.graphMonths[year][month] += 1
|
||||||
|
} else {
|
||||||
|
agrege.graph.visitors.graphMonths[year][month] = 1
|
||||||
|
agrege.graph.visitors.graphMonths.monthsLabels.push(month)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// traite graphe Year #visite
|
||||||
|
if (agrege.graph.visites.graphYears[year]) {
|
||||||
|
agrege.graph.visites.graphYears[year] += 1
|
||||||
|
} else {
|
||||||
|
agrege.graph.visites.graphYears[year] = 1
|
||||||
|
agrege.graph.visites.graphYears.years.push(year)
|
||||||
|
agrege.graph.visites.graphMonths[year] = {}
|
||||||
|
agrege.graph.visites.graphMonths.years.push(year)
|
||||||
|
}
|
||||||
|
// traite graphe Month
|
||||||
|
if (agrege.graph.visites.graphMonths[year][month]) {
|
||||||
|
agrege.graph.visites.graphMonths[year][month] += 1
|
||||||
|
} else {
|
||||||
|
agrege.graph.visites.graphMonths[year][month] = 1
|
||||||
|
agrege.graph.visites.graphMonths.monthsLabels.push(month)
|
||||||
|
}
|
||||||
|
// traite graphe Days of week
|
||||||
|
if (agrege.graph.visites.graphDayOfWeek[dayow]) {
|
||||||
|
agrege.graph.visites.graphDayOfWeek[dayow] += 1
|
||||||
|
} else {
|
||||||
|
agrege.graph.visites.graphDayOfWeek[dayow] = 1
|
||||||
|
agrege.graph.visites.graphDayOfWeek.labels.push(dayow)
|
||||||
|
}
|
||||||
|
// traite graphe Hour of day
|
||||||
|
if (agrege.graph.visites.graphHourOfDay[hourod]) {
|
||||||
|
agrege.graph.visites.graphHourOfDay[hourod] += 1
|
||||||
|
} else {
|
||||||
|
agrege.graph.visites.graphHourOfDay[hourod] = 1
|
||||||
|
agrege.graph.visites.graphHourOfDay.labels.push(hourod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
jsonfile.writeFileSync(`${config.tribes}/${tribeid}/tags/stats/data.json`, agrege.data, 'utf-8')
|
||||||
|
jsonfile.writeFileSync(`${config.tribes}/${tribeid}/tags/stats/graph.json`, agrege.graph, 'utf-8')
|
||||||
|
return { status: 200, payload: { info: ['Statsupdated'], model: 'Tags' } }
|
||||||
|
}
|
||||||
|
// logger.info(Tags.dataloadstat('yes'));
|
||||||
|
/* const ar = [
|
||||||
|
[1, 1],
|
||||||
|
[1, 2]
|
||||||
|
]
|
||||||
|
logger.info(ar.some(el => el[0] === 1 && el[1] === 1))
|
||||||
|
logger.info(ar.some(el => el === [1, 3]))
|
||||||
|
*/
|
||||||
|
module.exports = Tags
|
357
src/models/Tribes.js
Executable file
357
src/models/Tribes.js
Executable file
@ -0,0 +1,357 @@
|
|||||||
|
const bcrypt = require('bcrypt')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const path = require('path')
|
||||||
|
const glob = require('glob')
|
||||||
|
const Mustache = require('mustache')
|
||||||
|
const execSync = require('child_process')
|
||||||
|
.execSync
|
||||||
|
const dnsSync = require('dns-sync')
|
||||||
|
const jwt = require('jwt-simple')
|
||||||
|
const moment = require('moment')
|
||||||
|
const UUID = require('uuid')
|
||||||
|
const Outputs = require('./Outputs.js')
|
||||||
|
const Pagans = require('./Pagans.js')
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
const checkdata = require('../nationchains/socialworld/contracts/checkdata.js')
|
||||||
|
/*
|
||||||
|
tribeid manager
|
||||||
|
|
||||||
|
/tribes/tribeid
|
||||||
|
Manage a tribeid space
|
||||||
|
* create
|
||||||
|
* update by managing option and contract
|
||||||
|
* delete a tribeid
|
||||||
|
* check accountability and
|
||||||
|
|
||||||
|
*/
|
||||||
|
const Tribes = {}
|
||||||
|
Tribes.init = () => {
|
||||||
|
console.group('init Tribes')
|
||||||
|
const tribeids = []
|
||||||
|
const routes = glob.sync('./routes/*.js')
|
||||||
|
.map(f => {
|
||||||
|
return { url: `/${path.basename(f, '.js')}`, route: f }
|
||||||
|
})
|
||||||
|
let DOMs = []
|
||||||
|
const appname = {}
|
||||||
|
TribesGlobalConfig = glob.sync(`${config.tribes}/**/clientconf.json`)
|
||||||
|
.map(f => {
|
||||||
|
const conf = fs.readJSONSync(f)
|
||||||
|
// check if plugins exist and add it in .plugins of each tribeid conf
|
||||||
|
conf.plugins = glob.sync(`${config.tribes}/${conf.tribeid}/plugins/**/package.json`)
|
||||||
|
.map(p => {
|
||||||
|
const pack = fs.readJsonSync(p, 'utf8')
|
||||||
|
routes.push({
|
||||||
|
url: `/${pack.name}`,
|
||||||
|
route: `${config.tribes}/${conf.tribeid}/plugins/${pack.name}/route.js`
|
||||||
|
})
|
||||||
|
return pack
|
||||||
|
})
|
||||||
|
// Add here any other info to get a global view and init
|
||||||
|
// ...
|
||||||
|
tribeids.push(conf.tribeid)
|
||||||
|
DOMs = [...new Set([...DOMs, ...conf.allowedDOMs])]
|
||||||
|
if (conf.website) appname[conf.tribeid] = Object.keys(conf.website)
|
||||||
|
return conf
|
||||||
|
})
|
||||||
|
// store global conf fofs.existsSync( `${config.tmp}/clientconfglob.json` )r sharing to other api
|
||||||
|
fs.outputJsonSync(`${config.tmp}/clientconfglob.json`, TribesGlobalConfig, {
|
||||||
|
spaces: 2
|
||||||
|
})
|
||||||
|
return { tribeids, routes, DOMs, appname }
|
||||||
|
}
|
||||||
|
Tribes.create = (data) => {
|
||||||
|
/* data = clientconf.json
|
||||||
|
{
|
||||||
|
"tribeid": "apixtribe",
|
||||||
|
"genericpsw": "Trze3aze!",
|
||||||
|
"website": {
|
||||||
|
"presentation":"https://www.apixtribe.org",
|
||||||
|
"webapp": "https://webapp.apixtribe.org"
|
||||||
|
},
|
||||||
|
"allowedDOMs": ["local.fr", "localhost:9002", "ndda.fr", "apixtribe.org"],
|
||||||
|
"clientname": "apixtribe",
|
||||||
|
"clientlogo": "",
|
||||||
|
"geoloc": [],
|
||||||
|
"useradmin": {PUBKEY:"",EMAIL:"",LOGIN:"adminapixtribe",UUID:"adminapixtribe"},
|
||||||
|
"smtp": {
|
||||||
|
"emailFrom": "support@apixtribe.org",
|
||||||
|
"emailcc": [],
|
||||||
|
"service": "gmail",
|
||||||
|
"auth": {
|
||||||
|
"user": "antonin.ha@gmail.com",
|
||||||
|
"pass": "Ha06110"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"accepted-language": "fr,en",
|
||||||
|
"langueReferential": ["fr"]
|
||||||
|
}
|
||||||
|
What about:
|
||||||
|
"tribeid": same than the folder where all the client's file are stored
|
||||||
|
"genericpsw": a generic password for new user need upper lowercase number ans special char
|
||||||
|
"dnsname": a domain name belonging to the client
|
||||||
|
"subdns": "www", a sub domain subdns.dnsname give a public web access to
|
||||||
|
"website": { keywebsite:url}, give access to config.tribes/tribeid/www/keywebsite/index.html,
|
||||||
|
"allowedDOMs": ["local.fr", "localhost:9002", "nnda.fr"], //for CORS, @TODO generate from prévious URL this allow this apixtribe instance to be accessible
|
||||||
|
"clientname": Name of the organisation if any,
|
||||||
|
"clientlogo": logo of the organisation if any,
|
||||||
|
"geoloc": [], if any
|
||||||
|
"useradmin": { this is the 1st user create automaticaly to make gui available for the 1st user
|
||||||
|
"PUBKEY":public key to be authentify without an email,
|
||||||
|
"EMAIL":user email, we need at least one of authentification set up after the user can use both or only one
|
||||||
|
"LOGIN": login to use for access admintribeid,
|
||||||
|
"UUID": unique id normaly UUID but a uuid admintribeid is the same person in any apixtribe instance so we use it by convention.
|
||||||
|
"xlang": lang used by this user
|
||||||
|
},
|
||||||
|
"smtp": { smtp used to send email by nodemailer lib basic example with a google account
|
||||||
|
"emailFrom": "support@xx.fr",
|
||||||
|
"emailcc": [],
|
||||||
|
"service": "gmail",
|
||||||
|
"auth": {
|
||||||
|
"user": "antonin.ha@gmail.com",
|
||||||
|
"pass": "Ha06110"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"accepted-language": "fr,en", list of accepted-language in terme of http request.
|
||||||
|
"langueReferential": ["fr"], list of the text that have to be translate in referentials
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// update tmp/confglog.json
|
||||||
|
const dataclient = Tribes.init()
|
||||||
|
// return in prod all instance apxinfo={tribeids:[],logins:[]}
|
||||||
|
// in dev return only local
|
||||||
|
// check tribeid name is unique
|
||||||
|
logger.info('liste des tribeid', dataclient.tribeids)
|
||||||
|
if (dataclient.tribeids.includes(data.tribeid)) {
|
||||||
|
return { status: 403, payload: { model: 'client', info: ['tribeidalreadyexist'] } }
|
||||||
|
}
|
||||||
|
// loginsglob = {login:tribeid}
|
||||||
|
let loginsglob = {}
|
||||||
|
if (fs.existsSync(`${config.tmp}/loginsglob.json`, 'utf-8')) {
|
||||||
|
loginsglob = fs.readJsonSync(`${config.tmp}/loginsglob.json`, 'utf-8')
|
||||||
|
}
|
||||||
|
const logins = Object.keys(loginsglob)
|
||||||
|
if (logins.includes(data.useradmin.login)) {
|
||||||
|
return { status: 403, payload: { model: 'client', info: ['loginalreadyexist'] } }
|
||||||
|
}
|
||||||
|
fs.ensureDirSync(`${config.tribes}/${data.tribeid}`);
|
||||||
|
['users', 'www', 'referentials', 'nationchains'].forEach(r => {
|
||||||
|
fs.copySync(`${config.mainDir}/setup/tribes/apixtribe/${r}`, `${config.tribes}/${data.tribeid}/${r}`)
|
||||||
|
})
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${data.tribeid}/clientconf.json`, data)
|
||||||
|
const confcli = JSON.parse(Mustache.render(fs.readFileSync(`${config.mainDir}/setup/tribes/apixtribe/clientconf.mustache`, 'utf8'), data))
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${data.tribeid}/clientconf.json`, confcli)
|
||||||
|
|
||||||
|
return Pagans.createUser({
|
||||||
|
xpaganid: 'setup',
|
||||||
|
xworkon: data.tribeid,
|
||||||
|
xlang: data.useradmin.xlang
|
||||||
|
}, data.useradmin)
|
||||||
|
}
|
||||||
|
Tribes.archive = (tribeid) => {
|
||||||
|
// A faire zip un repertoire tribeid dans
|
||||||
|
// remove tribeid de data ou devdata
|
||||||
|
try {
|
||||||
|
fs.moveSync(`${config.tribes}/${tribeid}`, `${config.archivefolder}/${tribeid}`)
|
||||||
|
// update apixtribeenv
|
||||||
|
Tribes.init()
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: { info: ['deletetribeidsuccessfull'], models: 'Tribes', moreinfo: 'TODO see in Tribes.archive' }
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.info("Erreur d'archivage", err)
|
||||||
|
return { status: 403, payload: { info: ['archiveerror'], models: 'Tribes', moreinfo: err } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// /////////// Manage file for Tribes
|
||||||
|
Tribes.checkaccessfolder = (folder, typeaccessrequested, useraccessrights, useruuid) => {
|
||||||
|
// check folder right
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Tribes.checkaccessfiles = (listfile, typeaccessrequested, useraccessrights, useruuid) => {
|
||||||
|
// @listfile to check accessright on file or folder
|
||||||
|
// @typeaccessrequested on files R read or download, U for pdate, D for delete , O for owned a Owner has all rights RUD on its files
|
||||||
|
// @useraccessright from its account /userd/uuid.json
|
||||||
|
// @useruuid public uuid user
|
||||||
|
// return {'ok':[file auhtorized],'ko':[files not authorized]}
|
||||||
|
|
||||||
|
const checkauthlistfile = { ok: [], ko: [] }
|
||||||
|
let structf = []
|
||||||
|
let inforep = { file: {}, dir: {} }
|
||||||
|
let done
|
||||||
|
for (const f of listfile) {
|
||||||
|
done = false
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${f}`)) {
|
||||||
|
done = true
|
||||||
|
checkauthlistfile.ko.push(f)
|
||||||
|
logger.info(`${f} file does not exist`)
|
||||||
|
} else {
|
||||||
|
structf = f.split('/')
|
||||||
|
}
|
||||||
|
// on ckeck tribeid existe / tribeid/object/
|
||||||
|
if (!done &&
|
||||||
|
useraccessrights.data[structf[0]] &&
|
||||||
|
useraccessrights.data[structf[0]][structf[1]] &&
|
||||||
|
useraccessrights.data[structf[0]][structf[1]].includes(typeaccessrequested)) {
|
||||||
|
done = true
|
||||||
|
checkauthlistfile.ok.push(f)
|
||||||
|
} else {
|
||||||
|
// check if in folder we have a.info.json .file[f].shared{useruuid:'CRUDO'}
|
||||||
|
logger.info('structf', structf)
|
||||||
|
if (fs.existsSync(`${config.tribes}/${structf.slice(0, -1).join('/')}/.info.json`)) {
|
||||||
|
inforep = fs.readJsonSync(`${config.tribes}/${structf.slice(0, -1).join('/')}/.info.json`, 'utf8')
|
||||||
|
}
|
||||||
|
logger.info(`no accessrights for ${f} for ${useruuid} `)
|
||||||
|
}
|
||||||
|
if (!done && inforep.file[f] && inforep.file[f] && inforep.file[f].shared && inforep.file[f].shared[useruuid] && inforep.file[f].shared[useruuid].includes(typeaccessrequested)) {
|
||||||
|
done = true
|
||||||
|
checkauthlistfile.ok.push(f)
|
||||||
|
}
|
||||||
|
// If no authorization then ko
|
||||||
|
if (!done) {
|
||||||
|
checkauthlistfile.ko.push(f)
|
||||||
|
}
|
||||||
|
} // end loop for
|
||||||
|
// logger.info( 'checkauthlistfile', checkauthlistfile )
|
||||||
|
return checkauthlistfile
|
||||||
|
}
|
||||||
|
|
||||||
|
Tribes.dirls = (tribeid, dir) => {
|
||||||
|
/*
|
||||||
|
Return list of file into tribeid/dir
|
||||||
|
*/
|
||||||
|
let comment = { src: `${tribeid}/${dir}`, file: {}, dir: {} }
|
||||||
|
if (fs.existsSync(`${config.tribes}/${tribeid}/${dir}/.info.json`)) {
|
||||||
|
comment = fs.readJsonSync(`${config.tribes}/${tribeid}/${dir}/.info.json`, 'utf-8')
|
||||||
|
}
|
||||||
|
const listfile = []
|
||||||
|
const listdir = []
|
||||||
|
glob.sync(`${config.tribes}/${tribeid}/${dir}/*`)
|
||||||
|
.forEach(f => {
|
||||||
|
// logger.info( f )
|
||||||
|
const stats = fs.statSync(f)
|
||||||
|
// logger.info( stats )
|
||||||
|
if (stats.isFile()) {
|
||||||
|
listfile.push(path.basename(f))
|
||||||
|
if (!comment.file[path.basename(f)]) {
|
||||||
|
comment.file[path.basename(f)] = { tags: [], info: '', thumbb64: '' }
|
||||||
|
}
|
||||||
|
comment.file[path.basename(f)].mtime = stats.mtime
|
||||||
|
comment.file[path.basename(f)].ctime = stats.ctime
|
||||||
|
comment.file[path.basename(f)].size = stats.size
|
||||||
|
}
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
listdir.push(path.basename(f))
|
||||||
|
if (!comment.dir[path.basename(f)]) {
|
||||||
|
comment.dir[path.basename(f)] = { tags: [], info: '', thumbb64: '' }
|
||||||
|
}
|
||||||
|
comment.dir[path.basename(f)].nbfile = glob.sync(`${f}/*.*`)
|
||||||
|
.length
|
||||||
|
comment.dir[path.basename(f)].mtime = stats.mtime
|
||||||
|
comment.dir[path.basename(f)].ctime = stats.mtime
|
||||||
|
logger.info('comment.dir', comment.dir)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// on remove les file or dir that was deleted
|
||||||
|
Object.keys(comment.file)
|
||||||
|
.forEach(f => {
|
||||||
|
if (!listfile.includes(f)) delete comment.file[f]
|
||||||
|
})
|
||||||
|
Object.keys(comment.dir)
|
||||||
|
.forEach(d => {
|
||||||
|
if (!listdir.includes(d)) delete comment.dir[d]
|
||||||
|
})
|
||||||
|
// logger.info( comment )
|
||||||
|
fs.outputJson(`${config.tribes}/${tribeid}/${dir}/.info.json`, comment, 'utf-8')
|
||||||
|
return { status: 200, payload: { info: ['succestogetls'], models: 'Tribes', moreinfo: comment } }
|
||||||
|
}
|
||||||
|
Tribes.addspaceweb = (data) => {
|
||||||
|
/*
|
||||||
|
To create a public spaceweb accessible from https://dnsname/pageindex
|
||||||
|
|
||||||
|
input:
|
||||||
|
{dnsname:["archilinea.fr","www.archilinea.fr"], 1st is tha main dns other are just servername redirection
|
||||||
|
tribeid:"archilinea", from req.session.header.xworkon
|
||||||
|
website:"presentation",
|
||||||
|
pageindex:"app_index_fr.html"
|
||||||
|
mode:dev(local no ssl) | prod(IP + ssl)
|
||||||
|
}
|
||||||
|
output:
|
||||||
|
nginx conf and ssl to serve each https://dnsname to /{tribeid}/www/app/{website}
|
||||||
|
|
||||||
|
Carefull this action is executed with root and restart nginx + apixtribe to work
|
||||||
|
*/
|
||||||
|
data.configdomain = config.tribes
|
||||||
|
data.porthttp = config.porthttp
|
||||||
|
console.assert(config.loglevel === 'quiet', 'data to create spaceweb:', data)
|
||||||
|
// create spaceweb app for tribeid/www/app/website/pageindexname.html
|
||||||
|
if (!fs.existsSync(`${config.tribes}/${data.tribeid}/www/app/${data.website}`)) {
|
||||||
|
fs.outputFileSync(`${config.tribes}/${data.tribeid}/www/app/${data.website}/${data.pageindex}`, `<h1>Hello ${data.tribeid} ${data.website} onto ${data.dnsname.join(',')}`, 'utf-8')
|
||||||
|
}
|
||||||
|
// create logs folder
|
||||||
|
fs.ensureDirSync(`${config.tribes}/${data.tribeid}/logs/nginx`)
|
||||||
|
// add nginx http config
|
||||||
|
const confnginx = fs.readFileSync('setup/nginx/modelwebsite.conf.mustache', 'utf-8')
|
||||||
|
fs.outputFileSync(`/etc/nginx/conf.d/${data.dnsname[0]}.conf`, Mustache.render(confnginx, data), 'utf-8')
|
||||||
|
if (data.mode === 'dev') {
|
||||||
|
// add in /etc/hosts
|
||||||
|
let hosts = fs.readFileSync('/etc/hosts', 'utf8')
|
||||||
|
let chg = false
|
||||||
|
data.dnsname.forEach(d => {
|
||||||
|
if (!hosts.includes(`127.0.0.1 ${d}`)) {
|
||||||
|
hosts += `\n127.0.0.1 ${d}`
|
||||||
|
chg = true
|
||||||
|
}
|
||||||
|
if (chg) {
|
||||||
|
fs.outputFileSync('/etc/hosts', hosts, 'utf8')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
;
|
||||||
|
// Ckeck dns respond
|
||||||
|
data.dnsname.forEach(d => {
|
||||||
|
if (!dnsSync.resolve(`${d}`)) {
|
||||||
|
rep += `\nresolving ${d} will not responding valid IP, please setup domain redirection IP before runing this script`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// update clienconf.json
|
||||||
|
const clientconf = fs.readJsonSync(`${config.tribes}/${data.tribeid}/clientconf.json`)
|
||||||
|
clientconf.website[data.website] = data.dnsname[0]
|
||||||
|
// merge allowedDOMs in unique concat
|
||||||
|
clientconf.allowedDOMs = [...new Set(...clientconf.allowedDOMs, ...data.dnsname)]
|
||||||
|
fs.outputJsonSync(`${config.tribes}/${data.tribeid}/clientconf.json`, clientconf, 'utf-8')
|
||||||
|
if (!data.setup) {
|
||||||
|
// in setup apixtribe is not running and it will be start manually at the 1st run
|
||||||
|
// in other case need to plan a restart for CORS
|
||||||
|
setTimeout(Tribes.restartapixtribe, 300000, data.clienId)
|
||||||
|
}
|
||||||
|
const nginxrestart = execSync('sudo systemctl restart nginx')
|
||||||
|
.toString()
|
||||||
|
logger.info('Restart nginx', nginxrestart)
|
||||||
|
if (data.mode === 'prod') {
|
||||||
|
// get ssl certificate ATTENTION il faut ajouter -d devant chaque domain qui redirige vers l'espace web.
|
||||||
|
const certbot = execSync(`sudo certbot --nginx -d ${data.dnsname.join(' -d ')}`)
|
||||||
|
.toString()
|
||||||
|
logger.info('certbot is running A CHECKER POUR UNE VRAIE PROD ????', certbot)
|
||||||
|
}
|
||||||
|
// sh execution to update change requested
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
model: 'Tribes',
|
||||||
|
info: ['webspacecreated'],
|
||||||
|
moreinfo: 'Space web well created'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tribes.restartapixtribe = (tribeid) => {
|
||||||
|
logger.info('A restarting was requested 5mn ago from a new spacedev for ' + tribeid)
|
||||||
|
execSync('yarn restartpm2')
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Tribes
|
154
src/models/UploadFiles.js
Executable file
154
src/models/UploadFiles.js
Executable file
@ -0,0 +1,154 @@
|
|||||||
|
const fs = require('fs-extra')
|
||||||
|
const path = require('path')
|
||||||
|
const formidable = require('formidable')
|
||||||
|
const jsonfile = require('jsonfile')
|
||||||
|
const mustache = require('mustache')
|
||||||
|
const config = require('../tribes/townconf.js')
|
||||||
|
|
||||||
|
const logger = require('../core/logger')
|
||||||
|
|
||||||
|
/*
|
||||||
|
model A SUPPRIMER !!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
les functions d'upload de file et de droits d'accès doivent être gérer dans Tribes
|
||||||
|
|
||||||
|
*/
|
||||||
|
const UploadFiles = {}
|
||||||
|
UploadFiles.get = function (filename, header) {
|
||||||
|
// check file exist
|
||||||
|
const file = `${config.tribes}/${header.xworkon}/${filename}`
|
||||||
|
// logger.info('fichier demande ', file);
|
||||||
|
if (!fs.existsSync(file)) {
|
||||||
|
// logger.info('le fichier demande n existe pas ', file);
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: { info: ['fileUnknown'], model: 'UploadFiles' }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info('envoie le fichier ', file)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: { info: ['fileknown'], model: 'UploadFiles', file }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UploadFiles.addjson = function (data, header) {
|
||||||
|
/*
|
||||||
|
Le header = {X-WorkOn:"",destinationfile:"", filename:""}
|
||||||
|
Le body = {jsonp:{},callback:function to launch after download,'code':'mot cle pour verifier que le fichier est à garder'}
|
||||||
|
*/
|
||||||
|
// logger.info(req.body.jsonp);
|
||||||
|
try {
|
||||||
|
jsonfile.writeFileSync(header.destinationfile + '/' + header.filename, data.jsonp)
|
||||||
|
if (data.callback) {
|
||||||
|
const execCB = require(`${config.mainDir}/models/tribeid/${
|
||||||
|
header.xworkon
|
||||||
|
}`)
|
||||||
|
execCB[data.callback]()
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['wellUpload'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
render: {
|
||||||
|
destination: header.destinationfile,
|
||||||
|
filename: header.filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Impossible de sauvegarder le fichier, A COMPRENDRE', err)
|
||||||
|
return {
|
||||||
|
status: 503,
|
||||||
|
payload: { info: ['savingError'], model: 'UploadFiles' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UploadFiles.add = function (req, header) {
|
||||||
|
const form = new formidable.IncomingForm()
|
||||||
|
logger.info('req.headers', req.headers)
|
||||||
|
logger.info('req.params', req.params)
|
||||||
|
logger.info('req.query', req.query)
|
||||||
|
logger.info('req.body', req.body)
|
||||||
|
let destinationfile = `${config.tribes}/${header.xworkon}/${
|
||||||
|
header.destinationfile
|
||||||
|
}`
|
||||||
|
form.parse(req, function (err, fields, files) {
|
||||||
|
logger.info('files', files.file.path)
|
||||||
|
logger.info('fields', fields)
|
||||||
|
const oldpath = files.file.path
|
||||||
|
destinationfile += '/' + files.file.name
|
||||||
|
logger.info('oldpath', oldpath)
|
||||||
|
logger.info('destinationfile', destinationfile)
|
||||||
|
fs.copyFile(oldpath, destinationfile, function (err) {
|
||||||
|
if (err) {
|
||||||
|
logger.info(err)
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
payload: { info: ['savingError'], model: 'UploadFiles' }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info('passe')
|
||||||
|
fs.unlink(oldpath)
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: {
|
||||||
|
info: ['wellUpload'],
|
||||||
|
model: 'UploadFiles',
|
||||||
|
render: {
|
||||||
|
destination: destinationfile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
UploadFiles.updateEvent = function (domainId, eventId, event) {
|
||||||
|
// checkAndCreateNeededDirectories(domainId);
|
||||||
|
const eventsFile = `${config.tribes}/${domainId}/actions/events/events.json`
|
||||||
|
if (!fs.existsSync(eventsFile)) {
|
||||||
|
jsonfile.writeFileSync(eventsFile, {})
|
||||||
|
return { status: 404, payload: 'You have not any events.' }
|
||||||
|
}
|
||||||
|
const events = jsonfile.readFileSync(eventsFile)
|
||||||
|
if (!events.hasOwnProperty(eventId)) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: 'The event you are trying to update does not exist.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events[eventId] = {
|
||||||
|
eventName: event.eventName,
|
||||||
|
eventDate: event.eventDate,
|
||||||
|
eventDescription: event.eventDescription
|
||||||
|
}
|
||||||
|
jsonfile.writeFileSync(eventsFile, events, { spaces: 2 })
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: events
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UploadFiles.deleteEvent = function (domainId, eventId) {
|
||||||
|
// checkAndCreateNeededDirectories(domainId);
|
||||||
|
const eventsFile = `${config.tribes}/${domainId}/actions/events/events.json`
|
||||||
|
if (!fs.existsSync(eventsFile)) {
|
||||||
|
jsonfile.writeFileSync(eventsFile, {})
|
||||||
|
return { status: 404, payload: 'You have not any events.' }
|
||||||
|
}
|
||||||
|
const events = jsonfile.readFileSync(eventsFile)
|
||||||
|
if (events.hasOwnProperty(eventId)) {
|
||||||
|
delete events[eventId]
|
||||||
|
jsonfile.writeFileSync(eventsFile, events, { spaces: 2 })
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
payload: events
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
payload: 'The event you are trying to delete does not exist.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = UploadFiles
|
184
src/nationchains/socialworld/contracts/checkdata.js
Executable file
184
src/nationchains/socialworld/contracts/checkdata.js
Executable file
@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
This module have to be independant of any external package
|
||||||
|
it is shared between back and front and is usefull
|
||||||
|
to apply common check in front before sending it in back
|
||||||
|
can be include in project with
|
||||||
|
<script src="https://apiback.maildigit.fr/js/checkdata.js"></script>
|
||||||
|
or with const checkdata = require('../public/js/checkdata.js')
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// --##
|
||||||
|
|
||||||
|
const checkdata = {}
|
||||||
|
// each checkdata.test. return true or false
|
||||||
|
checkdata.test = {}
|
||||||
|
|
||||||
|
checkdata.test.emailadress = (ctx, email) => {
|
||||||
|
const regExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||||
|
return regExp.test(email)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* @emaillist = "email1,email2, email3"
|
||||||
|
* it check if each eamil separate by , are correct
|
||||||
|
*/
|
||||||
|
checkdata.test.emailadresslist = (ctx, emaillist) => {
|
||||||
|
// logger.info(emaillist.split(','))
|
||||||
|
if (emaillist.length > 0) {
|
||||||
|
const emails = emaillist.split(',')
|
||||||
|
for (const i in emails) {
|
||||||
|
// logger.info(emails[i])
|
||||||
|
if (!checkdata.test.emailadress('', emails[i].trim())) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
checkdata.test.password = (ctx, pwd) => {
|
||||||
|
const regExp = new RegExp(
|
||||||
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&.])[A-Za-z\d$@$!%*?&.{}:|\s]{8,}/
|
||||||
|
)
|
||||||
|
return regExp.test(pwd)
|
||||||
|
}
|
||||||
|
checkdata.test.required = (ctx, val) =>
|
||||||
|
(val !== null && val !== 'undefined' && val.length > 0) || (!!val && val.constructor ==== Array && val.length > 0) || (!!val && val.constructor ==== Object && Object.keys(val)
|
||||||
|
.length > 0)
|
||||||
|
|
||||||
|
checkdata.test.isNumber = (ctx, n) => typeof n ==== 'number'
|
||||||
|
checkdata.test.isInt = (ctx, n) => n !== '' && !isNaN(n) && Math.round(n) === n
|
||||||
|
checkdata.test.isFloat = (ctx, n) => n !== '' && !isNaN(n) && Math.round(n) !== n
|
||||||
|
checkdata.test.unique = (ctx, val) => {
|
||||||
|
if (ctx.list[ctx.currentfield]) {
|
||||||
|
return !ctx.list[ctx.currentfield].includes(val)
|
||||||
|
} else {
|
||||||
|
logger.info('ERR no list for field:' + ctx.currentfield)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkdata.test.isDateDay = (ctx, dateDay) => true
|
||||||
|
/* checkdata.test.filterInvalidInArray = (array, validate) =>
|
||||||
|
array ? array.filter(el => !validate(el)) : true;
|
||||||
|
// return true when every elements is valid
|
||||||
|
*/
|
||||||
|
|
||||||
|
checkdata.test.postalCode = (ctx, postalCode) => {
|
||||||
|
if (postalCode.length === 0) return true
|
||||||
|
const regExp = new RegExp(/(^\d{5}$)|(^\d{5}-\d{4}$)/)
|
||||||
|
return regExp.test(postalCode)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* PHONE
|
||||||
|
*/
|
||||||
|
checkdata.test.phoneNumber = (ctx, phoneNumber) => {
|
||||||
|
if (phoneNumber.length === 0) return true
|
||||||
|
phoneNumber = phoneNumber.trim()
|
||||||
|
.replace(/[- .]/g, '')
|
||||||
|
// french number
|
||||||
|
const regExpfr = new RegExp(/^0[1-9][0-9]{9}$/)
|
||||||
|
const regExpInternational = new RegExp(/^\+*(\d{3})*[0-9,\-]{8,}/)
|
||||||
|
return regExpfr.test(phoneNumber) || regExpInternational.test(phoneNumber)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* @phonelist = "phone1,phone2,phone3"
|
||||||
|
* it check if each phone separate by , are correct
|
||||||
|
*/
|
||||||
|
checkdata.test.phoneNumberlist = (ctx, phonelist) => {
|
||||||
|
// logger.info(emaillist.split(','))
|
||||||
|
if (phonelist.length > 0) {
|
||||||
|
const phones = phonelist.split(',')
|
||||||
|
for (const i in phones) {
|
||||||
|
// logger.info(emails[i])
|
||||||
|
if (!checkdata.test.phoneNumber('', phones[i].trim())) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkdata.normalize take a correct data then reformat it to harmonise it
|
||||||
|
checkdata.normalize = {}
|
||||||
|
checkdata.normalize.phoneNumber = (ctx, phone) => {
|
||||||
|
phone = phone.trim()
|
||||||
|
.replace(/[- .]/g, '')
|
||||||
|
if (checkdata.test.phoneNumber('', phone) && phone.length === 10 && phone[0] === '0') {
|
||||||
|
phone = '+33 ' + phone.substring(1)
|
||||||
|
}
|
||||||
|
return phone
|
||||||
|
}
|
||||||
|
checkdata.normalize.upperCase = (ctx, txt) => txt.toUpperCase()
|
||||||
|
checkdata.normalize.lowerCase = (ctx, txt) => txt.toLowerCase()
|
||||||
|
// fixe 10 position et complete par des 0 devant
|
||||||
|
checkdata.normalize.zfill10 = (ctx, num) => {
|
||||||
|
let s = num + ''
|
||||||
|
while (s.length < 10) s = '0' + s
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
/* let tt = "+33 1 02.03 04 05";
|
||||||
|
logger.info(checkdata.test.phoneNumber('', tt))
|
||||||
|
logger.info(checkdata.normalize.phoneNumber('', tt))
|
||||||
|
*/
|
||||||
|
checkdata.evaluate = (contexte, referential, data) => {
|
||||||
|
/*
|
||||||
|
* contexte object {} with full info for evaluation
|
||||||
|
* file referential path to get object to apply
|
||||||
|
* data related to object
|
||||||
|
- return {validefor =[keyword of error] if empty no error,
|
||||||
|
clean data eventually reformated
|
||||||
|
updateDatabase}
|
||||||
|
*/
|
||||||
|
logger.info('contexte', contexte)
|
||||||
|
logger.info('referentiel', referential)
|
||||||
|
logger.info('data', data)
|
||||||
|
const invalidefor = []
|
||||||
|
const objectdef = {}
|
||||||
|
const listfield = referential.map(ch => {
|
||||||
|
objectdef[ch.idfield] = ch
|
||||||
|
return ch.idfield
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.keys(data)
|
||||||
|
.forEach(field => {
|
||||||
|
if (!listfield.includes(field)) {
|
||||||
|
// some data can be inside an object with no control at all
|
||||||
|
// they are used for process only
|
||||||
|
// i leave it in case it will become a non sens
|
||||||
|
// invalidefor.push('ERRFIELD unknown of referentials ' + field);
|
||||||
|
} else {
|
||||||
|
if (objectdef[field].check) {
|
||||||
|
// check data with rule list in check
|
||||||
|
objectdef[field].check.forEach(ctrl => {
|
||||||
|
logger.info('ctrl', ctrl)
|
||||||
|
contexte.currentfield = field
|
||||||
|
if (!checkdata.test[ctrl]) {
|
||||||
|
invalidefor.push('ERR check function does not exist :' + ctrl + '___' + field)
|
||||||
|
} else {
|
||||||
|
if (!checkdata.test[ctrl](contexte, data[field])) { invalidefor.push('ERR' + ctrl + '___' + field) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectdef[field].nouserupdate) {
|
||||||
|
// check if user can modify this information
|
||||||
|
logger.info(
|
||||||
|
'evaluation :' + field + ' -- ' + objectdef[field].nouserupdate,
|
||||||
|
eval(objectdef[field].nouserupdate)
|
||||||
|
)
|
||||||
|
const evalright = eval(objectdef[field].nouserupdate)
|
||||||
|
objectdef[field].nouserupdate = evalright
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
logger.info({
|
||||||
|
invalidefor,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
invalidefor,
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !==== 'undefined') module.exports = checkdata
|
604
src/nationchains/socialworld/contracts/toolsbox.js
Executable file
604
src/nationchains/socialworld/contracts/toolsbox.js
Executable file
@ -0,0 +1,604 @@
|
|||||||
|
/* eslint-disable no-useless-escape */
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
const bcrypt = require('bcrypt')
|
||||||
|
const moment = require('moment')
|
||||||
|
const config = require('../config')
|
||||||
|
const utils = {}
|
||||||
|
|
||||||
|
logger.info("Check in /utils/index.js to find usefull function for your dev.\n Feel free to send suggestion, code to maintainer of apixtribe project (see /package.json to get email).\n We'll add to the roadmap to add it.")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EMAIL
|
||||||
|
*/
|
||||||
|
/* const validateEmail = email => {
|
||||||
|
const regExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
return regExp.test(email);
|
||||||
|
};
|
||||||
|
|
||||||
|
const validatePassword = pwd => {
|
||||||
|
const regExp = new RegExp(
|
||||||
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&.])[A-Za-z\d$@$!%*?&.{}:|\s]{8,}/
|
||||||
|
);
|
||||||
|
return regExp.test(pwd);
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterInvalidInArray = (array, validate) =>
|
||||||
|
array ? array.filter(el => !validate(el)) : undefined; // return undefined when every elements is valid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POSTAL CODE
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
const validatePostalCode = postalCode =>
|
||||||
|
/(^\d{5}$)|(^\d{5}-\d{4}$)/.test(postalCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHONE
|
||||||
|
*/
|
||||||
|
/* const validatePhoneNumber = phoneNumber =>
|
||||||
|
/((^0[1-9]|\+[0-9]{3})([-. ]?[0-9]{2}){4}$)/.test(phoneNumber);
|
||||||
|
|
||||||
|
const correctPhoneNumber = phone =>
|
||||||
|
phone[0] ==== '0' ? '+33' + phone.substr(1) : phone;
|
||||||
|
|
||||||
|
const checkData = (appProfil, referential, data) => {
|
||||||
|
// @TODO get a referentiel per object then check data validity and allowed access
|
||||||
|
// need to add referentiel manager
|
||||||
|
const invalidefor = [];
|
||||||
|
let updateDatabase = false;
|
||||||
|
Object.keys(data).forEach(field => {
|
||||||
|
switch (field) {
|
||||||
|
case 'token':
|
||||||
|
updateDatabase = true;
|
||||||
|
break;
|
||||||
|
case 'email':
|
||||||
|
if (!validateEmail(data.email)) {
|
||||||
|
invalidefor.push('ERREMAIL:' + field);
|
||||||
|
} else {
|
||||||
|
updateDatabase = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'password':
|
||||||
|
if (!validatePassword(data.password)) {
|
||||||
|
invalidefor.push('ERRPWD:' + field);
|
||||||
|
} else {
|
||||||
|
data.password = bcrypt.hash(data.password, config.saltRounds);
|
||||||
|
updateDatabase = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { invalidefor, data, updateDatabase };
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
// Permet d'attendre en milliseconde
|
||||||
|
// s'utilise avec async ()=>{
|
||||||
|
// await sleep(2000)
|
||||||
|
// }
|
||||||
|
utils.sleep = (ms) => {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
}
|
||||||
|
utils.generemdp = (nbpos) => {
|
||||||
|
const chaine = 'ABCDEFGHIJKLMNPQRSTUVWZY123456789'
|
||||||
|
let mdp = ''
|
||||||
|
for (let i = 0; i < nbpos; i++) {
|
||||||
|
const pos = Math.floor(Math.random() * chaine.length)
|
||||||
|
mdp += chaine.substring(pos, pos + 1)
|
||||||
|
}
|
||||||
|
return mdp
|
||||||
|
}
|
||||||
|
utils.generecompteur = (filecpt, typeincrement) => {
|
||||||
|
let file = `${filecpt}/${typeincrement}.json`
|
||||||
|
let prefix = ''
|
||||||
|
if (typeincrement = 'ANNEESEMAINE') {
|
||||||
|
file = `${filecpt}/${typeincrement}${moment().format('YYYY')}${moment().format('WW')}.json`
|
||||||
|
prefix = `${moment().format('YYYY')}${moment().format('WW')}`
|
||||||
|
}
|
||||||
|
let num = 1
|
||||||
|
try {
|
||||||
|
num = parseInt(fs.readFileSync(file, 'utf8')) + 1
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('Nouveau compteur incrementale ', file)
|
||||||
|
}
|
||||||
|
fs.writeFileSync(file, num, 'utf8')
|
||||||
|
return prefix + num
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* CSV
|
||||||
|
*/
|
||||||
|
utils.json2csv = (jsondata, options, callback) => {
|
||||||
|
// uniquement json = [{niv1:val,niv1:[liste of val]}]
|
||||||
|
// logger.info('_________________________');
|
||||||
|
// logger.info(jsondata)
|
||||||
|
// logger.info('_________________________');
|
||||||
|
if (jsondata.length === 0) {
|
||||||
|
return callback('Empty json', null)
|
||||||
|
}
|
||||||
|
if (!options.retln) options.retln = '\n'
|
||||||
|
if (!options.sep) options.sep = ';'
|
||||||
|
if (!options.arraysplitsep) options.arraysplitsep = ','
|
||||||
|
if (!options.replacespecialcarJson2Csv) {
|
||||||
|
options.replacespecialcarJson2Csv = []
|
||||||
|
} else {
|
||||||
|
if (typeof options.replacespecialcarJson2Csv ==== 'string') {
|
||||||
|
// permet de passer des regex en string
|
||||||
|
options.replacespecialcarJson2Csv = eval(options.replacespecialcarJson2Csv)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const etat = ''
|
||||||
|
let csv = ''
|
||||||
|
let entete = ''
|
||||||
|
let prem = true
|
||||||
|
for (const j in jsondata) {
|
||||||
|
// logger.info(jsondata[j])
|
||||||
|
for (const c in options.champs) {
|
||||||
|
if (prem) {
|
||||||
|
entete += options.champs[c] + options.sep
|
||||||
|
}
|
||||||
|
if (jsondata[j][options.champs[c]]) {
|
||||||
|
if (options.array.indexOf(options.champs[c]) > -1) {
|
||||||
|
csv += jsondata[j][options.champs[c]].join(options.arraysplitsep) + options.sep
|
||||||
|
} else {
|
||||||
|
let currentValue = ''
|
||||||
|
if (jsondata[j][options.champs[c]]) currentValue += jsondata[j][options.champs[c]]
|
||||||
|
options.replacespecialcarJson2Csv.forEach(re => {
|
||||||
|
// logger.info(currentValue)
|
||||||
|
currentValue = currentValue.replace(re[1], re[0])
|
||||||
|
})
|
||||||
|
csv += currentValue + options.sep
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
csv += options.sep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
csv = csv.substring(0, csv.length - 1) + options.retln
|
||||||
|
if (prem) {
|
||||||
|
prem = false
|
||||||
|
entete = entete.substring(0, entete.length - 1) + options.retln
|
||||||
|
// logger.info(entete)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return entete + csv;
|
||||||
|
if (etat === '') {
|
||||||
|
return callback(null, entete + csv)
|
||||||
|
} else {
|
||||||
|
return callback(etat, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get headers from first line of CSV
|
||||||
|
* @param {array} lines array of string which contains each csv lines
|
||||||
|
* @return {array} string array of headers
|
||||||
|
*/
|
||||||
|
utils.getHeaders = (lines, sep) => lines[0].split(sep)
|
||||||
|
.map(i => i.replace(/"/g, ''))
|
||||||
|
/**
|
||||||
|
* [csv2json description]
|
||||||
|
* @param {object} csv object of csv file that has been read
|
||||||
|
* @param {object} options object containing csv options, headers, ...
|
||||||
|
{retln:'code de retour de ligne \n ou \n\r',
|
||||||
|
sep:'code to split cells',
|
||||||
|
champs:[ch1,ch2,...] catch only those field,
|
||||||
|
array:[ch1, ] can have more than one field champs with same name then data are push into an array }
|
||||||
|
* @param {Function} callback callback function
|
||||||
|
* @return {callback} - return an error if error, else return json
|
||||||
|
it convert a csv file into a json = [{field:value}]
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
fiche.csv2article = (err, fiche) => {
|
||||||
|
if (!err) {
|
||||||
|
logger.info(fiche)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
utils.csv2json(fs.readFileSync('./devdata/tribee/aubergenville/infoexterne/localbusiness.csv', 'utf-8'), {
|
||||||
|
retln: "\n",
|
||||||
|
sep: ";",
|
||||||
|
champs: ["NOM", "OBJET", "ADRESSE_PRO", "CP_PRO", "VILLE_PRO", "ZONE", "PHONE_PRO", "HORAIRESDESC", "HORAIREDATA", "URL", "FACEBOOK", "INSTA", "EMAIL_PRO", "IMG", "TAG"],
|
||||||
|
array: ["TAG", "PHONE_PRO", "EMAIL_PRO"]
|
||||||
|
}, fiche.csv2article)
|
||||||
|
|
||||||
|
*/
|
||||||
|
utils.replacecarbtweendblquote = (csv, car, carremplacant) => {
|
||||||
|
/*
|
||||||
|
return csv text with any car betwenn 2 " by CARSEPARATOR
|
||||||
|
*/
|
||||||
|
let newcsv = ''
|
||||||
|
let txtencours = ''
|
||||||
|
let flagouvert = false
|
||||||
|
const sepreg = new RegExp(`${car}`, 'gmi')
|
||||||
|
for (let j = 0; j < csv.length; j++) {
|
||||||
|
// if((csv[j] === "\"" && csv[j + 1] && csv[j + 1] !== "\"") || (csv[j] === "\"" && csv[j - 1] && csv[j - 1] !== "\"") || (csv[j] === "\"" && csv[j - 1] && csv[j - 2] && csv[j - 1] !== "\"" && csv[j - 2] !== "\"")) {
|
||||||
|
if (csv[j] === '"') {
|
||||||
|
if (flagouvert) {
|
||||||
|
// on cherche à ferme une chaine de texte
|
||||||
|
if (csv[j + 1] === '"') {
|
||||||
|
// on a "" consecutif qu'on remplace par "" et on fait j+1
|
||||||
|
txtencours += '""'
|
||||||
|
j++
|
||||||
|
} else {
|
||||||
|
// on a bien une fermeture
|
||||||
|
flagouvert = false
|
||||||
|
newcsv += txtencours.replace(sepreg, carremplacant)
|
||||||
|
txtencours = '"'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// on ouvre une chaine
|
||||||
|
flagouvert = true
|
||||||
|
// on met le contenu précédent ds newcsv
|
||||||
|
newcsv += txtencours
|
||||||
|
txtencours = '"'
|
||||||
|
}
|
||||||
|
// } else if((csv[j] !==== "\n") && (csv[j + 1] && csv[j] + csv[j + 1] !==== "\n\r")) {
|
||||||
|
} else if (csv[j] !==== '\n') {
|
||||||
|
txtencours += csv[j]
|
||||||
|
// } else if((csv[j] === "\n") || (csv[j + 1] && csv[j] + csv[j + 1] === "\n\r")) {
|
||||||
|
} else if (csv[j] === '\n') {
|
||||||
|
if (!flagouvert) txtencours += '\n'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newcsv + txtencours
|
||||||
|
}
|
||||||
|
utils.analysestring = (string) => {
|
||||||
|
let buftxt = ''
|
||||||
|
let bufcode = ''
|
||||||
|
let i = 0
|
||||||
|
let avecRL = false
|
||||||
|
for (let p = 0; p < string.length; p++) {
|
||||||
|
if (string[p].charCodeAt() === 10) {
|
||||||
|
buftxt += '[RL]'
|
||||||
|
avecRL = true
|
||||||
|
} else {
|
||||||
|
buftxt += string[p]
|
||||||
|
}
|
||||||
|
bufcode += '-' + string[p].charCodeAt()
|
||||||
|
if (i === 20) {
|
||||||
|
if (avecRL) {
|
||||||
|
logger.info(`${buftxt} - ${bufcode}`)
|
||||||
|
} else {
|
||||||
|
logger.info(`${buftxt} ---- ${bufcode}`)
|
||||||
|
}
|
||||||
|
i = 0
|
||||||
|
buftxt = ''
|
||||||
|
bufcode = ''
|
||||||
|
avecRL = false
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const txtstring = `32932,BK_F2F_B_COM_10x1H-09,"My Communication Workshop ""Session N°9 - 1H""","<p> </p>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<p>Learner who needs to develop their ability to communicate effectively at work, both in writing and speaking</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>",,english,2,0,,2,0,classroom,"0000-00-00 00:00:00","0000-00-00 00:00:00",0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32932,1101,,"BUSINESS KEYS",0,
|
||||||
|
32933,BK_F2F_B_COM_10x1H-10,"My Communication Workshop Session N°10 - 1H","<p> </p>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<p>Learner who needs to develop their ability to communicate effectively at work, both in writing and speaking</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>",,english,2,0,,2,0,classroom,"0000-00-00 00:00:00","0000-00-00 00:00:00",0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32933,1101,,"BUSINESS KEYS",0,
|
||||||
|
32934,BK_F2F_B_JOB_10x1H-01,"My Job Search Workshop Session N°1 - 1H","<p>PACK JOB SEARCH</p>",,english,2,0,,2,0,classroom,,,0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32934,1108,,,0,
|
||||||
|
32935,BK_F2F_B_JOB_10x1H-02,"My Job Search Workshop Session N°2 - 1H","<p>PACK JOB SEARCH</p>",,english,2,0,,2,0,classroom,,,0000-00-00,0000-00-00,https://www.yesnyoulearning.com/lms/index.php?r=player&course_id=32935,1108,,,0,`
|
||||||
|
// utils.analysestring(txtstring)
|
||||||
|
// logger.info(utils.replacecarbtweendblquote(txtstring, ",", 'CARSEPARATOR')
|
||||||
|
// .split("\n")[0].split(","))
|
||||||
|
utils.csv2json = (csv, options, callback) => {
|
||||||
|
// EN CAS DE PB AVEC UN FICHIER EXCEL RECALCITRANT
|
||||||
|
// l'ouvrir dans calc linux et sauvegarder csv utf8, ; , " enregistrer le contenu de la cellule comme affiché
|
||||||
|
logger.info('\n--------------- CSV2JSON ---------------\n')
|
||||||
|
// Default CSV options
|
||||||
|
if (!options.retln) options.retln = '\n'
|
||||||
|
if (csv.indexOf('\n\r') > -1) options.retln = '\n\r'
|
||||||
|
if (!options.sep) options.sep = ';'
|
||||||
|
// gestion d un separateur dans une chaine de texte
|
||||||
|
// const regseptext = new RegExp(`${options.sep}(?!(?:[^"]*"[^"]*")*[^"]*$)`, 'gm');
|
||||||
|
// csv = csv.replace(regseptext, "CARACSEPAR");
|
||||||
|
// csv = utils.replacecarbtweendblquote(csv, options.retln, "RETLIGNE")
|
||||||
|
csv = utils.replacecarbtweendblquote(csv, options.sep, 'CARSEPARATOR')
|
||||||
|
if (!options.replacespecialcarCsv2Json) {
|
||||||
|
options.replacespecialcarCsv2Json = []
|
||||||
|
} else {
|
||||||
|
if (typeof options.replacespecialcarCsv2Json ==== 'string') {
|
||||||
|
// permet de passer des regex en string
|
||||||
|
options.replacespecialcarCsv2Json = eval(options.replacespecialcarCsv2Json)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = []
|
||||||
|
const lines = csv.split(options.retln)
|
||||||
|
const headers = utils.getHeaders(lines, options.sep)
|
||||||
|
let unknownHeaders = ''
|
||||||
|
// logger.info('headers', headers)
|
||||||
|
// logger.info('options.champs', options.champs)
|
||||||
|
headers.forEach(header => {
|
||||||
|
// Si un header n'est pas présent dans la liste des champs prédéfinis
|
||||||
|
// on l'ajoute aux champs inconnus
|
||||||
|
if (options.champs.indexOf(header) ==== -1) {
|
||||||
|
unknownHeaders += `${header}, `
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (unknownHeaders !==== '') {
|
||||||
|
const errorMsg = `CSV2JSON() - Champs inconnus : ${unknownHeaders}`
|
||||||
|
return callback(errorMsg, null)
|
||||||
|
}
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
// Skip headers line or empty lines
|
||||||
|
if (index ==== 0 || line.replace(/\s/g, '')
|
||||||
|
.length ==== 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// pour debuguer on met origincsv pour voir la ligne d'origine
|
||||||
|
const currentLineData = { origincsv: line, linenumber: index }
|
||||||
|
const currentLine = line.split(options.sep) // Current string in the line
|
||||||
|
for (let j = 0; j < headers.length; j++) {
|
||||||
|
// Si la ligne n'est pas vide
|
||||||
|
if (currentLine[j]) {
|
||||||
|
// On clean le champs
|
||||||
|
// ajout eventuel de modification de caracter reservé ; dans les libelléetc...
|
||||||
|
let currentValue = currentLine[j].trim()
|
||||||
|
// on transforme le caractere separateur modifié entre double quote
|
||||||
|
currentValue = currentValue.replace('CARSEPARATOR', options.sep)
|
||||||
|
options.replacespecialcarCsv2Json.forEach(re => {
|
||||||
|
currentValue = currentValue.replace(re[0], re[1])
|
||||||
|
})
|
||||||
|
// Si le header est un email
|
||||||
|
if (headers[j].includes('EMAIL')) {
|
||||||
|
// Supprimer tous les espaces
|
||||||
|
currentValue = currentLine[j].replace(/\s/g, '')
|
||||||
|
}
|
||||||
|
// on check si le chamos doit être numerique
|
||||||
|
if (options.numericfield.includes(headers[j])) {
|
||||||
|
currentValue = currentLine[j].replace(/\,/g, '.')
|
||||||
|
try {
|
||||||
|
const test = parseFloat(currentValue)
|
||||||
|
} catch (er) {
|
||||||
|
return callback(`${headers[j]} contiens la valeur -${currentValue}- et devrait être numerique`, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentValue) {
|
||||||
|
// Si le header actuel est de type array
|
||||||
|
// Cela signifie que le header apparaît plusieurs fois dans le CSV
|
||||||
|
// et que les valeurs correspondantes à ce header
|
||||||
|
// doivent être mis dans un array
|
||||||
|
if (options.array && options.array.indexOf(headers[j]) > -1) {
|
||||||
|
// Si le tableau pour ce header n'existe pas on le crée
|
||||||
|
if (!currentLineData[headers[j]]) {
|
||||||
|
currentLineData[headers[j]] = []
|
||||||
|
}
|
||||||
|
if (options.arraysplitsep) {
|
||||||
|
currentValue.split(options.arraysplitsep)
|
||||||
|
.forEach(v => {
|
||||||
|
currentLineData[headers[j]].push(v)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
currentLineData[headers[j]].push(currentValue)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Si un header est déjà présent pour la ligne
|
||||||
|
// alors que il n'est pas spécifié comme étant un array
|
||||||
|
// on retourne une erreur
|
||||||
|
if (currentLineData[headers[j]]) {
|
||||||
|
const errorMsg = `Le champ ${
|
||||||
|
headers[j]
|
||||||
|
} est présent plusieurs fois alors qu'il n'est pas spécifié comme étant un array !`
|
||||||
|
return callback(errorMsg, null)
|
||||||
|
}
|
||||||
|
currentLineData[headers[j]] = currentValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(currentLineData)
|
||||||
|
})
|
||||||
|
return callback(null, result)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* [csvparam2json description]
|
||||||
|
* @param {object} csv object of csv file that has been read
|
||||||
|
* @param {object} options object containing csv options, headers, ...
|
||||||
|
{retln:'code de retour de ligne \n ou \n\r',
|
||||||
|
sep:'code to split cells',
|
||||||
|
champs:[ch1,ch2,...] catch only those field,
|
||||||
|
array:[ch1, ] can have more than one field champs with same name then data are push into an array }
|
||||||
|
* @param {Function} callback callback function
|
||||||
|
* @return {callback} - return an error if error, else return json
|
||||||
|
it converts a csv with 3 column col1;col2;col3 in a json in a tree
|
||||||
|
if in col1 we have __ => then it splits a leaf
|
||||||
|
col1 = xxxx__yyyy ; col2 = value ; col3 = comment that is ignored
|
||||||
|
return data = {xxxx:{yyyy:value}}
|
||||||
|
col1 = xxxx; col2 = value; col3 = comment ignored
|
||||||
|
return data = {xxxx:value}
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
fiche.csvparam2article = (err, fiche) => {
|
||||||
|
if (!err) {
|
||||||
|
logger.info(fiche)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
utils.csvparam2json(fs.readFileSync('./devdata/tribee/aubergenville/infoexterne/localbusiness.csv', 'utf-8'), {
|
||||||
|
retln: "\n",
|
||||||
|
sep: ";",
|
||||||
|
champs: ["NOM", "OBJET", "ADRESSE_PRO", "CP_PRO", "VILLE_PRO", "ZONE", "PHONE_PRO", "HORAIRESDESC", "HORAIREDATA", "URL", "FACEBOOK", "INSTA", "EMAIL_PRO", "IMG", "TAG"],
|
||||||
|
array: ["TAG", "PHONE_PRO", "EMAIL_PRO"]
|
||||||
|
}, fiche.csv2article)
|
||||||
|
|
||||||
|
*/
|
||||||
|
utils.csvparam2json = (csv, options, callback) => {
|
||||||
|
logger.info('\n--------------- CSVPARAM2JSON ---------------\n')
|
||||||
|
let etat = ''
|
||||||
|
const param = {}
|
||||||
|
if (!options.retln) {
|
||||||
|
options.retln = '\n'
|
||||||
|
}
|
||||||
|
if (csv.indexOf('\n\r') > -1) {
|
||||||
|
options.retln = '\n\r'
|
||||||
|
}
|
||||||
|
if (!options.sep) {
|
||||||
|
options.sep = ';'
|
||||||
|
}
|
||||||
|
if (!options.seplevel) {
|
||||||
|
options.seplevel = '__'
|
||||||
|
}
|
||||||
|
if (!options.replacespecialcarCsv2Json) {
|
||||||
|
options.replacespecialcarCsv2Json = []
|
||||||
|
} else {
|
||||||
|
if (typeof options.replacespecialcarCsv2Json ==== 'string') {
|
||||||
|
// permet de passer des regex en string
|
||||||
|
options.replacespecialcarCsv2Json = eval(options.replacespecialcarCsv2Json)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const lines = csv.split(options.retln)
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const infol = lines[i].split(options.sep)
|
||||||
|
// logger.info(infol)
|
||||||
|
if (infol[0].length > 4 && infol.length < 2) {
|
||||||
|
// si le 1er element à plus de 4 caractere et s'il y a moins de 3 colonnes c'est qu'il y a un pb
|
||||||
|
etat += `Erreur sur ${lines[i]} moins de 3 column separé par ${options.sep}`
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// On ajoute ici la gestion de tous les caracteres spéciaux
|
||||||
|
// reservées pour le csv ; ' etc..'
|
||||||
|
if (infol[1] && infol[1] + '' === infol[1]) {
|
||||||
|
options.replacespecialcarCsv2Json.forEach(re => {
|
||||||
|
// logger.info("gggggggggggggggggggg", infol[1])
|
||||||
|
infol[1] = infol[1].replace(re[0], re[1])
|
||||||
|
})
|
||||||
|
// logger.info(infol[1])
|
||||||
|
infol[1] = infol[1].replace(/'|’/g, '"')
|
||||||
|
// logger.info(infol[1])
|
||||||
|
if (infol[1].toLowerCase() ==== 'true') {
|
||||||
|
infol[1] = true
|
||||||
|
} else if (infol[1].toLowerCase() ==== 'false') {
|
||||||
|
infol[1] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info(infol[1])
|
||||||
|
// supprime des lignes vides
|
||||||
|
if (infol[0] === '') continue
|
||||||
|
if (infol[0].indexOf(options.seplevel) === -1) {
|
||||||
|
param[infol[0]] = infol[1]
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
const arbre = infol[0].split(options.seplevel)
|
||||||
|
switch (arbre.length) {
|
||||||
|
case 1:
|
||||||
|
param[arbre[0]] = infol[1]
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
if (arbre[1] !== 'ARRAY') {
|
||||||
|
if (!param[arbre[0]]) param[arbre[0]] = {}
|
||||||
|
param[arbre[0]][arbre[1]] = infol[1]
|
||||||
|
} else {
|
||||||
|
if (!param[arbre[0]]) param[arbre[0]] = []
|
||||||
|
// logger.info('aff', infol[1].substring(1, infol[1].length - 1).replace(/""/g, '"'))
|
||||||
|
eval('result=' + infol[1])
|
||||||
|
// .substring(1, infol[1].length - 1).replace(/""/g, '"'))
|
||||||
|
param[arbre[0]].push(result)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
if (arbre[2] !== 'ARRAY') {
|
||||||
|
if (!param[arbre[0]]) param[arbre[0]] = {}
|
||||||
|
if (!param[arbre[0]][arbre[1]]) param[arbre[0]][arbre[1]] = {}
|
||||||
|
param[arbre[0]][arbre[1]][arbre[2]] = infol[1]
|
||||||
|
} else {
|
||||||
|
if (!param[arbre[0]]) param[arbre[0]] = {}
|
||||||
|
if (!param[arbre[0]][arbre[1]]) param[arbre[0]][arbre[1]] = []
|
||||||
|
// eval("result = \"test\"");
|
||||||
|
// logger.info(result);
|
||||||
|
eval('result=' + infol[1])
|
||||||
|
// .substring(1, infol[1].length - 1).replace(/""/g, '"'))
|
||||||
|
param[arbre[0]][arbre[1]].push(result)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
if (arbre[3] !== 'ARRAY') {
|
||||||
|
if (!param[arbre[0]]) param[arbre[0]] = {}
|
||||||
|
if (!param[arbre[0]][arbre[1]]) param[arbre[0]][arbre[1]] = {}
|
||||||
|
if (!param[arbre[0]][arbre[1]][arbre[2]]) param[arbre[0]][arbre[1]][arbre[2]] = {}
|
||||||
|
param[arbre[0]][arbre[1]][arbre[2]][arbre[3]] = infol[1]
|
||||||
|
} else {
|
||||||
|
if (!param[arbre[0]]) param[arbre[0]] = {}
|
||||||
|
if (!param[arbre[0]][arbre[1]]) param[arbre[0]][arbre[1]] = {}
|
||||||
|
if (!param[arbre[0]][arbre[1]][arbre[2]]) param[arbre[0]][arbre[1]][arbre[2]] = []
|
||||||
|
eval('result=' + infol[1])
|
||||||
|
// .substring(1, infol[1].length - 1).replace(/""/g, '"'))
|
||||||
|
param[arbre[0]][arbre[1]][arbre[2]].push(result)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// JSON.parse(JSON.stringify(param))
|
||||||
|
logger.info('kkkkkkkkkkkkkkkkkk', param.catalogue.filtrecatalog.searchengine)
|
||||||
|
if (etat === '') {
|
||||||
|
return callback(null, JSON.parse(JSON.stringify(param)))
|
||||||
|
} else {
|
||||||
|
return callback(etat, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
utils.levenshtein = (a, b) => {
|
||||||
|
if (a.length ==== 0) return b.length
|
||||||
|
if (b.length ==== 0) return a.length
|
||||||
|
let tmp, i, j, prev, val, row
|
||||||
|
// swap to save some memory O(min(a,b)) instead of O(a)
|
||||||
|
if (a.length > b.length) {
|
||||||
|
tmp = a
|
||||||
|
a = b
|
||||||
|
b = tmp
|
||||||
|
}
|
||||||
|
row = Array(a.length + 1)
|
||||||
|
// init the row
|
||||||
|
for (i = 0; i <= a.length; i++) {
|
||||||
|
row[i] = i
|
||||||
|
}
|
||||||
|
// fill in the rest
|
||||||
|
for (i = 1; i <= b.length; i++) {
|
||||||
|
prev = i
|
||||||
|
for (j = 1; j <= a.length; j++) {
|
||||||
|
if (b[i - 1] ==== a[j - 1]) {
|
||||||
|
val = row[j - 1] // match
|
||||||
|
} else {
|
||||||
|
val = Math.min(row[j - 1] + 1, // substitution
|
||||||
|
Math.min(prev + 1, // insertion
|
||||||
|
row[j] + 1)) // deletion
|
||||||
|
}
|
||||||
|
row[j - 1] = prev
|
||||||
|
prev = val
|
||||||
|
}
|
||||||
|
row[a.length] = prev
|
||||||
|
}
|
||||||
|
return row[a.length]
|
||||||
|
}
|
||||||
|
utils.testinarray = (array, arrayreferent) => {
|
||||||
|
// au moins un element de array existe dans arryreferent
|
||||||
|
let exist = false
|
||||||
|
if (arrayreferent) {
|
||||||
|
// logger.info('arrrrrrrrrrrrrrr', arrayreferent)
|
||||||
|
array.forEach(e => {
|
||||||
|
// logger.info(e)
|
||||||
|
if (arrayreferent.includes(e)) exist = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
DIRECTORY
|
||||||
|
*/
|
||||||
|
const isDirectory = source => fs.lstatSync(source)
|
||||||
|
.isDirectory()
|
||||||
|
const getDirectories = source => fs.readdirSync(source)
|
||||||
|
.map(name => path.join(source, name))
|
||||||
|
.filter(isDirectory)
|
||||||
|
module.exports = utils
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user