6 Commits
Author SHA1 Message Date
msoer a32b303e7a [MiG] port from 19 2026-08-24 13:40:43 +02:00
msoer ac8cb24bfb [FiX] update 18 . 2026-08-10 12:26:30 +02:00
dochse 1e6dc183d0 Update install_mailhog_as_service.sh
1.25.0
2026-05-26 12:52:47 +00:00
Thomas Pot ef684151c9 NEW] Add methods to disable web push notifications 2026-04-28 19:39:01 +02:00
Thomas Pot ba955407e5 NEW] Add methods to disable web push notifications 2026-04-28 19:32:40 +02:00
Thomas Pot 1d5e7f504c [FIX] Domain must be unique for each website 2026-02-19 17:07:57 +01:00
19 changed files with 322 additions and 49 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ echo ""
echo "==================================="
echo "Install Go 1.24.3..."
echo "==================================="
GO_VERSION=1.24.3
GO_VERSION=1.26.0
wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
+2
View File
@@ -2,3 +2,5 @@
from . import controllers
from . import models
from . import wizard
from .hooks import post_init_hook
+6 -2
View File
@@ -11,12 +11,16 @@
'author': "Open2bizz",
'website': "http://www.open2bizz.tech",
'category': 'Uncategorized',
'category': 'Tools',
'license': "AGPL-3",
'version': '18.0.1.0.0',
'module_type': 'official',
'depends': ['mail','base',],
'data': [
'wizard/install_warning_wizard_view.xml',
'views/views.xml',
'data/data.xml'
'data/data.xml',
'security/ir.model.access.csv',
],
'post_init_hook': 'post_init_hook',
}
+51 -17
View File
@@ -1,21 +1,55 @@
# -*- coding: utf-8 -*-
# from odoo import http
# Copyright 2024 Open2Bizz <info@open2bizz.nl>
# License LGPL-3
import logging
import requests
from odoo import http
from odoo.http import request, Response
_logger = logging.getLogger(__name__)
MAILHOG_BASE = "http://localhost:8025"
# class TestserverO2b(http.Controller):
# @http.route('/testserver_o2b/testserver_o2b/', auth='public')
# def index(self, **kw):
# return "Hello, world"
class MailcatcherProxy(http.Controller):
# @http.route('/testserver_o2b/testserver_o2b/objects/', auth='public')
# def list(self, **kw):
# return http.request.render('testserver_o2b.listing', {
# 'root': '/testserver_o2b/testserver_o2b',
# 'objects': http.request.env['testserver_o2b.testserver_o2b'].search([]),
# })
@http.route(['/mailcatcher', '/mailcatcher/<path:subpath>'],
type='http', auth='user', website=False, csrf=False)
def mailcatcher_proxy(self, subpath='', **kw):
"""Proxy requests to the local MailHog instance."""
target_url = f"{MAILHOG_BASE}/{subpath}"
if request.httprequest.query_string:
target_url += '?' + request.httprequest.query_string.decode('utf-8')
# @http.route('/testserver_o2b/testserver_o2b/objects/<model("testserver_o2b.testserver_o2b"):obj>/', auth='public')
# def object(self, obj, **kw):
# return http.request.render('testserver_o2b.object', {
# 'object': obj
# })
try:
headers = {
k: v for k, v in request.httprequest.headers
if k.lower() not in ('host', 'cookie', 'authorization')
}
resp = requests.request(
method=request.httprequest.method,
url=target_url,
headers=headers,
data=request.httprequest.get_data(),
timeout=30,
allow_redirects=False,
)
except requests.exceptions.ConnectionError:
_logger.warning("Could not connect to MailHog at %s", MAILHOG_BASE)
return Response("MailHog is not available", status=502)
excluded_headers = {
'content-encoding', 'content-length', 'transfer-encoding',
'connection',
}
response_headers = [
(k, v) for k, v in resp.headers.items()
if k.lower() not in excluded_headers
]
return Response(
resp.content,
status=resp.status_code,
headers=response_headers,
)
-1
View File
@@ -1,5 +1,4 @@
<odoo>
<data>
<function model="ir.config_parameter" name="_mailcatcher_url"/>
</data>
</odoo>
+40
View File
@@ -0,0 +1,40 @@
# Copyright 2024 Open2Bizz <info@open2bizz.nl>
# License LGPL-3
import logging
import os
import subprocess
_logger = logging.getLogger(__name__)
def post_init_hook(env):
"""Install MailHog as a systemd service when the module is installed."""
script_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'install_mailhog_as_service.sh',
)
if not os.path.isfile(script_path):
_logger.error("MailHog install script not found at %s", script_path)
return
_logger.info("Running MailHog install script: %s", script_path)
try:
result = subprocess.run(
['bash', script_path],
capture_output=True,
text=True,
timeout=300,
)
if result.returncode == 0:
_logger.info("MailHog installed successfully.\n%s", result.stdout)
else:
_logger.error(
"MailHog install script failed (exit %s):\n%s\n%s",
result.returncode, result.stdout, result.stderr,
)
except subprocess.TimeoutExpired:
_logger.error("MailHog install script timed out after 300 seconds")
except Exception as e:
_logger.error("Failed to run MailHog install script: %s", e)
@@ -0,0 +1,86 @@
#!/bin/bash
set -e # stop bij fouten
echo ""
echo "==================================="
echo "Update apt..."
echo "==================================="
apt update
echo ""
echo "==================================="
echo "Install dependencies..."
echo "==================================="
apt-get -y install wget tar
echo ""
echo "==================================="
echo "Install Go 1.24.3..."
echo "==================================="
GO_VERSION=1.26.0
wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
# Go environment correct instellen
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH
echo "Go version:"
go version
echo ""
echo "==================================="
echo "Installing MailHog..."
echo "==================================="
/usr/local/go/bin/go install github.com/mailhog/MailHog@latest
if [ -f "$HOME/go/bin/MailHog" ]; then
echo "MailHog installed successfully"
else
echo "ERROR: MailHog binary not found!"
exit 1
fi
echo ""
echo "==================================="
echo "Copy MailHog to /usr/local/bin..."
echo "==================================="
sudo cp "$HOME/go/bin/MailHog" /usr/local/bin/MailHog
echo ""
echo "==================================="
echo "Create MailHog service..."
echo "==================================="
sudo tee /etc/systemd/system/mailhog.service > /dev/null <<EOL
[Unit]
Description=MailHog
After=network.target
[Service]
User=root
ExecStart=/usr/local/bin/MailHog
Restart=always
Environment="PATH=/usr/local/go/bin:/usr/bin:/bin"
[Install]
WantedBy=multi-user.target
EOL
echo ""
echo "==================================="
echo "Enable + start MailHog service..."
echo "==================================="
systemctl daemon-reload
systemctl enable mailhog
systemctl start mailhog
echo ""
echo "==================================="
echo "MailHog is running!"
echo "Web UI: http://localhost:8025"
echo "SMTP: localhost:1025"
echo "==================================="
+1 -1
View File
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
from . import mailcatcher_menu
# No models needed — MailHog is accessed via the proxy controller
+2 -22
View File
@@ -1,27 +1,7 @@
# Copyright 2024 Open2Bizz <info@open2bizz.nl>
# License LGPL-3
from odoo import api, fields, models, _
import logging
_logger = logging.getLogger(__name__)
class IrActionUrl(models.Model):
_inherit = "ir.config_parameter"
@api.model
#def init(self):
def _mailcatcher_url(self):
parameters = self.env['ir.config_parameter'].search([('key','=', 'web.base.url')])
if parameters:
parameter = parameters[0]
cont_nr = parameter.value.split("//",1)[1] #Haal https:// van string
cont_nr = cont_nr[0:3] #De eerste 3 karakters is het containernummer
act_url = "https://" + cont_nr + "-mailcatcher.staging.open2bizz.com"
_logger.info('---------- ACT_URL ---------- %s', act_url)
action_id = self.env['ir.model.data']._xmlid_to_res_id('mailcatcher_menu.action_mailcatcher_url')
action = self.env['ir.actions.act_url'].browse(action_id)
if action:
action.write({'url':act_url})
# MailHog is now accessed via the internal proxy controller at /mailcatcher/
# so no dynamic URL computation is needed anymore.
@@ -1,2 +1,2 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_mailcatcher_menu_mailcatcher_menu","mailcatcher_menu.mailcatcher_menu","model_mailcatcher_menu_mailcatcher_menu","base.group_user",1,1,1,1
"access_mailcatcher_install_warning_wizard","mailcatcher.install.warning.wizard","model_mailcatcher_install_warning_wizard","base.group_user",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mailcatcher_menu_mailcatcher_menu access_mailcatcher_install_warning_wizard mailcatcher_menu.mailcatcher_menu mailcatcher.install.warning.wizard model_mailcatcher_menu_mailcatcher_menu model_mailcatcher_install_warning_wizard base.group_user 1 1 1 1
+1 -1
View File
@@ -3,7 +3,7 @@
<record model="ir.actions.act_url" id="action_mailcatcher_url">
<field name="name">Open Mailcatcher</field>
<field name="url">https://mailcatcher.staging.open2bizz.com</field>
<field name="url">/mailcatcher/</field>
<field name="target">new</field>
</record>
+3
View File
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import install_warning_wizard
@@ -0,0 +1,43 @@
# Copyright 2024 Open2Bizz <info@open2bizz.nl>
# License LGPL-3
from odoo import models, fields, api, _
class MailcatcherInstallWarningWizard(models.TransientModel):
_name = 'mailcatcher.install.warning.wizard'
_description = 'MailCatcher Install Warning'
module_id = fields.Many2one('ir.module.module', string='Module', readonly=True)
def action_confirm_install(self):
"""User confirmed — proceed with the actual module installation."""
self.ensure_one()
return self.module_id.with_context(
skip_mailcatcher_warning=True
).button_immediate_install()
class IrModuleModule(models.Model):
_inherit = 'ir.module.module'
def button_immediate_install(self):
"""Override to show a warning before installing mailcatcher_menu."""
if self.env.context.get('skip_mailcatcher_warning'):
return super().button_immediate_install()
mailcatcher_modules = self.filtered(lambda m: m.name == 'mailcatcher_menu')
if mailcatcher_modules:
wizard = self.env['mailcatcher.install.warning.wizard'].create({
'module_id': mailcatcher_modules[0].id,
})
return {
'name': _('Install MailCatcher Module'),
'type': 'ir.actions.act_window',
'res_model': 'mailcatcher.install.warning.wizard',
'res_id': wizard.id,
'view_mode': 'form',
'target': 'new',
}
return super().button_immediate_install()
@@ -0,0 +1,28 @@
<odoo>
<record id="view_mailcatcher_install_warning_wizard" model="ir.ui.view">
<field name="name">mailcatcher.install.warning.wizard.form</field>
<field name="model">mailcatcher.install.warning.wizard</field>
<field name="arch" type="xml">
<form string="Install MailCatcher">
<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Warning</h4>
<p>
You are about to install the <strong>MailCatcher</strong> module.
</p>
<p>
This will also install <strong>MailHog</strong> as a systemd service on this server.
MailHog will listen on port <strong>8025</strong> (Web UI) and port <strong>1025</strong> (SMTP).
</p>
<p>
Do you want to proceed with the installation?
</p>
</div>
<footer>
<button name="action_confirm_install" type="object"
string="Confirm Install" class="btn-primary"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>
+1
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from .hooks import post_init_hook
from . import models
+4 -1
View File
@@ -8,10 +8,13 @@
'author': "Open2bizz",
'website': "http://www.open2bizz.tech",
'category': 'Uncategorized',
'category': 'Tools',
'license': "AGPL-3",
'version': '18.0.1.0.0',
'depends': ['mail','base','mailcatcher_menu',],
'data': [
'views/templates.xml',
'data/ir_cron.xml',
],
'post_init_hook': 'post_init_hook',
}
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="ir_cron_set_db_parameter" model="ir.cron">
<field name="name">Testserver: Set Date (ONLY FOR TEST ENV.)</field>
<field name="model_id" ref="base.model_ir_logging"/>
<field name="state">code</field>
<field name="code">model.set_db_param()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="user_id" ref="base.user_root"/>
<field name="active">True</field>
</record>
<record id="ir_cron_cleanup_filestore_attachments" model="ir.cron">
<field name="name">Testserver: Cleanup Unneeded Filestore</field>
<field name="model_id" ref="base.model_ir_logging"/>
<field name="state">code</field>
<field name="code">model.cleanup_unneeded_filestore_attachments(5000)</field>
<field name="interval_number">15</field>
<field name="interval_type">minutes</field>
<field name="user_id" ref="base.user_root"/>
<field name="active">True</field>
</record>
<record id="base.autovacuum_job" model="ir.cron">
<field name="active">True</field>
</record>
</data>
</odoo>
+7
View File
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from odoo import api
def post_init_hook(env):
env['ir.logging'].sudo().cleanup_unneeded_filestore_attachments(limit=50)
+14
View File
@@ -31,6 +31,8 @@ class IrLogging(models.Model):
self.env.cr.execute("""DELETE FROM ir_config_parameter WHERE key = 'web.base.url.freeze';""")
if not self.env['ir.config_parameter'].search([('key','=','report.url')]):
self.env.cr.execute("""INSERT INTO ir_config_parameter(key,value) VALUES ('report.url','http://127.0.0.1:8069');""")
if self.env['ir.model'].search([('model','=','mail.partner.device')]):
self.env.cr.execute("""DELETE FROM mail_partner_device;""")
model = self.env['ir.model'].search([('model','=','delivery.carrier')])
if model:
field = self.env['ir.model.fields'].search([('name','=','prod_environment'),('model_id','=',model.id)])
@@ -64,3 +66,15 @@ class IrLogging(models.Model):
for ap in ADDONS_PATH.split(','):
if ap[-10:] == 'testserver':
_logger.error(os.system('./%s/testserver.sh' %(ap)))
self._disable_web_push_notifications()
_logger.debug(f"_______ disable_web_push_notifications ____")
def _disable_web_push_notifications(self):
parameter = self.env['ir.config_parameter'].sudo()
parameter.set_param('web_push.notification.enabled', '0')
parameters = self.env['ir.config_parameter'].sudo().search([
('key', 'in', ('mail.web_push_vapid_private_key', 'mail.web_push_vapid_public_key', 'mail.sfu_server_key'))])
for parameter in parameters:
parameter.sudo().unlink()