351 lines
13 KiB
JavaScript
Executable File
351 lines
13 KiB
JavaScript
Executable File
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 Pagans = require( './Pagans.js' );
|
|
const config = require( '../../nationchains/tribes/conf.json' );
|
|
const Checkjson = require( `./Checkjson.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": "apxtrib",
|
|
"genericpsw": "Trze3aze!",
|
|
"website": {
|
|
"presentation":"https://www.apxtrib.org",
|
|
"webapp": "https://webapp.apxtrib.org"
|
|
},
|
|
"allowedDOMs": ["local.fr", "localhost:9002", "ndda.fr", "apxtrib.org"],
|
|
"clientname": "apxtrib",
|
|
"clientlogo": "",
|
|
"geoloc": [],
|
|
"useradmin": {PUBKEY:"",EMAIL:"",LOGIN:"adminapxtrib",UUID:"adminapxtrib"},
|
|
"smtp": {
|
|
"emailFrom": "support@apxtrib.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 apxtrib 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 apxtrib 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( `${__base}/setup/tribes/apxtrib/${r}`, `${config.tribes}/${data.tribeid}/${r}` );
|
|
} )
|
|
fs.outputJsonSync( `${config.tribes}/${data.tribeid}/clientconf.json`, data );
|
|
const confcli = JSON.parse( Mustache.render( fs.readFileSync( `${__base}/setup/tribes/apxtrib/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 apxtribenv
|
|
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 + apxtrib 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.dns == "unchain" ) {
|
|
//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 apxtrib 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.restartapxtrib, 300000, data.clienId );
|
|
}
|
|
const nginxrestart = execSync( `sudo systemctl restart nginx` )
|
|
.toString();
|
|
console.log( 'Restart nginx', nginxrestart )
|
|
if( data.mode != "unchain" ) {
|
|
// 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.restartapxtrib = ( tribeid ) => {
|
|
console.log( 'A restarting was requested 5mn ago from a new spacedev for ' + tribeid )
|
|
execSync( 'yarn restartpm2' );
|
|
}
|
|
|
|
|
|
module.exports = Tribes;
|