{ config, pkgs, lib, ... }: let cfg = config.services.odoo; format = pkgs.formats.ini {}; in { options = { services.odoo = { enable = lib.mkEnableOption "odoo, an open source ERP and CRM system"; package = lib.mkPackageOption pkgs "odoo" { }; addons = lib.mkOption { type = with lib.types; listOf package; default = []; example = lib.literalExpression "[ pkgs.odoo_enterprise ]"; description = "Odoo addons."; }; autoInit = lib.mkEnableOption "automatically initialize the DB"; autoInitExtraFlags = lib.mkOption { type = with lib.types; listOf str; default = [ ]; example = lib.literalExpression /*nix*/ '' [ "--without-demo=all" ] ''; description = "Extra flags passed to odoo when run for the first time by autoInit"; }; settings = lib.mkOption { type = format.type; default = {}; description = '' Odoo configuration settings. For more details see ''; example = lib.literalExpression '' options = { db_user = "odoo"; db_password="odoo"; }; ''; }; domain = lib.mkOption { type = with lib.types; nullOr str; description = "Domain to host Odoo with nginx"; default = null; }; }; }; config = lib.mkIf (cfg.enable) (let cfgFile = format.generate "odoo.cfg" cfg.settings; in { services.nginx = lib.mkIf (cfg.domain != null) { upstreams = { odoo.servers = { "127.0.0.1:8069" = {}; }; odoochat.servers = { "127.0.0.1:8072" = {}; }; }; virtualHosts."${cfg.domain}" = { extraConfig = '' proxy_read_timeout 720s; proxy_connect_timeout 720s; proxy_send_timeout 720s; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; ''; locations = { "/longpolling" = { proxyPass = "http://odoochat"; }; "/" = { proxyPass = "http://odoo"; extraConfig = '' proxy_redirect off; ''; }; }; }; }; services.odoo.settings.options = { data_dir = "/var/lib/private/odoo/data"; proxy_mode = cfg.domain != null; } // (lib.optionalAttrs (cfg.addons != []) { addons_path = lib.concatMapStringsSep "," lib.escapeShellArg cfg.addons; }); users.users.odoo = { isSystemUser = true; group = "odoo"; }; users.groups.odoo = {}; systemd.services.odoo = { wantedBy = [ "multi-user.target" ]; after = [ "network.target" "postgresql.service" ]; # pg_dump path = [ config.services.postgresql.package ]; requires = [ "postgresql.service" ]; serviceConfig = { ExecStart = "${cfg.package}/bin/odoo"; ExecStartPre = pkgs.writeShellScript "odoo-start-pre.sh" ( '' set -euo pipefail cd "$STATE_DIRECTORY" # Auto-migrate old deployments if [[ -d .local/share/Odoo ]]; then echo "pre-start: migrating state directory from $STATE_DIRECTORY/.local/share/Odoo to $STATE_DIRECTORY/data" mv .local/share/Odoo ./data rmdir .local/share rmdir .local fi '' + (lib.optionalString cfg.autoInit '' echo "pre-start: auto-init" INITIALIZED="${cfg.settings.options.data_dir}/.odoo.initialized" if [ ! -e "$INITIALIZED" ]; then ${cfg.package}/bin/odoo --init=INIT --database=odoo --db_user=odoo --stop-after-init ${lib.concatStringsSep " " cfg.autoInitExtraFlags} touch "$INITIALIZED" fi '') + "echo pre-start: OK" ); DynamicUser = true; User = "odoo"; StateDirectory = "odoo"; Environment = [ "ODOO_RC=${cfgFile}" ]; }; }; services.postgresql = { enable = true; ensureDatabases = [ "odoo" ]; ensureUsers = [{ name = "odoo"; ensureDBOwnership = true; }]; }; }); }