OdooshCN Lab Environments
Syncs the users of this Odoo instance to an OdooshCN platform and gives every user a one-click entry to their own lab environment.
Install
-
Add the parent folder to
addons_path:addons_path = ...,D:\odoo_project\odoo18-2\odoo\custom_project\school-python -
Restart Odoo, update the app list and install OdooshCN Lab Environments.
Configure
1. Create an access token on OdooshCN
Sign in as a super administrator, open the user menu in the top right corner and choose Access tokens, then create one:
| Field | Suggested value |
|---|---|
| Name | Odoo course system |
| Source | school (synced users are recorded under this source) |
| Organisation | where the users should land |
| Scopes | tick all four |
| Maximum grantable role | Organisation manager, if any user needs that role |
| Instances per user | 1 |
| Allow deleting users | on, if deleting a user here should delete the platform account |
| Allow other organisations / platform administrator | off |
The token is shown once, so copy it immediately. Scopes can be edited later without changing the token value.
2. Fill it in here
Settings > General Settings > OdooshCN Lab Environments:
- Turn on Enable sync.
- Enter the platform URL and the token, then press Test connection. It validates the
URL and token only, reports the organisation, the scopes and the limits, and warns when
a scope is missing.
For local testing use
http://127.0.0.1:portrather thanlocalhost: on Windowslocalhostresolves to IPv6 first while WSL2 and Docker only forward IPv4, and every request would wait out a 20 second timeout. The module rewrites localhost anyway, but an explicit address is safer. - Adjust the environment defaults if needed and save.
That is all. There is nothing to configure per user.
Daily use
New users are synced automatically. Everything the platform knows about a user sits on the Lab Environment tab of the user form:
| Field | Meaning | Default |
|---|---|---|
| Sync to lab platform | Master switch for this user | On |
| Platform username override | Empty derives it from the login | Empty |
| Organisation | Platform organisation slug | Empty (default) |
| Platform role | Regular user or platform administrator | Regular user |
| Role in organisation | Tester / Developer / Organisation manager | Developer |
| Web Shell, Enterprise edition, Auto-provision | Switches | On / Off / On |
| Instance limit, Extra database limit | 0 means unlimited | 1 / 2 |
| Platform menus | Overview / Git / AI / Backups | Off / Off / On / Off |
Select several users in the list and use the Actions menu to sync, enable or disable them in bulk.
Users open their environment from the Lab Environment menu. The platform console - the instance list and the instance detail page - is embedded in a frame on that same page, so nobody leaves the course system, with an Open in a new tab button for a full window. The instance's own Odoo always opens in a new tab: nesting a whole Odoo inside another one stacks three navigation bars and two sidebars.
For the frame to work the platform has to allow being embedded by this Odoo. Its console
answers X-Frame-Options: SAMEORIGIN by default and the frame stays blank; the platform
administrator drops a file console-frame-odoo.inc into <data dir>/nginx/ naming this
server, then reloads the platform's web container:
add_header Content-Security-Policy "frame-ancestors 'self' http://school.example.com:8069" always;
The value has to match what users actually have in the address bar, scheme and port
included: http://localhost:8069 does not cover http://the-machine-name:8069.
What happens when
| In Odoo | On the platform |
|---|---|
| Create a user | Account created, sync switch turned on, environment provisioned |
| Update a user | Pushed when the switch is on, ignored when it is off |
| Archive a user | Account deactivated, instances stopped, nothing deleted |
| Restore an archived user | Account activated again |
| Delete a user | Account deleted; the instances move to the platform recycle bin, where the data stays recoverable for the retention period |
Deleting requires the access token to allow it. Without that permission the platform
refuses and the queue row shows delete_not_allowed; the account is then only deactivated.
Portal users and built-in Odoo accounts (OdooBot, the public user, the portal template and the new-user default template) are never synced.
How syncing works
The hooks only write a row into odoosh.sync.log; no HTTP happens inside a user
request. A dedicated thread pushes right after the transaction commits, using its own
database connection, so a failure there can never affect what was already saved.
Three paths back each other up:
| Path | Trigger | Purpose |
|---|---|---|
| Background thread | Right after commit | The user barely notices a delay |
| Queue cron | Every minute | Takes over when the thread was killed with the worker, and drives the retry back-off |
| User scan | Inside that same cron, every 30 minutes | Walks users changed since the last scan and compares fingerprints |
The scan has no cron of its own: it is cheap, but Odoo's cron pool is shared by the whole
database (max_cron_threads, 2 by default), so a slot costs more than the scan does. Set the
system parameter odoosh.scan_interval_minutes to change the interval, or to 0 to turn the
scan off and rely on the hooks alone.
Cost control: only login, name, active, the sync switch and the platform permission
fields trigger a push, so changing an avatar or a preference does not; the scan reads at
most 200 users per round through an index; identical content is never pushed twice.
A daily reconciliation sends the full list of users so the platform can deactivate accounts that disappeared. An empty local list is skipped, and the platform refuses a list that would deactivate an implausible share of the accounts.
Security
- The token is stored in a system parameter, readable by system administrators only, and the settings page shows only a mask plus the last four characters.
- The identity sent to the platform always comes from the current session, never from a request parameter. Otherwise a user could edit a URL and land in somebody else's environment.
- Entry URLs are short lived and single use: they are redirected to immediately, never stored and never logged.
Adjusting the code
Everything a user gets is a field, so day-to-day changes need no code. If the mapping
itself has to change, it lives in models/res_users.py:
_odoosh_payload()builds the user payload from the fields._odoosh_env_spec()builds the environment specification.