forked from meterriblecrew/packages
modules: add services.snipe-it
parent
b5e7f4a3a9
commit
f8c0d8a036
|
@ -19,6 +19,8 @@
|
|||
|
||||
nixosModules = {
|
||||
ryzenSMU = import ./modules/ryzenSMU;
|
||||
|
||||
servicesSnipeIT = import ./modules/services/snipe-it.nix;
|
||||
};
|
||||
|
||||
packages = forAllSystems (system: import ./. {
|
||||
|
|
|
@ -0,0 +1,517 @@
|
|||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types
|
||||
boolToString callPackageWith optional optionals optionalString recursiveUpdate;
|
||||
|
||||
cfg = config.services.snipe-it;
|
||||
|
||||
phpPackage = cfg.phpPackage.withExtensions ({ enabled, all }:
|
||||
enabled ++ (with all; [
|
||||
json
|
||||
openssl
|
||||
pdo
|
||||
mbstring
|
||||
tokenizer
|
||||
curl
|
||||
ldap
|
||||
zip
|
||||
fileinfo
|
||||
bcmath
|
||||
gd
|
||||
] ++ optionals usePgsql [
|
||||
pdo_pgsql
|
||||
] ++ optionals useMysql [
|
||||
pdo_mysql
|
||||
])
|
||||
);
|
||||
|
||||
nginxPackage = config.services.nginx.package;
|
||||
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
|
||||
db = cfg.database;
|
||||
usePgsql = db.type == "pgsql";
|
||||
useMysql = db.type == "mysql";
|
||||
useSqlite = db.type == "sqlite";
|
||||
|
||||
mail = cfg.mail;
|
||||
|
||||
useSSL = with cfg.nginx; (addSSL || forceSSL || onlySSL || enableACME);
|
||||
|
||||
snipe-it = cfg.package.override { inherit (cfg) cacheDir dataDir; };
|
||||
|
||||
artisan = pkgs.writeShellScriptBin "snipe-it" ''
|
||||
cd ${snipe-it}
|
||||
sudo=exec
|
||||
if [[ "$USER" != ${user} ]]; then
|
||||
sudo='exec /run/wrappers/bin/sudo -u ${user}'
|
||||
fi
|
||||
$sudo ${phpPackage}/bin/php artisan $*
|
||||
'';
|
||||
|
||||
in {
|
||||
|
||||
options.services.snipe-it = {
|
||||
enable = mkEnableOption "Snipe-IT free open source IT asset management";
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = callPackageWith pkgs ../../pkgs/snipe-it { };
|
||||
description = "Snipe-IT derivation to use.";
|
||||
};
|
||||
|
||||
phpPackage = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.php74;
|
||||
description = "PHP package to use.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "snipe-it";
|
||||
description = "User Snipe-IT runs as.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "snipe-it";
|
||||
description = "Group Snipe-IT runs as.";
|
||||
};
|
||||
|
||||
hostName = mkOption {
|
||||
type = types.str;
|
||||
example = "assets.example.com";
|
||||
description = "FQDN for the Snipe-IT instance.";
|
||||
};
|
||||
|
||||
maxResults = mkOption {
|
||||
type = types.int;
|
||||
default = 500;
|
||||
description = ''
|
||||
The result limit. This value determines the maximum number of results to return,
|
||||
even if a higher limit is passed in an API request. This is done to prevent
|
||||
timeouts when custom scripts are requesting large numbers of assets at a time.
|
||||
'';
|
||||
};
|
||||
|
||||
# Basic app settings
|
||||
appKeyFile = mkOption {
|
||||
type = types.path;
|
||||
example = "/run/keys/snipe-it-appkey";
|
||||
description = ''
|
||||
A file containing the app key. Used for encryption where needed.
|
||||
Can be generated with <code>head -c32 /dev/urandom | base64</code>.
|
||||
'';
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/snipe-it";
|
||||
description = "Snipe-IT's data directory.";
|
||||
};
|
||||
|
||||
cacheDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/cache/snipe-it";
|
||||
description = "Snipe-IT's cache directory";
|
||||
};
|
||||
|
||||
database = {
|
||||
type = mkOption {
|
||||
type = types.enum [ "pgsql" "mysql" "sqlite" ];
|
||||
default = "pgsql";
|
||||
description = "Database engine to use.";
|
||||
};
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = "Database host address.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = if useMysql then 3306 else 5432;
|
||||
description = ''
|
||||
Database host port. This currently only has
|
||||
an effect when using MySQL.
|
||||
'';
|
||||
};
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "snipe-it";
|
||||
description = "Name of the PostgreSQL or MySQL database.";
|
||||
};
|
||||
username = mkOption {
|
||||
type = types.str;
|
||||
default = user;
|
||||
description = "Username to use to connect to database.";
|
||||
};
|
||||
passwordFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
example = "/run/keys/snipe-it-dbpass";
|
||||
description = ''
|
||||
File containing the password corresponding to
|
||||
<option>database.username</option>.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
mail = {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = "Mail server host address.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 25;
|
||||
description = "Mail server host port.";
|
||||
};
|
||||
encryption = mkOption {
|
||||
type = types.nullOr (types.enum [ "ssl" "tls" ]);
|
||||
default = null;
|
||||
example = "tls";
|
||||
description = ''
|
||||
Type of transport encryption to use.
|
||||
'';
|
||||
};
|
||||
username = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "User to use to connect to mail server.";
|
||||
};
|
||||
passwordFile = mkOption {
|
||||
type = types.path;
|
||||
example = "/run/keys/snipe-it-mailpass";
|
||||
description = ''
|
||||
File containing the password corresponding to
|
||||
<option>mail.username</option>.
|
||||
'';
|
||||
};
|
||||
fromAddress = mkOption {
|
||||
type = types.str;
|
||||
description = ''Global "From" address.'';
|
||||
};
|
||||
fromName = mkOption {
|
||||
type = types.str;
|
||||
default = "Snipe-IT";
|
||||
description = ''Global "From" name.'';
|
||||
};
|
||||
replytoAddress = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
Global "Reply-To" address. If null (the default),
|
||||
"Reply-To" won't be set.
|
||||
'';
|
||||
};
|
||||
replytoName = mkOption {
|
||||
type = types.str;
|
||||
default = "Snipe-IT";
|
||||
description = ''
|
||||
Global "Reply-To" name. <option>mail.replytoAddress</option>
|
||||
also has to be set for this to have any effect.
|
||||
'';
|
||||
};
|
||||
autoEmbed = {
|
||||
enable = mkEnableOption "Embed images into mails instead of hyperlinking them.";
|
||||
|
||||
method = mkOption {
|
||||
type = types.enum [ "attachment" "base64" ];
|
||||
default = "attachment";
|
||||
description = "Embedding method to use.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
memcached = {
|
||||
enable = mkEnableOption "memcached as caching backend";
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = "Memcached host address.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 11211;
|
||||
description = "Memcached host port.";
|
||||
};
|
||||
};
|
||||
|
||||
session = {
|
||||
lifeTime = mkOption {
|
||||
type = types.int;
|
||||
default = 12000;
|
||||
description = "Session lifetime in minutes.";
|
||||
};
|
||||
expireOnClose = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Expire sessions when closing browser window.";
|
||||
};
|
||||
encrypt = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Encrypt stored session data.";
|
||||
};
|
||||
};
|
||||
|
||||
maxUploadSize = mkOption {
|
||||
type = types.str;
|
||||
default = "16M";
|
||||
example = "1G";
|
||||
description = "The maximum size for uploads (e.g. images).";
|
||||
};
|
||||
|
||||
poolConfig = mkOption {
|
||||
type = with types; attrsOf (oneOf [ str int bool ]);
|
||||
default = {
|
||||
"pm" = "dynamic";
|
||||
"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 the Snipe-IT PHP pool. See the documentation on
|
||||
<literal>php-fpm.conf</literal> for details on configuration directives.
|
||||
'';
|
||||
};
|
||||
|
||||
nginx = mkOption {
|
||||
type = types.submodule (
|
||||
recursiveUpdate
|
||||
(import
|
||||
(modulesPath + "/services/web-servers/nginx/vhost-options.nix")
|
||||
{ inherit config lib; })
|
||||
{}
|
||||
);
|
||||
default = {};
|
||||
example = {
|
||||
serverAliases = [
|
||||
"snipe-it.\${config.networking.domain}"
|
||||
];
|
||||
# To enable encryption and let letsencrypt take care of certificate
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
};
|
||||
description = ''
|
||||
With this option, you can customize the nginz virtualHost settings.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.nullOr types.lines;
|
||||
default = null;
|
||||
example = ''
|
||||
LOGIN_MAX_ATTEMPTS=3
|
||||
LOGIN_LOCKOUT_DURATION=300
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions = [
|
||||
{ assertion = usePgsql -> db.port == 5432;
|
||||
message = "PostgreSQL is currently only supported with default port 5432.";
|
||||
}
|
||||
];
|
||||
|
||||
warnings =
|
||||
optional (!useMysql) ''
|
||||
Please note: Using another database than MySQL isn't officially supported.
|
||||
'';
|
||||
|
||||
environment.systemPackages = [ artisan ];
|
||||
|
||||
services.postgresql = mkIf (usePgsql && db.host == "localhost") {
|
||||
enable = mkDefault true;
|
||||
ensureDatabases = [ db.name ];
|
||||
ensureUsers = [{
|
||||
name = db.username;
|
||||
ensurePermissions = { "DATABASE \"${db.name}\"" = "ALL PRIVILEGES"; };
|
||||
}];
|
||||
};
|
||||
|
||||
services.mysql = mkIf (useMysql && db.host == "localhost") {
|
||||
enable = mkDefault true;
|
||||
package = mkDefault pkgs.mariadb;
|
||||
ensureDatabases = [ db.name ];
|
||||
ensureUsers = [{
|
||||
name = db.username;
|
||||
ensurePermissions = { "${db.name}.*" = "ALL PRIVILEGES"; };
|
||||
}];
|
||||
};
|
||||
|
||||
services.phpfpm.pools.snipe-it = {
|
||||
inherit user group;
|
||||
phpOptions = ''
|
||||
log_errors = on
|
||||
post_max_size = ${cfg.maxUploadSize}
|
||||
upload_max_filesize = ${cfg.maxUploadSize}
|
||||
'';
|
||||
settings = {
|
||||
"listen.mode" = "0660";
|
||||
"listen.owner" = user;
|
||||
"listen.group" = group;
|
||||
} // cfg.poolConfig;
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = mkDefault true;
|
||||
virtualHosts."${cfg.hostName}" = mkMerge [ cfg.nginx {
|
||||
root = mkForce "${snipe-it}/public";
|
||||
extraConfig = ''
|
||||
index index.php index.html index.htm;
|
||||
${optionalString useSSL "fastcgi_param HTTPS on;"}
|
||||
'';
|
||||
locations = {
|
||||
"/" = {
|
||||
extraConfig = ''try_files $uri $uri/ /index.php$is_args$args;'';
|
||||
};
|
||||
"~ \.php$" = {
|
||||
extraConfig = ''
|
||||
try_files $uri $uri/ =404;
|
||||
include ${nginxPackage}/conf/fastcgi_params;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_pass unix:${config.services.phpfpm.pools."snipe-it".socket};
|
||||
${optionalString useSSL "fastcgi_param HTTPS on;"}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.cacheDir} 0700 ${user} ${group} - -"
|
||||
"d ${cfg.cacheDir}/bootstrap 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir} 0710 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/accessories 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/assets 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/avatars 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/barcodes 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/categories 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/companies 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/components 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/consumables 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/departments 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/locations 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/manufacturers 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/models 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/uploads/suppliers 0750 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/app 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/app/backups 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/app/backups/env-backups 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/debugbar 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/framework 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/framework/cache 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/framework/sessions 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/framework/views 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/logs 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/private_uploads 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/private_uploads/assetmodels 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/private_uploads/assets 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/private_uploads/audits 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/private_uploads/imports 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/private_uploads/licenses 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/private_uploads/signatures 0700 ${user} ${group} - -"
|
||||
"d ${cfg.dataDir}/storage/private_uploads/users 0700 ${user} ${group} - -"
|
||||
] ++ optionals useSqlite [
|
||||
"f ${cfg.dataDir}/database.sqlite 0600 ${user} ${group} -"
|
||||
];
|
||||
|
||||
|
||||
systemd.services.snipe-it-setup = {
|
||||
description = "Preparation tasks for Snipe-IT";
|
||||
before = [ "phpfpm-snipe-it.service" ];
|
||||
after = optional useMysql "mysql.service"
|
||||
++ optional usePgsql "postgresql.service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = user;
|
||||
WorkingDirectory = "${snipe-it}";
|
||||
};
|
||||
script = ''
|
||||
# create .env file
|
||||
cat > ${cfg.dataDir}/.env << EOF
|
||||
APP_KEY="base64:$(head -n1 ${cfg.appKeyFile})"
|
||||
APP_URL="http${optionalString useSSL "s"}://${cfg.hostName}"
|
||||
APP_LOG=syslog
|
||||
MAX_RESULTS=${toString cfg.maxResults}
|
||||
|
||||
'' + optionalString useSSL ''
|
||||
ENABLE_HSTS=true
|
||||
SECURE_COOKIES=true
|
||||
|
||||
'' + ''
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=${toString cfg.session.lifeTime}
|
||||
EXPIRE_ON_CLOSE=${boolToString cfg.session.expireOnClose}
|
||||
ENCRYPT=${boolToString cfg.session.encrypt}
|
||||
|
||||
DB_CONNECTION=${db.type}
|
||||
'' + optionalString (db.type != "sqlite") ''
|
||||
DB_HOST=${db.host}
|
||||
DB_PORT=${toString db.port}
|
||||
DB_PASSWORD="$(head -n1 ${db.passwordFile})"
|
||||
DB_USERNAME=${db.username}
|
||||
DB_DATABASE=${db.name}
|
||||
'' + ''
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=${mail.host}
|
||||
MAIL_PORT=${toString mail.port}
|
||||
${optionalString (mail.encryption != null) "MAIL_ENCRYPTION=${mail.encryption}"}
|
||||
MAIL_FROM_ADDR=${mail.fromAddress}
|
||||
MAIL_FROM_NAME=${mail.fromName}
|
||||
MAIL_AUTO_EMBED=${boolToString mail.autoEmbed.enable}
|
||||
MAIL_AUTO_EMBED_METHOD=${mail.autoEmbed.method}
|
||||
'' + optionalString (mail.username != null) ''
|
||||
MAIL_USERNAME=${mail.username}
|
||||
MAIL_PASSWORD="$(head -n1 ${mail.passwordFile})"
|
||||
'' + optionalString (mail.replytoAddress != null) ''
|
||||
MAIL_REPLYTO_ADDR=${mail.replytoAddress}
|
||||
MAIL_REPLYTO_NAME=${mail.replytoName}
|
||||
'' + optionalString cfg.memcached.enable ''
|
||||
|
||||
CACHE_DRIVER=memcached
|
||||
MEMCACHED_HOST=${cfg.memcached.host}
|
||||
MEMCACHED_PORT=${toString cfg.memcached.port}
|
||||
'' + ''
|
||||
|
||||
${optionalString (cfg.extraConfig != null) cfg.extraConfig}
|
||||
EOF
|
||||
chmod 600 ${cfg.dataDir}/.env
|
||||
|
||||
# re-evaluate configuration
|
||||
${phpPackage}/bin/php artisan config:clear
|
||||
${phpPackage}/bin/php artisan config:cache
|
||||
|
||||
# migrate db
|
||||
${phpPackage}/bin/php artisan migrate --force
|
||||
|
||||
# create caches
|
||||
${phpPackage}/bin/php artisan event:cache
|
||||
${phpPackage}/bin/php artisan view:cache
|
||||
'';
|
||||
};
|
||||
|
||||
users = {
|
||||
users."${user}" = {
|
||||
isSystemUser = true;
|
||||
home = cfg.dataDir;
|
||||
group = group;
|
||||
};
|
||||
groups."${group}" = {};
|
||||
users."${config.services.nginx.user}".extraGroups = [ group ];
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue