/* Copyright (c) 2020, 2023, The Unified Company. This code is part of Unify. This program is free software; you can redistribute it and/or modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE, as published by the Free Software Foundation. See the GNU AFFERO GENERAL PUBLIC LICENSE, for more details. https://unifyjs.org */ import tools from '../unify/tools.js'; import socketMessage from '../unify/socketMessage.js'; import promiseManager from './promiseManager.js'; import collection from './collection.js'; //import debugManager from './debugManager.js'; import unify from '../unify/unify.js'; import defaultObject from '../unify/defaultObject.js'; //import unParsedObjects from './unparsed.js' import processManager from "./processManager/processManager.js" import ajaxSocket from './ajaxSocket.js' document.requests = new Array(); export default class socketManager{ promiseManager = new promiseManager(); //debugger = new debugManager(); processManager = new processManager(); socket = false; mode = "client"; state = "disconnected"; serverAddress = "localhost"; messageID = 0; tries = 0; port = 5002; ssl = false; promises = new Array(); constructor( port ) { this.promiseManager.socketManager = this; this.processManager.promise.promiseManager = this.promiseManager; //this.debugger.socketManager = this; document.socketManager = this; if( port ) { this.port = port; } } getSocketAddress() { if( !this.ssl ) { console.log( "%c Trying to connect with server on host: ws://" + this.serverAddress + ":" + this.port, "background: yellow;" ); var socketAddress = "ws://" + this.serverAddress + ":" + this.port; } else { console.log( "%c Ssl set to true, Tying to create Secure connection with server on host: wss://" + this.serverAddress.split(":")[0], "background: yellow;" ); var socketAddress = "wss://" + this.serverAddress.split(":")[0] + ":" + this.port ;// + ":" + this.port } return socketAddress; } createSocket() { var socketManager = this; var socketAddress = this.getSocketAddress() this.socket = new WebSocket( socketAddress ); var promiseManager = this.promiseManager; this.socket.onclose = function(){ socketManager.onClose() }; this.socket.onopen = function(){ socketManager.onOpen() }; } pingServer() { var message = new socketMessage(); message.controller = "ping"; this.socket.send( JSON.stringify( message ) ); var that = this; setTimeout( function(){ that.pingServer() }, 5000 ); } async connect() { var promiseManager = this.promiseManager; promiseManager.promises = new Array(); promiseManager.messages = new Array(); this.messageID = 0; this.createSocket(); var promiseFunction = promiseManager.createPromiseFunction( this.messageID ); var promise = new Promise( promiseFunction ); await promise; var message = promiseManager.getMessageByID( this.messageID ); this.logRecieveMessage( message ); this.messageID++; this.pingServer(); return message.applications; } logRecieveMessage( message ) { //console.log( "Client recieved message via socket: ", message ); var className = tools.getClassNameByObject( message ); if( document.debugALL ) { console.log(""); console.log( "%c recieved message: ", "background: #8bc34a;" ); console.log( "className: ", className); console.log( message ); console.log(""); } } onOpen() { console.log("%c Connected to Socket server", "background: green;"); this.state = "connected"; this.registerClientType( this.mode ); this.createEventListeners(); } logAjaxConnection( data ) { console.log( "first message recieved", data ); console.log( "sessionKey", this.sessionKey ); } async registerClient( sessionKey ) { var promiseManager = this.promiseManager; var promiseFunction = promiseManager.createPromiseFunction( this.messageID ); var promise = new Promise( promiseFunction ); var message = new socketMessage(); message.id = this.messageID; if( this.sessionKey ) { message.type = "reconnectClient"; message.object = this.sessionKey; } else { message.type = "registerClient"; message.object = "none"; } console.log("message", message); this.socket.send( JSON.stringify( message ) ); console.log("awaiting promise"); await promise; var message = promiseManager.getMessageByID( this.messageID ); this.messageID++ console.log("promise recieved", message); } disconnect() { this.socket.close(); } async addClient() { var promiseManager = this.promiseManager; this.messageID++; var messageID = this.messageID; var data = await this.registerClient( ); var message = promiseManager.getMessageByID( messageID ); this.sessionKey = message.data.sessionKey; return message; } createAjaxSocketClass() { var that = this; return class ajaxSocket{ async send( message ) { var json = JSON.parse( message ); json.sessionKey = new String( that.sessionKey ); message = JSON.stringify( json ); var result = await that.fetch( message ); var a = new Object(); a.data = result; that.message( a ) } } } async connectAjax() { var message = new Object(); message.eventName = "connect"; var data = await this.fetch( JSON.stringify( message ) ); this.sessionKey = new String( JSON.parse( data ).sessionKey ); this.logAjaxConnection( data ); //this.socket = new ajaxSocket(); this.socket = this.createAjaxSocketClass(); this.registerClientType( this.mode ); } async fetch( jsonString ) { const response = await fetch( "/api/", { body: jsonString, method:'post', headers:{ "Accept": "application/json, text/plain, */*", "Content-type": "application/json; charset = UTF-8" }, } ); return response.json(); } onClose() { this.state = "disconnected"; this.tries++; this.reconnect(); this.retry(); } retry() { // Ajax fallback // todo disabled because android requires reconnection /* if( this.tries < 1 ) { this.reconnect(); } else { console.log("connect ajax"); var message = this.connectAjax(); //this.message( content ); } */ } async reconnect() { await this.connect(); this.registerClient( this.sessionKey ); } createEventListeners() { var socketManager = this; this.socket.onmessage = function( content ) { socketManager.message( content ) } } registerClientType( type ) { this.mode = type; var message = new socketMessage(); message.controller = "settings"; message.method = "setType"; message.path = new Array(); message.object = type; this.socket.send( JSON.stringify( message ) ); } signin( user ) { var message = new socketMessage(); message.controller = "settings"; message.method = "signIn"; message.object = user; this.socket.send( JSON.stringify( message ) ); } async signOut() { var message = new socketMessage(); message.controller = "settings"; message.method = "signOut"; this.get( "settings", "signOut", new Object() ); } message( content ) { this.processManager.process( content ); } createMessage( messageID, controller, method, eventName, object, data ) { var message = new socketMessage(); message.id = messageID; message.controller = controller; message.method = method; message.parse = object.parse; message.data = data; message.eventName = eventName; if( object.getClassName ) { message.object = object.clean( method )[ object.getClassName() ]; if( object.nodeMethodArguments && method == "callNodeMethod" ) { message.object.nodeMethodName = tools.validateValue( object.nodeMethodName ); message.object.nodeMethodArguments = tools.validateValue( object.nodeMethodArguments ); } message.applicationPath = tools.getApplicationPath( object ); } else { message.object = object; } if( typeof object == "string" ) { message.object = object; } return message; } createPromise( promiseFunction ) { var promise = new Promise( promiseFunction ) document.promises.push( promise ); return promise; } async get( controller, method, object, eventName, data ) { var messageID = ++this.messageID; var promiseFunction = this.promiseManager.createPromiseFunction( messageID, object ); var message = this.createMessage( messageID, controller, method, eventName, object, data ); this.logSendMessage( message, object ); console.log("send raw message", JSON.stringify( message )); if( document.requestsObject ) { document.requestsObject.addRequest(controller, method, object, eventName, message); } else { document.requests.push({ controller, method, object, eventName, message }); } this.socket.send( JSON.stringify( message ) ); var promise = this.createPromise( promiseFunction ); await promise; return this.processMessage( method, messageID ); } async getRaw( message ) { var messageID = ++this.messageID; var promiseFunction = this.promiseManager.createPromiseFunction( messageID ); console.log( "" ); console.log("messageID", messageID) console.log( "%c send message: ", "background: #11db33;", message ); console.log( "" ); message.id = messageID; this.socket.send( JSON.stringify( message ) ); var promise = this.createPromise( promiseFunction ); await promise; console.log("promise fulfilled"); var data = this.promiseManager.getMessageByID( messageID ); return JSON.stringify( data, null, 4 ); } processMessage( method, messageID ) { var data = this.promiseManager.getMessageByID( messageID ).data; switch( method ) { case "count": if( !tools.getFirstChild( data ) ) { return 0; } return tools.getFirstChild( data ).count; break; default: if( Object.prototype.toString.call( data ) === '[object Array]' ) { return data; } if( typeof data == "object" ) { return tools.getFirstChild( data ); } else { return data; } } } logInjection( message ) { var path = message.applicationPath.join("/"); console.log( "socketMessage", message.id ); console.log( "" ); console.log( "%c send message: ", "background: #eb2666;", path, message ); console.log( "" ); } handleInjection( messageID, data ) { var data = this.promiseManager.getMessageByID( messageID ).data; switch( method ) { case "count": return tools.getFirstChild( data ).count; break; default: return tools.getFirstChild( data ); } } async inject( message ) { var promiseFunction = this.promiseManager.createPromiseFunction( message.id ); var messageID = message.id; var method = message.method; this.logInjection( message ); this.socket.send( JSON.stringify( message ) ); await new Promise( promiseFunction ); return this.handleInjection( messageID, data ); } logSendMessage( message, object ) { var path = ""; if( message.applicationPath ) { path = message.applicationPath.join("/"); } if( ( object.getClassName && object.debug ) || document.debugALL ) { console.log( "" ); console.log( "%c send message: ", "background: #11db33;", path, message ); //console.log( object.getClassName() ); //console.log( path ); //console.log( message ); console.log( "" ); } } async update( eventName, object ) { var result = await this.get( "column", "update", object, eventName ); } }