aotrautils-srv 0.0.1808 → 0.0.1810
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aotrautils-srv/aotrautils-srv.build.js +252 -1240
- aotrautils-srv/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
/*utils COMMONS library associated with aotra version : «1_29072022-2359 (
|
|
3
|
+
/*utils COMMONS library associated with aotra version : «1_29072022-2359 (14/03/2026-03:54:12)»*/
|
|
4
4
|
/*-----------------------------------------------------------------------------*/
|
|
5
5
|
|
|
6
6
|
|
|
@@ -1631,10 +1631,11 @@ window.PeriodicalExecuter=class PeriodicalExecuter{
|
|
|
1631
1631
|
//================================================================
|
|
1632
1632
|
|
|
1633
1633
|
// Node dependencies :
|
|
1634
|
-
if(typeof(require)
|
|
1635
|
-
sjcl=require("sjcl");
|
|
1636
|
-
|
|
1637
|
-
|
|
1634
|
+
if(typeof(require)!=="undefined" && typeof(sjcl)==="undefined"){
|
|
1635
|
+
window.sjcl=require("sjcl");
|
|
1636
|
+
}
|
|
1637
|
+
// For debug (WORKAROUND):
|
|
1638
|
+
sjcl=window.sjcl;
|
|
1638
1639
|
|
|
1639
1640
|
|
|
1640
1641
|
// NOT AOTESTABLE !
|
|
@@ -5075,7 +5076,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
|
|
|
5075
5076
|
|
|
5076
5077
|
|
|
5077
5078
|
|
|
5078
|
-
/*utils AI library associated with aotra version : «1_29072022-2359 (
|
|
5079
|
+
/*utils AI library associated with aotra version : «1_29072022-2359 (14/03/2026-03:54:12)»*/
|
|
5079
5080
|
/*-----------------------------------------------------------------------------*/
|
|
5080
5081
|
|
|
5081
5082
|
|
|
@@ -5221,7 +5222,7 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
|
5221
5222
|
|
|
5222
5223
|
|
|
5223
5224
|
|
|
5224
|
-
/*utils CONSOLE library associated with aotra version : «1_29072022-2359 (
|
|
5225
|
+
/*utils CONSOLE library associated with aotra version : «1_29072022-2359 (14/03/2026-03:54:12)»*/
|
|
5225
5226
|
/*-----------------------------------------------------------------------------*/
|
|
5226
5227
|
|
|
5227
5228
|
|
|
@@ -5317,6 +5318,9 @@ class AORTACServerCell{
|
|
|
5317
5318
|
|
|
5318
5319
|
this.handleCommonListeners();
|
|
5319
5320
|
|
|
5321
|
+
// TRACE
|
|
5322
|
+
lognow("AORTAC Server cell started on URL : ",this.selfOrigin);
|
|
5323
|
+
|
|
5320
5324
|
return this;
|
|
5321
5325
|
}
|
|
5322
5326
|
|
|
@@ -5892,7 +5896,7 @@ window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model,
|
|
|
5892
5896
|
|
|
5893
5897
|
|
|
5894
5898
|
|
|
5895
|
-
/* ## Utility
|
|
5899
|
+
/* ## Utility methods in a javascript, for internationalzation (i18n) subsystem (server & client)
|
|
5896
5900
|
*
|
|
5897
5901
|
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
5898
5902
|
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
@@ -5915,1301 +5919,309 @@ window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model,
|
|
|
5915
5919
|
if(typeof(window)==="undefined") window=global;
|
|
5916
5920
|
|
|
5917
5921
|
|
|
5922
|
+
// ==================================================================================================================
|
|
5918
5923
|
|
|
5919
5924
|
|
|
5920
|
-
// OLD : socket.io :
|
|
5921
|
-
// https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
|
|
5922
|
-
// https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
|
|
5923
|
-
// https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
|
|
5924
|
-
// https://socket.io/docs/v4/client-socket-instance/
|
|
5925
|
-
|
|
5926
|
-
// NEW : ws :
|
|
5927
|
-
// https://github.com/websockets/ws#installing
|
|
5928
|
-
// https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
|
|
5929
|
-
// ON BROWSER SIDE : Native Websockets :
|
|
5930
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
|
5931
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
|
|
5932
|
-
|
|
5933
|
-
// We have to import both implementations, regardless of which one is chosen :
|
|
5934
|
-
//Socket=require("socket.io");
|
|
5935
|
-
//WebSocket=require("ws");
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
// =================================================================================
|
|
5941
|
-
// NODEJS UTILS
|
|
5942
|
-
|
|
5943
|
-
const FILE_ENCODING="utf8";
|
|
5944
|
-
|
|
5945
|
-
const ADD_CORS_HEADER=true;
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
if(typeof(require)!=="undefined" && typeof(fs)==="undefined" ){
|
|
5950
|
-
window.fs=require("fs");
|
|
5951
|
-
}
|
|
5952
5925
|
|
|
5953
|
-
fs=window.fs;
|
|
5954
5926
|
|
|
5955
|
-
//
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
|
|
5959
|
-
getPersister=()=>{ return null; };
|
|
5927
|
+
// A little method to help i18ning aotra user interface :
|
|
5928
|
+
const I18N_REPOSITORY_VARIABLE_NAME="I18N_KEYS";
|
|
5929
|
+
window.i18n=(languagesAndStrings, langParam=null)=>{
|
|
5960
5930
|
|
|
5961
|
-
|
|
5931
|
+
// ******************* CONFIG *******************
|
|
5932
|
+
const PREFERED_LANGUAGE="en";
|
|
5962
5933
|
|
|
5934
|
+
const NO_TRANSLATION_LABEL="NO TRANSLATION FOUND";
|
|
5963
5935
|
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
let self={
|
|
5967
|
-
|
|
5968
|
-
dataDirPath:dataDirPath,
|
|
5969
|
-
prefix:prefix,
|
|
5970
|
-
|
|
5971
|
-
//FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
|
|
5972
|
-
/*private*/getPath:function(clientId="noclient", repositoryName="norepository"){
|
|
5973
|
-
// let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
|
|
5974
|
-
let path=`${self.dataDirPath}`
|
|
5975
|
-
+ (blank(self.prefix)?"":(self.prefix+"."))
|
|
5976
|
-
+`${clientId}.${repositoryName}.json`;
|
|
5977
|
-
return path;
|
|
5978
|
-
},
|
|
5979
|
-
|
|
5980
|
-
readTreeObjectFromFile:function(clientId="noclient", repositoryName="norepository", keepClassName=false){
|
|
5981
|
-
|
|
5982
|
-
let path=self.getPath(clientId,repositoryName);
|
|
5983
|
-
let resultFlat=null;
|
|
5984
|
-
|
|
5985
|
-
try{
|
|
5936
|
+
const ADD_SYSTEMATICALLY_TO_REPOSITORY=true;
|
|
5937
|
+
const MAX_CHARACTERS_FOR_TRANSLATION_KEY=64; // Really if you have more than 64 characters in commons, then it really should be the same string !
|
|
5986
5938
|
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
}catch(error){
|
|
5990
|
-
// TRACE
|
|
5991
|
-
console.log("ERROR : Could not read file «"+path+"».");
|
|
5992
|
-
|
|
5993
|
-
return null;
|
|
5994
|
-
}
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
let resultData={};
|
|
5998
|
-
|
|
5999
|
-
// 1)
|
|
6000
|
-
if(!empty(resultFlat)) resultData=parseJSON(resultFlat);
|
|
6001
|
-
|
|
6002
|
-
// 2)
|
|
6003
|
-
if(!empty(resultData) && isFlatMap(resultData)){
|
|
6004
|
-
// CAUTION : We have to keep the type information, here too ! (in the sub-objects)
|
|
6005
|
-
resultData=getAsTreeStructure(resultData, keepClassName);
|
|
6006
|
-
}
|
|
6007
|
-
|
|
6008
|
-
return resultData;
|
|
6009
|
-
},
|
|
5939
|
+
// ***************** END CONFIG *****************
|
|
6010
5940
|
|
|
6011
|
-
|
|
6012
|
-
saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
|
|
6013
|
-
|
|
6014
|
-
if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
|
|
6015
|
-
dataFlatForClient=getAsFlatStructure(dataFlatForClient);
|
|
6016
|
-
}
|
|
6017
|
-
|
|
6018
|
-
// reserved characters : -/\^$*+?.()|[]{}
|
|
6019
|
-
// CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
|
|
6020
|
-
let dataFlatStr=stringifyObject(dataFlatForClient)
|
|
6021
|
-
// We «aerate» the produced JSON :
|
|
6022
|
-
.replace(/":[\w]*\{/gim,"\":{\n").replace(/,"/gim,",\n\"")
|
|
6023
|
-
// ...except for inline, escaped JSON string representations :
|
|
6024
|
-
.replace(/\\\":[\w]*\{\n/gim,"\\\":{");
|
|
6025
|
-
// NO : .replace(/}/gim,"}\n");
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
let path=self.getPath(clientId,repositoryName);
|
|
6029
|
-
fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
|
|
6030
|
-
if(error){
|
|
6031
|
-
// TRACE
|
|
6032
|
-
console.log("ERROR : Could not write file «"+path+"»:",error);
|
|
6033
|
-
throw error;
|
|
6034
|
-
}
|
|
6035
|
-
if(doOnSuccess) doOnSuccess(dataFlatForClient);
|
|
6036
|
-
});
|
|
6037
|
-
}
|
|
6038
|
-
|
|
6039
|
-
};
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
return self;
|
|
6043
|
-
};
|
|
6044
5941
|
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
window.fileExists=(filePath)=>{
|
|
6049
|
-
if(typeof(fs)=="undefined"){
|
|
5942
|
+
if(!languagesAndStrings){
|
|
6050
5943
|
// TRACE
|
|
6051
|
-
|
|
6052
|
-
return
|
|
5944
|
+
console.log("ERROR : No parameter for i18n provided!",languagesAndStrings);
|
|
5945
|
+
return NO_TRANSLATION_LABEL;
|
|
6053
5946
|
}
|
|
6054
|
-
return fs.existsSync(filePath);
|
|
6055
|
-
};
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
// Nodejs server launching helper functions :
|
|
6060
|
-
//Networking management :
|
|
6061
|
-
//- WEBSOCKETS AND NODEJS :
|
|
6062
|
-
|
|
6063
|
-
function isConnected(clientSocket){
|
|
6064
|
-
if(!WebsocketImplementation.useSocketIOImplementation)
|
|
6065
|
-
return (clientSocket.readyState===WebSocket.OPEN)
|
|
6066
|
-
return (clientSocket.connected);
|
|
6067
|
-
}
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
// -Server :
|
|
6072
|
-
|
|
6073
|
-
getConsoleServerParams=function(portParam=null, certPathParam=null, keyPathParam=null, argsOffset=0, ignoreConsoleArgs=false){
|
|
6074
5947
|
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
5948
|
+
let lang=null;
|
|
5949
|
+
if(langParam){
|
|
5950
|
+
lang=langParam;
|
|
5951
|
+
}else{
|
|
5952
|
+
// Parameters priority :
|
|
5953
|
+
if(window){
|
|
5954
|
+
if(window.getURLParameter){
|
|
5955
|
+
lang=nonull(window.getURLParameter("language"), window.getURLParameter("lang"));
|
|
5956
|
+
}
|
|
5957
|
+
if(!lang && (window.language || window.lang)){
|
|
5958
|
+
lang=nonull(window.language, window.lang);
|
|
5959
|
+
}
|
|
5960
|
+
}else if(global){ // (NodeJS compatibility)
|
|
5961
|
+
if(global.getURLParameter){
|
|
5962
|
+
lang=nonull(global.getURLParameter("language"), global.getURLParameter("lang"));
|
|
5963
|
+
}
|
|
5964
|
+
if(!lang && (global.language || global.lang)){
|
|
5965
|
+
lang=nonull(global.language, global.lang);
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
lang=nonull(lang, PREFERED_LANGUAGE);
|
|
6083
5969
|
}
|
|
6084
|
-
|
|
5970
|
+
|
|
5971
|
+
if(contains(["capitalize","append"],lang)){
|
|
6085
5972
|
// TRACE
|
|
6086
|
-
console.log("WARN :
|
|
6087
|
-
|
|
5973
|
+
console.log("WARN : Language parameter has for value a reserved attribute keyword, using default language «"+PREFERED_LANGUAGE+"»");
|
|
5974
|
+
lang=PREFERED_LANGUAGE;
|
|
6088
5975
|
}
|
|
6089
|
-
|
|
6090
|
-
const result={};
|
|
6091
5976
|
|
|
6092
|
-
// We read the command-line arguments if needed :
|
|
6093
|
-
const argCLPort=getConsoleParam(1,argsOffset);
|
|
6094
|
-
const argCLCertPath=getConsoleParam(2,argsOffset);
|
|
6095
|
-
const argCLKeyPath=getConsoleParam(3,argsOffset);
|
|
6096
5977
|
|
|
6097
|
-
|
|
5978
|
+
let result=null;
|
|
6098
5979
|
|
|
6099
|
-
result.port=(ignoreConsoleArgs?portParam:nonull(argCLPort,portParam));
|
|
6100
|
-
result.certPath=null;
|
|
6101
|
-
result.keyPath=null;
|
|
6102
|
-
result.isSecure=!!(certPathParam || keyPathParam);
|
|
6103
5980
|
|
|
6104
|
-
if(
|
|
6105
|
-
|
|
6106
|
-
result
|
|
6107
|
-
}
|
|
5981
|
+
if(!isString(languagesAndStrings)){ // Case direct i18n translation, with no translation keys :
|
|
5982
|
+
|
|
5983
|
+
result=languagesAndStrings[lang];
|
|
6108
5984
|
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
cert: fs.readFileSync(result.certPath),
|
|
6114
|
-
key: fs.readFileSync(result.keyPath),
|
|
6115
|
-
};
|
|
6116
|
-
}
|
|
5985
|
+
if (!result){
|
|
5986
|
+
// TRACE
|
|
5987
|
+
console.log("ERROR : No label found for language «"+lang+"»!",languagesAndStrings);
|
|
5988
|
+
}
|
|
6117
5989
|
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
if(i<=argsOffset+1) return;
|
|
6128
|
-
else if(i==argsOffset+index+1) result=val;
|
|
6129
|
-
});
|
|
6130
|
-
return result;
|
|
6131
|
-
}
|
|
6132
|
-
|
|
6133
|
-
|
|
5990
|
+
if (!result){
|
|
5991
|
+
for(key in languagesAndStrings){
|
|
5992
|
+
if (!languagesAndStrings.hasOwnProperty(key))
|
|
5993
|
+
continue;
|
|
5994
|
+
// We take the first that we find, in the other languages:
|
|
5995
|
+
result=languagesAndStrings[key];
|
|
5996
|
+
break;
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
6134
5999
|
|
|
6135
|
-
|
|
6000
|
+
if (!result) return NO_TRANSLATION_LABEL;
|
|
6001
|
+
|
|
6002
|
+
|
|
6003
|
+
// We add this «rogue» usage of i18n to the whole repository at all useful ends :
|
|
6004
|
+
if(ADD_SYSTEMATICALLY_TO_REPOSITORY){
|
|
6005
|
+
// let declarable={};
|
|
6006
|
+
let maxLength=Math.min(result.length, MAX_CHARACTERS_FOR_TRANSLATION_KEY);
|
|
6007
|
+
let key=toConvention("camel", result.substring(0,maxLength).toLowerCase() );
|
|
6008
|
+
// let calculatedKey=radicalKey+"_"+getUUID("short");
|
|
6009
|
+
// declarable[calculatedKey]=languagesAndStrings;
|
|
6010
|
+
i18n.declareSingleEntry(key, languagesAndStrings);
|
|
6011
|
+
}
|
|
6136
6012
|
|
|
6137
|
-
|
|
6013
|
+
// Eventual post-treatments :
|
|
6014
|
+
if(languagesAndStrings.capitalize) result=capitalize(result);
|
|
6015
|
+
if(languagesAndStrings.append) result+=languagesAndStrings.append;
|
|
6138
6016
|
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
prompt: nonoull(promptText,"Enter command> ")
|
|
6143
|
-
});
|
|
6017
|
+
return result;
|
|
6018
|
+
}
|
|
6019
|
+
// else // Case we use translation keys : (here, languagesAndStrings parameter holds just the wished translation key)
|
|
6144
6020
|
|
|
6145
|
-
cliInterface.prompt();
|
|
6146
|
-
|
|
6147
|
-
cliInterface.on("line", (line) => {
|
|
6148
|
-
const input = line.trim();
|
|
6149
|
-
switch (input) {
|
|
6150
|
-
case "quit":
|
|
6151
|
-
console.log("Bye!");
|
|
6152
|
-
cliInterface.close();
|
|
6153
|
-
break;
|
|
6154
|
-
case "help":
|
|
6155
|
-
console.log("Available commands: quit, help and :", Object.keys(doOnCommands));
|
|
6156
|
-
break;
|
|
6157
|
-
default:
|
|
6158
|
-
if(doOnCommands) doOnCommands[input]();
|
|
6159
|
-
break;
|
|
6160
|
-
}
|
|
6161
|
-
|
|
6162
|
-
// Show the prompt again :
|
|
6163
|
-
cliInterface.prompt();
|
|
6164
|
-
|
|
6165
|
-
}).on("close", () => {
|
|
6166
|
-
process.exit(0);
|
|
6167
|
-
});
|
|
6168
6021
|
|
|
6169
|
-
|
|
6022
|
+
let repository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
6023
|
+
if(!repository){
|
|
6024
|
+
// TRACE
|
|
6025
|
+
console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
|
|
6026
|
+
return NO_TRANSLATION_LABEL;
|
|
6027
|
+
}
|
|
6028
|
+
|
|
6029
|
+
// OLD (ie. splitted format :)
|
|
6030
|
+
// let repositoryForLang=repository[lang];
|
|
6031
|
+
// if (!repositoryForLang){
|
|
6032
|
+
// // TRACE
|
|
6033
|
+
// console.log("ERROR : No translation found for language «"+lang+"»!",languagesAndStrings);
|
|
6034
|
+
// return NO_TRANSLATION_LABEL;
|
|
6035
|
+
// }
|
|
6036
|
+
// result=repositoryForLang[languagesAndStrings]; // Here, languagesAndStrings is just a bundle key.
|
|
6037
|
+
// if (!result) return NO_TRANSLATION_LABEL+" (for key «"+languagesAndStrings+"»)";
|
|
6038
|
+
|
|
6039
|
+
let languagesAndStringsForTranslationKey=repository[languagesAndStrings];
|
|
6040
|
+
if (!languagesAndStringsForTranslationKey){
|
|
6041
|
+
// SILENT ERROR :
|
|
6042
|
+
// // TRACE
|
|
6043
|
+
// console.log("ERROR : No translation found for key «"+languagesAndStrings+"» for language «"+lang+"»!");
|
|
6044
|
+
// NO, OLD : return NO_TRANSLATION_LABEL;
|
|
6045
|
+
return languagesAndStrings;
|
|
6046
|
+
}
|
|
6047
|
+
result=i18n(languagesAndStringsForTranslationKey, lang); // Since we use the same format !
|
|
6048
|
+
|
|
6049
|
+
return result;
|
|
6170
6050
|
};
|
|
6171
6051
|
|
|
6172
6052
|
|
|
6053
|
+
i18n.declareSingleEntry=function(key, languagesAndStringsEntry){
|
|
6054
|
+
let newRepository;
|
|
6055
|
+
let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
6056
|
+
if(!foundRepository){
|
|
6057
|
+
// TRACE
|
|
6058
|
+
console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
|
|
6059
|
+
return;
|
|
6060
|
+
}
|
|
6061
|
+
|
|
6062
|
+
let foundEntryForKey=foundRepository[key];
|
|
6063
|
+
if(!foundEntryForKey){
|
|
6064
|
+
foundRepository[key]=languagesAndStringsEntry;
|
|
6065
|
+
// }else{
|
|
6066
|
+
// // TRACE
|
|
6067
|
+
// console.log("WARN : Duplicate entry for translation key «"+key+"», keeping only the first declared :",foundEntryForKey);
|
|
6068
|
+
}
|
|
6069
|
+
|
|
6070
|
+
};
|
|
6173
6071
|
|
|
6174
6072
|
|
|
6073
|
+
//USAGE :
|
|
6074
|
+
//Example :
|
|
6075
|
+
//i18n.declare({
|
|
6076
|
+
// "commencer":{"fr":"Commencer","en":"Start"}
|
|
6077
|
+
//});
|
|
6078
|
+
i18n.declare=function(repositoryInfos){
|
|
6079
|
+
let newRepository;
|
|
6080
|
+
let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
6081
|
+
if(foundRepository){
|
|
6082
|
+
// // TRACE
|
|
6083
|
+
// console.log("WARN : A repository has already been declared, merging.",foundRepository);
|
|
6084
|
+
newRepository=merge(foundRepository, repositoryInfos);
|
|
6085
|
+
}else{
|
|
6086
|
+
newRepository=repositoryInfos;
|
|
6087
|
+
}
|
|
6088
|
+
(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME]=newRepository;
|
|
6089
|
+
};
|
|
6175
6090
|
|
|
6176
|
-
//
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
// CONSOLE NODE SERVER/CLIENT
|
|
6192
|
-
//
|
|
6193
|
-
getStatic:(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false)=>{
|
|
6091
|
+
// USAGE :
|
|
6092
|
+
// Example :
|
|
6093
|
+
//i18n.declareSplitted({
|
|
6094
|
+
// "fr":{
|
|
6095
|
+
// "commencer":"Commencer",
|
|
6096
|
+
// },
|
|
6097
|
+
// "en":{
|
|
6098
|
+
// "commencer":"Start",
|
|
6099
|
+
// },
|
|
6100
|
+
//});
|
|
6101
|
+
i18n.declareSplitted=function(repositoryInfos){
|
|
6102
|
+
let nonSplittedFormat={};
|
|
6103
|
+
|
|
6104
|
+
foreach(repositoryInfos,(allKeys, lang)=>{
|
|
6105
|
+
foreach(allKeys,(text, key)=>{
|
|
6194
6106
|
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
// TRACE
|
|
6200
|
-
lognow("INFO : (SERVER/CLIENT) Using native WebSocket implementation.");
|
|
6201
|
-
|
|
6202
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
|
|
6203
|
-
if(isNodeContext){
|
|
6204
|
-
if(typeof(WebSocket)==="undefined"){
|
|
6205
|
-
// TRACE
|
|
6206
|
-
console.log("«ws» SERVER library not called yet, calling it now.");
|
|
6207
|
-
|
|
6208
|
-
WebSocket=require("ws");
|
|
6209
|
-
if(typeof(WebSocket)==="undefined"){
|
|
6210
|
-
// TRACE
|
|
6211
|
-
console.log("ERROR : «ws» CONSOLE/BROWSER CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
|
|
6212
|
-
}
|
|
6213
|
-
}
|
|
6214
|
-
}
|
|
6215
|
-
|
|
6216
|
-
}else{
|
|
6217
|
-
// TRACE
|
|
6218
|
-
lognow("INFO : (SERVER/CLIENT) Using socket.io websocket implementation.");
|
|
6219
|
-
|
|
6220
|
-
if(isNodeContext){
|
|
6221
|
-
|
|
6222
|
-
// NODE SERVER :
|
|
6223
|
-
// DBG
|
|
6224
|
-
lognow("INFO : Loading NODE SERVER socket.io libraries.")
|
|
6225
|
-
if(typeof(Socket)==="undefined"){
|
|
6226
|
-
// TRACE
|
|
6227
|
-
console.log("«socket.io» NODE SERVER library not called yet, calling it now.");
|
|
6228
|
-
Socket=require("socket.io");
|
|
6229
|
-
}
|
|
6230
|
-
if(typeof(Socket)==="undefined"){
|
|
6231
|
-
// TRACE
|
|
6232
|
-
console.log("ERROR : «socket.io» NODE CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
|
|
6233
|
-
}
|
|
6234
|
-
|
|
6235
|
-
// NODE CLIENT :
|
|
6236
|
-
// DBG
|
|
6237
|
-
lognow("INFO : Loading NODE CLIENT socket.io libraries.")
|
|
6238
|
-
if(typeof(io)==="undefined"){
|
|
6239
|
-
// TRACE
|
|
6240
|
-
console.log("«socket.io-client» NODE CLIENT library not called yet, calling it now.");
|
|
6241
|
-
io=require("socket.io-client");
|
|
6242
|
-
}
|
|
6243
|
-
if(typeof(io)==="undefined"){
|
|
6244
|
-
// TRACE
|
|
6245
|
-
console.log("ERROR : «socket-client.io» NODE CLIENT library not found. Cannot launch nodejs server. Aborting.");
|
|
6246
|
-
}
|
|
6247
|
-
}
|
|
6248
|
-
|
|
6249
|
-
|
|
6107
|
+
let foundKeyInfos=nonSplittedFormat[key];
|
|
6108
|
+
if(!foundKeyInfos){
|
|
6109
|
+
nonSplittedFormat[key]={};
|
|
6110
|
+
foundKeyInfos=nonSplittedFormat[key];
|
|
6250
6111
|
}
|
|
6251
|
-
|
|
6252
|
-
// *********************************************************************************
|
|
6253
|
-
|
|
6254
|
-
return WebsocketImplementation;
|
|
6255
|
-
},
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
/*private*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
|
|
6259
|
-
|
|
6260
|
-
const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
|
|
6261
|
-
|
|
6262
|
-
let dataResult=eventOrMessage;
|
|
6263
|
-
|
|
6264
|
-
try{
|
|
6265
|
-
dataResult=(WebsocketImplementation.useFlatStrings || isString(eventOrMessage)?parseJSON(eventOrMessage):eventOrMessage);
|
|
6266
|
-
}catch(error1){
|
|
6267
|
-
// TRACE
|
|
6268
|
-
lognow(`ERROR : Failed to parse JSON for string «${dataResult}»`,error1);
|
|
6269
|
-
dataResult=(isString(eventOrMessage)?eventOrMessage:stringifyObject(eventOrMessage));
|
|
6270
|
-
}
|
|
6271
|
-
|
|
6272
|
-
return dataResult;
|
|
6273
|
-
},
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
getServer:(listenableServer)=>{
|
|
6277
6112
|
|
|
6278
|
-
|
|
6113
|
+
let foundTextForLanguage=foundKeyInfos[lang];
|
|
6114
|
+
if(foundTextForLanguage){
|
|
6279
6115
|
// TRACE
|
|
6280
|
-
|
|
6281
|
-
}
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
// TODO : FIXME : Use one single interface !
|
|
6285
|
-
// NODE SERVER MODE ONLY :
|
|
6286
|
-
let serverSocket;
|
|
6287
|
-
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
6288
|
-
serverSocket=new WebSocket.Server({ "server":listenableServer });
|
|
6116
|
+
console.log("WARN : Duplicate entry for translation key «"+key+"» for language «"+lang+"», keeping only the first declared :",foundTextForLanguage);
|
|
6289
6117
|
}else{
|
|
6290
|
-
|
|
6291
|
-
// NOW : socket.io :
|
|
6292
|
-
// Loading socket.io
|
|
6293
|
-
// OLD SYNTAX : serverSocket=Socket.listen(listenableServer);
|
|
6294
|
-
serverSocket=new Socket.Server(listenableServer);
|
|
6295
|
-
|
|
6296
|
-
if(listenableServer.cert || listenableServer.key){
|
|
6297
|
-
// TRACE :
|
|
6298
|
-
lognow("WARN : CAUTION ! ON TODAY (01/08/2022) socket.io SERVER LIBRARY IS BOGUS AND WON'T WORK (node clients can't connect !!) IF listenableServer IS A https SERVER !!!");
|
|
6299
|
-
}
|
|
6300
|
-
|
|
6301
|
-
// Setting up the disconnect event for a client :
|
|
6302
|
-
serverSocket.on("endConnection",()=>{
|
|
6303
|
-
serverSocket.disconnect();
|
|
6304
|
-
});
|
|
6118
|
+
foundKeyInfos[lang]=text;
|
|
6305
6119
|
}
|
|
6306
6120
|
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
lognow("ERROR : An error occurred when trying to start the server : ",error);
|
|
6310
|
-
|
|
6311
|
-
});
|
|
6121
|
+
});
|
|
6122
|
+
});
|
|
6312
6123
|
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
// Join room server part protocol :
|
|
6317
|
-
nodeServerInstance.receive("protocol",(message, clientSocket)=>{
|
|
6318
|
-
nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
|
|
6319
|
-
},{listenerMessageType:"joinRoom"});
|
|
6320
|
-
|
|
6321
|
-
// To make the server aware of the clients connections states :
|
|
6322
|
-
nodeServerInstance.serverSocket.on("close", function close() {
|
|
6323
|
-
|
|
6324
|
-
// TODO : FIXME : Use one single interface !
|
|
6325
|
-
if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
|
|
6326
|
-
// OLD : else serverClients=nodeServerInstance.serverSocket.sockets.clients();
|
|
6327
|
-
else serverClients=nodeServerInstance.serverSocket.sockets.sockets;
|
|
6328
|
-
|
|
6329
|
-
serverClients.forEach((clientSocket)=>{
|
|
6330
|
-
clearInterval(clientSocket.stateCheckInterval);
|
|
6331
|
-
});
|
|
6332
|
-
});
|
|
6124
|
+
// Then we use the standard declaration method :
|
|
6125
|
+
i18n.declare(nonSplittedFormat);
|
|
6126
|
+
};
|
|
6333
6127
|
|
|
6334
|
-
|
|
6335
|
-
|
|
6128
|
+
// For generating languages files helping purposes :
|
|
6129
|
+
i18n.displayAll=function(displaySingleEntriesCalls=false){
|
|
6130
|
+
let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
|
|
6131
|
+
if(!foundRepository){
|
|
6132
|
+
// TRACE
|
|
6133
|
+
console.log("ERROR : No repository found under the variable name «"+I18N_REPOSITORY_VARIABLE_NAME+"»");
|
|
6134
|
+
}else{
|
|
6135
|
+
console.log("TRACE : Variable under the name of «"+I18N_REPOSITORY_VARIABLE_NAME+"»:", foundRepository);
|
|
6136
|
+
console.log("\n\n");
|
|
6137
|
+
|
|
6138
|
+
let message="";
|
|
6139
|
+
foreach(foundRepository, (entry, entryKey)=>{
|
|
6140
|
+
let normalizedEntry={};
|
|
6141
|
+
if(entry.capitalize) normalizedEntry.capitalize=entry.capitalize;
|
|
6142
|
+
if(entry.append) normalizedEntry.append=entry.append;
|
|
6143
|
+
foreach(entry,(e,k)=>{
|
|
6144
|
+
normalizedEntry[k]=e;
|
|
6145
|
+
}
|
|
6146
|
+
,(i,key)=>{ return (!contains(["append","capitalize"],key)); }
|
|
6147
|
+
,(i1,i2)=>{ return (i1.key==i2.key?0:(i1.key=="fr"?-1:1));/*(Sorry, French always comes first !)*/}
|
|
6148
|
+
);
|
|
6149
|
+
let entryStr=stringifyObject(normalizedEntry);
|
|
6150
|
+
message+="\""+entryKey+"\":"+entryStr+",\n";
|
|
6151
|
+
if(displaySingleEntriesCalls) message+="// i18n("+entryStr+");\n";
|
|
6152
|
+
});
|
|
6153
|
+
|
|
6154
|
+
console.log("\n"+message);
|
|
6336
6155
|
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
connectToServer:(serverURL, port, isSecure=false, timeout)=>{
|
|
6156
|
+
}
|
|
6157
|
+
}
|
|
6340
6158
|
|
|
6341
|
-
// TRACE
|
|
6342
|
-
lognow("INFO : Using socket library flavor : "+(WebsocketImplementation.isNodeContext?"node (client/server-side)":"browser (client-side only)"));
|
|
6343
6159
|
|
|
6344
|
-
if(WebsocketImplementation.isNodeContext)
|
|
6345
|
-
return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
|
|
6346
|
-
else
|
|
6347
|
-
return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
|
|
6348
|
-
},
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
//
|
|
6352
|
-
// NODE CLIENT
|
|
6353
|
-
//
|
|
6354
|
-
/*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
|
|
6355
|
-
|
|
6356
|
-
// NODE CLIENT MODE ONLY :
|
|
6357
|
-
let clientSocket;
|
|
6358
|
-
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
6359
|
-
|
|
6360
|
-
// NEW : ws :
|
|
6361
|
-
if(typeof(WebSocket)==="undefined"){
|
|
6362
|
-
// TRACE
|
|
6363
|
-
lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
|
|
6364
|
-
return null;
|
|
6365
|
-
}
|
|
6366
6160
|
|
|
6367
|
-
clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{
|
|
6368
|
-
// CAUTION : SECURITY BREACH :
|
|
6369
|
-
// BUT ALSO NECESSARY TO ALLOW SELF-SIGNED CERTIFICATES USAGE WITH THE YESBOT SYSTEM !
|
|
6370
|
-
rejectUnauthorized:false, // (THIS IS A KNOWN SECURITY BREACH)
|
|
6371
|
-
secure: isSecure
|
|
6372
|
-
});
|
|
6373
|
-
|
|
6374
|
-
clientSocket.addEventListener("error", error=>{
|
|
6375
|
-
// TRACE
|
|
6376
|
-
lognow("ERROR : (NODEJS) A WebSocket client error occurred while trying to connect to server:", error.message);
|
|
6377
|
-
});
|
|
6378
|
-
|
|
6379
|
-
}else{
|
|
6380
|
-
// NOW : socket.io :
|
|
6381
|
-
//client on server-side:
|
|
6382
|
-
|
|
6383
|
-
if(WebsocketImplementation.isNodeContext && typeof(io)!=="undefined"){
|
|
6384
|
-
|
|
6385
|
-
// OLD SYNTAX: clientSocket=Socket.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
6386
|
-
// NO : clientSocket=new Socket.Client(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
|
|
6387
|
-
clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure, autoConnect:true});
|
|
6388
|
-
// UNUSEFUL (since we have the autoconnect:true option) : clientSocket.connect();
|
|
6389
|
-
|
|
6390
|
-
clientSocket.on("connect_error", error=>{
|
|
6391
|
-
// TRACE
|
|
6392
|
-
lognow("ERROR : (NODEJS) A SocketIO client error occurred while trying to connect to server:", error.message);
|
|
6393
|
-
});
|
|
6394
6161
|
|
|
6395
|
-
}
|
|
6396
|
-
}
|
|
6397
|
-
|
|
6398
|
-
// DBG
|
|
6399
|
-
lognow("DEBUG : CLIENT : clientSocket created:");
|
|
6400
6162
|
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6163
|
+
function isCharacterToSkip(c){
|
|
6164
|
+
return RegExp("\\s").test(c) || contains([",","\'","´","\"",":","-","(",")","[","]","<",">","!","?","%",".","/","=",],c);
|
|
6165
|
+
}
|
|
6166
|
+
|
|
6167
|
+
function snakize(strParam, separator="_"){
|
|
6168
|
+
let result="";
|
|
6169
|
+
for(let i=0;i<strParam.length;i++){
|
|
6170
|
+
let c=strParam[i];
|
|
6171
|
+
if(isCharacterToSkip(c)){
|
|
6172
|
+
result+=separator;
|
|
6173
|
+
}else{
|
|
6174
|
+
result+=c.toLowerCase();
|
|
6175
|
+
}
|
|
6176
|
+
if(separator===".") result=result.replace(/\\.+/,separator);
|
|
6177
|
+
else result=result.replace(RegExp(separator+"+"),separator);
|
|
6178
|
+
}
|
|
6179
|
+
return result;
|
|
6180
|
+
}
|
|
6181
|
+
|
|
6182
|
+
function toConvention(type="snake",str){
|
|
6183
|
+
if(empty(str)) return str;
|
|
6184
|
+
str=str.trim();
|
|
6185
|
+
let result="";
|
|
6186
|
+
if(type==="camel"){
|
|
6187
|
+
for(let i=0;i<str.length;i++){
|
|
6188
|
+
let c=str[i];
|
|
6417
6189
|
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
let clientSocket;
|
|
6421
|
-
if(!WebsocketImplementation.useSocketIOImplementation){
|
|
6422
|
-
// CAUTION : PARAMETER rejectUnauthorized:false WILL DO NOTHING,
|
|
6423
|
-
// BECAUSE THIS IS COMPLETLY HANDLED BY THE BROWSER SECURITY POLICY !
|
|
6424
|
-
// SO TO CLEAR THE SSL ERROR :
|
|
6425
|
-
// - FIRST GO TO THE HTTPS:// SERVER ADDRESS WITH BROWSER
|
|
6426
|
-
// - THEN ADD THE SECURITY EXCEPTION IN THE BROWSER !
|
|
6427
|
-
clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
|
|
6428
|
-
|
|
6429
|
-
clientSocket.addEventListener("error", error=>{
|
|
6430
|
-
// TRACE
|
|
6431
|
-
lognow("ERROR : (BROWSER) A WebSocket client error occurred while trying to connect to server:", error.message);
|
|
6432
|
-
});
|
|
6190
|
+
if(isCharacterToSkip(c)){
|
|
6191
|
+
let nextChar="";
|
|
6433
6192
|
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6193
|
+
if(i<str.length-1){// If we are not at the very last character :
|
|
6194
|
+
do{
|
|
6195
|
+
nextChar=str[i+1];
|
|
6196
|
+
i++;
|
|
6197
|
+
}while(isCharacterToSkip(nextChar) && i<str.length-1);
|
|
6198
|
+
if(isCharacterToSkip(nextChar)) nextChar="";
|
|
6199
|
+
}
|
|
6200
|
+
result+=nextChar.toUpperCase();
|
|
6201
|
+
}else{
|
|
6438
6202
|
|
|
6439
|
-
|
|
6440
|
-
// TRACE
|
|
6441
|
-
lognow("ERROR : (BROWSER) A SocketIO client error occurred while trying to connect to server:", error.message);
|
|
6442
|
-
});
|
|
6443
|
-
|
|
6203
|
+
result+=c;
|
|
6444
6204
|
}
|
|
6445
6205
|
|
|
6446
|
-
// BROWSER CLIENT INSTANCE :
|
|
6447
|
-
const browserClientInstance=new ClientInstance(clientSocket);
|
|
6448
|
-
return browserClientInstance;
|
|
6449
|
-
},
|
|
6450
|
-
|
|
6451
|
-
};
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
|
|
6456
|
-
|
|
6457
|
-
const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
if(typeof(https)==="undefined"){
|
|
6462
|
-
// TRACE
|
|
6463
|
-
console.log("«https» SERVER library not called yet, calling it now.");
|
|
6464
|
-
https=require("https");
|
|
6465
|
-
}
|
|
6466
|
-
if(typeof(http)==="undefined"){
|
|
6467
|
-
// TRACE
|
|
6468
|
-
console.log("«http» SERVER library not called yet, calling it now.");
|
|
6469
|
-
http=require("http");
|
|
6470
|
-
}
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
const DEFAULT_HANDLER=function(request, response){
|
|
6474
|
-
|
|
6475
|
-
const url=request.url;
|
|
6476
|
-
|
|
6477
|
-
let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
|
|
6478
|
-
if(contains(url,excludedStr)){
|
|
6479
|
-
return true;
|
|
6480
|
-
}
|
|
6481
|
-
});
|
|
6482
|
-
|
|
6483
|
-
if(isURLInExclusionZone){
|
|
6484
|
-
// TRACE
|
|
6485
|
-
console.log("ERROR 403 forbidden access error :");
|
|
6486
|
-
console.log(error);
|
|
6487
|
-
|
|
6488
|
-
response.writeHead(403);
|
|
6489
|
-
response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
|
|
6490
|
-
response.end();
|
|
6491
|
-
return;
|
|
6492
|
-
}
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
const urlFile="." + url;
|
|
6497
|
-
|
|
6498
|
-
let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
|
|
6499
|
-
if(filePath=="./") filePath="./index.html";
|
|
6500
|
-
|
|
6501
|
-
let contentType="text/html";
|
|
6502
|
-
|
|
6503
|
-
const headers={ "Content-Type": contentType };
|
|
6504
|
-
|
|
6505
|
-
// To remove the CORS error message (cf. https://medium.com/@dtkatz/3-ways-to-fix-the-cors-error-and-how-access-control-allow-origin-works-d97d55946d9)
|
|
6506
|
-
if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
fs.readFile(filePath, function(error, fileContent){
|
|
6510
|
-
if(error){
|
|
6511
|
-
if(error.code=="ENOENT"){
|
|
6512
|
-
// TRACE
|
|
6513
|
-
console.log("ERROR 404 file not found :"+filePath);
|
|
6514
|
-
|
|
6515
|
-
fs.readFile("./404.html", function(error, fileContent){
|
|
6516
|
-
response.writeHead(200, headers);
|
|
6517
|
-
response.end(fileContent, "utf-8");
|
|
6518
|
-
});
|
|
6519
|
-
|
|
6520
|
-
}else {
|
|
6521
|
-
|
|
6522
|
-
// TRACE
|
|
6523
|
-
console.log("ERROR 500 server error :");
|
|
6524
|
-
console.log(error);
|
|
6525
|
-
|
|
6526
|
-
response.writeHead(500);
|
|
6527
|
-
response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
|
|
6528
|
-
response.end();
|
|
6529
|
-
|
|
6530
|
-
}
|
|
6531
|
-
}else {
|
|
6532
|
-
|
|
6533
|
-
// TRACE
|
|
6534
|
-
console.log("INFO 200 OK :"+filePath);
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
response.writeHead(200, headers);
|
|
6538
|
-
response.end(fileContent, "utf-8");
|
|
6539
|
-
|
|
6540
|
-
// res.writeHead(200);
|
|
6541
|
-
// res.end("hello world\n");
|
|
6542
|
-
// res.sendFile(__dirname + "/public/index.html");
|
|
6543
|
-
|
|
6544
|
-
}
|
|
6545
|
-
});
|
|
6546
|
-
|
|
6547
|
-
};
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
let listenableServer;
|
|
6555
|
-
if(sslOptions){
|
|
6556
|
-
let httpsServer=https.createServer(sslOptions, handler).listen(port);
|
|
6557
|
-
// TRACE
|
|
6558
|
-
console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
|
|
6559
|
-
listenableServer=httpsServer;
|
|
6560
|
-
}else{
|
|
6561
|
-
let httpServer=http.createServer(handler).listen(port);
|
|
6562
|
-
// TRACE
|
|
6563
|
-
console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
|
|
6564
|
-
listenableServer=httpServer;
|
|
6565
|
-
}
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
const server=WebsocketImplementation.getStatic(true).getServer(listenableServer);
|
|
6569
|
-
|
|
6570
|
-
// When a client connects, we execute the callback :
|
|
6571
|
-
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
6572
|
-
server.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
6573
|
-
if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
|
|
6574
|
-
});
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
server.onFinalize((serverParam)=>{
|
|
6578
|
-
|
|
6579
|
-
// DBG
|
|
6580
|
-
lognow("onFinalize() server");
|
|
6581
|
-
|
|
6582
|
-
if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
|
|
6583
|
-
});
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
// TRACE
|
|
6587
|
-
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
return server;
|
|
6594
|
-
}
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OPTIONAL*/portParam, /*OPTIONAL*/certPathParam, /*OPTIONAL*/keyPathParam){
|
|
6598
|
-
|
|
6599
|
-
// TRACE
|
|
6600
|
-
console.log("Server launched.");
|
|
6601
|
-
console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
|
|
6602
|
-
console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearTextSecretString>");
|
|
6603
|
-
// EXAMPLE : node orita-srv.js hash orita.global@keyHash 1234567890
|
|
6604
|
-
console.log("Server launched.");
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
// We read the command-line arguments if needed :
|
|
6608
|
-
|
|
6609
|
-
let argCLPort;
|
|
6610
|
-
let argCLCertPath;
|
|
6611
|
-
let argCLKeyPath;
|
|
6612
|
-
|
|
6613
|
-
let serverConfig={};
|
|
6614
|
-
let isForceUnsecure;
|
|
6615
|
-
|
|
6616
|
-
let isHashAsked=false;
|
|
6617
|
-
let clearTextParam=null;
|
|
6618
|
-
let persisterId=null;
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
process.argv.forEach(function (val, i){
|
|
6622
|
-
if(!val) return;
|
|
6623
|
-
// 0 corresponds to «node / nodejs»
|
|
6624
|
-
if(i<=1) return; // 1 corresponds to « <server.js> »
|
|
6625
|
-
else if(i==2){
|
|
6626
|
-
if(val==="hash") isHashAsked=true;
|
|
6627
|
-
}else if(i==3){
|
|
6628
|
-
if(!isHashAsked){
|
|
6629
|
-
try{
|
|
6630
|
-
const jsonConf=parseJSON(val);
|
|
6631
|
-
argCLPort=jsonConf.port;
|
|
6632
|
-
argCLCertPath=jsonConf.sslCertPath;
|
|
6633
|
-
argCLKeyPath=jsonConf.sslKeyPath;
|
|
6634
|
-
serverConfig=nonull(jsonConf.serverConfig,{});
|
|
6635
|
-
}catch(err1){
|
|
6636
|
-
lognow("ERROR : Cannot parse argument JSON string «"+val+"».",err1);
|
|
6637
|
-
}
|
|
6638
|
-
} else persisterId=val;
|
|
6639
|
-
}else if(i==4){
|
|
6640
|
-
if(isHashAsked) clearTextParam=val;
|
|
6641
|
-
}
|
|
6642
|
-
});
|
|
6643
|
-
isForceUnsecure=(argCLCertPath==="unsecure");
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
const aotraNodeServer={config:serverConfig};
|
|
6650
|
-
aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
|
|
6651
|
-
|
|
6652
|
-
if(isHashAsked){
|
|
6653
|
-
// We instanciate a temporary persister just to read the key hash file:
|
|
6654
|
-
const persister=getPersister("./");
|
|
6655
|
-
let persisterIdSplits=persisterId.split("@");
|
|
6656
|
-
if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
|
|
6657
|
-
// TRACE
|
|
6658
|
-
console.log("ERROR : No persister repository IDs provided correctly. Cannot read key hash. Aborting hash generation.");
|
|
6659
|
-
return aotraNodeServer;
|
|
6660
|
-
}
|
|
6661
|
-
const persisterClientId=persisterIdSplits[0];
|
|
6662
|
-
const persisterRepositoryName=persisterIdSplits[1];
|
|
6663
|
-
let globalKeyHashObject=persister.readTreeObjectFromFile(persisterClientId, persisterRepositoryName);
|
|
6664
|
-
if(!globalKeyHashObject || !globalKeyHashObject.keyHash){
|
|
6665
|
-
// TRACE
|
|
6666
|
-
console.log("WARN : No key hash found. Generating one now.");
|
|
6667
|
-
globalKeyHashObject={keyHash:getUUID(), hashes:[]};
|
|
6668
|
-
persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
|
|
6669
|
-
// TRACE
|
|
6670
|
-
console.log("INFO : Key hash generated and saved successfully.");
|
|
6671
|
-
});
|
|
6672
|
-
}
|
|
6673
|
-
const globalKeyHash=globalKeyHashObject.keyHash;
|
|
6674
|
-
|
|
6675
|
-
let firstHash=getHashedString(clearTextParam);
|
|
6676
|
-
|
|
6677
|
-
let generatedHash=getHashedString( firstHash + globalKeyHash, "SHA-256", true);// (we use the heavy treatment thing.)
|
|
6678
|
-
globalKeyHashObject.hashes.push(generatedHash);
|
|
6679
|
-
|
|
6680
|
-
// We update the repository :
|
|
6681
|
-
persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
|
|
6682
|
-
// TRACE
|
|
6683
|
-
console.log("INFO : Hash added to repository and saved successfully.");
|
|
6684
|
-
});
|
|
6685
|
-
|
|
6686
|
-
// OUTPUT
|
|
6687
|
-
console.log("Here is your key : share it with your main clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
|
|
6688
|
-
|
|
6689
|
-
return aotraNodeServer;
|
|
6690
|
-
}
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
const DEFAULT_PORT=nonull(argCLPort,25000);
|
|
6694
|
-
const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
|
|
6695
|
-
const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
let port=portParam ? portParam : DEFAULT_PORT;
|
|
6699
|
-
let certPath=null;
|
|
6700
|
-
let keyPath=null;
|
|
6701
|
-
|
|
6702
|
-
if(!isForceUnsecure){
|
|
6703
|
-
certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
|
|
6704
|
-
keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
|
|
6705
|
-
}
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
// UNUSEFUL :
|
|
6709
|
-
//aotraNodeServer.serverManager.microClientsSockets=[];
|
|
6710
|
-
// UNUSEFUL :
|
|
6711
|
-
//aotraNodeServer.serverManager.mainClientsSockets=[];
|
|
6712
|
-
|
|
6713
|
-
aotraNodeServer.serverManager.start=function(){
|
|
6714
|
-
|
|
6715
|
-
// Eventual encryption options :
|
|
6716
|
-
let sslOptions=null;
|
|
6717
|
-
if(!isForceUnsecure){
|
|
6718
|
-
if(typeof(fs)==="undefined"){
|
|
6719
|
-
// TRACE
|
|
6720
|
-
lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
|
|
6721
|
-
}else{
|
|
6722
|
-
try{
|
|
6723
|
-
sslOptions={
|
|
6724
|
-
cert: fs.readFileSync(certPath, {encoding: "utf8"}),
|
|
6725
|
-
key: fs.readFileSync(keyPath, {encoding: "utf8"}),
|
|
6726
|
-
};
|
|
6727
|
-
}catch(exception){
|
|
6728
|
-
// TRACE
|
|
6729
|
-
lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
|
|
6730
|
-
}
|
|
6731
|
-
}
|
|
6732
|
-
}
|
|
6733
|
-
|
|
6734
|
-
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
return aotraNodeServer;
|
|
6738
|
-
};
|
|
6739
|
-
|
|
6740
|
-
return aotraNodeServer;
|
|
6741
|
-
}
|
|
6742
|
-
|
|
6743
|
-
// ========================= FUSRODA SERVER : =========================
|
|
6744
|
-
|
|
6745
|
-
//
|
|
6746
|
-
// DOES NOT WORK : USE Java FusrodaServer instead :
|
|
6747
|
-
//
|
|
6748
|
-
///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
|
|
6749
|
-
//createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=4000){
|
|
6750
|
-
//
|
|
6751
|
-
// // https://www.npmjs.com/package/screenshot-desktop
|
|
6752
|
-
// // https://github.com/octalmage/robotjs
|
|
6753
|
-
// // npm install --save screenshot-desktop
|
|
6754
|
-
// //
|
|
6755
|
-
// // sudo apt-get install libxtst-dev libx11-dev
|
|
6756
|
-
// // npm install --save robotjs
|
|
6757
|
-
//
|
|
6758
|
-
// // http://getrobot.net/docs/usage.html
|
|
6759
|
-
// //
|
|
6760
|
-
// // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
|
|
6761
|
-
//
|
|
6762
|
-
// const screenshot=require("screenshot-desktop");
|
|
6763
|
-
//
|
|
6764
|
-
// const REFRESH_SCREENSHOTS_MILLIS=500;
|
|
6765
|
-
//
|
|
6766
|
-
//
|
|
6767
|
-
// const server=initNodeServerInfrastructureWrapper(
|
|
6768
|
-
// // On each client connection :
|
|
6769
|
-
// // (serverParam, clientSocketParam)=>{},
|
|
6770
|
-
// null,
|
|
6771
|
-
// // On server finalization :
|
|
6772
|
-
// (serverParam)=>{
|
|
6773
|
-
//
|
|
6774
|
-
// serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
|
|
6775
|
-
// serverParam.addToRoom(clientSocket,"clients");
|
|
6776
|
-
// });
|
|
6777
|
-
//
|
|
6778
|
-
//
|
|
6779
|
-
// serverParam.sendScreenshotsRoutine=setInterval(()=>{
|
|
6780
|
-
// if(serverParam.isScreenshotStarted) return;
|
|
6781
|
-
//
|
|
6782
|
-
// serverParam.isScreenshotStarted=true;
|
|
6783
|
-
//
|
|
6784
|
-
// screenshot().then((img) => {
|
|
6785
|
-
//
|
|
6786
|
-
// const data={image:img,listenerMessageType:"imageData"};
|
|
6787
|
-
// serverParam.send("message", data, "clients");
|
|
6788
|
-
//
|
|
6789
|
-
// serverParam.isScreenshotStarted=false;
|
|
6790
|
-
//
|
|
6791
|
-
// }).catch((error) => {
|
|
6792
|
-
// // TRACE
|
|
6793
|
-
// lognow("ERROR : Error during screenshot :",error);
|
|
6794
|
-
// });
|
|
6795
|
-
//
|
|
6796
|
-
// },REFRESH_SCREENSHOTS_MILLIS);
|
|
6797
|
-
//
|
|
6798
|
-
//
|
|
6799
|
-
// },portParam,certPathParam,keyPathParam);
|
|
6800
|
-
//
|
|
6801
|
-
//
|
|
6802
|
-
// // const doOnConnect=(serverParam, clientSocketParam)=>{
|
|
6803
|
-
// // };
|
|
6804
|
-
// // const doOnFinalizeServer=(serverParam)=>{
|
|
6805
|
-
// // /*DO NOTHING*/
|
|
6806
|
-
// // };
|
|
6807
|
-
// // const server={};
|
|
6808
|
-
// // server.start=(port=4000)=>{
|
|
6809
|
-
// // server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
|
|
6810
|
-
// // };
|
|
6811
|
-
//
|
|
6812
|
-
// return server;
|
|
6813
|
-
//}
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
// ========================= UTILITY SERVERSIDE METHODS : =========================
|
|
6817
|
-
|
|
6818
|
-
class ListManager{
|
|
6819
|
-
|
|
6820
|
-
constructor(config){
|
|
6821
|
-
this.config=config;
|
|
6822
|
-
this.maxItemsNumber=nonull(this.config.max,999);
|
|
6823
|
-
this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
|
|
6824
|
-
this.sessionDurationSeconds=nonull(this.config.duration,null);
|
|
6825
|
-
this.mode=nonull(this.config.mode,"startAtConnexion");
|
|
6826
|
-
|
|
6827
|
-
this.itemsInfos={};
|
|
6828
|
-
this.time=null;
|
|
6829
|
-
this.started=false;
|
|
6830
|
-
}
|
|
6831
|
-
|
|
6832
|
-
addItem(id,item){
|
|
6833
|
-
if(id==null){
|
|
6834
|
-
// TRACE
|
|
6835
|
-
lognow("ERROR : Cannot add item with no id.");
|
|
6836
|
-
return;
|
|
6837
|
-
}
|
|
6838
|
-
if(!item){
|
|
6839
|
-
// TRACE
|
|
6840
|
-
lognow("ERROR : Cannot add null item.");
|
|
6841
|
-
return;
|
|
6842
|
-
}
|
|
6843
|
-
const numberOfItemsCurrently=getArraySize(this.itemsInfos);
|
|
6844
|
-
|
|
6845
|
-
// DBG
|
|
6846
|
-
lognow(">>>>>>>>>numberOfItemsCurrently:",numberOfItemsCurrently);
|
|
6847
|
-
lognow(">>>>>>>>>Object.keys(arrayOfValues):",Object.keys(this.itemsInfos));
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
if(this.maxItemsNumber<=numberOfItemsCurrently){
|
|
6851
|
-
// TRACE
|
|
6852
|
-
lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
|
|
6853
|
-
return;
|
|
6854
|
-
}
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
|
|
6858
|
-
|
|
6859
|
-
// DBG
|
|
6860
|
-
lognow(">>>>>>>>>START SESSION !!!!");
|
|
6861
|
-
|
|
6862
|
-
this.startSession();
|
|
6863
|
-
}
|
|
6864
|
-
this.itemsInfos[id]={
|
|
6865
|
-
item:item,
|
|
6866
|
-
time:getNow(),
|
|
6867
|
-
};
|
|
6868
|
-
}
|
|
6869
|
-
|
|
6870
|
-
startSession(){
|
|
6871
|
-
this.time=getNow();
|
|
6872
|
-
this.started=true;
|
|
6873
|
-
}
|
|
6874
|
-
|
|
6875
|
-
stopSession(){
|
|
6876
|
-
this.started=false;
|
|
6877
|
-
}
|
|
6878
|
-
|
|
6879
|
-
isSessionActive(){
|
|
6880
|
-
|
|
6881
|
-
// DBG
|
|
6882
|
-
lognow(" !!! this.sessionDurationSeconds : "+this.sessionDurationSeconds);
|
|
6883
|
-
lognow(" !!! this.started : "+this.started);
|
|
6884
|
-
lognow(" !!! this.time : "+this.time);
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
if(!this.sessionDurationSeconds) return true;
|
|
6888
|
-
if(!this.started) return false;
|
|
6889
|
-
const result=!hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
|
|
6890
|
-
|
|
6891
|
-
// DBG
|
|
6892
|
-
lognow(" !!! HAS DELAY PASSED : "+result);
|
|
6893
|
-
|
|
6894
|
-
return result;
|
|
6895
|
-
}
|
|
6896
|
-
|
|
6897
|
-
isClientActive(clientId){
|
|
6898
|
-
|
|
6899
|
-
// DBG
|
|
6900
|
-
lognow(" this.isSessionActive()"+this.isSessionActive());
|
|
6901
|
-
|
|
6902
|
-
if(!this.isSessionActive()) return false;
|
|
6903
|
-
const clientPosition=this.getItemPosition(clientId);
|
|
6904
|
-
//CAUTION : Client position starts at 1 !
|
|
6905
|
-
const clientIndex=(clientPosition-1);
|
|
6906
|
-
const result=(clientIndex<=this.maxItemsNumber && clientIndex<=this.simultaneousItemsNumber);
|
|
6907
|
-
|
|
6908
|
-
return result;
|
|
6909
|
-
}
|
|
6910
|
-
|
|
6911
|
-
removeItemById(id){
|
|
6912
|
-
if(id==null){
|
|
6913
|
-
// TRACE
|
|
6914
|
-
lognow("ERROR : Cannot remove item, no id specified.");
|
|
6915
|
-
return;
|
|
6916
|
-
}
|
|
6917
|
-
if(!this.itemsInfos[id]){
|
|
6918
|
-
// TRACE
|
|
6919
|
-
lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
|
|
6920
|
-
return;
|
|
6921
|
-
}
|
|
6922
|
-
delete this.itemsInfos[id];
|
|
6923
|
-
}
|
|
6924
|
-
|
|
6925
|
-
removeItem(item){
|
|
6926
|
-
if(item==null){
|
|
6927
|
-
// TRACE
|
|
6928
|
-
lognow("ERROR : Cannot remove item, none specified.");
|
|
6929
|
-
return;
|
|
6930
|
-
}
|
|
6931
|
-
|
|
6932
|
-
let id=null;
|
|
6933
|
-
foreach(this.itemsInfos,(itemInfos,key)=>{
|
|
6934
|
-
if(itemInfos.item===item
|
|
6935
|
-
// DEBUG ONLY :
|
|
6936
|
-
|| (itemInfos.item.id && item.id && itemInfos.item.id===item.id)
|
|
6937
|
-
){
|
|
6938
|
-
id=key;
|
|
6939
|
-
return "break";
|
|
6940
|
-
}
|
|
6941
6206
|
|
|
6942
|
-
});
|
|
6943
|
-
|
|
6944
|
-
if(!id){
|
|
6945
|
-
// TRACE
|
|
6946
|
-
lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
|
|
6947
|
-
return;
|
|
6948
6207
|
}
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
// Goes from 1 to <length>:
|
|
6954
|
-
getItemPosition(id){
|
|
6955
|
-
if(id==null){
|
|
6956
|
-
// TRACE
|
|
6957
|
-
lognow("ERROR : Cannot calculate item position, no id specified.");
|
|
6958
|
-
return null;
|
|
6959
|
-
}
|
|
6960
|
-
if(!this.itemsInfos[id]){
|
|
6961
|
-
// TRACE
|
|
6962
|
-
lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
|
|
6963
|
-
return null;
|
|
6964
|
-
}
|
|
6965
|
-
let result=0;
|
|
6966
|
-
foreach(this.itemsInfos, (itemInfo, key)=>{
|
|
6967
|
-
result++;
|
|
6968
|
-
if(id===key) return "break";
|
|
6969
|
-
},null,(item1, item2)=>item1.time<item2.time);
|
|
6970
|
-
return result;
|
|
6971
|
-
}
|
|
6972
|
-
|
|
6973
|
-
getItemsNumber(){
|
|
6974
|
-
return getArraySize(this.itemsInfos);
|
|
6975
|
-
}
|
|
6976
|
-
|
|
6977
|
-
};
|
|
6978
|
-
|
|
6979
|
-
getListManager=function(config){
|
|
6980
|
-
return new ListManager(config);
|
|
6981
|
-
};
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
// CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
|
|
6985
|
-
// NO : IN A NODE CONTEXT WITH require("...") WILL RESULT IN AN UNDEFINED FUNCTION ERROR !!!
|
|
6986
|
-
// function performHTTPRequest(...){...
|
|
6987
|
-
// USE THIS INSTEAD :
|
|
6988
|
-
performHTTPRequest=function(completeURL, httpMethod="GET", headers={}, requestBodyOrNamedArgs=null, isNodeContext=false, addCORSHeader=ADD_CORS_HEADER){
|
|
6989
|
-
|
|
6990
|
-
// Body
|
|
6991
|
-
let requestBodyOrNamedArgsStr=null;
|
|
6992
|
-
if(requestBodyOrNamedArgs){
|
|
6993
|
-
if(isString(requestBodyOrNamedArgs) && !isObject(requestBodyOrNamedArgs)){
|
|
6994
|
-
// If it's already a string, we assume it's a valid JSOn string already :
|
|
6995
|
-
requestBodyOrNamedArgsStr=requestBodyOrNamedArgs.replace(/[\r\n]+/gim, ""); // We remove all the breaklines
|
|
6208
|
+
}else{
|
|
6209
|
+
if(type==="bundle"){
|
|
6210
|
+
result=snakize(str,".");
|
|
6996
6211
|
}else{
|
|
6997
|
-
|
|
6998
|
-
requestBodyOrNamedArgsStr=stringifyObject(requestBodyOrNamedArgs).replace(/[\r\n]+/gim, ""); // We remove all the breaklines
|
|
6999
|
-
}catch(parseErr){
|
|
7000
|
-
requestBodyOrNamedArgsStr=""+requestBodyOrNamedArgs;
|
|
7001
|
-
}
|
|
6212
|
+
result=snakize(str);
|
|
7002
6213
|
}
|
|
7003
6214
|
}
|
|
7004
6215
|
|
|
7005
|
-
let body=null;
|
|
7006
|
-
// Not the same way to send parameters, with POST/PUT http method :
|
|
7007
|
-
if(contains(["POST","PUT"],httpMethod) && requestBodyOrNamedArgs){
|
|
7008
|
-
body=requestBodyOrNamedArgsStr;
|
|
7009
|
-
}
|
|
7010
|
-
|
|
7011
|
-
// Headers
|
|
7012
|
-
if(contains(["POST","PUT"],httpMethod)){
|
|
7013
|
-
headers["Content-Type"]="application/json";
|
|
7014
|
-
|
|
7015
|
-
if(requestBodyOrNamedArgsStr && isString(body)){
|
|
7016
|
-
headers["Content-Length"]=Buffer.byteLength(requestBodyOrNamedArgsStr);
|
|
7017
|
-
}
|
|
7018
|
-
|
|
7019
|
-
// To remove the CORS error message (cf. https://medium.com/@dtkatz/3-ways-to-fix-the-cors-error-and-how-access-control-allow-origin-works-d97d55946d9)
|
|
7020
|
-
if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
|
|
7021
|
-
|
|
7022
|
-
} else if(httpMethod==="GET" && requestBodyOrNamedArgs){
|
|
7023
|
-
// Not the same way to send parameters in GET http method :
|
|
7024
|
-
// DBG
|
|
7025
|
-
lognow("unformatted API URL : "+completeURL);
|
|
7026
|
-
|
|
7027
|
-
completeURL=appendGetParameters(completeURL, requestBodyOrNamedArgs);
|
|
7028
|
-
|
|
7029
|
-
// DBG
|
|
7030
|
-
lognow("formatted API URL : "+completeURL);
|
|
7031
|
-
}
|
|
7032
|
-
|
|
7033
|
-
// CASE BROWSER CONTEXT :
|
|
7034
|
-
if(!isNodeContext || typeof(require)=="undefined"){
|
|
7035
|
-
// TRACE
|
|
7036
|
-
lognow("INFO : We are not running in a browser context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using browser library.");
|
|
7037
|
-
|
|
7038
|
-
return new Promise((resolve,reject)=>{
|
|
7039
|
-
fetch(completeURL, {
|
|
7040
|
-
method: httpMethod,
|
|
7041
|
-
headers: headers,
|
|
7042
|
-
body: body,
|
|
7043
|
-
})
|
|
7044
|
-
// STRANGE : DOES NOT WORK !!:
|
|
7045
|
-
//.then(response => response.json())
|
|
7046
|
-
// STRANGE : DOES WORK :
|
|
7047
|
-
.then(response => {
|
|
7048
|
-
|
|
7049
|
-
// DBG
|
|
7050
|
-
console.log("~~~~~~~~~~~response :",response);
|
|
7051
|
-
|
|
7052
|
-
return response.json();
|
|
7053
|
-
}).then(data => {
|
|
7054
|
-
|
|
7055
|
-
// CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
|
|
7056
|
-
const status=data.status;
|
|
7057
|
-
// if(status!=200 && data.error){
|
|
7058
|
-
// // const error=nonull(data.error, data.detail);
|
|
7059
|
-
// const error=data.error;
|
|
7060
|
-
// // TRACE
|
|
7061
|
-
// console.error("Error:", error);
|
|
7062
|
-
// reject({error:""+error, httpStatus:status});
|
|
7063
|
-
// return;
|
|
7064
|
-
// }
|
|
7065
|
-
|
|
7066
|
-
// DBG
|
|
7067
|
-
console.log("~~~~~~~~~~~data :",data);
|
|
7068
|
-
|
|
7069
|
-
data.httpStatus=status;
|
|
7070
|
-
resolve(data);
|
|
7071
|
-
}).catch(error => {
|
|
7072
|
-
// TRACE
|
|
7073
|
-
console.error("Error:", error);
|
|
7074
|
-
reject({error:`${error}`, httpStatus:null});
|
|
7075
|
-
});
|
|
7076
|
-
});
|
|
7077
|
-
}// else :
|
|
7078
|
-
|
|
7079
|
-
// CASE NODEJS CONTEXT :
|
|
7080
|
-
|
|
7081
|
-
// TRACE
|
|
7082
|
-
lognow("INFO : We are running in a nodejs context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using nodejs library.");
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
const isSecure=(!empty(completeURL) && contains(completeURL.toLowerCase(),"https://"));
|
|
7086
|
-
const httpHandler=isSecure?require("https"):require("http");
|
|
7087
|
-
|
|
7088
|
-
// Options for the HTTP request
|
|
7089
|
-
const options = {
|
|
7090
|
-
url: completeURL,
|
|
7091
|
-
method: httpMethod,
|
|
7092
|
-
};
|
|
7093
|
-
|
|
7094
|
-
if(contains(["POST","PUT"],httpMethod)){
|
|
7095
|
-
options.json=true;
|
|
7096
|
-
}
|
|
7097
|
-
|
|
7098
|
-
options.headers=headers;
|
|
7099
|
-
|
|
7100
|
-
return new Promise((resolve,reject)=>{
|
|
7101
|
-
|
|
7102
|
-
// Create the HTTP request
|
|
7103
|
-
// DOES NOT WORK : const request = httpHandler.request(options, (response) => {
|
|
7104
|
-
// UNLESS YOU SPECIFY in options : hostname, port, path
|
|
7105
|
-
const request = httpHandler.request(completeURL, options, (response) => {
|
|
7106
|
-
|
|
7107
|
-
let responseDataStr = "";
|
|
7108
|
-
|
|
7109
|
-
// A chunk of data has been received.
|
|
7110
|
-
response.on("data", (chunk) => {
|
|
7111
|
-
responseDataStr += chunk;
|
|
7112
|
-
// // DEBUG ONLY
|
|
7113
|
-
// const str = Buffer.from(chunk).toString("utf8");
|
|
7114
|
-
// lognow(">>>str:"+str);
|
|
7115
|
-
});
|
|
7116
|
-
|
|
7117
|
-
// The whole response has been received.
|
|
7118
|
-
response.on("end", () => {
|
|
7119
|
-
|
|
7120
|
-
try{
|
|
7121
|
-
let responseData;
|
|
7122
|
-
try{
|
|
7123
|
-
responseData=parseJSON(responseDataStr);
|
|
7124
|
-
}catch(parseError1){
|
|
7125
|
-
// CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
|
|
7126
|
-
// DEBUG
|
|
7127
|
-
responseDataStr=responseDataStr.replace(/'/gim,"`");
|
|
7128
|
-
try{
|
|
7129
|
-
responseData=parseJSON(responseDataStr);
|
|
7130
|
-
}catch(parseError2){
|
|
7131
|
-
// DBG
|
|
7132
|
-
lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError2);
|
|
7133
|
-
resolve( {response:response, responseDataStr:responseDataStr} );
|
|
7134
|
-
return;
|
|
7135
|
-
}
|
|
7136
|
-
}
|
|
7137
|
-
|
|
7138
|
-
// CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
|
|
7139
|
-
const status=response.statusCode;
|
|
7140
|
-
// if(status!=200){
|
|
7141
|
-
// const error=nonull(response.statusMessage, "No error message for error code "+status);
|
|
7142
|
-
// // TRACE
|
|
7143
|
-
// console.error("Error:", error);
|
|
7144
|
-
// reject({error:""+error, httpStatus:status});
|
|
7145
|
-
// return;
|
|
7146
|
-
// }
|
|
7147
|
-
|
|
7148
|
-
resolve( {responseData:responseData, response:response, responseDataStr:responseDataStr, httpStatus: status} );
|
|
7149
|
-
}catch(parseError3){
|
|
7150
|
-
// DBG
|
|
7151
|
-
lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError3);
|
|
7152
|
-
resolve( {response:response, responseDataStr:responseDataStr} );
|
|
7153
|
-
return;
|
|
7154
|
-
}
|
|
7155
|
-
|
|
7156
|
-
});
|
|
7157
|
-
});
|
|
7158
|
-
|
|
7159
|
-
// Handle errors
|
|
7160
|
-
request.on("error", (error) => {
|
|
7161
|
-
reject({error:error, httpStatus:null});
|
|
7162
|
-
});
|
|
7163
|
-
|
|
7164
|
-
// Not the same way to send parameters, with POST/PUT http method :
|
|
7165
|
-
if(contains(["POST","PUT"],httpMethod) && body){
|
|
7166
|
-
request.write(body);
|
|
7167
|
-
}
|
|
7168
|
-
|
|
7169
|
-
// End the request
|
|
7170
|
-
request.end();
|
|
7171
|
-
|
|
7172
|
-
});
|
|
7173
|
-
|
|
7174
|
-
};
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
replacePathVariablesNamesWithValuesIfPossible=function(apiURL, namedArgs){
|
|
7180
|
-
let result=apiURL;
|
|
7181
|
-
foreach(namedArgs,(namedArgValue, namedArgKey)=>{
|
|
7182
|
-
result=result.replace("{"+namedArgKey+"}",namedArgValue);
|
|
7183
|
-
});
|
|
7184
6216
|
return result;
|
|
7185
|
-
}
|
|
7186
|
-
|
|
7187
|
-
appendGetParameters=function(apiURL, namedArgsParam){
|
|
7188
|
-
if(nothing(namedArgsParam)) return "";
|
|
7189
|
-
try{
|
|
7190
|
-
|
|
7191
|
-
const namedArgs=isString(namedArgsParam)?parseJSON(namedArgsParam):namedArgsParam;
|
|
7192
|
-
|
|
7193
|
-
let result=apiURL;
|
|
7194
|
-
|
|
7195
|
-
const paramCouples=[];
|
|
7196
|
-
foreach(namedArgs,(value,key)=>{paramCouples.push(key+"="+value);});
|
|
7197
|
-
|
|
7198
|
-
if(!empty(paramCouples)) result+=("?"+paramCouples.join("&"));
|
|
7199
|
-
|
|
7200
|
-
return result;
|
|
7201
|
-
}catch(parseError){
|
|
7202
|
-
// TRACE
|
|
7203
|
-
lognow("ERROR : Could not parse string parameters object «"+namedArgsParam+"», aborting GET parameter request string calculation:", parseError);
|
|
7204
|
-
return "";
|
|
7205
|
-
}
|
|
7206
|
-
};
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
6217
|
+
}
|
|
7210
6218
|
|
|
7211
6219
|
|
|
7212
6220
|
|
|
6221
|
+
function capitalize(str){
|
|
6222
|
+
if(nothing(str)) return "";
|
|
6223
|
+
return str.replace(/(?:^|\s)\S/m, firstChar => firstChar.toUpperCase()); // We replace only the first character.
|
|
6224
|
+
}
|
|
7213
6225
|
|
|
7214
6226
|
|
|
7215
6227
|
/* ## Utility network global methods in a javascript, console (nodejs), or vanilla javascript with no browser environment.
|