320 lines
9.9 KiB
Nix
320 lines
9.9 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.webstack;
|
|
|
|
instanceOpts = { name, ... }:
|
|
{
|
|
options = {
|
|
user = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = lib.mdDoc ''
|
|
User of the typo3 instance. Defaults to attribute name in instances.
|
|
'';
|
|
example = "example.org";
|
|
};
|
|
|
|
domain = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = lib.mdDoc ''
|
|
Domain of the typo3 instance. Defaults to attribute name in instances.
|
|
'';
|
|
example = "example.org";
|
|
};
|
|
|
|
domainAliases = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
example = [ "www.example.org" "example.org" ];
|
|
description = lib.mdDoc ''
|
|
Additional domains served by this typo3 instance.
|
|
'';
|
|
};
|
|
|
|
phpPackage = mkOption {
|
|
type = types.package;
|
|
example = literalExpression "pkgs.php";
|
|
description = lib.mdDoc ''
|
|
Which PHP package to use in this typo3 instance.
|
|
'';
|
|
};
|
|
|
|
enableMysql = mkEnableOption (lib.mdDoc "MySQL Database");
|
|
enableDefaultLocations = mkEnableOption (lib.mdDoc "Create default nginx location directives") // { default = true; };
|
|
|
|
authorizedKeys = mkOption {
|
|
type = types.listOf types.str;
|
|
default = null;
|
|
description = lib.mdDoc ''
|
|
Authorized keys for the typo3 instance ssh user.
|
|
'';
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = ''
|
|
if (!-e $request_filename) {
|
|
rewrite ^/(.+)\.(\d+)\.(php|js|css|png|jpg|gif|gzip)$ /$1.$3 last;
|
|
}
|
|
'';
|
|
description = lib.mdDoc ''
|
|
These lines go to the end of the vhost verbatim.
|
|
'';
|
|
};
|
|
|
|
locations = mkOption {
|
|
type = types.attrsOf (types.submodule (import <nixpkgs/nixos/modules/services/web-servers/nginx/location-options.nix> {
|
|
inherit lib config;
|
|
}));
|
|
default = {};
|
|
example = literalExpression ''
|
|
{
|
|
"/" = {
|
|
proxyPass = "http://localhost:3000";
|
|
};
|
|
};
|
|
'';
|
|
description = lib.mdDoc "Declarative location config";
|
|
};
|
|
|
|
};
|
|
};
|
|
in
|
|
|
|
{
|
|
options.services.webstack = {
|
|
dataDir = mkOption {
|
|
type = types.path;
|
|
default = "/var/www";
|
|
description = lib.mdDoc ''
|
|
The data directory for MySQL.
|
|
|
|
::: {.note}
|
|
If left as the default value of `/var/www` this directory will automatically be created before the web
|
|
server starts, otherwise you are responsible for ensuring the directory exists with appropriate ownership and permissions.
|
|
:::
|
|
'';
|
|
};
|
|
|
|
instances = mkOption {
|
|
type = types.attrsOf (types.submodule instanceOpts);
|
|
default = {};
|
|
description = lib.mdDoc "Create vhosts for typo3";
|
|
example = literalExpression ''
|
|
{
|
|
"typo3.example.com" = {
|
|
domain = "example.com";
|
|
domainAliases = [ "www.example.com" ];
|
|
phpPackage = pkgs.php81;
|
|
authorizedKeys = [
|
|
"ssh-rsa AZA=="
|
|
];
|
|
};
|
|
};
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = {
|
|
systemd.services = mapAttrs' (instance: instanceOpts:
|
|
let
|
|
domain = if instanceOpts.domain != null then instanceOpts.domain else instance;
|
|
in
|
|
nameValuePair "phpfpm-${domain}" {
|
|
serviceConfig = {
|
|
ProtectHome = lib.mkForce "tmpfs";
|
|
BindPaths = "BindPaths=/var/www/${domain}:/var/www/${domain}";
|
|
};
|
|
}
|
|
) cfg.instances;
|
|
|
|
services.phpfpm.pools = mapAttrs' (instance: instanceOpts:
|
|
let
|
|
domain = if instanceOpts.domain != null then instanceOpts.domain else instance;
|
|
user = if instanceOpts.user != null
|
|
then instanceOps.user
|
|
else builtins.replaceStrings ["." "-"] ["_" "_"] domain;
|
|
in
|
|
nameValuePair domain {
|
|
user = user;
|
|
settings = {
|
|
"listen.owner" = config.services.nginx.user;
|
|
"pm" = "dynamic";
|
|
"pm.max_children" = 32;
|
|
"pm.max_requests" = 500;
|
|
"pm.start_servers" = 2;
|
|
"pm.min_spare_servers" = 2;
|
|
"pm.max_spare_servers" = 5;
|
|
"php_admin_value[error_log]" = "syslog";
|
|
"php_admin_value[max_execution_time]" = 240;
|
|
"php_admin_value[max_input_vars]" = 1500;
|
|
"access.log" = "/var/log/$pool.access.log";
|
|
};
|
|
phpPackage = instanceOpts.phpPackage;
|
|
phpEnv."PATH" = pkgs.lib.makeBinPath [ instanceOpts.phpPackage ];
|
|
}
|
|
) cfg.instances;
|
|
|
|
};
|
|
|
|
|
|
config.services.nginx.virtualHosts = mapAttrs' (instance: instanceOpts:
|
|
let
|
|
domain = if instanceOpts.domain != null then instanceOpts.domain else instance;
|
|
user = if instanceOpts.user != null
|
|
then instanceOps.user
|
|
else builtins.replaceStrings ["." "-"] ["_" "_"] domain;
|
|
in
|
|
nameValuePair domain {
|
|
forceSSL = true;
|
|
enableACME = true;
|
|
acmeRoot = null;
|
|
root = cfg.dataDir + "/" + domain + "/public";
|
|
|
|
locations = lib.mkMerge [
|
|
instanceOpts.locations
|
|
(mkIf instanceOpts.enableDefaultLocations {
|
|
"/favicon.ico".extraConfig = ''
|
|
log_not_found off;
|
|
access_log off;
|
|
'';
|
|
|
|
# Cache.appcache, your document html and data
|
|
"~* \\.(?:manifest|appcache|html?|xml|json)$".extraConfig = ''
|
|
expires -1;
|
|
# access_log logs/static.log; # I don't usually include a static log
|
|
'';
|
|
|
|
"~* \\.(jpe?g|png)$".extraConfig = ''
|
|
set $red Z;
|
|
|
|
if ($http_accept ~* "webp") {
|
|
set $red A;
|
|
}
|
|
|
|
if (-f $document_root/webp/$request_uri.webp) {
|
|
set $red "''${red}B";
|
|
}
|
|
|
|
if ($red = "AB") {
|
|
add_header Vary Accept;
|
|
rewrite ^ /webp/$request_uri.webp;
|
|
}
|
|
'';
|
|
|
|
# Cache Media: images, icons, video, audio, HTC
|
|
"~* \\.(?:jpg|jpeg|gif|png|webp|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc|woff2)$".extraConfig = ''
|
|
expires 1y;
|
|
access_log off;
|
|
add_header Cache-Control "public";
|
|
'';
|
|
|
|
# Feed
|
|
"~* \\.(?:rss|atom)$".extraConfig = ''
|
|
expires 1h;
|
|
add_header Cache-Control "public";
|
|
'';
|
|
|
|
# Cache CSS, Javascript, Images, Icons, Video, Audio, HTC, Fonts
|
|
"~* \\.(?:css|js|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc|woff2)$".extraConfig = ''
|
|
expires 1y;
|
|
access_log off;
|
|
add_header Cache-Control "public";
|
|
'';
|
|
|
|
"/".extraConfig = ''
|
|
index index.php index.html;
|
|
try_files $uri $uri/ /index.php$is_args$args;
|
|
'';
|
|
})
|
|
{
|
|
"~ [^/]\\.php(/|$)".extraConfig = ''
|
|
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
|
if (!-f $document_root$fastcgi_script_name) {
|
|
return 404;
|
|
}
|
|
include ${pkgs.nginx}/conf/fastcgi_params;
|
|
include ${pkgs.nginx}/conf/fastcgi.conf;
|
|
fastcgi_buffer_size 32k;
|
|
fastcgi_buffers 8 16k;
|
|
fastcgi_connect_timeout 240s;
|
|
fastcgi_read_timeout 240s;
|
|
fastcgi_send_timeout 240s;
|
|
fastcgi_pass unix:${config.services.phpfpm.pools."${domain}".socket};
|
|
fastcgi_index index.php;
|
|
'';
|
|
}
|
|
];
|
|
|
|
extraConfig = instanceOpts.extraConfig;
|
|
|
|
|
|
# locations = mapAttrs' (location: locationOpts:
|
|
# nameValuePair location locationOpts) instanceOpts.locations;
|
|
|
|
}
|
|
) cfg.instances;
|
|
|
|
config.users.users = mapAttrs' (instance: instanceOpts:
|
|
let
|
|
domain = if instanceOpts.domain != null then instanceOpts.domain else instance;
|
|
user = if instanceOpts.user != null
|
|
then instanceOps.user
|
|
else builtins.replaceStrings ["." "-"] ["_" "_"] domain;
|
|
in
|
|
nameValuePair user {
|
|
isNormalUser = true;
|
|
createHome = true;
|
|
home = "/var/www/" + domain;
|
|
homeMode= "770";
|
|
group = config.services.nginx.group;
|
|
openssh.authorizedKeys.keys = instanceOpts.authorizedKeys;
|
|
}
|
|
) cfg.instances;
|
|
config.users.groups = mapAttrs' (instance: instanceOpts:
|
|
let
|
|
domain = if instanceOpts.domain != null then instanceOpts.domain else instance;
|
|
user = if instanceOpts.user != null
|
|
then instanceOps.user
|
|
else builtins.replaceStrings ["." "-"] ["_" "_"] domain;
|
|
in nameValuePair user {}) cfg.instances;
|
|
|
|
config.services.mysql.ensureUsers = mapAttrsToList (instance: instanceOpts:
|
|
let
|
|
domain = if instanceOpts.domain != null then instanceOpts.domain else instance;
|
|
user = if instanceOpts.user != null
|
|
then instanceOps.user
|
|
else builtins.replaceStrings ["." "-"] ["_" "_"] domain;
|
|
in
|
|
mkIf instanceOpts.enableMysql {
|
|
name = user;
|
|
ensurePermissions = {
|
|
"${user}.*" = "ALL PRIVILEGES";
|
|
};
|
|
}) cfg.instances;
|
|
|
|
config.services.mysql.ensureDatabases = mapAttrsToList (instance: instanceOpts:
|
|
let
|
|
domain = if instanceOpts.domain != null then instanceOpts.domain else instance;
|
|
user = if instanceOpts.user != null
|
|
then instanceOps.user
|
|
else builtins.replaceStrings ["." "-"] ["_" "_"] domain;
|
|
in
|
|
mkIf instanceOpts.enableMysql user
|
|
) cfg.instances;
|
|
config.services.mysqlBackup.databases = mapAttrsToList (instance: instanceOpts:
|
|
let
|
|
domain = if instanceOpts.domain != null then instanceOpts.domain else instance;
|
|
user = if instanceOpts.user != null
|
|
then instanceOps.user
|
|
else builtins.replaceStrings ["." "-"] ["_" "_"] domain;
|
|
in
|
|
mkIf instanceOpts.enableMysql user
|
|
) cfg.instances;
|
|
}
|
|
|