finished
This commit is contained in:
175
decrypt.js
Normal file
175
decrypt.js
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
function TS_decrypt(input, secret, callback, table, id) {
|
||||||
|
// Accept objects or plaintext strings. Also support legacy RSA{...} AES-encrypted entries.
|
||||||
|
var __ts_sync = true;
|
||||||
|
if (typeof input !== "string") {
|
||||||
|
try {
|
||||||
|
callback(input, false);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
return __ts_sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy encrypted format: RSA{...}
|
||||||
|
if (
|
||||||
|
input.startsWith("RSA{") &&
|
||||||
|
input.endsWith("}") &&
|
||||||
|
typeof CryptoJS !== "undefined"
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
var data = input.slice(4, -1);
|
||||||
|
var words = CryptoJS.AES.decrypt(data, secret);
|
||||||
|
var decryptedUtf8 = null;
|
||||||
|
try {
|
||||||
|
decryptedUtf8 = words.toString(CryptoJS.enc.Utf8);
|
||||||
|
} catch (utfErr) {
|
||||||
|
// Malformed UTF-8 — try Latin1 fallback
|
||||||
|
try {
|
||||||
|
decryptedUtf8 = words.toString(CryptoJS.enc.Latin1);
|
||||||
|
} catch (latinErr) {
|
||||||
|
console.warn(
|
||||||
|
"TS_decrypt: failed to decode decrypted bytes",
|
||||||
|
utfErr,
|
||||||
|
latinErr
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
callback(input, false);
|
||||||
|
} catch (ee) {}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var parsed = null;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(decryptedUtf8);
|
||||||
|
} catch (pe) {
|
||||||
|
// If JSON parsing fails, the decrypted string may be raw Latin1 bytes.
|
||||||
|
// Try to convert Latin1 byte string -> UTF-8 and parse again.
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
typeof TextDecoder !== "undefined" &&
|
||||||
|
typeof decryptedUtf8 === "string"
|
||||||
|
) {
|
||||||
|
var bytes = new Uint8Array(decryptedUtf8.length);
|
||||||
|
for (var _i = 0; _i < decryptedUtf8.length; _i++)
|
||||||
|
bytes[_i] = decryptedUtf8.charCodeAt(_i) & 0xff;
|
||||||
|
var converted = new TextDecoder("utf-8").decode(bytes);
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(converted);
|
||||||
|
decryptedUtf8 = converted;
|
||||||
|
} catch (e2) {
|
||||||
|
parsed = decryptedUtf8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parsed = decryptedUtf8;
|
||||||
|
}
|
||||||
|
} catch (convErr) {
|
||||||
|
parsed = decryptedUtf8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
callback(parsed, true);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
// Migrate to plaintext in DB if table/id provided
|
||||||
|
if (table && id && window.DB && DB.put && typeof parsed !== "string") {
|
||||||
|
DB.put(table, id, parsed).catch(() => {});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("TS_decrypt: invalid encrypted payload", e);
|
||||||
|
try {
|
||||||
|
callback(input, false);
|
||||||
|
} catch (ee) {}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (input.startsWith("SEA{") && input.endsWith("}")) {
|
||||||
|
__ts_sync = false;
|
||||||
|
SEA.decrypt(input, secret, (decrypted) => {
|
||||||
|
try {
|
||||||
|
callback(decrypted, true);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return __ts_sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse JSON strings and migrate to object
|
||||||
|
try {
|
||||||
|
var parsed = JSON.parse(input);
|
||||||
|
try {
|
||||||
|
callback(parsed, false);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
if (table && id && window.DB && DB.put) {
|
||||||
|
DB.put(table, id, parsed).catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Not JSON, return raw string
|
||||||
|
try {
|
||||||
|
callback(input, false);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return __ts_sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
function recursiveTSDecrypt(input, secret = "") {
|
||||||
|
// Skip null values (do not show on decrypted output)
|
||||||
|
if (input === null) return null;
|
||||||
|
if (typeof input === "string") {
|
||||||
|
let result = null;
|
||||||
|
let resolver = null;
|
||||||
|
const promise = new Promise((resolve) => {
|
||||||
|
resolver = resolve;
|
||||||
|
});
|
||||||
|
const sync = TS_decrypt(input, secret, (decrypted) => {
|
||||||
|
result = decrypted;
|
||||||
|
if (resolver) resolver(decrypted);
|
||||||
|
});
|
||||||
|
if (sync === false) return promise;
|
||||||
|
return result;
|
||||||
|
} else if (Array.isArray(input)) {
|
||||||
|
const mapped = input.map((item) => recursiveTSDecrypt(item, secret));
|
||||||
|
if (mapped.some((v) => v && typeof v.then === "function")) {
|
||||||
|
return Promise.all(mapped).then((values) => values.filter((v) => v !== null && typeof v !== 'undefined'));
|
||||||
|
}
|
||||||
|
return mapped.filter((v) => v !== null && typeof v !== 'undefined');
|
||||||
|
} else if (typeof input === "object" && input !== null) {
|
||||||
|
const keys = Object.keys(input);
|
||||||
|
const mapped = keys.map((k) => recursiveTSDecrypt(input[k], secret));
|
||||||
|
if (mapped.some((v) => v && typeof v.then === "function")) {
|
||||||
|
return Promise.all(mapped).then((values) => {
|
||||||
|
const out = {};
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
const val = values[i];
|
||||||
|
if (val !== null && typeof val !== 'undefined') out[keys[i]] = val;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const out = {};
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
const val = mapped[i];
|
||||||
|
if (val !== null && typeof val !== 'undefined') out[keys[i]] = val;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gun.get(TABLE).load((DATA) => {
|
||||||
|
var plain2 = recursiveTSDecrypt(DATA, SECRET);
|
||||||
|
plain2.then(function (result) {
|
||||||
|
download(
|
||||||
|
`Export TeleSec ${GROUPID} Decrypted.json.txt`,
|
||||||
|
JSON.stringify(result)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -9,10 +9,12 @@ function tableScroll(query) {
|
|||||||
var container = document.getElementById("container");
|
var container = document.getElementById("container");
|
||||||
|
|
||||||
function LinkAccount(LinkAccount_secret, refresh = false) {
|
function LinkAccount(LinkAccount_secret, refresh = false) {
|
||||||
// Store group id for backward compatibility and keep secret
|
// Group identifier (no encryption). Secret usage removed.
|
||||||
localStorage.setItem("TELESEC_AUTO", "YES");
|
if (LinkAccount_secret) {
|
||||||
SECRET = LinkAccount_secret.toUpperCase();
|
SECRET = String(LinkAccount_secret).toUpperCase();
|
||||||
localStorage.setItem("TELESEC_secret", SECRET);
|
} else {
|
||||||
|
SECRET = "";
|
||||||
|
}
|
||||||
|
|
||||||
if (refresh == true) {
|
if (refresh == true) {
|
||||||
location.reload();
|
location.reload();
|
||||||
@@ -30,17 +32,6 @@ function LinkAccount(LinkAccount_secret, refresh = false) {
|
|||||||
console.warn('DB.init failed or not available yet', e);
|
console.warn('DB.init failed or not available yet', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (localStorage.getItem("TELESEC_AUTO") == "YES") {
|
|
||||||
LinkAccount(
|
|
||||||
localStorage.getItem("TELESEC_secret")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (urlParams.get("login") != null) {
|
|
||||||
LinkAccount(
|
|
||||||
urlParams.get("enc_password"),
|
|
||||||
);
|
|
||||||
//location.search = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function open_page(params) {
|
function open_page(params) {
|
||||||
// Clear stored event listeners and timers
|
// Clear stored event listeners and timers
|
||||||
@@ -153,11 +144,11 @@ function fixGunLocalStorage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Heartbeat: store a small "last seen" doc locally and replicate to remote when available
|
// Heartbeat: store a small "last seen" doc locally and replicate to remote when available
|
||||||
setInterval(() => {
|
// setInterval(() => {
|
||||||
if (typeof DB !== 'undefined') {
|
// if (typeof DB !== 'undefined') {
|
||||||
DB.put('heartbeat', getDBName() || 'heartbeat', 'heartbeat-' + CurrentISOTime());
|
// DB.put('heartbeat', getDBName() || 'heartbeat', 'heartbeat-' + CurrentISOTime());
|
||||||
}
|
// }
|
||||||
}, 5000);
|
// }, 5000);
|
||||||
|
|
||||||
|
|
||||||
function betterSorter(a, b) {
|
function betterSorter(a, b) {
|
||||||
|
|||||||
@@ -313,6 +313,15 @@ function addCategory_Personas(
|
|||||||
btn.append(br2);
|
btn.append(br2);
|
||||||
var img = document.createElement("img");
|
var img = document.createElement("img");
|
||||||
img.src = value.Foto || "static/ico/user_generic.png";
|
img.src = value.Foto || "static/ico/user_generic.png";
|
||||||
|
// Prefer attachment 'foto' for this persona
|
||||||
|
try {
|
||||||
|
const personaKey = key;
|
||||||
|
if (personaKey) {
|
||||||
|
DB.getAttachment('personas', personaKey, 'foto').then((durl) => {
|
||||||
|
if (durl) img.src = durl;
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
img.style.height = "60px";
|
img.style.height = "60px";
|
||||||
img.style.padding = "5px";
|
img.style.padding = "5px";
|
||||||
img.style.backgroundColor = "white";
|
img.style.backgroundColor = "white";
|
||||||
@@ -330,7 +339,16 @@ function addCategory_Personas(
|
|||||||
defaultval = key;
|
defaultval = key;
|
||||||
span_0.innerText = "";
|
span_0.innerText = "";
|
||||||
var img_5 = document.createElement("img");
|
var img_5 = document.createElement("img");
|
||||||
img_5.src = value.Foto;
|
img_5.src = value.Foto || "static/ico/user_generic.png";
|
||||||
|
// Prefer attachment 'foto' when available
|
||||||
|
try {
|
||||||
|
const personaKey2 = key;
|
||||||
|
if (personaKey2) {
|
||||||
|
DB.getAttachment('personas', personaKey2, 'foto').then((durl) => {
|
||||||
|
if (durl) img_5.src = durl;
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
img_5.style.height = "30px";
|
img_5.style.height = "30px";
|
||||||
span_0.append(img_5, value.Nombre);
|
span_0.append(img_5, value.Nombre);
|
||||||
change_cb(defaultval);
|
change_cb(defaultval);
|
||||||
@@ -537,29 +555,62 @@ const SC_actions = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
// Listado precargado de personas:
|
// Listado precargado de personas:
|
||||||
function TS_decrypt(input, secret, callback) {
|
function TS_decrypt(input, secret, callback, table, id) {
|
||||||
// Only support AES-based encrypted payloads in the format: RSA{<ciphertext>} or plain objects/strings
|
// Accept objects or plaintext strings. Also support legacy RSA{...} AES-encrypted entries.
|
||||||
if (typeof input != "string") {
|
if (typeof input !== "string") {
|
||||||
callback(input);
|
try { callback(input, false); } catch (e) { console.error(e); }
|
||||||
} else if (input.startsWith("RSA{") && input.endsWith("}")) {
|
return;
|
||||||
var data = input.slice(4, -1);
|
|
||||||
var decrypted = CryptoJS.AES.decrypt(data, secret).toString(CryptoJS.enc.Utf8);
|
|
||||||
try {
|
|
||||||
callback(JSON.parse(decrypted));
|
|
||||||
} catch (e) {
|
|
||||||
console.error('TS_decrypt: invalid JSON', e);
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// plain string (settings, etc.)
|
|
||||||
callback(input);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Legacy encrypted format: RSA{...}
|
||||||
|
if (input.startsWith("RSA{") && input.endsWith("}") && typeof CryptoJS !== 'undefined') {
|
||||||
|
try {
|
||||||
|
var data = input.slice(4, -1);
|
||||||
|
var words = CryptoJS.AES.decrypt(data, secret);
|
||||||
|
var decryptedUtf8 = null;
|
||||||
|
try {
|
||||||
|
decryptedUtf8 = words.toString(CryptoJS.enc.Utf8);
|
||||||
|
} catch (utfErr) {
|
||||||
|
// Malformed UTF-8 — try Latin1 fallback
|
||||||
|
try {
|
||||||
|
decryptedUtf8 = words.toString(CryptoJS.enc.Latin1);
|
||||||
|
} catch (latinErr) {
|
||||||
|
console.warn('TS_decrypt: failed to decode decrypted bytes', utfErr, latinErr);
|
||||||
|
try { callback(input, false); } catch (ee) { }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var parsed = null;
|
||||||
|
try { parsed = JSON.parse(decryptedUtf8); } catch (pe) { parsed = decryptedUtf8; }
|
||||||
|
try { callback(parsed, true); } catch (e) { console.error(e); }
|
||||||
|
// Migrate to plaintext in DB if table/id provided
|
||||||
|
if (table && id && window.DB && DB.put && typeof parsed !== 'string') {
|
||||||
|
DB.put(table, id, parsed).catch(() => {});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('TS_decrypt: invalid encrypted payload', e);
|
||||||
|
try { callback(input, false); } catch (ee) { }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse JSON strings and migrate to object
|
||||||
|
try {
|
||||||
|
var parsed = JSON.parse(input);
|
||||||
|
try { callback(parsed, false); } catch (e) { console.error(e); }
|
||||||
|
if (table && id && window.DB && DB.put) {
|
||||||
|
DB.put(table, id, parsed).catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Not JSON, return raw string
|
||||||
|
try { callback(input, false); } catch (err) { console.error(err); }
|
||||||
|
}
|
||||||
|
}
|
||||||
function TS_encrypt(input, secret, callback, mode = "RSA") {
|
function TS_encrypt(input, secret, callback, mode = "RSA") {
|
||||||
// Use AES symmetric encryption (RSA{} envelope for backwards compatibility)
|
// Encryption removed: store plaintext objects directly
|
||||||
var encrypted = CryptoJS.AES.encrypt(JSON.stringify(input), secret).toString();
|
try { callback(input); } catch (e) { console.error(e); }
|
||||||
callback("RSA{" + encrypted + "}");
|
}
|
||||||
}
|
|
||||||
// Populate SC_Personas from DB (PouchDB)
|
// Populate SC_Personas from DB (PouchDB)
|
||||||
DB.map('personas', (data, key) => {
|
DB.map('personas', (data, key) => {
|
||||||
function add_row(data, key) {
|
function add_row(data, key) {
|
||||||
@@ -571,9 +622,9 @@ DB.map('personas', (data, key) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
add_row(data, key);
|
add_row(data, key);
|
||||||
});
|
}, 'personas', key);
|
||||||
} else {
|
} else {
|
||||||
add_row(data, key);
|
add_row(data, key);
|
||||||
}
|
}
|
||||||
@@ -903,21 +954,19 @@ function TS_IndexElement(
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
data.Estado = state;
|
data.Estado = state;
|
||||||
TS_encrypt(data, SECRET, (encrypted) => {
|
if (typeof ref === 'string') {
|
||||||
if (typeof ref === 'string') {
|
DB.put(ref, data._key, data).then(() => {
|
||||||
DB.put(ref, data._key, encrypted).then(() => {
|
toastr.success("Guardado!");
|
||||||
toastr.success("Guardado!");
|
}).catch((e) => { console.warn('DB.put error', e); });
|
||||||
});
|
} else {
|
||||||
} else {
|
try {
|
||||||
try {
|
// legacy
|
||||||
// legacy
|
ref.get(data._key).put(data);
|
||||||
ref.get(data._key).put(encrypted);
|
toastr.success("Guardado!");
|
||||||
toastr.success("Guardado!");
|
} catch (e) {
|
||||||
} catch (e) {
|
console.warn('Could not save item', e);
|
||||||
console.warn('Could not save item', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
return button;
|
return button;
|
||||||
@@ -992,6 +1041,17 @@ function TS_IndexElement(
|
|||||||
infoSpan.style.color = "black";
|
infoSpan.style.color = "black";
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
img.src = persona.Foto || "static/ico/user_generic.png";
|
img.src = persona.Foto || "static/ico/user_generic.png";
|
||||||
|
// Prefer attachment 'foto' stored in PouchDB if available
|
||||||
|
try {
|
||||||
|
const personaId = key.self === true ? (data._key || data._id || data.id) : data[key.key];
|
||||||
|
if (personaId) {
|
||||||
|
DB.getAttachment('personas', personaId, 'foto').then((durl) => {
|
||||||
|
if (durl) img.src = durl;
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
img.height = 70;
|
img.height = 70;
|
||||||
infoSpan.appendChild(img);
|
infoSpan.appendChild(img);
|
||||||
infoSpan.appendChild(document.createElement("br"));
|
infoSpan.appendChild(document.createElement("br"));
|
||||||
@@ -1033,9 +1093,9 @@ function TS_IndexElement(
|
|||||||
debounce(debounce_load, render, 300, [rows]);
|
debounce(debounce_load, render, 300, [rows]);
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
add_row(data, key);
|
add_row(data, key);
|
||||||
});
|
}, ref, key);
|
||||||
} else {
|
} else {
|
||||||
add_row(data, key);
|
add_row(data, key);
|
||||||
}
|
}
|
||||||
@@ -1054,9 +1114,9 @@ function TS_IndexElement(
|
|||||||
debounce(debounce_load, render, 300, [rows]);
|
debounce(debounce_load, render, 300, [rows]);
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
add_row(data, key);
|
add_row(data, key);
|
||||||
});
|
}, undefined, undefined);
|
||||||
} else {
|
} else {
|
||||||
add_row(data, key);
|
add_row(data, key);
|
||||||
}
|
}
|
||||||
@@ -1127,7 +1187,7 @@ function SetPages() {
|
|||||||
document.getElementById("appendApps2").append(a);
|
document.getElementById("appendApps2").append(a);
|
||||||
}
|
}
|
||||||
var Booted = false;
|
var Booted = false;
|
||||||
var TimeoutBoot = 6;
|
var TimeoutBoot = 3; // in loops of 750ms
|
||||||
var BootLoops = 0;
|
var BootLoops = 0;
|
||||||
|
|
||||||
function getPeers() {
|
function getPeers() {
|
||||||
@@ -1220,12 +1280,10 @@ var BootIntervalID = setInterval(() => {
|
|||||||
}
|
}
|
||||||
if (!data) {
|
if (!data) {
|
||||||
const persona = { Nombre: 'Admin (bypass)', Roles: 'ADMIN,' };
|
const persona = { Nombre: 'Admin (bypass)', Roles: 'ADMIN,' };
|
||||||
TS_encrypt(persona, SECRET, (encrypted) => {
|
DB.put('personas', bypassId, persona).then(() => finish(persona, bypassId)).catch((e) => { console.warn('AC_BYPASS create error', e); open_page('login'); });
|
||||||
DB.put('personas', bypassId, encrypted).then(() => finish(persona, bypassId)).catch((e) => { console.warn('AC_BYPASS create error', e); open_page('login'); });
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
if (typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
TS_decrypt(data, SECRET, (pdata) => finish(pdata, bypassId));
|
TS_decrypt(data, SECRET, (pdata) => finish(pdata, bypassId), 'personas', bypassId);
|
||||||
} else {
|
} else {
|
||||||
finish(data, bypassId);
|
finish(data, bypassId);
|
||||||
}
|
}
|
||||||
|
|||||||
92
src/db.js
92
src/db.js
@@ -10,6 +10,20 @@ var DB = (function () {
|
|||||||
let changes = null;
|
let changes = null;
|
||||||
let callbacks = {}; // table -> [cb]
|
let callbacks = {}; // table -> [cb]
|
||||||
|
|
||||||
|
function ensureLocal() {
|
||||||
|
if (local) return;
|
||||||
|
try {
|
||||||
|
const localName = localStorage.getItem('TELESEC_COUCH_DBNAME') || 'telesec';
|
||||||
|
local = new PouchDB(localName);
|
||||||
|
if (changes) {
|
||||||
|
try { changes.cancel(); } catch (e) {}
|
||||||
|
changes = local.changes({ live: true, since: 'now', include_docs: true }).on('change', onChange);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('ensureLocal error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function makeId(table, id) {
|
function makeId(table, id) {
|
||||||
return table + ':' + id;
|
return table + ':' + id;
|
||||||
}
|
}
|
||||||
@@ -47,6 +61,7 @@ var DB = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function replicateToRemote() {
|
function replicateToRemote() {
|
||||||
|
ensureLocal();
|
||||||
if (!local || !remote) return;
|
if (!local || !remote) return;
|
||||||
PouchDB.replicate(local, remote, { live: true, retry: true }).on('error', function (err) {
|
PouchDB.replicate(local, remote, { live: true, retry: true }).on('error', function (err) {
|
||||||
console.warn('Replication error', err);
|
console.warn('Replication error', err);
|
||||||
@@ -67,6 +82,7 @@ var DB = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function put(table, id, data) {
|
async function put(table, id, data) {
|
||||||
|
ensureLocal();
|
||||||
const _id = makeId(table, id);
|
const _id = makeId(table, id);
|
||||||
try {
|
try {
|
||||||
const existing = await local.get(_id).catch(() => null);
|
const existing = await local.get(_id).catch(() => null);
|
||||||
@@ -89,6 +105,7 @@ var DB = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function get(table, id) {
|
async function get(table, id) {
|
||||||
|
ensureLocal();
|
||||||
const _id = makeId(table, id);
|
const _id = makeId(table, id);
|
||||||
try {
|
try {
|
||||||
const doc = await local.get(_id);
|
const doc = await local.get(_id);
|
||||||
@@ -103,6 +120,7 @@ var DB = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function list(table) {
|
async function list(table) {
|
||||||
|
ensureLocal();
|
||||||
try {
|
try {
|
||||||
const res = await local.allDocs({ include_docs: true, startkey: table + ':', endkey: table + ':\uffff' });
|
const res = await local.allDocs({ include_docs: true, startkey: table + ':', endkey: table + ':\uffff' });
|
||||||
return res.rows.map(r => {
|
return res.rows.map(r => {
|
||||||
@@ -112,7 +130,62 @@ var DB = (function () {
|
|||||||
} catch (e) { return []; }
|
} catch (e) { return []; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert data URL to Blob
|
||||||
|
function dataURLtoBlob(dataurl) {
|
||||||
|
const arr = dataurl.split(',');
|
||||||
|
const mime = arr[0].match(/:(.*?);/)[1];
|
||||||
|
const bstr = atob(arr[1]);
|
||||||
|
let n = bstr.length;
|
||||||
|
const u8arr = new Uint8Array(n);
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
|
}
|
||||||
|
return new Blob([u8arr], { type: mime });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function putAttachment(table, id, name, dataUrlOrBlob, contentType) {
|
||||||
|
ensureLocal();
|
||||||
|
const _id = makeId(table, id);
|
||||||
|
try {
|
||||||
|
let doc = await local.get(_id).catch(() => null);
|
||||||
|
if (!doc) {
|
||||||
|
// create a minimal doc so attachments can be put
|
||||||
|
await local.put({ _id: _id, table: table, ts: new Date().toISOString(), data: {} });
|
||||||
|
doc = await local.get(_id);
|
||||||
|
}
|
||||||
|
let blob = dataUrlOrBlob;
|
||||||
|
if (typeof dataUrlOrBlob === 'string' && dataUrlOrBlob.indexOf('data:') === 0) {
|
||||||
|
blob = dataURLtoBlob(dataUrlOrBlob);
|
||||||
|
}
|
||||||
|
const type = contentType || (blob && blob.type) || 'application/octet-stream';
|
||||||
|
await local.putAttachment(_id, name, doc._rev, blob, type);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('putAttachment error', e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAttachment(table, id, name) {
|
||||||
|
ensureLocal();
|
||||||
|
const _id = makeId(table, id);
|
||||||
|
try {
|
||||||
|
const blob = await local.getAttachment(_id, name);
|
||||||
|
if (!blob) return null;
|
||||||
|
// convert blob to data URL
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function (e) { resolve(e.target.result); };
|
||||||
|
reader.onerror = function (e) { reject(e); };
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function map(table, cb) {
|
function map(table, cb) {
|
||||||
|
ensureLocal();
|
||||||
callbacks[table] = callbacks[table] || [];
|
callbacks[table] = callbacks[table] || [];
|
||||||
callbacks[table].push(cb);
|
callbacks[table].push(cb);
|
||||||
// initial load
|
// initial load
|
||||||
@@ -131,8 +204,27 @@ var DB = (function () {
|
|||||||
list,
|
list,
|
||||||
map,
|
map,
|
||||||
replicateToRemote,
|
replicateToRemote,
|
||||||
|
putAttachment,
|
||||||
|
getAttachment,
|
||||||
_internal: { local }
|
_internal: { local }
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
window.DB = DB;
|
window.DB = DB;
|
||||||
|
|
||||||
|
// Auto-initialize DB on startup using saved settings (non-blocking)
|
||||||
|
(function autoInitDB() {
|
||||||
|
try {
|
||||||
|
const remoteServer = localStorage.getItem('TELESEC_COUCH_URL') || '';
|
||||||
|
const username = localStorage.getItem('TELESEC_COUCH_USER') || '';
|
||||||
|
const password = localStorage.getItem('TELESEC_COUCH_PASS') || '';
|
||||||
|
const dbname = localStorage.getItem('TELESEC_COUCH_DBNAME') || undefined;
|
||||||
|
const secret = localStorage.getItem('TELESEC_secret') || '';
|
||||||
|
// Call init but don't await; DB functions are safe-guarded with ensureLocal()
|
||||||
|
DB.init({ secret, remoteServer, username, password, dbname }).catch((e) => {
|
||||||
|
console.warn('DB.autoInit error', e);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('DB.autoInit unexpected error', e);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ PAGES.aulas = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
});
|
}, 'comedor', CurrentISODate());
|
||||||
} else {
|
} else {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
}
|
}
|
||||||
@@ -93,9 +93,9 @@ PAGES.aulas = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
});
|
}, 'notas', 'tareas');
|
||||||
} else {
|
} else {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
}
|
}
|
||||||
@@ -113,9 +113,9 @@ PAGES.aulas = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
});
|
}, 'aulas_informes', 'diario-' + CurrentISODate());
|
||||||
} else {
|
} else {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
}
|
}
|
||||||
@@ -191,9 +191,9 @@ PAGES.aulas = {
|
|||||||
document.getElementById(field_autor).value = data["Solicitante"] || SUB_LOGGED_IN_ID || "";
|
document.getElementById(field_autor).value = data["Solicitante"] || SUB_LOGGED_IN_ID || "";
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
load_data(data, "%E");
|
load_data(data, "%E");
|
||||||
});
|
}, 'aulas_solicitudes', mid);
|
||||||
} else {
|
} else {
|
||||||
load_data(data || {});
|
load_data(data || {});
|
||||||
}
|
}
|
||||||
@@ -204,16 +204,14 @@ PAGES.aulas = {
|
|||||||
Contenido: document.getElementById(field_contenido).value,
|
Contenido: document.getElementById(field_contenido).value,
|
||||||
Asunto: document.getElementById(field_asunto).value,
|
Asunto: document.getElementById(field_asunto).value,
|
||||||
};
|
};
|
||||||
var enc = TS_encrypt(data, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('aulas_solicitudes', mid, data).then(() => {
|
||||||
DB.put('aulas_solicitudes', mid, encrypted).then(() => {
|
toastr.success("Guardado!");
|
||||||
toastr.success("Guardado!");
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
setUrlHash("aulas,solicitudes");
|
||||||
setUrlHash("aulas,solicitudes");
|
}, SAVE_WAIT);
|
||||||
}, SAVE_WAIT);
|
}).catch((e) => { console.warn('DB.put error', e); });
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
document.getElementById(btn_borrar).onclick = () => {
|
document.getElementById(btn_borrar).onclick = () => {
|
||||||
if (confirm("¿Quieres borrar esta solicitud?") == true) {
|
if (confirm("¿Quieres borrar esta solicitud?") == true) {
|
||||||
@@ -338,16 +336,14 @@ PAGES.aulas = {
|
|||||||
Asunto: document.getElementById(field_asunto).value,
|
Asunto: document.getElementById(field_asunto).value,
|
||||||
Fecha: document.getElementById(field_fecha).value || CurrentISODate(),
|
Fecha: document.getElementById(field_fecha).value || CurrentISODate(),
|
||||||
};
|
};
|
||||||
var enc = TS_encrypt(data, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('aulas_informes', mid, data).then(() => {
|
||||||
DB.put('aulas_informes', mid, encrypted).then(() => {
|
toastr.success("Guardado!");
|
||||||
toastr.success("Guardado!");
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
setUrlHash("aulas,informes");
|
||||||
setUrlHash("aulas,informes");
|
}, SAVE_WAIT);
|
||||||
}, SAVE_WAIT);
|
}).catch((e) => { console.warn('DB.put error', e); });
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
document.getElementById(btn_borrar).onclick = () => {
|
document.getElementById(btn_borrar).onclick = () => {
|
||||||
if (confirm("¿Quieres borrar este informe?") == true) {
|
if (confirm("¿Quieres borrar este informe?") == true) {
|
||||||
|
|||||||
@@ -114,9 +114,9 @@ PAGES.avisos = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
load_data(data, "%E");
|
load_data(data, "%E");
|
||||||
});
|
}, 'notificaciones', mid);
|
||||||
} else {
|
} else {
|
||||||
load_data(data || {});
|
load_data(data || {});
|
||||||
}
|
}
|
||||||
@@ -141,16 +141,14 @@ PAGES.avisos = {
|
|||||||
.getElementById(field_estado)
|
.getElementById(field_estado)
|
||||||
.value.replace("%%", "por_leer"),
|
.value.replace("%%", "por_leer"),
|
||||||
};
|
};
|
||||||
var enc = TS_encrypt(data, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('notificaciones', mid, data).then(() => {
|
||||||
DB.put('notificaciones', mid, encrypted).then(() => {
|
toastr.success("Guardado!");
|
||||||
toastr.success("Guardado!");
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
setUrlHash("avisos");
|
||||||
setUrlHash("avisos");
|
}, SAVE_WAIT);
|
||||||
}, SAVE_WAIT);
|
}).catch((e) => { console.warn('DB.put error', e); });
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
document.getElementById(btn_borrar).onclick = () => {
|
document.getElementById(btn_borrar).onclick = () => {
|
||||||
if (confirm("¿Quieres borrar esta notificación?") == true) {
|
if (confirm("¿Quieres borrar esta notificación?") == true) {
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ PAGES.comedor = {
|
|||||||
data["Platos"] || "";
|
data["Platos"] || "";
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
load_data(data, "%E");
|
load_data(data, "%E");
|
||||||
});
|
}, 'comedor', mid);
|
||||||
} else {
|
} else {
|
||||||
load_data(data || {});
|
load_data(data || {});
|
||||||
}
|
}
|
||||||
@@ -55,16 +55,14 @@ PAGES.comedor = {
|
|||||||
DB.del('comedor', mid);
|
DB.del('comedor', mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
var enc = TS_encrypt(data, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('comedor', newDate, data).then(() => {
|
||||||
DB.put('comedor', newDate, encrypted).then(() => {
|
toastr.success("Guardado!");
|
||||||
toastr.success("Guardado!");
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
setUrlHash("comedor");
|
||||||
setUrlHash("comedor");
|
}, SAVE_WAIT);
|
||||||
}, SAVE_WAIT);
|
}).catch((e) => { console.warn('DB.put error', e); });
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
document.getElementById(btn_borrar).onclick = () => {
|
document.getElementById(btn_borrar).onclick = () => {
|
||||||
if (confirm("¿Quieres borrar esta entrada?") == true) {
|
if (confirm("¿Quieres borrar esta entrada?") == true) {
|
||||||
|
|||||||
@@ -75,9 +75,9 @@ PAGES.dataman = {
|
|||||||
const value = entry.data;
|
const value = entry.data;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (typeof value == 'string') {
|
if (typeof value == 'string') {
|
||||||
TS_decrypt(value, SECRET, (data) => {
|
TS_decrypt(value, SECRET, (data, wasEncrypted) => {
|
||||||
output.materiales[key] = data;
|
output.materiales[key] = data;
|
||||||
});
|
}, 'materiales', key);
|
||||||
} else {
|
} else {
|
||||||
output.materiales[key] = value;
|
output.materiales[key] = value;
|
||||||
}
|
}
|
||||||
@@ -89,9 +89,9 @@ PAGES.dataman = {
|
|||||||
const value = entry.data;
|
const value = entry.data;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (typeof value == 'string') {
|
if (typeof value == 'string') {
|
||||||
TS_decrypt(value, SECRET, (data) => {
|
TS_decrypt(value, SECRET, (data, wasEncrypted) => {
|
||||||
output.personas[key] = data;
|
output.personas[key] = data;
|
||||||
});
|
}, 'personas', key);
|
||||||
} else {
|
} else {
|
||||||
output.personas[key] = value;
|
output.personas[key] = value;
|
||||||
}
|
}
|
||||||
@@ -168,12 +168,18 @@ PAGES.dataman = {
|
|||||||
var sel = document.getElementById(select_type).value;
|
var sel = document.getElementById(select_type).value;
|
||||||
if (sel == "%telesec") {
|
if (sel == "%telesec") {
|
||||||
// legacy import, store entire payload as-is
|
// legacy import, store entire payload as-is
|
||||||
DB.put('%telesec', 'export_' + Date.now(), JSON.parse(val));
|
// for each top-level key, store their items in DB
|
||||||
|
var parsed = JSON.parse(val);
|
||||||
|
Object.entries(parsed).forEach((section) => {
|
||||||
|
const sectionName = section[0];
|
||||||
|
const sectionData = section[1];
|
||||||
|
Object.entries(sectionData).forEach((entry) => {
|
||||||
|
DB.put(sectionName, entry[0], entry[1]).catch((e) => { console.warn('DB.put error', e); });
|
||||||
|
});
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
Object.entries(JSON.parse(val)["data"]).forEach((entry) => {
|
Object.entries(JSON.parse(val)["data"]).forEach((entry) => {
|
||||||
var enc = TS_encrypt(entry[1], SECRET, (encrypted) => {
|
DB.put(sel, entry[0], entry[1]).catch((e) => { console.warn('DB.put error', e); });
|
||||||
DB.put(sel, entry[0], encrypted);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -101,10 +101,9 @@ PAGES.login = {
|
|||||||
document.getElementById(btn_bypass_create).onclick = () => {
|
document.getElementById(btn_bypass_create).onclick = () => {
|
||||||
var name = prompt("Nombre de la persona (ej: Admin):");
|
var name = prompt("Nombre de la persona (ej: Admin):");
|
||||||
if (!name) return;
|
if (!name) return;
|
||||||
var id = 'bypass-' + Date.now();
|
var id = 'bypass-' + Date.now();
|
||||||
var persona = { Nombre: name, Roles: 'ADMIN,' };
|
var persona = { Nombre: name, Roles: 'ADMIN,' };
|
||||||
TS_encrypt(persona, SECRET, (encrypted) => {
|
DB.put('personas', id, persona).then(() => {
|
||||||
DB.put('personas', id, encrypted).then(() => {
|
|
||||||
toastr.success('Persona creada: ' + id);
|
toastr.success('Persona creada: ' + id);
|
||||||
localStorage.setItem('TELESEC_BYPASS_ID', id);
|
localStorage.setItem('TELESEC_BYPASS_ID', id);
|
||||||
SUB_LOGGED_IN_ID = id;
|
SUB_LOGGED_IN_ID = id;
|
||||||
@@ -115,7 +114,6 @@ PAGES.login = {
|
|||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
toastr.error('Error creando persona: ' + (e && e.message ? e.message : e));
|
toastr.error('Error creando persona: ' + (e && e.message ? e.message : e));
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,9 +74,9 @@ PAGES.materiales = {
|
|||||||
document.getElementById(field_notas).value = data["Notas"] || "";
|
document.getElementById(field_notas).value = data["Notas"] || "";
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
load_data(data, "%E");
|
load_data(data, "%E");
|
||||||
});
|
}, 'materiales', mid);
|
||||||
} else {
|
} else {
|
||||||
load_data(data || {});
|
load_data(data || {});
|
||||||
}
|
}
|
||||||
@@ -91,16 +91,14 @@ PAGES.materiales = {
|
|||||||
Revision: document.getElementById(field_revision).value,
|
Revision: document.getElementById(field_revision).value,
|
||||||
Notas: document.getElementById(field_notas).value,
|
Notas: document.getElementById(field_notas).value,
|
||||||
};
|
};
|
||||||
var enc = TS_encrypt(data, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('materiales', mid, data).then(() => {
|
||||||
DB.put('materiales', mid, encrypted).then(() => {
|
toastr.success("Guardado!");
|
||||||
toastr.success("Guardado!");
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
setUrlHash("materiales");
|
||||||
setUrlHash("materiales");
|
}, SAVE_WAIT);
|
||||||
}, SAVE_WAIT);
|
}).catch((e) => { console.warn('DB.put error', e); });
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
document.getElementById(btn_borrar).onclick = () => {
|
document.getElementById(btn_borrar).onclick = () => {
|
||||||
if (confirm("¿Quieres borrar este material?") == true) {
|
if (confirm("¿Quieres borrar este material?") == true) {
|
||||||
@@ -184,11 +182,11 @@ PAGES.materiales = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof data === "string") {
|
if (typeof data === "string") {
|
||||||
TS_decrypt(data, SECRET, (dec) => {
|
TS_decrypt(data, SECRET, (dec, wasEncrypted) => {
|
||||||
if (dec && typeof dec === "object") {
|
if (dec && typeof dec === "object") {
|
||||||
addUbicacion(dec);
|
addUbicacion(dec);
|
||||||
}
|
}
|
||||||
});
|
}, 'materiales', key);
|
||||||
} else {
|
} else {
|
||||||
addUbicacion(data);
|
addUbicacion(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,16 +79,14 @@ PAGES.notas = {
|
|||||||
Contenido: document.getElementById(field_contenido).value,
|
Contenido: document.getElementById(field_contenido).value,
|
||||||
Asunto: document.getElementById(field_asunto).value,
|
Asunto: document.getElementById(field_asunto).value,
|
||||||
};
|
};
|
||||||
var enc = TS_encrypt(data, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('notas', mid, data).then(() => {
|
||||||
DB.put('notas', mid, encrypted).then(() => {
|
toastr.success("Guardado!");
|
||||||
toastr.success("Guardado!");
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
setUrlHash("notas");
|
||||||
setUrlHash("notas");
|
}, SAVE_WAIT);
|
||||||
}, SAVE_WAIT);
|
}).catch((e) => { console.warn('DB.put error', e); });
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
document.getElementById(btn_borrar).onclick = () => {
|
document.getElementById(btn_borrar).onclick = () => {
|
||||||
if (confirm("¿Quieres borrar esta nota?") == true) {
|
if (confirm("¿Quieres borrar esta nota?") == true) {
|
||||||
|
|||||||
@@ -476,37 +476,32 @@ PAGES.pagos = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
persona.Monedero_Balance = fixfloat(newBalance);
|
persona.Monedero_Balance = fixfloat(newBalance);
|
||||||
|
DB.put('personas', personaId, persona).then(() => {
|
||||||
TS_encrypt(persona, SECRET, (encrypted) => {
|
if (callback) callback();
|
||||||
DB.put('personas', personaId, encrypted).then(() => {
|
}).catch((e) => { console.warn('DB.put error', e); if (callback) callback(); });
|
||||||
if (callback) callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveTransaction(ticketId, data) {
|
function saveTransaction(ticketId, data) {
|
||||||
TS_encrypt(data, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('pagos', ticketId, data).then(() => {
|
||||||
DB.put('pagos', ticketId, encrypted).then(() => {
|
// If this is from SuperCafé, update the order
|
||||||
// If this is from SuperCafé, update the order
|
if (data.Origen === "SuperCafé" && data.OrigenID) {
|
||||||
if (data.Origen === "SuperCafé" && data.OrigenID) {
|
handleSuperCafePayment(data);
|
||||||
handleSuperCafePayment(data);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check for promotional bonus on Ingreso transactions (Efectivo only)
|
// Check for promotional bonus on Ingreso transactions (Efectivo only)
|
||||||
if (data.Tipo === "Ingreso" && data.Metodo === "Efectivo") {
|
if (data.Tipo === "Ingreso" && data.Metodo === "Efectivo") {
|
||||||
var bonusAmount = calculatePromoBonus(data.Monto);
|
var bonusAmount = calculatePromoBonus(data.Monto);
|
||||||
if (bonusAmount > 0) {
|
if (bonusAmount > 0) {
|
||||||
createPromoBonusTransaction(data.Persona, bonusAmount, data.Monto);
|
createPromoBonusTransaction(data.Persona, bonusAmount, data.Monto);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toastr.success("¡Transacción completada!");
|
toastr.success("¡Transacción completada!");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
setUrlHash("pagos," + ticketId);
|
setUrlHash("pagos," + ticketId);
|
||||||
}, SAVE_WAIT);
|
}, SAVE_WAIT);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,16 +550,11 @@ PAGES.pagos = {
|
|||||||
var currentBalance = parseFloat(persona.Monedero_Balance || 0);
|
var currentBalance = parseFloat(persona.Monedero_Balance || 0);
|
||||||
var newBalance = currentBalance + bonusAmount;
|
var newBalance = currentBalance + bonusAmount;
|
||||||
persona.Monedero_Balance = fixfloat(newBalance);
|
persona.Monedero_Balance = fixfloat(newBalance);
|
||||||
|
DB.put('personas', personaId, persona).catch((e) => { console.warn('DB.put error', e); });
|
||||||
TS_encrypt(persona, SECRET, (encrypted) => {
|
|
||||||
DB.put('personas', personaId, encrypted);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save bonus transaction
|
// Save bonus transaction
|
||||||
TS_encrypt(bonusData, SECRET, (encrypted) => {
|
DB.put('pagos', bonusTicketId, bonusData).catch((e) => { console.warn('DB.put error', e); });
|
||||||
DB.put('pagos', bonusTicketId, encrypted);
|
|
||||||
});
|
|
||||||
|
|
||||||
toastr.success(
|
toastr.success(
|
||||||
"🎉 ¡Promo Bono aplicado! +" + bonusAmount.toFixed(2) + "€ extra"
|
"🎉 ¡Promo Bono aplicado! +" + bonusAmount.toFixed(2) + "€ extra"
|
||||||
@@ -580,9 +570,7 @@ PAGES.pagos = {
|
|||||||
var persona = SC_Personas[transactionData.Persona];
|
var persona = SC_Personas[transactionData.Persona];
|
||||||
if (!persona) return;
|
if (!persona) return;
|
||||||
|
|
||||||
TS_encrypt(persona, SECRET, (encrypted) => {
|
DB.put('personas', transactionData.Persona, persona).catch((e) => { console.warn('DB.put error', e); });
|
||||||
DB.put('personas', transactionData.Persona, encrypted);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-fill if data provided
|
// Pre-fill if data provided
|
||||||
@@ -847,12 +835,9 @@ PAGES.pagos = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
persona.Monedero_Balance = fixfloat(newBalance);
|
persona.Monedero_Balance = fixfloat(newBalance);
|
||||||
|
DB.put('personas', personaId, persona).then(() => {
|
||||||
TS_encrypt(persona, SECRET, (encrypted) => {
|
if (callback) callback();
|
||||||
DB.put('personas', personaId, encrypted).then(() => {
|
}).catch((e) => { console.warn('DB.put error', e); if (callback) callback(); });
|
||||||
if (callback) callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteTransaction(transactionKey) {
|
function deleteTransaction(transactionKey) {
|
||||||
@@ -1383,16 +1368,14 @@ PAGES.pagos = {
|
|||||||
delete updatedData.PersonaDestino;
|
delete updatedData.PersonaDestino;
|
||||||
}
|
}
|
||||||
|
|
||||||
TS_encrypt(updatedData, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('pagos', transactionId, updatedData).then(() => {
|
||||||
DB.put('pagos', transactionId, encrypted).then(() => {
|
toastr.success("¡Transacción actualizada!");
|
||||||
toastr.success("¡Transacción actualizada!");
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
setUrlHash("pagos," + transactionId);
|
||||||
setUrlHash("pagos," + transactionId);
|
}, SAVE_WAIT);
|
||||||
}, SAVE_WAIT);
|
}).catch((e) => { console.warn('DB.put error', e); });
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cancel button
|
// Cancel button
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ PAGES.personas = {
|
|||||||
<button type="button" id="${btn_ver_monedero}" class="btn5">Ver Transacciones del Monedero</button>
|
<button type="button" id="${btn_ver_monedero}" class="btn5">Ver Transacciones del Monedero</button>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
<details style="background: #e3fde3ff; border: 2px solid #21f328ff; border-radius: 8px; padding: 10px; margin: 15px 0;">
|
<details style="background: #e3fde3ff; border: 2px solid #21f328ff; border-radius: 8px; padding: 10px; margin: 15px 0; display: none;">
|
||||||
<summary style="cursor: pointer; font-weight: bold; color: rgba(26, 141, 3, 1);">🔗 Generar enlaces</summary>
|
<summary style="cursor: pointer; font-weight: bold; color: rgba(26, 141, 3, 1);">🔗 Generar enlaces</summary>
|
||||||
<div style="padding: 15px;">
|
<div style="padding: 15px;">
|
||||||
<label>
|
<label>
|
||||||
@@ -110,9 +110,16 @@ PAGES.personas = {
|
|||||||
document.getElementById(field_nombre).value = data["Nombre"] || "";
|
document.getElementById(field_nombre).value = data["Nombre"] || "";
|
||||||
document.getElementById(field_zona).value = data["Region"] || "";
|
document.getElementById(field_zona).value = data["Region"] || "";
|
||||||
document.getElementById(field_anilla).value = data["SC_Anilla"] || "";
|
document.getElementById(field_anilla).value = data["SC_Anilla"] || "";
|
||||||
document.getElementById(render_foto).src =
|
// set fallback image immediately
|
||||||
data["Foto"] || "static/ico/user_generic.png";
|
document.getElementById(render_foto).src = data["Foto"] || "static/ico/user_generic.png";
|
||||||
resized = data["Foto"] || "static/ico/user_generic.png";
|
resized = data["Foto"] || "static/ico/user_generic.png";
|
||||||
|
// try to load attachment 'foto' if present (preferred storage)
|
||||||
|
DB.getAttachment('personas', mid, 'foto').then((durl) => {
|
||||||
|
if (durl) {
|
||||||
|
document.getElementById(render_foto).src = durl;
|
||||||
|
resized = durl;
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
document.getElementById(field_notas).value = data["markdown"] || "";
|
document.getElementById(field_notas).value = data["markdown"] || "";
|
||||||
document.getElementById(field_monedero_balance).value =
|
document.getElementById(field_monedero_balance).value =
|
||||||
data["Monedero_Balance"] || 0;
|
data["Monedero_Balance"] || 0;
|
||||||
@@ -120,29 +127,25 @@ PAGES.personas = {
|
|||||||
data["Monedero_Notas"] || "";
|
data["Monedero_Notas"] || "";
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
load_data(data, "%E");
|
load_data(data, "%E");
|
||||||
});
|
}, 'personas', mid);
|
||||||
} else {
|
} else {
|
||||||
load_data(data || {});
|
load_data(data || {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
document
|
document.getElementById(field_foto).addEventListener("change", function (e) {
|
||||||
.getElementById(field_foto)
|
const file = e.target.files[0];
|
||||||
.addEventListener("change", function (e) {
|
if (!file) return;
|
||||||
const file = e.target.files[0];
|
// Do NOT resize — keep original uploaded image
|
||||||
if (!file) return;
|
const reader = new FileReader();
|
||||||
|
reader.onload = function (ev) {
|
||||||
resizeInputImage(
|
const url = ev.target.result;
|
||||||
file,
|
document.getElementById(render_foto).src = url;
|
||||||
function (url) {
|
resized = url;
|
||||||
document.getElementById(render_foto).src = url;
|
};
|
||||||
resized = url;
|
reader.readAsDataURL(file);
|
||||||
},
|
});
|
||||||
256,
|
|
||||||
0.7
|
|
||||||
);
|
|
||||||
});
|
|
||||||
document.getElementById(btn_guardar).onclick = () => {
|
document.getElementById(btn_guardar).onclick = () => {
|
||||||
var dt = new FormData(pdel);
|
var dt = new FormData(pdel);
|
||||||
var data = {
|
var data = {
|
||||||
@@ -150,23 +153,28 @@ PAGES.personas = {
|
|||||||
Region: document.getElementById(field_zona).value,
|
Region: document.getElementById(field_zona).value,
|
||||||
Roles: dt.getAll("perm").join(",") + ",",
|
Roles: dt.getAll("perm").join(",") + ",",
|
||||||
SC_Anilla: document.getElementById(field_anilla).value,
|
SC_Anilla: document.getElementById(field_anilla).value,
|
||||||
Foto: resized,
|
// Foto moved to PouchDB attachment named 'foto'
|
||||||
markdown: document.getElementById(field_notas).value,
|
markdown: document.getElementById(field_notas).value,
|
||||||
Monedero_Balance:
|
Monedero_Balance:
|
||||||
parseFloat(document.getElementById(field_monedero_balance).value) ||
|
parseFloat(document.getElementById(field_monedero_balance).value) ||
|
||||||
0,
|
0,
|
||||||
Monedero_Notas: document.getElementById(field_monedero_notas).value,
|
Monedero_Notas: document.getElementById(field_monedero_notas).value,
|
||||||
};
|
};
|
||||||
var enc = TS_encrypt(data, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('personas', mid, data).then(() => {
|
||||||
DB.put('personas', mid, encrypted).then(() => {
|
// if resized is a data URL (new/updated image), save as attachment
|
||||||
|
var attachPromise = Promise.resolve(true);
|
||||||
|
if (typeof resized === 'string' && resized.indexOf('data:') === 0) {
|
||||||
|
attachPromise = DB.putAttachment('personas', mid, 'foto', resized, 'image/png');
|
||||||
|
}
|
||||||
|
attachPromise.then(() => {
|
||||||
toastr.success("Guardado!");
|
toastr.success("Guardado!");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
setUrlHash("personas");
|
setUrlHash("personas");
|
||||||
}, SAVE_WAIT);
|
}, SAVE_WAIT);
|
||||||
});
|
}).catch((e) => { console.warn('putAttachment error', e); document.getElementById("actionStatus").style.display = "none"; });
|
||||||
});
|
}).catch((e) => { console.warn('DB.put error', e); document.getElementById("actionStatus").style.display = "none"; });
|
||||||
};
|
};
|
||||||
document.getElementById(btn_ver_monedero).onclick = () => {
|
document.getElementById(btn_ver_monedero).onclick = () => {
|
||||||
setUrlHash("pagos"); // Navigate to pagos and show transactions for this person
|
setUrlHash("pagos"); // Navigate to pagos and show transactions for this person
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ PAGES.resumen_diario = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
});
|
}, 'comedor', CurrentISODate());
|
||||||
} else {
|
} else {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
}
|
}
|
||||||
@@ -71,9 +71,9 @@ PAGES.resumen_diario = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
});
|
}, 'notas', 'tareas');
|
||||||
} else {
|
} else {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
}
|
}
|
||||||
@@ -91,9 +91,9 @@ PAGES.resumen_diario = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
});
|
}, 'aulas_informes', 'diario-' + CurrentISODate());
|
||||||
} else {
|
} else {
|
||||||
add_row(data || {});
|
add_row(data || {});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,9 +91,9 @@ PAGES.supercafe = {
|
|||||||
loadActions();
|
loadActions();
|
||||||
}
|
}
|
||||||
if (typeof data == "string") {
|
if (typeof data == "string") {
|
||||||
TS_decrypt(data, SECRET, (data) => {
|
TS_decrypt(data, SECRET, (data, wasEncrypted) => {
|
||||||
load_data(data, "%E");
|
load_data(data, "%E");
|
||||||
});
|
}, 'supercafe', mid);
|
||||||
} else {
|
} else {
|
||||||
load_data(data || {});
|
load_data(data || {});
|
||||||
}
|
}
|
||||||
@@ -112,16 +112,14 @@ PAGES.supercafe = {
|
|||||||
.getElementById(field_estado)
|
.getElementById(field_estado)
|
||||||
.value.replace("%%", "Pedido"),
|
.value.replace("%%", "Pedido"),
|
||||||
};
|
};
|
||||||
var enc = TS_encrypt(data, SECRET, (encrypted) => {
|
document.getElementById("actionStatus").style.display = "block";
|
||||||
document.getElementById("actionStatus").style.display = "block";
|
DB.put('supercafe', mid, data).then(() => {
|
||||||
DB.put('supercafe', mid, encrypted).then(() => {
|
toastr.success("Guardado!");
|
||||||
toastr.success("Guardado!");
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
document.getElementById("actionStatus").style.display = "none";
|
||||||
document.getElementById("actionStatus").style.display = "none";
|
setUrlHash("supercafe");
|
||||||
setUrlHash("supercafe");
|
}, SAVE_WAIT);
|
||||||
}, SAVE_WAIT);
|
}).catch((e) => { console.warn('DB.put error', e); });
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
document.getElementById(btn_borrar).onclick = () => {
|
document.getElementById(btn_borrar).onclick = () => {
|
||||||
if (
|
if (
|
||||||
|
|||||||
Reference in New Issue
Block a user