From 3764473b5b8f4e9458c7b5ea1fdd984da7515217 Mon Sep 17 00:00:00 2001
From: Naiel <109038805+naielv@users.noreply.github.com>
Date: Wed, 25 Feb 2026 12:45:38 +0000
Subject: [PATCH] =?UTF-8?q?feat:=20A=C3=B1adir=20funcionalidad=20de=20gest?=
=?UTF-8?q?i=C3=B3n=20de=20apps=20en=20la=20tienda=20de=20apps?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/app_logic.js | 18 +-
src/app_modules.js | 536 +++++++++++++++++++++++++++++++++++++++-
src/index.html | 1 +
src/page/tienda_apps.js | 176 +++++++++++++
4 files changed, 729 insertions(+), 2 deletions(-)
create mode 100644 src/page/tienda_apps.js
diff --git a/src/app_logic.js b/src/app_logic.js
index 22d0eee..a97d717 100644
--- a/src/app_logic.js
+++ b/src/app_logic.js
@@ -8,7 +8,7 @@ function tableScroll(query) {
//var secretTokenEl = document.getElementById("secretToken");
var container = document.getElementById('container');
-function open_page(params) {
+function open_page(params, retryAfterExternalLoad = false) {
// Clear stored event listeners and timers
EventListeners.GunJS = [];
EventListeners.Timeout.forEach((ev) => clearTimeout(ev));
@@ -31,6 +31,22 @@ function open_page(params) {
}
var path = params.split(',');
var app = path[0];
+ if (!PAGES[app]) {
+ if (!retryAfterExternalLoad && typeof TS_loadExternalAppsFromDB === 'function') {
+ TS_loadExternalAppsFromDB().then(() => {
+ open_page(params, true);
+ });
+ return;
+ }
+ toastr.error('La app solicitada no existe.');
+ setUrlHash('index');
+ return;
+ }
+ if (typeof TS_isAppInstalled === 'function' && !TS_isAppInstalled(app)) {
+ toastr.error('Esta app no está instalada. Instálala desde la Tienda de apps.');
+ setUrlHash('tienda_apps');
+ return;
+ }
if (path[1] == undefined) {
PAGES[app].index();
return;
diff --git a/src/app_modules.js b/src/app_modules.js
index ccc543d..680ef20 100644
--- a/src/app_modules.js
+++ b/src/app_modules.js
@@ -1725,6 +1725,531 @@ var PAGES = {};
var PERMS = {
ADMIN: 'Administrador',
};
+
+var TS_INSTALLED_APPS_CACHE = null;
+var TS_INSTALLED_APPS_CACHE_KEY = '';
+var TS_INSTALLED_APPS_LOADING = false;
+var TS_ESSENTIAL_APPS = new Set(['index', 'personas', 'dataman']);
+var TS_LOCKED_APPS = new Set(['index', 'personas', 'dataman']);
+
+function TS_getAppInstallStorageKey() {
+ var dbName = 'telesec';
+ try {
+ dbName = typeof getDBName === 'function' ? getDBName() : 'telesec';
+ } catch (e) {
+ dbName = 'telesec';
+ }
+ var personaId = SUB_LOGGED_IN_ID || 'anon';
+ return 'TELESEC_INSTALLED_APPS::' + dbName + '::' + personaId;
+}
+
+function TS_getAppInstallDocId() {
+ var dbName = 'telesec';
+ try {
+ dbName = typeof getDBName === 'function' ? getDBName() : 'telesec';
+ } catch (e) {
+ dbName = 'telesec';
+ }
+ var personaId = SUB_LOGGED_IN_ID || 'anon';
+ return 'installed_apps::' + dbName + '::' + personaId;
+}
+
+function TS_buildDefaultInstalledSet() {
+ var installedSet = new Set();
+ TS_ESSENTIAL_APPS.forEach((key) => {
+ if (!PAGES[key]) return;
+ if (TS_isSystemApp(key)) return;
+ if (PAGES[key].ExternalApp === true) return;
+ if (PAGES[key].Esconder === true) return;
+ installedSet.add(key);
+ });
+ TS_LOCKED_APPS.forEach((key) => {
+ if (PAGES[key] && PAGES[key].Esconder !== true) {
+ installedSet.add(key);
+ }
+ });
+ return installedSet;
+}
+
+function TS_parseInstalledAppsPayload(payload) {
+ if (!payload || !Array.isArray(payload.apps)) return null;
+ var set = new Set();
+ payload.apps.forEach((appKey) => {
+ if (typeof appKey === 'string' && appKey.trim() !== '') {
+ set.add(appKey);
+ }
+ });
+ return set;
+}
+
+function TS_readInstalledAppsFallback() {
+ try {
+ var raw = localStorage.getItem(TS_getAppInstallStorageKey());
+ if (!raw) return null;
+ var parsed = JSON.parse(raw);
+ return TS_parseInstalledAppsPayload(parsed);
+ } catch (e) {
+ return null;
+ }
+}
+
+function TS_writeInstalledAppsFallback(appSet) {
+ var apps = [];
+ if (appSet && typeof appSet.forEach === 'function') {
+ appSet.forEach((appKey) => {
+ if (typeof appKey === 'string' && appKey.trim() !== '') {
+ apps.push(appKey);
+ }
+ });
+ }
+ localStorage.setItem(
+ TS_getAppInstallStorageKey(),
+ JSON.stringify({ version: 1, apps: apps.sort(), updatedAt: CurrentISOTime() })
+ );
+}
+
+function TS_setInstalledAppsCache(appSet) {
+ TS_LOCKED_APPS.forEach((key) => {
+ if (PAGES[key] && PAGES[key].Esconder !== true) {
+ appSet.add(key);
+ }
+ });
+ TS_INSTALLED_APPS_CACHE = appSet;
+ TS_INSTALLED_APPS_CACHE_KEY = TS_getAppInstallDocId();
+ TS_writeInstalledAppsFallback(appSet);
+}
+
+function TS_persistInstalledAppsToDB(appSet) {
+ if (!window.DB || typeof DB.put !== 'function') {
+ return Promise.resolve(false);
+ }
+ var apps = [];
+ appSet.forEach((appKey) => {
+ if (typeof appKey === 'string' && appKey.trim() !== '') {
+ apps.push(appKey);
+ }
+ });
+ return DB.put('config', TS_getAppInstallDocId(), {
+ version: 1,
+ apps: apps.sort(),
+ updatedAt: CurrentISOTime(),
+ })
+ .then(() => true)
+ .catch((e) => {
+ console.warn('Error saving installed apps in DB', e);
+ return false;
+ });
+}
+
+function TS_loadInstalledAppsFromDB(forceReload = false) {
+ var expectedKey = TS_getAppInstallDocId();
+ if (!forceReload && TS_INSTALLED_APPS_CACHE && TS_INSTALLED_APPS_CACHE_KEY === expectedKey) {
+ return Promise.resolve(TS_INSTALLED_APPS_CACHE);
+ }
+ if (TS_INSTALLED_APPS_LOADING) {
+ return Promise.resolve(TS_INSTALLED_APPS_CACHE);
+ }
+ TS_INSTALLED_APPS_LOADING = true;
+
+ var fallbackSet = TS_readInstalledAppsFallback() || TS_buildDefaultInstalledSet();
+ TS_setInstalledAppsCache(fallbackSet);
+
+ if (!window.DB || typeof DB.get !== 'function') {
+ TS_INSTALLED_APPS_LOADING = false;
+ return Promise.resolve(TS_INSTALLED_APPS_CACHE);
+ }
+
+ return DB.get('config', expectedKey)
+ .then((raw) => {
+ if (raw == null) {
+ TS_INSTALLED_APPS_LOADING = false;
+ return TS_persistInstalledAppsToDB(TS_INSTALLED_APPS_CACHE).then(() => TS_INSTALLED_APPS_CACHE);
+ }
+
+ return new Promise((resolve) => {
+ if (typeof raw === 'string') {
+ TS_decrypt(
+ raw,
+ SECRET,
+ (decrypted) => {
+ var parsed = TS_parseInstalledAppsPayload(decrypted);
+ if (parsed) {
+ TS_setInstalledAppsCache(parsed);
+ }
+ TS_INSTALLED_APPS_LOADING = false;
+ resolve(TS_INSTALLED_APPS_CACHE);
+ },
+ 'config',
+ expectedKey
+ );
+ } else {
+ var parsed = TS_parseInstalledAppsPayload(raw);
+ if (parsed) {
+ TS_setInstalledAppsCache(parsed);
+ }
+ TS_INSTALLED_APPS_LOADING = false;
+ resolve(TS_INSTALLED_APPS_CACHE);
+ }
+ });
+ })
+ .catch((e) => {
+ console.warn('Error loading installed apps from DB', e);
+ TS_INSTALLED_APPS_LOADING = false;
+ return TS_INSTALLED_APPS_CACHE;
+ });
+}
+
+function TS_getInstalledAppsSet() {
+ var expectedKey = TS_getAppInstallDocId();
+ if (!TS_INSTALLED_APPS_CACHE || TS_INSTALLED_APPS_CACHE_KEY !== expectedKey) {
+ TS_loadInstalledAppsFromDB().then(() => {
+ SetPages();
+ });
+ return null;
+ }
+ return TS_INSTALLED_APPS_CACHE;
+}
+
+function TS_setInstalledAppsSet(appSet) {
+ TS_setInstalledAppsCache(appSet);
+ TS_persistInstalledAppsToDB(appSet);
+}
+
+function TS_isSystemApp(appKey) {
+ if (appKey === 'login') return true;
+ if (!PAGES[appKey]) return false;
+ return PAGES[appKey].SystemApp === true;
+}
+
+function TS_isLockedApp(appKey) {
+ return TS_LOCKED_APPS.has(appKey);
+}
+
+function TS_isMandatoryApp(appKey) {
+ return TS_isSystemApp(appKey) || TS_isLockedApp(appKey);
+}
+
+function TS_isAppInstalled(appKey) {
+ if (TS_isMandatoryApp(appKey)) return true;
+ var installedSet = TS_getInstalledAppsSet();
+ if (installedSet == null) {
+ return true;
+ }
+ return installedSet.has(appKey);
+}
+
+function TS_installApp(appKey) {
+ if (!PAGES[appKey] || TS_isSystemApp(appKey)) return;
+ var installedSet = TS_getInstalledAppsSet();
+ if (installedSet == null) {
+ installedSet = TS_buildDefaultInstalledSet();
+ }
+ installedSet.add(appKey);
+ if (appKey === 'supercafe' && PAGES.pagos) {
+ installedSet.add('pagos');
+ }
+ TS_setInstalledAppsSet(installedSet);
+}
+
+function TS_uninstallApp(appKey) {
+ if (!PAGES[appKey] || TS_isMandatoryApp(appKey)) return;
+ var installedSet = TS_getInstalledAppsSet();
+ if (installedSet == null) {
+ installedSet = TS_buildDefaultInstalledSet();
+ }
+ installedSet.delete(appKey);
+ TS_setInstalledAppsSet(installedSet);
+}
+
+function TS_resetInstalledApps() {
+ var defaults = TS_buildDefaultInstalledSet();
+ TS_setInstalledAppsSet(defaults);
+}
+
+function TS_getAppCatalog() {
+ return Object.keys(PAGES)
+ .filter((key) => PAGES[key].Esconder !== true)
+ .map((key) => {
+ return {
+ key: key,
+ title: PAGES[key].Title || key,
+ icon: PAGES[key].icon || 'static/appico/application_enterprise.png',
+ installed: TS_isAppInstalled(key),
+ system: TS_isSystemApp(key),
+ canAccess: !PAGES[key].AccessControl || checkRole(key),
+ requiresRole: PAGES[key].AccessControl === true,
+ };
+ })
+ .sort((a, b) => a.title.localeCompare(b.title, 'es'));
+}
+
+var TS_EXTERNAL_APPS_CACHE = [];
+var TS_EXTERNAL_APPS_CACHE_KEY = '';
+var TS_EXTERNAL_APPS_LOADING = false;
+var TS_EXTERNAL_APPS_READY = false;
+
+function TS_getExternalAppsStorageKey() {
+ var dbName = 'telesec';
+ try {
+ dbName = typeof getDBName === 'function' ? getDBName() : 'telesec';
+ } catch (e) {
+ dbName = 'telesec';
+ }
+ var personaId = SUB_LOGGED_IN_ID || 'anon';
+ return 'TELESEC_EXTERNAL_APPS::' + dbName + '::' + personaId;
+}
+
+function TS_getExternalAppsDocId() {
+ var dbName = 'telesec';
+ try {
+ dbName = typeof getDBName === 'function' ? getDBName() : 'telesec';
+ } catch (e) {
+ dbName = 'telesec';
+ }
+ var personaId = SUB_LOGGED_IN_ID || 'anon';
+ return 'external_apps::' + dbName + '::' + personaId;
+}
+
+function TS_parseExternalAppsPayload(payload) {
+ if (!payload || !Array.isArray(payload.apps)) return [];
+ return payload.apps.filter((entry) => {
+ return (
+ entry &&
+ typeof entry.appKey === 'string' &&
+ entry.appKey.trim() !== '' &&
+ typeof entry.code === 'string' &&
+ entry.code.trim() !== ''
+ );
+ });
+}
+
+function TS_readExternalAppsFallback() {
+ try {
+ var raw = localStorage.getItem(TS_getExternalAppsStorageKey());
+ if (!raw) return [];
+ return TS_parseExternalAppsPayload(JSON.parse(raw));
+ } catch (e) {
+ return [];
+ }
+}
+
+function TS_writeExternalAppsFallback(appsList) {
+ localStorage.setItem(
+ TS_getExternalAppsStorageKey(),
+ JSON.stringify({
+ version: 1,
+ apps: appsList,
+ updatedAt: CurrentISOTime(),
+ })
+ );
+}
+
+function TS_setExternalAppsCache(appsList) {
+ TS_EXTERNAL_APPS_CACHE = Array.isArray(appsList) ? appsList : [];
+ TS_EXTERNAL_APPS_CACHE_KEY = TS_getExternalAppsDocId();
+ TS_EXTERNAL_APPS_READY = true;
+ TS_writeExternalAppsFallback(TS_EXTERNAL_APPS_CACHE);
+}
+
+function TS_saveExternalAppsToDB(appsList) {
+ if (!window.DB || typeof DB.put !== 'function') {
+ return Promise.resolve(false);
+ }
+ return DB.put('config', TS_getExternalAppsDocId(), {
+ version: 1,
+ apps: appsList,
+ updatedAt: CurrentISOTime(),
+ })
+ .then(() => true)
+ .catch((e) => {
+ console.warn('Error saving external apps in DB', e);
+ return false;
+ });
+}
+
+function TS_evalExternalAppCode(code, expectedAppKey = '') {
+ if (typeof code !== 'string' || code.trim() === '') {
+ throw new Error('Código vacío en app externa.');
+ }
+
+ var beforeKeys = Object.keys(PAGES);
+ var beforeSet = new Set(beforeKeys);
+ try {
+ new Function(code)();
+ } catch (e) {
+ throw new Error('Error ejecutando app externa: ' + (e && e.message ? e.message : e));
+ }
+
+ var afterKeys = Object.keys(PAGES);
+ var newKeys = afterKeys.filter((key) => !beforeSet.has(key));
+ var appKey = expectedAppKey || '';
+
+ if (!appKey) {
+ if (newKeys.length === 0) {
+ throw new Error('La app externa no registró ninguna entrada en PAGES.');
+ }
+ appKey = newKeys[0];
+ }
+
+ if (!PAGES[appKey]) {
+ throw new Error('No se pudo detectar la app externa cargada.');
+ }
+
+ if (beforeSet.has(appKey) && PAGES[appKey].ExternalApp !== true) {
+ throw new Error('La app externa intenta sobrescribir una app interna: ' + appKey);
+ }
+
+ PAGES[appKey].ExternalApp = true;
+ PAGES[appKey].SystemApp = false;
+
+ return {
+ appKey: appKey,
+ title: PAGES[appKey].Title || appKey,
+ icon: PAGES[appKey].icon || 'static/appico/application_enterprise.png',
+ };
+}
+
+function TS_applyExternalAppsRegistry(appsList) {
+ appsList.forEach((entry) => {
+ try {
+ if (entry.appKey && PAGES[entry.appKey] && PAGES[entry.appKey].ExternalApp === true) {
+ return;
+ }
+ TS_evalExternalAppCode(entry.code, entry.appKey || '');
+ } catch (e) {
+ console.warn('Error loading external app', entry && entry.appKey, e);
+ }
+ });
+}
+
+function TS_loadExternalAppsFromDB(forceReload = false) {
+ var expectedKey = TS_getExternalAppsDocId();
+ if (!forceReload && TS_EXTERNAL_APPS_READY && TS_EXTERNAL_APPS_CACHE_KEY === expectedKey) {
+ return Promise.resolve(TS_EXTERNAL_APPS_CACHE);
+ }
+ if (TS_EXTERNAL_APPS_LOADING) {
+ return Promise.resolve(TS_EXTERNAL_APPS_CACHE);
+ }
+ TS_EXTERNAL_APPS_LOADING = true;
+
+ var fallback = TS_readExternalAppsFallback();
+ TS_setExternalAppsCache(fallback);
+ TS_applyExternalAppsRegistry(TS_EXTERNAL_APPS_CACHE);
+
+ if (!window.DB || typeof DB.get !== 'function') {
+ TS_EXTERNAL_APPS_LOADING = false;
+ return Promise.resolve(TS_EXTERNAL_APPS_CACHE);
+ }
+
+ return DB.get('config', expectedKey)
+ .then((raw) => {
+ if (raw == null) {
+ TS_EXTERNAL_APPS_LOADING = false;
+ return TS_saveExternalAppsToDB(TS_EXTERNAL_APPS_CACHE).then(() => TS_EXTERNAL_APPS_CACHE);
+ }
+
+ return new Promise((resolve) => {
+ if (typeof raw === 'string') {
+ TS_decrypt(
+ raw,
+ SECRET,
+ (decrypted) => {
+ var parsed = TS_parseExternalAppsPayload(decrypted);
+ TS_setExternalAppsCache(parsed);
+ TS_applyExternalAppsRegistry(TS_EXTERNAL_APPS_CACHE);
+ TS_EXTERNAL_APPS_LOADING = false;
+ resolve(TS_EXTERNAL_APPS_CACHE);
+ },
+ 'config',
+ expectedKey
+ );
+ } else {
+ var parsed = TS_parseExternalAppsPayload(raw);
+ TS_setExternalAppsCache(parsed);
+ TS_applyExternalAppsRegistry(TS_EXTERNAL_APPS_CACHE);
+ TS_EXTERNAL_APPS_LOADING = false;
+ resolve(TS_EXTERNAL_APPS_CACHE);
+ }
+ });
+ })
+ .catch((e) => {
+ console.warn('Error loading external apps from DB', e);
+ TS_EXTERNAL_APPS_LOADING = false;
+ return TS_EXTERNAL_APPS_CACHE;
+ });
+}
+
+function TS_getExternalAppsCatalog() {
+ var expectedKey = TS_getExternalAppsDocId();
+ if (!TS_EXTERNAL_APPS_READY || TS_EXTERNAL_APPS_CACHE_KEY !== expectedKey) {
+ TS_loadExternalAppsFromDB().then(() => {
+ SetPages();
+ });
+ return [];
+ }
+ return TS_EXTERNAL_APPS_CACHE.map((entry) => {
+ return {
+ appKey: entry.appKey,
+ title: entry.title || entry.appKey,
+ sourceType: entry.sourceType || 'file',
+ source: entry.source || '',
+ installedAt: entry.installedAt || '',
+ installed: TS_isAppInstalled(entry.appKey),
+ };
+ });
+}
+
+function TS_installExternalAppFromCode(code, sourceType = 'file', sourceRef = '') {
+ return TS_loadExternalAppsFromDB().then(() => {
+ var info = TS_evalExternalAppCode(code, '');
+ var next = TS_EXTERNAL_APPS_CACHE.filter((entry) => entry.appKey !== info.appKey);
+ var item = {
+ id: safeuuid('extapp-'),
+ appKey: info.appKey,
+ title: info.title,
+ icon: info.icon,
+ sourceType: sourceType,
+ source: sourceRef,
+ code: code,
+ installedAt: CurrentISOTime(),
+ updatedAt: CurrentISOTime(),
+ };
+ next.push(item);
+ TS_setExternalAppsCache(next);
+ TS_saveExternalAppsToDB(next);
+ TS_installApp(info.appKey);
+ return item;
+ });
+}
+
+function TS_uninstallExternalApp(appKey) {
+ return TS_loadExternalAppsFromDB().then(() => {
+ var next = TS_EXTERNAL_APPS_CACHE.filter((entry) => entry.appKey !== appKey);
+ TS_setExternalAppsCache(next);
+ TS_saveExternalAppsToDB(next);
+ TS_uninstallApp(appKey);
+ if (PAGES[appKey] && PAGES[appKey].ExternalApp === true) {
+ delete PAGES[appKey];
+ }
+ return true;
+ });
+}
+
+function TS_resetAppsToDefault() {
+ return TS_loadExternalAppsFromDB().then(() => {
+ TS_EXTERNAL_APPS_CACHE.forEach((entry) => {
+ var appKey = entry.appKey;
+ if (PAGES[appKey] && PAGES[appKey].ExternalApp === true) {
+ delete PAGES[appKey];
+ }
+ });
+ TS_setExternalAppsCache([]);
+ TS_saveExternalAppsToDB([]);
+ TS_resetInstalledApps();
+ return true;
+ });
+}
+
function checkRole(role) {
var roles = SUB_LOGGED_IN_DETAILS.Roles || '';
var rolesArr = roles.split(',');
@@ -1735,15 +2260,24 @@ function checkRole(role) {
}
}
function SetPages() {
+ var expectedExternalKey = TS_getExternalAppsDocId();
+ if (!TS_EXTERNAL_APPS_READY || TS_EXTERNAL_APPS_CACHE_KEY !== expectedExternalKey) {
+ TS_loadExternalAppsFromDB().then(() => {
+ SetPages();
+ });
+ }
document.getElementById('appendApps2').innerHTML = '';
Object.keys(PAGES).forEach((key) => {
if (PAGES[key].Esconder == true) {
return;
}
+ if (!TS_isAppInstalled(key)) {
+ return;
+ }
if (PAGES[key].AccessControl == true) {
var roles = SUB_LOGGED_IN_DETAILS.Roles || '';
var rolesArr = roles.split(',');
- if (rolesArr.includes('ADMIN') || rolesArr.includes(key) || AC_BYPASS) {
+ if (rolesArr.includes('ADMIN') || rolesArr.includes(PAGES[key].AccessControlRole || key) || AC_BYPASS) {
} else {
return;
}
diff --git a/src/index.html b/src/index.html
index eac39b6..5df1051 100644
--- a/src/index.html
+++ b/src/index.html
@@ -94,6 +94,7 @@
+