Skip to content

Commit

Permalink
Merge pull request NixOS#79485 from Ma27/grocy
Browse files Browse the repository at this point in the history
grocy: init at 2.6.0
  • Loading branch information
Ma27 committed Feb 9, 2020
2 parents c8718e2 + 13f7b75 commit c2f2366
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 0 deletions.
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Expand Up @@ -814,6 +814,7 @@
./services/web-apps/dokuwiki.nix
./services/web-apps/frab.nix
./services/web-apps/gotify-server.nix
./services/web-apps/grocy.nix
./services/web-apps/icingaweb2/icingaweb2.nix
./services/web-apps/icingaweb2/module-monitoring.nix
./services/web-apps/ihatemoney
Expand Down
172 changes: 172 additions & 0 deletions nixos/modules/services/web-apps/grocy.nix
@@ -0,0 +1,172 @@
{ config, lib, pkgs, ... }:

with lib;

let
cfg = config.services.grocy;
in {
options.services.grocy = {
enable = mkEnableOption "grocy";

hostName = mkOption {
type = types.str;
description = ''
FQDN for the grocy instance.
'';
};

nginx.enableSSL = mkOption {
type = types.bool;
default = true;
description = ''
Whether or not to enable SSL (with ACME and let's encrypt)
for the grocy vhost.
'';
};

phpfpm.settings = mkOption {
type = with types; attrsOf (oneOf [ int str bool ]);
default = {
"pm" = "dynamic";
"php_admin_value[error_log]" = "stderr";
"php_admin_flag[log_errors]" = true;
"listen.owner" = "nginx";
"catch_workers_output" = true;
"pm.max_children" = "32";
"pm.start_servers" = "2";
"pm.min_spare_servers" = "2";
"pm.max_spare_servers" = "4";
"pm.max_requests" = "500";
};

description = ''
Options for grocy's PHPFPM pool.
'';
};

dataDir = mkOption {
type = types.str;
default = "/var/lib/grocy";
description = ''
Home directory of the <literal>grocy</literal> user which contains
the application's state.
'';
};

settings = {
currency = mkOption {
type = types.str;
default = "USD";
example = "EUR";
description = ''
ISO 4217 code for the currency to display.
'';
};

culture = mkOption {
type = types.enum [ "de" "en" "da" "en_GB" "es" "fr" "hu" "it" "nl" "no" "pl" "pt_BR" "ru" "sk_SK" "sv_SE" "tr" ];
default = "en";
description = ''
Display language of the frontend.
'';
};

calendar = {
showWeekNumber = mkOption {
default = true;
type = types.bool;
description = ''
Show the number of the weeks in the calendar views.
'';
};
firstDayOfWeek = mkOption {
default = null;
type = types.nullOr (types.enum (range 0 6));
description = ''
Which day of the week (0=Sunday, 1=Monday etc.) should be the
first day.
'';
};
};
};
};

config = mkIf cfg.enable {
environment.etc."grocy/config.php".text = ''
<?php
Setting('CULTURE', '${cfg.settings.culture}');
Setting('CURRENCY', '${cfg.settings.currency}');
Setting('CALENDAR_FIRST_DAY_OF_WEEK', '${toString cfg.settings.calendar.firstDayOfWeek}');
Setting('CALENDAR_SHOW_WEEK_OF_YEAR', ${boolToString cfg.settings.calendar.showWeekNumber});
'';

users.users.grocy = {
isSystemUser = true;
createHome = true;
home = cfg.dataDir;
group = "nginx";
};

systemd.tmpfiles.rules = map (
dirName: "d '${cfg.dataDir}/${dirName}' - grocy nginx - -"
) [ "viewcache" "plugins" "settingoverrides" "storage" ];

services.phpfpm.pools.grocy = {
user = "grocy";
group = "nginx";

# PHP 7.3 is the only version which is supported/tested by upstream:
# https://github.com/grocy/grocy/blob/v2.6.0/README.md#how-to-install
phpPackage = pkgs.php73;

inherit (cfg.phpfpm) settings;

phpEnv = {
GROCY_CONFIG_FILE = "/etc/grocy/config.php";
GROCY_DB_FILE = "${cfg.dataDir}/grocy.db";
GROCY_STORAGE_DIR = "${cfg.dataDir}/storage";
GROCY_PLUGIN_DIR = "${cfg.dataDir}/plugins";
GROCY_CACHE_DIR = "${cfg.dataDir}/viewcache";
};
};

services.nginx = {
enable = true;
virtualHosts."${cfg.hostName}" = mkMerge [
{ root = "${pkgs.grocy}/public";
locations."/".extraConfig = ''
rewrite ^ /index.php;
'';
locations."~ \\.php$".extraConfig = ''
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:${config.services.phpfpm.pools.grocy.socket};
include ${config.services.nginx.package}/conf/fastcgi.conf;
include ${config.services.nginx.package}/conf/fastcgi_params;
'';
locations."~ \\.(js|css|ttf|woff2?|png|jpe?g|svg)$".extraConfig = ''
add_header Cache-Control "public, max-age=15778463";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header Referrer-Policy no-referrer;
access_log off;
'';
extraConfig = ''
try_files $uri /index.php;
'';
}
(mkIf cfg.nginx.enableSSL {
enableACME = true;
forceSSL = true;
})
];
};
};

meta = {
maintainers = with maintainers; [ ma27 ];
doc = ./grocy.xml;
};
}
77 changes: 77 additions & 0 deletions nixos/modules/services/web-apps/grocy.xml
@@ -0,0 +1,77 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="module-services-grocy">

<title>Grocy</title>
<para>
<link xlink:href="https://grocy.info/">Grocy</link> is a web-based self-hosted groceries
&amp; household management solution for your home.
</para>

<section xml:id="module-services-grocy-basic-usage">
<title>Basic usage</title>
<para>
A very basic configuration may look like this:
<programlisting>{ pkgs, ... }:
{
services.grocy = {
<link linkend="opt-services.grocy.enable">enable</link> = true;
<link linkend="opt-services.grocy.hostName">hostName</link> = "grocy.tld";
};
}</programlisting>
This configures a simple vhost using <link linkend="opt-services.nginx.enable">nginx</link>
which listens to <literal>grocy.tld</literal> with fully configured ACME/LE (this can be
disabled by setting <link linkend="opt-services.grocy.nginx.enableSSL">services.grocy.nginx.enableSSL</link>
to <literal>false</literal>). After the initial setup the credentials <literal>admin:admin</literal>
can be used to login.
</para>
<para>
The application's state is persisted at <literal>/var/lib/grocy/grocy.db</literal> in a
<package>sqlite3</package> database. The migration is applied when requesting the <literal>/</literal>-route
of the application.
</para>
</section>

<section xml:id="module-services-grocy-settings">
<title>Settings</title>
<para>
The configuration for <literal>grocy</literal> is located at <literal>/etc/grocy/config.php</literal>.
By default, the following settings can be defined in the NixOS-configuration:
<programlisting>{ pkgs, ... }:
{
services.grocy.settings = {
# The default currency in the system for invoices etc.
# Please note that exchange rates aren't taken into account, this
# is just the setting for what's shown in the frontend.
<link linkend="opt-services.grocy.settings.currency">currency</link> = "EUR";

# The display language (and locale configuration) for grocy.
<link linkend="opt-services.grocy.settings.currency">culture</link> = "de";

calendar = {
# Whether or not to show the week-numbers
# in the calendar.
<link linkend="opt-services.grocy.settings.calendar.showWeekNumber">showWeekNumber</link> = true;

# Index of the first day to be shown in the calendar (0=Sunday, 1=Monday,
# 2=Tuesday and so on).
<link linkend="opt-services.grocy.settings.calendar.firstDayOfWeek">firstDayOfWeek</link> = 2;
};
};
}</programlisting>
</para>
<para>
If you want to alter the configuration file on your own, you can do this manually with
an expression like this:
<programlisting>{ lib, ... }:
{
environment.etc."grocy/config.php".text = lib.mkAfter ''
// Arbitrary PHP code in grocy's configuration file
'';
}</programlisting>
</para>
</section>

</chapter>
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Expand Up @@ -96,6 +96,7 @@ in
freeswitch = handleTest ./freeswitch.nix {};
fsck = handleTest ./fsck.nix {};
gotify-server = handleTest ./gotify-server.nix {};
grocy = handleTest ./grocy.nix {};
gitea = handleTest ./gitea.nix {};
gitlab = handleTest ./gitlab.nix {};
gitolite = handleTest ./gitolite.nix {};
Expand Down
47 changes: 47 additions & 0 deletions nixos/tests/grocy.nix
@@ -0,0 +1,47 @@
import ./make-test-python.nix ({ pkgs, ... }: {
name = "grocy";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ ma27 ];
};

machine = { pkgs, ... }: {
services.grocy = {
enable = true;
hostName = "localhost";
nginx.enableSSL = false;
};
environment.systemPackages = [ pkgs.jq ];
};

testScript = ''
machine.start()
machine.wait_for_open_port(80)
machine.wait_for_unit("multi-user.target")
machine.succeed("curl -sSf http://localhost")
machine.succeed(
"curl -c cookies -sSf -X POST http://localhost/login -d 'username=admin&password=admin'"
)
cookie = machine.succeed(
"grep -v '^#' cookies | awk '{ print $7 }' | sed -e '/^$/d' | perl -pe 'chomp'"
)
machine.succeed(
f"curl -sSf -X POST http://localhost/api/objects/tasks -b 'grocy_session={cookie}' "
+ '-d \'{"assigned_to_user_id":1,"name":"Test Task","due_date":"1970-01-01"}\'''
+ " --header 'Content-Type: application/json'"
)
task_name = machine.succeed(
f"curl -sSf http://localhost/api/tasks -b 'grocy_session={cookie}' --header 'Accept: application/json' | jq '.[].name' | xargs echo | perl -pe 'chomp'"
)
assert task_name == "Test Task"
machine.succeed("curl -sSfI http://localhost/api/tasks 2>&1 | grep '401 Unauthorized'")
machine.shutdown()
'';
})
61 changes: 61 additions & 0 deletions pkgs/servers/grocy/config-locations.patch
@@ -0,0 +1,61 @@
diff --git a/app.php b/app.php
index 5f91e4d..09c6010 100644
--- a/app.php
+++ b/app.php
@@ -23,7 +23,7 @@ else
require_once __DIR__ . '/vendor/autoload.php';

