@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
JavaScript
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);
}
}
}
};