|
|
|
@@ -7,19 +7,91 @@
|
|
|
|
|
let
|
|
|
|
|
cfg = config.services.meilisearch;
|
|
|
|
|
|
|
|
|
|
settingsFormat = pkgs.formats.toml { };
|
|
|
|
|
|
|
|
|
|
# These secrets are used in the config file and can be set to paths.
|
|
|
|
|
secrets-with-path =
|
|
|
|
|
builtins.map
|
|
|
|
|
(
|
|
|
|
|
{ environment, name }:
|
|
|
|
|
{
|
|
|
|
|
inherit name environment;
|
|
|
|
|
setting = cfg.settings.${name};
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
environment = "MEILI_SSL_CERT_PATH";
|
|
|
|
|
name = "ssl_cert_path";
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
environment = "MEILI_SSL_KEY_PATH";
|
|
|
|
|
name = "ssl_key_path";
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
environment = "MEILI_SSL_AUTH_PATH";
|
|
|
|
|
name = "ssl_auth_path";
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
environment = "MEILI_SSL_OCSP_PATH";
|
|
|
|
|
name = "ssl_ocsp_path";
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
# We also handle `master_key` separately.
|
|
|
|
|
# It cannot be set to a path, so we template it.
|
|
|
|
|
master-key-placeholder = "@MASTER_KEY@";
|
|
|
|
|
|
|
|
|
|
configFile = settingsFormat.generate "config.toml" (
|
|
|
|
|
builtins.removeAttrs (
|
|
|
|
|
if cfg.masterKeyFile != null then
|
|
|
|
|
cfg.settings // { master_key = master-key-placeholder; }
|
|
|
|
|
else
|
|
|
|
|
builtins.removeAttrs cfg.settings [ "master_key" ]
|
|
|
|
|
) (map (secret: secret.name) secrets-with-path)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
in
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
meta.maintainers = with lib.maintainers; [
|
|
|
|
|
Br1ght0ne
|
|
|
|
|
happysalada
|
|
|
|
|
];
|
|
|
|
|
meta.doc = ./meilisearch.md;
|
|
|
|
|
|
|
|
|
|
###### interface
|
|
|
|
|
imports = [
|
|
|
|
|
(lib.mkRenamedOptionModule
|
|
|
|
|
[ "services" "meilisearch" "environment" ]
|
|
|
|
|
[ "services" "meilisearch" "settings" "env" ]
|
|
|
|
|
)
|
|
|
|
|
(lib.mkRenamedOptionModule
|
|
|
|
|
[ "services" "meilisearch" "logLevel" ]
|
|
|
|
|
[ "services" "meilisearch" "settings" "log_level" ]
|
|
|
|
|
)
|
|
|
|
|
(lib.mkRenamedOptionModule
|
|
|
|
|
[ "services" "meilisearch" "noAnalytics" ]
|
|
|
|
|
[ "services" "meilisearch" "settings" "no_analytics" ]
|
|
|
|
|
)
|
|
|
|
|
(lib.mkRenamedOptionModule
|
|
|
|
|
[ "services" "meilisearch" "maxIndexSize" ]
|
|
|
|
|
[ "services" "meilisearch" "settings" "max_index_size" ]
|
|
|
|
|
)
|
|
|
|
|
(lib.mkRenamedOptionModule
|
|
|
|
|
[ "services" "meilisearch" "payloadSizeLimit" ]
|
|
|
|
|
[ "services" "meilisearch" "settings" "http_payload_size_limit" ]
|
|
|
|
|
)
|
|
|
|
|
(lib.mkRenamedOptionModule
|
|
|
|
|
[ "services" "meilisearch" "dumplessUpgrade" ]
|
|
|
|
|
[ "services" "meilisearch" "settings" "experimental_dumpless_upgrade" ]
|
|
|
|
|
)
|
|
|
|
|
(lib.mkRemovedOptionModule [ "services" "meilisearch" "masterKeyEnvironmentFile" ] ''
|
|
|
|
|
Use `services.meilisearch.masterKeyFile` instead. It does not require you to prefix the file with "MEILI_MASTER_KEY=".
|
|
|
|
|
If you were abusing this option to set other options, you can now configure them with `services.meilisearch.settings`.
|
|
|
|
|
'')
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
options.services.meilisearch = {
|
|
|
|
|
enable = lib.mkEnableOption "MeiliSearch - a RESTful search API";
|
|
|
|
|
enable = lib.mkEnableOption "Meilisearch - a RESTful search API";
|
|
|
|
|
|
|
|
|
|
package = lib.mkPackageOption pkgs "meilisearch" {
|
|
|
|
|
extraDescription = ''
|
|
|
|
@@ -28,106 +100,87 @@ in
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
listenAddress = lib.mkOption {
|
|
|
|
|
description = "MeiliSearch listen address.";
|
|
|
|
|
default = "127.0.0.1";
|
|
|
|
|
default = "localhost";
|
|
|
|
|
type = lib.types.str;
|
|
|
|
|
description = ''
|
|
|
|
|
The IP address that Meilisearch will listen on.
|
|
|
|
|
|
|
|
|
|
It can also be a hostname like "localhost". If it resolves to an IPv4 and IPv6 address, Meilisearch will listen on both.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
listenPort = lib.mkOption {
|
|
|
|
|
description = "MeiliSearch port to listen on.";
|
|
|
|
|
default = 7700;
|
|
|
|
|
type = lib.types.port;
|
|
|
|
|
description = ''
|
|
|
|
|
The port that Meilisearch will listen on.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
environment = lib.mkOption {
|
|
|
|
|
description = "Defines the running environment of MeiliSearch.";
|
|
|
|
|
default = "development";
|
|
|
|
|
type = lib.types.enum [
|
|
|
|
|
"development"
|
|
|
|
|
"production"
|
|
|
|
|
];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# TODO change this to LoadCredentials once possible
|
|
|
|
|
masterKeyEnvironmentFile = lib.mkOption {
|
|
|
|
|
masterKeyFile = lib.mkOption {
|
|
|
|
|
description = ''
|
|
|
|
|
Path to file which contains the master key.
|
|
|
|
|
By doing so, all routes will be protected and will require a key to be accessed.
|
|
|
|
|
If no master key is provided, all routes can be accessed without requiring any key.
|
|
|
|
|
The format is the following:
|
|
|
|
|
MEILI_MASTER_KEY=my_secret_key
|
|
|
|
|
'';
|
|
|
|
|
default = null;
|
|
|
|
|
type = with lib.types; nullOr path;
|
|
|
|
|
type = lib.types.nullOr lib.types.path;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
noAnalytics = lib.mkOption {
|
|
|
|
|
settings = lib.mkOption {
|
|
|
|
|
description = ''
|
|
|
|
|
Deactivates analytics.
|
|
|
|
|
Analytics allow MeiliSearch to know how many users are using MeiliSearch,
|
|
|
|
|
which versions and which platforms are used.
|
|
|
|
|
This process is entirely anonymous.
|
|
|
|
|
Configuration settings for Meilisearch.
|
|
|
|
|
Look at the documentation for available options:
|
|
|
|
|
https://github.com/meilisearch/meilisearch/blob/main/config.toml
|
|
|
|
|
https://www.meilisearch.com/docs/learn/self_hosted/configure_meilisearch_at_launch#all-instance-options
|
|
|
|
|
'';
|
|
|
|
|
default = true;
|
|
|
|
|
type = lib.types.bool;
|
|
|
|
|
|
|
|
|
|
default = { };
|
|
|
|
|
|
|
|
|
|
type = lib.types.submodule {
|
|
|
|
|
freeformType = settingsFormat.type;
|
|
|
|
|
|
|
|
|
|
imports = builtins.map (secret: {
|
|
|
|
|
# give them proper types, just so they're easier to consume from this file
|
|
|
|
|
options.${secret.name} = lib.mkOption {
|
|
|
|
|
# but they should not show up in documentation as special in any way.
|
|
|
|
|
visible = false;
|
|
|
|
|
|
|
|
|
|
type = lib.types.nullOr lib.types.path;
|
|
|
|
|
default = null;
|
|
|
|
|
};
|
|
|
|
|
}) secrets-with-path;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
logLevel = lib.mkOption {
|
|
|
|
|
description = ''
|
|
|
|
|
Defines how much detail should be present in MeiliSearch's logs.
|
|
|
|
|
MeiliSearch currently supports four log levels, listed in order of increasing verbosity:
|
|
|
|
|
- 'ERROR': only log unexpected events indicating MeiliSearch is not functioning as expected
|
|
|
|
|
- 'WARN:' log all unexpected events, regardless of their severity
|
|
|
|
|
- 'INFO:' log all events. This is the default value
|
|
|
|
|
- 'DEBUG': log all events and including detailed information on MeiliSearch's internal processes.
|
|
|
|
|
Useful when diagnosing issues and debugging
|
|
|
|
|
'';
|
|
|
|
|
default = "INFO";
|
|
|
|
|
type = lib.types.str;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
maxIndexSize = lib.mkOption {
|
|
|
|
|
description = ''
|
|
|
|
|
Sets the maximum size of the index.
|
|
|
|
|
Value must be given in bytes or explicitly stating a base unit.
|
|
|
|
|
For example, the default value can be written as 107374182400, '107.7Gb', or '107374 Mb'.
|
|
|
|
|
Default is 100 GiB
|
|
|
|
|
'';
|
|
|
|
|
default = "107374182400";
|
|
|
|
|
type = lib.types.str;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
payloadSizeLimit = lib.mkOption {
|
|
|
|
|
description = ''
|
|
|
|
|
Sets the maximum size of accepted JSON payloads.
|
|
|
|
|
Value must be given in bytes or explicitly stating a base unit.
|
|
|
|
|
For example, the default value can be written as 107374182400, '107.7Gb', or '107374 Mb'.
|
|
|
|
|
Default is ~ 100 MB
|
|
|
|
|
'';
|
|
|
|
|
default = "104857600";
|
|
|
|
|
type = lib.types.str;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# TODO: turn on by default when it stops being experimental
|
|
|
|
|
dumplessUpgrade = lib.mkOption {
|
|
|
|
|
default = false;
|
|
|
|
|
example = true;
|
|
|
|
|
description = ''
|
|
|
|
|
Whether to enable (experimental) dumpless upgrade.
|
|
|
|
|
|
|
|
|
|
Allows upgrading from Meilisearch >=v1.12 to Meilisearch >=v1.13 without manually
|
|
|
|
|
dumping and importing the database.
|
|
|
|
|
|
|
|
|
|
More information at https://www.meilisearch.com/docs/learn/update_and_migration/updating#dumpless-upgrade
|
|
|
|
|
'';
|
|
|
|
|
type = lib.types.bool;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
###### implementation
|
|
|
|
|
|
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
|
|
|
assertions = [
|
|
|
|
|
{
|
|
|
|
|
assertion = !cfg.settings ? master_key;
|
|
|
|
|
message = ''
|
|
|
|
|
Do not set `services.meilisearch.settings.master_key` in your configuration.
|
|
|
|
|
Use `services.meilisearch.masterKeyFile` instead.
|
|
|
|
|
'';
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
services.meilisearch.settings = {
|
|
|
|
|
# we use `listenAddress` and `listenPort` to derive the `http_addr` setting.
|
|
|
|
|
# this is the only setting we treat like this.
|
|
|
|
|
# we do this because some dependent services like Misskey/Sharkey need separate host,port for no good reason.
|
|
|
|
|
http_addr = "${cfg.listenAddress}:${toString cfg.listenPort}";
|
|
|
|
|
|
|
|
|
|
# upstream's default for `db_path` is `/var/lib/meilisearch/data.ms/`, but ours is different for no reason.
|
|
|
|
|
db_path = lib.mkDefault "/var/lib/meilisearch";
|
|
|
|
|
# these are equivalent to the upstream defaults, because we set a working directory.
|
|
|
|
|
# they are only set here for consistency with `db_path`.
|
|
|
|
|
dump_dir = lib.mkDefault "/var/lib/meilisearch/dumps";
|
|
|
|
|
snapshot_dir = lib.mkDefault "/var/lib/meilisearch/snapshots";
|
|
|
|
|
|
|
|
|
|
# this is intentionally different from upstream's default.
|
|
|
|
|
no_analytics = lib.mkDefault true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
warnings = lib.optional (lib.versionOlder cfg.package.version "1.12") ''
|
|
|
|
|
Meilisearch 1.11 will be removed in NixOS 25.11. As it was the last
|
|
|
|
@@ -149,24 +202,41 @@ in
|
|
|
|
|
environment.systemPackages = [ cfg.package ];
|
|
|
|
|
|
|
|
|
|
systemd.services.meilisearch = {
|
|
|
|
|
description = "MeiliSearch daemon";
|
|
|
|
|
description = "Meilisearch daemon";
|
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
|
after = [ "network.target" ];
|
|
|
|
|
environment = {
|
|
|
|
|
MEILI_DB_PATH = "/var/lib/meilisearch";
|
|
|
|
|
MEILI_HTTP_ADDR = "${cfg.listenAddress}:${toString cfg.listenPort}";
|
|
|
|
|
MEILI_NO_ANALYTICS = lib.boolToString cfg.noAnalytics;
|
|
|
|
|
MEILI_ENV = cfg.environment;
|
|
|
|
|
MEILI_DUMP_DIR = "/var/lib/meilisearch/dumps";
|
|
|
|
|
MEILI_LOG_LEVEL = cfg.logLevel;
|
|
|
|
|
MEILI_MAX_INDEX_SIZE = cfg.maxIndexSize;
|
|
|
|
|
MEILI_EXPERIMENTAL_DUMPLESS_UPGRADE = lib.boolToString cfg.dumplessUpgrade;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
preStart = lib.mkMerge [
|
|
|
|
|
''
|
|
|
|
|
install -m 700 '${configFile}' "$RUNTIME_DIRECTORY/config.toml"
|
|
|
|
|
''
|
|
|
|
|
(lib.mkIf (cfg.masterKeyFile != null) ''
|
|
|
|
|
${lib.getExe pkgs.replace-secret} '${master-key-placeholder}' "$CREDENTIALS_DIRECTORY/master_key" "$RUNTIME_DIRECTORY/config.toml"
|
|
|
|
|
'')
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
environment = builtins.listToAttrs (
|
|
|
|
|
builtins.map (secret: {
|
|
|
|
|
name = secret.environment;
|
|
|
|
|
value = lib.mkIf (secret.setting != null) "%d/${secret.name}";
|
|
|
|
|
}) secrets-with-path
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
|
ExecStart = "${cfg.package}/bin/meilisearch";
|
|
|
|
|
LoadCredential = lib.mkMerge (
|
|
|
|
|
[
|
|
|
|
|
(lib.mkIf (cfg.masterKeyFile != null) [ "master_key:${cfg.masterKeyFile}" ])
|
|
|
|
|
]
|
|
|
|
|
++ builtins.map (
|
|
|
|
|
secret: lib.mkIf (secret.setting != null) [ "${secret.name}:${secret.setting}" ]
|
|
|
|
|
) secrets-with-path
|
|
|
|
|
);
|
|
|
|
|
ExecStart = "${lib.getExe cfg.package} --config-file-path \${RUNTIME_DIRECTORY}/config.toml";
|
|
|
|
|
DynamicUser = true;
|
|
|
|
|
StateDirectory = "meilisearch";
|
|
|
|
|
EnvironmentFile = lib.mkIf (cfg.masterKeyEnvironmentFile != null) cfg.masterKeyEnvironmentFile;
|
|
|
|
|
WorkingDirectory = "%S/meilisearch";
|
|
|
|
|
RuntimeDirectory = "meilisearch";
|
|
|
|
|
RuntimeDirectoryMode = "0700";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|