// Load config files
-require_once GROCY_DATAPATH . '/config.php';
+require_once getenv('GROCY_CONFIG_FILE');
require_once __DIR__ . '/config-dist.php'; // For not in own config defined values we use the default ones

// Definitions for dev/demo/prerelease mode
@@ -49,7 +49,7 @@ $appContainer = new \Slim\Container([
],
'view' => function($container)
{
- return new \Slim\Views\Blade(__DIR__ . '/views', GROCY_DATAPATH . '/viewcache');
+ return new \Slim\Views\Blade(__DIR__ . '/views', getenv('GROCY_CACHE_DIR'));
},
'LoginControllerInstance' => function($container)
{
diff --git a/services/DatabaseService.php b/services/DatabaseService.php
index 0bcf9b8..ec45e93 100644
--- a/services/DatabaseService.php
+++ b/services/DatabaseService.php
@@ -13,7 +13,7 @@ class DatabaseService
return GROCY_DATAPATH . '/grocy_' . GROCY_CULTURE . '.db';
}

- return GROCY_DATAPATH . '/grocy.db';
+ return getenv('GROCY_DB_FILE');
}

private $DbConnectionRaw;
diff --git a/services/FilesService.php b/services/FilesService.php
index 7933b73..f52657e 100644
--- a/services/FilesService.php
+++ b/services/FilesService.php
@@ -12,7 +12,7 @@ class FilesService extends BaseService
{
parent::__construct();

- $this->StoragePath = GROCY_DATAPATH . '/storage';
+ $this->StoragePath = getenv('GROCY_STORAGE_DIR');

if (!file_exists($this->StoragePath))
{
diff --git a/services/StockService.php b/services/StockService.php
index d7482ef..d1399a7 100644
--- a/services/StockService.php
+++ b/services/StockService.php
@@ -933,7 +933,7 @@ class StockService extends BaseService
throw new \Exception('No barcode lookup plugin defined');
}

- $path = GROCY_DATAPATH . "/plugins/$pluginName.php";
+ $path = getenv('GROCY_PLUGIN_DIR');
if (file_exists($path))
{
require_once $path;

0 comments on commit c2f2366

Please sign in to comment.