From 68b9860855f2af5fd685d514645c3e15bc03dd14 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Tue, 2 Jun 2026 20:27:43 +0700 Subject: [PATCH] first commit --- .gitignore | 4 ++ README.md | 10 ++++ __init__.py | 2 + __manifest__.py | 26 +++++++++ static/src/workers/patch_logger.js | 84 ++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 __init__.py create mode 100644 __manifest__.py create mode 100644 static/src/workers/patch_logger.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07d48cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +__pycache__/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e0997ea --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +POS IndexedDB Patch +=================== + +This module patches Odoo's bus Logger to avoid IndexedDB connection crashes. + +Features +-------- +- Automatically recreates database connection if closing or closed. +- Adds error safety handlers to log writes and reads. +- Works in both browser thread and shared/web worker threads. diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..67dee8c --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. diff --git a/__manifest__.py b/__manifest__.py new file mode 100644 index 0000000..e5bfb1b --- /dev/null +++ b/__manifest__.py @@ -0,0 +1,26 @@ +{ + 'name': 'POS IndexedDB Patch', + 'version': '19.0.1.0.0', + 'category': 'Technical', + 'summary': 'Patches IndexedDB Logger connection issues in bus worker and main thread', + 'description': """ +This module patches the Logger class inside the bus module to handle IndexedDB connection closures gracefully, preventing POS crashes when the browser suspends/hibernates database connections. +""", + 'author': 'Suherdy Yacob', + 'depends': ['bus', 'point_of_sale'], + 'data': [], + 'assets': { + 'web.assets_backend': [ + 'pos_indexeddb_patch/static/src/workers/patch_logger.js', + ], + 'web.assets_frontend': [ + 'pos_indexeddb_patch/static/src/workers/patch_logger.js', + ], + 'bus.websocket_worker_assets': [ + 'pos_indexeddb_patch/static/src/workers/patch_logger.js', + ], + }, + 'installable': True, + 'auto_install': False, + 'license': 'LGPL-3', +} diff --git a/static/src/workers/patch_logger.js b/static/src/workers/patch_logger.js new file mode 100644 index 0000000..b216e11 --- /dev/null +++ b/static/src/workers/patch_logger.js @@ -0,0 +1,84 @@ +import { Logger } from "@bus/workers/bus_worker_utils"; + +function patchLogger(LoggerClass) { + if (!LoggerClass || LoggerClass.prototype._patchedByIndexedDbPatch) { + return; + } + LoggerClass.prototype._patchedByIndexedDbPatch = true; + + LoggerClass.prototype._ensureDatabaseAvailable = async function() { + if (this._db) { + try { + this._db.transaction("logs", "readonly"); + } catch (e) { + this._db = null; + } + } + if (this._db) { + return; + } + return new Promise((res, rej) => { + const request = indexedDB.open(this._name, 1); + request.onsuccess = (event) => { + this._db = event.target.result; + this._db.onversionchange = () => { + this._db.close(); + this._db = null; + }; + this._db.onclose = () => { + this._db = null; + }; + res(); + }; + request.onupgradeneeded = (event) => { + if (!event.target.result.objectStoreNames.contains("logs")) { + const store = event.target.result.createObjectStore("logs", { + autoIncrement: true, + }); + store.createIndex("timestamp", "timestamp", { unique: false }); + } + }; + request.onerror = rej; + }); + }; + + LoggerClass.prototype.log = async function(message) { + try { + await this._ensureDatabaseAvailable(); + if (!this._db) { + return; + } + const transaction = this._db.transaction("logs", "readwrite"); + const store = transaction.objectStore("logs"); + const addRequest = store.add({ timestamp: Date.now(), message }); + return new Promise((res, rej) => { + addRequest.onsuccess = res; + addRequest.onerror = rej; + }); + } catch (error) { + console.error("Failed to write log to IndexedDB:", error); + } + }; + + LoggerClass.prototype.getLogs = async function() { + try { + await LoggerClass.gcOutdatedLogs(); + await this._ensureDatabaseAvailable(); + if (!this._db) { + return []; + } + const transaction = this._db.transaction("logs", "readonly"); + const store = transaction.objectStore("logs"); + const request = store.getAll(); + return new Promise((res, rej) => { + request.onsuccess = (ev) => res(ev.target.result.map(({ message }) => message)); + request.onerror = rej; + }); + } catch (error) { + console.error("Failed to retrieve logs from IndexedDB:", error); + return []; + } + }; +} + +patchLogger(Logger);