2023-05-31 15:19:21 +02:00
const { argv } = require ( "process" ) ;
2023-05-12 07:59:32 +02:00
const fs = require ( "fs-extra" ) ;
2023-05-31 15:19:21 +02:00
const mustache = require ( "mustache" ) ;
2023-05-12 07:59:32 +02:00
const bodyParser = require ( "body-parser" ) ;
const glob = require ( "glob" ) ;
const path = require ( "path" ) ;
const cors = require ( "cors" ) ;
const express = require ( "express" ) ;
const process = require ( "process" ) ;
2023-01-22 10:53:09 +01:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2023-03-27 07:52:21 +02:00
SEE https : //gitea.ndda.fr/apxtrib/apxtrib/wiki/Devrules
To have a quick understanding and convention before doing deeply in source code
2023-01-22 10:53:09 +01:00
2023-05-31 15:19:21 +02:00
To share configuration :
process . env . dirtown is folder where town folder name / townId - nationId is accessible
const conf = require ( ` ${ process . env . dirtown } /conf.json ` ) ;
app . locals . tribeids is defined later in apixtrib . js and allow express app to always have in memory a dynamic of tribeId available in req . app . locals . tribeids
2023-04-27 06:17:20 +02:00
* /
2023-05-31 15:19:21 +02:00
/ * *
* 1 st install for dev
* run $ node apxtrib . js nationId : ants townId : devfarm dns : devfarm - ants
* then just yarn dev
* it create a folder outside . . / townId - nationId /
* To convert a dev into a chain town run again with relevant param :
* run $ node apxtrib . js nationId : ants townId : devfarm dns : devfarm - ants
* check the web interface http : //dns
* then just yarn startpm2 your town is under PM2 control
*
*
* @ param { args } args key : value example node apxtrib nationId : ants townId : devfarm dns : devfarm - ants
* if no parammeter from adminapi / www / adminapx / conf / setup _xx . json
*
* Keyword townId = "devfarm" then this is unchain town to dev
* else this is a production town ready to chain to the nationId
*
* @ returns listen onto http : /dns (80 and 443) for admin webapp and http:/ / localhost : initconf . api . port
* by setting the nginx parameter
* A folder for town data is created at the same level than apxtrib as / townId - nationId / conf . json ...
* /
2023-05-12 07:59:32 +02:00
2023-05-31 15:19:21 +02:00
const setconf = ( param ) => {
// set conf from argv = param={nationId,townId,dns}
console . log (
` RUNNING A NEW SETUP with nation ${ param . nationId } and town ${ param . townId } to be accessible in dns http:// ${ param . dns } `
) ;
fs . outputJsonSync (
` ${ _ _dirname } /adminapi/www/adminapx/conf/setup_xx.json ` ,
{
nationId : param . nationId ,
townId : param . townId ,
dns : [ param . dns ] ,
comment :
"Auto generate setup from apxtrib after node apxtrib nationId:value townId:value dns:domaine_to_access" ,
} ,
{ space : 2 }
) ;
// Add this town localy
const townid = {
townId : param . townId ,
nationId : param . nationId ,
dns : param . dns ,
IP : "127.0.0.1" ,
status : "unchain" ,
tribes : [ ] ,
} ;
const townidkey = { } ;
townidkey [ param . townId ] = townid ;
fs . outputJsonSync ( ` ./nationchains/towns/idx/townId_all.json ` , townidkey ) ;
fs . outputJsonSync ( ` ./nationchains/towns/itm/ ${ param . townId } .json ` , townid ) ;
initconf = fs . readJsonSync ( "./adminapi/www/adminapx/conf/initconf.json" ) ;
initconf . dirapi = _ _dirname ;
initconf . dirtown = path . resolve (
` ${ _ _dirname } /../ ${ param . townId } - ${ param . nationId } `
) ;
initconf . nationId = param . nationId ;
initconf . townId = param . townId ;
initconf . sudoerUser = process . env . USER ;
if ( ! initconf . dns . includes ( param . dns ) ) {
initconf . dns . push ( param . dns ) ;
}
initconf . nginx . include . push ( ` ${ initconf . dirapi } /adminapi/www/nginx_*.conf ` ) ;
initconf . nginx . include . push (
path . resolve (
` ../ ${ param . townId } - ${ param . nationId } /tribes/**/www/nginx_*.conf `
)
) ;
initconf . nginx . logs = ` ${ initconf . dirtown } /logs/nginx/adminapx ` ;
initconf . nginx . website = "adminapx" ;
initconf . nginx . fswww = ` ${ _ _dirname } /adminapi/www ` ;
initconf . nginx . pageindex = "index_en.html" ;
const { exec } = require ( "child_process" ) ;
exec (
` sudo chown -R ${ process . env . USER } : ${ process . env . USER } /etc/nginx ` ,
( error , stdout , stderr ) => {
if ( error ) {
console . log ( "\x1b[42m" , error , stdout , stderr , "x1b[0m" ) ;
console . log ( "impossible to change owner of /etc/nginx by phil:phil" ) ;
process . exit ( ) ;
} else {
console . log (
` successfull sudo chown -R ${ process . env . USER } : ${ process . env . USER } /etc/nginx `
) ;
}
}
) ;
fs . outputJsonSync (
` ../ ${ param . townId } - ${ param . nationId } /conf.json ` ,
initconf ,
{ space : 2 }
) ;
fs . ensureDirSync ( ` ../ ${ param . townId } - ${ param . nationId } /logs/nginx ` ) ;
2023-06-02 10:21:01 +02:00
fs . ensureDirSync ( ` ../ ${ param . townId } - ${ param . nationId } /tmp/tokens ` ) ;
2023-05-31 15:19:21 +02:00
const nginxconf = fs . readFileSync (
"./adminapi/www/adminapx/conf/nginx.conf.mustache" ,
"utf8"
) ;
const proxyparams = fs . readFileSync (
"./adminapi/www/adminapx/conf/nginxproxyparams.mustache" ,
"utf8"
) ;
const websiteconf = fs . readFileSync (
"./adminapi/www/adminapx/conf/nginxmodelwebsite.conf.mustache" ,
"utf8"
) ;
2023-05-16 10:31:27 +02:00
2023-05-31 15:19:21 +02:00
// saved and change nginx conf
if ( ! fs . existsSync ( "/etc/nginx/nginxconf.saved" ) ) {
fs . moveSync ( "/etc/nginx/nginx.conf" , "/etc/nginx/nginxconf.saved" ) ;
console . log (
"your previous /etc/nginx/nginx.conf was backup in /etc/nginx/nginxconf.saved"
) ;
}
fs . outputFileSync (
"/etc/nginx/nginx.conf" ,
mustache . render ( nginxconf , initconf ) ,
"utf8"
) ;
fs . outputFileSync (
"/etc/nginx/proxy_params" ,
mustache . render ( proxyparams , initconf ) ,
"utf8"
) ;
fs . outputFileSync (
` ${ _ _dirname } /adminapi/www/nginx_adminapx.conf ` ,
mustache . render ( websiteconf , initconf ) ,
"utf8"
) ;
exec ( initconf . nginx . restart , ( error , stdout , stderr ) => {
if ( error ) {
console . log ( "\x1b[42m" , error , stdout , stderr , "x1b[0m" ) ;
//@todo supprimer la derniere config nginx et relancer
fs . moveSync ( "/etc/nginx/nginxconf.saved" , "/etc/nginx/nginx.conf" ) ;
console . log ( "Restart yarn dev" ) ;
} else {
console . log ( ` ready to use http:// ${ param . dns } ` ) ;
}
} ) ;
} ;
// check nginx exist
2023-05-12 07:59:32 +02:00
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 ( ) ;
2023-01-22 10:53:09 +01:00
}
2023-05-31 15:19:21 +02:00
const param = { } ;
argv . slice ( 2 ) . forEach ( ( arg ) => {
const kv = arg . split ( ":" ) ;
if ( kv . length == 2 ) {
param [ kv [ 0 ] ] = kv [ 1 ] ;
}
} ) ;
if (
Object . keys ( param ) . length > 0 &&
param . nationId &&
param . townId &&
param . dns
) {
setconf ( param ) ;
}
2023-06-07 07:32:23 +02:00
// setup_xx.json is gitignore so at first install we are in dev configuration
let infotown = {
nationId : "ants" ,
townId : "devfarm" ,
dns : [ "devfarm-ants" ] ,
comment :
"Auto generate setup from apxtrib after node apxtrib nationId:value townId:value dns:domaine_to_access" ,
} ;
if ( fs . existsSync ( ` ${ _ _dirname } /adminapi/www/adminapx/conf/setup_xx.json ` ) ) {
infotown = fs . readJsonSync (
` ${ _ _dirname } /adminapi/www/adminapx/conf/setup_xx.json `
) ;
} else {
fs . outputJsonSync (
` ${ _ _dirname } /adminapi/www/adminapx/conf/setup_xx.json ` ,
infotown
) ;
}
2023-05-16 10:31:27 +02:00
2023-05-31 15:19:21 +02:00
if (
! fs . existsSync (
path . resolve (
` ${ _ _dirname } /../ ${ infotown . townId } - ${ infotown . nationId } /conf.json `
)
) ||
! fs . existsSync ( ` ${ _ _dirname } /adminapi/www/nginx_adminapx.conf ` )
) {
2023-06-07 07:32:23 +02:00
// Case of new town or request a reset of dns to access adminapx
2023-05-31 15:19:21 +02:00
setconf ( infotown ) ;
2023-01-22 10:53:09 +01:00
}
2023-05-31 15:19:21 +02:00
const conf = require ( path . resolve (
` ${ _ _dirname } /../ ${ infotown . townId } - ${ infotown . nationId } /conf.json `
) ) ;
2023-05-16 10:31:27 +02:00
2023-05-31 15:19:21 +02:00
process . env . dirtown = conf . dirtown ;
2023-05-16 10:31:27 +02:00
2023-05-31 15:19:21 +02:00
// Create and update ./nationchains
2023-05-16 10:31:27 +02:00
2023-05-31 15:19:21 +02:00
const { updateobjectsfromfreshesttown } = require ( "./api/models/Nations.js" ) ;
updateobjectsfromfreshesttown ( conf . towns , {
pagans : "alias_all.json" ,
towns : "townId_all.json" ,
nations : "nationId_all.json" ,
} ) ;
2023-05-12 07:59:32 +02:00
2023-05-31 15:19:21 +02:00
// Run main express process for a /towId-nationId/tribes
2023-05-12 07:59:32 +02:00
2023-05-31 15:19:21 +02:00
let tribelist = { } ;
if ( fs . existsSync ( ` ${ conf . dirtown } /tribes/idx/tribeId_all.json ` ) ) {
tribelist = fs . readJsonSync ( ` ${ conf . dirtown } /tribes/idx/tribeId_all.json ` ) ;
}
2023-05-12 07:59:32 +02:00
let doms = conf . dns ; // only dns of town during the init process
let tribeIds = [ ] ;
2023-05-16 10:31:27 +02:00
let routes = glob . sync ( ` ${ conf . dirapi } /api/routes/*.js ` ) . map ( ( f ) => {
2023-05-12 07:59:32 +02:00
return { url : ` / ${ path . basename ( f , ".js" ) } ` , route : f } ;
} ) ;
//routes={url,route} check how to add plugin tribe route later
// keep only the 2 last part (.) of domain name to validate cors with it (generic domain)
Object . keys ( tribelist ) . forEach ( ( t ) => {
tribelist [ t ] . dns . forEach ( ( d ) => {
const dm = d . split ( "." ) . slice ( - 2 ) . join ( "." ) ;
if ( ! doms . includes ( dm ) ) doms . push ( dm ) ;
} ) ;
tribeIds . push ( t ) ;
} ) ;
console . log ( "Allowed DOMs to access to this apxtrib server: " , doms ) ;
2023-04-13 07:46:35 +02:00
2023-04-27 06:17:20 +02:00
const app = express ( ) ;
2023-05-12 07:59:32 +02:00
// load express parameter from conf
Object . keys ( conf . api . appset ) . forEach ( ( p ) => {
app . set ( p , conf . api . appset [ p ] ) ;
} ) ;
2023-01-22 10:53:09 +01:00
// To set depending of data form or get size to send
2023-05-12 07:59:32 +02:00
app . use ( bodyParser . urlencoded ( conf . api . bodyparse . urlencoded ) ) ;
2023-01-22 10:53:09 +01:00
// To set depending of post put json data size to send
2023-05-12 07:59:32 +02:00
app . use ( express . json ( ) ) ;
app . use ( bodyParser . json ( conf . api . bodyparse . json ) ) ;
2023-04-27 06:17:20 +02:00
app . locals . tribeids = tribeIds ;
2023-05-12 07:59:32 +02:00
console . log ( "app.locals.tribeids" , app . locals . tribeids ) ;
2023-01-22 10:53:09 +01:00
// Cors management
const corsOptions = {
2023-05-12 07:59:32 +02:00
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 ( "." ) ;
2023-01-22 10:53:09 +01:00
2023-05-12 07:59:32 +02:00
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? : ${ doms . includes ( dom ) } `
) ;
if ( 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 ( conf . api . exposedHeaders ) ,
2023-01-22 10:53:09 +01:00
} ;
// CORS
2023-05-12 07:59:32 +02:00
app . use ( cors ( corsOptions ) ) ;
// Static Routes // try to use nginx route instead in comments
2023-04-28 13:21:02 +02:00
/ * a p p . u s e ( e x p r e s s . s t a t i c ( ` $ { _ _ d i r n a m e } / n a t i o n c h a i n s / t r i b e s / $ { c o n f . m a y o r I d } / w w w / c d n / p u b l i c ` , {
2023-05-31 15:19:21 +02:00
dotfiles : 'allow'
2023-01-22 10:53:09 +01:00
} ) ) ;
2023-04-27 06:17:20 +02:00
* /
2023-01-22 10:53:09 +01:00
// Routers add any routes from /routes and /plugins
2023-05-31 15:19:21 +02:00
let logroute = "Routes available on this apxtrib instance: " ;
2023-05-12 07:59:32 +02:00
routes . forEach ( ( r ) => {
try {
2023-05-31 15:19:21 +02:00
logroute += r . url + "|" + r . route ;
2023-05-12 07:59:32 +02:00
app . use ( r . url , require ( r . route ) ) ;
} catch ( err ) {
2023-05-31 15:19:21 +02:00
logroute += " (err check it)" ;
2023-05-12 07:59:32 +02:00
console . log ( "raise err-:" , err ) ;
}
} ) ;
2023-05-31 15:19:21 +02:00
console . log ( logroute ) ;
if ( infotown . townId == "devfarm" ) {
console . log (
` \x 1b[42m############################################################################################ \x 1b[0m \n \x 1b[42mThis is dev conf to switch this as production, you must run: \n 1 - 'yarn dev nationId:ants townId:usbfarm dns:usbfarm-ants ' to conf your town and check it. \n 2 - 'yarn startpm2' \n Where: \n \x 1b[42m * nationId have to exist in the nationchains \n * townId new or if exist must have the smae current dns, \n * dns domaine that has to redirect 80/443 into this server (example wall-ants.ndda.fr redirect to 213.32.65.213 ). \n Check README's project to learn more. \x 1b[0m \n \x 1b[42m############################################################################################ \x 1b[0m `
) ;
}
2023-05-12 07:59:32 +02:00
app . listen ( conf . api . port , ( ) => {
2023-05-31 15:19:21 +02:00
let webaccess = ` check in your browser that api works ` ;
conf . dns . forEach ( ( u ) => {
webaccess += ` http:// ${ u } : ${ conf . api . port } ` ;
} ) ;
console . log ( webaccess ) ;
2023-05-12 07:59:32 +02:00
} ) ;
console . log (
"\x1b[42m\x1b[37m" ,
"Made with love for people's freedom, enjoy !!!" ,
"\x1b[0m"
) ;