UNPKG

@xuda.io/runtime-bundle

Version:

The Xuda Runtime Bundle refers to a collection of scripts and libraries packaged together to provide the necessary runtime environment for executing plugins or components in the Xuda platform.

499 lines (445 loc) • 16.7 kB
const _this = {}; // boaz export const init_module = (e) => { _this.func = e.func; _this.glb = e.glb; _this.SESSION_OBJ = e.SESSION_OBJ; _this.APP_OBJ = e.APP_OBJ; _this.IS_DOCKER = e.IS_DOCKER; _this.IS_API_SERVER = e.IS_API_SERVER; _this.IS_PROCESS_SERVER = e.IS_PROCESS_SERVER; }; export const project_loader = async function (SESSION_ID, app_id, prog_id) { try { var _session = _this.SESSION_OBJ[SESSION_ID]; if (_this.func.UI.utils.get_url_attribute(SESSION_ID, 'clear_cache')) { await func.index.delete_pouch(SESSION_ID); } let last_changed_ts = 0; var ret_build_info = {}; // call from xuda real-preview app if (typeof XUDA_BUILD_INFO !== 'undefined') { _session.build_info = XUDA_BUILD_INFO; } else { // test const db = await func.utils.connect_pouchdb(SESSION_ID); if (['live_preview', 'miniapp'].includes(_session.engine_mode)) { // if on-line if (IS_ONLINE) { ret_build_info = await get_app_build_info(SESSION_ID, app_id); if (ret_build_info.code < 0) { return console.error(ret_build_info); } // save to pouch try { const { _id, _rev } = await db.get(`cache_build_info`); await db.remove({ _id, _rev }); } catch (error) {} try { await db.put({ _id: `cache_build_info`, build_info: ret_build_info, docType: 'cache_build_info', }); } catch (error) {} } else { // off-line get from pouch try { ret_build_info = (await db.get(`cache_build_info`)).build_info; } catch (error) { return console.error('cache_build_info error'); } } last_changed_ts = ret_build_info.data.last_changed_ts; } else { // deployments try { // const db = await func.utils.connect_pouchdb(SESSION_ID); // only get indication for fresh installation await db.get(`cache_rt_info`); } catch (err) { // fresh load const startup_module = await func.common.get_module(SESSION_ID, 'xuda-deploy-startup-loader.mjs'); await startup_module.loader(SESSION_ID); } last_changed_ts = _session.opt.last_changed_ts; } } // if (_session.engine_mode !== 'live_preview') { // try { // const db = await func.utils.connect_pouchdb(SESSION_ID); // // only get indication for fresh installation // await db.get(`cache_rt_info`); // } catch (err) { // const startup_module = await func.common.get_module(SESSION_ID, 'xuda-deploy-startup-loader.mjs'); // await startup_module.loader(SESSION_ID); // } // last_changed_ts = _session.opt.last_changed_ts; // } await get_rt_info(SESSION_ID, app_id, last_changed_ts); insert_custom_prop(SESSION_ID); if (_session?.app_admin_prop?.app_admin_direction) { $(_session.root_element).attr('dir', _session.app_admin_prop.app_admin_direction); } // load live preview module if (_session.engine_mode === 'live_preview') { const module = await _this.func.common.get_module(SESSION_ID, 'xuda-live-preview-module.esm.js'); module.live_preview_loader(SESSION_ID); return; } if (_this.APP_OBJ[app_id]?.is_deployment) { try { await init_runtime_websocket(SESSION_ID, app_id); } catch (error) { throw error; } } const module = await _this.func.common.get_module(SESSION_ID, `xuda-progs-loader-module.mjs`); if (app_id !== 'unknown') { await module.load_objects_cache(SESSION_ID); } await _this.func.UI.main.embed_loader(SESSION_ID); } catch (error) { throw error; } }; const get_app_build_info = async function (SESSION_ID, app_id) { return new Promise(function (resolve, reject) { var _session = _this.SESSION_OBJ[SESSION_ID]; let app_id_reference = _this.APP_OBJ[app_id].app_id_reference; fetch(_this.func.common.get_url(SESSION_ID, 'rpi', 'get_app_build'), { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'xu-gtp-token': _session.gtp_token, 'xu-app-token': _session.app_token, }, body: JSON.stringify({ app_id: app_id, app_id_reference, engine_mode: _session.engine_mode, }), }) .then((response) => { if (!response.ok) { return response.text().then((text) => { throw new Error(text); }); } return response.json(); }) .then(async (json) => { _this.SESSION_OBJ[SESSION_ID].build_info = json.data; resolve(json); }) .catch((err) => { try { resolve(JSON.parse(err.message)); } catch (error) { resolve({ code: -1, data: err.message }); } }); }); }; const get_user_group_account_info = async function (SESSION_ID, uid) { var _session = SESSION_OBJ[SESSION_ID]; const response = await fetch(`https://${_session.domain}/cpi/get_account_info`, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ uid, uid_query: $.cookie('uid'), }), }); const json = await response.json(); return json.data; }; const get_rt_info = async function (SESSION_ID, app_id, last_changed_ts) { var _session = _this.SESSION_OBJ[SESSION_ID]; return new Promise(async function (resolve, reject) { const response = { success: async function (ret, ajaxP) { if (ret.code < 0) { return response.error(ret.data); } const rt_info_obj = ret.data; var app_id = rt_info_obj._id; _this.APP_OBJ[app_id] = rt_info_obj; _session.app_id = app_id; if (rt_info_obj?.deploy_data?.global_variables) { _session.url_params = { ..._session.url_params, ...rt_info_obj.deploy_data.global_variables, }; } let account_info = _.clone(rt_info_obj?.account_info || {}); if (_session.engine_mode === 'user_group') { const user_group_data = await get_user_group_account_info(SESSION_ID, rt_info_obj?.account_info?.uid); account_info = user_group_data.account_info; account_info.uid = user_group_data._id; } _session.USR_OBJ = { _id: account_info?.uid, usr_name: account_info?.username, usr_first_name: account_info?.first_name || account_info?.email, usr_last_name: account_info?.last_name || '', usr_email: account_info?.email, usr_profile_picture: account_info?.profile_picture, }; _session.login_info = rt_info_obj?.login_info; _session.client_ip = rt_info_obj.client_ip; _session.rpi_http_methods = rt_info_obj.rpi_http_methods; _session.app_admin_prop = rt_info_obj.app_admin_prop; _session.is_deployment = rt_info_obj.is_deployment; // const set_prog_cache = async function () { // if (_session.prog_id) { // if (!rt_info_obj.prog_docs[_session.prog_id]) { // return console.error( // `error - program ${_session.prog_id} not found.` // ); // } // DOCS_OBJ[app_id][_session.prog_id] = rt_info_obj.prog_docs[ // _session.prog_id // ] || { // _id: _session.prog_id, // }; // const module = await _this.func.common.get_module( // SESSION_ID, // `xuda-progs-loader-module.mjs` // ); // module.save_objects_cache( // SESSION_ID, // _session.prog_id, // "DOCS_OBJ", // rt_info_obj.prog_docs[_session.prog_id] // ); // } // // for (let prog_id of [ // // ...rt_info_obj?.accessible_deployed_progs_arr, // // ...rt_info_obj?.accessible_deployed_tables_arr, // // ] || []) { // // module.DOCS_OBJ_get(SESSION_ID, prog_id); // // } // }; // if (_session.engine_mode !== "live_preview") { // set_prog_cache(); // } resolve(); $('.loader_logo').css('background-image', `url( ${rt_info_obj.app_pic})`).hide().fadeIn(); }, error: async function (err) { if (err) { return _this.func.UI.utils.progressScreen.show(SESSION_ID, err, null, true); } // location.reload(); console.warn('** reload request'); }, }; const db = await func.utils.connect_pouchdb(SESSION_ID); try { let ret = await db.get(`cache_rt_info`); const rt_info_obj = ret.data; if (rt_info_obj.data.last_changed_ts !== last_changed_ts || (!['live_preview', 'miniapp'].includes(_session.engine_mode) && rt_info_obj.data.app_build_id !== _session.opt.app_build_id)) { _this.func.UI.utils.progressScreen.show(SESSION_ID, 'New application setup detected, refreshing data and reloading in 5 sec'); setTimeout(async () => { await func.index.delete_pouch(SESSION_ID); location.reload(); }, 5000); return; } response.success(rt_info_obj); } catch (err) { fetch(_this.func.common.get_url(SESSION_ID, 'rpi', 'get_rt_info'), { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'xu-gtp-token': _session.gtp_token, 'xu-app-token': _session.app_token, }, body: JSON.stringify({ app_id, prog_id: _session.engine_mode === 'live_preview' ? '' : _session.prog_id, engine_mode: _session.engine_mode, build: _session.build_info.build, session_id: SESSION_ID, is_cordova: _this.glb.is_cordova, client_info: _session.SYS_GLOBAL_OBJ_CLIENT_INFO, client_id: _session.SYS_GLOBAL_OBJ_CLIENT_INFO.fingerprint, }), }) .then((response) => { if (!response.ok) { return response.text().then((text) => { throw new Error(text); }); } return response.json(); }) .then((json) => { if (json.code < 0) { throw new Error(json.data || 'unknown error'); } response.success(json); var doc = { _id: `cache_rt_info`, data: json, docType: 'cache_app', }; db.put(doc); }) .catch((err) => { response.error(err.message); }); } }); }; const insert_custom_prop = function (SESSION_ID) { try { var app_id = _this.SESSION_OBJ[SESSION_ID].app_id; if (_this.APP_OBJ[app_id]?.app_custom_prop?.app_custom_header) { var head = document.getElementsByTagName('head')[0]; const app_custom_header = _this.APP_OBJ[app_id].app_custom_prop.app_custom_header; $(head).append(func.utils.replace_studio_drive_url(SESSION_ID, app_custom_header)); } if (_this.APP_OBJ[app_id]?.app_custom_prop?.app_custom_body) { $(_this.SESSION_OBJ[SESSION_ID].root_element).prepend(_this.APP_OBJ[app_id].app_custom_prop.app_custom_body); } } catch (err) { console.error(err); } }; const init_runtime_websocket = function (SESSION_ID, app_id) { return new Promise(function (resolve, reject) { const set_connected = async function (stat) { var datasource_changes = { [0]: { ['data_system']: { SYS_GLOBAL_BOL_CONNECTED: stat }, }, }; await func.datasource.update(SESSION_ID, datasource_changes); }; //////////////// IO ////////////////// var _session = _this.SESSION_OBJ[SESSION_ID]; var _data_system = _session?.DS_GLB?.[0]?.data_system; const url = `https://${location.hostname}`; var error; RUNTIME_SERVER_WEBSOCKET = io(url, { secure: true, reconnection: true, // _this.glb.debug_js ? false : true, // reconnection: true, // reconnectionDelayMax: 10000, rejectUnauthorized: false, path: '/ws/socket.io', // transports: ['websocket'] // query: { session_obj: SESSION_OBJ[SESSION_ID] }, }); RUNTIME_SERVER_WEBSOCKET.on('connect', () => { console.info('RUNTIME_SERVER_WEBSOCKET connected'); if (_data_system) { set_connected(1); } if (_session.opt.enable_offline) { if ($(_session.root_element).hasClass('runtime_offline')) { $(_session.root_element).removeClass('runtime_offline'); func.utils.alerts.toast(SESSION_ID, 'Switched to on-line mode', 'You are now online. All data stored while you were offline will be synchronized to the server.', 'success'); } } else { // back from temporarily disconnected from the server _this.func.UI.utils.progressScreen.hide(SESSION_ID); } if (error) { if (!RUNTIME_SERVER_WEBSOCKET_CONNECTED) { // location.reload(); console.warn('** reload request'); } } }); RUNTIME_SERVER_WEBSOCKET.on('message', (e) => { if (_this.APP_OBJ[app_id].is_deployment) { _this.func.UI.utils.indicator.server.busy(); setTimeout(function () { _this.func.UI.utils.indicator.server.normal(); }, e?.data?.length * 100 || 100); } var data = e.data; if (e.source === 'http_call') { if (e.service === 'get_doc_obj_from_build') { return $('body').trigger('get_doc_obj_from_build_response_' + data._id, { data, }); } if (e.service === 'heartbeat') { return $('body').trigger('heartbeat_response', { data, }); } return $('body').trigger('get_ws_data_response_' + e.websocket_queue_num, { data, e, }); } if (e.source === 'deployment_server') { console.log('document_changed', e); return func.UI.screen.refresh_document_changes_for_realtime_update(SESSION_ID, e.data); } if (data !== 'connected') return; RUNTIME_SERVER_WEBSOCKET_CONNECTED = true; resolve(); }); var callback_done = false; RUNTIME_SERVER_WEBSOCKET.on('connect_error', (error) => { if (!callback_done) { resolve(); callback_done = true; error = true; } }); RUNTIME_SERVER_WEBSOCKET.on('disconnect', async () => { RUNTIME_SERVER_WEBSOCKET_CONNECTED = false; if (_data_system) { set_connected(0); } if (_session.opt.enable_offline) { $(_session.root_element).addClass('runtime_offline'); func.utils.alerts.toast(SESSION_ID, 'Switched to off-line mode', 'You have lost connection to the server and are now working offline. Once the connection is restored, all data will be synchronized.', 'warning'); } else { // await func.index.delete_pouch(); // window.location.href = `https://${_session.domain}/error?error_code=408`; _this.func.UI.utils.progressScreen.show(SESSION_ID, 'Your browser has temporarily disconnected from the server. Please wait while we attempt to reconnect.'); } }); window.addEventListener('beforeunload', function (event) { var obj = { service: 'close_websocket', data: { session_id: SESSION_ID }, }; RUNTIME_SERVER_WEBSOCKET.emit('message', obj); }); }); }; export const run_plugins_runtime_init = async function (SESSION_ID, app_id, method) { var _session = _this.SESSION_OBJ[SESSION_ID]; const get_path = function (plugin_name, resource) { if (_session.worker_type === 'Dev') { return `../../plugins/${_session.domain}/${plugin_name}/${resource}`; } return `https://${_session.domain}/plugins/${plugin_name}/runtime/${resource}?gtp_token=${_session.gtp_token}&app_id=${_session.app_id}`; }; if (!_this.APP_OBJ[app_id].app_plugins_purchased) return; for await (const [key, val] of Object.entries(_this.APP_OBJ[app_id].app_plugins_purchased)) { if (!val.installed) continue; if (val.manifest?.[method].mjs?.exist && !val.manifest[method].mjs.is_empty) { const module = await import(get_path(key, method + '.mjs')); try { await module.default({ SESSION_ID: SESSION_ID }); } catch (error) { console.error(error); } } } };