2023-12-07 12:04:19 +01:00
const express = require ( 'express' ) ;
const fs = require ( 'fs-extra' ) ;
const path = require ( 'path' ) ;
const glob = require ( 'glob' ) ;
2024-10-16 12:55:17 +02:00
const conf = require ( ` ../../../adminapi/objects/tribes/itm/adminapi.json ` ) ;
2023-12-07 12:04:19 +01:00
// Classes
const Tribes = require ( '../models/Tribes.js' ) ;
// Middlewares
const checkHeaders = require ( '../middlewares/checkHeaders' ) ;
const isAuthenticated = require ( '../middlewares/isAuthenticated' ) ;
const router = express . Router ( ) ;
2024-03-15 08:49:23 +01:00
/ * *
* @ api { get } adminapi / tribes / conf / : tribe - tribe list
* @ apiName getconf
* @ apiDescription Get list of route and model for a town server
* @ apiGroup Tribes
*
* @ apiSuccess ( object ) get tribes conf on this server
* @ apiSuccessExample { json }
* HTTP / 1.1 200 OK
* { status : 200 , ref : "Tribes" , msg : "tribesconf" , data : { routes : [ ] , modele : [ { model : , tplstrings : [ lg , lg ] } ] } }
* /
router . get ( '/config/:tribe' , checkHeaders , isAuthenticated , ( req , res ) => {
/ * c o n s o l e . l o g ( " p a s s e l a " )
AJOUTER gestion accessright
req . session . header . accessrights . data [ req . params . tribe ] &&
req . session . header . accessrights . data [ req . params . tribe ] . tribeid &&
req . session . header . accessrights . data [ req . params . tribe ] . tribeid . includes ( 'R' )
* /
if ( "authorize" == "authorize" ) {
2024-10-16 12:55:17 +02:00
const tribconf = ` ../../adminapi/objects/tribes/itm/ ${ req . params . tribe } .json `
2024-03-15 08:49:23 +01:00
if ( ! fs . existsSync ( tribconf ) ) {
res . status ( 404 ) . json ( { status : 404 , ref : "Tribes" , msg : "tribedoesnotexist" , data : { tribe : req . params . tribe } } )
} else {
res . status ( 200 ) . json ( { satatus : 200 , ref : "Tribes" , msg : "tribconf" , data : { conf : fs . readJsonSync ( tribconf ) } } )
}
} else {
res . status ( 403 )
. json ( { msg : [ 'forbidenAccess' ] , ref : 'Tribes' } )
. end ( ) ;
}
} )
2023-12-07 12:04:19 +01:00
/ * *
2024-03-15 08:49:23 +01:00
* @ api { get } adminapi / tribes / www / : tribeId - tribe list
2023-12-07 12:04:19 +01:00
* @ apiName getlisttrib
* @ apiDescription Get list of www object ( space web )
* @ apiGroup Tribes
*
* @ apiParam { String } tribeId it identify an existing tribe *
* @ apiSuccess ( object ) listwww contains folder name in www for tribeId
* @ apiSuccessExample { json } listwww
* HTTP / 1.1 200 OK
* { status : 200 , ref : "Tribes" , msg : "listwww" , data : { listwww } }
* /
2024-03-15 08:49:23 +01:00
router . get ( '/www' , checkHeaders , isAuthenticated , ( req , res ) => {
2023-12-07 12:04:19 +01:00
let listwww = [ ]
glob . sync ( ` ${ conf . dirtown } /tribes/ ${ req . params . tribeId } /www/* ` ) . forEach ( d => {
listwww . push ( d . split ( "/" ) . pop ( ) )
} )
res . status ( 200 ) . json ( { status : 200 , ref : "Tribes" , msg : "listwww" , data : { listwww } } )
} )
//router.post('www/') to create a webspace
//router.put('www/:app') to update
//router.delete('www/:tribeId/:app)
router . post ( '/actionanonyme' , checkHeaders , ( req , res ) => {
if ( ! fs . existsSync ( ` ${ conf . dirtown } /tribes/ ${ req . session . header . xtribe } /actions/ ${ req . body . action } .js ` ) ) {
res . status ( 403 ) . send ( { status : 403 , msg : "actionmissing" , ref : "Tribes" , data : { action : req . body . action , tribe : req . session . header . xtribe } } )
}
const action = require ( ` ${ conf . dirtown } /tribes/ ${ req . session . header . xtribe } /actions/ ${ req . body . action } .js ` )
const resaction = action . run ( req . body , req . session . header ) ;
res . status ( resaction . status ) . send ( resaction ) ;
} )
router . post ( '/action' , checkHeaders , isAuthenticated , ( req , res ) => {
} )
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 apxtri => 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 . apxtri && req . session . header . accessrights . data . apxtri . tribeid && req . session . header . accessrights . data . apxtri . 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 ) => {
2024-03-15 08:49:23 +01:00
// url adminapi/tribes/dirls?rep=referentials/dataManagement
2023-12-07 12:04:19 +01:00
// 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
2024-03-15 08:49:23 +01:00
// url adminapi/tribes/ls
2023-12-07 12:04:19 +01:00
// 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
//A REVOIR hasAccessrighton( req.body.object, "U" );
} else {
// AREVOIRhasAccessrighton( 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' )
}
} ) ;
2024-04-12 12:49:48 +02:00
2023-12-07 12:04:19 +01:00
router . post ( '/upfilepond' , checkHeaders , isAuthenticated , ( req , res ) => {
2024-03-15 08:49:23 +01:00
console . log ( 'post adminapi/tribes/uploadfilepond' ) ;
2023-12-07 12:04:19 +01:00
// 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 ;
} ;
// A REVOIR 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 ' )
/ * A u t h e n t i f i c a t i o n i s n e e d e d t o g e t a T O K E N
curl - X POST - H "xtribe: apxtri" - H "xworkon: pvmsaveurs" - H "xlang: fr" - H "xpaganid: 1" - H "xauth: 1" - H "xapp: pvmsaveurs:pvmsaveurs" - H "Content-Type: application/json" - d '{"LOGIN":"adminapxtri","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: adminapxtri" - 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 ' )
2024-03-15 08:49:23 +01:00
/ * n o a u t h e n t i f i c a t i o n t o u p l o a d a z i p f i l e n a m e i n t o a d m i n a p i / t r i b e s / $ { x w o r k o n } / $ { c l i e n t c o n f . u p l o a d z i p [ f i l e n a m e ] . d e s t }
2023-12-07 12:04:19 +01:00
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: adminapxtri" - 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 apxtri 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
2024-03-15 08:49:23 +01:00
// url adminapi/tribes/upload?save=tmp&rep=referentials/dataManagement
2023-12-07 12:04:19 +01:00
// 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" / >
< / f o r m >
* /
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 ;