| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  | # self-hosted photo gallery | 
					
						
							|  |  |  | { config, pkgs, lib, ... }: | 
					
						
							|  |  |  | let | 
					
						
							|  |  |  |   cfg = config.my.services.photoprism; | 
					
						
							|  |  |  |   domain = config.networking.domain; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   env = { | 
					
						
							|  |  |  |     PHOTOPRISM_ORIGINALS_PATH = cfg.originalsPath; | 
					
						
							|  |  |  |     PHOTOPRISM_STORAGE_PATH = cfg.storagePath; | 
					
						
							|  |  |  |     PHOTOPRISM_IMPORT_PATH = cfg.importPath; | 
					
						
							|  |  |  |     PHOTOPRISM_HTTP_HOST = cfg.address; | 
					
						
							|  |  |  |     PHOTOPRISM_HTTP_PORT = toString cfg.port; | 
					
						
							|  |  |  |   } // ( | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     lib.mapAttrs (_: toString) cfg.settings | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   manage = | 
					
						
							|  |  |  |     let | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |       setupEnv = lib.concatStringsSep "\n" (lib.mapAttrsToList (name: val: "export ${name}=${lib.escapeShellArg val}") env); | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |     in | 
					
						
							|  |  |  |     pkgs.writeShellScript "manage" ''
 | 
					
						
							|  |  |  |       ${setupEnv} | 
					
						
							|  |  |  |       exec ${cfg.package}/bin/photoprism "$@" | 
					
						
							|  |  |  |     '';
 | 
					
						
							|  |  |  | in | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   meta.maintainers = with lib.maintainers; [ stunkymonkey ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |   options.my.services.photoprism = { | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     enable = lib.mkEnableOption (lib.mdDoc "Photoprism web server"); | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     passwordFile = lib.mkOption { | 
					
						
							|  |  |  |       type = lib.types.nullOr lib.types.path; | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |       default = null; | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |       description = lib.mdDoc ''
 | 
					
						
							|  |  |  |         Admin password file. | 
					
						
							|  |  |  |       '';
 | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     address = lib.mkOption { | 
					
						
							|  |  |  |       type = lib.types.str; | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |       default = "localhost"; | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |       description = lib.mdDoc ''
 | 
					
						
							|  |  |  |         Web interface address. | 
					
						
							|  |  |  |       '';
 | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     port = lib.mkOption { | 
					
						
							|  |  |  |       type = lib.types.port; | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |       default = 2342; | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |       description = lib.mdDoc ''
 | 
					
						
							|  |  |  |         Web interface port. | 
					
						
							|  |  |  |       '';
 | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     originalsPath = lib.mkOption { | 
					
						
							|  |  |  |       type = lib.types.path; | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |       default = null; | 
					
						
							|  |  |  |       example = "/data/photos"; | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |       description = lib.mdDoc ''
 | 
					
						
							|  |  |  |         Storage path of your original media files (photos and videos) | 
					
						
							|  |  |  |       '';
 | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     importPath = lib.mkOption { | 
					
						
							|  |  |  |       type = lib.types.str; | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |       default = "import"; | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |       description = lib.mdDoc ''
 | 
					
						
							|  |  |  |         Relative or absolute to the `originalsPath` from where the files should be imported. | 
					
						
							|  |  |  |       '';
 | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     storagePath = lib.mkOption { | 
					
						
							|  |  |  |       type = lib.types.path; | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |       default = "/var/lib/photoprism"; | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |       description = lib.mdDoc ''
 | 
					
						
							|  |  |  |         location for sidecar, cache, and database files. | 
					
						
							|  |  |  |       '';
 | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     package = lib.mkPackageOption pkgs "photoprism" { }; | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |     settings = lib.mkOption { | 
					
						
							|  |  |  |       type = lib.types.attrsOf lib.types.str; | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |       default = { }; | 
					
						
							|  |  |  |       description = lib.mdDoc ''
 | 
					
						
							| 
									
										
										
										
											2023-01-16 22:45:53 +01:00
										 |  |  |         Extra photoprism config options. See [the getting-started guide](https://docs.photoprism.app/getting-started/config-options/) for available options. | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |       '';
 | 
					
						
							|  |  |  |       example = { | 
					
						
							|  |  |  |         PHOTOPRISM_DEFAULT_LOCALE = "de"; | 
					
						
							|  |  |  |         PHOTOPRISM_ADMIN_USER = "root"; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   config = lib.mkIf cfg.enable { | 
					
						
							|  |  |  |     systemd.services.photoprism = { | 
					
						
							|  |  |  |       description = "Photoprism server"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       serviceConfig = { | 
					
						
							|  |  |  |         Restart = "on-failure"; | 
					
						
							|  |  |  |         User = "photoprism"; | 
					
						
							|  |  |  |         Group = "photoprism"; | 
					
						
							|  |  |  |         DynamicUser = true; | 
					
						
							|  |  |  |         StateDirectory = "photoprism"; | 
					
						
							|  |  |  |         WorkingDirectory = "/var/lib/photoprism"; | 
					
						
							|  |  |  |         RuntimeDirectory = "photoprism"; | 
					
						
							|  |  |  |         LoadCredential = lib.optionalString (cfg.passwordFile != null) | 
					
						
							|  |  |  |           "PHOTOPRISM_ADMIN_PASSWORD:${cfg.passwordFile}"; | 
					
						
							|  |  |  |         CapabilityBoundingSet = ""; | 
					
						
							|  |  |  |         LockPersonality = true; | 
					
						
							|  |  |  |         PrivateDevices = true; | 
					
						
							|  |  |  |         PrivateUsers = true; | 
					
						
							|  |  |  |         ProtectClock = true; | 
					
						
							|  |  |  |         ProtectControlGroups = true; | 
					
						
							|  |  |  |         ProtectHome = true; | 
					
						
							|  |  |  |         ProtectHostname = true; | 
					
						
							|  |  |  |         ProtectKernelLogs = true; | 
					
						
							|  |  |  |         ProtectKernelModules = true; | 
					
						
							|  |  |  |         ProtectKernelTunables = true; | 
					
						
							|  |  |  |         RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; | 
					
						
							|  |  |  |         RestrictNamespaces = true; | 
					
						
							|  |  |  |         RestrictRealtime = true; | 
					
						
							|  |  |  |         SystemCallArchitectures = "native"; | 
					
						
							| 
									
										
										
										
											2023-01-15 12:17:21 +01:00
										 |  |  |         #SystemCallFilter = [ "@system-service" "~@privileged @resources @setuid @keyring" ]; | 
					
						
							|  |  |  |         SystemCallFilter = [ "@system-service" "~@privileged @setuid @keyring" ]; | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |         UMask = "0066"; | 
					
						
							|  |  |  |       } // lib.optionalAttrs (cfg.port < 1024) { | 
					
						
							|  |  |  |         AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; | 
					
						
							|  |  |  |         CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       wantedBy = [ "multi-user.target" ]; | 
					
						
							|  |  |  |       environment = env; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-16 22:45:53 +01:00
										 |  |  |       # wait for easier password configuration: https://github.com/photoprism/photoprism/pull/2302 | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |       preStart = ''
 | 
					
						
							| 
									
										
										
										
											2023-01-16 22:45:53 +01:00
										 |  |  |         ${lib.optionalString (cfg.passwordFile != null) ''
 | 
					
						
							|  |  |  |           export PHOTOPRISM_ADMIN_PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/PHOTOPRISM_ADMIN_PASSWORD") | 
					
						
							|  |  |  |         ''}
 | 
					
						
							|  |  |  |         exec ${cfg.package}/bin/photoprism migrations run -f | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-25 12:24:28 +01:00
										 |  |  |         ln -sf ${manage} photoprism-manage | 
					
						
							|  |  |  |       '';
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       # wait for easier password configuration: https://github.com/photoprism/photoprism/pull/2302 | 
					
						
							|  |  |  |       script = ''
 | 
					
						
							|  |  |  |         ${lib.optionalString (cfg.passwordFile != null) ''
 | 
					
						
							|  |  |  |           export PHOTOPRISM_ADMIN_PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/PHOTOPRISM_ADMIN_PASSWORD") | 
					
						
							|  |  |  |         ''}
 | 
					
						
							|  |  |  |         exec ${cfg.package}/bin/photoprism start | 
					
						
							|  |  |  |       '';
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Proxy to Photoprism | 
					
						
							|  |  |  |     my.services.nginx.virtualHosts = [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         subdomain = "photos"; | 
					
						
							|  |  |  |         inherit (cfg) port; | 
					
						
							|  |  |  |         extraConfig = { | 
					
						
							|  |  |  |           locations."/" = { | 
					
						
							|  |  |  |             proxyWebsockets = true; | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     webapps.apps.photoprism = { | 
					
						
							|  |  |  |       dashboard = { | 
					
						
							|  |  |  |         name = "Photos"; | 
					
						
							|  |  |  |         category = "media"; | 
					
						
							|  |  |  |         icon = "image"; | 
					
						
							|  |  |  |         link = "https://photos.${domain}/library/login"; | 
					
						
							|  |  |  |         method = "get"; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } |